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