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