1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the examples of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include "calculator.h" |
52 | #include "button.h" |
53 | |
54 | #include <QGridLayout> |
55 | #include <QLineEdit> |
56 | #include <QtMath> |
57 | |
58 | //! [0] |
59 | Calculator::Calculator(QWidget *parent) |
60 | : QWidget(parent), sumInMemory(0.0), sumSoFar(0.0) |
61 | , factorSoFar(0.0), waitingForOperand(true) |
62 | { |
63 | //! [0] |
64 | |
65 | //! [1] |
66 | display = new QLineEdit("0" ); |
67 | //! [1] //! [2] |
68 | display->setReadOnly(true); |
69 | display->setAlignment(Qt::AlignRight); |
70 | display->setMaxLength(15); |
71 | |
72 | QFont font = display->font(); |
73 | font.setPointSize(font.pointSize() + 8); |
74 | display->setFont(font); |
75 | //! [2] |
76 | |
77 | //! [4] |
78 | for (int i = 0; i < NumDigitButtons; ++i) |
79 | digitButtons[i] = createButton(text: QString::number(i), SLOT(digitClicked())); |
80 | |
81 | Button *pointButton = createButton(text: tr(s: "." ), SLOT(pointClicked())); |
82 | Button *changeSignButton = createButton(text: tr(s: "\302\261" ), SLOT(changeSignClicked())); |
83 | |
84 | Button *backspaceButton = createButton(text: tr(s: "Backspace" ), SLOT(backspaceClicked())); |
85 | Button *clearButton = createButton(text: tr(s: "Clear" ), SLOT(clear())); |
86 | Button *clearAllButton = createButton(text: tr(s: "Clear All" ), SLOT(clearAll())); |
87 | |
88 | Button *clearMemoryButton = createButton(text: tr(s: "MC" ), SLOT(clearMemory())); |
89 | Button *readMemoryButton = createButton(text: tr(s: "MR" ), SLOT(readMemory())); |
90 | Button *setMemoryButton = createButton(text: tr(s: "MS" ), SLOT(setMemory())); |
91 | Button *addToMemoryButton = createButton(text: tr(s: "M+" ), SLOT(addToMemory())); |
92 | |
93 | Button *divisionButton = createButton(text: tr(s: "\303\267" ), SLOT(multiplicativeOperatorClicked())); |
94 | Button *timesButton = createButton(text: tr(s: "\303\227" ), SLOT(multiplicativeOperatorClicked())); |
95 | Button *minusButton = createButton(text: tr(s: "-" ), SLOT(additiveOperatorClicked())); |
96 | Button *plusButton = createButton(text: tr(s: "+" ), SLOT(additiveOperatorClicked())); |
97 | |
98 | Button *squareRootButton = createButton(text: tr(s: "Sqrt" ), SLOT(unaryOperatorClicked())); |
99 | Button *powerButton = createButton(text: tr(s: "x\302\262" ), SLOT(unaryOperatorClicked())); |
100 | Button *reciprocalButton = createButton(text: tr(s: "1/x" ), SLOT(unaryOperatorClicked())); |
101 | Button *equalButton = createButton(text: tr(s: "=" ), SLOT(equalClicked())); |
102 | //! [4] |
103 | |
104 | //! [5] |
105 | QGridLayout *mainLayout = new QGridLayout; |
106 | //! [5] //! [6] |
107 | mainLayout->setSizeConstraint(QLayout::SetFixedSize); |
108 | mainLayout->addWidget(display, row: 0, column: 0, rowSpan: 1, columnSpan: 6); |
109 | mainLayout->addWidget(backspaceButton, row: 1, column: 0, rowSpan: 1, columnSpan: 2); |
110 | mainLayout->addWidget(clearButton, row: 1, column: 2, rowSpan: 1, columnSpan: 2); |
111 | mainLayout->addWidget(clearAllButton, row: 1, column: 4, rowSpan: 1, columnSpan: 2); |
112 | |
113 | mainLayout->addWidget(clearMemoryButton, row: 2, column: 0); |
114 | mainLayout->addWidget(readMemoryButton, row: 3, column: 0); |
115 | mainLayout->addWidget(setMemoryButton, row: 4, column: 0); |
116 | mainLayout->addWidget(addToMemoryButton, row: 5, column: 0); |
117 | |
118 | for (int i = 1; i < NumDigitButtons; ++i) { |
119 | int row = ((9 - i) / 3) + 2; |
120 | int column = ((i - 1) % 3) + 1; |
121 | mainLayout->addWidget(digitButtons[i], row, column); |
122 | } |
123 | |
124 | mainLayout->addWidget(digitButtons[0], row: 5, column: 1); |
125 | mainLayout->addWidget(pointButton, row: 5, column: 2); |
126 | mainLayout->addWidget(changeSignButton, row: 5, column: 3); |
127 | |
128 | mainLayout->addWidget(divisionButton, row: 2, column: 4); |
129 | mainLayout->addWidget(timesButton, row: 3, column: 4); |
130 | mainLayout->addWidget(minusButton, row: 4, column: 4); |
131 | mainLayout->addWidget(plusButton, row: 5, column: 4); |
132 | |
133 | mainLayout->addWidget(squareRootButton, row: 2, column: 5); |
134 | mainLayout->addWidget(powerButton, row: 3, column: 5); |
135 | mainLayout->addWidget(reciprocalButton, row: 4, column: 5); |
136 | mainLayout->addWidget(equalButton, row: 5, column: 5); |
137 | setLayout(mainLayout); |
138 | |
139 | setWindowTitle(tr(s: "Calculator" )); |
140 | } |
141 | //! [6] |
142 | |
143 | //! [7] |
144 | void Calculator::digitClicked() |
145 | { |
146 | Button *clickedButton = qobject_cast<Button *>(object: sender()); |
147 | int digitValue = clickedButton->text().toInt(); |
148 | if (display->text() == "0" && digitValue == 0.0) |
149 | return; |
150 | |
151 | if (waitingForOperand) { |
152 | display->clear(); |
153 | waitingForOperand = false; |
154 | } |
155 | display->setText(display->text() + QString::number(digitValue)); |
156 | } |
157 | //! [7] |
158 | |
159 | //! [8] |
160 | void Calculator::unaryOperatorClicked() |
161 | //! [8] //! [9] |
162 | { |
163 | Button *clickedButton = qobject_cast<Button *>(object: sender()); |
164 | QString clickedOperator = clickedButton->text(); |
165 | double operand = display->text().toDouble(); |
166 | double result = 0.0; |
167 | |
168 | if (clickedOperator == tr(s: "Sqrt" )) { |
169 | if (operand < 0.0) { |
170 | abortOperation(); |
171 | return; |
172 | } |
173 | result = std::sqrt(x: operand); |
174 | } else if (clickedOperator == tr(s: "x\302\262" )) { |
175 | result = std::pow(x: operand, y: 2.0); |
176 | } else if (clickedOperator == tr(s: "1/x" )) { |
177 | if (operand == 0.0) { |
178 | abortOperation(); |
179 | return; |
180 | } |
181 | result = 1.0 / operand; |
182 | } |
183 | display->setText(QString::number(result)); |
184 | waitingForOperand = true; |
185 | } |
186 | //! [9] |
187 | |
188 | //! [10] |
189 | void Calculator::additiveOperatorClicked() |
190 | //! [10] //! [11] |
191 | { |
192 | Button *clickedButton = qobject_cast<Button *>(object: sender()); |
193 | if (!clickedButton) |
194 | return; |
195 | QString clickedOperator = clickedButton->text(); |
196 | double operand = display->text().toDouble(); |
197 | |
198 | //! [11] //! [12] |
199 | if (!pendingMultiplicativeOperator.isEmpty()) { |
200 | //! [12] //! [13] |
201 | if (!calculate(rightOperand: operand, pendingOperator: pendingMultiplicativeOperator)) { |
202 | abortOperation(); |
203 | return; |
204 | } |
205 | display->setText(QString::number(factorSoFar)); |
206 | operand = factorSoFar; |
207 | factorSoFar = 0.0; |
208 | pendingMultiplicativeOperator.clear(); |
209 | } |
210 | |
211 | //! [13] //! [14] |
212 | if (!pendingAdditiveOperator.isEmpty()) { |
213 | //! [14] //! [15] |
214 | if (!calculate(rightOperand: operand, pendingOperator: pendingAdditiveOperator)) { |
215 | abortOperation(); |
216 | return; |
217 | } |
218 | display->setText(QString::number(sumSoFar)); |
219 | } else { |
220 | sumSoFar = operand; |
221 | } |
222 | |
223 | //! [15] //! [16] |
224 | pendingAdditiveOperator = clickedOperator; |
225 | //! [16] //! [17] |
226 | waitingForOperand = true; |
227 | } |
228 | //! [17] |
229 | |
230 | //! [18] |
231 | void Calculator::multiplicativeOperatorClicked() |
232 | { |
233 | Button *clickedButton = qobject_cast<Button *>(object: sender()); |
234 | if (!clickedButton) |
235 | return; |
236 | QString clickedOperator = clickedButton->text(); |
237 | double operand = display->text().toDouble(); |
238 | |
239 | if (!pendingMultiplicativeOperator.isEmpty()) { |
240 | if (!calculate(rightOperand: operand, pendingOperator: pendingMultiplicativeOperator)) { |
241 | abortOperation(); |
242 | return; |
243 | } |
244 | display->setText(QString::number(factorSoFar)); |
245 | } else { |
246 | factorSoFar = operand; |
247 | } |
248 | |
249 | pendingMultiplicativeOperator = clickedOperator; |
250 | waitingForOperand = true; |
251 | } |
252 | //! [18] |
253 | |
254 | //! [20] |
255 | void Calculator::equalClicked() |
256 | { |
257 | double operand = display->text().toDouble(); |
258 | |
259 | if (!pendingMultiplicativeOperator.isEmpty()) { |
260 | if (!calculate(rightOperand: operand, pendingOperator: pendingMultiplicativeOperator)) { |
261 | abortOperation(); |
262 | return; |
263 | } |
264 | operand = factorSoFar; |
265 | factorSoFar = 0.0; |
266 | pendingMultiplicativeOperator.clear(); |
267 | } |
268 | if (!pendingAdditiveOperator.isEmpty()) { |
269 | if (!calculate(rightOperand: operand, pendingOperator: pendingAdditiveOperator)) { |
270 | abortOperation(); |
271 | return; |
272 | } |
273 | pendingAdditiveOperator.clear(); |
274 | } else { |
275 | sumSoFar = operand; |
276 | } |
277 | |
278 | display->setText(QString::number(sumSoFar)); |
279 | sumSoFar = 0.0; |
280 | waitingForOperand = true; |
281 | } |
282 | //! [20] |
283 | |
284 | //! [22] |
285 | void Calculator::pointClicked() |
286 | { |
287 | if (waitingForOperand) |
288 | display->setText("0" ); |
289 | if (!display->text().contains(c: '.')) |
290 | display->setText(display->text() + tr(s: "." )); |
291 | waitingForOperand = false; |
292 | } |
293 | //! [22] |
294 | |
295 | //! [24] |
296 | void Calculator::changeSignClicked() |
297 | { |
298 | QString text = display->text(); |
299 | double value = text.toDouble(); |
300 | |
301 | if (value > 0.0) { |
302 | text.prepend(s: tr(s: "-" )); |
303 | } else if (value < 0.0) { |
304 | text.remove(i: 0, len: 1); |
305 | } |
306 | display->setText(text); |
307 | } |
308 | //! [24] |
309 | |
310 | //! [26] |
311 | void Calculator::backspaceClicked() |
312 | { |
313 | if (waitingForOperand) |
314 | return; |
315 | |
316 | QString text = display->text(); |
317 | text.chop(n: 1); |
318 | if (text.isEmpty()) { |
319 | text = "0" ; |
320 | waitingForOperand = true; |
321 | } |
322 | display->setText(text); |
323 | } |
324 | //! [26] |
325 | |
326 | //! [28] |
327 | void Calculator::clear() |
328 | { |
329 | if (waitingForOperand) |
330 | return; |
331 | |
332 | display->setText("0" ); |
333 | waitingForOperand = true; |
334 | } |
335 | //! [28] |
336 | |
337 | //! [30] |
338 | void Calculator::clearAll() |
339 | { |
340 | sumSoFar = 0.0; |
341 | factorSoFar = 0.0; |
342 | pendingAdditiveOperator.clear(); |
343 | pendingMultiplicativeOperator.clear(); |
344 | display->setText("0" ); |
345 | waitingForOperand = true; |
346 | } |
347 | //! [30] |
348 | |
349 | //! [32] |
350 | void Calculator::clearMemory() |
351 | { |
352 | sumInMemory = 0.0; |
353 | } |
354 | |
355 | void Calculator::readMemory() |
356 | { |
357 | display->setText(QString::number(sumInMemory)); |
358 | waitingForOperand = true; |
359 | } |
360 | |
361 | void Calculator::setMemory() |
362 | { |
363 | equalClicked(); |
364 | sumInMemory = display->text().toDouble(); |
365 | } |
366 | |
367 | void Calculator::addToMemory() |
368 | { |
369 | equalClicked(); |
370 | sumInMemory += display->text().toDouble(); |
371 | } |
372 | //! [32] |
373 | //! [34] |
374 | Button *Calculator::createButton(const QString &text, const char *member) |
375 | { |
376 | Button *button = new Button(text); |
377 | connect(sender: button, SIGNAL(clicked()), receiver: this, member); |
378 | return button; |
379 | } |
380 | //! [34] |
381 | |
382 | //! [36] |
383 | void Calculator::abortOperation() |
384 | { |
385 | clearAll(); |
386 | display->setText(tr(s: "####" )); |
387 | } |
388 | //! [36] |
389 | |
390 | //! [38] |
391 | bool Calculator::calculate(double rightOperand, const QString &pendingOperator) |
392 | { |
393 | if (pendingOperator == tr(s: "+" )) { |
394 | sumSoFar += rightOperand; |
395 | } else if (pendingOperator == tr(s: "-" )) { |
396 | sumSoFar -= rightOperand; |
397 | } else if (pendingOperator == tr(s: "\303\227" )) { |
398 | factorSoFar *= rightOperand; |
399 | } else if (pendingOperator == tr(s: "\303\267" )) { |
400 | if (rightOperand == 0.0) |
401 | return false; |
402 | factorSoFar /= rightOperand; |
403 | } |
404 | return true; |
405 | } |
406 | //! [38] |
407 | |