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 BLocale locale; 461 BNumberFormat format(&locale); 462 463 fExpressionTextView->SetValue(result, format.GetSeparator(B_DECIMAL_SEPARATOR)); 464 } 465 466 // stop the message runner 467 delete fEvaluateMessageRunner; 468 fEvaluateMessageRunner = NULL; 469 break; 470 } 471 472 default: 473 BView::MessageReceived(message); 474 break; 475 } 476 } 477 } 478 479 480 void 481 CalcView::Draw(BRect updateRect) 482 { 483 bool drawBackground = !_IsEmbedded(); 484 485 SetHighColor(fBaseColor); 486 BRect expressionRect(_ExpressionRect()); 487 if (updateRect.Intersects(expressionRect)) { 488 if (fOptions->keypad_mode == KEYPAD_MODE_COMPACT 489 && expressionRect.Height() >= fCalcIcon->Bounds().Height()) { 490 // render calc icon 491 expressionRect.left = fExpressionTextView->Frame().right + 2; 492 if (drawBackground) { 493 SetHighColor(fBaseColor); 494 FillRect(updateRect & expressionRect); 495 } 496 497 SetDrawingMode(B_OP_ALPHA); 498 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 499 500 BPoint iconPos; 501 iconPos.x = expressionRect.right - (expressionRect.Width() 502 + fCalcIcon->Bounds().Width()) / 2.0; 503 iconPos.y = expressionRect.top + (expressionRect.Height() 504 - fCalcIcon->Bounds().Height()) / 2.0; 505 DrawBitmap(fCalcIcon, iconPos); 506 507 SetDrawingMode(B_OP_COPY); 508 } 509 510 // render border around expression text view 511 expressionRect = fExpressionTextView->Frame(); 512 expressionRect.InsetBy(-2, -2); 513 if (fOptions->keypad_mode != KEYPAD_MODE_COMPACT && drawBackground) { 514 expressionRect.InsetBy(-2, -2); 515 StrokeRect(expressionRect); 516 expressionRect.InsetBy(1, 1); 517 StrokeRect(expressionRect); 518 expressionRect.InsetBy(1, 1); 519 } 520 521 uint32 flags = 0; 522 if (!drawBackground) 523 flags |= BControlLook::B_BLEND_FRAME; 524 be_control_look->DrawTextControlBorder(this, expressionRect, 525 updateRect, fBaseColor, flags); 526 } 527 528 if (fOptions->keypad_mode == KEYPAD_MODE_COMPACT) 529 return; 530 531 // calculate grid sizes 532 BRect keypadRect(_KeypadRect()); 533 534 if (be_control_look != NULL) { 535 if (drawBackground) 536 StrokeRect(keypadRect); 537 keypadRect.InsetBy(1, 1); 538 } 539 540 float sizeDisp = keypadRect.top; 541 float sizeCol = (keypadRect.Width() + 1) / (float)fColumns; 542 float sizeRow = (keypadRect.Height() + 1) / (float)fRows; 543 544 if (!updateRect.Intersects(keypadRect)) 545 return; 546 547 SetFontSize(min_c(sizeRow * kFontScaleY, sizeCol * kFontScaleX)); 548 549 CalcKey* key = fKeypad; 550 for (int row = 0; row < fRows; row++) { 551 for (int col = 0; col < fColumns; col++) { 552 BRect frame; 553 frame.left = keypadRect.left + col * sizeCol; 554 frame.right = keypadRect.left + (col + 1) * sizeCol - 1; 555 frame.top = sizeDisp + row * sizeRow; 556 frame.bottom = sizeDisp + (row + 1) * sizeRow - 1; 557 558 if (drawBackground) { 559 SetHighColor(fBaseColor); 560 StrokeRect(frame); 561 } 562 frame.InsetBy(1, 1); 563 564 uint32 flags = 0; 565 if (!drawBackground) 566 flags |= BControlLook::B_BLEND_FRAME; 567 if (key->flags != 0) 568 flags |= BControlLook::B_ACTIVATED; 569 flags |= BControlLook::B_IGNORE_OUTLINE; 570 571 be_control_look->DrawButtonFrame(this, frame, updateRect, 572 fBaseColor, fBaseColor, flags); 573 574 be_control_look->DrawButtonBackground(this, frame, updateRect, 575 fBaseColor, flags); 576 577 be_control_look->DrawLabel(this, key->label, frame, updateRect, 578 fBaseColor, flags, BAlignment(B_ALIGN_HORIZONTAL_CENTER, 579 B_ALIGN_VERTICAL_CENTER), &fButtonTextColor); 580 581 key++; 582 } 583 } 584 } 585 586 587 void 588 CalcView::MouseDown(BPoint point) 589 { 590 // ensure this view is the current focus 591 if (!fExpressionTextView->IsFocus()) { 592 // Call our version of MakeFocus(), since that will also apply the 593 // num_lock setting. 594 MakeFocus(); 595 } 596 597 // read mouse buttons state 598 int32 buttons = 0; 599 Window()->CurrentMessage()->FindInt32("buttons", &buttons); 600 601 if ((B_PRIMARY_MOUSE_BUTTON & buttons) == 0) { 602 // display popup menu if not primary mouse button 603 BMenuItem* selected; 604 if ((selected = fPopUpMenu->Go(ConvertToScreen(point))) != NULL 605 && selected->Message() != NULL) { 606 Window()->PostMessage(selected->Message(), this); 607 } 608 return; 609 } 610 611 if (fOptions->keypad_mode == KEYPAD_MODE_COMPACT) { 612 if (fCalcIcon != NULL) { 613 BRect bounds(Bounds()); 614 bounds.left = bounds.right - fCalcIcon->Bounds().Width(); 615 if (bounds.Contains(point)) { 616 // user clicked on calculator icon 617 fExpressionTextView->Clear(); 618 } 619 } 620 return; 621 } 622 623 // calculate grid sizes 624 float sizeDisp = fHeight * kDisplayScaleY; 625 float sizeCol = fWidth / (float)fColumns; 626 float sizeRow = (fHeight - sizeDisp) / (float)fRows; 627 628 // calculate location within grid 629 int gridCol = (int)floorf(point.x / sizeCol); 630 int gridRow = (int)floorf((point.y - sizeDisp) / sizeRow); 631 632 // check limits 633 if ((gridCol >= 0) && (gridCol < fColumns) 634 && (gridRow >= 0) && (gridRow < fRows)) { 635 636 // process key press 637 int key = gridRow * fColumns + gridCol; 638 _FlashKey(key, FLAGS_MOUSE_DOWN); 639 _PressKey(key); 640 641 // make sure we receive the mouse up! 642 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); 643 } 644 } 645 646 647 void 648 CalcView::MouseUp(BPoint point) 649 { 650 if (fOptions->keypad_mode == KEYPAD_MODE_COMPACT) 651 return; 652 653 int keys = fRows * fColumns; 654 for (int i = 0; i < keys; i++) { 655 if (fKeypad[i].flags & FLAGS_MOUSE_DOWN) { 656 _FlashKey(i, 0); 657 break; 658 } 659 } 660 } 661 662 663 void 664 CalcView::KeyDown(const char* bytes, int32 numBytes) 665 { 666 // if single byte character... 667 if (numBytes == 1) { 668 669 //printf("Key pressed: %c\n", bytes[0]); 670 671 switch (bytes[0]) { 672 673 case B_ENTER: 674 // translate to evaluate key 675 _PressKey("="); 676 break; 677 678 case B_LEFT_ARROW: 679 case B_BACKSPACE: 680 // translate to backspace key 681 _PressKey("BS"); 682 break; 683 684 case B_SPACE: 685 case B_ESCAPE: 686 case 'c': 687 // translate to clear key 688 _PressKey("C"); 689 break; 690 691 // bracket translation 692 case '[': 693 case '{': 694 _PressKey("("); 695 break; 696 697 case ']': 698 case '}': 699 _PressKey(")"); 700 break; 701 702 default: { 703 // scan the keymap array for match 704 int keys = fRows * fColumns; 705 for (int i = 0; i < keys; i++) { 706 if (fKeypad[i].keymap[0] == bytes[0]) { 707 _PressKey(i); 708 return; 709 } 710 } 711 break; 712 } 713 } 714 } 715 } 716 717 718 void 719 CalcView::MakeFocus(bool focused) 720 { 721 if (focused) { 722 // set num lock 723 if (fOptions->auto_num_lock) { 724 set_keyboard_locks(B_NUM_LOCK 725 | (modifiers() & (B_CAPS_LOCK | B_SCROLL_LOCK))); 726 } 727 } 728 729 // pass on request to text view 730 fExpressionTextView->MakeFocus(focused); 731 } 732 733 734 void 735 CalcView::FrameResized(float width, float height) 736 { 737 fWidth = width; 738 fHeight = height; 739 740 // layout expression text view 741 BRect expressionRect = _ExpressionRect(); 742 if (fOptions->keypad_mode == KEYPAD_MODE_COMPACT) { 743 expressionRect.InsetBy(2, 2); 744 expressionRect.right -= ceilf(fCalcIcon->Bounds().Width() * 1.5); 745 } else 746 expressionRect.InsetBy(4, 4); 747 748 fExpressionTextView->MoveTo(expressionRect.LeftTop()); 749 fExpressionTextView->ResizeTo(expressionRect.Width(), expressionRect.Height()); 750 751 // configure expression text view font size and color 752 float sizeDisp = fOptions->keypad_mode == KEYPAD_MODE_COMPACT 753 ? fHeight : fHeight * kDisplayScaleY; 754 BFont font(be_bold_font); 755 font.SetSize(sizeDisp * kExpressionFontScaleY); 756 rgb_color fontColor = fExpressionTextView->HighColor(); 757 fExpressionTextView->SetFontAndColor(&font, B_FONT_ALL, &fontColor); 758 759 expressionRect.OffsetTo(B_ORIGIN); 760 fExpressionTextView->SetTextRect(expressionRect); 761 Invalidate(); 762 } 763 764 765 status_t 766 CalcView::Archive(BMessage* archive, bool deep) const 767 { 768 fExpressionTextView->RemoveSelf(); 769 770 // passed on request to parent 771 status_t ret = BView::Archive(archive, deep); 772 773 const_cast<CalcView*>(this)->AddChild(fExpressionTextView); 774 775 // save app signature for replicant add-on loading 776 if (ret == B_OK) 777 ret = archive->AddString("add_on", kSignature); 778 779 // save all the options 780 if (ret == B_OK) 781 ret = SaveSettings(archive); 782 783 // add class info last 784 if (ret == B_OK) 785 ret = archive->AddString("class", "CalcView"); 786 787 return ret; 788 } 789 790 791 void 792 CalcView::Cut() 793 { 794 Copy(); // copy data to clipboard 795 fExpressionTextView->Clear(); // remove data 796 } 797 798 799 void 800 CalcView::Copy() 801 { 802 // access system clipboard 803 ClipboardLocker locker(be_clipboard); 804 if (!locker.IsLocked()) 805 return; 806 807 if (be_clipboard->Clear() != B_OK) 808 return; 809 810 BMessage* clipper = be_clipboard->Data(); 811 if (clipper == NULL) 812 return; 813 814 BString expression = fExpressionTextView->Text(); 815 if (clipper->AddData("text/plain", B_MIME_TYPE, 816 expression.String(), expression.Length()) == B_OK) { 817 clipper->what = B_MIME_DATA; 818 be_clipboard->Commit(); 819 } 820 } 821 822 823 void 824 CalcView::Paste(BMessage* message) 825 { 826 // handle files first 827 int32 count; 828 if (message->GetInfo("refs", NULL, &count) == B_OK) { 829 entry_ref ref; 830 ssize_t read; 831 BFile file; 832 char buffer[256]; 833 memset(buffer, 0, sizeof(buffer)); 834 for (int32 i = 0; i < count; i++) { 835 if (message->FindRef("refs", i, &ref) == B_OK) { 836 if (file.SetTo(&ref, B_READ_ONLY) == B_OK) { 837 read = file.Read(buffer, sizeof(buffer) - 1); 838 if (read <= 0) 839 continue; 840 BString expression(buffer); 841 int32 j = expression.Length(); 842 while (j > 0 && expression[j - 1] == '\n') 843 j--; 844 expression.Truncate(j); 845 if (expression.Length() > 0) 846 fExpressionTextView->Insert(expression.String()); 847 } 848 } 849 } 850 return; 851 } 852 // handle color drops 853 // read incoming color 854 const rgb_color* dropColor = NULL; 855 ssize_t dataSize; 856 if (message->FindData("RGBColor", B_RGB_COLOR_TYPE, 857 (const void**)&dropColor, &dataSize) == B_OK 858 && dataSize == sizeof(rgb_color)) { 859 860 // calculate view relative drop point 861 BPoint dropPoint = ConvertFromScreen(message->DropPoint()); 862 863 // calculate current keypad area 864 float sizeDisp = fHeight * kDisplayScaleY; 865 BRect keypadRect(0.0, sizeDisp, fWidth, fHeight); 866 867 // check location of color drop 868 if (keypadRect.Contains(dropPoint) && dropColor != NULL) { 869 fBaseColor = *dropColor; 870 fHasCustomBaseColor = 871 fBaseColor != ui_color(B_PANEL_BACKGROUND_COLOR); 872 _Colorize(); 873 // redraw 874 Invalidate(); 875 } 876 877 } else { 878 // look for text/plain MIME data 879 const char* text; 880 ssize_t numBytes; 881 if (message->FindData("text/plain", B_MIME_TYPE, 882 (const void**)&text, &numBytes) == B_OK) { 883 BString temp; 884 temp.Append(text, numBytes); 885 fExpressionTextView->Insert(temp.String()); 886 } 887 } 888 } 889 890 891 status_t 892 CalcView::SaveSettings(BMessage* archive) const 893 { 894 status_t ret = archive ? B_OK : B_BAD_VALUE; 895 896 // record grid dimensions 897 if (ret == B_OK) 898 ret = archive->AddInt16("cols", fColumns); 899 900 if (ret == B_OK) 901 ret = archive->AddInt16("rows", fRows); 902 903 // record color scheme 904 if (ret == B_OK) { 905 ret = archive->AddData("rgbBaseColor", B_RGB_COLOR_TYPE, 906 &fBaseColor, sizeof(rgb_color)); 907 } 908 909 if (ret == B_OK) { 910 ret = archive->AddData("rgbDisplay", B_RGB_COLOR_TYPE, 911 &fExpressionBGColor, sizeof(rgb_color)); 912 } 913 914 // record current options 915 if (ret == B_OK) 916 ret = fOptions->SaveSettings(archive); 917 918 // record display text 919 if (ret == B_OK) 920 ret = archive->AddString("displayText", fExpressionTextView->Text()); 921 922 // record expression history 923 if (ret == B_OK) 924 ret = fExpressionTextView->SaveSettings(archive); 925 926 // record calculator description 927 if (ret == B_OK) 928 ret = archive->AddString("calcDesc", 929 fKeypadDescription == kKeypadDescriptionBasic 930 ? "basic" : "scientific"); 931 932 return ret; 933 } 934 935 936 void 937 CalcView::Evaluate() 938 { 939 if (fExpressionTextView->TextLength() == 0) { 940 beep(); 941 return; 942 } 943 944 fEvaluateThread = spawn_thread(_EvaluateThread, "Evaluate Thread", 945 B_LOW_PRIORITY, this); 946 if (fEvaluateThread < B_OK) { 947 // failed to create evaluate thread, error out 948 fExpressionTextView->SetText(strerror(fEvaluateThread)); 949 return; 950 } 951 952 _SetEnabled(false); 953 // Disable input while we evaluate 954 955 status_t threadStatus = resume_thread(fEvaluateThread); 956 if (threadStatus != B_OK) { 957 // evaluate thread failed to start, error out 958 fExpressionTextView->SetText(strerror(threadStatus)); 959 _SetEnabled(true); 960 return; 961 } 962 963 if (fEvaluateMessageRunner == NULL) { 964 BMessage message(kMsgCalculating); 965 fEvaluateMessageRunner = new (std::nothrow) BMessageRunner( 966 BMessenger(this), &message, kCalculatingInterval, 1); 967 status_t runnerStatus = fEvaluateMessageRunner->InitCheck(); 968 if (runnerStatus != B_OK) 969 printf("Evaluate Message Runner: %s\n", strerror(runnerStatus)); 970 } 971 } 972 973 974 void 975 CalcView::FlashKey(const char* bytes, int32 numBytes) 976 { 977 BString temp; 978 temp.Append(bytes, numBytes); 979 int32 key = _KeyForLabel(temp.String()); 980 if (key >= 0) 981 _FlashKey(key, FLAGS_FLASH_KEY); 982 } 983 984 985 void 986 CalcView::ToggleAutoNumlock(void) 987 { 988 fOptions->auto_num_lock = !fOptions->auto_num_lock; 989 fAutoNumlockItem->SetMarked(fOptions->auto_num_lock); 990 } 991 992 993 void 994 CalcView::SetDegreeMode(bool degrees) 995 { 996 fOptions->degree_mode = degrees; 997 fAngleModeRadianItem->SetMarked(!degrees); 998 fAngleModeDegreeItem->SetMarked(degrees); 999 } 1000 1001 1002 void 1003 CalcView::SetKeypadMode(uint8 mode) 1004 { 1005 if (_IsEmbedded()) 1006 return; 1007 1008 BWindow* window = Window(); 1009 if (window == NULL) 1010 return; 1011 1012 if (fOptions->keypad_mode == mode) 1013 return; 1014 1015 fOptions->keypad_mode = mode; 1016 _MarkKeypadItems(fOptions->keypad_mode); 1017 1018 float width = fWidth; 1019 float height = fHeight; 1020 1021 switch (fOptions->keypad_mode) { 1022 case KEYPAD_MODE_COMPACT: 1023 { 1024 if (window->Bounds() == Frame()) { 1025 window->SetSizeLimits(kMinimumWidthCompact, 1026 kMaximumWidthCompact, kMinimumHeightCompact, 1027 kMaximumHeightCompact); 1028 window->ResizeTo(width, height * kDisplayScaleY); 1029 } else 1030 ResizeTo(width, height * kDisplayScaleY); 1031 1032 break; 1033 } 1034 1035 case KEYPAD_MODE_SCIENTIFIC: 1036 { 1037 fKeypadDescription = kKeypadDescriptionScientific; 1038 fRows = 8; 1039 _ParseCalcDesc(fKeypadDescription); 1040 1041 window->SetSizeLimits(kMinimumWidthScientific, 1042 kMaximumWidthScientific, kMinimumHeightScientific, 1043 kMaximumHeightScientific); 1044 1045 if (width < kMinimumWidthScientific) 1046 width = kMinimumWidthScientific; 1047 else if (width > kMaximumWidthScientific) 1048 width = kMaximumWidthScientific; 1049 1050 if (height < kMinimumHeightScientific) 1051 height = kMinimumHeightScientific; 1052 else if (height > kMaximumHeightScientific) 1053 height = kMaximumHeightScientific; 1054 1055 if (width != fWidth || height != fHeight) 1056 ResizeTo(width, height); 1057 else 1058 Invalidate(); 1059 1060 break; 1061 } 1062 1063 case KEYPAD_MODE_BASIC: 1064 default: 1065 { 1066 fKeypadDescription = kKeypadDescriptionBasic; 1067 fRows = 4; 1068 _ParseCalcDesc(fKeypadDescription); 1069 1070 window->SetSizeLimits(kMinimumWidthBasic, kMaximumWidthBasic, 1071 kMinimumHeightBasic, kMaximumHeightBasic); 1072 1073 if (width < kMinimumWidthBasic) 1074 width = kMinimumWidthBasic; 1075 else if (width > kMaximumWidthBasic) 1076 width = kMaximumWidthBasic; 1077 1078 if (height < kMinimumHeightBasic) 1079 height = kMinimumHeightBasic; 1080 else if (height > kMaximumHeightBasic) 1081 height = kMaximumHeightBasic; 1082 1083 if (width != fWidth || height != fHeight) 1084 ResizeTo(width, height); 1085 else 1086 Invalidate(); 1087 } 1088 } 1089 } 1090 1091 1092 // #pragma mark - 1093 1094 1095 /*static*/ status_t 1096 CalcView::_EvaluateThread(void* data) 1097 { 1098 CalcView* calcView = reinterpret_cast<CalcView*>(data); 1099 if (calcView == NULL) 1100 return B_BAD_TYPE; 1101 1102 BMessenger messenger(calcView); 1103 if (!messenger.IsValid()) 1104 return B_BAD_VALUE; 1105 1106 BString result; 1107 status_t status = acquire_sem(calcView->fEvaluateSemaphore); 1108 if (status == B_OK) { 1109 BLocale locale; 1110 BNumberFormat format(&locale); 1111 1112 ExpressionParser parser; 1113 parser.SetDegreeMode(calcView->fOptions->degree_mode); 1114 parser.SetSeparators(format.GetSeparator(B_DECIMAL_SEPARATOR), 1115 format.GetSeparator(B_GROUPING_SEPARATOR)); 1116 1117 BString expression(calcView->fExpressionTextView->Text()); 1118 try { 1119 result = parser.Evaluate(expression.String()); 1120 } catch (ParseException& e) { 1121 result << e.message.String() << " at " << (e.position + 1); 1122 status = B_ERROR; 1123 } 1124 release_sem(calcView->fEvaluateSemaphore); 1125 } else 1126 result = strerror(status); 1127 1128 BMessage message(kMsgDoneEvaluating); 1129 message.AddString(status == B_OK ? "value" : "error", result.String()); 1130 messenger.SendMessage(&message); 1131 1132 return status; 1133 } 1134 1135 1136 void 1137 CalcView::_Init(BMessage* settings) 1138 { 1139 // create expression text view 1140 fExpressionTextView = new ExpressionTextView(_ExpressionRect(), this); 1141 AddChild(fExpressionTextView); 1142 1143 // read data from archive 1144 _LoadSettings(settings); 1145 1146 // fetch the calc icon for compact view 1147 _FetchAppIcon(fCalcIcon); 1148 1149 fEvaluateSemaphore = create_sem(1, "Evaluate Semaphore"); 1150 } 1151 1152 1153 status_t 1154 CalcView::_LoadSettings(BMessage* archive) 1155 { 1156 if (!archive) 1157 return B_BAD_VALUE; 1158 1159 // record calculator description 1160 BString calcDesc; 1161 archive->FindString("calcDesc", &calcDesc); 1162 if (calcDesc == "scientific" || calcDesc.StartsWith("ln")) 1163 fKeypadDescription = kKeypadDescriptionScientific; 1164 else 1165 fKeypadDescription = kKeypadDescriptionBasic; 1166 1167 // read grid dimensions 1168 if (archive->FindInt16("cols", &fColumns) < B_OK) 1169 fColumns = 5; 1170 if (archive->FindInt16("rows", &fRows) < B_OK) 1171 fRows = 4; 1172 1173 // read color scheme 1174 const rgb_color* color; 1175 ssize_t size; 1176 if (archive->FindData("rgbBaseColor", B_RGB_COLOR_TYPE, 1177 (const void**)&color, &size) < B_OK 1178 || size != sizeof(rgb_color)) { 1179 fBaseColor = ui_color(B_PANEL_BACKGROUND_COLOR); 1180 puts("Missing rgbBaseColor from CalcView archive!\n"); 1181 } else 1182 fBaseColor = *color; 1183 1184 if (archive->FindData("rgbDisplay", B_RGB_COLOR_TYPE, 1185 (const void**)&color, &size) < B_OK 1186 || size != sizeof(rgb_color)) { 1187 fExpressionBGColor = (rgb_color){ 0, 0, 0, 255 }; 1188 puts("Missing rgbBaseColor from CalcView archive!\n"); 1189 } else { 1190 fExpressionBGColor = *color; 1191 } 1192 1193 fHasCustomBaseColor = fBaseColor != ui_color(B_PANEL_BACKGROUND_COLOR); 1194 1195 // load options 1196 fOptions->LoadSettings(archive); 1197 1198 // load display text 1199 const char* display; 1200 if (archive->FindString("displayText", &display) < B_OK) { 1201 puts("Missing expression text from CalcView archive.\n"); 1202 } else { 1203 // init expression text 1204 fExpressionTextView->SetText(display); 1205 } 1206 1207 // load expression history 1208 fExpressionTextView->LoadSettings(archive); 1209 1210 // parse calculator description 1211 _ParseCalcDesc(fKeypadDescription); 1212 1213 // colorize based on base color. 1214 _Colorize(); 1215 1216 return B_OK; 1217 } 1218 1219 1220 void 1221 CalcView::_ParseCalcDesc(const char** keypadDescription) 1222 { 1223 // TODO: should calculate dimensions from desc here! 1224 fKeypad = new CalcKey[fRows * fColumns]; 1225 1226 // scan through calculator description and assemble keypad 1227 CalcKey* key = fKeypad; 1228 for (int i = 0; const char* p = keypadDescription[i]; i++) { 1229 // Move to next row as needed 1230 if (strcmp(p, "\n") == 0) 1231 continue; 1232 1233 // copy label 1234 strlcpy(key->label, B_TRANSLATE_NOCOLLECT(p), sizeof(key->label)); 1235 1236 // set code 1237 if (strcmp(p, "=") == 0) 1238 strlcpy(key->code, "\n", sizeof(key->code)); 1239 else 1240 strlcpy(key->code, p, sizeof(key->code)); 1241 1242 // set keymap 1243 if (strlen(key->label) == 1) 1244 strlcpy(key->keymap, key->label, sizeof(key->keymap)); 1245 else 1246 *key->keymap = '\0'; 1247 1248 key->flags = 0; 1249 1250 // add this to the expression text view, so that it 1251 // will forward the respective KeyDown event to us 1252 fExpressionTextView->AddKeypadLabel(key->label); 1253 1254 // advance 1255 key++; 1256 } 1257 } 1258 1259 1260 void 1261 CalcView::_PressKey(int key) 1262 { 1263 if (!fEnabled) 1264 return; 1265 1266 assert(key < (fRows * fColumns)); 1267 assert(key >= 0); 1268 1269 if (strcmp(fKeypad[key].code, "BS") == 0) { 1270 // BS means backspace 1271 fExpressionTextView->BackSpace(); 1272 } else if (strcmp(fKeypad[key].code, "C") == 0) { 1273 // C means clear 1274 fExpressionTextView->Clear(); 1275 } else if (strcmp(fKeypad[key].code, "acos") == 0 1276 || strcmp(fKeypad[key].code, "asin") == 0 1277 || strcmp(fKeypad[key].code, "atan") == 0 1278 || strcmp(fKeypad[key].code, "cbrt") == 0 1279 || strcmp(fKeypad[key].code, "ceil") == 0 1280 || strcmp(fKeypad[key].code, "cos") == 0 1281 || strcmp(fKeypad[key].code, "cosh") == 0 1282 || strcmp(fKeypad[key].code, "exp") == 0 1283 || strcmp(fKeypad[key].code, "floor") == 0 1284 || strcmp(fKeypad[key].code, "log") == 0 1285 || strcmp(fKeypad[key].code, "ln") == 0 1286 || strcmp(fKeypad[key].code, "sin") == 0 1287 || strcmp(fKeypad[key].code, "sinh") == 0 1288 || strcmp(fKeypad[key].code, "sqrt") == 0 1289 || strcmp(fKeypad[key].code, "tan") == 0 1290 || strcmp(fKeypad[key].code, "tanh") == 0) { 1291 int32 labelLen = strlen(fKeypad[key].code); 1292 int32 startSelection = 0; 1293 int32 endSelection = 0; 1294 fExpressionTextView->GetSelection(&startSelection, &endSelection); 1295 if (endSelection > startSelection) { 1296 // There is selected text, put it inbetween the parens 1297 fExpressionTextView->Insert(startSelection, fKeypad[key].code, 1298 labelLen); 1299 fExpressionTextView->Insert(startSelection + labelLen, "(", 1); 1300 fExpressionTextView->Insert(endSelection + labelLen + 1, ")", 1); 1301 // Put the cursor after the ending paren 1302 // Need to cast to BTextView because Select() is protected 1303 // in the InputTextView class 1304 static_cast<BTextView*>(fExpressionTextView)->Select( 1305 endSelection + labelLen + 2, endSelection + labelLen + 2); 1306 } else { 1307 // There is no selected text, insert at the cursor location 1308 fExpressionTextView->Insert(fKeypad[key].code); 1309 fExpressionTextView->Insert("()"); 1310 // Put the cursor inside the parens so you can enter an argument 1311 // Need to cast to BTextView because Select() is protected 1312 // in the InputTextView class 1313 static_cast<BTextView*>(fExpressionTextView)->Select( 1314 endSelection + labelLen + 1, endSelection + labelLen + 1); 1315 } 1316 } else if (strcmp(fKeypad[key].code, ".") == 0) { 1317 BLocale locale; 1318 BNumberFormat format(&locale); 1319 1320 fExpressionTextView->Insert(format.GetSeparator(B_DECIMAL_SEPARATOR)); 1321 } else { 1322 // check for evaluation order 1323 if (fKeypad[key].code[0] == '\n') { 1324 fExpressionTextView->ApplyChanges(); 1325 } else { 1326 // insert into expression text 1327 fExpressionTextView->Insert(fKeypad[key].code); 1328 } 1329 } 1330 } 1331 1332 1333 void 1334 CalcView::_PressKey(const char* label) 1335 { 1336 int32 key = _KeyForLabel(label); 1337 if (key >= 0) 1338 _PressKey(key); 1339 } 1340 1341 1342 int32 1343 CalcView::_KeyForLabel(const char* label) const 1344 { 1345 int keys = fRows * fColumns; 1346 for (int i = 0; i < keys; i++) { 1347 if (strcmp(fKeypad[i].label, label) == 0) { 1348 return i; 1349 } 1350 } 1351 return -1; 1352 } 1353 1354 1355 void 1356 CalcView::_FlashKey(int32 key, uint32 flashFlags) 1357 { 1358 if (fOptions->keypad_mode == KEYPAD_MODE_COMPACT) 1359 return; 1360 1361 if (flashFlags != 0) 1362 fKeypad[key].flags |= flashFlags; 1363 else 1364 fKeypad[key].flags = 0; 1365 Invalidate(); 1366 1367 if (fKeypad[key].flags == FLAGS_FLASH_KEY) { 1368 BMessage message(MSG_UNFLASH_KEY); 1369 message.AddInt32("key", key); 1370 BMessageRunner::StartSending(BMessenger(this), &message, 1371 kFlashOnOffInterval, 1); 1372 } 1373 } 1374 1375 1376 void 1377 CalcView::_Colorize() 1378 { 1379 // calculate light and dark color from base color 1380 fLightColor.red = (uint8)(fBaseColor.red * 1.25); 1381 fLightColor.green = (uint8)(fBaseColor.green * 1.25); 1382 fLightColor.blue = (uint8)(fBaseColor.blue * 1.25); 1383 fLightColor.alpha = 255; 1384 1385 fDarkColor.red = (uint8)(fBaseColor.red * 0.75); 1386 fDarkColor.green = (uint8)(fBaseColor.green * 0.75); 1387 fDarkColor.blue = (uint8)(fBaseColor.blue * 0.75); 1388 fDarkColor.alpha = 255; 1389 1390 // keypad text color 1391 if (fBaseColor.Brightness() > 100) 1392 fButtonTextColor = (rgb_color){ 0, 0, 0, 255 }; 1393 else 1394 fButtonTextColor = (rgb_color){ 255, 255, 255, 255 }; 1395 1396 // expression text color 1397 if (fExpressionBGColor.Brightness() > 100) 1398 fExpressionTextColor = (rgb_color){ 0, 0, 0, 255 }; 1399 else 1400 fExpressionTextColor = (rgb_color){ 255, 255, 255, 255 }; 1401 } 1402 1403 1404 void 1405 CalcView::_CreatePopUpMenu(bool addKeypadModeMenuItems) 1406 { 1407 // construct items 1408 fAutoNumlockItem = new BMenuItem(B_TRANSLATE("Enable Num Lock on startup"), 1409 new BMessage(MSG_OPTIONS_AUTO_NUM_LOCK)); 1410 fAngleModeRadianItem = new BMenuItem(B_TRANSLATE("Radians"), 1411 new BMessage(MSG_OPTIONS_ANGLE_MODE_RADIAN)); 1412 fAngleModeDegreeItem = new BMenuItem(B_TRANSLATE("Degrees"), 1413 new BMessage(MSG_OPTIONS_ANGLE_MODE_DEGREE)); 1414 if (addKeypadModeMenuItems) { 1415 fKeypadModeCompactItem = new BMenuItem(B_TRANSLATE("Compact"), 1416 new BMessage(MSG_OPTIONS_KEYPAD_MODE_COMPACT), '0'); 1417 fKeypadModeBasicItem = new BMenuItem(B_TRANSLATE("Basic"), 1418 new BMessage(MSG_OPTIONS_KEYPAD_MODE_BASIC), '1'); 1419 fKeypadModeScientificItem = new BMenuItem(B_TRANSLATE("Scientific"), 1420 new BMessage(MSG_OPTIONS_KEYPAD_MODE_SCIENTIFIC), '2'); 1421 } 1422 1423 // apply current settings 1424 fAutoNumlockItem->SetMarked(fOptions->auto_num_lock); 1425 fAngleModeRadianItem->SetMarked(!fOptions->degree_mode); 1426 fAngleModeDegreeItem->SetMarked(fOptions->degree_mode); 1427 1428 // construct menu 1429 fPopUpMenu = new BPopUpMenu("pop-up", false, false); 1430 1431 fPopUpMenu->AddItem(fAutoNumlockItem); 1432 fPopUpMenu->AddSeparatorItem(); 1433 fPopUpMenu->AddItem(fAngleModeRadianItem); 1434 fPopUpMenu->AddItem(fAngleModeDegreeItem); 1435 if (addKeypadModeMenuItems) { 1436 fPopUpMenu->AddSeparatorItem(); 1437 fPopUpMenu->AddItem(fKeypadModeCompactItem); 1438 fPopUpMenu->AddItem(fKeypadModeBasicItem); 1439 fPopUpMenu->AddItem(fKeypadModeScientificItem); 1440 _MarkKeypadItems(fOptions->keypad_mode); 1441 } 1442 } 1443 1444 1445 BRect 1446 CalcView::_ExpressionRect() const 1447 { 1448 BRect r(0.0, 0.0, fWidth, fHeight); 1449 if (fOptions->keypad_mode != KEYPAD_MODE_COMPACT) { 1450 r.bottom = floorf(fHeight * kDisplayScaleY) + 1; 1451 } 1452 return r; 1453 } 1454 1455 1456 BRect 1457 CalcView::_KeypadRect() const 1458 { 1459 BRect r(0.0, 0.0, -1.0, -1.0); 1460 if (fOptions->keypad_mode != KEYPAD_MODE_COMPACT) { 1461 r.right = fWidth; 1462 r.bottom = fHeight; 1463 r.top = floorf(fHeight * kDisplayScaleY); 1464 } 1465 return r; 1466 } 1467 1468 1469 void 1470 CalcView::_MarkKeypadItems(uint8 keypad_mode) 1471 { 1472 switch (keypad_mode) { 1473 case KEYPAD_MODE_COMPACT: 1474 fKeypadModeCompactItem->SetMarked(true); 1475 fKeypadModeBasicItem->SetMarked(false); 1476 fKeypadModeScientificItem->SetMarked(false); 1477 break; 1478 1479 case KEYPAD_MODE_SCIENTIFIC: 1480 fKeypadModeCompactItem->SetMarked(false); 1481 fKeypadModeBasicItem->SetMarked(false); 1482 fKeypadModeScientificItem->SetMarked(true); 1483 break; 1484 1485 default: // KEYPAD_MODE_BASIC is the default 1486 fKeypadModeCompactItem->SetMarked(false); 1487 fKeypadModeBasicItem->SetMarked(true); 1488 fKeypadModeScientificItem->SetMarked(false); 1489 } 1490 } 1491 1492 1493 void 1494 CalcView::_FetchAppIcon(BBitmap* into) 1495 { 1496 entry_ref appRef; 1497 status_t status = be_roster->FindApp(kSignature, &appRef); 1498 if (status == B_OK) { 1499 BFile file(&appRef, B_READ_ONLY); 1500 BAppFileInfo appInfo(&file); 1501 status = appInfo.GetIcon(into, B_MINI_ICON); 1502 } 1503 if (status != B_OK) 1504 memset(into->Bits(), 0, into->BitsLength()); 1505 } 1506 1507 1508 // Returns whether or not CalcView is embedded somewhere, most likely 1509 // the Desktop 1510 bool 1511 CalcView::_IsEmbedded() 1512 { 1513 return Parent() != NULL && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0; 1514 } 1515 1516 1517 void 1518 CalcView::_SetEnabled(bool enable) 1519 { 1520 fEnabled = enable; 1521 fExpressionTextView->MakeSelectable(enable); 1522 fExpressionTextView->MakeEditable(enable); 1523 } 1524