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