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