2 * Copyright 2006-2013, Haiku, Inc. All rights reserved.
3 * Copyright 1997, 1998 R3 Software Ltd. All Rights Reserved.
4 * Distributed under the terms of the MIT License.
7 * Stephan Aßmus, superstippi@gmx.de
8 * Philippe Saint-Pierre, stpere@gmail.com
9 * John Scipione, jscipione@gmail.com
10 * Timothy Wayper, timmy@wunderbear.com
22 #include <AboutWindow.h>
24 #include <Application.h>
25 #include <AppFileInfo.h>
29 #include <ControlLook.h>
30 #include <Clipboard.h>
35 #include <MessageRunner.h>
36 #include <PlaySound.h>
38 #include <PopUpMenu.h>
42 #include <ExpressionParser.h>
44 #include "CalcApplication.h"
45 #include "CalcOptions.h"
46 #include "ExpressionTextView.h"
49 #undef B_TRANSLATION_CONTEXT
50 #define B_TRANSLATION_CONTEXT "CalcView"
53 static const int32 kMsgCalculating
= 'calc';
54 static const int32 kMsgAnimateDots
= 'dots';
55 static const int32 kMsgDoneEvaluating
= 'done';
57 //const uint8 K_COLOR_OFFSET = 32;
58 const float kFontScaleY
= 0.4f
;
59 const float kFontScaleX
= 0.4f
;
60 const float kExpressionFontScaleY
= 0.6f
;
61 const float kDisplayScaleY
= 0.2f
;
63 static const bigtime_t kFlashOnOffInterval
= 100000;
64 static const bigtime_t kCalculatingInterval
= 1000000;
65 static const bigtime_t kAnimationInterval
= 333333;
67 static const float kMinimumWidthCompact
= 130.0f
;
68 static const float kMaximumWidthCompact
= 400.0f
;
69 static const float kMinimumHeightCompact
= 20.0f
;
70 static const float kMaximumHeightCompact
= 60.0f
;
72 // Basic mode size limits are defined in CalcView.h so
73 // that they can be used by the CalcWindow constructor.
75 static const float kMinimumWidthScientific
= 240.0f
;
76 static const float kMaximumWidthScientific
= 400.0f
;
77 static const float kMinimumHeightScientific
= 200.0f
;
78 static const float kMaximumHeightScientific
= 400.0f
;
80 // basic mode keypad layout (default)
81 const char *kKeypadDescriptionBasic
=
87 // scientific mode keypad layout
88 const char *kKeypadDescriptionScientific
=
90 "log asin acos atan sqrt \n"
91 "exp sinh cosh tanh cbrt \n"
100 FLAGS_FLASH_KEY
= 1 << 0,
101 FLAGS_MOUSE_DOWN
= 1 << 1
105 struct CalcView::CalcKey
{
115 CalcView::Instantiate(BMessage
* archive
)
117 if (!validate_instantiation(archive
, "CalcView"))
120 return new CalcView(archive
);
124 CalcView::CalcView(BRect frame
, rgb_color rgbBaseColor
, BMessage
* settings
)
126 BView(frame
, "DeskCalc", B_FOLLOW_ALL_SIDES
, B_WILL_DRAW
| B_FRAME_EVENTS
),
130 fBaseColor(rgbBaseColor
),
131 fExpressionBGColor((rgb_color
){ 0, 0, 0, 255 }),
133 fHasCustomBaseColor(rgbBaseColor
!= ui_color(B_PANEL_BACKGROUND_COLOR
)),
138 fKeypadDescription(strdup(kKeypadDescriptionBasic
)),
142 fCalcIcon(new BBitmap(BRect(0, 0, 15, 15), 0, B_RGBA32
)),
144 fCalcIcon(new BBitmap(BRect(0, 0, 15, 15), 0, B_CMAP8
)),
148 fAutoNumlockItem(NULL
),
149 fAudioFeedbackItem(NULL
),
150 fOptions(new CalcOptions()),
152 fEvaluateMessageRunner(NULL
),
153 fEvaluateSemaphore(B_BAD_SEM_ID
),
156 // tell the app server not to erase our b/g
157 SetViewColor(B_TRANSPARENT_32_BIT
);
163 CalcView::CalcView(BMessage
* archive
)
169 fBaseColor(ui_color(B_PANEL_BACKGROUND_COLOR
)),
170 fExpressionBGColor((rgb_color
){ 0, 0, 0, 255 }),
172 fHasCustomBaseColor(false),
177 fKeypadDescription(strdup(kKeypadDescriptionBasic
)),
181 fCalcIcon(new BBitmap(BRect(0, 0, 15, 15), 0, B_RGBA32
)),
183 fCalcIcon(new BBitmap(BRect(0, 0, 15, 15), 0, B_CMAP8
)),
187 fAutoNumlockItem(NULL
),
188 fAudioFeedbackItem(NULL
),
189 fOptions(new CalcOptions()),
191 fEvaluateMessageRunner(NULL
),
192 fEvaluateSemaphore(B_BAD_SEM_ID
),
195 // Do not restore the follow mode, in shelfs, we never follow.
196 SetResizingMode(B_FOLLOW_NONE
);
202 CalcView::~CalcView()
206 free(fKeypadDescription
);
207 delete fEvaluateMessageRunner
;
208 delete_sem(fEvaluateSemaphore
);
213 CalcView::AttachedToWindow()
215 if (be_control_look
== NULL
)
216 SetFont(be_bold_font
);
218 BRect
frame(Frame());
219 FrameResized(frame
.Width(), frame
.Height());
221 bool addKeypadModeMenuItems
= true;
222 if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN
) != 0) {
223 // don't add these items if we are a replicant on the desktop
224 addKeypadModeMenuItems
= false;
227 // create and attach the pop-up menu
228 _CreatePopUpMenu(addKeypadModeMenuItems
);
230 if (addKeypadModeMenuItems
)
231 SetKeypadMode(fOptions
->keypad_mode
);
236 CalcView::MessageReceived(BMessage
* message
)
238 if (message
->what
== B_COLORS_UPDATED
&& !fHasCustomBaseColor
) {
239 const char* panelBgColorName
= ui_color_name(B_PANEL_BACKGROUND_COLOR
);
240 if (message
->HasColor(panelBgColorName
)) {
241 fBaseColor
= message
->GetColor(panelBgColorName
, fBaseColor
);
248 if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN
) != 0) {
249 // if we are embedded in desktop we need to receive these
250 // message here since we don't have a parent BWindow
251 switch (message
->what
) {
252 case MSG_OPTIONS_AUTO_NUM_LOCK
:
256 case MSG_OPTIONS_AUDIO_FEEDBACK
:
257 ToggleAudioFeedback();
260 case MSG_OPTIONS_ANGLE_MODE_RADIAN
:
261 SetDegreeMode(false);
264 case MSG_OPTIONS_ANGLE_MODE_DEGREE
:
270 // check if message was dropped
271 if (message
->WasDropped()) {
272 // pass message on to paste
273 if (message
->IsSourceRemote())
276 // act on posted message type
277 switch (message
->what
) {
291 // access system clipboard
292 if (be_clipboard
->Lock()) {
293 BMessage
* clipper
= be_clipboard
->Data();
294 //clipper->PrintToStream();
296 be_clipboard
->Unlock();
300 // (replicant) about box requested
301 case B_ABOUT_REQUESTED
:
303 BAboutWindow
* window
= new BAboutWindow(kAppName
, kSignature
);
305 // create the about window
306 const char* extraCopyrights
[] = {
307 "1997, 1998 R3 Software Ltd.",
311 const char* authors
[] = {
319 window
->AddCopyright(2006, "Haiku, Inc.", extraCopyrights
);
320 window
->AddAuthors(authors
);
327 case MSG_UNFLASH_KEY
:
330 if (message
->FindInt32("key", &key
) == B_OK
)
336 case kMsgAnimateDots
:
338 int32 end
= fExpressionTextView
->TextLength();
339 int32 start
= end
- 3;
340 if (fEnabled
|| strcmp(fExpressionTextView
->Text() + start
,
342 // stop the message runner
343 delete fEvaluateMessageRunner
;
344 fEvaluateMessageRunner
= NULL
;
349 if (message
->FindUInt8("dot", &dot
) == B_OK
) {
350 rgb_color fontColor
= fExpressionTextView
->HighColor();
351 rgb_color backColor
= fExpressionTextView
->LowColor();
352 fExpressionTextView
->SetStylable(true);
353 fExpressionTextView
->SetFontAndColor(start
, end
, NULL
, 0,
355 fExpressionTextView
->SetFontAndColor(start
+ dot
- 1,
356 start
+ dot
, NULL
, 0, &fontColor
);
357 fExpressionTextView
->SetStylable(false);
364 delete fEvaluateMessageRunner
;
365 BMessage
animate(kMsgAnimateDots
);
366 animate
.AddUInt8("dot", dot
);
367 fEvaluateMessageRunner
= new (std::nothrow
) BMessageRunner(
368 BMessenger(this), &animate
, kAnimationInterval
, 1);
372 case kMsgCalculating
:
374 // calculation has taken more than 3 seconds
376 // stop the message runner
377 delete fEvaluateMessageRunner
;
378 fEvaluateMessageRunner
= NULL
;
383 calculating
<< B_TRANSLATE("Calculating") << "...";
384 fExpressionTextView
->SetText(calculating
.String());
386 delete fEvaluateMessageRunner
;
387 BMessage
animate(kMsgAnimateDots
);
388 animate
.AddUInt8("dot", 1U);
389 fEvaluateMessageRunner
= new (std::nothrow
) BMessageRunner(
390 BMessenger(this), &animate
, kAnimationInterval
, 1);
394 case kMsgDoneEvaluating
:
397 rgb_color fontColor
= fExpressionTextView
->HighColor();
398 fExpressionTextView
->SetFontAndColor(NULL
, 0, &fontColor
);
401 if (message
->FindString("error", &result
) == B_OK
)
402 fExpressionTextView
->SetText(result
);
403 else if (message
->FindString("value", &result
) == B_OK
)
404 fExpressionTextView
->SetValue(result
);
406 // stop the message runner
407 delete fEvaluateMessageRunner
;
408 fEvaluateMessageRunner
= NULL
;
413 BView::MessageReceived(message
);
421 CalcView::Draw(BRect updateRect
)
423 bool drawBackground
= !_IsEmbedded();
425 SetHighColor(fBaseColor
);
426 BRect
expressionRect(_ExpressionRect());
427 if (updateRect
.Intersects(expressionRect
)) {
428 if (fOptions
->keypad_mode
== KEYPAD_MODE_COMPACT
429 && expressionRect
.Height() >= fCalcIcon
->Bounds().Height()) {
431 expressionRect
.left
= fExpressionTextView
->Frame().right
+ 2;
432 if (drawBackground
) {
433 SetHighColor(fBaseColor
);
434 FillRect(updateRect
& expressionRect
);
437 if (fCalcIcon
->ColorSpace() == B_RGBA32
) {
438 SetDrawingMode(B_OP_ALPHA
);
439 SetBlendingMode(B_PIXEL_ALPHA
, B_ALPHA_OVERLAY
);
441 SetDrawingMode(B_OP_OVER
);
445 iconPos
.x
= expressionRect
.right
- (expressionRect
.Width()
446 + fCalcIcon
->Bounds().Width()) / 2.0;
447 iconPos
.y
= expressionRect
.top
+ (expressionRect
.Height()
448 - fCalcIcon
->Bounds().Height()) / 2.0;
449 DrawBitmap(fCalcIcon
, iconPos
);
451 SetDrawingMode(B_OP_COPY
);
454 // render border around expression text view
455 expressionRect
= fExpressionTextView
->Frame();
456 expressionRect
.InsetBy(-2, -2);
457 if (fOptions
->keypad_mode
!= KEYPAD_MODE_COMPACT
&& drawBackground
) {
458 expressionRect
.InsetBy(-2, -2);
459 StrokeRect(expressionRect
);
460 expressionRect
.InsetBy(1, 1);
461 StrokeRect(expressionRect
);
462 expressionRect
.InsetBy(1, 1);
467 flags
|= BControlLook::B_BLEND_FRAME
;
468 be_control_look
->DrawTextControlBorder(this, expressionRect
,
469 updateRect
, fBaseColor
, flags
);
472 if (fOptions
->keypad_mode
== KEYPAD_MODE_COMPACT
)
475 // calculate grid sizes
476 BRect
keypadRect(_KeypadRect());
478 if (be_control_look
!= NULL
) {
480 StrokeRect(keypadRect
);
481 keypadRect
.InsetBy(1, 1);
484 float sizeDisp
= keypadRect
.top
;
485 float sizeCol
= (keypadRect
.Width() + 1) / (float)fColumns
;
486 float sizeRow
= (keypadRect
.Height() + 1) / (float)fRows
;
488 if (!updateRect
.Intersects(keypadRect
))
491 SetFontSize(min_c(sizeRow
* kFontScaleY
, sizeCol
* kFontScaleX
));
493 CalcKey
* key
= fKeypad
;
494 for (int row
= 0; row
< fRows
; row
++) {
495 for (int col
= 0; col
< fColumns
; col
++) {
497 frame
.left
= keypadRect
.left
+ col
* sizeCol
;
498 frame
.right
= keypadRect
.left
+ (col
+ 1) * sizeCol
- 1;
499 frame
.top
= sizeDisp
+ row
* sizeRow
;
500 frame
.bottom
= sizeDisp
+ (row
+ 1) * sizeRow
- 1;
502 if (drawBackground
) {
503 SetHighColor(fBaseColor
);
510 flags
|= BControlLook::B_BLEND_FRAME
;
512 flags
|= BControlLook::B_ACTIVATED
;
513 flags
|= BControlLook::B_IGNORE_OUTLINE
;
515 be_control_look
->DrawButtonFrame(this, frame
, updateRect
,
516 fBaseColor
, fBaseColor
, flags
);
518 be_control_look
->DrawButtonBackground(this, frame
, updateRect
,
521 be_control_look
->DrawLabel(this, key
->label
, frame
, updateRect
,
522 fBaseColor
, flags
, BAlignment(B_ALIGN_HORIZONTAL_CENTER
,
523 B_ALIGN_VERTICAL_CENTER
), &fButtonTextColor
);
532 CalcView::MouseDown(BPoint point
)
534 // ensure this view is the current focus
535 if (!fExpressionTextView
->IsFocus()) {
536 // Call our version of MakeFocus(), since that will also apply the
541 // read mouse buttons state
543 Window()->CurrentMessage()->FindInt32("buttons", &buttons
);
545 if ((B_PRIMARY_MOUSE_BUTTON
& buttons
) == 0) {
546 // display popup menu if not primary mouse button
548 if ((selected
= fPopUpMenu
->Go(ConvertToScreen(point
))) != NULL
549 && selected
->Message() != NULL
) {
550 Window()->PostMessage(selected
->Message(), this);
555 if (fOptions
->keypad_mode
== KEYPAD_MODE_COMPACT
) {
556 if (fCalcIcon
!= NULL
) {
557 BRect
bounds(Bounds());
558 bounds
.left
= bounds
.right
- fCalcIcon
->Bounds().Width();
559 if (bounds
.Contains(point
)) {
560 // user clicked on calculator icon
561 fExpressionTextView
->Clear();
567 // calculate grid sizes
568 float sizeDisp
= fHeight
* kDisplayScaleY
;
569 float sizeCol
= fWidth
/ (float)fColumns
;
570 float sizeRow
= (fHeight
- sizeDisp
) / (float)fRows
;
572 // calculate location within grid
573 int gridCol
= (int)floorf(point
.x
/ sizeCol
);
574 int gridRow
= (int)floorf((point
.y
- sizeDisp
) / sizeRow
);
577 if ((gridCol
>= 0) && (gridCol
< fColumns
)
578 && (gridRow
>= 0) && (gridRow
< fRows
)) {
581 int key
= gridRow
* fColumns
+ gridCol
;
582 _FlashKey(key
, FLAGS_MOUSE_DOWN
);
585 // make sure we receive the mouse up!
586 SetMouseEventMask(B_POINTER_EVENTS
, B_LOCK_WINDOW_FOCUS
);
592 CalcView::MouseUp(BPoint point
)
594 if (fOptions
->keypad_mode
== KEYPAD_MODE_COMPACT
)
597 int keys
= fRows
* fColumns
;
598 for (int i
= 0; i
< keys
; i
++) {
599 if (fKeypad
[i
].flags
& FLAGS_MOUSE_DOWN
) {
608 CalcView::KeyDown(const char* bytes
, int32 numBytes
)
610 // if single byte character...
613 //printf("Key pressed: %c\n", bytes[0]);
618 // translate to evaluate key
624 // translate to backspace key
631 // translate to clear key
635 // bracket translation
647 // scan the keymap array for match
648 int keys
= fRows
* fColumns
;
649 for (int i
= 0; i
< keys
; i
++) {
650 if (fKeypad
[i
].keymap
[0] == bytes
[0]) {
663 CalcView::MakeFocus(bool focused
)
667 if (fOptions
->auto_num_lock
) {
668 set_keyboard_locks(B_NUM_LOCK
669 | (modifiers() & (B_CAPS_LOCK
| B_SCROLL_LOCK
)));
673 // pass on request to text view
674 fExpressionTextView
->MakeFocus(focused
);
679 CalcView::FrameResized(float width
, float height
)
684 // layout expression text view
685 BRect expressionRect
= _ExpressionRect();
686 if (fOptions
->keypad_mode
== KEYPAD_MODE_COMPACT
) {
687 expressionRect
.InsetBy(2, 2);
688 expressionRect
.right
-= ceilf(fCalcIcon
->Bounds().Width() * 1.5);
690 expressionRect
.InsetBy(4, 4);
692 fExpressionTextView
->MoveTo(expressionRect
.LeftTop());
693 fExpressionTextView
->ResizeTo(expressionRect
.Width(), expressionRect
.Height());
695 // configure expression text view font size and color
696 float sizeDisp
= fOptions
->keypad_mode
== KEYPAD_MODE_COMPACT
697 ? fHeight
: fHeight
* kDisplayScaleY
;
698 BFont
font(be_bold_font
);
699 font
.SetSize(sizeDisp
* kExpressionFontScaleY
);
700 rgb_color fontColor
= fExpressionTextView
->HighColor();
701 fExpressionTextView
->SetFontAndColor(&font
, B_FONT_ALL
, &fontColor
);
703 expressionRect
.OffsetTo(B_ORIGIN
);
704 float inset
= (expressionRect
.Height() - fExpressionTextView
->LineHeight(0)) / 2;
705 expressionRect
.InsetBy(0, inset
);
706 fExpressionTextView
->SetTextRect(expressionRect
);
712 CalcView::Archive(BMessage
* archive
, bool deep
) const
714 fExpressionTextView
->RemoveSelf();
716 // passed on request to parent
717 status_t ret
= BView::Archive(archive
, deep
);
719 const_cast<CalcView
*>(this)->AddChild(fExpressionTextView
);
721 // save app signature for replicant add-on loading
723 ret
= archive
->AddString("add_on", kSignature
);
725 // save all the options
727 ret
= SaveSettings(archive
);
729 // add class info last
731 ret
= archive
->AddString("class", "CalcView");
740 Copy(); // copy data to clipboard
741 fExpressionTextView
->Clear(); // remove data
748 // access system clipboard
749 if (be_clipboard
->Lock()) {
750 be_clipboard
->Clear();
751 BMessage
* clipper
= be_clipboard
->Data();
752 clipper
->what
= B_MIME_DATA
;
753 // TODO: should check return for errors!
754 BString expression
= fExpressionTextView
->Text();
755 clipper
->AddData("text/plain", B_MIME_TYPE
,
756 expression
.String(), expression
.Length());
757 //clipper->PrintToStream();
758 be_clipboard
->Commit();
759 be_clipboard
->Unlock();
765 CalcView::Paste(BMessage
* message
)
767 // handle files first
769 if (message
->GetInfo("refs", NULL
, &count
) == B_OK
) {
774 memset(buffer
, 0, sizeof(buffer
));
775 for (int32 i
= 0; i
< count
; i
++) {
776 if (message
->FindRef("refs", i
, &ref
) == B_OK
) {
777 if (file
.SetTo(&ref
, B_READ_ONLY
) == B_OK
) {
778 read
= file
.Read(buffer
, sizeof(buffer
) - 1);
781 BString
expression(buffer
);
782 int32 j
= expression
.Length();
783 while (j
> 0 && expression
[j
- 1] == '\n')
785 expression
.Truncate(j
);
786 if (expression
.Length() > 0)
787 fExpressionTextView
->Insert(expression
.String());
793 // handle color drops
794 // read incoming color
795 const rgb_color
* dropColor
= NULL
;
797 if (message
->FindData("RGBColor", B_RGB_COLOR_TYPE
,
798 (const void**)&dropColor
, &dataSize
) == B_OK
799 && dataSize
== sizeof(rgb_color
)) {
801 // calculate view relative drop point
802 BPoint dropPoint
= ConvertFromScreen(message
->DropPoint());
804 // calculate current keypad area
805 float sizeDisp
= fHeight
* kDisplayScaleY
;
806 BRect
keypadRect(0.0, sizeDisp
, fWidth
, fHeight
);
808 // check location of color drop
809 if (keypadRect
.Contains(dropPoint
) && dropColor
!= NULL
) {
810 fBaseColor
= *dropColor
;
811 fHasCustomBaseColor
=
812 fBaseColor
!= ui_color(B_PANEL_BACKGROUND_COLOR
);
819 // look for text/plain MIME data
822 if (message
->FindData("text/plain", B_MIME_TYPE
,
823 (const void**)&text
, &numBytes
) == B_OK
) {
825 temp
.Append(text
, numBytes
);
826 fExpressionTextView
->Insert(temp
.String());
833 CalcView::SaveSettings(BMessage
* archive
) const
835 status_t ret
= archive
? B_OK
: B_BAD_VALUE
;
837 // record grid dimensions
839 ret
= archive
->AddInt16("cols", fColumns
);
842 ret
= archive
->AddInt16("rows", fRows
);
844 // record color scheme
846 ret
= archive
->AddData("rgbBaseColor", B_RGB_COLOR_TYPE
,
847 &fBaseColor
, sizeof(rgb_color
));
851 ret
= archive
->AddData("rgbDisplay", B_RGB_COLOR_TYPE
,
852 &fExpressionBGColor
, sizeof(rgb_color
));
855 // record current options
857 ret
= fOptions
->SaveSettings(archive
);
859 // record display text
861 ret
= archive
->AddString("displayText", fExpressionTextView
->Text());
863 // record expression history
865 ret
= fExpressionTextView
->SaveSettings(archive
);
867 // record calculator description
869 ret
= archive
->AddString("calcDesc", fKeypadDescription
);
878 if (fExpressionTextView
->TextLength() == 0) {
883 fEvaluateThread
= spawn_thread(_EvaluateThread
, "Evaluate Thread",
884 B_LOW_PRIORITY
, this);
885 if (fEvaluateThread
< B_OK
) {
886 // failed to create evaluate thread, error out
887 fExpressionTextView
->SetText(strerror(fEvaluateThread
));
891 _AudioFeedback(false);
893 // Disable input while we evaluate
895 status_t threadStatus
= resume_thread(fEvaluateThread
);
896 if (threadStatus
!= B_OK
) {
897 // evaluate thread failed to start, error out
898 fExpressionTextView
->SetText(strerror(threadStatus
));
903 if (fEvaluateMessageRunner
== NULL
) {
904 BMessage
message(kMsgCalculating
);
905 fEvaluateMessageRunner
= new (std::nothrow
) BMessageRunner(
906 BMessenger(this), &message
, kCalculatingInterval
, 1);
907 status_t runnerStatus
= fEvaluateMessageRunner
->InitCheck();
908 if (runnerStatus
!= B_OK
)
909 printf("Evaluate Message Runner: %s\n", strerror(runnerStatus
));
915 CalcView::FlashKey(const char* bytes
, int32 numBytes
)
918 temp
.Append(bytes
, numBytes
);
919 int32 key
= _KeyForLabel(temp
.String());
921 _FlashKey(key
, FLAGS_FLASH_KEY
);
926 CalcView::ToggleAutoNumlock(void)
928 fOptions
->auto_num_lock
= !fOptions
->auto_num_lock
;
929 fAutoNumlockItem
->SetMarked(fOptions
->auto_num_lock
);
934 CalcView::ToggleAudioFeedback(void)
936 fOptions
->audio_feedback
= !fOptions
->audio_feedback
;
937 fAudioFeedbackItem
->SetMarked(fOptions
->audio_feedback
);
942 CalcView::SetDegreeMode(bool degrees
)
944 fOptions
->degree_mode
= degrees
;
945 fAngleModeRadianItem
->SetMarked(!degrees
);
946 fAngleModeDegreeItem
->SetMarked(degrees
);
951 CalcView::SetKeypadMode(uint8 mode
)
956 BWindow
* window
= Window();
960 if (fOptions
->keypad_mode
== mode
)
963 fOptions
->keypad_mode
= mode
;
964 _MarkKeypadItems(fOptions
->keypad_mode
);
966 float width
= fWidth
;
967 float height
= fHeight
;
969 switch (fOptions
->keypad_mode
) {
970 case KEYPAD_MODE_COMPACT
:
972 if (window
->Bounds() == Frame()) {
973 window
->SetSizeLimits(kMinimumWidthCompact
,
974 kMaximumWidthCompact
, kMinimumHeightCompact
,
975 kMaximumHeightCompact
);
976 window
->ResizeTo(width
, height
* kDisplayScaleY
);
978 ResizeTo(width
, height
* kDisplayScaleY
);
983 case KEYPAD_MODE_SCIENTIFIC
:
985 free(fKeypadDescription
);
986 fKeypadDescription
= strdup(kKeypadDescriptionScientific
);
988 _ParseCalcDesc(fKeypadDescription
);
990 window
->SetSizeLimits(kMinimumWidthScientific
,
991 kMaximumWidthScientific
, kMinimumHeightScientific
,
992 kMaximumHeightScientific
);
994 if (width
< kMinimumWidthScientific
)
995 width
= kMinimumWidthScientific
;
996 else if (width
> kMaximumWidthScientific
)
997 width
= kMaximumWidthScientific
;
999 if (height
< kMinimumHeightScientific
)
1000 height
= kMinimumHeightScientific
;
1001 else if (height
> kMaximumHeightScientific
)
1002 height
= kMaximumHeightScientific
;
1004 if (width
!= fWidth
|| height
!= fHeight
)
1005 ResizeTo(width
, height
);
1012 case KEYPAD_MODE_BASIC
:
1015 free(fKeypadDescription
);
1016 fKeypadDescription
= strdup(kKeypadDescriptionBasic
);
1018 _ParseCalcDesc(fKeypadDescription
);
1020 window
->SetSizeLimits(kMinimumWidthBasic
, kMaximumWidthBasic
,
1021 kMinimumHeightBasic
, kMaximumHeightBasic
);
1023 if (width
< kMinimumWidthBasic
)
1024 width
= kMinimumWidthBasic
;
1025 else if (width
> kMaximumWidthBasic
)
1026 width
= kMaximumWidthBasic
;
1028 if (height
< kMinimumHeightBasic
)
1029 height
= kMinimumHeightBasic
;
1030 else if (height
> kMaximumHeightBasic
)
1031 height
= kMaximumHeightBasic
;
1033 if (width
!= fWidth
|| height
!= fHeight
)
1034 ResizeTo(width
, height
);
1046 CalcView::_EvaluateThread(void* data
)
1048 CalcView
* calcView
= reinterpret_cast<CalcView
*>(data
);
1049 if (calcView
== NULL
)
1052 BMessenger
messenger(calcView
);
1053 if (!messenger
.IsValid())
1057 status_t status
= acquire_sem(calcView
->fEvaluateSemaphore
);
1058 if (status
== B_OK
) {
1059 ExpressionParser parser
;
1060 parser
.SetDegreeMode(calcView
->fOptions
->degree_mode
);
1061 BString
expression(calcView
->fExpressionTextView
->Text());
1063 result
= parser
.Evaluate(expression
.String());
1064 } catch (ParseException e
) {
1065 result
<< e
.message
.String() << " at " << (e
.position
+ 1);
1068 release_sem(calcView
->fEvaluateSemaphore
);
1070 result
= strerror(status
);
1072 BMessage
message(kMsgDoneEvaluating
);
1073 message
.AddString(status
== B_OK
? "value" : "error", result
.String());
1074 messenger
.SendMessage(&message
);
1081 CalcView::_Init(BMessage
* settings
)
1083 // create expression text view
1084 fExpressionTextView
= new ExpressionTextView(_ExpressionRect(), this);
1085 AddChild(fExpressionTextView
);
1087 // read data from archive
1088 _LoadSettings(settings
);
1090 // fetch the calc icon for compact view
1091 _FetchAppIcon(fCalcIcon
);
1093 fEvaluateSemaphore
= create_sem(1, "Evaluate Semaphore");
1098 CalcView::_LoadSettings(BMessage
* archive
)
1103 // record calculator description
1104 const char* calcDesc
;
1105 if (archive
->FindString("calcDesc", &calcDesc
) < B_OK
)
1106 calcDesc
= kKeypadDescriptionBasic
;
1108 // save calculator description for reference
1109 free(fKeypadDescription
);
1110 fKeypadDescription
= strdup(calcDesc
);
1112 // read grid dimensions
1113 if (archive
->FindInt16("cols", &fColumns
) < B_OK
)
1115 if (archive
->FindInt16("rows", &fRows
) < B_OK
)
1118 // read color scheme
1119 const rgb_color
* color
;
1121 if (archive
->FindData("rgbBaseColor", B_RGB_COLOR_TYPE
,
1122 (const void**)&color
, &size
) < B_OK
1123 || size
!= sizeof(rgb_color
)) {
1124 fBaseColor
= ui_color(B_PANEL_BACKGROUND_COLOR
);
1125 puts("Missing rgbBaseColor from CalcView archive!\n");
1127 fBaseColor
= *color
;
1129 if (archive
->FindData("rgbDisplay", B_RGB_COLOR_TYPE
,
1130 (const void**)&color
, &size
) < B_OK
1131 || size
!= sizeof(rgb_color
)) {
1132 fExpressionBGColor
= (rgb_color
){ 0, 0, 0, 255 };
1133 puts("Missing rgbBaseColor from CalcView archive!\n");
1135 fExpressionBGColor
= *color
;
1138 fHasCustomBaseColor
= fBaseColor
!= ui_color(B_PANEL_BACKGROUND_COLOR
);
1141 fOptions
->LoadSettings(archive
);
1143 // load display text
1144 const char* display
;
1145 if (archive
->FindString("displayText", &display
) < B_OK
) {
1146 puts("Missing expression text from CalcView archive.\n");
1148 // init expression text
1149 fExpressionTextView
->SetText(display
);
1152 // load expression history
1153 fExpressionTextView
->LoadSettings(archive
);
1155 // parse calculator description
1156 _ParseCalcDesc(fKeypadDescription
);
1158 // colorize based on base color.
1166 CalcView::_ParseCalcDesc(const char* keypadDescription
)
1168 // TODO: should calculate dimensions from desc here!
1169 fKeypad
= new CalcKey
[fRows
* fColumns
];
1171 // scan through calculator description and assemble keypad
1172 CalcKey
* key
= fKeypad
;
1173 const char* p
= keypadDescription
;
1177 char* l
= key
->label
;
1178 while (!isspace(*p
))
1183 if (strcmp(key
->label
, "=") == 0)
1184 strlcpy(key
->code
, "\n", sizeof(key
->code
));
1186 strlcpy(key
->code
, key
->label
, sizeof(key
->code
));
1189 if (strlen(key
->label
) == 1)
1190 strlcpy(key
->keymap
, key
->label
, sizeof(key
->keymap
));
1192 *key
->keymap
= '\0';
1196 // add this to the expression text view, so that it
1197 // will forward the respective KeyDown event to us
1198 fExpressionTextView
->AddKeypadLabel(key
->label
);
1209 CalcView::_PressKey(int key
)
1214 assert(key
< (fRows
* fColumns
));
1217 if (strcmp(fKeypad
[key
].label
, "BS") == 0) {
1218 // BS means backspace
1219 fExpressionTextView
->BackSpace();
1220 } else if (strcmp(fKeypad
[key
].label
, "C") == 0) {
1222 fExpressionTextView
->Clear();
1223 } else if (strcmp(fKeypad
[key
].label
, "acos") == 0
1224 || strcmp(fKeypad
[key
].label
, "asin") == 0
1225 || strcmp(fKeypad
[key
].label
, "atan") == 0
1226 || strcmp(fKeypad
[key
].label
, "cbrt") == 0
1227 || strcmp(fKeypad
[key
].label
, "ceil") == 0
1228 || strcmp(fKeypad
[key
].label
, "cos") == 0
1229 || strcmp(fKeypad
[key
].label
, "cosh") == 0
1230 || strcmp(fKeypad
[key
].label
, "exp") == 0
1231 || strcmp(fKeypad
[key
].label
, "floor") == 0
1232 || strcmp(fKeypad
[key
].label
, "log") == 0
1233 || strcmp(fKeypad
[key
].label
, "ln") == 0
1234 || strcmp(fKeypad
[key
].label
, "sin") == 0
1235 || strcmp(fKeypad
[key
].label
, "sinh") == 0
1236 || strcmp(fKeypad
[key
].label
, "sqrt") == 0
1237 || strcmp(fKeypad
[key
].label
, "tan") == 0
1238 || strcmp(fKeypad
[key
].label
, "tanh") == 0) {
1239 int32 labelLen
= strlen(fKeypad
[key
].label
);
1240 int32 startSelection
= 0;
1241 int32 endSelection
= 0;
1242 fExpressionTextView
->GetSelection(&startSelection
, &endSelection
);
1243 if (endSelection
> startSelection
) {
1244 // There is selected text, put it inbetween the parens
1245 fExpressionTextView
->Insert(startSelection
, fKeypad
[key
].label
,
1247 fExpressionTextView
->Insert(startSelection
+ labelLen
, "(", 1);
1248 fExpressionTextView
->Insert(endSelection
+ labelLen
+ 1, ")", 1);
1249 // Put the cursor after the ending paren
1250 // Need to cast to BTextView because Select() is protected
1251 // in the InputTextView class
1252 static_cast<BTextView
*>(fExpressionTextView
)->Select(
1253 endSelection
+ labelLen
+ 2, endSelection
+ labelLen
+ 2);
1255 // There is no selected text, insert at the cursor location
1256 fExpressionTextView
->Insert(fKeypad
[key
].label
);
1257 fExpressionTextView
->Insert("()");
1258 // Put the cursor inside the parens so you can enter an argument
1259 // Need to cast to BTextView because Select() is protected
1260 // in the InputTextView class
1261 static_cast<BTextView
*>(fExpressionTextView
)->Select(
1262 endSelection
+ labelLen
+ 1, endSelection
+ labelLen
+ 1);
1265 // check for evaluation order
1266 if (fKeypad
[key
].code
[0] == '\n') {
1267 fExpressionTextView
->ApplyChanges();
1269 // insert into expression text
1270 fExpressionTextView
->Insert(fKeypad
[key
].code
);
1274 _AudioFeedback(true);
1279 CalcView::_PressKey(const char* label
)
1281 int32 key
= _KeyForLabel(label
);
1288 CalcView::_KeyForLabel(const char* label
) const
1290 int keys
= fRows
* fColumns
;
1291 for (int i
= 0; i
< keys
; i
++) {
1292 if (strcmp(fKeypad
[i
].label
, label
) == 0) {
1301 CalcView::_FlashKey(int32 key
, uint32 flashFlags
)
1303 if (fOptions
->keypad_mode
== KEYPAD_MODE_COMPACT
)
1306 if (flashFlags
!= 0)
1307 fKeypad
[key
].flags
|= flashFlags
;
1309 fKeypad
[key
].flags
= 0;
1312 if (fKeypad
[key
].flags
== FLAGS_FLASH_KEY
) {
1313 BMessage
message(MSG_UNFLASH_KEY
);
1314 message
.AddInt32("key", key
);
1315 BMessageRunner::StartSending(BMessenger(this), &message
,
1316 kFlashOnOffInterval
, 1);
1322 CalcView::_AudioFeedback(bool inBackGround
)
1324 // TODO: Use beep events... This interface is not implemented on Haiku
1327 if (fOptions
->audio_feedback
) {
1328 BEntry
zimp("key.AIFF");
1330 zimp
.GetRef(&zimp_ref
);
1331 play_sound(&zimp_ref
, true, false, inBackGround
);
1338 CalcView::_Colorize()
1340 // calculate light and dark color from base color
1341 fLightColor
.red
= (uint8
)(fBaseColor
.red
* 1.25);
1342 fLightColor
.green
= (uint8
)(fBaseColor
.green
* 1.25);
1343 fLightColor
.blue
= (uint8
)(fBaseColor
.blue
* 1.25);
1344 fLightColor
.alpha
= 255;
1346 fDarkColor
.red
= (uint8
)(fBaseColor
.red
* 0.75);
1347 fDarkColor
.green
= (uint8
)(fBaseColor
.green
* 0.75);
1348 fDarkColor
.blue
= (uint8
)(fBaseColor
.blue
* 0.75);
1349 fDarkColor
.alpha
= 255;
1351 // keypad text color
1352 if (fBaseColor
.Brightness() > 100)
1353 fButtonTextColor
= (rgb_color
){ 0, 0, 0, 255 };
1355 fButtonTextColor
= (rgb_color
){ 255, 255, 255, 255 };
1357 // expression text color
1358 if (fExpressionBGColor
.Brightness() > 100)
1359 fExpressionTextColor
= (rgb_color
){ 0, 0, 0, 255 };
1361 fExpressionTextColor
= (rgb_color
){ 255, 255, 255, 255 };
1366 CalcView::_CreatePopUpMenu(bool addKeypadModeMenuItems
)
1369 fAutoNumlockItem
= new BMenuItem(B_TRANSLATE("Enable Num Lock on startup"),
1370 new BMessage(MSG_OPTIONS_AUTO_NUM_LOCK
));
1371 fAudioFeedbackItem
= new BMenuItem(B_TRANSLATE("Audio Feedback"),
1372 new BMessage(MSG_OPTIONS_AUDIO_FEEDBACK
));
1373 fAngleModeRadianItem
= new BMenuItem(B_TRANSLATE("Radians"),
1374 new BMessage(MSG_OPTIONS_ANGLE_MODE_RADIAN
));
1375 fAngleModeDegreeItem
= new BMenuItem(B_TRANSLATE("Degrees"),
1376 new BMessage(MSG_OPTIONS_ANGLE_MODE_DEGREE
));
1377 if (addKeypadModeMenuItems
) {
1378 fKeypadModeCompactItem
= new BMenuItem(B_TRANSLATE("Compact"),
1379 new BMessage(MSG_OPTIONS_KEYPAD_MODE_COMPACT
), '0');
1380 fKeypadModeBasicItem
= new BMenuItem(B_TRANSLATE("Basic"),
1381 new BMessage(MSG_OPTIONS_KEYPAD_MODE_BASIC
), '1');
1382 fKeypadModeScientificItem
= new BMenuItem(B_TRANSLATE("Scientific"),
1383 new BMessage(MSG_OPTIONS_KEYPAD_MODE_SCIENTIFIC
), '2');
1386 // apply current settings
1387 fAutoNumlockItem
->SetMarked(fOptions
->auto_num_lock
);
1388 fAudioFeedbackItem
->SetMarked(fOptions
->audio_feedback
);
1389 fAngleModeRadianItem
->SetMarked(!fOptions
->degree_mode
);
1390 fAngleModeDegreeItem
->SetMarked(fOptions
->degree_mode
);
1393 fPopUpMenu
= new BPopUpMenu("pop-up", false, false);
1395 fPopUpMenu
->AddItem(fAutoNumlockItem
);
1396 // TODO: Enable this when we use beep events which can be configured
1397 // in the Sounds preflet.
1398 //fPopUpMenu->AddItem(fAudioFeedbackItem);
1399 fPopUpMenu
->AddSeparatorItem();
1400 fPopUpMenu
->AddItem(fAngleModeRadianItem
);
1401 fPopUpMenu
->AddItem(fAngleModeDegreeItem
);
1402 if (addKeypadModeMenuItems
) {
1403 fPopUpMenu
->AddSeparatorItem();
1404 fPopUpMenu
->AddItem(fKeypadModeCompactItem
);
1405 fPopUpMenu
->AddItem(fKeypadModeBasicItem
);
1406 fPopUpMenu
->AddItem(fKeypadModeScientificItem
);
1407 _MarkKeypadItems(fOptions
->keypad_mode
);
1413 CalcView::_ExpressionRect() const
1415 BRect
r(0.0, 0.0, fWidth
, fHeight
);
1416 if (fOptions
->keypad_mode
!= KEYPAD_MODE_COMPACT
) {
1417 r
.bottom
= floorf(fHeight
* kDisplayScaleY
) + 1;
1424 CalcView::_KeypadRect() const
1426 BRect
r(0.0, 0.0, -1.0, -1.0);
1427 if (fOptions
->keypad_mode
!= KEYPAD_MODE_COMPACT
) {
1430 r
.top
= floorf(fHeight
* kDisplayScaleY
);
1437 CalcView::_MarkKeypadItems(uint8 keypad_mode
)
1439 switch (keypad_mode
) {
1440 case KEYPAD_MODE_COMPACT
:
1441 fKeypadModeCompactItem
->SetMarked(true);
1442 fKeypadModeBasicItem
->SetMarked(false);
1443 fKeypadModeScientificItem
->SetMarked(false);
1446 case KEYPAD_MODE_SCIENTIFIC
:
1447 fKeypadModeCompactItem
->SetMarked(false);
1448 fKeypadModeBasicItem
->SetMarked(false);
1449 fKeypadModeScientificItem
->SetMarked(true);
1452 default: // KEYPAD_MODE_BASIC is the default
1453 fKeypadModeCompactItem
->SetMarked(false);
1454 fKeypadModeBasicItem
->SetMarked(true);
1455 fKeypadModeScientificItem
->SetMarked(false);
1461 CalcView::_FetchAppIcon(BBitmap
* into
)
1464 status_t status
= be_roster
->FindApp(kSignature
, &appRef
);
1465 if (status
== B_OK
) {
1466 BFile
file(&appRef
, B_READ_ONLY
);
1467 BAppFileInfo
appInfo(&file
);
1468 status
= appInfo
.GetIcon(into
, B_MINI_ICON
);
1471 memset(into
->Bits(), 0, into
->BitsLength());
1475 // Returns whether or not CalcView is embedded somewhere, most likely
1478 CalcView::_IsEmbedded()
1480 return Parent() != NULL
&& (Parent()->Flags() & B_DRAW_ON_CHILDREN
) != 0;
1485 CalcView::_SetEnabled(bool enable
)
1488 fExpressionTextView
->MakeSelectable(enable
);
1489 fExpressionTextView
->MakeEditable(enable
);