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