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