Guest User

Untitled

a guest
Jun 18th, 2018
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.50 KB | None | 0 0
  1. struct State
  2. {
  3. bool digitAllowed;
  4. bool operatorAllowed;
  5. bool openParenthAllowed;
  6. bool closingParenthAllowed;
  7. int numOpenParenths;
  8. int numClosingParenths;
  9. State(bool dig, bool op, bool oP, bool cP, int nO=0, int nC=0) :
  10. digitAllowed(dig), operatorAllowed(op),
  11. openParenthAllowed(oP), closingParenthAllowed(cP),
  12. numOpenParenths(nO), numClosingParenths(nC) {}
  13. State() {}
  14. };
  15.  
  16. #ifndef MAINWINDOW_H
  17. #define MAINWINDOW_H
  18.  
  19. #include <QMainWindow>
  20. #include <QPushButton>
  21. #include <QLabel>
  22. #include <QKeyEvent>
  23. #include <QStack>
  24.  
  25. namespace Ui {
  26. class MainWindow;
  27. }
  28.  
  29. class MainWindow : public QMainWindow
  30. {
  31. Q_OBJECT
  32.  
  33. /* Used to keep track of the history of operations that the user
  34. * performed when entering input. Primarily used by backspace and
  35. * clear to undo previous user input and restore the calculator's
  36. * state to its previous input restrictions.
  37. */
  38. struct State
  39. {
  40. bool digitAllowed;
  41. bool operatorAllowed;
  42. bool openParenthAllowed;
  43. bool closingParenthAllowed;
  44. int numOpenParenths;
  45. int numClosingParenths;
  46. State(bool dig, bool op, bool oP, bool cP, int nO=0, int nC=0) :
  47. digitAllowed(dig), operatorAllowed(op),
  48. openParenthAllowed(oP), closingParenthAllowed(cP),
  49. numOpenParenths(nO), numClosingParenths(nC) {}
  50. State() {}
  51. };
  52.  
  53. public:
  54. explicit MainWindow(QWidget *parent = 0);
  55. Ui::MainWindow* getUI() const { return ui; }
  56. ~MainWindow();
  57.  
  58. private:
  59. Ui::MainWindow *ui;
  60. QLabel *input;
  61. QStack<State> history;
  62. bool operatorUsedDirectlyBefore() const;
  63. void reset();
  64.  
  65. signals:
  66. void input_is_ready(QString input);
  67.  
  68. private slots:
  69. void on_digit_released();
  70. void on_buttonDecimalPoint_released();
  71. void on_buttonNegate_released();
  72. void on_binary_button_released();
  73. void on_buttonEquals_released();
  74. void on_buttonClear_released();
  75. void on_buttonRoot_released();
  76. void on_buttonOpenParenth_released();
  77. void on_buttonCloseParenth_released();
  78. void on_buttonXSquared_released();
  79. void on_buttonSqrt_released();
  80. void on_buttonBack_released();
  81. };
  82.  
  83. #endif // MAINWINDOW_H
  84.  
  85. QStack<State> history;
  86.  
  87. #include "mainwindow.h"
  88. #include "ui_mainwindow.h"
  89. #include "operator.h"
  90. #include <QDebug> // debug output
  91. #include <QChar>
  92. #include <QShortcut> // keyboard input
  93. #include <QKeySequence> // keyboard input
  94. #include <QFont> // Qt fonts
  95.  
  96.  
  97. /* Constructor for MainWindow objects. Connects all button signals
  98. * to their appropriate private slots to handle user input and sets
  99. * up some basic member variables.
  100. */
  101. MainWindow::MainWindow(QWidget *parent) :
  102. QMainWindow(parent),
  103. ui(new Ui::MainWindow)
  104. {
  105. // Basic variable setups
  106. ui->setupUi(this);
  107. input = ui->labelInput;
  108. reset();
  109.  
  110. // Connect digits' released() signals to on_digit_released
  111. connect(ui->button0, SIGNAL(released()), this, SLOT(on_digit_released()));
  112. connect(ui->button1, SIGNAL(released()), this, SLOT(on_digit_released()));
  113. connect(ui->button2, SIGNAL(released()), this, SLOT(on_digit_released()));
  114. connect(ui->button3, SIGNAL(released()), this, SLOT(on_digit_released()));
  115. connect(ui->button4, SIGNAL(released()), this, SLOT(on_digit_released()));
  116. connect(ui->button5, SIGNAL(released()), this, SLOT(on_digit_released()));
  117. connect(ui->button6, SIGNAL(released()), this, SLOT(on_digit_released()));
  118. connect(ui->button7, SIGNAL(released()), this, SLOT(on_digit_released()));
  119. connect(ui->button8, SIGNAL(released()), this, SLOT(on_digit_released()));
  120. connect(ui->button9, SIGNAL(released()), this, SLOT(on_digit_released()));
  121.  
  122. // Connect binary operators' released() signals to on_binary_button_released
  123. connect(ui->buttonPlus, SIGNAL(released()),
  124. this, SLOT(on_binary_button_released()));
  125. connect(ui->buttonMinus, SIGNAL(released()),
  126. this, SLOT(on_binary_button_released()));
  127. connect(ui->buttonTimes, SIGNAL(released()),
  128. this, SLOT(on_binary_button_released()));
  129. connect(ui->buttonDivide, SIGNAL(released()),
  130. this, SLOT(on_binary_button_released()));
  131.  
  132. // Shortcuts for math input via keyboard
  133. // TODO potential memory leaks
  134. QShortcut *shortcut0 = new QShortcut(QKeySequence("0"), this);
  135. QShortcut *shortcut1 = new QShortcut(QKeySequence("1"), this);
  136. QShortcut *shortcut2 = new QShortcut(QKeySequence("2"), this);
  137. QShortcut *shortcut3 = new QShortcut(QKeySequence("3"), this);
  138. QShortcut *shortcut4 = new QShortcut(QKeySequence("4"), this);
  139. QShortcut *shortcut5 = new QShortcut(QKeySequence("5"), this);
  140. QShortcut *shortcut6 = new QShortcut(QKeySequence("6"), this);
  141. QShortcut *shortcut7 = new QShortcut(QKeySequence("7"), this);
  142. QShortcut *shortcut8 = new QShortcut(QKeySequence("8"), this);
  143. QShortcut *shortcut9 = new QShortcut(QKeySequence("9"), this);
  144. QShortcut *shortcutDecimal = new QShortcut(QKeySequence("."), this);
  145. QShortcut *shortcutPlus = new QShortcut(QKeySequence("+"), this);
  146. QShortcut *shortcutMinus = new QShortcut(QKeySequence("-"), this);
  147. QShortcut *shortcutTimes = new QShortcut(QKeySequence("SHIFT+8"), this);
  148. QShortcut *shortcutDivide = new QShortcut(QKeySequence("/"), this);
  149. QShortcut *shortcutOpenParenth = new QShortcut(QKeySequence("SHIFT+9"), this);
  150. QShortcut *shortcutCloseParenth = new QShortcut(QKeySequence("SHIFT+0"), this);
  151. QShortcut *shortcutEnter = new QShortcut(QKeySequence(Qt::Key_Return), this);
  152. QShortcut *shortcutBackspace = new QShortcut(QKeySequence("Backspace"), this);
  153. connect(shortcut0, SIGNAL(activated()), ui->button0, SLOT(click()));
  154. connect(shortcut1, SIGNAL(activated()), ui->button1, SLOT(click()));
  155. connect(shortcut2, SIGNAL(activated()), ui->button2, SLOT(click()));
  156. connect(shortcut3, SIGNAL(activated()), ui->button3, SLOT(click()));
  157. connect(shortcut4, SIGNAL(activated()), ui->button4, SLOT(click()));
  158. connect(shortcut5, SIGNAL(activated()), ui->button5, SLOT(click()));
  159. connect(shortcut6, SIGNAL(activated()), ui->button6, SLOT(click()));
  160. connect(shortcut7, SIGNAL(activated()), ui->button7, SLOT(click()));
  161. connect(shortcut8, SIGNAL(activated()), ui->button8, SLOT(click()));
  162. connect(shortcut9, SIGNAL(activated()), ui->button9, SLOT(click()));
  163. connect(shortcutDecimal, SIGNAL(activated()), ui->buttonDecimalPoint, SLOT(click()));
  164. connect(shortcutPlus, SIGNAL(activated()), ui->buttonPlus, SLOT(click()));
  165. connect(shortcutMinus, SIGNAL(activated()), ui->buttonMinus, SLOT(click()));
  166. connect(shortcutTimes, SIGNAL(activated()), ui->buttonTimes, SLOT(click()));
  167. connect(shortcutDivide, SIGNAL(activated()), ui->buttonDivide, SLOT(click()));
  168. connect(shortcutOpenParenth, SIGNAL(activated()), ui->buttonOpenParenth, SLOT(click()));
  169. connect(shortcutCloseParenth, SIGNAL(activated()), ui->buttonCloseParenth, SLOT(click()));
  170. connect(shortcutEnter, SIGNAL(activated()), ui->buttonEquals, SLOT(click()));
  171. connect(shortcutBackspace, SIGNAL(activated()), ui->buttonBack, SLOT(click()));
  172. }
  173.  
  174. MainWindow::~MainWindow()
  175. {
  176. delete input;
  177. delete ui;
  178. }
  179.  
  180. /* Used by other functions to check if an operator was
  181. * applied directly before the current user's input token.
  182. */
  183. bool MainWindow::operatorUsedDirectlyBefore() const
  184. {
  185. return ((bool)operators.count(input->text().at(input->text().length()-1)));
  186. }
  187.  
  188.  
  189. /* Called when a user clicks any of the digit buttons (0-9) or
  190. * enters a digit from their keyboard (0-9). Appends the corresponding
  191. * digit to the input.
  192. */
  193. void MainWindow::on_digit_released()
  194. {
  195. State currentState = history.top();
  196.  
  197. if(currentState.digitAllowed)
  198. {
  199. QPushButton *button = (QPushButton*)sender();
  200.  
  201. history.push(State(true, true, false, true,
  202. history.top().numOpenParenths,
  203. history.top().numClosingParenths));
  204.  
  205. // Initial input is just a 0
  206. if(input->text().length() == 1 && input->text()[0] == '0')
  207. {
  208. input->setText(input->text().replace(0, 1, button->text()));
  209. }
  210. // Otherwise, append the digit
  211. else
  212. {
  213. input->setText(input->text().append(button->text()));
  214. }
  215. }
  216. }
  217.  
  218. /* Called when a user clicks the '.' button or uses the keyboard
  219. * shortcut '.' Appends a decimal point to the last digit that was
  220. * entered.
  221. */
  222. void MainWindow::on_buttonDecimalPoint_released()
  223. {
  224. // TODO prevent entry of multiple decimal points
  225. if(!operatorUsedDirectlyBefore())
  226. {
  227. input->setText(input->text() + ".");
  228. }
  229. }
  230.  
  231. /* Called when a user clicks the '±' button. Negates an input expression.
  232. * Applies the negative sign to the last number entered (if one exists).
  233. * Negating a negative number will remove the negative sign. Negating the
  234. * right operand of addition will change the '+' operator to subtraction.
  235. * Similarly, negating the right operand of subtraction will change the
  236. * '–' operator to addition.
  237. */
  238. void MainWindow::on_buttonNegate_released()
  239. {
  240. // Check if there are currently any operators in input
  241. bool inputHasOperators = false;
  242. // If so, get the index of the last one (hence reverse traversal)
  243. int indexOfLastOperator = -1;
  244. for(int i = input->text().length() - 1; i >= 0; i--)
  245. {
  246. if(operators.count(input->text().at(i)) == 1)
  247. {
  248. inputHasOperators = true;
  249. indexOfLastOperator = i;
  250. break;
  251. }
  252. }
  253.  
  254. // If we entered an operator and then tried to negate something
  255. if(indexOfLastOperator == input->text().length()-1){ return; }
  256.  
  257. // Case 1: Input contains operators
  258. if(inputHasOperators)
  259. {
  260. QChar lastOperator = input->text().at(indexOfLastOperator);
  261.  
  262. // If the number in question is already negative, undo it
  263. if(input->text().at(indexOfLastOperator+1) == '-')
  264. {
  265. input->setText(input->text().replace(indexOfLastOperator+1, 1, ""));
  266. }
  267.  
  268. // If not, let's check what kind of operator came before it
  269. else
  270. {
  271. // If it was a plus, change that to a minus
  272. if(lastOperator == '+')
  273. {
  274. input->setText(input->text().replace(indexOfLastOperator, 1, "–"));
  275. }
  276. // If it was a minus, change that to a plus
  277. else if(lastOperator == '–')
  278. {
  279. input->setText(input->text().replace(indexOfLastOperator, 1, "+"));
  280. }
  281. // Otherwise, just negate the last number
  282. else
  283. {
  284. input->setText(input->text().insert(indexOfLastOperator+1, '-'));
  285. }
  286. }
  287. }
  288.  
  289. // TODO or parenthetical expression?
  290. // Case 2: No operators and already negative number
  291. else if(input->text().at(0) == '-')
  292. {
  293. input->setText(input->text().replace(0, 1, ""));
  294. }
  295.  
  296. // Case 3: No operators and positive number
  297. else
  298. {
  299. input->setText(input->text().prepend("-"));
  300. }
  301. }
  302.  
  303. /* Called when a user clicks any binary operation's button
  304. * (+, -, *, /). Inserts the appropriate operation into input.
  305. */
  306. void MainWindow::on_binary_button_released()
  307. {
  308. QPushButton *button = (QPushButton*)sender();
  309. State currentState = history.top();
  310.  
  311. if(currentState.operatorAllowed)
  312. {
  313. input->setText(input->text().append(button->text()));
  314. history.push(State(true, false, true, false,
  315. currentState.numOpenParenths,
  316. currentState.numClosingParenths));
  317. }
  318. }
  319.  
  320. /* Called when a user clicks the '=' button or uses the keyboard
  321. * shortcut 'Enter'. Checks if the input is ready for evaluation.
  322. * If so, emits input_is_ready signal, effectively handing its
  323. * input string to the Calculator for processing.
  324. */
  325. void MainWindow::on_buttonEquals_released()
  326. {
  327. State currentState = history.top();
  328. if(!operatorUsedDirectlyBefore() &&
  329. currentState.numOpenParenths == currentState.numClosingParenths)
  330. {
  331. emit input_is_ready(input->text());
  332. }
  333. }
  334.  
  335. /* Used to reset user input. Sets the input string to "0", clears its
  336. * history, and applies all initial input restrictions.
  337. */
  338. void MainWindow::reset()
  339. {
  340. input->setText("0");
  341. history.clear();
  342. history.push(State(true, true, false, false));
  343. }
  344.  
  345. /* Called when a user clicks the 'Clear' button or uses the keyboard
  346. * shortcut 'Delete'. Resets the input. See MainWindow::reset().
  347. */
  348. void MainWindow::on_buttonClear_released()
  349. {
  350. reset();
  351. }
  352.  
  353. /* Called when a user clicks the 'Back' button or uses the
  354. * keyboard shortcut 'Backspace'. Reverts the calculator to
  355. * its previous state of input. Reapplies all appropriate input
  356. * restrictions that existed prior to deletion of the last token.
  357. */
  358. void MainWindow::on_buttonBack_released()
  359. {
  360. // If the input is just a single digit, set it to 0
  361. if(input->text().length() == 1)
  362. {
  363. input->setText("0");
  364. }
  365.  
  366. // Otherwise, if we have 2 or more entries, remove last char
  367. else
  368. {
  369. QString text = input->text();
  370. input->setText(text.remove(text.length()-1, 1));
  371. }
  372.  
  373. if(history.size() >= 2){ history.pop();}
  374. }
  375.  
  376. void MainWindow::on_buttonRoot_released()
  377. {
  378. // TODO nth root
  379. }
  380.  
  381. /* Called when a user pushes the '(' button or uses the
  382. * keyboard shortcut SHIFT+9. Appends an opening parenthesis
  383. * to the input. Disables entry of closing parentheses
  384. * and operators directly afterwards.
  385. */
  386. void MainWindow::on_buttonOpenParenth_released()
  387. {
  388. if(history.top().openParenthAllowed)
  389. {
  390. input->setText(input->text().append("("));
  391. history.push(State(true, false, true, false,
  392. history.top().numOpenParenths+1,
  393. history.top().numClosingParenths));
  394. }
  395. }
  396.  
  397. /* Called when a user pushes the ')' button or uses the
  398. * keyboard shortcut SHIFT+0. Appends a closing parenthesis
  399. * to the input. Disables entry of opening parentheses and
  400. * digits directly afterwards.
  401. */
  402. void MainWindow::on_buttonCloseParenth_released()
  403. {
  404. State currentState = history.top();
  405. if(currentState.closingParenthAllowed &&
  406. (currentState.numClosingParenths < currentState.numOpenParenths))
  407. {
  408. input->setText(input->text().append(")"));
  409. history.push(State(false, true, false, true,
  410. history.top().numOpenParenths,
  411. history.top().numClosingParenths+1));
  412. }
  413. }
  414.  
  415. void MainWindow::on_buttonXSquared_released()
  416. {
  417. // TODO x squared
  418. }
  419.  
  420. void MainWindow::on_buttonSqrt_released()
  421. {
  422. // TODO square root
  423. }
  424.  
  425. Ui::MainWindow *ui;
  426. QLabel *input;
  427.  
  428. Ui::MainWindow* getUI() const { return ui; }
  429.  
  430. connect(ui->button0, SIGNAL(released()), this, SLOT(on_digit_released()));
  431. connect(ui->button1, SIGNAL(released()), this, SLOT(on_digit_released()));
  432. connect(ui->button2, SIGNAL(released()), this, SLOT(on_digit_released()));
  433. connect(ui->button3, SIGNAL(released()), this, SLOT(on_digit_released()));
  434. connect(ui->button4, SIGNAL(released()), this, SLOT(on_digit_released()));
  435. connect(ui->button5, SIGNAL(released()), this, SLOT(on_digit_released()));
  436. connect(ui->button6, SIGNAL(released()), this, SLOT(on_digit_released()));
  437. connect(ui->button7, SIGNAL(released()), this, SLOT(on_digit_released()));
  438. connect(ui->button8, SIGNAL(released()), this, SLOT(on_digit_released()));
  439. connect(ui->button9, SIGNAL(released()), this, SLOT(on_digit_released()));
  440.  
  441. for (auto&& button : container_of_buttons)
  442. {
  443. //...
  444. }
  445.  
  446. struct Input {
  447. bool isOperator;
  448. int val;
  449. OperatorType operator;
  450. }
  451.  
  452. enum class OperatorType {
  453. plus,
  454. minus,
  455. et al...
  456. }
Add Comment
Please, Sign In to add comment