summaryrefslogtreecommitdiff
path: root/noncore/tools/calculator/calculatorimpl.cpp
Unidiff
Diffstat (limited to 'noncore/tools/calculator/calculatorimpl.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/tools/calculator/calculatorimpl.cpp601
1 files changed, 601 insertions, 0 deletions
diff --git a/noncore/tools/calculator/calculatorimpl.cpp b/noncore/tools/calculator/calculatorimpl.cpp
new file mode 100644
index 0000000..2f7d7ce
--- a/dev/null
+++ b/noncore/tools/calculator/calculatorimpl.cpp
@@ -0,0 +1,601 @@
1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved.
3**
4** This file is part of Qtopia Environment.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#include "calculatorimpl.h"
22
23#include <qpe/resource.h>
24#include <qpe/qmath.h>
25#include <qpe/qpeapplication.h>
26
27#include <qpushbutton.h>
28#include <qcombobox.h>
29#include <qlabel.h>
30#include <qfont.h>
31#include <qlayout.h>
32#include <qstringlist.h>
33#include <qfile.h>
34#include <qtextstream.h>
35#include <qmessagebox.h>
36#include <math.h>
37
38CalculatorImpl::CalculatorImpl( QWidget * parent, const char * name,
39 WFlags f )
40 : Calculator( parent, name, f )
41{
42 xtopowerofy = Resource::loadPixmap("xtopowerofy");
43 ythrootofx = Resource::loadPixmap("ythrootofx");
44 oneoverx = Resource::loadPixmap("oneoverx");
45
46 memMark = new QLabel( "m", LCD );
47 memMark->setFont( QFont( "helvetica", 12, QFont::Bold, TRUE ) );
48 memMark->resize( 12, 12 );
49 memMark->move( 4, 2 );
50 memMark->hide();
51 mem = 0;
52
53 PushButtonMR->setEnabled( FALSE );
54
55 current_mode = max_mode = conversion_mode_count = 0;
56 last_conversion = -1;
57
58//bgr_command.insert( PushButtonFunction);
59 bgr_command.insert( PushButtonMPlus);
60 bgr_command.insert( PushButtonMR);
61 bgr_command.insert( PushButtonMC);
62 bgr_command.insert( PushButtonCE);
63 connect( &bgr_command, SIGNAL(clicked(int) ), this, SLOT(command_buttons(int)));
64
65 bgr_digits.insert(PushButton0);
66 bgr_digits.insert(PushButton1);
67 bgr_digits.insert(PushButton2);
68 bgr_digits.insert(PushButton3);
69 bgr_digits.insert(PushButton4);
70 bgr_digits.insert(PushButton5);
71 bgr_digits.insert(PushButton6);
72 bgr_digits.insert(PushButton7);
73 bgr_digits.insert(PushButton8);
74 bgr_digits.insert(PushButton9);
75 connect( &bgr_digits, SIGNAL(clicked(int) ), this, SLOT(enterNumber(int)));
76
77
78 bgr_std.insert(PushButtonEquals);
79 bgr_std.insert(PushButtonDecimal);
80 bgr_std.insert(PushButtonAdd);
81 bgr_std.insert(PushButtonMinus);
82 bgr_std.insert(PushButtonDivide);
83 bgr_std.insert(PushButtonTimes);
84 connect( &bgr_std, SIGNAL(clicked(int) ), this, SLOT(std_buttons(int)));
85
86// change the / to a proper division signal
87 PushButtonDivide->setText(QChar(0xF7));
88
89 func_buttons[0] = PushButtonF1;
90 func_buttons[1] = PushButtonF2;
91 func_buttons[2] = PushButtonF3;
92 func_buttons[3] = PushButtonF4;
93 func_buttons[4] = PushButtonF5;
94 func_buttons[5] = PushButtonF6;
95 func_buttons[6] = PushButtonF7;
96 func_buttons[7] = PushButtonF8;
97 func_buttons[8] = PushButtonF9;
98 func_buttons[9] = PushButtonF10;
99 func_buttons[10] = PushButtonF11;
100 func_buttons[11] = PushButtonF12;
101
102 for ( int x = 0 ; x < func_button_count ; x++ ) {
103 QPushButton* tmpbutton = func_buttons[x];
104 faces << tmpbutton->text();
105 bgr_function.insert(tmpbutton);
106 }
107 connect( &bgr_function, SIGNAL(clicked(int) ) , this, SLOT(do_convert(int) ) );
108 connect( &bgr_function, SIGNAL(clicked(int) ) , this, SLOT(std_funcs (int) ) );
109
110 connect(ComboBoxFunction, SIGNAL(activated(int) ), this, SLOT(function_button(int) ) );
111
112 captions.append("Standard");
113 ComboBoxFunction->insertItem(captions.last());
114
115 // now add in the conversion modes
116 // when the menu gets done, these should be in a submenu
117 QString tmp = QPEApplication::qpeDir();
118 tmp += "/etc/unit_conversion.dat";
119 QFile myfile(tmp);
120 if ( !myfile.open( IO_Translate | IO_ReadOnly ) ) {
121 // QMessageBox::warning(this, "Warning", "Data file\nunit_conversion.dat\nnot found\nNo conversion\nfeatures will\nbe available");
122 // disable the f button if no conv file available
123 ComboBoxFunction->setEnabled(FALSE);
124 }
125 else {
126 QString line, line2;
127 QTextStream ts(&myfile);
128
129 // first pass, see how many conversion types there are in order to allocate for them
130 while ( ! ts.eof() ) {
131 line = ts.readLine();
132 if ( line.contains ("STARTTYPE" ) )
133 conversion_mode_count++;
134 }
135
136 entry_list = new double[conversion_mode_count*func_button_count];
137
138 myfile.close();
139 myfile.open( IO_Translate | IO_ReadOnly );
140 QTextStream ts2(&myfile);
141
142 // second pass, read in values
143 int x = 0;
144 while ( ! ts2.eof() ) {
145 line = ts2.readLine();
146 if ( line.contains("STARTTYPE") ) {
147 captions << line.remove(0,10);
148 ComboBoxFunction->insertItem(captions.last());
149 while ( !line.contains("ENDTYPE") ) {
150 line = ts2.readLine();
151 if ( line.contains("NAME") ) {
152 faces << line.remove(0,5);
153 line2 = ts2.readLine();
154 line2.remove(0,6);
155 entry_list[x] = line2.toDouble();
156 x++;
157 }
158 }
159 }
160 }
161 }
162 myfile.close();
163 clear();
164 max_mode = pre_conv_modes_count + conversion_mode_count + post_conv_modes_count - 1;
165 display_pixmap_faces();
166
167 qApp->installEventFilter( this );
168}
169
170bool CalculatorImpl::eventFilter( QObject *o, QEvent *e )
171{
172 if ( e->type() == QEvent::KeyPress && state != sError ) {
173 QKeyEvent *k = (QKeyEvent*)e;
174 if ( k->key() >= Key_0 && k->key() <= Key_9 ) {
175 enterNumber( k->key() - Key_0 );
176 return true;
177 } else {
178 switch ( k->key() ) {
179 case Key_Equal:
180 std_buttons(0);
181 return true;
182 case Key_Period:
183 std_buttons(1);
184 return true;
185 case Key_Plus:
186 std_buttons(2);
187 return true;
188 case Key_Minus:
189 std_buttons(3);
190 return true;
191 case Key_Slash:
192 std_buttons(4);
193 return true;
194 case Key_Asterisk:
195 std_buttons(5);
196 return true;
197 case Key_Percent:
198 execOp( oPercent );
199 return true;
200 case Key_ParenLeft:
201 if ( current_mode < pre_conv_modes_count )
202 execOp( oOpenBrace );
203 return true;
204 case Key_ParenRight:
205 if ( current_mode < pre_conv_modes_count )
206 execOp( oCloseBrace );
207 return true;
208 default:
209 break;
210 }
211 }
212 }
213 return Calculator::eventFilter( o, e );
214}
215
216void CalculatorImpl::do_convert(int button) {
217 if ( state == sError )
218 return;
219 if ( current_mode >= pre_conv_modes_count && current_mode <= (max_mode - post_conv_modes_count) &&
220 button < changeable_func_button_count ) {
221 if ( last_conversion > -1 ) {
222 if( state == sNewNumber ){
223 acc = num
224 / (entry_list[(current_mode - pre_conv_modes_count) * func_button_count + last_conversion])
225 * (entry_list[(current_mode - pre_conv_modes_count) * func_button_count + button]) ;
226 num = acc;
227 LCD->display( acc );
228 } else {
229 state = sNewNumber;
230 num = num
231 / (entry_list[(current_mode - pre_conv_modes_count) * func_button_count + last_conversion])
232 * (entry_list[(current_mode - pre_conv_modes_count) * func_button_count + button]) ;
233 LCD->display( num );
234 acc = num;
235 }
236 }
237 last_conversion = button;
238 }
239}
240
241
242void CalculatorImpl::function_button(int mode){
243 if ( state == sError )
244 clear();
245 // dont need the next line when using a popup menu
246 current_mode = mode;
247
248 // reset the last conv
249 last_conversion = -1;
250
251 // set the caption
252 this->setCaption( captions[current_mode] );
253
254 reset_conv();
255
256 for ( int x = 0 ; x < changeable_func_button_count ; x++ ) {
257 QPushButton* tmpbutton = func_buttons[x];
258
259 // if its a conversion , make it a toggle button
260 if ( current_mode >= pre_conv_modes_count && current_mode <= (max_mode - post_conv_modes_count) ) {
261 tmpbutton->setToggleButton(TRUE);
262 } else {
263 tmpbutton->setToggleButton(FALSE);
264 }
265 tmpbutton->setText( faces[current_mode * func_button_count + x] );
266 }
267
268 if ( current_mode == 0 ) display_pixmap_faces();
269
270 if ( current_mode >= pre_conv_modes_count && current_mode <= (max_mode - post_conv_modes_count) ) {
271 bgr_function.setExclusive(TRUE);
272 } else {
273 bgr_function.setExclusive(FALSE);
274 }
275}
276
277void CalculatorImpl::display_pixmap_faces() {
278 QPushButton* tmpbutton = func_buttons[5];
279 tmpbutton->setPixmap(xtopowerofy);
280
281 tmpbutton = func_buttons[6];
282 tmpbutton->setPixmap(ythrootofx);
283
284 tmpbutton = func_buttons[3];
285 tmpbutton->setPixmap(oneoverx);
286}
287
288void CalculatorImpl::clear() {
289 acc = num = 0;
290 operationStack.clear();
291 state = sStart;
292 numDecimals = 0;
293 numOpenBraces = 0;
294 flPoint = FALSE;
295 LCD->display( 0 );
296 fake = QString::null;
297
298 reset_conv();
299}
300
301void CalculatorImpl::reset_conv() {
302 for ( int x = 0 ; x < changeable_func_button_count ; x++ ) {
303 QPushButton* tmpbutton = func_buttons[x];
304
305 // dont carry any selections into the next mode
306 if ( tmpbutton->state() == QPushButton::On ) {
307 tmpbutton->toggle();
308 }
309 }
310
311 last_conversion = -1;
312}
313
314void CalculatorImpl::std_buttons(int button)
315{
316 if ( state == sError )
317 return;
318 execOp( (Operation)(button + oSum) );
319}
320
321void CalculatorImpl::std_funcs(int button) {
322 if ( state == sError )
323 return;
324 if ( current_mode < pre_conv_modes_count ||
325 button > changeable_func_button_count-1 ) {
326 Operation op;
327 if ( button < 10 )
328 op = (Operation)(button + oSin);
329 else if ( button == 10 )
330 op = oOpenBrace;
331 else
332 op = oCloseBrace;
333 execOp( op );
334 }
335}
336
337void CalculatorImpl::execOp( Operation i )
338{
339 switch (i) {
340 // these operators only affect the current number.
341 case oDivX:
342 case oLog:
343 case oLn:
344 case oSin:
345 case oCos:
346 case oTan:
347 num = evalExpr(i);
348 break;
349
350 case oAdd:
351 case oSub: {
352 processStack( oAdd );
353 Op op( num, i );
354 operationStack.push( op );
355 break;
356 }
357 case oDiv:
358 case oMult:
359 case oRoot:
360 case oXsquared: {
361 processStack( oDiv );
362 Op op( num, i );
363 operationStack.push( op );
364 break;
365 }
366 case oChSign:
367 num = -num;
368 LCD->display(num);
369 return;
370
371 case oOpenBrace: {
372 Op op( 0, oOpenBrace );
373 operationStack.push( op );
374 numOpenBraces++;
375 state = sNewNumber;
376 return;
377 }
378 case oCloseBrace: {
379 if ( numOpenBraces == 0 )
380 return;
381 processStack( oAdd );
382 if ( operationStack.top().operation != oOpenBrace )
383 qDebug( "Calculator: internal Error" );
384 operationStack.pop();
385 state = sNewNumber;
386 numOpenBraces--;
387 break;
388 }
389
390 case oPoint:
391 flPoint = TRUE;
392 return;
393
394 case oPercent:
395 processStack( oPercent );
396 break;
397
398
399 case oSum:
400 processStack( oSum );
401 break;
402
403 default:
404 return;
405 };
406
407 if ( state == sError ) {
408 LCD->display( "Error" );
409 return;
410 } else {
411 LCD->display(num);
412 }
413 state = sNewNumber;
414 numDecimals = 0;
415 flPoint = FALSE;
416}
417
418
419void CalculatorImpl::processStack( int op )
420{
421 //dubious percent hack, since the changeable operator precedences are
422 //pretty much hardwired to be less than the non-changeable
423 bool percent = FALSE;
424 if ( op == oPercent ) {
425 percent = TRUE;
426 op = oSum;
427 }
428 while( !operationStack.isEmpty() && operationStack.top().operation >= op ) {
429 Op operation = operationStack.pop();
430 acc = operation.number;
431 if ( percent ) {
432 if ( operation.operation == oAdd || operation.operation == oSub )
433 num = acc*num/100;
434 else
435 num = num / 100;
436 }
437 num = evalExpr( operation.operation );
438 percent = FALSE;
439 }
440}
441
442
443double CalculatorImpl::evalExpr( int op ) {
444 double sum = 0;
445
446 switch( op ){
447 case oPercent: sum = num / 100.; break;
448 case oDivX:
449 if (num == 0)
450 state = sError;
451 else
452 sum = 1 / num;
453 break;
454 case oXsquared:
455 sum = pow(acc,num);
456 break;
457 case oChSign: (state == sStart) ? sum = -num : sum = -acc; break;
458 case oSub: sum = acc - num; break;
459 case oMult: sum = acc * num; break;
460 case oAdd: sum = acc + num; break;
461 case oDiv: {
462 if (num == 0) {
463 state = sError;
464 } else {
465 sum = acc / num;
466 }
467 break;
468 }
469 case oRoot:
470 /* the linux library is dumb, and can't to -x to 1/n
471 when n is odd. (even and error of course is acceptable */
472 if((acc < 0) && (int(num) == num) && (int(num) % 2 == 1 )) {
473 sum = pow(-acc, 1 / num);
474 sum = -sum;
475 } else {
476 sum = pow(acc, 1 / num);
477 }
478 break;
479 case oLog:
480 sum = log10(num);
481 break;
482 case oLn:
483 sum = log(num);
484 break;
485 case oTan: sum = qTan(num);break;
486 case oSin: sum = qSin(num);break;
487 case oCos: sum = qCos(num);break;
488 default: sum = num; break;
489 }
490
491 if ( isinf( sum ) || isnan( sum ) )
492 state = sError;
493 return sum;
494}
495
496
497void CalculatorImpl::enterNumber( int n )
498{
499 if ( state == sError )
500 return;
501 if( state == sStart ){
502 if( LCD->value() > 0 ){
503 QString s = QString::number( LCD->value(), 'g', LCD->numDigits());
504 if( s.length() > (uint)(LCD->numDigits() - 2)) return;
505
506 } else if( (int)fake.length() >= LCD->numDigits() || numDecimals >=12 ){
507 return;
508 }
509 }
510
511 if( state == sNewNumber ){
512 state = sStart;
513 acc = 0;
514 if( flPoint ){
515 numDecimals = 1;
516 num = n / pow(10, numDecimals);
517 } else
518 num = n;
519 } else if( flPoint ){
520 numDecimals++;
521 if( num < 0 ){
522 num -= n / pow(10, numDecimals);
523 } else {
524 num += n / pow(10, numDecimals);
525 }
526 } else {
527 num *= 10;
528 if( num < 0 )
529 num -= n;
530 else
531 num += n;
532 }
533
534 // We need feedback in the calc display while entering fl.point zeros.
535 // This is a small hack to display sequences like: 0.000 and 1.100
536 double integer, fraction;
537 fraction = modf( num, &integer );
538 if( flPoint ){
539 QString is, fs, zeros;
540
541 is = QString::number( integer, 'g', 13 );
542 fs = QString::number( fraction, 'g', numDecimals );
543 if( fs.contains('e') ){
544 fs = QString::number( fraction, 'f', LCD->numDigits() );
545 }
546 fs = fs.mid( 2, numDecimals );
547
548 if( (integer == 0) && (fraction == 0) )
549 fake = "0.";
550 else if( (integer != 0) && (fraction == 0) )
551 fake = is + ".";
552 else
553 fake = is + "." + fs;
554
555 zeros.fill( '0', (numDecimals - fs.length()) );
556 fake += zeros;
557 // ### This code sets LCD->value() to zero since it sets a text
558 // ### Avoid getting the current value from LCD->value() for
559 // ### calculations.
560 LCD->display( fake );
561 } else
562 LCD->display( num );
563}
564
565void CalculatorImpl::command_buttons(int i) {
566 if ( state == sError && i != 3 )
567 return;
568 switch (i) {
569 case 0: // M+
570 mem += num;
571 if( mem != 0 ){
572 memMark->show();
573 PushButtonMR->setEnabled( TRUE ); };
574 state = sNewNumber;
575 break;
576 case 1: // MR
577 acc = num = mem;
578 state = sNewNumber;
579 LCD->display( mem );
580 break;
581 case 2: // MC
582 mem = 0;
583 memMark->hide();
584 PushButtonMR->setEnabled( FALSE );
585 break;
586 case 3: // CE
587 if ( state == sStart ) {
588 // clear the entered number on the first press
589 state = sNewNumber;
590 num = acc = 0;
591 flPoint = FALSE;
592 LCD->display( 0 );
593 fake = QString::null;
594 numDecimals = 0;
595 } else {
596 clear();
597 }
598 break;
599 };
600
601}