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();
|
||||||
~ExpressionParser();
|
~ExpressionParser();
|
||||||
|
|
||||||
|
bool DegreeMode();
|
||||||
|
void SetDegreeMode(bool degrees);
|
||||||
|
|
||||||
void SetSupportHexInput(bool enabled);
|
void SetSupportHexInput(bool enabled);
|
||||||
|
|
||||||
BString Evaluate(const char* expressionString);
|
BString Evaluate(const char* expressionString);
|
||||||
@ -49,7 +52,6 @@ class ExpressionParser {
|
|||||||
double EvaluateToDouble(const char* expressionString);
|
double EvaluateToDouble(const char* expressionString);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
MAPM _ParseBinary();
|
MAPM _ParseBinary();
|
||||||
MAPM _ParseSum();
|
MAPM _ParseSum();
|
||||||
MAPM _ParseProduct();
|
MAPM _ParseProduct();
|
||||||
@ -64,6 +66,8 @@ class ExpressionParser {
|
|||||||
void _EatToken(int32 type);
|
void _EatToken(int32 type);
|
||||||
|
|
||||||
Tokenizer* fTokenizer;
|
Tokenizer* fTokenizer;
|
||||||
|
|
||||||
|
bool fDegreeMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // EXPRESSION_PARSER_H
|
#endif // EXPRESSION_PARSER_H
|
||||||
|
@ -20,6 +20,7 @@ CalcOptions::CalcOptions()
|
|||||||
:
|
:
|
||||||
auto_num_lock(false),
|
auto_num_lock(false),
|
||||||
audio_feedback(false),
|
audio_feedback(false),
|
||||||
|
degree_mode(false),
|
||||||
keypad_mode(KEYPAD_MODE_BASIC)
|
keypad_mode(KEYPAD_MODE_BASIC)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -37,6 +38,9 @@ CalcOptions::LoadSettings(const BMessage* archive)
|
|||||||
if (archive->FindBool("audio feedback", &option) == B_OK)
|
if (archive->FindBool("audio feedback", &option) == B_OK)
|
||||||
audio_feedback = option;
|
audio_feedback = option;
|
||||||
|
|
||||||
|
if (archive->FindBool("degree mode", &option) == B_OK)
|
||||||
|
degree_mode = option;
|
||||||
|
|
||||||
if (archive->FindUInt8("keypad mode", &keypad_mode_option) == B_OK)
|
if (archive->FindUInt8("keypad mode", &keypad_mode_option) == B_OK)
|
||||||
keypad_mode = keypad_mode_option;
|
keypad_mode = keypad_mode_option;
|
||||||
}
|
}
|
||||||
@ -50,9 +54,11 @@ CalcOptions::SaveSettings(BMessage* archive) const
|
|||||||
if (ret == B_OK)
|
if (ret == B_OK)
|
||||||
ret = archive->AddBool("audio feedback", audio_feedback);
|
ret = archive->AddBool("audio feedback", audio_feedback);
|
||||||
|
|
||||||
|
if (ret == B_OK)
|
||||||
|
ret = archive->AddBool("degree mode", degree_mode);
|
||||||
|
|
||||||
if (ret == B_OK)
|
if (ret == B_OK)
|
||||||
ret = archive->AddUInt8("keypad mode", keypad_mode);
|
ret = archive->AddUInt8("keypad mode", keypad_mode);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ class BMessage;
|
|||||||
struct CalcOptions {
|
struct CalcOptions {
|
||||||
bool auto_num_lock; // automatically activate numlock
|
bool auto_num_lock; // automatically activate numlock
|
||||||
bool audio_feedback; // provide audio feedback
|
bool audio_feedback; // provide audio feedback
|
||||||
|
bool degree_mode; // radian or degree mode
|
||||||
uint8 keypad_mode; // keypad mode options
|
uint8 keypad_mode; // keypad mode options
|
||||||
|
|
||||||
CalcOptions();
|
CalcOptions();
|
||||||
|
@ -244,6 +244,10 @@ CalcView::MessageReceived(BMessage* message)
|
|||||||
case MSG_OPTIONS_AUDIO_FEEDBACK:
|
case MSG_OPTIONS_AUDIO_FEEDBACK:
|
||||||
ToggleAudioFeedback();
|
ToggleAudioFeedback();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case MSG_OPTIONS_ANGLE_MODE:
|
||||||
|
ToggleAngleMode();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -931,6 +935,7 @@ CalcView::Evaluate()
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
ExpressionParser parser;
|
ExpressionParser parser;
|
||||||
|
parser.SetDegreeMode(fOptions->degree_mode);
|
||||||
value = parser.Evaluate(expression.String());
|
value = parser.Evaluate(expression.String());
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
BString error(e.message.String());
|
BString error(e.message.String());
|
||||||
@ -970,6 +975,16 @@ CalcView::ToggleAudioFeedback(void)
|
|||||||
fAudioFeedbackItem->SetMarked(fOptions->audio_feedback);
|
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
|
void
|
||||||
CalcView::SetKeypadMode(uint8 mode)
|
CalcView::SetKeypadMode(uint8 mode)
|
||||||
{
|
{
|
||||||
@ -1257,6 +1272,10 @@ CalcView::_CreatePopUpMenu(bool addKeypadModeMenuItems)
|
|||||||
new BMessage(MSG_OPTIONS_AUTO_NUM_LOCK));
|
new BMessage(MSG_OPTIONS_AUTO_NUM_LOCK));
|
||||||
fAudioFeedbackItem = new BMenuItem(B_TRANSLATE("Audio Feedback"),
|
fAudioFeedbackItem = new BMenuItem(B_TRANSLATE("Audio Feedback"),
|
||||||
new BMessage(MSG_OPTIONS_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) {
|
if (addKeypadModeMenuItems) {
|
||||||
fKeypadModeCompactItem = new BMenuItem(B_TRANSLATE("Compact"),
|
fKeypadModeCompactItem = new BMenuItem(B_TRANSLATE("Compact"),
|
||||||
new BMessage(MSG_OPTIONS_KEYPAD_MODE_COMPACT), '0');
|
new BMessage(MSG_OPTIONS_KEYPAD_MODE_COMPACT), '0');
|
||||||
@ -1269,6 +1288,8 @@ CalcView::_CreatePopUpMenu(bool addKeypadModeMenuItems)
|
|||||||
// apply current settings
|
// apply current settings
|
||||||
fAutoNumlockItem->SetMarked(fOptions->auto_num_lock);
|
fAutoNumlockItem->SetMarked(fOptions->auto_num_lock);
|
||||||
fAudioFeedbackItem->SetMarked(fOptions->audio_feedback);
|
fAudioFeedbackItem->SetMarked(fOptions->audio_feedback);
|
||||||
|
fAngleModeRadianItem->SetMarked(!fOptions->degree_mode);
|
||||||
|
fAngleModeDegreeItem->SetMarked(fOptions->degree_mode);
|
||||||
|
|
||||||
// construct menu
|
// construct menu
|
||||||
fPopUpMenu = new BPopUpMenu("pop-up", false, false);
|
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
|
// TODO: Enable this when we use beep events which can be configured
|
||||||
// in the Sounds preflet.
|
// in the Sounds preflet.
|
||||||
//fPopUpMenu->AddItem(fAudioFeedbackItem);
|
//fPopUpMenu->AddItem(fAudioFeedbackItem);
|
||||||
|
fPopUpMenu->AddSeparatorItem();
|
||||||
|
fPopUpMenu->AddItem(fAngleModeRadianItem);
|
||||||
|
fPopUpMenu->AddItem(fAngleModeDegreeItem);
|
||||||
if (addKeypadModeMenuItems) {
|
if (addKeypadModeMenuItems) {
|
||||||
fPopUpMenu->AddSeparatorItem();
|
fPopUpMenu->AddSeparatorItem();
|
||||||
fPopUpMenu->AddItem(fKeypadModeCompactItem);
|
fPopUpMenu->AddItem(fKeypadModeCompactItem);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
enum {
|
enum {
|
||||||
MSG_OPTIONS_AUTO_NUM_LOCK = 'oanl',
|
MSG_OPTIONS_AUTO_NUM_LOCK = 'oanl',
|
||||||
MSG_OPTIONS_AUDIO_FEEDBACK = 'oafb',
|
MSG_OPTIONS_AUDIO_FEEDBACK = 'oafb',
|
||||||
|
MSG_OPTIONS_ANGLE_MODE = 'oamd',
|
||||||
MSG_OPTIONS_KEYPAD_MODE_COMPACT = 'okmc',
|
MSG_OPTIONS_KEYPAD_MODE_COMPACT = 'okmc',
|
||||||
MSG_OPTIONS_KEYPAD_MODE_BASIC = 'okmb',
|
MSG_OPTIONS_KEYPAD_MODE_BASIC = 'okmb',
|
||||||
MSG_OPTIONS_KEYPAD_MODE_SCIENTIFIC = 'okms',
|
MSG_OPTIONS_KEYPAD_MODE_SCIENTIFIC = 'okms',
|
||||||
@ -90,6 +91,9 @@ class CalcView : public BView {
|
|||||||
// (option currently disabled)
|
// (option currently disabled)
|
||||||
void ToggleAudioFeedback(void);
|
void ToggleAudioFeedback(void);
|
||||||
|
|
||||||
|
// Toggle radian/degree mode
|
||||||
|
void ToggleAngleMode(void);
|
||||||
|
|
||||||
// Set the keypad mode
|
// Set the keypad mode
|
||||||
void SetKeypadMode(uint8 mode);
|
void SetKeypadMode(uint8 mode);
|
||||||
|
|
||||||
@ -147,6 +151,10 @@ class CalcView : public BView {
|
|||||||
BPopUpMenu* fPopUpMenu;
|
BPopUpMenu* fPopUpMenu;
|
||||||
BMenuItem* fAutoNumlockItem;
|
BMenuItem* fAutoNumlockItem;
|
||||||
BMenuItem* fAudioFeedbackItem;
|
BMenuItem* fAudioFeedbackItem;
|
||||||
|
|
||||||
|
BMenuItem* fAngleModeRadianItem;
|
||||||
|
BMenuItem* fAngleModeDegreeItem;
|
||||||
|
|
||||||
BMenuItem* fKeypadModeCompactItem;
|
BMenuItem* fKeypadModeCompactItem;
|
||||||
BMenuItem* fKeypadModeBasicItem;
|
BMenuItem* fKeypadModeBasicItem;
|
||||||
BMenuItem* fKeypadModeScientificItem;
|
BMenuItem* fKeypadModeScientificItem;
|
||||||
|
@ -88,6 +88,10 @@ CalcWindow::MessageReceived(BMessage* message)
|
|||||||
fCalcView->ToggleAudioFeedback();
|
fCalcView->ToggleAudioFeedback();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MSG_OPTIONS_ANGLE_MODE:
|
||||||
|
fCalcView->ToggleAngleMode();
|
||||||
|
break;
|
||||||
|
|
||||||
case MSG_OPTIONS_KEYPAD_MODE_COMPACT:
|
case MSG_OPTIONS_KEYPAD_MODE_COMPACT:
|
||||||
fCalcView->SetKeypadMode(KEYPAD_MODE_COMPACT);
|
fCalcView->SetKeypadMode(KEYPAD_MODE_COMPACT);
|
||||||
break;
|
break;
|
||||||
|
@ -338,7 +338,8 @@ class Tokenizer {
|
|||||||
|
|
||||||
|
|
||||||
ExpressionParser::ExpressionParser()
|
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
|
void
|
||||||
ExpressionParser::SetSupportHexInput(bool enabled)
|
ExpressionParser::SetSupportHexInput(bool enabled)
|
||||||
{
|
{
|
||||||
@ -594,19 +609,36 @@ ExpressionParser::_ParseFunction(const Token& token)
|
|||||||
return _ParseFactorial(values[0].abs());
|
return _ParseFactorial(values[0].abs());
|
||||||
} else if (strcasecmp("acos", token.string.String()) == 0) {
|
} else if (strcasecmp("acos", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
|
if (fDegreeMode)
|
||||||
|
values[0] = values[0] * MM_PI / 180;
|
||||||
|
|
||||||
if (values[0] < -1 || values[0] > 1)
|
if (values[0] < -1 || values[0] > 1)
|
||||||
throw ParseException("out of domain", token.position);
|
throw ParseException("out of domain", token.position);
|
||||||
|
|
||||||
return _ParseFactorial(values[0].acos());
|
return _ParseFactorial(values[0].acos());
|
||||||
} else if (strcasecmp("asin", token.string.String()) == 0) {
|
} else if (strcasecmp("asin", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
|
if (fDegreeMode)
|
||||||
|
values[0] = values[0] * MM_PI / 180;
|
||||||
|
|
||||||
if (values[0] < -1 || values[0] > 1)
|
if (values[0] < -1 || values[0] > 1)
|
||||||
throw ParseException("out of domain", token.position);
|
throw ParseException("out of domain", token.position);
|
||||||
|
|
||||||
return _ParseFactorial(values[0].asin());
|
return _ParseFactorial(values[0].asin());
|
||||||
} else if (strcasecmp("atan", token.string.String()) == 0) {
|
} else if (strcasecmp("atan", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
|
if (fDegreeMode)
|
||||||
|
values[0] = values[0] * MM_PI / 180;
|
||||||
|
|
||||||
return _ParseFactorial(values[0].atan());
|
return _ParseFactorial(values[0].atan());
|
||||||
} else if (strcasecmp("atan2", token.string.String()) == 0) {
|
} else if (strcasecmp("atan2", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 2);
|
_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]));
|
return _ParseFactorial(values[0].atan2(values[1]));
|
||||||
} else if (strcasecmp("cbrt", token.string.String()) == 0) {
|
} else if (strcasecmp("cbrt", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
@ -616,9 +648,13 @@ ExpressionParser::_ParseFunction(const Token& token)
|
|||||||
return _ParseFactorial(values[0].ceil());
|
return _ParseFactorial(values[0].ceil());
|
||||||
} else if (strcasecmp("cos", token.string.String()) == 0) {
|
} else if (strcasecmp("cos", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
|
if (fDegreeMode)
|
||||||
|
values[0] = values[0] * MM_PI / 180;
|
||||||
|
|
||||||
return _ParseFactorial(values[0].cos());
|
return _ParseFactorial(values[0].cos());
|
||||||
} else if (strcasecmp("cosh", token.string.String()) == 0) {
|
} else if (strcasecmp("cosh", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
|
// This function always uses radians
|
||||||
return _ParseFactorial(values[0].cosh());
|
return _ParseFactorial(values[0].cosh());
|
||||||
} else if (strcasecmp("exp", token.string.String()) == 0) {
|
} else if (strcasecmp("exp", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
@ -630,34 +666,46 @@ ExpressionParser::_ParseFunction(const Token& token)
|
|||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
if (values[0] <= 0)
|
if (values[0] <= 0)
|
||||||
throw ParseException("out of domain", token.position);
|
throw ParseException("out of domain", token.position);
|
||||||
|
|
||||||
return _ParseFactorial(values[0].log());
|
return _ParseFactorial(values[0].log());
|
||||||
} else if (strcasecmp("log", token.string.String()) == 0) {
|
} else if (strcasecmp("log", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
if (values[0] <= 0)
|
if (values[0] <= 0)
|
||||||
throw ParseException("out of domain", token.position);
|
throw ParseException("out of domain", token.position);
|
||||||
|
|
||||||
return _ParseFactorial(values[0].log10());
|
return _ParseFactorial(values[0].log10());
|
||||||
} else if (strcasecmp("pow", token.string.String()) == 0) {
|
} else if (strcasecmp("pow", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 2);
|
_InitArguments(values, 2);
|
||||||
return _ParseFactorial(values[0].pow(values[1]));
|
return _ParseFactorial(values[0].pow(values[1]));
|
||||||
} else if (strcasecmp("sin", token.string.String()) == 0) {
|
} else if (strcasecmp("sin", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
|
if (fDegreeMode)
|
||||||
|
values[0] = values[0] * MM_PI / 180;
|
||||||
|
|
||||||
return _ParseFactorial(values[0].sin());
|
return _ParseFactorial(values[0].sin());
|
||||||
} else if (strcasecmp("sinh", token.string.String()) == 0) {
|
} else if (strcasecmp("sinh", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
|
// This function always uses radians
|
||||||
return _ParseFactorial(values[0].sinh());
|
return _ParseFactorial(values[0].sinh());
|
||||||
} else if (strcasecmp("sqrt", token.string.String()) == 0) {
|
} else if (strcasecmp("sqrt", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
if (values[0] < 0)
|
if (values[0] < 0)
|
||||||
throw ParseException("out of domain", token.position);
|
throw ParseException("out of domain", token.position);
|
||||||
|
|
||||||
return _ParseFactorial(values[0].sqrt());
|
return _ParseFactorial(values[0].sqrt());
|
||||||
} else if (strcasecmp("tan", token.string.String()) == 0) {
|
} else if (strcasecmp("tan", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
|
if (fDegreeMode)
|
||||||
|
values[0] = values[0] * MM_PI / 180;
|
||||||
|
|
||||||
MAPM divided_by_half_pi = values[0] / MM_HALF_PI;
|
MAPM divided_by_half_pi = values[0] / MM_HALF_PI;
|
||||||
if (divided_by_half_pi.is_integer() && divided_by_half_pi.is_odd())
|
if (divided_by_half_pi.is_integer() && divided_by_half_pi.is_odd())
|
||||||
throw ParseException("out of domain", token.position);
|
throw ParseException("out of domain", token.position);
|
||||||
|
|
||||||
return _ParseFactorial(values[0].tan());
|
return _ParseFactorial(values[0].tan());
|
||||||
} else if (strcasecmp("tanh", token.string.String()) == 0) {
|
} else if (strcasecmp("tanh", token.string.String()) == 0) {
|
||||||
_InitArguments(values, 1);
|
_InitArguments(values, 1);
|
||||||
|
// This function always uses radians
|
||||||
return _ParseFactorial(values[0].tanh());
|
return _ParseFactorial(values[0].tanh());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user