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