1 /* 2 * Copyright 2006 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 * Timothy Wayper <timmy@wunderbear.com> 8 * Stephan Aßmus <superstippi@gmx.de> 9 */ 10 11 #include "CalcView.h" 12 13 #include <stdlib.h> 14 #include <stdio.h> 15 #include <string.h> 16 #include <ctype.h> 17 #include <assert.h> 18 19 #include <Alert.h> 20 #include <Application.h> 21 #include <AppFileInfo.h> 22 #include <Beep.h> 23 #include <Bitmap.h> 24 #include <Clipboard.h> 25 #include <File.h> 26 #include <Font.h> 27 #include <MenuItem.h> 28 #include <Message.h> 29 #include <PlaySound.h> 30 #include <Point.h> 31 #include <PopUpMenu.h> 32 #include <Roster.h> 33 34 #include "CalcApplication.h" 35 #include "CalcOptions.h" 36 #include "ExpressionParser.h" 37 #include "ExpressionTextView.h" 38 39 40 const uint8 K_COLOR_OFFSET = 32; 41 const float K_FONT_YPROP = 0.6f; 42 const float K_DISPLAY_YPROP = 0.2f; 43 44 enum { 45 K_OPTIONS_AUTO_NUM_LOCK = 'opan', 46 K_OPTIONS_AUDIO_FEEDBACK = 'opaf', 47 K_OPTIONS_SHOW_KEYPAD = 'opsk' 48 }; 49 50 // default calculator key pad layout 51 const char *kDefaultKeypadDescription = 52 "7 8 9 ( ) \n" 53 "4 5 6 * / \n" 54 "1 2 3 + - \n" 55 "0 . BS = C \n"; 56 57 58 struct CalcView::CalcKey { 59 char label[8]; 60 char code[8]; 61 char keymap[4]; 62 uint32 flags; 63 float width; 64 }; 65 66 67 CalcView *CalcView::Instantiate(BMessage *archive) 68 { 69 if (!validate_instantiation(archive, "CalcView")) 70 return NULL; 71 72 return new CalcView(archive); 73 } 74 75 76 CalcView::CalcView(BRect frame, rgb_color rgbBaseColor) 77 : BView(frame, "DeskCalc", B_FOLLOW_ALL_SIDES, 78 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS), 79 fColums(5), 80 fRows(4), 81 82 fBaseColor(rgbBaseColor), 83 fExpressionBGColor((rgb_color){ 0, 0, 0, 255 }), 84 85 fWidth(1), 86 fHeight(1), 87 88 fKeypadDescription(strdup(kDefaultKeypadDescription)), 89 fKeypad(NULL), 90 91 #ifdef __HAIKU__ 92 fCalcIcon(new BBitmap(BRect(0, 0, 15, 15), 0, B_RGBA32)), 93 #else 94 fCalcIcon(new BBitmap(BRect(0, 0, 15, 15), 0, B_CMAP8)), 95 #endif 96 97 fPopUpMenu(NULL), 98 fAutoNumlockItem(NULL), 99 fAudioFeedbackItem(NULL), 100 fShowKeypadItem(NULL), 101 fAboutItem(NULL), 102 103 fOptions(new CalcOptions()), 104 fShowKeypad(true) 105 { 106 // create expression text view 107 fExpressionTextView = new ExpressionTextView(_ExpressionRect(), this); 108 AddChild(fExpressionTextView); 109 110 // tell the app server not to erase our b/g 111 SetViewColor(B_TRANSPARENT_32_BIT); 112 113 // parse calculator description 114 _ParseCalcDesc(fKeypadDescription); 115 116 // colorize based on base color. 117 _Colorize(); 118 119 // create pop-up menu system 120 _CreatePopUpMenu(); 121 122 _FetchAppIcon(fCalcIcon); 123 } 124 125 126 CalcView::CalcView(BMessage* archive) 127 : BView(archive), 128 fColums(5), 129 fRows(4), 130 131 fBaseColor((rgb_color){ 128, 128, 128, 255 }), 132 fExpressionBGColor((rgb_color){ 0, 0, 0, 255 }), 133 134 fWidth(1), 135 fHeight(1), 136 137 fKeypadDescription(strdup(kDefaultKeypadDescription)), 138 fKeypad(NULL), 139 140 #ifdef __HAIKU__ 141 fCalcIcon(new BBitmap(BRect(0, 0, 15, 15), 0, B_RGBA32)), 142 #else 143 fCalcIcon(new BBitmap(BRect(0, 0, 15, 15), 0, B_CMAP8)), 144 #endif 145 146 fPopUpMenu(NULL), 147 fAutoNumlockItem(NULL), 148 fAudioFeedbackItem(NULL), 149 fShowKeypadItem(NULL), 150 fAboutItem(NULL), 151 152 fOptions(new CalcOptions()), 153 fShowKeypad(true) 154 { 155 // create expression text view 156 fExpressionTextView = new ExpressionTextView(_ExpressionRect(), this); 157 AddChild(fExpressionTextView); 158 159 // read data from archive 160 LoadSettings(archive); 161 162 // create pop-up menu system 163 _CreatePopUpMenu(); 164 165 _FetchAppIcon(fCalcIcon); 166 } 167 168 169 CalcView::~CalcView() 170 { 171 delete fKeypad; 172 delete fOptions; 173 free(fKeypadDescription); 174 } 175 176 177 void 178 CalcView::AttachedToWindow() 179 { 180 SetFont(be_bold_font); 181 182 BRect frame(Frame()); 183 FrameResized(frame.Width(), frame.Height()); 184 185 fPopUpMenu->SetTargetForItems(this); 186 } 187 188 189 void 190 CalcView::MessageReceived(BMessage* message) 191 { 192 //message->PrintToStream(); 193 194 // check if message was dropped 195 if (message->WasDropped()) { 196 // pass message on to paste 197 if (message->IsSourceRemote()) 198 Paste(message); 199 200 } else { 201 202 // act on posted message type 203 switch (message->what) { 204 205 // handle "cut" 206 case B_CUT: 207 Cut(); 208 break; 209 210 // handle copy 211 case B_COPY: 212 Copy(); 213 break; 214 215 // handle paste 216 case B_PASTE: 217 // access system clipboard 218 if (be_clipboard->Lock()){ 219 BMessage *clipper = be_clipboard->Data(); 220 //clipper->PrintToStream(); 221 Paste(clipper); 222 be_clipboard->Unlock(); 223 } // end if 224 break; 225 226 // (replicant) about box requested 227 case B_ABOUT_REQUESTED: 228 AboutRequested(); 229 break; 230 231 case K_OPTIONS_AUTO_NUM_LOCK: 232 fOptions->auto_num_lock = !fOptions->auto_num_lock; 233 fAutoNumlockItem->SetMarked(fOptions->auto_num_lock); 234 break; 235 236 case K_OPTIONS_AUDIO_FEEDBACK: 237 fOptions->audio_feedback = !fOptions->audio_feedback; 238 fAudioFeedbackItem->SetMarked(fOptions->audio_feedback); 239 break; 240 241 case K_OPTIONS_SHOW_KEYPAD: 242 fOptions->show_keypad = !fOptions->show_keypad; 243 fShowKeypadItem->SetMarked(fOptions->show_keypad); 244 _ShowKeypad(fOptions->show_keypad); 245 break; 246 247 default: 248 BView::MessageReceived(message); 249 break; 250 } 251 } 252 } 253 254 255 void 256 CalcView::Draw(BRect updateRect) 257 { 258 BRect expressionRect(_ExpressionRect()); 259 if (updateRect.Intersects(expressionRect)) { 260 if (!fShowKeypad && expressionRect.Height() >= fCalcIcon->Bounds().Height()) { 261 // render calc icon 262 expressionRect.left = fExpressionTextView->Frame().right + 2; 263 SetHighColor(fBaseColor); 264 FillRect(updateRect & expressionRect); 265 266 if (fCalcIcon->ColorSpace() == B_RGBA32) { 267 SetDrawingMode(B_OP_ALPHA); 268 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 269 } else { 270 SetDrawingMode(B_OP_OVER); 271 } 272 273 BPoint iconPos; 274 iconPos.x = expressionRect.right - (expressionRect.Width() 275 + fCalcIcon->Bounds().Width()) / 2.0; 276 iconPos.y = expressionRect.top + (expressionRect.Height() 277 - fCalcIcon->Bounds().Height()) / 2.0; 278 DrawBitmap(fCalcIcon, iconPos); 279 280 SetDrawingMode(B_OP_COPY); 281 } 282 283 // render border around expression text view 284 expressionRect = fExpressionTextView->Frame(); 285 expressionRect.InsetBy(-2, -2); 286 BeginLineArray(8); 287 288 rgb_color lightShadow = tint_color(fBaseColor, B_DARKEN_1_TINT); 289 rgb_color darkShadow = tint_color(fBaseColor, B_DARKEN_3_TINT); 290 291 AddLine(BPoint(expressionRect.left, expressionRect.bottom), 292 BPoint(expressionRect.left, expressionRect.top), 293 lightShadow); 294 AddLine(BPoint(expressionRect.left + 1, expressionRect.top), 295 BPoint(expressionRect.right, expressionRect.top), 296 lightShadow); 297 AddLine(BPoint(expressionRect.right, expressionRect.top + 1), 298 BPoint(expressionRect.right, expressionRect.bottom), 299 fLightColor); 300 AddLine(BPoint(expressionRect.left + 1, expressionRect.bottom), 301 BPoint(expressionRect.right - 1, expressionRect.bottom), 302 fLightColor); 303 304 expressionRect.InsetBy(1, 1); 305 AddLine(BPoint(expressionRect.left, expressionRect.bottom), 306 BPoint(expressionRect.left, expressionRect.top), 307 darkShadow); 308 AddLine(BPoint(expressionRect.left + 1, expressionRect.top), 309 BPoint(expressionRect.right, expressionRect.top), 310 darkShadow); 311 AddLine(BPoint(expressionRect.right, expressionRect.top + 1), 312 BPoint(expressionRect.right, expressionRect.bottom), 313 fBaseColor); 314 AddLine(BPoint(expressionRect.left + 1, expressionRect.bottom), 315 BPoint(expressionRect.right - 1, expressionRect.bottom), 316 fBaseColor); 317 318 EndLineArray(); 319 } 320 321 if (!fShowKeypad) 322 return; 323 324 // calculate grid sizes 325 BRect keypadRect(_KeypadRect()); 326 float sizeDisp = keypadRect.top; 327 float sizeCol = fWidth / (float)fColums; 328 float sizeRow = (fHeight - sizeDisp) / (float)fRows; 329 330 if (updateRect.Intersects(keypadRect)) { 331 // TODO: support pressed keys 332 333 // paint keypad b/g 334 SetHighColor(fBaseColor); 335 FillRect(updateRect & keypadRect); 336 337 // render key main grid 338 BeginLineArray(((fColums + fRows) << 1) + 1); 339 340 // render cols 341 AddLine(BPoint(0.0, sizeDisp), 342 BPoint(0.0, fHeight), 343 fLightColor); 344 for (int col = 1; col < fColums; col++) { 345 AddLine(BPoint(col * sizeCol - 1.0, sizeDisp), 346 BPoint(col * sizeCol - 1.0, fHeight), 347 fDarkColor); 348 AddLine(BPoint(col * sizeCol, sizeDisp), 349 BPoint(col * sizeCol, fHeight), 350 fLightColor); 351 } 352 AddLine(BPoint(fColums * sizeCol, sizeDisp), 353 BPoint(fColums * sizeCol, fHeight), 354 fDarkColor); 355 356 // render rows 357 for (int row = 0; row < fRows; row++) { 358 AddLine(BPoint(0.0, sizeDisp + row * sizeRow - 1.0), 359 BPoint(fWidth, sizeDisp + row * sizeRow - 1.0), 360 fDarkColor); 361 AddLine(BPoint(0.0, sizeDisp + row * sizeRow), 362 BPoint(fWidth, sizeDisp + row * sizeRow), 363 fLightColor); 364 } 365 AddLine(BPoint(0.0, sizeDisp + fRows * sizeRow), 366 BPoint(fWidth, sizeDisp + fRows * sizeRow), 367 fDarkColor); 368 369 // main grid complete 370 EndLineArray(); 371 372 // render key symbols 373 float halfSizeCol = sizeCol * 0.5f; 374 SetHighColor(fButtonTextColor); 375 SetLowColor(fBaseColor); 376 SetDrawingMode(B_OP_COPY); 377 SetFontSize(((fHeight - sizeDisp) / (float)fRows) * K_FONT_YPROP); 378 float baselineOffset = ((fHeight - sizeDisp) / (float)fRows) 379 * (1.0 - K_FONT_YPROP) * 0.5; 380 CalcKey *key = fKeypad; 381 for (int row = 0; row < fRows; row++) { 382 for (int col = 0; col < fColums; col++) { 383 float halfSymbolWidth = StringWidth(key->label) * 0.5f; 384 DrawString(key->label, 385 BPoint(col * sizeCol + halfSizeCol - halfSymbolWidth, 386 sizeDisp + (row + 1) * sizeRow - baselineOffset)); 387 key++; 388 } 389 } 390 } 391 } 392 393 394 void 395 CalcView::MouseDown(BPoint point) 396 { 397 // ensure this view is the current focus 398 if (!IsFocus()) 399 MakeFocus(); 400 401 // read mouse buttons state 402 int32 buttons = 0; 403 Window()->CurrentMessage()->FindInt32("buttons", &buttons); 404 405 // display popup menu if not primary mouse button 406 if ((B_PRIMARY_MOUSE_BUTTON & buttons) == 0) { 407 BMenuItem* selected; 408 if ((selected = fPopUpMenu->Go(ConvertToScreen(point))) != NULL) 409 MessageReceived(selected->Message()); 410 return; 411 } 412 413 if (!fShowKeypad) 414 return; 415 416 // calculate grid sizes 417 float sizeDisp = fHeight * K_DISPLAY_YPROP; 418 float sizeCol = fWidth / (float)fColums; 419 float sizeRow = (fHeight - sizeDisp) / (float)fRows; 420 421 // calculate location within grid 422 int gridCol = (int)floorf(point.x / sizeCol); 423 int gridRow = (int)floorf((point.y - sizeDisp) / sizeRow); 424 425 // check limits 426 if ((gridCol >= 0) && (gridCol < fColums) && 427 (gridRow >= 0) && (gridRow < fRows)) { 428 429 // process key press 430 _PressKey(gridRow * fColums + gridCol); 431 } 432 } 433 434 435 void 436 CalcView::KeyDown(const char *bytes, int32 numBytes) 437 { 438 // if single byte character... 439 if (numBytes == 1) { 440 441 //printf("Key pressed: %c\n", bytes[0]); 442 443 switch (bytes[0]) { 444 445 case B_ENTER: 446 // translate to evaluate key 447 _PressKey("="); 448 break; 449 450 case B_LEFT_ARROW: 451 case B_BACKSPACE: 452 // translate to backspace key 453 _PressKey("BS"); 454 break; 455 456 case B_SPACE: 457 case B_ESCAPE: 458 case 'c': 459 // translate to clear key 460 _PressKey("C"); 461 break; 462 463 // bracket translation 464 case '[': 465 case '{': 466 _PressKey("("); 467 break; 468 469 case ']': 470 case '}': 471 _PressKey(")"); 472 break; 473 474 default: { 475 // scan the keymap array for match 476 int keys = fRows * fColums; 477 for (int i = 0; i < keys; i++) { 478 if (fKeypad[i].keymap[0] == bytes[0]) { 479 _PressKey(i); 480 return; 481 } 482 } 483 break; 484 } 485 } 486 } 487 } 488 489 490 void 491 CalcView::MakeFocus(bool focused) 492 { 493 if (focused) { 494 // set num lock 495 if (fOptions->auto_num_lock) { 496 set_keyboard_locks(B_NUM_LOCK | 497 (modifiers() & (B_CAPS_LOCK | B_SCROLL_LOCK))); 498 } 499 } 500 501 // pass on request to text view 502 fExpressionTextView->MakeFocus(focused); 503 } 504 505 506 void 507 CalcView::FrameResized(float width, float height) 508 { 509 fWidth = width; 510 fHeight = height; 511 512 // layout expression text view 513 BRect frame = _ExpressionRect(); 514 frame.InsetBy(2, 2); 515 516 if (!fShowKeypad) 517 frame.right -= ceilf(fCalcIcon->Bounds().Width() * 1.5); 518 519 fExpressionTextView->MoveTo(frame.LeftTop()); 520 fExpressionTextView->ResizeTo(frame.Width(), frame.Height()); 521 522 frame.OffsetTo(B_ORIGIN); 523 frame.InsetBy(2, 2); 524 fExpressionTextView->SetTextRect(frame); 525 526 // configure expression text view font size and color 527 float sizeDisp = fShowKeypad ? fHeight * K_DISPLAY_YPROP : fHeight; 528 BFont font(be_bold_font); 529 font.SetSize(sizeDisp * K_FONT_YPROP); 530 // fExpressionTextView->SetViewColor(fExpressionBGColor); 531 // fExpressionTextView->SetLowColor(fExpressionBGColor); 532 // fExpressionTextView->SetFontAndColor(&font, B_FONT_ALL, &fExpressionTextColor); 533 fExpressionTextView->SetFontAndColor(&font, B_FONT_ALL); 534 } 535 536 537 void 538 CalcView::AboutRequested() 539 { 540 BAlert* alert = new BAlert("about", 541 "DeskCalc v2.1.0\n\n" 542 "written by Timothy Wayper,\nStephan Aßmus and Ingo Weinhold\n\n" 543 B_UTF8_COPYRIGHT"1997, 1998 R3 Software Ltd.\n" 544 B_UTF8_COPYRIGHT"2006-2009 Haiku, Inc.\n\n" 545 "All Rights Reserved.", "Cool"); 546 alert->Go(NULL); 547 } 548 549 550 status_t 551 CalcView::Archive(BMessage* archive, bool deep) const 552 { 553 fExpressionTextView->RemoveSelf(); 554 555 // passed on request to parent 556 status_t ret = BView::Archive(archive, deep); 557 558 const_cast<CalcView*>(this)->AddChild(fExpressionTextView); 559 560 // save app signature for replicant add-on loading 561 if (ret == B_OK) 562 ret = archive->AddString("add_on", kAppSig); 563 564 // save all the options 565 if (ret == B_OK) 566 ret = SaveSettings(archive); 567 568 // add class info last 569 if (ret == B_OK) 570 ret = archive->AddString("class", "CalcView"); 571 572 return ret; 573 } 574 575 576 void 577 CalcView::Cut() 578 { 579 Copy(); // copy data to clipboard 580 fExpressionTextView->Clear(); // remove data 581 } 582 583 584 void 585 CalcView::Copy() 586 { 587 // access system clipboard 588 if (be_clipboard->Lock()) { 589 be_clipboard->Clear(); 590 BMessage *clipper = be_clipboard->Data(); 591 clipper->what = B_MIME_DATA; 592 // TODO: should check return for errors! 593 BString expression = fExpressionTextView->Text(); 594 clipper->AddData("text/plain", 595 B_MIME_TYPE, 596 expression.String(), 597 expression.Length()); 598 //clipper->PrintToStream(); 599 be_clipboard->Commit(); 600 be_clipboard->Unlock(); 601 } 602 } 603 604 605 void 606 CalcView::Paste(BMessage *message) 607 { 608 // handle color drops first 609 // read incoming color 610 const rgb_color* dropColor = NULL; 611 ssize_t dataSize; 612 if (message->FindData("RGBColor", 613 B_RGB_COLOR_TYPE, 614 (const void**)&dropColor, 615 &dataSize) == B_OK 616 && dataSize == sizeof(rgb_color)) { 617 618 // calculate view relative drop point 619 BPoint dropPoint = ConvertFromScreen(message->DropPoint()); 620 621 // calculate current keypad area 622 float sizeDisp = fHeight * K_DISPLAY_YPROP; 623 BRect keypadRect(0.0, sizeDisp, fWidth, fHeight); 624 625 // check location of color drop 626 if (keypadRect.Contains(dropPoint) && dropColor) { 627 fBaseColor = *dropColor; 628 _Colorize(); 629 // redraw keypad 630 Invalidate(keypadRect); 631 } 632 633 } else { 634 // look for text/plain MIME data 635 const char* text; 636 ssize_t numBytes; 637 if (message->FindData("text/plain", 638 B_MIME_TYPE, 639 (const void**)&text, 640 &numBytes) == B_OK) { 641 BString temp; 642 temp.Append(text, numBytes); 643 fExpressionTextView->Insert(temp.String()); 644 } 645 } 646 } 647 648 649 status_t 650 CalcView::LoadSettings(BMessage* archive) 651 { 652 if (!archive) 653 return B_BAD_VALUE; 654 655 // record calculator description 656 const char* calcDesc; 657 if (archive->FindString("calcDesc", &calcDesc) < B_OK) 658 calcDesc = kDefaultKeypadDescription; 659 660 // save calculator description for reference 661 free(fKeypadDescription); 662 fKeypadDescription = strdup(calcDesc); 663 664 // read grid dimensions 665 if (archive->FindInt16("cols", &fColums) < B_OK) 666 fColums = 5; 667 if (archive->FindInt16("rows", &fRows) < B_OK) 668 fRows = 4; 669 670 // read color scheme 671 const rgb_color* color; 672 ssize_t size; 673 if (archive->FindData("rgbBaseColor", B_RGB_COLOR_TYPE, 674 (const void**)&color, &size) < B_OK 675 || size != sizeof(rgb_color)) { 676 fBaseColor = (rgb_color){ 128, 128, 128, 255 }; 677 puts("Missing rgbBaseColor from CalcView archive!\n"); 678 } else { 679 fBaseColor = *color; 680 } 681 682 if (archive->FindData("rgbDisplay", B_RGB_COLOR_TYPE, 683 (const void**)&color, &size) < B_OK 684 || size != sizeof(rgb_color)) { 685 fExpressionBGColor = (rgb_color){ 0, 0, 0, 255 }; 686 puts("Missing rgbBaseColor from CalcView archive!\n"); 687 } else { 688 fExpressionBGColor = *color; 689 } 690 691 // load options 692 fOptions->LoadSettings(archive); 693 fShowKeypad = fOptions->show_keypad; 694 695 // load display text 696 const char* display; 697 if (archive->FindString("displayText", &display) < B_OK) { 698 puts("Missing expression text from CalcView archive.\n"); 699 } else { 700 // init expression text 701 fExpressionTextView->SetText(display); 702 } 703 704 // load expression history 705 fExpressionTextView->LoadSettings(archive); 706 707 // parse calculator description 708 _ParseCalcDesc(fKeypadDescription); 709 710 // colorize based on base color. 711 _Colorize(); 712 713 return B_OK; 714 } 715 716 717 status_t 718 CalcView::SaveSettings(BMessage* archive) const 719 { 720 status_t ret = archive ? B_OK : B_BAD_VALUE; 721 722 // record grid dimensions 723 if (ret == B_OK) 724 ret = archive->AddInt16("cols", fColums); 725 if (ret == B_OK) 726 ret = archive->AddInt16("rows", fRows); 727 728 // record color scheme 729 if (ret == B_OK) 730 ret = archive->AddData("rgbBaseColor", B_RGB_COLOR_TYPE, 731 &fBaseColor, sizeof(rgb_color)); 732 if (ret == B_OK) 733 ret = archive->AddData("rgbDisplay", B_RGB_COLOR_TYPE, 734 &fExpressionBGColor, sizeof(rgb_color)); 735 736 // record current options 737 if (ret == B_OK) 738 ret = fOptions->SaveSettings(archive); 739 740 // record display text 741 if (ret == B_OK) 742 ret = archive->AddString("displayText", fExpressionTextView->Text()); 743 744 // record expression history 745 if (ret == B_OK) 746 ret = fExpressionTextView->SaveSettings(archive); 747 748 // record calculator description 749 if (ret == B_OK) 750 ret = archive->AddString("calcDesc", fKeypadDescription); 751 752 return ret; 753 } 754 755 756 void 757 CalcView::Evaluate() 758 { 759 BString expression = fExpressionTextView->Text(); 760 761 if (expression.Length() == 0) { 762 beep(); 763 return; 764 } 765 766 // audio feedback 767 if (fOptions->audio_feedback) { 768 BEntry zimp("zimp.AIFF"); 769 entry_ref zimp_ref; 770 zimp.GetRef(&zimp_ref); 771 play_sound(&zimp_ref, true, false, false); 772 } 773 774 // evaluate expression 775 BString value; 776 777 try { 778 ExpressionParser parser; 779 value = parser.Evaluate(expression.String()); 780 } catch (ParseException e) { 781 BString error(e.message.String()); 782 error << " at " << (e.position + 1); 783 fExpressionTextView->SetText(error.String()); 784 return; 785 } 786 787 // render new result to display 788 fExpressionTextView->SetExpression(value.String()); 789 } 790 791 792 void 793 CalcView::FlashKey(const char* bytes, int32 numBytes) 794 { 795 BString temp; 796 temp.Append(bytes, numBytes); 797 int32 key = _KeyForLabel(temp.String()); 798 if (key >= 0) 799 _FlashKey(key); 800 } 801 802 803 // #pragma mark - 804 805 806 void 807 CalcView::_ParseCalcDesc(const char* keypadDescription) 808 { 809 // TODO: should calculate dimensions from desc here! 810 fKeypad = new CalcKey[fRows * fColums]; 811 812 // scan through calculator description and assemble keypad 813 CalcKey *key = fKeypad; 814 const char *p = keypadDescription; 815 816 while (*p != 0) { 817 // copy label 818 char *l = key->label; 819 while (!isspace(*p)) 820 *l++ = *p++; 821 *l = '\0'; 822 823 // set code 824 if (strcmp(key->label, "=") == 0) 825 strcpy(key->code, "\n"); 826 else 827 strcpy(key->code, key->label); 828 829 // set keymap 830 if (strlen(key->label) == 1) { 831 strcpy(key->keymap, key->label); 832 } else { 833 *key->keymap = '\0'; 834 } // end if 835 836 // add this to the expression text view, so that it 837 // will forward the respective KeyDown event to us 838 fExpressionTextView->AddKeypadLabel(key->label); 839 840 // advance 841 while (isspace(*p)) 842 ++p; 843 key++; 844 } 845 } 846 847 848 void 849 CalcView::_PressKey(int key) 850 { 851 assert(key < (fRows * fColums)); 852 assert(key >= 0); 853 854 // check for backspace 855 if (strcmp(fKeypad[key].label, "BS") == 0) { 856 fExpressionTextView->BackSpace(); 857 } else if (strcmp(fKeypad[key].label, "C") == 0) { 858 // C means clear 859 fExpressionTextView->Clear(); 860 } else { 861 // check for evaluation order 862 if (fKeypad[key].code[0] == '\n') { 863 Evaluate(); 864 } else { 865 // insert into expression text 866 fExpressionTextView->Insert(fKeypad[key].code); 867 868 // audio feedback 869 if (fOptions->audio_feedback) { 870 BEntry zimp("key.AIFF"); 871 entry_ref zimp_ref; 872 zimp.GetRef(&zimp_ref); 873 play_sound(&zimp_ref, true, false, true); 874 } 875 } 876 } 877 878 // redraw display 879 // _InvalidateExpression(); 880 } 881 882 883 void 884 CalcView::_PressKey(const char *label) 885 { 886 int32 key = _KeyForLabel(label); 887 if (key >= 0) 888 _PressKey(key); 889 } 890 891 892 int32 893 CalcView::_KeyForLabel(const char *label) const 894 { 895 int keys = fRows * fColums; 896 for (int i = 0; i < keys; i++) { 897 if (strcmp(fKeypad[i].label, label) == 0) { 898 return i; 899 } 900 } 901 return -1; 902 } 903 904 905 void 906 CalcView::_FlashKey(int32 key) 907 { 908 // TODO ... 909 } 910 911 912 void 913 CalcView::_Colorize() 914 { 915 // calculate light and dark color from base color 916 fLightColor.red = (uint8)(fBaseColor.red * 1.25); 917 fLightColor.green = (uint8)(fBaseColor.green * 1.25); 918 fLightColor.blue = (uint8)(fBaseColor.blue * 1.25); 919 fLightColor.alpha = 255; 920 921 fDarkColor.red = (uint8)(fBaseColor.red * 0.75); 922 fDarkColor.green = (uint8)(fBaseColor.green * 0.75); 923 fDarkColor.blue = (uint8)(fBaseColor.blue * 0.75); 924 fDarkColor.alpha = 255; 925 926 // keypad text color 927 uint8 lightness = (fBaseColor.red + fBaseColor.green + fBaseColor.blue) / 3; 928 if (lightness > 200) 929 fButtonTextColor = (rgb_color){ 0, 0, 0, 255 }; 930 else 931 fButtonTextColor = (rgb_color){ 255, 255, 255, 255 }; 932 933 // expression text color 934 lightness = (fExpressionBGColor.red + 935 fExpressionBGColor.green + fExpressionBGColor.blue) / 3; 936 if (lightness > 200) 937 fExpressionTextColor = (rgb_color){ 0, 0, 0, 255 }; 938 else 939 fExpressionTextColor = (rgb_color){ 255, 255, 255, 255 }; 940 } 941 942 943 void 944 CalcView::_CreatePopUpMenu() 945 { 946 // construct items 947 fAutoNumlockItem = new BMenuItem("Enable Num Lock on start up", 948 new BMessage(K_OPTIONS_AUTO_NUM_LOCK)); 949 fAudioFeedbackItem = new BMenuItem("Audio Feedback" B_UTF8_ELLIPSIS, 950 new BMessage(K_OPTIONS_AUDIO_FEEDBACK)); 951 fShowKeypadItem = new BMenuItem("Show Keypad", 952 new BMessage(K_OPTIONS_SHOW_KEYPAD)); 953 fAboutItem = new BMenuItem("About DeskCalc", 954 new BMessage(B_ABOUT_REQUESTED)); 955 956 // apply current settings 957 fAutoNumlockItem->SetMarked(fOptions->auto_num_lock); 958 fAudioFeedbackItem->SetMarked(fOptions->audio_feedback); 959 fShowKeypadItem->SetMarked(fOptions->show_keypad); 960 961 // construct menu 962 fPopUpMenu = new BPopUpMenu("pop-up", false, false); 963 964 fPopUpMenu->AddItem(fAutoNumlockItem); 965 fPopUpMenu->AddItem(fAudioFeedbackItem); 966 fPopUpMenu->AddItem(fShowKeypadItem); 967 fPopUpMenu->AddSeparatorItem(); 968 fPopUpMenu->AddItem(fAboutItem); 969 } 970 971 972 BRect 973 CalcView::_ExpressionRect() const 974 { 975 BRect r(0.0, 0.0, fWidth, fHeight); 976 if (fShowKeypad) { 977 r.bottom = floorf(fHeight * K_DISPLAY_YPROP); 978 } 979 return r; 980 } 981 982 983 BRect 984 CalcView::_KeypadRect() const 985 { 986 BRect r(0.0, 0.0, -1.0, -1.0); 987 if (fShowKeypad) { 988 r.right = fWidth; 989 r.bottom = fHeight; 990 r.top = floorf(fHeight * K_DISPLAY_YPROP) + 1; 991 } 992 return r; 993 } 994 995 996 void 997 CalcView::_ShowKeypad(bool show) 998 { 999 if (fShowKeypad == show) 1000 return; 1001 1002 fShowKeypad = show; 1003 1004 float height = fShowKeypad ? fHeight / K_DISPLAY_YPROP 1005 : fHeight * K_DISPLAY_YPROP; 1006 1007 BWindow* window = Window(); 1008 if (window->Bounds() == Frame()) 1009 window->ResizeTo(fWidth, height); 1010 else 1011 ResizeTo(fWidth, height); 1012 } 1013 1014 1015 void 1016 CalcView::_FetchAppIcon(BBitmap* into) 1017 { 1018 entry_ref appRef; 1019 be_roster->FindApp(kAppSig, &appRef); 1020 BFile file(&appRef, B_READ_ONLY); 1021 BAppFileInfo appInfo(&file); 1022 if (appInfo.GetIcon(into, B_MINI_ICON) != B_OK) 1023 memset(into->Bits(), 0, into->BitsLength()); 1024 } 1025