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