Ticket #5203: deskcalc.patch
File deskcalc.patch, 9.9 KB (added by , 14 years ago) |
---|
-
src/apps/deskcalc/CalcView.cpp
60 60 61 61 // default calculator key pad layout 62 62 const char *kDefaultKeypadDescription = 63 "7 8 9 ( )\n"64 "4 5 6 * /\n"65 "1 2 3 + -\n"66 "0 . BS = C\n";63 "7 8 9 ( ) \n" 64 "4 5 6 * / \n" 65 "1 2 3 + - \n" 66 "0 . BS = C \n"; 67 67 68 68 69 69 enum { … … 204 204 CalcView::AttachedToWindow() 205 205 { 206 206 if (be_control_look == NULL) 207 SetFont(be_ bold_font);207 SetFont(be_fixed_font); 208 208 209 209 BRect frame(Frame()); 210 210 FrameResized(frame.Width(), frame.Height()); … … 579 579 void 580 580 CalcView::KeyDown(const char* bytes, int32 numBytes) 581 581 { 582 582 // if single byte character... 583 583 if (numBytes == 1) { 584 584 585 585 //printf("Key pressed: %c\n", bytes[0]); … … 668 668 669 669 // configure expression text view font size and color 670 670 float sizeDisp = fShowKeypad ? fHeight * kDisplayScaleY : fHeight; 671 BFont font(be_bold_font); 671 BFont font(be_fixed_font); 672 font.SetSpacing(B_FIXED_SPACING); 672 673 font.SetSize(sizeDisp * kExpressionFontScaleY); 673 674 fExpressionTextView->SetFontAndColor(&font, B_FONT_ALL); 674 675 … … 721 722 void 722 723 CalcView::Cut() 723 724 { 724 Copy(); 725 Copy(); // copy data to clipboard 725 726 fExpressionTextView->Clear(); // remove data 726 727 } 727 728 … … 903 904 904 905 _AudioFeedback(false); 905 906 907 ExpressionParser parser; 908 double value = 0.0; 909 BString result; 910 906 911 // evaluate expression 907 BString value;908 909 912 try { 910 ExpressionParser parser; 911 value = parser.Evaluate(expression.String()); 913 value = parser.EvaluateToDouble(expression.String()); 912 914 } catch (ParseException e) { 913 BString error(e.message.String()); 914 error << " at " << (e.position + 1); 915 fExpressionTextView->SetText(error.String()); 915 result.SetTo("error"); // just print error 916 fExpressionTextView->SetExpression(result.String()); 916 917 return; 917 918 } 918 919 919 // render new result to display 920 fExpressionTextView->SetExpression(value.String()); 920 // special case for value = 0 921 if (value == 0) { 922 result.SetTo("0"); 923 fExpressionTextView->SetExpression(result.String()); 924 return; 925 } 926 927 // set the number of places that will fit in the display based on 928 // the expressionWidth (92px to 392px) and fontWidth (7px to 29px) 929 double expressionWidth = fExpressionTextView->Frame().Width(); 930 BFont font; 931 uint32 sameProperties; 932 fExpressionTextView->GetFontAndColor(&font, &sameProperties); 933 double fontWidth = font.StringWidth("0"); // Any 1 char string will do 934 // subtract 1 for the decimal point and 1 to leave space at the end 935 int32 places = (int32)((expressionWidth / fontWidth) - 2); 936 937 if (value < 0) 938 places--; // subtract 1 place to fit the negative sign 939 if (places < 0) 940 places = 0; // minimum of 0 decimal places 941 942 // Standard Notation 943 944 result = parser.EvaluateToStandard(expression.String(), places); 945 946 // If the result is too large then converting to scientific notation 947 // will hopefully make it small enough to fit in the window by sacrificing 948 // decimal digits of precision. I cannot garentee that even with 0 decimal 949 // places the result will fit because the exponent can make the result 950 // too large to fit in the window. 951 // 952 // The result can also get cutoff if places is too small to fit the result. 953 // For example 1E-10 (0.000000000001) requires 13 places so if places 954 // is 12 or less than the result will be 0. By converting the result 955 // to a double and comparing with the originally calculated result I can 956 // detect these errors and convert to scientific notation which will 957 // hopefully make the result fit by sacrificing precision after the 958 // decimal point. 959 960 int32 resultlen = result.Length(); 961 if (result.FindFirst('.') == B_ERROR) 962 resultlen--; // subtract one for the lack of a decimal point 963 if (result.FindFirst('-') != B_ERROR) 964 resultlen--; // subtract one for the negative sign 965 966 // If result is too large to fit or got cutoff then convert to 967 // scientific notation. 968 if ((resultlen > places) || (fabs(atof(result)) < fabs(value))) { 969 // Calculate the number of digits in the exponent 970 int32 expdigits = 1; 971 double tmpvalue = fabs(value); 972 while ((tmpvalue /= 10.0) > 1.0) 973 expdigits++; 974 expdigits = (int32)(ceil(log10(expdigits))); 975 expdigits += 1; // add one for the E 976 977 if (places >= expdigits) 978 places -= expdigits; 979 if (places < 0) 980 places = 0; // minimum of 0 decimal places 981 982 // Scientific Notation 983 result = parser.EvaluateToScientific(expression.String(), places); 984 985 // If result ends in E0 then remove it 986 if (result[result.Length() - 1] == '0' 987 && result[result.Length() - 2] == 'E') 988 result = result.Remove(result.Length() - 2, 2 * sizeof(char)); 989 } 990 991 fExpressionTextView->SetExpression(result.String()); 921 992 } 922 993 923 924 994 void 925 995 CalcView::FlashKey(const char* bytes, int32 numBytes) 926 996 { -
src/kits/shared/ExpressionParser.cpp
348 348 BString 349 349 ExpressionParser::Evaluate(const char* expressionString) 350 350 { 351 fTokenizer->SetTo(expressionString); 351 return EvaluateToStandard(expressionString, kMaxDecimalPlaces); 352 } 352 353 353 MAPM value = _ParseBinary();354 Token token = fTokenizer->NextToken();355 if (token.type != TOKEN_END_OF_LINE)356 throw ParseException("parse error", token.position);357 354 358 if (value == 0) 359 return BString("0"); 355 BString 356 ExpressionParser::EvaluateToStandard(const char* expressionString, 357 int32 places) 358 { 359 fTokenizer->SetTo(expressionString); 360 360 361 char* buffer = value.toFixPtStringExp(kMaxDecimalPlaces, '.', 0, 0); 362 if (buffer == NULL) 363 throw ParseException("out of memory", 0); 361 MAPM value = _ParseBinary(); 362 Token token = fTokenizer->NextToken(); 363 if (token.type != TOKEN_END_OF_LINE) 364 throw ParseException("parse error", token.position); 364 365 365 // remove surplus zeros 366 int32 lastChar = strlen(buffer) - 1; 367 if (strchr(buffer, '.')) { 368 while (buffer[lastChar] == '0') 369 lastChar--; 370 if (buffer[lastChar] == '.') 371 lastChar--; 372 } 366 if (value == 0) 367 return BString("0"); 373 368 374 BString result(buffer, lastChar + 1); 375 free(buffer); 376 return result; 369 char* buffer = value.toFixPtStringExp(places, '.', 0, 0); 370 if (buffer == NULL) 371 throw ParseException("out of memory", 0); 372 BString result(buffer); 373 free(buffer); 374 375 // remove trailing decimal zeros 376 if (result.FindFirst('.') != B_ERROR) { 377 int32 offset = result.Length() - 1; 378 while (result[offset] == '0') { 379 result = result.Remove(offset, sizeof(char)); 380 offset--; 381 } 382 if (result[offset] == '.') 383 result = result.Remove(offset, sizeof(char)); 384 } 385 386 return result; 377 387 } 378 388 379 389 390 BString 391 ExpressionParser::EvaluateToScientific(const char* expressionString, 392 int32 places) 393 { 394 fTokenizer->SetTo(expressionString); 395 396 MAPM value = _ParseBinary(); 397 Token token = fTokenizer->NextToken(); 398 if (token.type != TOKEN_END_OF_LINE) 399 throw ParseException("parse error", token.position); 400 401 if (value == 0) 402 return BString("0"); 403 404 // max number of characters possible for 32 bits of decimal places is 405 // decimal places + 15 for -0.9999999....E+2147483647 406 // 3 + decimal digits + 2 + 10 = places + 1 407 char buffer[places + 15]; 408 value.toString(buffer, places); 409 BString result(buffer, places + 15); 410 411 // remove the '+' after the 'E' (not needed) 412 int32 offset = result.FindLast('E'); 413 if (offset != B_ERROR && result[offset + 1] == '+') { 414 result = result.Remove(offset + 1, sizeof(char)); 415 } 416 417 // remove trailing decimal zeros 418 if (offset != B_ERROR && result.FindFirst('.') != B_ERROR) { 419 offset--; 420 while (result[offset] == '0') { 421 result = result.Remove(offset, sizeof(char)); 422 offset--; 423 } 424 if (result[offset] == '.') 425 result = result.Remove(offset, sizeof(char)); 426 } 427 428 return result; 429 } 430 431 380 432 int64 381 433 ExpressionParser::EvaluateToInt64(const char* expressionString) 382 434 { … … 565 617 ExpressionParser::_ParseFunction(const Token& token) 566 618 { 567 619 if (strcasecmp("e", token.string.String()) == 0) 568 return MAPM(M _E);620 return MAPM(MM_E); 569 621 else if (strcasecmp("pi", token.string.String()) == 0) 570 return MAPM(M _PI);622 return MAPM(MM_PI); 571 623 572 624 // hard coded cases for different count of arguments 573 625 // supports functions with 3 arguments at most … … 604 656 } else if (strcasecmp("floor", token.string.String()) == 0) { 605 657 _InitArguments(values, 1); 606 658 return values[0].floor(); 607 } else if (strcasecmp("l og", token.string.String()) == 0) {659 } else if (strcasecmp("ln", token.string.String()) == 0) { 608 660 _InitArguments(values, 1); 609 661 return values[0].log(); 610 } else if (strcasecmp("log 10", token.string.String()) == 0) {662 } else if (strcasecmp("log", token.string.String()) == 0) { 611 663 _InitArguments(values, 1); 612 664 return values[0].log10(); 613 665 } else if (strcasecmp("pow", token.string.String()) == 0) { -
headers/private/shared/ExpressionParser.h
29 29 { 30 30 } 31 31 32 BString 32 BString message; 33 33 int32 position; 34 34 }; 35 35 … … 45 45 void SetSupportHexInput(bool enabled); 46 46 47 47 BString Evaluate(const char* expressionString); 48 BString EvaluateToStandard( 49 const char* expressionString, int32 places); 50 BString EvaluateToScientific( 51 const char* expressionString, int32 places); 48 52 int64 EvaluateToInt64(const char* expressionString); 49 53 double EvaluateToDouble(const char* expressionString); 50 54