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