diff --git a/src/apps/deskcalc/CalcView.cpp b/src/apps/deskcalc/CalcView.cpp index c23f7a5aa5..7da8893f6a 100644 --- a/src/apps/deskcalc/CalcView.cpp +++ b/src/apps/deskcalc/CalcView.cpp @@ -917,7 +917,7 @@ CalcView::Evaluate() } // render new result to display - fExpressionTextView->SetExpression(value.String()); + fExpressionTextView->SetValue(value.String()); } diff --git a/src/apps/deskcalc/ExpressionTextView.cpp b/src/apps/deskcalc/ExpressionTextView.cpp index da3c6a5a4a..1f992adfd0 100644 --- a/src/apps/deskcalc/ExpressionTextView.cpp +++ b/src/apps/deskcalc/ExpressionTextView.cpp @@ -30,7 +30,9 @@ ExpressionTextView::ExpressionTextView(BRect frame, CalcView* calcView) fKeypadLabels(""), fPreviousExpressions(20), fHistoryPos(0), - fCurrentExpression("") + fCurrentExpression(""), + fCurrentValue(""), + fChangesApplied(false) { SetStylable(false); SetDoesUndo(true); @@ -64,7 +66,7 @@ ExpressionTextView::MakeFocus(bool focused) void ExpressionTextView::KeyDown(const char* bytes, int32 numBytes) { - // handle expression history + // Handle expression history if (bytes[0] == B_UP_ARROW) { PreviousExpression(); return; @@ -75,22 +77,30 @@ ExpressionTextView::KeyDown(const char* bytes, int32 numBytes) } BString current = Text(); - // handle in InputTextView, except B_TAB + // Handle in InputTextView, except B_TAB if (bytes[0] == '=') ApplyChanges(); else if (bytes[0] != B_TAB) InputTextView::KeyDown(bytes, numBytes); - // pass on to CalcView if this was a label on a key + // Pass on to CalcView if this was a label on a key if (fKeypadLabels.FindFirst(bytes[0]) >= 0) fCalcView->FlashKey(bytes, numBytes); else if (bytes[0] == B_BACKSPACE) fCalcView->FlashKey("BS", 2); - // as soon as something is typed, we are at the - // end of the expression history + // As soon as something is typed, we are at the end of the expression + // history. if (current != Text()) fHistoryPos = fPreviousExpressions.CountItems(); + + // If changes where not applied the value has become a new expression + // note that even if only the left or right arrow keys are pressed the + // fCurrentValue string will be cleared. + if (!fChangesApplied) + fCurrentValue.SetTo(""); + else + fChangesApplied = false; } @@ -117,6 +127,17 @@ ExpressionTextView::GetDragParameters(BMessage* dragMessage, } +void +ExpressionTextView::SetTextRect(BRect rect) +{ + InputTextView::SetTextRect(rect); + + int32 count = fPreviousExpressions.CountItems(); + if (fHistoryPos == count && fCurrentValue.CountChars() > 0) + SetValue(fCurrentValue.String()); +} + + // #pragma mark - @@ -133,6 +154,7 @@ ExpressionTextView::ApplyChanges() AddExpressionToHistory(Text()); fCalcView->FlashKey("=", 1); fCalcView->Evaluate(); + fChangesApplied = true; } @@ -155,6 +177,145 @@ ExpressionTextView::SetExpression(const char* expression) } +void +ExpressionTextView::SetValue(const char* value) +{ + // copy the value string so we can modify it + BString val(value); + + // save the value + fCurrentValue = val; + + // calculate the width of the string + BFont font; + uint32 mode = B_FONT_ALL; + GetFontAndColor(&font, &mode); + float stringWidth = font.StringWidth(val); + + // make the string shorter if it does not fit in the view + float viewWidth = Frame().Width(); + if (val.CountChars() > 3 && stringWidth > viewWidth) { + + // get the position of the first digit + int32 firstDigit = 0; + if (val[0] == '-') + firstDigit++; + + // calculate the value of the exponent + int32 exponent = 0; + int32 offset = val.FindFirst('.'); + if (offset == B_ERROR) { + exponent = val.CountChars() - 1 - firstDigit; + val.Insert('.', 1, firstDigit + 1); + } else { + if (offset == firstDigit + 1) { + // if the value is 0.01 or larger then scientific notation + // won't shorten the string + if (val[firstDigit] != '0' || val[firstDigit+2] != '0' + || val[firstDigit+3] != '0') { + exponent = 0; + } else { + // remove the period + val.Remove(offset, 1); + + // check for negative exponent value + exponent = 0; + while (val[firstDigit] == '0') { + val.Remove(firstDigit, 1); + exponent--; + } + + //add the period + val.Insert('.', 1, firstDigit + 1); + } + } else { + // if the period + 1 digit fits in the view scientific notation + // won't shorten the string + BString temp = val; + temp.Truncate(offset + 2); + stringWidth = font.StringWidth(temp); + if (stringWidth < viewWidth) + exponent = 0; + else { + // move the period + val.Remove(offset, 1); + val.Insert('.', 1, firstDigit + 1); + + exponent = offset - (firstDigit + 1); + } + } + } + + // add the exponent + offset = val.CountChars() - 1; + if (exponent != 0) + val << "E" << exponent; + + // reduce the number of digits until the string fits or can not be + // made any shorter + stringWidth = font.StringWidth(val); + char lastRemovedDigit = '0'; + while (offset > firstDigit && stringWidth > viewWidth) { + if (val[offset] != '.') + lastRemovedDigit = val[offset]; + val.Remove(offset--, 1); + stringWidth = font.StringWidth(val); + } + + // there is no need to keep the period if no digits follow + if (val[offset] == '.') { + val.Remove(offset, 1); + offset--; + } + + // take care of proper rounding of the result + int digit = (int)lastRemovedDigit - 48; // ascii to int + if (digit >= 5) { + for (; offset >= firstDigit; offset--) { + if (val[offset] == '.') + continue; + digit = (int)(val[offset]) - 47; // ascii to int + 1 + if (digit != 10) break; + val.Remove(offset, 1); + } + if (digit == 10) { + // carry over, shift the result + if (val[firstDigit+1] == '.') { + val[firstDigit+1] = '0'; + val[firstDigit] = '.'; + } + val.Insert('1', 1, firstDigit); + + //remove the exponent value and the last digit + offset = val.FindFirst('E'); + if (offset == B_ERROR) + offset = val.CountChars(); + val.Truncate(--offset); + offset--; //offset now points to the last digit + + // increase the exponent and add it back to the string + exponent++; + val << 'E' << exponent; + } else { + // increase the current digit value with one + val[offset] = char(digit + 48); + } + } + + // remove trailing zeros + while (val[offset] == '0') + val.Remove(offset--, 1); + + // there is no need to keep the period if no digits follow + if (val[offset] == '.') + val.Remove(offset, 1); + } + + // set the new value + SetExpression(val); +} + + void ExpressionTextView::BackSpace() { diff --git a/src/apps/deskcalc/ExpressionTextView.h b/src/apps/deskcalc/ExpressionTextView.h index ad3ae1a281..77b49a494d 100644 --- a/src/apps/deskcalc/ExpressionTextView.h +++ b/src/apps/deskcalc/ExpressionTextView.h @@ -32,6 +32,7 @@ class ExpressionTextView : public InputTextView { virtual void GetDragParameters(BMessage* dragMessage, BBitmap** bitmap, BPoint* point, BHandler** handler); + void SetTextRect(BRect rect); // InputTextView virtual void RevertChanges(); @@ -41,6 +42,7 @@ class ExpressionTextView : public InputTextView { void AddKeypadLabel(const char* label); void SetExpression(const char* expression); + void SetValue(const char* value); void BackSpace(); void Clear(); @@ -59,6 +61,9 @@ class ExpressionTextView : public InputTextView { BList fPreviousExpressions; int32 fHistoryPos; BString fCurrentExpression; + BString fCurrentValue; + + bool fChangesApplied; }; #endif // EXPRESSION_TEXT_VIEW_H