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