hrev44442 adds 1 changeset to branch 'master' old head: 93aac98d0a9b8ce27f94eb449cfc742446a50274 new head: 8ffd0477dd4998324e051800da35cadd91a7fb8c ---------------------------------------------------------------------------- 8ffd047: Implement degree mode in DeskCalc. Default is radian mode, You set the option in the right click menu like the other options. Note: degree mode does not affect hyperbolic trigonometric functions. This is how Mac Calculator, Windows Calculator, and Google Calculator work. [ John Scipione <jscipione@xxxxxxxxx> ] ---------------------------------------------------------------------------- Revision: hrev44442 Commit: 8ffd0477dd4998324e051800da35cadd91a7fb8c URL: http://cgit.haiku-os.org/haiku/commit/?id=8ffd047 Author: John Scipione <jscipione@xxxxxxxxx> Date: Tue Jul 31 02:35:57 2012 UTC ---------------------------------------------------------------------------- 7 files changed, 98 insertions(+), 3 deletions(-) headers/private/shared/ExpressionParser.h | 6 ++- src/apps/deskcalc/CalcOptions.cpp | 8 +++- src/apps/deskcalc/CalcOptions.h | 1 + src/apps/deskcalc/CalcView.cpp | 24 ++++++++++++ src/apps/deskcalc/CalcView.h | 8 ++++ src/apps/deskcalc/CalcWindow.cpp | 4 ++ src/kits/shared/ExpressionParser.cpp | 50 ++++++++++++++++++++++++- ---------------------------------------------------------------------------- diff --git a/headers/private/shared/ExpressionParser.h b/headers/private/shared/ExpressionParser.h index 9c835b3..4778b29 100644 --- a/headers/private/shared/ExpressionParser.h +++ b/headers/private/shared/ExpressionParser.h @@ -42,6 +42,9 @@ class ExpressionParser { ExpressionParser(); ~ExpressionParser(); + bool DegreeMode(); + void SetDegreeMode(bool degrees); + void SetSupportHexInput(bool enabled); BString Evaluate(const char* expressionString); @@ -49,7 +52,6 @@ class ExpressionParser { double EvaluateToDouble(const char* expressionString); private: - MAPM _ParseBinary(); MAPM _ParseSum(); MAPM _ParseProduct(); @@ -64,6 +66,8 @@ class ExpressionParser { void _EatToken(int32 type); Tokenizer* fTokenizer; + + bool fDegreeMode; }; #endif // EXPRESSION_PARSER_H diff --git a/src/apps/deskcalc/CalcOptions.cpp b/src/apps/deskcalc/CalcOptions.cpp index b737f4d..3b35379 100644 --- a/src/apps/deskcalc/CalcOptions.cpp +++ b/src/apps/deskcalc/CalcOptions.cpp @@ -20,6 +20,7 @@ CalcOptions::CalcOptions() : auto_num_lock(false), audio_feedback(false), + degree_mode(false), keypad_mode(KEYPAD_MODE_BASIC) { } @@ -37,6 +38,9 @@ CalcOptions::LoadSettings(const BMessage* archive) if (archive->FindBool("audio feedback", &option) == B_OK) audio_feedback = option; + if (archive->FindBool("degree mode", &option) == B_OK) + degree_mode = option; + if (archive->FindUInt8("keypad mode", &keypad_mode_option) == B_OK) keypad_mode = keypad_mode_option; } @@ -51,8 +55,10 @@ CalcOptions::SaveSettings(BMessage* archive) const ret = archive->AddBool("audio feedback", audio_feedback); if (ret == B_OK) + ret = archive->AddBool("degree mode", degree_mode); + + if (ret == B_OK) ret = archive->AddUInt8("keypad mode", keypad_mode); return ret; } - diff --git a/src/apps/deskcalc/CalcOptions.h b/src/apps/deskcalc/CalcOptions.h index 1eb013d..2fb45a0 100644 --- a/src/apps/deskcalc/CalcOptions.h +++ b/src/apps/deskcalc/CalcOptions.h @@ -24,6 +24,7 @@ class BMessage; struct CalcOptions { bool auto_num_lock; // automatically activate numlock bool audio_feedback; // provide audio feedback + bool degree_mode; // radian or degree mode uint8 keypad_mode; // keypad mode options CalcOptions(); diff --git a/src/apps/deskcalc/CalcView.cpp b/src/apps/deskcalc/CalcView.cpp index bab5c92..eba3f4f 100644 --- a/src/apps/deskcalc/CalcView.cpp +++ b/src/apps/deskcalc/CalcView.cpp @@ -244,6 +244,10 @@ CalcView::MessageReceived(BMessage* message) case MSG_OPTIONS_AUDIO_FEEDBACK: ToggleAudioFeedback(); return; + + case MSG_OPTIONS_ANGLE_MODE: + ToggleAngleMode(); + return; } } @@ -931,6 +935,7 @@ CalcView::Evaluate() try { ExpressionParser parser; + parser.SetDegreeMode(fOptions->degree_mode); value = parser.Evaluate(expression.String()); } catch (ParseException e) { BString error(e.message.String()); @@ -970,6 +975,16 @@ CalcView::ToggleAudioFeedback(void) fAudioFeedbackItem->SetMarked(fOptions->audio_feedback); } + +void +CalcView::ToggleAngleMode(void) +{ + fOptions->degree_mode = !fOptions->degree_mode; + fAngleModeRadianItem->SetMarked(!fOptions->degree_mode); + fAngleModeDegreeItem->SetMarked(fOptions->degree_mode); +} + + void CalcView::SetKeypadMode(uint8 mode) { @@ -1257,6 +1272,10 @@ CalcView::_CreatePopUpMenu(bool addKeypadModeMenuItems) new BMessage(MSG_OPTIONS_AUTO_NUM_LOCK)); fAudioFeedbackItem = new BMenuItem(B_TRANSLATE("Audio Feedback"), new BMessage(MSG_OPTIONS_AUDIO_FEEDBACK)); + fAngleModeRadianItem = new BMenuItem(B_TRANSLATE("Radian Mode"), + new BMessage(MSG_OPTIONS_ANGLE_MODE)); + fAngleModeDegreeItem = new BMenuItem(B_TRANSLATE("Degree Mode"), + new BMessage(MSG_OPTIONS_ANGLE_MODE)); if (addKeypadModeMenuItems) { fKeypadModeCompactItem = new BMenuItem(B_TRANSLATE("Compact"), new BMessage(MSG_OPTIONS_KEYPAD_MODE_COMPACT), '0'); @@ -1269,6 +1288,8 @@ CalcView::_CreatePopUpMenu(bool addKeypadModeMenuItems) // apply current settings fAutoNumlockItem->SetMarked(fOptions->auto_num_lock); fAudioFeedbackItem->SetMarked(fOptions->audio_feedback); + fAngleModeRadianItem->SetMarked(!fOptions->degree_mode); + fAngleModeDegreeItem->SetMarked(fOptions->degree_mode); // construct menu fPopUpMenu = new BPopUpMenu("pop-up", false, false); @@ -1277,6 +1298,9 @@ CalcView::_CreatePopUpMenu(bool addKeypadModeMenuItems) // TODO: Enable this when we use beep events which can be configured // in the Sounds preflet. //fPopUpMenu->AddItem(fAudioFeedbackItem); + fPopUpMenu->AddSeparatorItem(); + fPopUpMenu->AddItem(fAngleModeRadianItem); + fPopUpMenu->AddItem(fAngleModeDegreeItem); if (addKeypadModeMenuItems) { fPopUpMenu->AddSeparatorItem(); fPopUpMenu->AddItem(fKeypadModeCompactItem); diff --git a/src/apps/deskcalc/CalcView.h b/src/apps/deskcalc/CalcView.h index 16cf413..2c61fe7 100644 --- a/src/apps/deskcalc/CalcView.h +++ b/src/apps/deskcalc/CalcView.h @@ -16,6 +16,7 @@ enum { MSG_OPTIONS_AUTO_NUM_LOCK = 'oanl', MSG_OPTIONS_AUDIO_FEEDBACK = 'oafb', + MSG_OPTIONS_ANGLE_MODE = 'oamd', MSG_OPTIONS_KEYPAD_MODE_COMPACT = 'okmc', MSG_OPTIONS_KEYPAD_MODE_BASIC = 'okmb', MSG_OPTIONS_KEYPAD_MODE_SCIENTIFIC = 'okms', @@ -90,6 +91,9 @@ class CalcView : public BView { // (option currently disabled) void ToggleAudioFeedback(void); + // Toggle radian/degree mode + void ToggleAngleMode(void); + // Set the keypad mode void SetKeypadMode(uint8 mode); @@ -147,6 +151,10 @@ class CalcView : public BView { BPopUpMenu* fPopUpMenu; BMenuItem* fAutoNumlockItem; BMenuItem* fAudioFeedbackItem; + + BMenuItem* fAngleModeRadianItem; + BMenuItem* fAngleModeDegreeItem; + BMenuItem* fKeypadModeCompactItem; BMenuItem* fKeypadModeBasicItem; BMenuItem* fKeypadModeScientificItem; diff --git a/src/apps/deskcalc/CalcWindow.cpp b/src/apps/deskcalc/CalcWindow.cpp index 0ccb142..f91d0d4 100644 --- a/src/apps/deskcalc/CalcWindow.cpp +++ b/src/apps/deskcalc/CalcWindow.cpp @@ -88,6 +88,10 @@ CalcWindow::MessageReceived(BMessage* message) fCalcView->ToggleAudioFeedback(); break; + case MSG_OPTIONS_ANGLE_MODE: + fCalcView->ToggleAngleMode(); + break; + case MSG_OPTIONS_KEYPAD_MODE_COMPACT: fCalcView->SetKeypadMode(KEYPAD_MODE_COMPACT); break; diff --git a/src/kits/shared/ExpressionParser.cpp b/src/kits/shared/ExpressionParser.cpp index 6622433..4ea2387 100644 --- a/src/kits/shared/ExpressionParser.cpp +++ b/src/kits/shared/ExpressionParser.cpp @@ -338,7 +338,8 @@ class Tokenizer { ExpressionParser::ExpressionParser() - : fTokenizer(new Tokenizer()) + : fTokenizer(new Tokenizer()), + fDegreeMode(false) { } @@ -349,6 +350,20 @@ ExpressionParser::~ExpressionParser() } +bool +ExpressionParser::DegreeMode() +{ + return fDegreeMode; +} + + +void +ExpressionParser::SetDegreeMode(bool degrees) +{ + fDegreeMode = degrees; +} + + void ExpressionParser::SetSupportHexInput(bool enabled) { @@ -594,19 +609,36 @@ ExpressionParser::_ParseFunction(const Token& token) return _ParseFactorial(values[0].abs()); } else if (strcasecmp("acos", token.string.String()) == 0) { _InitArguments(values, 1); + if (fDegreeMode) + values[0] = values[0] * MM_PI / 180; + if (values[0] < -1 || values[0] > 1) throw ParseException("out of domain", token.position); + return _ParseFactorial(values[0].acos()); } else if (strcasecmp("asin", token.string.String()) == 0) { _InitArguments(values, 1); + if (fDegreeMode) + values[0] = values[0] * MM_PI / 180; + if (values[0] < -1 || values[0] > 1) throw ParseException("out of domain", token.position); + return _ParseFactorial(values[0].asin()); } else if (strcasecmp("atan", token.string.String()) == 0) { _InitArguments(values, 1); + if (fDegreeMode) + values[0] = values[0] * MM_PI / 180; + return _ParseFactorial(values[0].atan()); } else if (strcasecmp("atan2", token.string.String()) == 0) { _InitArguments(values, 2); + + if (fDegreeMode) { + values[0] = values[0] * MM_PI / 180; + values[1] = values[1] * MM_PI / 180; + } + return _ParseFactorial(values[0].atan2(values[1])); } else if (strcasecmp("cbrt", token.string.String()) == 0) { _InitArguments(values, 1); @@ -616,9 +648,13 @@ ExpressionParser::_ParseFunction(const Token& token) return _ParseFactorial(values[0].ceil()); } else if (strcasecmp("cos", token.string.String()) == 0) { _InitArguments(values, 1); + if (fDegreeMode) + values[0] = values[0] * MM_PI / 180; + return _ParseFactorial(values[0].cos()); } else if (strcasecmp("cosh", token.string.String()) == 0) { _InitArguments(values, 1); + // This function always uses radians return _ParseFactorial(values[0].cosh()); } else if (strcasecmp("exp", token.string.String()) == 0) { _InitArguments(values, 1); @@ -630,34 +666,46 @@ ExpressionParser::_ParseFunction(const Token& token) _InitArguments(values, 1); if (values[0] <= 0) throw ParseException("out of domain", token.position); + return _ParseFactorial(values[0].log()); } else if (strcasecmp("log", token.string.String()) == 0) { _InitArguments(values, 1); if (values[0] <= 0) throw ParseException("out of domain", token.position); + return _ParseFactorial(values[0].log10()); } else if (strcasecmp("pow", token.string.String()) == 0) { _InitArguments(values, 2); return _ParseFactorial(values[0].pow(values[1])); } else if (strcasecmp("sin", token.string.String()) == 0) { _InitArguments(values, 1); + if (fDegreeMode) + values[0] = values[0] * MM_PI / 180; + return _ParseFactorial(values[0].sin()); } else if (strcasecmp("sinh", token.string.String()) == 0) { _InitArguments(values, 1); + // This function always uses radians return _ParseFactorial(values[0].sinh()); } else if (strcasecmp("sqrt", token.string.String()) == 0) { _InitArguments(values, 1); if (values[0] < 0) throw ParseException("out of domain", token.position); + return _ParseFactorial(values[0].sqrt()); } else if (strcasecmp("tan", token.string.String()) == 0) { _InitArguments(values, 1); + if (fDegreeMode) + values[0] = values[0] * MM_PI / 180; + MAPM divided_by_half_pi = values[0] / MM_HALF_PI; if (divided_by_half_pi.is_integer() && divided_by_half_pi.is_odd()) throw ParseException("out of domain", token.position); + return _ParseFactorial(values[0].tan()); } else if (strcasecmp("tanh", token.string.String()) == 0) { _InitArguments(values, 1); + // This function always uses radians return _ParseFactorial(values[0].tanh()); }