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.
This commit is contained in:
parent
93aac98d0a
commit
8ffd0477dd
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
@ -50,9 +54,11 @@ CalcOptions::SaveSettings(BMessage* archive) const
|
||||
if (ret == B_OK)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user