00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "calculatorimpl.h"
00028
00029
00030 #include <opie2/odebug.h>
00031 #include <qpe/qmath.h>
00032 #include <qpe/qpeapplication.h>
00033 using namespace Opie::Core;
00034
00035
00036 #include <qpushbutton.h>
00037 #include <qcombobox.h>
00038 #include <qlabel.h>
00039 #include <qfont.h>
00040 #include <qlayout.h>
00041 #include <qstringlist.h>
00042 #include <qfile.h>
00043 #include <qtextstream.h>
00044 #include <qmessagebox.h>
00045
00046
00047 #include <math.h>
00048
00049
00050 static char *oneoverx_xpm[] = {
00051
00052 " 13 11 2 1",
00053
00054 ". c None",
00055 "# c #000000",
00056
00057 "......#......",
00058 ".....##......",
00059 "......#......"
00060 ".....###.....",
00061 ".............",
00062 "..#########..",
00063 ".............",
00064 "....##.##....",
00065 "......#......",
00066 "......#......",
00067 "....##.##....",
00068 };
00069
00070 static char *ythrootofx_xpm[] = {
00071
00072 " 13 11 2 1",
00073
00074 ". c None",
00075 "# c #000000",
00076
00077 "#.#..........",
00078 "#.#..........",
00079 "###...#######",
00080 "..#..#.......",
00081 "###..#.......",
00082 ".....#.#...#.",
00083 ".#..#...#.#..",
00084 "#.#.#....#...",
00085 "..#.#...#.#..",
00086 "...#...#...#.",
00087 "...#........."
00088 };
00089
00090 static char *xtopowerofy_xpm[] = {
00091
00092 " 9 8 2 1",
00093
00094 ". c None",
00095 "# c #000000",
00096
00097 "......#.#",
00098 "......#.#",
00099 "......###",
00100 "#...#...#",
00101 ".#.#..###",
00102 "..#......",
00103 ".#.#.....",
00104 "#...#...."
00105 };
00106
00107 CalculatorImpl::CalculatorImpl( QWidget * parent, const char * name,
00108 WFlags f )
00109 : Calculator( parent, name, f )
00110 {
00111 memMark = new QLabel( "m", LCD );
00112 memMark->setFont( QFont( "helvetica", 12, QFont::Bold, TRUE ) );
00113 memMark->resize( 12, 12 );
00114 memMark->move( 4, 2 );
00115 memMark->hide();
00116 mem = 0;
00117
00118 PushButtonMR->setEnabled( FALSE );
00119
00120 current_mode = max_mode = conversion_mode_count = 0;
00121 last_conversion = -1;
00122
00123
00124 QObject::tr("Standard");
00125 QObject::tr("Weight");
00126 QObject::tr("Distance");
00127 QObject::tr("Area");
00128 QObject::tr("Temperatures");
00129 QObject::tr("Volume");
00130 QObject::tr("acres");
00131 QObject::tr("C");
00132 QObject::tr("carats");
00133 QObject::tr("cm");
00134 QObject::tr("cu cm");
00135 QObject::tr("cu ft");
00136 QObject::tr("cu in");
00137 QObject::tr("F");
00138 QObject::tr("fl oz (US)");
00139 QObject::tr("ft");
00140 QObject::tr("g");
00141 QObject::tr("gal (US)");
00142 QObject::tr("hectares");
00143 QObject::tr("in");
00144 QObject::tr("kg");
00145 QObject::tr("km");
00146 QObject::tr("l");
00147 QObject::tr("lb");
00148 QObject::tr("Lg tons");
00149 QObject::tr("m");
00150 QObject::tr("mg");
00151 QObject::tr("mi");
00152 QObject::tr("ml");
00153 QObject::tr("mm");
00154 QObject::tr("naut. mi");
00155 QObject::tr("oz");
00156 QObject::tr("points");
00157 QObject::tr("pt");
00158 QObject::tr("qt");
00159 QObject::tr("sq cm");
00160 QObject::tr("sq ft");
00161 QObject::tr("sq in");
00162 QObject::tr("sq km");
00163 QObject::tr("sq m");
00164 QObject::tr("sq mi");
00165 QObject::tr("sq mm");
00166 QObject::tr("sq yd");
00167 QObject::tr("st");
00168 QObject::tr("St tons");
00169 QObject::tr("tblspoon");
00170 QObject::tr("teaspoons");
00171 QObject::tr("tonnes");
00172 QObject::tr("yd");
00173
00174
00175
00176 bgr_command.insert( PushButtonMPlus);
00177 bgr_command.insert( PushButtonMR);
00178 bgr_command.insert( PushButtonMC);
00179 bgr_command.insert( PushButtonCE);
00180 connect( &bgr_command, SIGNAL(clicked(int) ), this, SLOT(command_buttons(int)));
00181
00182 bgr_digits.insert(PushButton0);
00183 bgr_digits.insert(PushButton1);
00184 bgr_digits.insert(PushButton2);
00185 bgr_digits.insert(PushButton3);
00186 bgr_digits.insert(PushButton4);
00187 bgr_digits.insert(PushButton5);
00188 bgr_digits.insert(PushButton6);
00189 bgr_digits.insert(PushButton7);
00190 bgr_digits.insert(PushButton8);
00191 bgr_digits.insert(PushButton9);
00192 connect( &bgr_digits, SIGNAL(clicked(int) ), this, SLOT(enterNumber(int)));
00193
00194
00195 bgr_std.insert(PushButtonEquals);
00196 bgr_std.insert(PushButtonDecimal);
00197 bgr_std.insert(PushButtonAdd);
00198 bgr_std.insert(PushButtonMinus);
00199 bgr_std.insert(PushButtonDivide);
00200 bgr_std.insert(PushButtonTimes);
00201 connect( &bgr_std, SIGNAL(clicked(int) ), this, SLOT(std_buttons(int)));
00202
00203
00204 PushButtonDivide->setText(QChar(0xF7));
00205
00206 func_buttons[0] = PushButtonF1;
00207 func_buttons[1] = PushButtonF2;
00208 func_buttons[2] = PushButtonF3;
00209 func_buttons[3] = PushButtonF4;
00210 func_buttons[4] = PushButtonF5;
00211 func_buttons[5] = PushButtonF6;
00212 func_buttons[6] = PushButtonF7;
00213 func_buttons[7] = PushButtonF8;
00214 func_buttons[8] = PushButtonF9;
00215 func_buttons[9] = PushButtonF10;
00216 func_buttons[10] = PushButtonF11;
00217 func_buttons[11] = PushButtonF12;
00218
00219 for ( int x = 0 ; x < func_button_count ; x++ ) {
00220 QPushButton* tmpbutton = func_buttons[x];
00221 faces << tmpbutton->text();
00222 bgr_function.insert(tmpbutton);
00223 }
00224 connect( &bgr_function, SIGNAL(clicked(int) ) , this, SLOT(do_convert(int) ) );
00225 connect( &bgr_function, SIGNAL(clicked(int) ) , this, SLOT(std_funcs(int) ) );
00226
00227 connect(ComboBoxFunction, SIGNAL(activated(int) ), this, SLOT(function_button(int) ) );
00228
00229 captions.append(tr("Standard"));
00230 ComboBoxFunction->insertItem(captions.last());
00231
00232
00233
00234 QString tmp = QPEApplication::qpeDir();
00235 tmp += "etc/unit_conversion.dat";
00236 QFile myfile(tmp);
00237 if ( !myfile.open( IO_Translate | IO_ReadOnly ) ) {
00238 odebug << "Data file unit_conversion.dat not found\nNo conversion features will be available\n"+tmp << oendl;
00239
00240 ComboBoxFunction->setEnabled(FALSE);
00241 }
00242 else {
00243 QString line, line2;
00244 QTextStream ts(&myfile);
00245
00246
00247 while ( ! ts.eof() ) {
00248 line = ts.readLine();
00249 if ( line.contains ("STARTTYPE" ) )
00250 conversion_mode_count++;
00251 }
00252
00253 entry_list = new double[conversion_mode_count*func_button_count];
00254 preoffset_list = new double[conversion_mode_count*func_button_count];
00255 postoffset_list = new double[conversion_mode_count*func_button_count];
00256 myfile.close();
00257 myfile.open( IO_Translate | IO_ReadOnly );
00258 QTextStream ts2(&myfile);
00259
00260
00261 int x = 0;
00262 while ( ! ts2.eof() ) {
00263 line = ts2.readLine();
00264 if ( line.contains("STARTTYPE") ) {
00265 captions << tr( line.remove(0,10) );
00266 ComboBoxFunction->insertItem(captions.last());
00267 while ( !line.contains("ENDTYPE") ) {
00268 line = ts2.readLine();
00269 if ( line.contains("NAME") ) {
00270 faces << tr( line.remove(0,5) );
00271 line2 = ts2.readLine();
00272 line2.remove(0,6);
00273 entry_list[x] = line2.toDouble();
00274 line2 = ts2.readLine();
00275 line2.remove(0,7);
00276 preoffset_list[x] = line2.toDouble();
00277 line2 = ts2.readLine();
00278 line2.remove(0,8);
00279 postoffset_list[x] = line2.toDouble();
00280 x++;
00281 }
00282 }
00283 }
00284 }
00285 }
00286 myfile.close();
00287 clear();
00288 max_mode = pre_conv_modes_count + conversion_mode_count + post_conv_modes_count - 1;
00289 display_pixmap_faces();
00290
00291 qApp->installEventFilter( this );
00292 }
00293
00294 bool CalculatorImpl::eventFilter( QObject *o, QEvent *e )
00295 {
00296 if ( e->type() == QEvent::KeyPress && state != sError ) {
00297 QKeyEvent *k = (QKeyEvent*)e;
00298 if ( k->key() >= Key_0 && k->key() <= Key_9 ) {
00299 enterNumber( k->key() - Key_0 );
00300 return true;
00301 } else {
00302 switch ( k->key() ) {
00303 case Key_Equal:
00304 std_buttons(0);
00305 return true;
00306 case Key_Period:
00307 std_buttons(1);
00308 return true;
00309 case Key_Plus:
00310 std_buttons(2);
00311 return true;
00312 case Key_Minus:
00313 std_buttons(3);
00314 return true;
00315 case Key_Slash:
00316 std_buttons(4);
00317 return true;
00318 case Key_Asterisk:
00319 std_buttons(5);
00320 return true;
00321 case Key_Percent:
00322 execOp( oPercent );
00323 return true;
00324 case Key_ParenLeft:
00325 if ( current_mode < pre_conv_modes_count )
00326 execOp( oOpenBrace );
00327 return true;
00328 case Key_ParenRight:
00329 if ( current_mode < pre_conv_modes_count )
00330 execOp( oCloseBrace );
00331 return true;
00332 default:
00333 break;
00334 }
00335 }
00336 }
00337 return Calculator::eventFilter( o, e );
00338 }
00339
00340 void CalculatorImpl::do_convert(int button) {
00341 if ( state == sError )
00342 return;
00343 if ( current_mode >= pre_conv_modes_count && current_mode <= (max_mode - post_conv_modes_count) &&
00344 button < changeable_func_button_count ) {
00345 if ( last_conversion > -1 ) {
00346 if( state == sNewNumber ){
00347 acc = (num+ preoffset_list[(current_mode - pre_conv_modes_count) * func_button_count + last_conversion])
00348 / (entry_list[(current_mode - pre_conv_modes_count) * func_button_count + last_conversion])
00349 * (entry_list[(current_mode - pre_conv_modes_count) * func_button_count + button])
00350 +postoffset_list[(current_mode - pre_conv_modes_count) * func_button_count + button];
00351 num = acc;
00352 LCD->display( acc );
00353 } else {
00354 state = sNewNumber;
00355 num = (num+ preoffset_list[(current_mode - pre_conv_modes_count) * func_button_count + last_conversion])
00356 / (entry_list[(current_mode - pre_conv_modes_count) * func_button_count + last_conversion])
00357 * (entry_list[(current_mode - pre_conv_modes_count) * func_button_count + button])
00358 + postoffset_list[(current_mode - pre_conv_modes_count) * func_button_count + button];;
00359 LCD->display( num );
00360 acc = num;
00361 }
00362 }
00363 last_conversion = button;
00364 }
00365 }
00366
00367
00368 void CalculatorImpl::function_button(int mode){
00369 if ( state == sError )
00370 clear();
00371
00372 current_mode = mode;
00373
00374
00375 last_conversion = -1;
00376
00377
00378 this->setCaption( captions[current_mode] );
00379
00380 reset_conv();
00381
00382 for ( int x = 0 ; x < changeable_func_button_count ; x++ ) {
00383 QPushButton* tmpbutton = func_buttons[x];
00384
00385
00386 if ( current_mode >= pre_conv_modes_count && current_mode <= (max_mode - post_conv_modes_count) ) {
00387 tmpbutton->setToggleButton(TRUE);
00388 } else {
00389 tmpbutton->setToggleButton(FALSE);
00390 }
00391 tmpbutton->setText( faces[current_mode * func_button_count + x] );
00392 }
00393
00394 if ( current_mode == 0 ) display_pixmap_faces();
00395
00396 if ( current_mode >= pre_conv_modes_count && current_mode <= (max_mode - post_conv_modes_count) ) {
00397 bgr_function.setExclusive(TRUE);
00398 } else {
00399 bgr_function.setExclusive(FALSE);
00400 }
00401 }
00402
00403 void CalculatorImpl::display_pixmap_faces() {
00404 QPixmap image0( ( const char** ) xtopowerofy_xpm);
00405 QPushButton* tmpbutton = func_buttons[5];
00406 tmpbutton->setPixmap(image0);
00407
00408 QPixmap image1( ( const char** ) ythrootofx_xpm);
00409 tmpbutton = func_buttons[6];
00410 tmpbutton->setPixmap(image1);
00411
00412 QPixmap image2( ( const char** ) oneoverx_xpm);
00413 tmpbutton = func_buttons[3];
00414 tmpbutton->setPixmap(image2);
00415 }
00416
00417 void CalculatorImpl::clear() {
00418 acc = num = 0;
00419 operationStack.clear();
00420 state = sStart;
00421 numDecimals = 0;
00422 numOpenBraces = 0;
00423 flPoint = FALSE;
00424 LCD->display( 0 );
00425 fake = QString::null;
00426
00427 reset_conv();
00428 }
00429
00430 void CalculatorImpl::reset_conv() {
00431 for ( int x = 0 ; x < changeable_func_button_count ; x++ ) {
00432 QPushButton* tmpbutton = func_buttons[x];
00433
00434
00435 if ( tmpbutton->state() == QPushButton::On ) {
00436 tmpbutton->toggle();
00437 }
00438 }
00439
00440 last_conversion = -1;
00441 }
00442
00443 void CalculatorImpl::std_buttons(int button)
00444 {
00445 if ( state == sError )
00446 return;
00447 execOp( (Operation)(button + oSum) );
00448 }
00449
00450 void CalculatorImpl::std_funcs(int button) {
00451 if ( state == sError )
00452 return;
00453 if ( current_mode < pre_conv_modes_count ||
00454 button > changeable_func_button_count-1 ) {
00455 Operation op;
00456 if ( button < 10 )
00457 op = (Operation)(button + oSin);
00458 else if ( button == 10 )
00459 op = oOpenBrace;
00460 else
00461 op = oCloseBrace;
00462 execOp( op );
00463 }
00464 }
00465
00466 void CalculatorImpl::execOp( Operation i )
00467 {
00468 switch (i) {
00469
00470 case oDivX:
00471 case oLog:
00472 case oLn:
00473 case oSin:
00474 case oCos:
00475 case oTan:
00476 num = evalExpr(i);
00477 break;
00478
00479 case oAdd:
00480 case oSub: {
00481 processStack( oAdd );
00482 Op op( num, i );
00483 operationStack.push( op );
00484 break;
00485 }
00486 case oDiv:
00487 case oMult:
00488 case oRoot:
00489 case oXsquared: {
00490 processStack( oDiv );
00491 Op op( num, i );
00492 operationStack.push( op );
00493 break;
00494 }
00495 case oChSign:
00496 num = -num;
00497 LCD->display(num);
00498 return;
00499
00500 case oOpenBrace: {
00501 Op op( 0, oOpenBrace );
00502 operationStack.push( op );
00503 numOpenBraces++;
00504 state = sNewNumber;
00505 return;
00506 }
00507 case oCloseBrace: {
00508 if ( numOpenBraces == 0 )
00509 return;
00510 processStack( oAdd );
00511 if ( operationStack.top().operation != oOpenBrace )
00512 odebug << "Calculator: internal Error" << oendl;
00513 operationStack.pop();
00514 state = sNewNumber;
00515 numOpenBraces--;
00516 break;
00517 }
00518
00519 case oPoint:
00520 flPoint = TRUE;
00521 return;
00522
00523 case oPercent:
00524 processStack( oPercent );
00525 break;
00526
00527
00528 case oSum:
00529 processStack( oSum );
00530 break;
00531
00532 default:
00533 return;
00534 };
00535
00536 if ( state == sError ) {
00537 LCD->display( "Error" );
00538 return;
00539 } else {
00540 LCD->display(num);
00541 }
00542 state = sNewNumber;
00543 numDecimals = 0;
00544 flPoint = FALSE;
00545 }
00546
00547
00548 void CalculatorImpl::processStack( int op )
00549 {
00550
00551
00552 bool percent = FALSE;
00553 if ( op == oPercent ) {
00554 percent = TRUE;
00555 op = oSum;
00556 }
00557 while( !operationStack.isEmpty() && operationStack.top().operation >= op ) {
00558 Op operation = operationStack.pop();
00559 acc = operation.number;
00560 if ( percent ) {
00561 if ( operation.operation == oAdd || operation.operation == oSub )
00562 num = acc*num/100;
00563 else
00564 num = num / 100;
00565 }
00566 num = evalExpr( operation.operation );
00567 percent = FALSE;
00568 }
00569 }
00570
00571
00572 double CalculatorImpl::evalExpr( int op ) {
00573 double sum = 0;
00574
00575 switch( op ){
00576 case oPercent: sum = num / 100.; break;
00577 case oDivX:
00578 if (num == 0)
00579 state = sError;
00580 else
00581 sum = 1 / num;
00582 break;
00583 case oXsquared:
00584 sum = pow(acc,num);
00585 break;
00586 case oChSign: (state == sStart) ? sum = -num : sum = -acc; break;
00587 case oSub: sum = acc - num; break;
00588 case oMult: sum = acc * num; break;
00589 case oAdd: sum = acc + num; break;
00590 case oDiv: {
00591 if (num == 0) {
00592 state = sError;
00593 } else {
00594 sum = acc / num;
00595 }
00596 break;
00597 }
00598 case oRoot:
00599
00600
00601 if((acc < 0) && (int(num) == num) && (int(num) % 2 == 1 )) {
00602 sum = pow(-acc, 1 / num);
00603 sum = -sum;
00604 } else {
00605 sum = pow(acc, 1 / num);
00606 }
00607 break;
00608 case oLog:
00609 sum = log10(num);
00610 break;
00611 case oLn:
00612 sum = log(num);
00613 break;
00614 case oTan: sum = qTan(num);break;
00615 case oSin: sum = qSin(num);break;
00616 case oCos: sum = qCos(num);break;
00617 default: sum = num; break;
00618 }
00619
00620 if ( isinf( sum ) || isnan( sum ) )
00621 state = sError;
00622 return sum;
00623 }
00624
00625
00626 void CalculatorImpl::enterNumber( int n )
00627 {
00628 if ( state == sError )
00629 return;
00630 if( state == sStart ){
00631 if( LCD->value() > 0 ){
00632 QString s = QString::number( LCD->value(), 'g', LCD->numDigits());
00633 if( s.length() > (uint)(LCD->numDigits() - 2)) return;
00634
00635 } else if( (int)fake.length() >= LCD->numDigits() || numDecimals >=12 ){
00636 return;
00637 }
00638 }
00639
00640 if( state == sNewNumber ){
00641 state = sStart;
00642 acc = 0;
00643 if( flPoint ){
00644 numDecimals = 1;
00645 num = n / pow(10, numDecimals);
00646 } else
00647 num = n;
00648 } else if( flPoint ){
00649 numDecimals++;
00650 if( num < 0 ){
00651 num -= n / pow(10, numDecimals);
00652 } else {
00653 num += n / pow(10, numDecimals);
00654 }
00655 } else {
00656 num *= 10;
00657 if( num < 0 )
00658 num -= n;
00659 else
00660 num += n;
00661 }
00662
00663
00664
00665 double integer, fraction;
00666 fraction = modf( num, &integer );
00667 if( flPoint ){
00668 QString is, fs, zeros;
00669
00670 is = QString::number( integer, 'g', 13 );
00671 fs = QString::number( fraction, 'g', numDecimals );
00672 if( fs.contains('e') ){
00673 fs = QString::number( fraction, 'f', LCD->numDigits() );
00674 }
00675 fs = fs.mid( 2, numDecimals );
00676
00677 if( (integer == 0) && (fraction == 0) )
00678 fake = "0.";
00679 else if( (integer != 0) && (fraction == 0) )
00680 fake = is + ".";
00681 else
00682 fake = is + "." + fs;
00683
00684 zeros.fill( '0', (numDecimals - fs.length()) );
00685 fake += zeros;
00686
00687
00688
00689 LCD->display( fake );
00690 } else
00691 LCD->display( num );
00692 }
00693
00694 void CalculatorImpl::command_buttons(int i) {
00695 if ( state == sError && i != 3 )
00696 return;
00697 switch (i) {
00698 case 0:
00699 mem += num;
00700 if( mem != 0 ){
00701 memMark->show();
00702 PushButtonMR->setEnabled( TRUE ); };
00703 state = sNewNumber;
00704 break;
00705 case 1:
00706 acc = num = mem;
00707 state = sNewNumber;
00708 LCD->display( mem );
00709 break;
00710 case 2:
00711 mem = 0;
00712 memMark->hide();
00713 PushButtonMR->setEnabled( FALSE );
00714 break;
00715 case 3:
00716 if ( state == sStart ) {
00717
00718 state = sNewNumber;
00719 num = acc = 0;
00720 flPoint = FALSE;
00721 LCD->display( 0 );
00722 fake = QString::null;
00723 numDecimals = 0;
00724 } else {
00725 clear();
00726 }
00727 break;
00728 };
00729
00730 }