From 7cb395c433f4ba7efee9f127f9d41fb651f3c30f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Tue, 6 Jun 2006 16:07:47 +0000 Subject: [PATCH] adding DeskCalc by Timothy Wayper huge cleanup of the DeskCalc code: * applied style guide * fixed any TODOs from timmy * slightly nicer looking options window * new way to store settings * code should be more robust * includes parser kindly donated by Daniel Wallner git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17737 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/Jamfile | 1 + src/apps/deskcalc/CalcApplication.cpp | 157 ++++ src/apps/deskcalc/CalcApplication.h | 37 + src/apps/deskcalc/CalcOptionsWindow.cpp | 101 +++ src/apps/deskcalc/CalcOptionsWindow.h | 42 ++ src/apps/deskcalc/CalcView.cpp | 878 ++++++++++++++++++++++ src/apps/deskcalc/CalcView.h | 109 +++ src/apps/deskcalc/CalcWindow.cpp | 125 ++++ src/apps/deskcalc/CalcWindow.h | 37 + src/apps/deskcalc/DeskCalc.cpp | 22 + src/apps/deskcalc/DeskCalc.cpp_cli | 281 +++++++ src/apps/deskcalc/DeskCalc.h | 8 + src/apps/deskcalc/DeskCalc.rdef | 74 ++ src/apps/deskcalc/Jamfile | 21 + src/apps/deskcalc/Parser.cpp | 952 ++++++++++++++++++++++++ src/apps/deskcalc/Parser.h | 70 ++ 16 files changed, 2915 insertions(+) create mode 100644 src/apps/deskcalc/CalcApplication.cpp create mode 100644 src/apps/deskcalc/CalcApplication.h create mode 100644 src/apps/deskcalc/CalcOptionsWindow.cpp create mode 100644 src/apps/deskcalc/CalcOptionsWindow.h create mode 100644 src/apps/deskcalc/CalcView.cpp create mode 100644 src/apps/deskcalc/CalcView.h create mode 100644 src/apps/deskcalc/CalcWindow.cpp create mode 100644 src/apps/deskcalc/CalcWindow.h create mode 100644 src/apps/deskcalc/DeskCalc.cpp create mode 100644 src/apps/deskcalc/DeskCalc.cpp_cli create mode 100644 src/apps/deskcalc/DeskCalc.h create mode 100644 src/apps/deskcalc/DeskCalc.rdef create mode 100644 src/apps/deskcalc/Jamfile create mode 100644 src/apps/deskcalc/Parser.cpp create mode 100644 src/apps/deskcalc/Parser.h diff --git a/src/apps/Jamfile b/src/apps/Jamfile index 18ef266071..daf19fadc0 100644 --- a/src/apps/Jamfile +++ b/src/apps/Jamfile @@ -8,6 +8,7 @@ SubInclude HAIKU_TOP src apps clock ; SubInclude HAIKU_TOP src apps codycam ; SubInclude HAIKU_TOP src apps cortex ; SubInclude HAIKU_TOP src apps deskbar ; +SubInclude HAIKU_TOP src apps deskcalc ; SubInclude HAIKU_TOP src apps diskprobe ; SubInclude HAIKU_TOP src apps expander ; SubInclude HAIKU_TOP src apps glteapot ; diff --git a/src/apps/deskcalc/CalcApplication.cpp b/src/apps/deskcalc/CalcApplication.cpp new file mode 100644 index 0000000000..42672f6af9 --- /dev/null +++ b/src/apps/deskcalc/CalcApplication.cpp @@ -0,0 +1,157 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Copyright 1997, 1998 R3 Software Ltd. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Timothy Wayper + * Stephan Aßmus + */ + +#include "CalcApplication.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "CalcWindow.h" + + +static const char* kSettingsFileName = "DeskCalc_settings"; +extern const char* kAppSig = "application/x-vnd.Haiku-DeskCalc"; + +static const float kDefaultWindowWidth = 220.0; +static const float kDefaultWindowHeight = 140.0; + + +CalcApplication::CalcApplication() + : BApplication(kAppSig), + fCalcWindow(NULL) +{ +} + + +CalcApplication::~CalcApplication() +{ +} + + +void +CalcApplication::ReadyToRun() +{ + BRect frame(0, 0, kDefaultWindowWidth - 1, kDefaultWindowHeight - 1); + fCalcWindow = new CalcWindow(frame); + + if (!_LoadSettings()) + fCalcWindow->SetFrame(frame, true); + + // reveal window + fCalcWindow->Show(); +} + + +void +CalcApplication::AboutRequested() +{ + // TODO: implement me! + return BApplication::AboutRequested(); +} + + +bool +CalcApplication::QuitRequested() +{ + // save current user preferences + _SaveSettings(); + + return true; +} + + +// #pragma mark - + + +bool +CalcApplication::_LoadSettings() +{ + // locate preferences file + BFile prefsFile; + if (_InitSettingsFile(&prefsFile, false) < B_OK) { + printf("no preference file found.\n"); + return false; + } + + // unflatten settings data + BMessage archive; + if (archive.Unflatten(&prefsFile) < B_OK) { + printf("error unflattening settings.\n"); + return false; + } + + // apply settings + return fCalcWindow->LoadSettings(&archive); +} + + +void +CalcApplication::_SaveSettings() +{ + if (!fCalcWindow->Lock()) + return; + + // archive the current state of the calculator + BMessage archive; + status_t ret = fCalcWindow->SaveSettings(&archive); + + fCalcWindow->Unlock(); + + if (ret < B_OK) { + fprintf(stderr, "CalcApplication::_SaveSettings() - " + "error from window: %s\n", strerror(ret)); + return; + } + + // flatten entire acrhive and write to settings file + BFile prefsFile; + ret = _InitSettingsFile(&prefsFile, true); + if (ret < B_OK) { + fprintf(stderr, "CalcApplication::_SaveSettings() - " + "error creating file: %s\n", strerror(ret)); + return; + } + + ret = archive.Flatten(&prefsFile); + if (ret < B_OK) { + fprintf(stderr, "CalcApplication::_SaveSettings() - " + "error flattening to file: %s\n", strerror(ret)); + return; + } +} + +status_t +CalcApplication::_InitSettingsFile(BFile* file, bool write) +{ + // find user settings directory + BPath prefsPath; + status_t ret = find_directory(B_USER_SETTINGS_DIRECTORY, &prefsPath); + if (ret < B_OK) + return ret; + + ret = prefsPath.Append(kSettingsFileName); + if (ret < B_OK) + return ret; + + if (write) + ret = file->SetTo(prefsPath.Path(), B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); + else + ret = file->SetTo(prefsPath.Path(), B_READ_ONLY); + + return ret; +} + + diff --git a/src/apps/deskcalc/CalcApplication.h b/src/apps/deskcalc/CalcApplication.h new file mode 100644 index 0000000000..5850bc0463 --- /dev/null +++ b/src/apps/deskcalc/CalcApplication.h @@ -0,0 +1,37 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Copyright 1997, 1998 R3 Software Ltd. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Timothy Wayper + * Stephan Aßmus + */ + +#ifndef _CALC_APPLICATION_H +#define _CALC_APPLICATION_H + +#include + +extern const char* kAppSig; + +class CalcWindow; + +class CalcApplication : public BApplication { + public: + CalcApplication(); + virtual ~CalcApplication(); + + virtual void ReadyToRun(); + virtual void AboutRequested(); + virtual bool QuitRequested(); + + private: + bool _LoadSettings(); + void _SaveSettings(); + status_t _InitSettingsFile(BFile* file, bool write); + + CalcWindow* fCalcWindow; +}; + +#endif // _CALC_APPLICATION_H diff --git a/src/apps/deskcalc/CalcOptionsWindow.cpp b/src/apps/deskcalc/CalcOptionsWindow.cpp new file mode 100644 index 0000000000..cecdf6787e --- /dev/null +++ b/src/apps/deskcalc/CalcOptionsWindow.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Copyright 1997, 1998 R3 Software Ltd. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Timothy Wayper + * Stephan Aßmus + */ + +#include "CalcOptionsWindow.h" + +#include +#include + +#include +#include +#include + + +CalcOptions::CalcOptions() + : mAutoNumLock(true), + mAudioFeedback(false) +{ +} + + +CalcOptionsWindow::CalcOptionsWindow(BRect frame, CalcOptions *options) + : BWindow(frame, "Options", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS), + fOptions(options) +{ + // TODO: revisit when Ingo has created his layout framework... + frame.OffsetTo(B_ORIGIN); + BBox* bg = new BBox(frame, "bg", B_FOLLOW_ALL); + bg->SetBorder(B_PLAIN_BORDER); + AddChild(bg); + + frame.InsetBy(5.0f, 5.0f); + + // create interface components + float y = 16.0f, vw, vh; + BRect viewframe(4.0f, y, frame.right, y + 16.0f); + fAutoNumLockCheckBox = new BCheckBox(viewframe, + "autoNumLockCheckBox", "Auto Num Lock", NULL); + if (fOptions->mAutoNumLock) { + fAutoNumLockCheckBox->SetValue(B_CONTROL_ON); + } + bg->AddChild(fAutoNumLockCheckBox); + fAutoNumLockCheckBox->ResizeToPreferred(); + y += fAutoNumLockCheckBox->Frame().Height(); + + viewframe.Set(4.0f, y, frame.right, y + 16.0f); + fAudioFeedbackCheckBox = new BCheckBox(viewframe, + "audioFeedbackCheckBox", "Audio Feedback", NULL); + if (fOptions->mAudioFeedback) { + fAudioFeedbackCheckBox->SetValue(B_CONTROL_ON); + } + bg->AddChild(fAudioFeedbackCheckBox); + fAudioFeedbackCheckBox->ResizeToPreferred(); + y += fAudioFeedbackCheckBox->Frame().Height(); + + // create buttons + viewframe.Set(0.0f, 0.0f, 40.0f, 40.0f); + fOkButton = new BButton(viewframe, "okButton", "OK", + new BMessage(B_QUIT_REQUESTED)); + fOkButton->MakeDefault(true); + fOkButton->GetPreferredSize(&vw, &vh); + fOkButton->ResizeTo(vw, vh); + fOkButton->MoveTo(frame.right - vw - 8.0f, + frame.bottom - vh - 8.0f); + bg->AddChild(fOkButton); + + float cw, ch; + fCancelButton = new BButton(viewframe, "cancelButton", "Cancel", + new BMessage(B_QUIT_REQUESTED)); + fCancelButton->GetPreferredSize(&cw, &ch); + fCancelButton->ResizeTo(cw, ch); + fCancelButton->MoveTo(frame.right - vw - 16.0f - cw, + frame.bottom - ch - 8.0f); + bg->AddChild(fCancelButton); +} + + +CalcOptionsWindow::~CalcOptionsWindow() +{ + // auto num-lock + if (fAutoNumLockCheckBox->Value() == B_CONTROL_ON) + fOptions->mAutoNumLock = true; + else + fOptions->mAutoNumLock = false; + + // audio feedback + if (fAudioFeedbackCheckBox->Value() == B_CONTROL_ON) + fOptions->mAudioFeedback = true; + else + fOptions->mAudioFeedback = false; +} + + + + diff --git a/src/apps/deskcalc/CalcOptionsWindow.h b/src/apps/deskcalc/CalcOptionsWindow.h new file mode 100644 index 0000000000..ad8995acdd --- /dev/null +++ b/src/apps/deskcalc/CalcOptionsWindow.h @@ -0,0 +1,42 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Copyright 1997, 1998 R3 Software Ltd. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Timothy Wayper + * Stephan Aßmus + */ + +#ifndef _CALC_OPTIONS_WINDOW_H +#define _CALC_OPTIONS_WINDOW_H + +#include + +struct CalcOptions { + bool mAutoNumLock; // automatically activate numlock + bool mAudioFeedback; // provide audio feedback + + CalcOptions(); +}; + +class BCheckBox; +class BButton; + +class CalcOptionsWindow : public BWindow { + public: + CalcOptionsWindow(BRect frame, + CalcOptions* options); + + virtual ~CalcOptionsWindow(); + + private: + CalcOptions* fOptions; + + BCheckBox* fAutoNumLockCheckBox; + BCheckBox* fAudioFeedbackCheckBox; + BButton* fOkButton; + BButton* fCancelButton; +}; + +#endif // _CALC_OPTIONS_WINDOW_H diff --git a/src/apps/deskcalc/CalcView.cpp b/src/apps/deskcalc/CalcView.cpp new file mode 100644 index 0000000000..f01e179df1 --- /dev/null +++ b/src/apps/deskcalc/CalcView.cpp @@ -0,0 +1,878 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Copyright 1997, 1998 R3 Software Ltd. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Timothy Wayper + * Stephan Aßmus + */ + +#include "CalcView.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CalcApplication.h" +#include "CalcOptionsWindow.h" +#include "Parser.h" + + +const uint8 K_COLOR_OFFSET = 32; +const float K_FONT_YPROP = 0.6f; +const float K_DISPLAY_YPROP = 0.2; + +enum { + K_OPTIONS_REQUESTED = 'opts' +}; + +// default calculator key pad layout +const char *kDefaultKeypadDescription = + "7 8 9 ( ) \n" + "4 5 6 * / \n" + "1 2 3 + - \n" + "0 . BS = C \n"; + + +struct CalcView::CalcKey { + char label[8]; + char code[8]; + char keymap[4]; + uint32 flags; + float width; +}; + + +CalcView *CalcView::Instantiate(BMessage *archive) +{ + if (!validate_instantiation(archive, "CalcView")) + return NULL; + + return new CalcView(archive); +} + + +CalcView::CalcView(BRect frame, rgb_color rgbBaseColor) + : BView(frame, "calc-view", B_FOLLOW_ALL_SIDES, + B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS), + fColums(5), + fRows(4), + + fBaseColor(rgbBaseColor), + fExpressionBGColor((rgb_color){ 0, 0, 0, 255 }), + + fWidth(frame.Width()), + fHeight(frame.Height()), + + fKeypadDescription(strdup(kDefaultKeypadDescription)), + fKeypad(NULL), + + fExpression(""), + + fAboutItem(NULL), + fOptionsItem(NULL), + fPopUpMenu(NULL), + + fOptions(new CalcOptions()), + fOptionsWindow(NULL) +{ + // tell the app server not to erase our b/g + SetViewColor(B_TRANSPARENT_32_BIT); + + // parse calculator description + _ParseCalcDesc(fKeypadDescription); + + // colorize based on base color. + _Colorize(); + + // create pop-up menu system + _CreatePopUpMenu(); +} + + +CalcView::CalcView(BMessage* archive) + : BView(archive), + fColums(5), + fRows(4), + + fBaseColor((rgb_color){ 128, 128, 128, 255 }), + fExpressionBGColor((rgb_color){ 0, 0, 0, 255 }), + + fKeypadDescription(strdup(kDefaultKeypadDescription)), + fKeypad(NULL), + + fExpression(""), + + fAboutItem(NULL), + fOptionsItem(NULL), + fPopUpMenu(NULL), + + fOptions(new CalcOptions()), + fOptionsWindow(NULL) +{ + // read data from archive + LoadSettings(archive); + + // load frame dimensions + BRect frame = Frame(); + fWidth = frame.Width(); + fHeight = frame.Height(); + + // create pop-up menu system + _CreatePopUpMenu(); +} + + +CalcView::~CalcView() +{ + delete fKeypad; + delete fOptions; + free(fKeypadDescription); +} + + +void +CalcView::MessageReceived(BMessage* message) +{ + //message->PrintToStream(); + + // check if message was dropped + if (message->WasDropped()) { + // pass message on to paste + if (message->IsSourceRemote()) + Paste(message); + + } else { + + // act on posted message type + switch (message->what) { + + // handle "cut" + case B_CUT: + Cut(); + break; + + // handle copy + case B_COPY: + Copy(); + break; + + // handle paste + case B_PASTE: + // access system clipboard + if (be_clipboard->Lock()){ + BMessage *clipper = be_clipboard->Data(); + //clipper->PrintToStream(); + Paste(clipper); + be_clipboard->Unlock(); + } // end if + break; + + // (replicant) about box requested + case B_ABOUT_REQUESTED: + AboutRequested(); + break; + + // calculator options window requested + case K_OPTIONS_REQUESTED: { + BRect frame(30.0f, 50.0f, 230.0, 200.0); + fOptionsWindow = new CalcOptionsWindow(frame, fOptions); + fOptionsWindow->Show(); + break; + } + + default: + BView::MessageReceived(message); + break; + } + } +} + + +void +CalcView::Draw(BRect updateRect) +{ + rgb_color rgbWhite = { 255, 255, 255, 0 }; + + // calculate grid sizes + float sizeDisp = fHeight * K_DISPLAY_YPROP; + float sizeCol = fWidth / (float)fColums; + float sizeRow = (fHeight - sizeDisp) / (float)fRows; + + // setup areas + BRect displayRect(0.0, 0.0, fWidth, sizeDisp); + BRect keypadRect(0.0, sizeDisp, fWidth, fHeight); + + SetFont(be_bold_font); + + // ****** DISPLAY Area + if (updateRect.Intersects(displayRect)){ + + // paint display b/g + SetHighColor(fExpressionBGColor); + FillRect(updateRect & displayRect); + + // render display text + SetHighColor(rgbWhite); + SetLowColor(fExpressionBGColor); + SetDrawingMode(B_OP_COPY); + SetFontSize(sizeDisp * K_FONT_YPROP); + float baselineOffset = sizeDisp * (1.0 - K_FONT_YPROP) * 0.5; + DrawString(fExpression.String(), + BPoint(fWidth - StringWidth(fExpression.String()), + sizeDisp - baselineOffset)); + + } // end if + + + // ****** KEYPAD Area + if (updateRect.Intersects(keypadRect)){ + + // paint keypad b/g + SetHighColor(fBaseColor); + FillRect(updateRect & keypadRect); + + // render key main grid + BeginLineArray((fColums + fRows) << 1 + 1); + + // render cols + AddLine(BPoint(0.0, sizeDisp), + BPoint(0.0, fHeight), + fLightColor); + for (int col = 1; col < fColums; col++){ + AddLine(BPoint(col * sizeCol - 1.0, sizeDisp), + BPoint(col * sizeCol - 1.0, fHeight), + fDarkColor); + AddLine(BPoint(col * sizeCol, sizeDisp), + BPoint(col * sizeCol, fHeight), + fLightColor); + } // end for + AddLine(BPoint(fColums * sizeCol, sizeDisp), + BPoint(fColums * sizeCol, fHeight), + fDarkColor); + + // render rows + for (int row = 0; row < fRows; row++){ + AddLine(BPoint(0.0, sizeDisp + row * sizeRow - 1.0), + BPoint(fWidth, sizeDisp + row * sizeRow - 1.0), + fDarkColor); + AddLine(BPoint(0.0, sizeDisp + row * sizeRow), + BPoint(fWidth, sizeDisp + row * sizeRow), + fLightColor); + } // end for + AddLine(BPoint(0.0, sizeDisp + fRows * sizeRow), + BPoint(fWidth, sizeDisp + fRows * sizeRow), + fDarkColor); + + // main grid complete + EndLineArray(); + + // render key symbols + float halfSizeCol = sizeCol * 0.5f; + SetHighColor(rgbWhite); + SetLowColor(fBaseColor); + SetDrawingMode(B_OP_COPY); + SetFontSize(((fHeight - sizeDisp)/(float)fRows) * K_FONT_YPROP); + float baselineOffset = ((fHeight - sizeDisp)/(float)fRows) + * (1.0 - K_FONT_YPROP) * 0.5; + CalcKey *key = fKeypad; + for (int row = 0; row < fRows; row++){ + for (int col = 0; col < fColums; col++){ + float halfSymbolWidth = StringWidth(key->label) * 0.5f; + DrawString(key->label, + BPoint(col * sizeCol + halfSizeCol - halfSymbolWidth, + sizeDisp + (row + 1) * sizeRow - baselineOffset)); + key++; + } // end for + } // end for + + } // end if + +} + + +void +CalcView::MouseDown(BPoint point) +{ + // ensure this view is the current focus + MakeFocus(); + + // read mouse buttons state + int32 buttons = 0; + Window()->CurrentMessage()->FindInt32("buttons", &buttons); + + // display popup menu if not primary mouse button + if ((B_PRIMARY_MOUSE_BUTTON & buttons) == 0) { + BMenuItem* selected; + if ((selected = fPopUpMenu->Go(ConvertToScreen(point))) != NULL) + MessageReceived(selected->Message()); + return; + } + + // calculate grid sizes + float sizeDisp = fHeight * K_DISPLAY_YPROP; + float sizeCol = fWidth / (float)fColums; + float sizeRow = (fHeight - sizeDisp) / (float)fRows; + + // click on display, initiate drag if appropriate + if ((point.y - sizeDisp) < 0.0) { + // only drag if there's some text + if (fExpression.Length() > 0){ + // assemble drag message + BMessage dragmsg(B_MIME_DATA); + dragmsg.AddData("text/plain", + B_MIME_TYPE, + fExpression.String(), + fExpression.Length()); + + // initiate drag & drop + SetFontSize(sizeDisp * K_FONT_YPROP); + float left = fWidth; + float textWidth = StringWidth(fExpression.String()); + if (textWidth < fWidth) + left -= textWidth; + else + left = 0; + BRect displayRect(left, 0.0, fWidth, sizeDisp); + DragMessage(&dragmsg, displayRect); + } + } else { + // click on keypad + + // calculate location within grid + int gridCol = (int)(point.x / sizeCol); + int gridRow = (int)((point.y - sizeDisp) / sizeRow); + + // check limits + if ((gridCol >= 0) && (gridCol < fColums) && + (gridRow >= 0) && (gridRow < fRows)) { + + // process key press + _PressKey(gridRow * fColums + gridCol); + } + } +} + + +void +CalcView::KeyDown(const char *bytes, int32 numBytes) +{ + // if single byte character... + if (numBytes == 1) { + + //printf("Key pressed: %c\n", bytes[0]); + + switch (bytes[0]) { + + case B_ENTER: + // translate to evaluate key + _PressKey("="); + break; + + case B_LEFT_ARROW: + case B_BACKSPACE: + // translate to backspace key + _PressKey("BS"); + break; + + case B_SPACE: + case B_ESCAPE: + case 'c': // hack! + // translate to clear key + _PressKey("C"); + break; + + // bracket translation + case '[': + case '{': + _PressKey("("); + break; + + case ']': + case '}': + _PressKey(")"); + break; + + default: { + // scan the keymap array for match + int keys = fRows * fColums; + for (int i=0; imAutoNumLock) { + set_keyboard_locks(B_NUM_LOCK | + (modifiers() & (B_CAPS_LOCK | B_SCROLL_LOCK))); + } + } + + // pass on request to parent + BView::MakeFocus(focused); +} + + +void +CalcView::FrameResized(float width, float height) +{ + fWidth = width; + fHeight = height; +} + + +void +CalcView::AboutRequested() +{ +/* + BRect frame(100.0, 100.0, 300.0, 280.0); + new RAboutBox(frame, "DeskCalc v1.3.0", "code: timmy\nqa: rosalie\n\n" + B_UTF8_COPYRIGHT"1997 R3 Software Ltd.\nAll Rights Reserved.", + RAboutBox::R_SHOW_ON_OPEN); +*/ +} + + +status_t +CalcView::Archive(BMessage* archive, bool deep) const +{ + // passed on request to parent + status_t ret = BView::Archive(archive, deep); + + + // save app signature for replicant add-on loading + if (ret == B_OK) + ret = archive->AddString("add_on", kAppSig); + + // save all the options + if (ret == B_OK) + ret = SaveSettings(archive); + + // add class info last + if (ret == B_OK) + ret = archive->AddString("class", "CalcView"); + + return ret; +} + + +void +CalcView::Cut() +{ + Copy(); // copy data to clipboard + fExpression.SetTo(""); // remove data + + _InvalidateExpression(); +} + + +void +CalcView::Copy() +{ + // access system clipboard + if (be_clipboard->Lock()) { + be_clipboard->Clear(); + BMessage *clipper = be_clipboard->Data(); + clipper->what = B_MIME_DATA; + // TODO: should check return for errors! + clipper->AddData("text/plain", + B_MIME_TYPE, + fExpression.String(), + fExpression.Length()); + //clipper->PrintToStream(); + be_clipboard->Commit(); + be_clipboard->Unlock(); + } +} + + +void +CalcView::Paste(BMessage *message) +{ + // handle color drops first + // read incoming color + const rgb_color* dropColor = NULL; + ssize_t dataSize; + if (message->FindData("RGBColor", + B_RGB_COLOR_TYPE, + (const void**)&dropColor, + &dataSize) == B_OK + && dataSize == sizeof(rgb_color)) { + + // calculate view relative drop point + BPoint dropPoint = ConvertFromScreen(message->DropPoint()); + + // calculate current keypad area + float sizeDisp = fHeight * K_DISPLAY_YPROP; + BRect keypadRect(0.0, sizeDisp, fWidth, fHeight); + + // check location of color drop + if (keypadRect.Contains(dropPoint) && dropColor) { + fBaseColor = *dropColor; + _Colorize(); + // redraw keypad + Invalidate(keypadRect); + } + + } else { + // look for text/plain MIME data + const char* text; + ssize_t numBytes; + if (message->FindData("text/plain", + B_MIME_TYPE, + (const void**)&text, + &numBytes) == B_OK) { + fExpression.Append(text, numBytes); + _InvalidateExpression(); + } + } +} + + +status_t +CalcView::LoadSettings(BMessage* archive) +{ + if (!archive) + return B_BAD_VALUE; + + // record calculator description + const char* calcDesc; + if (archive->FindString("calcDesc", &calcDesc) < B_OK) + calcDesc = kDefaultKeypadDescription; + + // save calculator description for reference + free(fKeypadDescription); + fKeypadDescription = strdup(calcDesc); + + // read grid dimensions + if (archive->FindInt16("cols", &fColums) < B_OK) + fColums = 5; + if (archive->FindInt16("rows", &fRows) < B_OK) + fRows = 4; + + // read color scheme + const rgb_color* color; + ssize_t size; + if (archive->FindData("rgbBaseColor", B_RGB_COLOR_TYPE, + (const void**)&color, &size) < B_OK + || size != sizeof(rgb_color)) { + fBaseColor = (rgb_color){ 128, 128, 128, 255 }; + puts("Missing rgbBaseColor from CalcView archive!\n"); + } else { + fBaseColor = *color; + } + + if (archive->FindData("rgbDisplay", B_RGB_COLOR_TYPE, + (const void**)&color, &size) < B_OK + || size != sizeof(rgb_color)) { + fExpressionBGColor = (rgb_color){ 0, 0, 0, 255 }; + puts("Missing rgbBaseColor from CalcView archive!\n"); + } else { + fExpressionBGColor = *color; + } + + // load options + const CalcOptions* options; + if (archive->FindData("options", B_RAW_TYPE, + (const void**)&options, &size) == B_OK + && size == sizeof(CalcOptions)) { + memcpy(fOptions, options, size); + } else { + puts("Missing options from CalcView archive.\n"); + } + + // load display text + const char* display; + if (archive->FindString("displayText", &display) < B_OK) { + puts("Missing display text from CalcView archive.\n"); + } else { + // init expression text + fExpression = display; + } + + // parse calculator description + _ParseCalcDesc(fKeypadDescription); + + // colorize based on base color. + _Colorize(); + + return B_OK; +} + + +status_t +CalcView::SaveSettings(BMessage* archive) const +{ + status_t ret = archive ? B_OK : B_BAD_VALUE; + + // record grid dimensions + if (ret == B_OK) + ret = archive->AddInt16("cols", fColums); + if (ret == B_OK) + ret = archive->AddInt16("rows", fRows); + + // record color scheme + if (ret == B_OK) + ret = archive->AddData("rgbBaseColor", B_RGB_COLOR_TYPE, + &fBaseColor, sizeof(rgb_color)); + if (ret == B_OK) + ret = archive->AddData("rgbDisplay", B_RGB_COLOR_TYPE, + &fExpressionBGColor, sizeof(rgb_color)); + + // record current options + if (ret == B_OK) + ret = archive->AddData("options", B_RAW_TYPE, + fOptions, sizeof(CalcOptions)); + + // record display text + if (ret == B_OK) + ret = archive->AddString("displayText", fExpression.String()); + + // record calculator description + if (ret == B_OK) + ret = archive->AddString("calcDesc", fKeypadDescription); + + return ret; +} + + +// #pragma mark - + + +void +CalcView::_ParseCalcDesc(const char* keypadDescription) +{ + // TODO: should calculate dimensions from desc here! + fKeypad = new CalcKey[fRows * fColums]; + + // scan through calculator description and assemble keypad + bool scanFlag = true; + CalcKey *key = fKeypad; + const char *p = keypadDescription; + while (scanFlag) { + // copy label + char *l = key->label; + while (!isspace(*p)) + *l++ = *p++; + *l = '\0'; + + // set code + if (strcmp(key->label, "=") == 0) + strcpy(key->code, "\n"); + else + strcpy(key->code, key->label); + + // set keymap + if (strlen(key->label)==1) { + strcpy(key->keymap, key->label); + } else { + *key->keymap = '\0'; + } // end if + + // advance + while (isspace(*p)) + ++p; + key++; + + // check desc termination + if (*p == '\0') + scanFlag = false; + } +} + + +void +CalcView::_PressKey(int key) +{ + assert(key < (fRows * fColums)); + assert(key >= 0); + + // check for backspace + if (strcmp(fKeypad[key].label, "BS") == 0) { + int32 length = fExpression.Length(); + if (length > 0) { + fExpression.Remove(length - 1, 1); + } else { + beep(); + return; // no need to redraw + } + } else if (strcmp(fKeypad[key].label, "C") == 0) { + // C means clear + fExpression.SetTo(""); + } else { + // append to display text + fExpression.Append(fKeypad[key].code); + + // check for evaluation order + if (fKeypad[key].code[0] == '\n') { + _Evaluate(); + } else { + // audio feedback + if (fOptions->mAudioFeedback) { + BEntry zimp("key.AIFF"); + entry_ref zimp_ref; + zimp.GetRef(&zimp_ref); + play_sound(&zimp_ref, true, false, true); + } + } + } + + // redraw display + _InvalidateExpression(); +} + + +void +CalcView::_PressKey(char *label) +{ + int keys = fRows * fColums; + for (int i = 0; i < keys; i++) { + if (strcmp(fKeypad[i].label, label) == 0) { + _PressKey(i); + return; + } + } +} + + +void +CalcView::_Colorize() +{ + // calculate light and dark color from base color + fLightColor.red = (uint8)(fBaseColor.red * 1.25); + fLightColor.green = (uint8)(fBaseColor.green * 1.25); + fLightColor.blue = (uint8)(fBaseColor.blue * 1.25); + fLightColor.alpha = 255; + + fDarkColor.red = (uint8)(fBaseColor.red * 0.75); + fDarkColor.green = (uint8)(fBaseColor.green * 0.75); + fDarkColor.blue = (uint8)(fBaseColor.blue * 0.75); + fDarkColor.alpha = 255; +} + + +void +CalcView::_Evaluate() +{ + const double EXP_SWITCH_HI = 1e12; // # digits to switch from std->exp form + const double EXP_SWITCH_LO = 1e-12; + + if (fExpression.Length() == 0) { + beep(); + return; + } + + // audio feedback + if (fOptions->mAudioFeedback) { + BEntry zimp("zimp.AIFF"); + entry_ref zimp_ref; + zimp.GetRef(&zimp_ref); + play_sound(&zimp_ref, true, false, false); + } + +//printf("evaluate: %s\n", fExpression.String()); + + // evaluate expression + Expression parser; + + char* tmpstr = strdup(fExpression.String()); + char* start = tmpstr; + char* end = start + fExpression.Length() - 1; + double value = 0.0; + + try { + value = parser.Eval(start, end, true); + + } catch (const char* error) { + fExpression = error; + _InvalidateExpression(); + return; + } + + free(tmpstr); + +//printf(" -> value: %f\n", value); + + // beautify the expression + // TODO: see if this is necessary at all + char buf[64]; + if (value == 0) { + strcpy(buf, "0"); + } else if (((value < EXP_SWITCH_HI) && (value > EXP_SWITCH_LO)) || + ((value > -EXP_SWITCH_HI) && (value < -EXP_SWITCH_LO))) { + // print in std form + sprintf(buf, "%9lf", value); + + // hack to remove surplus zeros! + if (strchr(buf, '.')) { + int32 i = strlen(buf) - 1; + for (; i > 0; i--) { + if (buf[i] == '0') + buf[i] = '\0'; + else + break; + } + if (buf[i] == '.') + buf[i] = '\0'; + } + } else { + // print in exponential form + sprintf(buf, "%le", value); + } + + // render new result to display + fExpression = buf; + + // redraw display + _InvalidateExpression(); +} + + +void +CalcView::_CreatePopUpMenu() +{ + // construct items + fAboutItem = new BMenuItem("About Calculator...", + new BMessage(B_ABOUT_REQUESTED)); + fOptionsItem = new BMenuItem("Options...", + new BMessage(K_OPTIONS_REQUESTED)); + + // construct menu + fPopUpMenu = new BPopUpMenu("pop-up", false, false); + fPopUpMenu->AddItem(fAboutItem); + fPopUpMenu->AddItem(fOptionsItem); +} + + +void +CalcView::_InvalidateExpression() +{ + float sizeDisp = fHeight * K_DISPLAY_YPROP; + BRect displayRect(0.0, 0.0, fWidth, sizeDisp); + Invalidate(displayRect); +} + diff --git a/src/apps/deskcalc/CalcView.h b/src/apps/deskcalc/CalcView.h new file mode 100644 index 0000000000..1c07ee418d --- /dev/null +++ b/src/apps/deskcalc/CalcView.h @@ -0,0 +1,109 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Copyright 1997, 1998 R3 Software Ltd. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Timothy Wayper + * Stephan Aßmus + */ + +#ifndef _CALC_VIEW_H +#define _CALC_VIEW_H + +#include +#include + +class BString; +class BMenuItem; +class CalcOptions; +class CalcOptionsWindow; + +_EXPORT +class CalcView : public BView { + public: + + static CalcView* Instantiate(BMessage* archive); + + + CalcView(BRect frame, + rgb_color rgbBaseColor); + + CalcView(BMessage* archive); + + virtual ~CalcView(); + + + virtual void MessageReceived(BMessage* message); + virtual void Draw(BRect updateRect); + virtual void MouseDown(BPoint point); + virtual void KeyDown(const char* bytes, int32 numBytes); + virtual void MakeFocus(bool focused = true); + virtual void FrameResized(float width, float height); + + // Present about box for view (replicant). + virtual void AboutRequested(); + + // Archive this view. + virtual status_t Archive(BMessage* archive, bool deep) const; + + // Cut contents of view to system clipboard. + void Cut(); + + // Copy contents of view to system clipboard. + void Copy(); + + // Paste contents of system clipboard to view. + void Paste(BMessage *message); + + // Load/Save current settings + status_t LoadSettings(BMessage* archive); + status_t SaveSettings(BMessage* archive) const; + + private: + void _ParseCalcDesc(const char* keypadDescription); + + void _PressKey(int key); + void _PressKey(char* label); + + void _Colorize(); + + void _Evaluate(); + + void _CreatePopUpMenu(); + void _InvalidateExpression(); + + // grid dimensions + int16 fColums; + int16 fRows; + + // color scheme + rgb_color fBaseColor; + rgb_color fLightColor; + rgb_color fDarkColor; + rgb_color fExpressionBGColor; + + // view dimensions + float fWidth; + float fHeight; + + // keypad grid + struct CalcKey; + + char* fKeypadDescription; + CalcKey* fKeypad; + + // display text + BString fExpression; + + // pop-up context menu. + BMenuItem* fAboutItem; + BMenuItem* fOptionsItem; + BPopUpMenu* fPopUpMenu; + + // calculator options. + CalcOptions* fOptions; + CalcOptionsWindow* fOptionsWindow; +}; + +#endif // _CALC_VIEW_H diff --git a/src/apps/deskcalc/CalcWindow.cpp b/src/apps/deskcalc/CalcWindow.cpp new file mode 100644 index 0000000000..17ddd6febc --- /dev/null +++ b/src/apps/deskcalc/CalcWindow.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Copyright 1997, 1998 R3 Software Ltd. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Timothy Wayper + * Stephan Aßmus + */ + +#include "CalcWindow.h" + +#include +#include +#include + +#include +#include +#include + +#include "CalcView.h" + + +static const char* kWindowTitle = "Desk Calculator"; + + +CalcWindow::CalcWindow(BRect frame) + : BWindow(frame, kWindowTitle, B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS) +{ + // create calculator view with calculator description and + // desktop background color + BScreen screen(this); + rgb_color baseColor = screen.DesktopColor(); + + frame.OffsetTo(B_ORIGIN); + fCalcView = new CalcView(frame, baseColor); + + // create replicant dragger + frame.top = frame.bottom - 7.0f; + frame.left = frame.right - 7.0f; + BDragger* dragger = new BDragger(frame, fCalcView, + B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); + + // attach views + AddChild(fCalcView); + fCalcView->AddChild(dragger); +} + + +CalcWindow::~CalcWindow() +{ +} + + +void +CalcWindow::Show() +{ + // NOTE: done here because the CalcView + // turns on numlock if the options say so... + // so we want to call MakeFocus() after + // the options have been read for sure... + fCalcView->MakeFocus(); + BWindow::Show(); +} + + +bool +CalcWindow::QuitRequested() +{ + be_app->PostMessage(B_QUIT_REQUESTED); + Hide(); + + // NOTE: don't quit, since the app needs us + // for saving settings yet... + return false; +} + + +bool +CalcWindow::LoadSettings(BMessage* archive) +{ + status_t ret = fCalcView->LoadSettings(archive); + + if (ret < B_OK) + return false; + + BRect frame; + ret = archive->FindRect("window frame", &frame); + if (ret < B_OK) + return false; + + SetFrame(frame); + + return true; +} + + +status_t +CalcWindow::SaveSettings(BMessage* archive) const +{ + status_t ret = archive->AddRect("window frame", Frame()); + if (ret < B_OK) + return ret; + + return fCalcView->SaveSettings(archive); +} + + +void +CalcWindow::SetFrame(BRect frame, bool forceCenter) +{ + // make sure window frame is on screen (center, if not) + BScreen screen(this); + BRect screenFrame = screen.Frame(); + if (forceCenter || !screenFrame.Contains(frame)) { + float left = (screenFrame.Width() - frame.Width()) / 2.0; + float top = (screenFrame.Height() - frame.Height()) / 2.0; + left += screenFrame.left; + top += screenFrame.top; + frame.OffsetTo(left, top); + } + + MoveTo(frame.left, frame.top); + ResizeTo(frame.Width(), frame.Height()); +} diff --git a/src/apps/deskcalc/CalcWindow.h b/src/apps/deskcalc/CalcWindow.h new file mode 100644 index 0000000000..78431c7fce --- /dev/null +++ b/src/apps/deskcalc/CalcWindow.h @@ -0,0 +1,37 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Copyright 1997, 1998 R3 Software Ltd. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Timothy Wayper + * Stephan Aßmus + */ + + +#ifndef _CALC_WINDOW_H +#define _CALC_WINDOW_H + +#include + +class CalcView; + +class CalcWindow : public BWindow { + public: + CalcWindow(BRect frame); + virtual ~CalcWindow(); + + virtual void Show(); + virtual bool QuitRequested(); + + bool LoadSettings(BMessage* archive); + status_t SaveSettings(BMessage* archive) const; + + void SetFrame(BRect frame, + bool forceCenter = false); + + private: + CalcView* fCalcView; +}; + +#endif // _CALC_WINDOW_H diff --git a/src/apps/deskcalc/DeskCalc.cpp b/src/apps/deskcalc/DeskCalc.cpp new file mode 100644 index 0000000000..e46648bb61 --- /dev/null +++ b/src/apps/deskcalc/DeskCalc.cpp @@ -0,0 +1,22 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Copyright 1997, 1998 R3 Software Ltd. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Timothy Wayper + * Stephan Aßmus + */ + +#include "CalcApplication.h" + +int +main(int argc, char* argv[]) +{ + CalcApplication* app = new CalcApplication(); + + app->Run(); + delete app; + + return 0; +} diff --git a/src/apps/deskcalc/DeskCalc.cpp_cli b/src/apps/deskcalc/DeskCalc.cpp_cli new file mode 100644 index 0000000000..8edc1a5ac1 --- /dev/null +++ b/src/apps/deskcalc/DeskCalc.cpp_cli @@ -0,0 +1,281 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Copyright 2004 Daniel Wallner. All Rights Reserved. + * Copyright 1997, 1998 R3 Software Ltd. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Daniel Wallner + * Timothy Wayper + * Stephan Aßmus + */ + +#include +#include +#include +#include +#include +#include + +//#include +//#include +//#include + +#include "CalcApplication.h" + +using namespace std; +//using namespace xlds; + +void Usage() +{ + cerr << "Expression calculator" << endl << endl; + cerr << " usage: expcalc [expression] ... [options and arguments]" << endl << endl; + cerr << " -h print this message" << endl; + cerr << " -e even thousands exponent output" << endl; + cerr << " -p SI prefix output" << endl; + cerr << " -n newline after output" << endl << endl; + cerr << " If no arguments are given, expcalc enters interactive mode." << endl; + cerr << " You can combine short flags, so `-e -n' means the same as -en or -ne." << endl; +} + +int GetString(string &instring) +{ + instring.assign(""); + + char cchar; + + cchar = getc(stdin); + + while (cchar != EOF && cchar != 10) + { + instring.append(1, cchar); + cchar = getc(stdin); + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + if (argc == 1) { + CalcApplication* app = new CalcApplication(); + + app->Run(); + delete app; + + return 0; + } + + + try + { + char *expression = NULL; + char outputMode = 0; + bool newline = false; + + while (doptind < argc) + { + if (argv[doptind][0] != '-' || strlen(argv[doptind]) <= 1) + { + // File argument + if (expression) + throw "Multiple expressions not supported"; + + expression = argv[doptind]; + + doptind++; + } + else + { + // Option + int opt = dgetopt(argc, argv, ":hepn"); + if (opt != -1) + { + switch (opt) + { + case 'h': + Usage(); + return 1; + break; + + case 'e': + outputMode = 1; + break; + + case 'p': + outputMode = 2; + break; + + case 'n': + newline = true; + break; + + case ':': // No argument + cerr << "Option -" << (char)doptopt << " requires an argument" << endl; + throw ""; + + case '?': + default: + cerr << "Unknown option -" << (char)doptopt << endl; + throw ""; + } + } + } + } + + string instring; + string laststring; + string stored; + + Expression exp; + + if (expression) + { + double temp = exp.Eval(expression, expression + strlen(expression)); + + if (!exp.Error()) + { + cout << exp.dtostr(temp, 14, outputMode).c_str(); + if (newline) + cout << endl; + } + else + { + char *i; + cout << expression << endl; + for (i = expression; i < exp.Error(); i++) + cout << " "; + cout << "^\nError in input\n\n"; + } + } + else + { + cout << "Expression Calculator\n"; + cout << "Copyright (c) Daniel Wallner 2004.\n"; + cout << "Type exit to end program or ? for help.\n\n"; + + cout << "ec> "; + + if (GetString(instring)) + return 0; + + bool rec = false; + + while (strcmp(instring.c_str(), "exit")) + { + if (!strcmp(instring.c_str(), "?")) + { + cout << "\nCommands l : repeat last calculation (used with ans)\n"; + cout << " sto : store last expression\n"; + cout << " rec : recall stored expression\n"; + cout << " d : set trigonometric functions to degrees\n"; + cout << " r : set trigonometric functions to radians (default)\n"; + cout << " e : toggle exponent output mode\n"; + cout << " p : set prefix output mode\n"; + cout << " exit : exit program\n"; + cout << "\nFunctions sin( : sinus\n"; + cout << " cos( : cosinus\n"; + cout << " tan( : tangens\n"; + cout << " asin( : arcsin\n"; + cout << " acos( : arccos\n"; + cout << " atan( : arctan\n"; + cout << " lg( : logarithm with base 10\n"; + cout << " ln( : logarithm with base e\n"; + cout << " sqrt( : sqare root\n"; + cout << " ans : last answer\n"; + cout << " ^,*,+,-,/ : as usual :-)\n"; + cout << "\nConstants e : the constant e\n"; + cout << " pi : the constant pi\n\n"; + + rec=1; + instring = laststring; + } + + if (!strcmp(instring.c_str(), "l")) + { + instring = laststring; + } + + if (!strcmp(instring.c_str(), "sto")) + { + stored = laststring; + cout << "\nLast expression stored!\n\n"; + rec = 1; + instring = laststring; + } + + if (!strcmp(instring.c_str(), "rec")) + { + instring = stored; + } + + if (!strcmp(instring.c_str(), "d")) + { + exp.SetAglf(M_PI / 180.); + rec = 1; + cout << "\nDegree mode!\n\n"; + instring = laststring; + } + + if (!strcmp(instring.c_str(), "r")) + { + exp.SetAglf(1.); + rec = 1; + cout << "\nRadian mode!\n\n"; + instring = laststring; + } + + if (!strcmp(instring.c_str(), "e")) + { + outputMode = (outputMode + 1) % 2; + cout << "\nExponent output mode" << outputMode << "!\n\n"; + instring = laststring; + } + + if (!strcmp(instring.c_str(), "p")) + { + outputMode = 2; + cout << "\nPrefix output mode!\n\n"; + instring = laststring; + } + + if (!instring.size()) + rec = 1; + + if (rec == 0) + { + double temp = exp.Eval(instring.c_str(), instring.c_str() + instring.size()); + + if (!exp.Error()) + { + cout << "\n" << instring << " = "; + cout << exp.dtostr(temp, 14, outputMode).c_str(); + cout << "\n\n"; + exp.SetConstant("ans", temp); + } + else + { + const char *i; + for (i = instring.c_str(); i < exp.Error(); i++) + cout << " "; + cout << " "; + cout << "^\nError in input\n\n"; + } + } + + cout << "ec> "; + laststring = instring; + if (GetString(instring)) + return 0; + rec = 0; + } + } + } + catch (const char * str) + { + cerr << str << endl; + return 1; + } + + return 0; +} diff --git a/src/apps/deskcalc/DeskCalc.h b/src/apps/deskcalc/DeskCalc.h new file mode 100644 index 0000000000..6d8afa746e --- /dev/null +++ b/src/apps/deskcalc/DeskCalc.h @@ -0,0 +1,8 @@ +/* +** /boot/home/projects/develop/ThirdParty/Haiku/src/apps/deskcalc/DeskCalc.h +** +** Automatically generated by BResourceParser on +** Tuesday, June 06, 2006 at 17:38:14. +** +*/ + diff --git a/src/apps/deskcalc/DeskCalc.rdef b/src/apps/deskcalc/DeskCalc.rdef new file mode 100644 index 0000000000..1ce6fcffb1 --- /dev/null +++ b/src/apps/deskcalc/DeskCalc.rdef @@ -0,0 +1,74 @@ + +resource(1, "BEOS:APP_SIG") #'MIMS' "application/x-vnd.Haiku-DeskCalc"; + +resource app_version +{ + major = 2, + middle = 1, + minor = 0, + + /* 0 = development 1 = alpha 2 = beta + 3 = gamma 4 = golden master 5 = final */ + variety = 1, + + internal = 1, + + short_info = "DeskCalc", + long_info = "DeskCalc 2.1.0 ©2006 Haiku" +}; + +resource(101, "BEOS:L:STD_ICON") #'ICON' array { + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0013130000FFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFF0013060613130000FFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFF0013060808080613130000FFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFF0013080808083408060613130000FFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFF0013080808083408083408060613130000FFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFF00140F1313080808083408343408060613130000FFFF" + $"FFFFFFFFFFFFFFFFFF001413140F0F131308080808340808340808130000FFFF" + $"FFFFFFFFFFFFFFFF00130F13130F140F0F13130808083434080813000800FFFF" + $"FFFFFFFFFFFFFF001414140F0F1413140F0F0F13130808080813000808000808" + $"FFFFFFFFFFFF000F1313130F140F0F0F1313130F0F1313081300080800080808" + $"FFFFFFFFFF0014140F0F0F1413130F0F0F130F14140F0F1300080800080808FF" + $"FFFFFFFF00141313140F0F0F130F1413130F0F1313131300080800080808FFFF" + $"FFFFFF00140F13130F1413130F0F13130F14140F0F1300080800080808FFFFFF" + $"FFFF001413140F0F1413130F13130F0F131413131300080800080808FFFFFFFF" + $"FF00001313130F140F0F0F1313130F140F0F131300080800080808FFFFFFFFFF" + $"FF000E04040F1413130F0F0F130F141314130F00080800080808FFFFFFFFFFFF" + $"FF000E0E0E0404130F1313130F0F1313131300080800080808FFFFFFFFFFFFFF" + $"FFFF000E0E0E0E040413130F14140F0F1300080800080808FFFFFFFFFFFFFFFF" + $"FFFFFF00000E0E0E0E0404141313131300080800080808FFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFF00000E0E0E0E0404131300080800080808FFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFF00000E0E0E0E0400080800080808FFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFF00000E0E0E000800080808FFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFF00000E0000080808FFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFF0000080808FFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +}; + +resource(101, "BEOS:M:STD_ICON") #'MICN' array { + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFF0000FFFFFFFFFFFF" + $"FFFFFFFFFFFFFF0008080000FFFFFFFF" + $"FFFFFFFFFFFF0008080834060000FFFF" + $"FFFFFFFFFF00140F13080808340800FF" + $"FFFFFFFF00140F130F0F130808000808" + $"FFFFFF000F0F130F0F0F140F00080808" + $"FFFF00130F130F130F140F00080808FF" + $"FF00130F0F0F130F0F1300080808FFFF" + $"FF000E040F130F131300080808FFFFFF" + $"FFFF000E0E04131300080808FFFFFFFF" + $"FFFFFF00000E0E00080808FFFFFFFFFF" + $"FFFFFFFFFF0000000808FFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +}; + +resource(1, "BEOS:APP_FLAGS") (#'APPF') $"01000000"; diff --git a/src/apps/deskcalc/Jamfile b/src/apps/deskcalc/Jamfile new file mode 100644 index 0000000000..90de5b4801 --- /dev/null +++ b/src/apps/deskcalc/Jamfile @@ -0,0 +1,21 @@ +SubDir HAIKU_TOP src apps deskcalc ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +AddSubDirSupportedPlatforms libbe_test ; + +Application DeskCalc : + CalcApplication.cpp + CalcOptionsWindow.cpp + CalcView.cpp + CalcWindow.cpp + DeskCalc.cpp + Parser.cpp + : be $(TARGET_LIBSTDC++) media + : DeskCalc.rdef + ; + +if $(TARGET_PLATFORM) = libbe_test { + HaikuInstall install-test-apps : $(HAIKU_APP_TEST_DIR) : DeskCalc + : tests!apps ; +} diff --git a/src/apps/deskcalc/Parser.cpp b/src/apps/deskcalc/Parser.cpp new file mode 100644 index 0000000000..0b1d4067fb --- /dev/null +++ b/src/apps/deskcalc/Parser.cpp @@ -0,0 +1,952 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Copyright 2004 Daniel Wallner. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Daniel Wallner + */ + +#include "Parser.h" + +#include +#include +#include +#include + +#define NOLLF 5e-16 + +using namespace std; + +//LineParser::LineParser(istream *stream): +// m_stream(stream), +// m_currentLine(0) +//{ +//} +// +//void LineParser::AddSeparator(const char c) +//{ +// m_separators.push_back(c); +//} +// +//size_t LineParser::SetToNext(bool toUpperCase) +//{ +// m_line.erase(m_line.begin(), m_line.end()); +// m_unparsed.assign(""); +// +// // Skip newlines +// while (m_stream->peek() == 0xA || m_stream->peek() == 0xD) +// m_stream->get(); +// +// while (!m_stream->eof() && !m_stream->fail() && m_stream->peek() != 0xA && m_stream->peek() != 0xD) +// { +// string param; +// +// // Skip whitespaces +// while (m_stream->peek() == ' ' || m_stream->peek() == '\t') +// m_unparsed.append(1, char(m_stream->get())); +//// m_separators.push_back(c); add check for separators !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// if (m_stream->peek() == '"') +// { +// // Skip first " +// m_unparsed.append(1, char(m_stream->get())); +// +// int c; +// // Read until " +// while (!m_stream->fail() && m_stream->peek() != 0xA && m_stream->peek() != 0xD && +// (c = m_stream->get()) != '"' && !m_stream->eof()) +// { +// m_unparsed.append(1, char(c)); +// if (toUpperCase) +// param.append(1, char(toupper(c))); +// else +// param.append(1, char(c)); +// } +// if (c == '"') +// m_unparsed.append(1, char(c)); +// } +// else +// { +// int c; +// // Read until whitespace +//// m_separators.push_back(c); add check for separators !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// while (!m_stream->fail() && m_stream->peek() != 0xA && m_stream->peek() != 0xD && +// (c = m_stream->get()) != ' ' && c != '\t' && !m_stream->eof()) +// { +// m_unparsed.append(1, char(c)); +// if (toUpperCase) +// param.append(1, char(toupper(c))); +// else +// param.append(1, char(c)); +// } +// if (c == ' ') +// m_unparsed.append(1, char(c)); +// } +// +// // Store parameter +// if (param.size()) +// m_line.push_back(param); +// } +// +// // Skip newlines +// while (m_stream->peek() == 0xA || m_stream->peek() == 0xD) +// m_stream->get(); +// +// return m_line.size(); +//} +// +//void LineParser::WriteCompressed(string *s) const +//{ +// size_t i; +// for (i = 0; i < m_line.size(); i++) +// { +// if (i != 0) +// { +// *s += " "; +// } +// *s += m_line[i]; +// } +//} +// +//void LineParser::WriteCompressed(ostream *stream) const +//{ +// size_t i; +// for (i = 0; i < m_line.size(); i++) +// { +// if (i != 0) +// { +// *stream << " "; +// } +// *stream << m_line[i]; +// } +// *stream << endl; +//} +// +//size_t LineParser::ParsedLines() const +//{ +// return m_currentLine; +//} +// +//const char *LineParser::Param(const size_t param) const +//{ +// return m_line[param].c_str(); +//} +// +//const char *LineParser::Line() const +//{ +// return m_unparsed.c_str(); +//} + +// Expression + +double Expression::valtod(const char *exp, const char **end) +{ + // http://en.wikipedia.org/wiki/SI_prefix + + char *endptr; + double temp = strtod(exp, &endptr); + if (endptr == exp || !*endptr) + { + if (end) *end = endptr; + return temp; + } + if (end) *end = endptr + 1; + switch(*endptr) + { +/* case 'Y': // yotta + return temp * 1.E24; + break; + case 'Z': // zetta + return temp * 1.E21; + break; + case 'E': // exa, incompatible with exponent! + return temp * 1.E18; + break;*/ + case 'P': // peta + return temp * 1.E15; + break; + case 'T': // tera + return temp * 1.E12; + break; + case 'G': // giga + return temp * 1.E9; + break; + case 'M': // mega + return temp * 1.E6; + break; + case 'k': // kilo + return temp * 1.E3; + break; + case 'h': // hecto + return temp * 1.E2; + break; + case 'd': + if (endptr[1] == 'a') + { + if (end) *end = endptr + 2; + return temp * 1.E1; // deca + } + else + return temp * 1.E-1; // deci + break; + case 'c': // centi + return temp * 1.E-2; + break; + case 'm': // milli + return temp * 1.E-3; + break; + case 'u': // micro + return temp * 1.E-6; + break; + case 'n': // nano + return temp * 1.E-9; + break; + case 'p': // pico + return temp * 1.E-12; + break; + case 'f': // femto + return temp * 1.E-15; + break; + case 'a': // atto + return temp * 1.E-18; + break; + case 'z': // zepto + return temp * 1.E-21; + break; + case 'y': // yocto + return temp * 1.E-24; + break; + } + if (end) *end = endptr; + return temp; +} + +double Expression::exptod(const char *str, const char **end) +{ + Expression exp; + + const char *evalEnd = str + strlen(str); + double temp = exp.Eval(str, evalEnd); + + const char *err = exp.Error(); + if (end) *end = err ? err : evalEnd; + + return temp; +} + +string Expression::dtostr(const double value, const unsigned int precision, const char mode) +{ + if (value == 0.0) + return "0"; + string str; + char prefix[2]; + prefix[0] = 0; + prefix[1] = 0; + if (mode == 1) // exp3 + { + double exp = floor(log10(fabs(value)) / 3.) * 3; + string format; + strprintf(format, "%%.%dGE%%ld", precision); + strprintf(str, format.c_str(), value / pow(10., exp), exp); + } + else if (mode == 2) // prefix + { + double exp = floor(log10(fabs(value)) / 3.) * 3; + string format; + strprintf(format, "%%.%dG", precision); + strprintf(str, format.c_str(), value / pow(10., exp)); + switch(int(exp)) + { + case 24: // yotta + prefix[0] = 'Y'; + break; + case 21: // zetta + prefix[0] = 'Z'; + break; + case 18: // exa + prefix[0] = 'E'; + break; + case 15: // peta + prefix[0] = 'P'; + break; + case 12: // tera + prefix[0] = 'T'; + break; + case 9: // giga + prefix[0] = 'G'; + break; + case 6: // mega + prefix[0] = 'M'; + break; + case 3: // kilo + prefix[0] = 'k'; + break; + case -3: // milli + prefix[0] = 'm'; + break; + case -6: // micro + prefix[0] = 'u'; + break; + case -9: // nano + prefix[0] = 'n'; + break; + case -12: // pico + prefix[0] = 'p'; + break; + case -15: // femto + prefix[0] = 'f'; + break; + case -18: // atto + prefix[0] = 'a'; + break; + case -21: // zepto + prefix[0] = 'z'; + break; + case -24: // yocto + prefix[0] = 'y'; + break; + default: + if (exp) + { + strprintf(format, "%%.%dGE%%ld", precision); + strprintf(str, format.c_str(), value / pow(10., exp), exp); + } + else + { + strprintf(format, "%%.%dG", precision); + strprintf(str, format.c_str(), value); + } + break; + } + } + else // exp + { + string format; + strprintf(format, "%%.%dG", precision); + strprintf(str, format.c_str(), value); + } + return string(str) + prefix; +} + +template +class MemBlock +{ +public: + MemBlock() + : m_bSize(0), + m_buf(0){} + + MemBlock(const MemBlock& src) + : m_bSize(0), + m_buf(0) + { + SetSize(src.m_bSize); + memcpy(m_buf, src.m_buf, src.m_bSize); + } + + MemBlock& operator=(const MemBlock& src) + { + if (this != &src) + { + SetSize(src.m_bSize); + memcpy(m_buf, src.m_buf, src.m_bSize); + } + return (*this); + } + + ~MemBlock () + { + if (m_buf) + { + free(m_buf); + } + } + + void SetSize(const size_t size) + { + size_t oldsize = Size(); + if (oldsize != size) + { + if (!m_buf && size) + { + m_buf = malloc(size); + if (!m_buf) + throw "Out of memory"; + } + if (m_buf && size) + { + void *newBuf = realloc(m_buf, size); + if (newBuf) + m_buf = newBuf; + else + throw "Out of memory"; + } + if (m_buf && !size) + { + free(m_buf); + m_buf = 0; + } + m_bSize = size; + } + } + void * Buffer() const { return m_buf; } + T * Access() const { return (T *)m_buf; } + T & operator[](size_t i) const + { + return Access()[i]; + } + size_t Size() const { return m_bSize; } + +protected: + size_t m_bSize; + void *m_buf; +}; + +int Expression::strprintf(string &str, const char * format, ...) +{ + va_list argptr; + + char strfix[128]; + + va_start(argptr, format); + int ret = vsnprintf(strfix, 128, format, argptr); + va_end(argptr); + + if (ret == -1) + { + MemBlock strbuf; + strbuf.SetSize(128); + while (ret == -1) + { + strbuf.SetSize(strbuf.Size() * 2); + va_start(argptr, format); + ret = vsnprintf(strbuf.Access(), strbuf.Size(), format, argptr); + va_end(argptr); + } + str.assign(strbuf.Access()); + } + else if (ret >= 128) + { + MemBlock strbuf; + strbuf.SetSize(ret + 1); + va_start(argptr, format); + ret = vsnprintf(strbuf.Access(), ret + 1, format, argptr); + va_end(argptr); + str.assign(strbuf.Access()); + } + else + { + str.assign(strfix); + } + + return ret; +} + +Expression::Expression(): + m_aglf(1.), + m_errorPtr(NULL), + m_trig(false) +{ + m_functionNames.push_back("sin("); // 0 + m_functionTypes.push_back(1); + m_functionNames.push_back("asin("); // 1 + m_functionTypes.push_back(2); + m_functionNames.push_back("cos("); // 2 + m_functionTypes.push_back(1); + m_functionNames.push_back("acos("); // 3 + m_functionTypes.push_back(2); + m_functionNames.push_back("tan("); // 4 + m_functionTypes.push_back(1); + m_functionNames.push_back("atan("); // 5 + m_functionTypes.push_back(2); + m_functionNames.push_back("ln("); // 6 + m_functionTypes.push_back(0); + m_functionNames.push_back("lg("); // 7 + m_functionTypes.push_back(0); + m_functionNames.push_back("sqrt("); // 8 + m_functionTypes.push_back(0); + m_functionNames.push_back("("); // 9 + m_functionTypes.push_back(0); + + SetConstant("e", M_E); + SetConstant("pi", M_PI); +} + +double Expression::func(const double x, const size_t number) const +{ + switch (number) + { + case 0: + return sin(x); + case 1: + return asin(x); + case 2: + return cos(x); + case 3: + return acos(x); + case 4: + return tan(x); + case 5: + return atan(x); + case 6: + return log(x); + case 7: + return log10(x); + case 8: + return sqrt(x); + default: + return x; + } +} + +void Expression::SetAglf(const double value) +{ + m_aglf = value; +} + +bool Expression::SetConstant(const char *name, const double value) +{ + size_t i; + for (i = 0; name[i]; i++) + { + if (!isalnum(name[i])) + { + throw "Illegal character in constant name"; + } + } + for (i = 0; i < m_constantNames.size(); i++) + { + if (!strcmp(name, m_constantNames[i].c_str())) + { + m_constantValues[i] = value; + return true; + } + } + m_constantNames.push_back(name); + m_constantValues.push_back(value); + return false; +} + +double Expression::Eval(const char *exp) +{ + const char *evalEnd = exp + strlen(exp); + return Eval(exp, evalEnd); +} + +double Expression::Eval(const char *begin, const char *end, const bool allowWhiteSpaces) +{ + const char *curptr = begin; + int state = 1; + double operand; + double sum = 0.; + double prod = 0.0; + double power = 0.0; + + if (begin[0] == 0) + { + m_errorPtr = begin; + return 0.; + } + + if (m_errorPtr) + { + return 0.; + } + + for (;;) + { + const char *operandEnd; + bool minusBeforeOperand = false; + bool numberOperand = true; + + if (allowWhiteSpaces) + { + // TODO: Remove white spaces + } + + if (curptr[0] == '-') + minusBeforeOperand = true; + + operand = valtod(curptr, &operandEnd); + + if (operandEnd == curptr) + { // operand not a number + const char *operandStart = curptr; + double operandSign = 1.; + + numberOperand = false; + + if (curptr[0] == '+') + { + ++operandStart; + } + else if (curptr[0] == '-') + { + ++operandStart; + operandSign = -1.; + } + + // Iterate through functions + size_t i; + for (i = 0; i < m_functionNames.size(); i++) + { + if (!strncmp(operandStart, m_functionNames[i].c_str(), m_functionNames[i].size())) + { + const char *functionArg = operandStart + m_functionNames[i].size(); + const char *chptr = functionArg - 1; + const char *prptr = functionArg - 1; + + if (m_functionTypes[i]) + m_trig = true; + + // Find closing paranthesis + while (prptr) + { + chptr = strchr(chptr + 1, ')'); + prptr = strchr(prptr + 1, '('); + if (prptr > chptr) + break; + } + + if (chptr) + { // Closing paranthesis + if (m_functionTypes[i] == 1) // Trig + { + operand = operandSign * func(m_aglf * Eval(functionArg, chptr), i); + if (fabs(operand) < NOLLF) + operand = 0; + } + else if (m_functionTypes[i] == 2) // Inv trig + { + operand = operandSign / m_aglf * func(Eval(functionArg, chptr), i); + } + else // Normal + { + operand = operandSign * func(Eval(functionArg, chptr), i); + } + operandEnd = chptr + 1; + } + else + { // No closing paranthesis + if (m_functionTypes[i] == 1) + { + operand = operandSign * func(m_aglf * Eval(functionArg, end), i); + if (fabs(operand) < NOLLF) + operand = 0; + } + else if (m_functionTypes[i] == 2) + { + operand = operandSign / m_aglf * func(Eval(functionArg, end), i); + } + else + { + operand = operandSign * func(Eval(functionArg, end), i); + } + operandEnd = end; + } + + if (m_errorPtr) + { + return 0.; + } + + break; + } + } + + // Iterate through constants + if (i == m_functionNames.size()) // Only search if no function found + for (i = 0; i < m_constantNames.size(); i++) + { + if (!strncmp(operandStart, m_constantNames[i].c_str(), m_constantNames[i].size()) && !isalnum(operandStart[m_constantNames[i].size()])) + { + operandEnd = operandStart + m_constantNames[i].size(); + operand = operandSign * m_constantValues[i]; + break; + } + } + } + + if (operandEnd > end) + { + m_errorPtr = end; + return 0.; + } + + // Calculate expression in correct order + + // state == 1 => term => sum + operand + // state == 2 => product => sum + prod * operand + // state == 3 => quotient => sum + prod / operand + // state == 4 => plus & power => sum + power ^ operand + // state == 5 => minus & power => sum - power ^ operand + // state == 6 => product & power => sum + prod * power ^ operand + // state == 7 => quotient & power => sum + prod / power ^ operand + + if (curptr != operandEnd) // number? + { + switch (state) + { + case 1: // term + if (operandEnd == end) + { + return sum + operand; + } + switch (operandEnd[0]) + { // Check next + case '+': + case '-': + curptr = operandEnd; + sum = sum + operand; + break; + case '*': + curptr = operandEnd + 1; + prod = operand; + state = 2; + break; + case '/': + curptr = operandEnd + 1; + prod = operand; + state = 3; + break; + case '^': + curptr = operandEnd + 1; + power = operand; + if (minusBeforeOperand) + { + power = power * -1.; + state = 5; + } + else + { + state = 4; + } + break; + default: + if (numberOperand && isalpha(operandEnd[0])) + { // Assume multiplication of function/constant without * + curptr = operandEnd; + prod = operand; + state = 2; + } + else + { + m_errorPtr = operandEnd; + return 0.; + } + } + break; + case 2: // product + if (operandEnd == end) + { + return sum + prod * operand; + } + switch (operandEnd[0]) + { // Check next + case '+': + case '-': + curptr = operandEnd; + sum = sum + prod * operand; + state = 1; + break; + case '*': + curptr = operandEnd + 1; + prod = prod * operand; + break; + case '/': + curptr = operandEnd + 1; + prod = prod * operand; + state = 3; + break; + case '^': + curptr = operandEnd + 1; + power = operand; + state = 6; + break; + default: + m_errorPtr = operandEnd; + return 0.; + } + break; + case 3: // quotient + if (operandEnd == end) + { + return sum + prod / operand; + } + switch (operandEnd[0]) + { // Check next + case '+': + case '-': + curptr = operandEnd; + sum = sum + prod / operand; + state = 1; + break; + case '*': + curptr = operandEnd + 1; + prod = prod / operand; + state = 2; + break; + case '/': + curptr = operandEnd + 1; + prod = prod / operand; + break; + case '^': + curptr = operandEnd + 1; + power = operand; + state = 7; + break; + default: + m_errorPtr = operandEnd; + return 0.; + } + break; + case 4: // plus&power + if (operandEnd == end) + { + return sum + pow(power, operand); + } + switch (operandEnd[0]) + { // Check next + case '+': + case '-': + curptr = operandEnd; + sum = sum + pow(power, operand); + state = 1; + break; + case '*': + curptr = operandEnd + 1; + prod = pow(power, operand); + state = 2; + break; + case '/': + curptr = operandEnd + 1; + prod = pow(power, operand); + state = 3; + break; + case '^': + curptr = operandEnd + 1; + power = pow(power, operand); + break; + default: + m_errorPtr = operandEnd; + return 0.; + } + break; + case 5: // minus&power + if (operandEnd == end) + { + return sum - pow(power, operand); + } + switch (operandEnd[0]) + { // Check next + case '+': + case '-': + curptr = operandEnd; + sum = sum - pow(power, operand); + state = 1; + break; + case '*': + curptr = operandEnd + 1; + prod = pow(power, operand); + state = 2; + break; + case '/': + curptr = operandEnd + 1; + prod = pow(power, operand); + state = 3; + break; + case '^': + curptr = operandEnd + 1; + power = pow(power, operand); + break; + default: + m_errorPtr = operandEnd; + return 0.; + } + break; + case 6: // product&power + if (operandEnd == end) + { + return sum + prod * pow(power, operand); + } + switch (operandEnd[0]) + { // Check next + case '+': + case '-': + curptr = operandEnd; + sum = sum + prod * pow(power, operand); + state = 1; + break; + case '*': + curptr = operandEnd + 1; + prod = prod * pow(power, operand); + state = 2; + break; + case '/': + curptr = operandEnd + 1; + prod = prod * pow(power, operand); + state = 3; + break; + case '^': + curptr = operandEnd + 1; + power = pow(power, operand); + break; + default: + m_errorPtr = operandEnd; + return 0.; + } + break; + case 7: // quotient&power + if (operandEnd == end) + { + return sum + prod / pow(power, operand); + } + switch (operandEnd[0]) + { // Check next + case '+': + case '-': + curptr = operandEnd; + sum = sum + prod / pow(power, operand); + state = 1; + break; + case '*': + curptr = operandEnd + 1; + prod = prod / pow(power, operand); + state = 2; + break; + case '/': + curptr = operandEnd + 1; + prod = prod / pow(power, operand); + state = 3; + break; + case '^': + curptr = operandEnd + 1; + power = pow(power, operand); + break; + default: + m_errorPtr = operandEnd; + return 0.; + } + break; + default: + m_errorPtr = operandEnd; + return 0.; + } + } + else + { + m_errorPtr = curptr; + return 0.; + } + } +} + +const char *Expression::Error() +{ + const char *retval = m_errorPtr; + m_errorPtr = NULL; + m_trig = false; + return retval; +} + diff --git a/src/apps/deskcalc/Parser.h b/src/apps/deskcalc/Parser.h new file mode 100644 index 0000000000..d9d5d7a526 --- /dev/null +++ b/src/apps/deskcalc/Parser.h @@ -0,0 +1,70 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Copyright 2004 Daniel Wallner. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Daniel Wallner + */ + +#ifndef PARSER_H +#define PARSER_H + +#include +#include +#include + +//class LineParser { +// public: +// LineParser(std::istream *stream); +// +// void AddSeparator(const char c); +// +// size_t SetToNext(bool toUpperCase = false); +// void WriteCompressed(std::string *s) const; +// void WriteCompressed(std::ostream *stream) const; +// +// size_t ParsedLines() const; +// const char *Param(const size_t param) const; +// const char *Line() const; +// +// private: +// LineParser(); +// +// std::istream *m_stream; +// size_t m_currentLine; +// std::string m_unparsed; +// std::vector m_line; +// std::vector m_separators; +//}; + +class Expression { + public: + static double valtod(const char *exp, const char **end); + static double exptod(const char *exp, const char **end); + static std::string dtostr(const double value, const unsigned int precision, const char mode); // 0 => exp, 1 => exp3, 2 => SI + static int strprintf(std::string &str, const char * format, ...); + + Expression(); + + void SetAglf(const double value); + bool SetConstant(const char *name, const double value); + + double Eval(const char *exp); + double Eval(const char *begin, const char *end, const bool allowWhiteSpaces = false); + const char *Error(); + + private: + double m_aglf; + const char *m_errorPtr; + bool m_trig; + + double func(const double x, const size_t number) const; + std::vector m_functionNames; + std::vector m_functionTypes; + + std::vector m_constantNames; + std::vector m_constantValues; +}; + +#endif // PARSER_H