1 /*
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.
5 *
6 * Authors:
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
11 */
12
13
14 #include "CalcView.h"
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <assert.h>
21
22 #include <AboutWindow.h>
23 #include <Alert.h>
24 #include <Application.h>
25 #include <AppFileInfo.h>
26 #include <AutoLocker.h>
27 #include <Beep.h>
28 #include <Bitmap.h>
29 #include <Catalog.h>
30 #include <ControlLook.h>
31 #include <Clipboard.h>
32 #include <File.h>
33 #include <Font.h>
34 #include <Locale.h>
35 #include <MenuItem.h>
36 #include <Message.h>
37 #include <MessageRunner.h>
38 #include <NumberFormat.h>
39 #include <Point.h>
40 #include <PopUpMenu.h>
41 #include <Region.h>
42 #include <Roster.h>
43
44 #include <ExpressionParser.h>
45
46 #include "CalcApplication.h"
47 #include "CalcOptions.h"
48 #include "ExpressionTextView.h"
49
50
51 #undef B_TRANSLATION_CONTEXT
52 #define B_TRANSLATION_CONTEXT "CalcView"
53
54
55 static const int32 kMsgCalculating = 'calc';
56 static const int32 kMsgAnimateDots = 'dots';
57 static const int32 kMsgDoneEvaluating = 'done';
58
59 //const uint8 K_COLOR_OFFSET = 32;
60 const float kFontScaleY = 0.4f;
61 const float kFontScaleX = 0.4f;
62 const float kExpressionFontScaleY = 0.6f;
63 const float kDisplayScaleY = 0.2f;
64
65 static const bigtime_t kFlashOnOffInterval = 100000;
66 static const bigtime_t kCalculatingInterval = 1000000;
67 static const bigtime_t kAnimationInterval = 333333;
68
69 static const float kMinimumWidthCompact = 130.0f;
70 static const float kMaximumWidthCompact = 400.0f;
71 static const float kMinimumHeightCompact = 20.0f;
72 static const float kMaximumHeightCompact = 60.0f;
73
74 // Basic mode size limits are defined in CalcView.h so
75 // that they can be used by the CalcWindow constructor.
76
77 static const float kMinimumWidthScientific = 240.0f;
78 static const float kMaximumWidthScientific = 400.0f;
79 static const float kMinimumHeightScientific = 200.0f;
80 static const float kMaximumHeightScientific = 400.0f;
81
82 // basic mode keypad layout (default)
83 const char *kKeypadDescriptionBasic[] = {
84 B_TRANSLATE_MARK("7"),
85 B_TRANSLATE_MARK("8"),
86 B_TRANSLATE_MARK("9"),
87 B_TRANSLATE_MARK("("),
88 B_TRANSLATE_MARK(")"),
89 "\n",
90 B_TRANSLATE_MARK("4"),
91 B_TRANSLATE_MARK("5"),
92 B_TRANSLATE_MARK("6"),
93 B_TRANSLATE_MARK("*"),
94 B_TRANSLATE_MARK("/"),
95 "\n",
96 B_TRANSLATE_MARK("1"),
97 B_TRANSLATE_MARK("2"),
98 B_TRANSLATE_MARK("3"),
99 B_TRANSLATE_MARK("+"),
100 B_TRANSLATE_MARK("-"),
101 "\n",
102 B_TRANSLATE_MARK("0"),
103 B_TRANSLATE_MARK("."),
104 B_TRANSLATE_MARK("BS"),
105 B_TRANSLATE_MARK("="),
106 B_TRANSLATE_MARK("C"),
107 "\n",
108 NULL
109 };
110
111 // scientific mode keypad layout
112 const char *kKeypadDescriptionScientific[] = {
113 B_TRANSLATE_MARK("ln"),
114 B_TRANSLATE_MARK("sin"),
115 B_TRANSLATE_MARK("cos"),
116 B_TRANSLATE_MARK("tan"),
117 B_TRANSLATE_MARK("π"),
118 "\n",
119 B_TRANSLATE_MARK("log"),
120 B_TRANSLATE_MARK("asin"),
121 B_TRANSLATE_MARK("acos"),
122 B_TRANSLATE_MARK("atan"),
123 B_TRANSLATE_MARK("sqrt"),
124 "\n",
125 B_TRANSLATE_MARK("exp"),
126 B_TRANSLATE_MARK("sinh"),
127 B_TRANSLATE_MARK("cosh"),
128 B_TRANSLATE_MARK("tanh"),
129 B_TRANSLATE_MARK("cbrt"),
130 "\n",
131 B_TRANSLATE_MARK("!"),
132 B_TRANSLATE_MARK("ceil"),
133 B_TRANSLATE_MARK("floor"),
134 B_TRANSLATE_MARK("E"),
135 B_TRANSLATE_MARK("^"),
136 "\n",
137 B_TRANSLATE_MARK("7"),
138 B_TRANSLATE_MARK("8"),
139 B_TRANSLATE_MARK("9"),
140 B_TRANSLATE_MARK("("),
141 B_TRANSLATE_MARK(")"),
142 "\n",
143 B_TRANSLATE_MARK("4"),
144 B_TRANSLATE_MARK("5"),
145 B_TRANSLATE_MARK("6"),
146 B_TRANSLATE_MARK("*"),
147 B_TRANSLATE_MARK("/"),
148 "\n",
149 B_TRANSLATE_MARK("1"),
150 B_TRANSLATE_MARK("2"),
151 B_TRANSLATE_MARK("3"),
152 B_TRANSLATE_MARK("+"),
153 B_TRANSLATE_MARK("-"),
154 "\n",
155 B_TRANSLATE_MARK("0"),
156 B_TRANSLATE_MARK("."),
157 B_TRANSLATE_MARK("BS"),
158 B_TRANSLATE_MARK("="),
159 B_TRANSLATE_MARK("C"),
160 "\n",
161 NULL
162 };
163
164
165 enum {
166 FLAGS_FLASH_KEY = 1 << 0,
167 FLAGS_MOUSE_DOWN = 1 << 1
168 };
169
170
171 struct CalcView::CalcKey {
172 char label[8];
173 char code[8];
174 char keymap[4];
175 uint32 flags;
176 // float width;
177 };
178
179
180 typedef AutoLocker<BClipboard> ClipboardLocker;
181
182
183 CalcView*
Instantiate(BMessage * archive)184 CalcView::Instantiate(BMessage* archive)
185 {
186 if (!validate_instantiation(archive, "CalcView"))
187 return NULL;
188
189 return new CalcView(archive);
190 }
191
192
CalcView(BRect frame,rgb_color rgbBaseColor,BMessage * settings)193 CalcView::CalcView(BRect frame, rgb_color rgbBaseColor, BMessage* settings)
194 :
195 BView(frame, "DeskCalc", B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_FRAME_EVENTS),
196
197 fColumns(5),
198 fRows(4),
199
200 fBaseColor(rgbBaseColor),
201 fHasCustomBaseColor(rgbBaseColor != ui_color(B_PANEL_BACKGROUND_COLOR)),
202
203 fWidth(1),
204 fHeight(1),
205
206 fKeypadDescription(kKeypadDescriptionBasic),
207 fKeypad(NULL),
208
209 fCalcIcon(new BBitmap(BRect(0, 0, 15, 15), 0, B_RGBA32)),
210
211 fPopUpMenu(NULL),
212 fAutoNumlockItem(NULL),
213 fOptions(new CalcOptions()),
214 fEvaluateThread(-1),
215 fEvaluateMessageRunner(NULL),
216 fEvaluateSemaphore(B_BAD_SEM_ID),
217 fEnabled(true)
218 {
219 // tell the app server not to erase our b/g
220 SetViewColor(B_TRANSPARENT_32_BIT);
221
222 _Init(settings);
223 }
224
225
CalcView(BMessage * archive)226 CalcView::CalcView(BMessage* archive)
227 :
228 BView(archive),
229
230 fColumns(5),
231 fRows(4),
232
233 fBaseColor(ui_color(B_PANEL_BACKGROUND_COLOR)),
234
235 fHasCustomBaseColor(false),
236
237 fWidth(1),
238 fHeight(1),
239
240 fKeypadDescription(kKeypadDescriptionBasic),
241 fKeypad(NULL),
242
243 fCalcIcon(new BBitmap(BRect(0, 0, 15, 15), 0, B_RGBA32)),
244
245 fPopUpMenu(NULL),
246 fAutoNumlockItem(NULL),
247 fOptions(new CalcOptions()),
248 fEvaluateThread(-1),
249 fEvaluateMessageRunner(NULL),
250 fEvaluateSemaphore(B_BAD_SEM_ID),
251 fEnabled(true)
252 {
253 // Do not restore the follow mode, in shelfs, we never follow.
254 SetResizingMode(B_FOLLOW_NONE);
255
256 _Init(archive);
257 }
258
259
~CalcView()260 CalcView::~CalcView()
261 {
262 delete[] fKeypad;
263 delete fOptions;
264 delete fEvaluateMessageRunner;
265 delete_sem(fEvaluateSemaphore);
266 }
267
268
269 void
AttachedToWindow()270 CalcView::AttachedToWindow()
271 {
272 if (be_control_look == NULL)
273 SetFont(be_bold_font);
274
275 BRect frame(Frame());
276 FrameResized(frame.Width(), frame.Height());
277
278 bool addKeypadModeMenuItems = true;
279 if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0) {
280 // don't add these items if we are a replicant on the desktop
281 addKeypadModeMenuItems = false;
282 }
283
284 // create and attach the pop-up menu
285 _CreatePopUpMenu(addKeypadModeMenuItems);
286
287 if (addKeypadModeMenuItems)
288 SetKeypadMode(fOptions->keypad_mode);
289 }
290
291
292 void
MessageReceived(BMessage * message)293 CalcView::MessageReceived(BMessage* message)
294 {
295 if (message->what == B_COLORS_UPDATED) {
296 const char* panelBgColorName = ui_color_name(B_PANEL_BACKGROUND_COLOR);
297 if (message->HasColor(panelBgColorName) && !fHasCustomBaseColor) {
298 fBaseColor = message->GetColor(panelBgColorName, fBaseColor);
299 _Colorize();
300 }
301 if (message->HasColor(ui_color_name(B_PANEL_TEXT_COLOR)))
302 _Colorize();
303
304 return;
305 }
306
307 if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0) {
308 // if we are embedded in desktop we need to receive these
309 // message here since we don't have a parent BWindow
310 switch (message->what) {
311 case MSG_OPTIONS_AUTO_NUM_LOCK:
312 ToggleAutoNumlock();
313 return;
314
315 case MSG_OPTIONS_ANGLE_MODE_RADIAN:
316 SetDegreeMode(false);
317 return;
318
319 case MSG_OPTIONS_ANGLE_MODE_DEGREE:
320 SetDegreeMode(true);
321 return;
322 }
323 }
324
325 // check if message was dropped
326 if (message->WasDropped()) {
327 // pass message on to paste
328 if (message->IsSourceRemote())
329 Paste(message);
330 } else {
331 // act on posted message type
332 switch (message->what) {
333
334 // handle "cut"
335 case B_CUT:
336 Cut();
337 break;
338
339 // handle copy
340 case B_COPY:
341 Copy();
342 break;
343
344 // handle paste
345 case B_PASTE:
346 {
347 // access system clipboard
348 ClipboardLocker locker(be_clipboard);
349 if (locker.IsLocked()) {
350 BMessage* clipper = be_clipboard->Data();
351 if (clipper)
352 Paste(clipper);
353 }
354 break;
355 }
356
357 // (replicant) about box requested
358 case B_ABOUT_REQUESTED:
359 {
360 BAboutWindow* window = new BAboutWindow(kAppName, kSignature);
361
362 // create the about window
363 const char* extraCopyrights[] = {
364 "1997, 1998 R3 Software Ltd.",
365 NULL
366 };
367
368 const char* authors[] = {
369 "Stephan Aßmus",
370 "John Scipione",
371 "Timothy Wayper",
372 "Ingo Weinhold",
373 NULL
374 };
375
376 window->AddCopyright(2006, "Haiku, Inc.", extraCopyrights);
377 window->AddAuthors(authors);
378
379 window->Show();
380
381 break;
382 }
383
384 case MSG_UNFLASH_KEY:
385 {
386 int32 key;
387 if (message->FindInt32("key", &key) == B_OK)
388 _FlashKey(key, 0);
389
390 break;
391 }
392
393 case kMsgAnimateDots:
394 {
395 int32 end = fExpressionTextView->TextLength();
396 int32 start = end - 3;
397 if (fEnabled || strcmp(fExpressionTextView->Text() + start,
398 "...") != 0) {
399 // stop the message runner
400 delete fEvaluateMessageRunner;
401 fEvaluateMessageRunner = NULL;
402 break;
403 }
404
405 uint8 dot = 0;
406 if (message->FindUInt8("dot", &dot) == B_OK) {
407 rgb_color fontColor = fExpressionTextView->HighColor();
408 rgb_color backColor = fExpressionTextView->LowColor();
409 fExpressionTextView->SetStylable(true);
410 fExpressionTextView->SetFontAndColor(start, end, NULL, 0,
411 &backColor);
412 fExpressionTextView->SetFontAndColor(start + dot - 1,
413 start + dot, NULL, 0, &fontColor);
414 fExpressionTextView->SetStylable(false);
415 }
416
417 dot++;
418 if (dot == 4)
419 dot = 1;
420
421 delete fEvaluateMessageRunner;
422 BMessage animate(kMsgAnimateDots);
423 animate.AddUInt8("dot", dot);
424 fEvaluateMessageRunner = new (std::nothrow) BMessageRunner(
425 BMessenger(this), &animate, kAnimationInterval, 1);
426 break;
427 }
428
429 case kMsgCalculating:
430 {
431 // calculation has taken more than 3 seconds
432 if (fEnabled) {
433 // stop the message runner
434 delete fEvaluateMessageRunner;
435 fEvaluateMessageRunner = NULL;
436 break;
437 }
438
439 BString calculating;
440 calculating << B_TRANSLATE("Calculating") << "...";
441 fExpressionTextView->SetText(calculating.String());
442
443 delete fEvaluateMessageRunner;
444 BMessage animate(kMsgAnimateDots);
445 animate.AddUInt8("dot", 1U);
446 fEvaluateMessageRunner = new (std::nothrow) BMessageRunner(
447 BMessenger(this), &animate, kAnimationInterval, 1);
448 break;
449 }
450
451 case kMsgDoneEvaluating:
452 {
453 _SetEnabled(true);
454 rgb_color fontColor = fExpressionTextView->HighColor();
455 fExpressionTextView->SetFontAndColor(NULL, 0, &fontColor);
456
457 const char* result;
458 if (message->FindString("error", &result) == B_OK)
459 fExpressionTextView->SetText(result);
460 else if (message->FindString("value", &result) == B_OK) {
461 BLocale locale;
462 BNumberFormat format(&locale);
463
464 fExpressionTextView->SetValue(result, format.GetSeparator(B_DECIMAL_SEPARATOR));
465 }
466
467 // stop the message runner
468 delete fEvaluateMessageRunner;
469 fEvaluateMessageRunner = NULL;
470 break;
471 }
472
473 default:
474 BView::MessageReceived(message);
475 break;
476 }
477 }
478 }
479
480
481 void
Draw(BRect updateRect)482 CalcView::Draw(BRect updateRect)
483 {
484 bool drawBackground = !_IsEmbedded();
485
486 SetHighColor(fBaseColor);
487 BRect expressionRect(_ExpressionRect());
488 if (updateRect.Intersects(expressionRect)) {
489 if (fOptions->keypad_mode == KEYPAD_MODE_COMPACT
490 && expressionRect.Height() >= fCalcIcon->Bounds().Height()) {
491 // render calc icon
492 expressionRect.left = fExpressionTextView->Frame().right + 2;
493 if (drawBackground) {
494 SetHighColor(fBaseColor);
495 FillRect(updateRect & expressionRect);
496 }
497
498 SetDrawingMode(B_OP_ALPHA);
499 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
500
501 BPoint iconPos;
502 iconPos.x = expressionRect.right - (expressionRect.Width()
503 + fCalcIcon->Bounds().Width()) / 2.0;
504 iconPos.y = expressionRect.top + (expressionRect.Height()
505 - fCalcIcon->Bounds().Height()) / 2.0;
506 DrawBitmap(fCalcIcon, iconPos);
507
508 SetDrawingMode(B_OP_COPY);
509 }
510
511 // render border around expression text view
512 expressionRect = fExpressionTextView->Frame();
513 expressionRect.InsetBy(-2, -2);
514 if (fOptions->keypad_mode != KEYPAD_MODE_COMPACT && drawBackground) {
515 expressionRect.InsetBy(-2, -2);
516 StrokeRect(expressionRect);
517 expressionRect.InsetBy(1, 1);
518 StrokeRect(expressionRect);
519 expressionRect.InsetBy(1, 1);
520 }
521
522 uint32 flags = 0;
523 if (!drawBackground)
524 flags |= BControlLook::B_BLEND_FRAME;
525 be_control_look->DrawTextControlBorder(this, expressionRect,
526 updateRect, fBaseColor, flags);
527 }
528
529 if (fOptions->keypad_mode == KEYPAD_MODE_COMPACT)
530 return;
531
532 // calculate grid sizes
533 BRect keypadRect(_KeypadRect());
534
535 if (be_control_look != NULL) {
536 if (drawBackground)
537 StrokeRect(keypadRect);
538 keypadRect.InsetBy(1, 1);
539 }
540
541 float sizeDisp = keypadRect.top;
542 float sizeCol = (keypadRect.Width() + 1) / (float)fColumns;
543 float sizeRow = (keypadRect.Height() + 1) / (float)fRows;
544
545 if (!updateRect.Intersects(keypadRect))
546 return;
547
548 SetFontSize(min_c(sizeRow * kFontScaleY, sizeCol * kFontScaleX));
549
550 CalcKey* key = fKeypad;
551 for (int row = 0; row < fRows; row++) {
552 for (int col = 0; col < fColumns; col++) {
553 BRect frame;
554 frame.left = keypadRect.left + col * sizeCol;
555 frame.right = keypadRect.left + (col + 1) * sizeCol - 1;
556 frame.top = sizeDisp + row * sizeRow;
557 frame.bottom = sizeDisp + (row + 1) * sizeRow - 1;
558
559 if (drawBackground) {
560 SetHighColor(fBaseColor);
561 StrokeRect(frame);
562 }
563 frame.InsetBy(1, 1);
564
565 uint32 flags = 0;
566 if (!drawBackground)
567 flags |= BControlLook::B_BLEND_FRAME;
568 if (key->flags != 0)
569 flags |= BControlLook::B_ACTIVATED;
570 flags |= BControlLook::B_IGNORE_OUTLINE;
571
572 be_control_look->DrawButtonFrame(this, frame, updateRect,
573 fBaseColor, fBaseColor, flags);
574
575 be_control_look->DrawButtonBackground(this, frame, updateRect,
576 fBaseColor, flags);
577
578 be_control_look->DrawLabel(this, key->label, frame, updateRect,
579 fBaseColor, flags, BAlignment(B_ALIGN_HORIZONTAL_CENTER,
580 B_ALIGN_VERTICAL_CENTER), &fButtonTextColor);
581
582 key++;
583 }
584 }
585 }
586
587
588 void
MouseDown(BPoint point)589 CalcView::MouseDown(BPoint point)
590 {
591 // ensure this view is the current focus
592 if (!fExpressionTextView->IsFocus()) {
593 // Call our version of MakeFocus(), since that will also apply the
594 // num_lock setting.
595 MakeFocus();
596 }
597
598 // read mouse buttons state
599 int32 buttons = 0;
600 Window()->CurrentMessage()->FindInt32("buttons", &buttons);
601
602 if ((B_PRIMARY_MOUSE_BUTTON & buttons) == 0) {
603 // display popup menu if not primary mouse button
604 BMenuItem* selected;
605 if ((selected = fPopUpMenu->Go(ConvertToScreen(point))) != NULL
606 && selected->Message() != NULL) {
607 Window()->PostMessage(selected->Message(), this);
608 }
609 return;
610 }
611
612 if (fOptions->keypad_mode == KEYPAD_MODE_COMPACT) {
613 if (fCalcIcon != NULL) {
614 BRect bounds(Bounds());
615 bounds.left = bounds.right - fCalcIcon->Bounds().Width();
616 if (bounds.Contains(point)) {
617 // user clicked on calculator icon
618 fExpressionTextView->Clear();
619 }
620 }
621 return;
622 }
623
624 // calculate grid sizes
625 float sizeDisp = fHeight * kDisplayScaleY;
626 float sizeCol = fWidth / (float)fColumns;
627 float sizeRow = (fHeight - sizeDisp) / (float)fRows;
628
629 // calculate location within grid
630 int gridCol = (int)floorf(point.x / sizeCol);
631 int gridRow = (int)floorf((point.y - sizeDisp) / sizeRow);
632
633 // check limits
634 if ((gridCol >= 0) && (gridCol < fColumns)
635 && (gridRow >= 0) && (gridRow < fRows)) {
636
637 // process key press
638 int key = gridRow * fColumns + gridCol;
639 _FlashKey(key, FLAGS_MOUSE_DOWN);
640 _PressKey(key);
641
642 // make sure we receive the mouse up!
643 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
644 }
645 }
646
647
648 void
MouseUp(BPoint point)649 CalcView::MouseUp(BPoint point)
650 {
651 if (fOptions->keypad_mode == KEYPAD_MODE_COMPACT)
652 return;
653
654 int keys = fRows * fColumns;
655 for (int i = 0; i < keys; i++) {
656 if (fKeypad[i].flags & FLAGS_MOUSE_DOWN) {
657 _FlashKey(i, 0);
658 break;
659 }
660 }
661 }
662
663
664 void
KeyDown(const char * bytes,int32 numBytes)665 CalcView::KeyDown(const char* bytes, int32 numBytes)
666 {
667 // if single byte character...
668 if (numBytes == 1) {
669
670 //printf("Key pressed: %c\n", bytes[0]);
671
672 switch (bytes[0]) {
673
674 case B_ENTER:
675 // translate to evaluate key
676 _PressKey("=");
677 break;
678
679 case B_LEFT_ARROW:
680 case B_BACKSPACE:
681 // translate to backspace key
682 _PressKey("BS");
683 break;
684
685 case B_SPACE:
686 case B_ESCAPE:
687 case 'c':
688 // translate to clear key
689 _PressKey("C");
690 break;
691
692 // bracket translation
693 case '[':
694 case '{':
695 _PressKey("(");
696 break;
697
698 case ']':
699 case '}':
700 _PressKey(")");
701 break;
702
703 default: {
704 // scan the keymap array for match
705 int keys = fRows * fColumns;
706 for (int i = 0; i < keys; i++) {
707 if (fKeypad[i].keymap[0] == bytes[0]) {
708 _PressKey(i);
709 return;
710 }
711 }
712 break;
713 }
714 }
715 }
716 }
717
718
719 void
MakeFocus(bool focused)720 CalcView::MakeFocus(bool focused)
721 {
722 if (focused) {
723 // set num lock
724 if (fOptions->auto_num_lock) {
725 set_keyboard_locks(B_NUM_LOCK
726 | (modifiers() & (B_CAPS_LOCK | B_SCROLL_LOCK)));
727 }
728 }
729
730 // pass on request to text view
731 fExpressionTextView->MakeFocus(focused);
732 }
733
734
735 void
FrameResized(float width,float height)736 CalcView::FrameResized(float width, float height)
737 {
738 fWidth = width;
739 fHeight = height;
740
741 // layout expression text view
742 BRect expressionRect = _ExpressionRect();
743 if (fOptions->keypad_mode == KEYPAD_MODE_COMPACT) {
744 expressionRect.InsetBy(2, 2);
745 expressionRect.right -= ceilf(fCalcIcon->Bounds().Width() * 1.5);
746 } else
747 expressionRect.InsetBy(4, 4);
748
749 fExpressionTextView->MoveTo(expressionRect.LeftTop());
750 fExpressionTextView->ResizeTo(expressionRect.Width(), expressionRect.Height());
751
752 // configure expression text view font size and color
753 float sizeDisp = fOptions->keypad_mode == KEYPAD_MODE_COMPACT
754 ? fHeight : fHeight * kDisplayScaleY;
755 BFont font(be_bold_font);
756 font.SetSize(sizeDisp * kExpressionFontScaleY);
757 rgb_color fontColor = fExpressionTextView->HighColor();
758 fExpressionTextView->SetFontAndColor(&font, B_FONT_ALL, &fontColor);
759
760 expressionRect.OffsetTo(B_ORIGIN);
761 fExpressionTextView->SetTextRect(expressionRect);
762 Invalidate();
763 }
764
765
766 status_t
Archive(BMessage * archive,bool deep) const767 CalcView::Archive(BMessage* archive, bool deep) const
768 {
769 fExpressionTextView->RemoveSelf();
770
771 // passed on request to parent
772 status_t ret = BView::Archive(archive, deep);
773
774 const_cast<CalcView*>(this)->AddChild(fExpressionTextView);
775
776 // save app signature for replicant add-on loading
777 if (ret == B_OK)
778 ret = archive->AddString("add_on", kSignature);
779
780 // save all the options
781 if (ret == B_OK)
782 ret = SaveSettings(archive);
783
784 // add class info last
785 if (ret == B_OK)
786 ret = archive->AddString("class", "CalcView");
787
788 return ret;
789 }
790
791
792 void
Cut()793 CalcView::Cut()
794 {
795 Copy(); // copy data to clipboard
796 fExpressionTextView->Clear(); // remove data
797 }
798
799
800 void
Copy()801 CalcView::Copy()
802 {
803 // access system clipboard
804 ClipboardLocker locker(be_clipboard);
805 if (!locker.IsLocked())
806 return;
807
808 if (be_clipboard->Clear() != B_OK)
809 return;
810
811 BMessage* clipper = be_clipboard->Data();
812 if (clipper == NULL)
813 return;
814
815 BString expression = fExpressionTextView->Text();
816 if (clipper->AddData("text/plain", B_MIME_TYPE,
817 expression.String(), expression.Length()) == B_OK) {
818 clipper->what = B_MIME_DATA;
819 be_clipboard->Commit();
820 }
821 }
822
823
824 void
Paste(BMessage * message)825 CalcView::Paste(BMessage* message)
826 {
827 // handle files first
828 int32 count;
829 if (message->GetInfo("refs", NULL, &count) == B_OK) {
830 entry_ref ref;
831 ssize_t read;
832 BFile file;
833 char buffer[256];
834 memset(buffer, 0, sizeof(buffer));
835 for (int32 i = 0; i < count; i++) {
836 if (message->FindRef("refs", i, &ref) == B_OK) {
837 if (file.SetTo(&ref, B_READ_ONLY) == B_OK) {
838 read = file.Read(buffer, sizeof(buffer) - 1);
839 if (read <= 0)
840 continue;
841 BString expression(buffer);
842 int32 j = expression.Length();
843 while (j > 0 && expression[j - 1] == '\n')
844 j--;
845 expression.Truncate(j);
846 if (expression.Length() > 0)
847 fExpressionTextView->Insert(expression.String());
848 }
849 }
850 }
851 return;
852 }
853 // handle color drops
854 // read incoming color
855 const rgb_color* dropColor = NULL;
856 ssize_t dataSize;
857 if (message->FindData("RGBColor", B_RGB_COLOR_TYPE,
858 (const void**)&dropColor, &dataSize) == B_OK
859 && dataSize == sizeof(rgb_color)) {
860
861 // calculate view relative drop point
862 BPoint dropPoint = ConvertFromScreen(message->DropPoint());
863
864 // calculate current keypad area
865 float sizeDisp = fHeight * kDisplayScaleY;
866 BRect keypadRect(0.0, sizeDisp, fWidth, fHeight);
867
868 // check location of color drop
869 if (keypadRect.Contains(dropPoint) && dropColor != NULL) {
870 fBaseColor = *dropColor;
871 fHasCustomBaseColor =
872 fBaseColor != ui_color(B_PANEL_BACKGROUND_COLOR);
873 _Colorize();
874 // redraw
875 Invalidate();
876 }
877
878 } else {
879 // look for text/plain MIME data
880 const char* text;
881 ssize_t numBytes;
882 if (message->FindData("text/plain", B_MIME_TYPE,
883 (const void**)&text, &numBytes) == B_OK) {
884 BString temp;
885 temp.Append(text, numBytes);
886 fExpressionTextView->Insert(temp.String());
887 }
888 }
889 }
890
891
892 status_t
SaveSettings(BMessage * archive) const893 CalcView::SaveSettings(BMessage* archive) const
894 {
895 status_t ret = archive ? B_OK : B_BAD_VALUE;
896
897 // record grid dimensions
898 if (ret == B_OK)
899 ret = archive->AddInt16("cols", fColumns);
900
901 if (ret == B_OK)
902 ret = archive->AddInt16("rows", fRows);
903
904 // record color scheme
905 if (ret == B_OK) {
906 ret = archive->AddData("rgbBaseColor", B_RGB_COLOR_TYPE,
907 &fBaseColor, sizeof(rgb_color));
908 }
909
910 // record current options
911 if (ret == B_OK)
912 ret = fOptions->SaveSettings(archive);
913
914 // record display text
915 if (ret == B_OK)
916 ret = archive->AddString("displayText", fExpressionTextView->Text());
917
918 // record expression history
919 if (ret == B_OK)
920 ret = fExpressionTextView->SaveSettings(archive);
921
922 // record calculator description
923 if (ret == B_OK)
924 ret = archive->AddString("calcDesc",
925 fKeypadDescription == kKeypadDescriptionBasic
926 ? "basic" : "scientific");
927
928 return ret;
929 }
930
931
932 void
Evaluate()933 CalcView::Evaluate()
934 {
935 if (fExpressionTextView->TextLength() == 0) {
936 beep();
937 return;
938 }
939
940 fEvaluateThread = spawn_thread(_EvaluateThread, "Evaluate Thread",
941 B_LOW_PRIORITY, this);
942 if (fEvaluateThread < B_OK) {
943 // failed to create evaluate thread, error out
944 fExpressionTextView->SetText(strerror(fEvaluateThread));
945 return;
946 }
947
948 _SetEnabled(false);
949 // Disable input while we evaluate
950
951 status_t threadStatus = resume_thread(fEvaluateThread);
952 if (threadStatus != B_OK) {
953 // evaluate thread failed to start, error out
954 fExpressionTextView->SetText(strerror(threadStatus));
955 _SetEnabled(true);
956 return;
957 }
958
959 if (fEvaluateMessageRunner == NULL) {
960 BMessage message(kMsgCalculating);
961 fEvaluateMessageRunner = new (std::nothrow) BMessageRunner(
962 BMessenger(this), &message, kCalculatingInterval, 1);
963 status_t runnerStatus = fEvaluateMessageRunner->InitCheck();
964 if (runnerStatus != B_OK)
965 printf("Evaluate Message Runner: %s\n", strerror(runnerStatus));
966 }
967 }
968
969
970 void
FlashKey(const char * bytes,int32 numBytes)971 CalcView::FlashKey(const char* bytes, int32 numBytes)
972 {
973 BString temp;
974 temp.Append(bytes, numBytes);
975 int32 key = _KeyForLabel(temp.String());
976 if (key >= 0)
977 _FlashKey(key, FLAGS_FLASH_KEY);
978 }
979
980
981 void
ToggleAutoNumlock(void)982 CalcView::ToggleAutoNumlock(void)
983 {
984 fOptions->auto_num_lock = !fOptions->auto_num_lock;
985 fAutoNumlockItem->SetMarked(fOptions->auto_num_lock);
986 }
987
988
989 void
SetDegreeMode(bool degrees)990 CalcView::SetDegreeMode(bool degrees)
991 {
992 fOptions->degree_mode = degrees;
993 fAngleModeRadianItem->SetMarked(!degrees);
994 fAngleModeDegreeItem->SetMarked(degrees);
995 }
996
997
998 void
SetKeypadMode(uint8 mode)999 CalcView::SetKeypadMode(uint8 mode)
1000 {
1001 if (_IsEmbedded())
1002 return;
1003
1004 BWindow* window = Window();
1005 if (window == NULL)
1006 return;
1007
1008 if (fOptions->keypad_mode == mode)
1009 return;
1010
1011 fOptions->keypad_mode = mode;
1012 _MarkKeypadItems(fOptions->keypad_mode);
1013
1014 float width = fWidth;
1015 float height = fHeight;
1016
1017 switch (fOptions->keypad_mode) {
1018 case KEYPAD_MODE_COMPACT:
1019 {
1020 if (window->Bounds() == Frame()) {
1021 window->SetSizeLimits(kMinimumWidthCompact,
1022 kMaximumWidthCompact, kMinimumHeightCompact,
1023 kMaximumHeightCompact);
1024 window->ResizeTo(width, height * kDisplayScaleY);
1025 } else
1026 ResizeTo(width, height * kDisplayScaleY);
1027
1028 break;
1029 }
1030
1031 case KEYPAD_MODE_SCIENTIFIC:
1032 {
1033 fKeypadDescription = kKeypadDescriptionScientific;
1034 fRows = 8;
1035 _ParseCalcDesc(fKeypadDescription);
1036
1037 window->SetSizeLimits(kMinimumWidthScientific,
1038 kMaximumWidthScientific, kMinimumHeightScientific,
1039 kMaximumHeightScientific);
1040
1041 if (width < kMinimumWidthScientific)
1042 width = kMinimumWidthScientific;
1043 else if (width > kMaximumWidthScientific)
1044 width = kMaximumWidthScientific;
1045
1046 if (height < kMinimumHeightScientific)
1047 height = kMinimumHeightScientific;
1048 else if (height > kMaximumHeightScientific)
1049 height = kMaximumHeightScientific;
1050
1051 if (width != fWidth || height != fHeight)
1052 ResizeTo(width, height);
1053 else
1054 Invalidate();
1055
1056 break;
1057 }
1058
1059 case KEYPAD_MODE_BASIC:
1060 default:
1061 {
1062 fKeypadDescription = kKeypadDescriptionBasic;
1063 fRows = 4;
1064 _ParseCalcDesc(fKeypadDescription);
1065
1066 window->SetSizeLimits(kMinimumWidthBasic, kMaximumWidthBasic,
1067 kMinimumHeightBasic, kMaximumHeightBasic);
1068
1069 if (width < kMinimumWidthBasic)
1070 width = kMinimumWidthBasic;
1071 else if (width > kMaximumWidthBasic)
1072 width = kMaximumWidthBasic;
1073
1074 if (height < kMinimumHeightBasic)
1075 height = kMinimumHeightBasic;
1076 else if (height > kMaximumHeightBasic)
1077 height = kMaximumHeightBasic;
1078
1079 if (width != fWidth || height != fHeight)
1080 ResizeTo(width, height);
1081 else
1082 Invalidate();
1083 }
1084 }
1085 }
1086
1087
1088 // #pragma mark -
1089
1090
1091 /*static*/ status_t
_EvaluateThread(void * data)1092 CalcView::_EvaluateThread(void* data)
1093 {
1094 CalcView* calcView = reinterpret_cast<CalcView*>(data);
1095 if (calcView == NULL)
1096 return B_BAD_TYPE;
1097
1098 BMessenger messenger(calcView);
1099 if (!messenger.IsValid())
1100 return B_BAD_VALUE;
1101
1102 BString result;
1103 status_t status = acquire_sem(calcView->fEvaluateSemaphore);
1104 if (status == B_OK) {
1105 BLocale locale;
1106 BNumberFormat format(&locale);
1107
1108 ExpressionParser parser;
1109 parser.SetDegreeMode(calcView->fOptions->degree_mode);
1110 parser.SetSeparators(format.GetSeparator(B_DECIMAL_SEPARATOR),
1111 format.GetSeparator(B_GROUPING_SEPARATOR));
1112
1113 BString expression(calcView->fExpressionTextView->Text());
1114 try {
1115 result = parser.Evaluate(expression.String());
1116 } catch (ParseException& e) {
1117 result << e.message.String() << " at " << (e.position + 1);
1118 status = B_ERROR;
1119 }
1120 release_sem(calcView->fEvaluateSemaphore);
1121 } else
1122 result = strerror(status);
1123
1124 BMessage message(kMsgDoneEvaluating);
1125 message.AddString(status == B_OK ? "value" : "error", result.String());
1126 messenger.SendMessage(&message);
1127
1128 return status;
1129 }
1130
1131
1132 void
_Init(BMessage * settings)1133 CalcView::_Init(BMessage* settings)
1134 {
1135 // create expression text view
1136 fExpressionTextView = new ExpressionTextView(_ExpressionRect(), this);
1137 AddChild(fExpressionTextView);
1138
1139 // read data from archive
1140 _LoadSettings(settings);
1141
1142 // fetch the calc icon for compact view
1143 _FetchAppIcon(fCalcIcon);
1144
1145 fEvaluateSemaphore = create_sem(1, "Evaluate Semaphore");
1146 }
1147
1148
1149 status_t
_LoadSettings(BMessage * archive)1150 CalcView::_LoadSettings(BMessage* archive)
1151 {
1152 if (!archive)
1153 return B_BAD_VALUE;
1154
1155 // record calculator description
1156 BString calcDesc;
1157 archive->FindString("calcDesc", &calcDesc);
1158 if (calcDesc == "scientific" || calcDesc.StartsWith("ln"))
1159 fKeypadDescription = kKeypadDescriptionScientific;
1160 else
1161 fKeypadDescription = kKeypadDescriptionBasic;
1162
1163 // read grid dimensions
1164 if (archive->FindInt16("cols", &fColumns) < B_OK)
1165 fColumns = 5;
1166 if (archive->FindInt16("rows", &fRows) < B_OK)
1167 fRows = 4;
1168
1169 // read color scheme
1170 const rgb_color* color;
1171 ssize_t size;
1172 if (archive->FindData("rgbBaseColor", B_RGB_COLOR_TYPE,
1173 (const void**)&color, &size) < B_OK
1174 || size != sizeof(rgb_color)) {
1175 fBaseColor = ui_color(B_PANEL_BACKGROUND_COLOR);
1176 puts("Missing rgbBaseColor from CalcView archive!\n");
1177 } else
1178 fBaseColor = *color;
1179
1180 fHasCustomBaseColor = fBaseColor != ui_color(B_PANEL_BACKGROUND_COLOR);
1181
1182 // load options
1183 fOptions->LoadSettings(archive);
1184
1185 // load display text
1186 const char* display;
1187 if (archive->FindString("displayText", &display) < B_OK) {
1188 puts("Missing expression text from CalcView archive.\n");
1189 } else {
1190 // init expression text
1191 fExpressionTextView->SetText(display);
1192 }
1193
1194 // load expression history
1195 fExpressionTextView->LoadSettings(archive);
1196
1197 // parse calculator description
1198 _ParseCalcDesc(fKeypadDescription);
1199
1200 // colorize based on base color.
1201 _Colorize();
1202
1203 return B_OK;
1204 }
1205
1206
1207 void
_ParseCalcDesc(const char ** keypadDescription)1208 CalcView::_ParseCalcDesc(const char** keypadDescription)
1209 {
1210 // TODO: should calculate dimensions from desc here!
1211 fKeypad = new CalcKey[fRows * fColumns];
1212
1213 // scan through calculator description and assemble keypad
1214 CalcKey* key = fKeypad;
1215 for (int i = 0; const char* p = keypadDescription[i]; i++) {
1216 // Move to next row as needed
1217 if (strcmp(p, "\n") == 0)
1218 continue;
1219
1220 // copy label
1221 strlcpy(key->label, B_TRANSLATE_NOCOLLECT(p), sizeof(key->label));
1222
1223 // set code
1224 if (strcmp(p, "=") == 0)
1225 strlcpy(key->code, "\n", sizeof(key->code));
1226 else
1227 strlcpy(key->code, p, sizeof(key->code));
1228
1229 // set keymap
1230 if (strlen(key->label) == 1)
1231 strlcpy(key->keymap, key->label, sizeof(key->keymap));
1232 else
1233 *key->keymap = '\0';
1234
1235 key->flags = 0;
1236
1237 // add this to the expression text view, so that it
1238 // will forward the respective KeyDown event to us
1239 fExpressionTextView->AddKeypadLabel(key->label);
1240
1241 // advance
1242 key++;
1243 }
1244 }
1245
1246
1247 void
_PressKey(int key)1248 CalcView::_PressKey(int key)
1249 {
1250 if (!fEnabled)
1251 return;
1252
1253 assert(key < (fRows * fColumns));
1254 assert(key >= 0);
1255
1256 if (strcmp(fKeypad[key].code, "BS") == 0) {
1257 // BS means backspace
1258 fExpressionTextView->BackSpace();
1259 } else if (strcmp(fKeypad[key].code, "C") == 0) {
1260 // C means clear
1261 fExpressionTextView->Clear();
1262 } else if (strcmp(fKeypad[key].code, "acos") == 0
1263 || strcmp(fKeypad[key].code, "asin") == 0
1264 || strcmp(fKeypad[key].code, "atan") == 0
1265 || strcmp(fKeypad[key].code, "cbrt") == 0
1266 || strcmp(fKeypad[key].code, "ceil") == 0
1267 || strcmp(fKeypad[key].code, "cos") == 0
1268 || strcmp(fKeypad[key].code, "cosh") == 0
1269 || strcmp(fKeypad[key].code, "exp") == 0
1270 || strcmp(fKeypad[key].code, "floor") == 0
1271 || strcmp(fKeypad[key].code, "log") == 0
1272 || strcmp(fKeypad[key].code, "ln") == 0
1273 || strcmp(fKeypad[key].code, "sin") == 0
1274 || strcmp(fKeypad[key].code, "sinh") == 0
1275 || strcmp(fKeypad[key].code, "sqrt") == 0
1276 || strcmp(fKeypad[key].code, "tan") == 0
1277 || strcmp(fKeypad[key].code, "tanh") == 0) {
1278 int32 labelLen = strlen(fKeypad[key].code);
1279 int32 startSelection = 0;
1280 int32 endSelection = 0;
1281 fExpressionTextView->GetSelection(&startSelection, &endSelection);
1282 if (endSelection > startSelection) {
1283 // There is selected text, put it inbetween the parens
1284 fExpressionTextView->Insert(startSelection, fKeypad[key].code,
1285 labelLen);
1286 fExpressionTextView->Insert(startSelection + labelLen, "(", 1);
1287 fExpressionTextView->Insert(endSelection + labelLen + 1, ")", 1);
1288 // Put the cursor after the ending paren
1289 // Need to cast to BTextView because Select() is protected
1290 // in the InputTextView class
1291 static_cast<BTextView*>(fExpressionTextView)->Select(
1292 endSelection + labelLen + 2, endSelection + labelLen + 2);
1293 } else {
1294 // There is no selected text, insert at the cursor location
1295 fExpressionTextView->Insert(fKeypad[key].code);
1296 fExpressionTextView->Insert("()");
1297 // Put the cursor inside the parens so you can enter an argument
1298 // Need to cast to BTextView because Select() is protected
1299 // in the InputTextView class
1300 static_cast<BTextView*>(fExpressionTextView)->Select(
1301 endSelection + labelLen + 1, endSelection + labelLen + 1);
1302 }
1303 } else if (strcmp(fKeypad[key].code, ".") == 0) {
1304 BLocale locale;
1305 BNumberFormat format(&locale);
1306
1307 fExpressionTextView->Insert(format.GetSeparator(B_DECIMAL_SEPARATOR));
1308 } else {
1309 // check for evaluation order
1310 if (fKeypad[key].code[0] == '\n') {
1311 fExpressionTextView->ApplyChanges();
1312 } else {
1313 // insert into expression text
1314 fExpressionTextView->Insert(fKeypad[key].code);
1315 }
1316 }
1317 }
1318
1319
1320 void
_PressKey(const char * label)1321 CalcView::_PressKey(const char* label)
1322 {
1323 int32 key = _KeyForLabel(label);
1324 if (key >= 0)
1325 _PressKey(key);
1326 }
1327
1328
1329 int32
_KeyForLabel(const char * label) const1330 CalcView::_KeyForLabel(const char* label) const
1331 {
1332 int keys = fRows * fColumns;
1333 for (int i = 0; i < keys; i++) {
1334 if (strcmp(fKeypad[i].label, label) == 0) {
1335 return i;
1336 }
1337 }
1338 return -1;
1339 }
1340
1341
1342 void
_FlashKey(int32 key,uint32 flashFlags)1343 CalcView::_FlashKey(int32 key, uint32 flashFlags)
1344 {
1345 if (fOptions->keypad_mode == KEYPAD_MODE_COMPACT)
1346 return;
1347
1348 if (flashFlags != 0)
1349 fKeypad[key].flags |= flashFlags;
1350 else
1351 fKeypad[key].flags = 0;
1352 Invalidate();
1353
1354 if (fKeypad[key].flags == FLAGS_FLASH_KEY) {
1355 BMessage message(MSG_UNFLASH_KEY);
1356 message.AddInt32("key", key);
1357 BMessageRunner::StartSending(BMessenger(this), &message,
1358 kFlashOnOffInterval, 1);
1359 }
1360 }
1361
1362
1363 void
_Colorize()1364 CalcView::_Colorize()
1365 {
1366 rgb_color panelColor = ui_color(B_PANEL_TEXT_COLOR);
1367 if (rgb_color::Contrast(fBaseColor, panelColor) > 100)
1368 fButtonTextColor = panelColor;
1369 else {
1370 if (fBaseColor.IsLight())
1371 fButtonTextColor = (rgb_color){ 0, 0, 0, 255 };
1372 else
1373 fButtonTextColor = (rgb_color){ 255, 255, 255, 255 };
1374 }
1375 }
1376
1377
1378 void
_CreatePopUpMenu(bool addKeypadModeMenuItems)1379 CalcView::_CreatePopUpMenu(bool addKeypadModeMenuItems)
1380 {
1381 // construct items
1382 fAutoNumlockItem = new BMenuItem(B_TRANSLATE("Enable Num Lock on startup"),
1383 new BMessage(MSG_OPTIONS_AUTO_NUM_LOCK));
1384 fAngleModeRadianItem = new BMenuItem(B_TRANSLATE("Radians"),
1385 new BMessage(MSG_OPTIONS_ANGLE_MODE_RADIAN));
1386 fAngleModeDegreeItem = new BMenuItem(B_TRANSLATE("Degrees"),
1387 new BMessage(MSG_OPTIONS_ANGLE_MODE_DEGREE));
1388 if (addKeypadModeMenuItems) {
1389 fKeypadModeCompactItem = new BMenuItem(B_TRANSLATE("Compact"),
1390 new BMessage(MSG_OPTIONS_KEYPAD_MODE_COMPACT), '0');
1391 fKeypadModeBasicItem = new BMenuItem(B_TRANSLATE("Basic"),
1392 new BMessage(MSG_OPTIONS_KEYPAD_MODE_BASIC), '1');
1393 fKeypadModeScientificItem = new BMenuItem(B_TRANSLATE("Scientific"),
1394 new BMessage(MSG_OPTIONS_KEYPAD_MODE_SCIENTIFIC), '2');
1395 }
1396
1397 // apply current settings
1398 fAutoNumlockItem->SetMarked(fOptions->auto_num_lock);
1399 fAngleModeRadianItem->SetMarked(!fOptions->degree_mode);
1400 fAngleModeDegreeItem->SetMarked(fOptions->degree_mode);
1401
1402 // construct menu
1403 fPopUpMenu = new BPopUpMenu("pop-up", false, false);
1404
1405 fPopUpMenu->AddItem(fAutoNumlockItem);
1406 fPopUpMenu->AddSeparatorItem();
1407 fPopUpMenu->AddItem(fAngleModeRadianItem);
1408 fPopUpMenu->AddItem(fAngleModeDegreeItem);
1409 if (addKeypadModeMenuItems) {
1410 fPopUpMenu->AddSeparatorItem();
1411 fPopUpMenu->AddItem(fKeypadModeCompactItem);
1412 fPopUpMenu->AddItem(fKeypadModeBasicItem);
1413 fPopUpMenu->AddItem(fKeypadModeScientificItem);
1414 _MarkKeypadItems(fOptions->keypad_mode);
1415 }
1416 }
1417
1418
1419 BRect
_ExpressionRect() const1420 CalcView::_ExpressionRect() const
1421 {
1422 BRect r(0.0, 0.0, fWidth, fHeight);
1423 if (fOptions->keypad_mode != KEYPAD_MODE_COMPACT) {
1424 r.bottom = floorf(fHeight * kDisplayScaleY) + 1;
1425 }
1426 return r;
1427 }
1428
1429
1430 BRect
_KeypadRect() const1431 CalcView::_KeypadRect() const
1432 {
1433 BRect r(0.0, 0.0, -1.0, -1.0);
1434 if (fOptions->keypad_mode != KEYPAD_MODE_COMPACT) {
1435 r.right = fWidth;
1436 r.bottom = fHeight;
1437 r.top = floorf(fHeight * kDisplayScaleY);
1438 }
1439 return r;
1440 }
1441
1442
1443 void
_MarkKeypadItems(uint8 keypad_mode)1444 CalcView::_MarkKeypadItems(uint8 keypad_mode)
1445 {
1446 switch (keypad_mode) {
1447 case KEYPAD_MODE_COMPACT:
1448 fKeypadModeCompactItem->SetMarked(true);
1449 fKeypadModeBasicItem->SetMarked(false);
1450 fKeypadModeScientificItem->SetMarked(false);
1451 break;
1452
1453 case KEYPAD_MODE_SCIENTIFIC:
1454 fKeypadModeCompactItem->SetMarked(false);
1455 fKeypadModeBasicItem->SetMarked(false);
1456 fKeypadModeScientificItem->SetMarked(true);
1457 break;
1458
1459 default: // KEYPAD_MODE_BASIC is the default
1460 fKeypadModeCompactItem->SetMarked(false);
1461 fKeypadModeBasicItem->SetMarked(true);
1462 fKeypadModeScientificItem->SetMarked(false);
1463 }
1464 }
1465
1466
1467 void
_FetchAppIcon(BBitmap * into)1468 CalcView::_FetchAppIcon(BBitmap* into)
1469 {
1470 entry_ref appRef;
1471 status_t status = be_roster->FindApp(kSignature, &appRef);
1472 if (status == B_OK) {
1473 BFile file(&appRef, B_READ_ONLY);
1474 BAppFileInfo appInfo(&file);
1475 status = appInfo.GetIcon(into, B_MINI_ICON);
1476 }
1477 if (status != B_OK)
1478 memset(into->Bits(), 0, into->BitsLength());
1479 }
1480
1481
1482 // Returns whether or not CalcView is embedded somewhere, most likely
1483 // the Desktop
1484 bool
_IsEmbedded()1485 CalcView::_IsEmbedded()
1486 {
1487 return Parent() != NULL && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0;
1488 }
1489
1490
1491 void
_SetEnabled(bool enable)1492 CalcView::_SetEnabled(bool enable)
1493 {
1494 fEnabled = enable;
1495 fExpressionTextView->MakeSelectable(enable);
1496 fExpressionTextView->MakeEditable(enable);
1497 }
1498