Ticket #5203: deskcalc.patch

File deskcalc.patch, 9.9 KB (added by jscipione, 12 years ago)

Patch to make DeskCalc always fit the result in the window converting to scientific notation if necessary

  • src/apps/deskcalc/CalcView.cpp

     
    6060
    6161// default calculator key pad layout
    6262const 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";
    6767
    6868
    6969enum {
     
    204204CalcView::AttachedToWindow()
    205205{
    206206    if (be_control_look == NULL)
    207         SetFont(be_bold_font);
     207        SetFont(be_fixed_font);
    208208
    209209    BRect frame(Frame());
    210210    FrameResized(frame.Width(), frame.Height());
     
    579579void
    580580CalcView::KeyDown(const char* bytes, int32 numBytes)
    581581{
    582     // if single byte character...
     582    // if single byte character...
    583583    if (numBytes == 1) {
    584584
    585585        //printf("Key pressed: %c\n", bytes[0]);
     
    668668
    669669    // configure expression text view font size and color
    670670    float sizeDisp = fShowKeypad ? fHeight * kDisplayScaleY : fHeight;
    671     BFont font(be_bold_font);
     671    BFont font(be_fixed_font);
     672    font.SetSpacing(B_FIXED_SPACING);
    672673    font.SetSize(sizeDisp * kExpressionFontScaleY);
    673674    fExpressionTextView->SetFontAndColor(&font, B_FONT_ALL);
    674675
     
    721722void
    722723CalcView::Cut()
    723724{
    724     Copy(); // copy data to clipboard
     725    Copy(); // copy data to clipboard
    725726    fExpressionTextView->Clear(); // remove data
    726727}
    727728
     
    903904
    904905    _AudioFeedback(false);
    905906
     907    ExpressionParser parser;
     908    double value = 0.0;
     909    BString result;
     910
    906911    // evaluate expression
    907     BString value;
    908 
    909912    try {
    910         ExpressionParser parser;
    911         value = parser.Evaluate(expression.String());
     913        value = parser.EvaluateToDouble(expression.String());
    912914    } 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());
    916917        return;
    917918    }
    918919
    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());
    921992}
    922993
    923 
    924994void
    925995CalcView::FlashKey(const char* bytes, int32 numBytes)
    926996{
  • src/kits/shared/ExpressionParser.cpp

     
    348348BString
    349349ExpressionParser::Evaluate(const char* expressionString)
    350350{
    351     fTokenizer->SetTo(expressionString);
     351    return EvaluateToStandard(expressionString, kMaxDecimalPlaces);
     352}
    352353
    353     MAPM value = _ParseBinary();
    354     Token token = fTokenizer->NextToken();
    355     if (token.type != TOKEN_END_OF_LINE)
    356         throw ParseException("parse error", token.position);
    357354
    358     if (value == 0)
    359         return BString("0");
     355BString
     356ExpressionParser::EvaluateToStandard(const char* expressionString,
     357                                     int32 places)
     358{
     359    fTokenizer->SetTo(expressionString);
    360360
    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);
    364365
    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");
    373368
    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;
    377387}
    378388
    379389
     390BString
     391ExpressionParser::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
    380432int64
    381433ExpressionParser::EvaluateToInt64(const char* expressionString)
    382434{
     
    565617ExpressionParser::_ParseFunction(const Token& token)
    566618{
    567619    if (strcasecmp("e", token.string.String()) == 0)
    568         return MAPM(M_E);
     620        return MAPM(MM_E);
    569621    else if (strcasecmp("pi", token.string.String()) == 0)
    570         return MAPM(M_PI);
     622        return MAPM(MM_PI);
    571623
    572624    // hard coded cases for different count of arguments
    573625    // supports functions with 3 arguments at most
     
    604656    } else if (strcasecmp("floor", token.string.String()) == 0) {
    605657        _InitArguments(values, 1);
    606658        return values[0].floor();
    607     } else if (strcasecmp("log", token.string.String()) == 0) {
     659    } else if (strcasecmp("ln", token.string.String()) == 0) {
    608660        _InitArguments(values, 1);
    609661        return values[0].log();
    610     } else if (strcasecmp("log10", token.string.String()) == 0) {
     662    } else if (strcasecmp("log", token.string.String()) == 0) {
    611663        _InitArguments(values, 1);
    612664        return values[0].log10();
    613665    } else if (strcasecmp("pow", token.string.String()) == 0) {
  • headers/private/shared/ExpressionParser.h

     
    2929    {
    3030    }
    3131
    32     BString message;
     32    BString message;
    3333    int32   position;
    3434};
    3535
     
    4545            void                SetSupportHexInput(bool enabled);
    4646
    4747            BString             Evaluate(const char* expressionString);
     48            BString             EvaluateToStandard(
     49                                    const char* expressionString, int32 places);
     50            BString             EvaluateToScientific(
     51                                    const char* expressionString, int32 places);
    4852            int64               EvaluateToInt64(const char* expressionString);
    4953            double              EvaluateToDouble(const char* expressionString);
    5054