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.2; 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 MakeFocus(); 408 409 // read mouse buttons state 410 int32 buttons = 0; 411 Window()->CurrentMessage()->FindInt32("buttons", &buttons); 412 413 // display popup menu if not primary mouse button 414 if ((B_PRIMARY_MOUSE_BUTTON & buttons) == 0) { 415 BMenuItem* selected; 416 if ((selected = fPopUpMenu->Go(ConvertToScreen(point))) != NULL) 417 MessageReceived(selected->Message()); 418 return; 419 } 420 421 if (!fShowKeypad) 422 return; 423 424 // calculate grid sizes 425 float sizeDisp = fHeight * K_DISPLAY_YPROP; 426 float sizeCol = fWidth / (float)fColums; 427 float sizeRow = (fHeight - sizeDisp) / (float)fRows; 428 429 // calculate location within grid 430 int gridCol = (int)(point.x / sizeCol); 431 int gridRow = (int)((point.y - sizeDisp) / sizeRow); 432 433 // check limits 434 if ((gridCol >= 0) && (gridCol < fColums) && 435 (gridRow >= 0) && (gridRow < fRows)) { 436 437 // process key press 438 _PressKey(gridRow * fColums + gridCol); 439 } 440 } 441 442 443 void 444 CalcView::KeyDown(const char *bytes, int32 numBytes) 445 { 446 // if single byte character... 447 if (numBytes == 1) { 448 449 //printf("Key pressed: %c\n", bytes[0]); 450 451 switch (bytes[0]) { 452 453 case B_ENTER: 454 // translate to evaluate key 455 _PressKey("="); 456 break; 457 458 case B_LEFT_ARROW: 459 case B_BACKSPACE: 460 // translate to backspace key 461 _PressKey("BS"); 462 break; 463 464 case B_SPACE: 465 case B_ESCAPE: 466 case 'c': 467 // translate to clear key 468 _PressKey("C"); 469 break; 470 471 // bracket translation 472 case '[': 473 case '{': 474 _PressKey("("); 475 break; 476 477 case ']': 478 case '}': 479 _PressKey(")"); 480 break; 481 482 default: { 483 // scan the keymap array for match 484 int keys = fRows * fColums; 485 for (int i = 0; i < keys; i++) { 486 if (fKeypad[i].keymap[0] == bytes[0]) { 487 _PressKey(i); 488 return; 489 } 490 } 491 break; 492 } 493 } 494 } 495 } 496 497 498 void 499 CalcView::MakeFocus(bool focused) 500 { 501 if (focused) { 502 // set num lock 503 if (fOptions->auto_num_lock) { 504 set_keyboard_locks(B_NUM_LOCK | 505 (modifiers() & (B_CAPS_LOCK | B_SCROLL_LOCK))); 506 } 507 } 508 509 // pass on request to text view 510 fExpressionTextView->MakeFocus(focused); 511 } 512 513 514 void 515 CalcView::FrameResized(float width, float height) 516 { 517 fWidth = width; 518 fHeight = height; 519 520 // layout expression text view 521 BRect frame = _ExpressionRect(); 522 frame.InsetBy(2, 2); 523 524 if (!fShowKeypad) 525 frame.right -= ceilf(fCalcIcon->Bounds().Width() * 1.5); 526 527 fExpressionTextView->MoveTo(frame.LeftTop()); 528 fExpressionTextView->ResizeTo(frame.Width(), frame.Height()); 529 530 frame.OffsetTo(B_ORIGIN); 531 frame.InsetBy(2, 2); 532 fExpressionTextView->SetTextRect(frame); 533 534 // configure expression text view font size and color 535 float sizeDisp = fShowKeypad ? fHeight * K_DISPLAY_YPROP : fHeight; 536 BFont font(be_bold_font); 537 font.SetSize(sizeDisp * K_FONT_YPROP); 538 // fExpressionTextView->SetViewColor(fExpressionBGColor); 539 // fExpressionTextView->SetLowColor(fExpressionBGColor); 540 // fExpressionTextView->SetFontAndColor(&font, B_FONT_ALL, &fExpressionTextColor); 541 fExpressionTextView->SetFontAndColor(&font, B_FONT_ALL); 542 } 543 544 545 void 546 CalcView::AboutRequested() 547 { 548 BAlert* alert = new BAlert("about", 549 "DeskCalc v2.1.0\n\n" 550 "written by Timothy Wayper,\nStephan Aßmus and Ingo Weinhold\n\n" 551 B_UTF8_COPYRIGHT"1997, 1998 R3 Software Ltd.\n" 552 B_UTF8_COPYRIGHT"2006 Haiku, Inc.\n\n" 553 "All Rights Reserved.", "Cool"); 554 alert->Go(NULL); 555 } 556 557 558 status_t 559 CalcView::Archive(BMessage* archive, bool deep) const 560 { 561 fExpressionTextView->RemoveSelf(); 562 563 // passed on request to parent 564 status_t ret = BView::Archive(archive, deep); 565 566 const_cast<CalcView*>(this)->AddChild(fExpressionTextView); 567 568 // save app signature for replicant add-on loading 569 if (ret == B_OK) 570 ret = archive->AddString("add_on", kAppSig); 571 572 // save all the options 573 if (ret == B_OK) 574 ret = SaveSettings(archive); 575 576 // add class info last 577 if (ret == B_OK) 578 ret = archive->AddString("class", "CalcView"); 579 580 return ret; 581 } 582 583 584 void 585 CalcView::Cut() 586 { 587 Copy(); // copy data to clipboard 588 fExpressionTextView->Clear(); // remove data 589 } 590 591 592 void 593 CalcView::Copy() 594 { 595 // access system clipboard 596 if (be_clipboard->Lock()) { 597 be_clipboard->Clear(); 598 BMessage *clipper = be_clipboard->Data(); 599 clipper->what = B_MIME_DATA; 600 // TODO: should check return for errors! 601 BString expression = fExpressionTextView->Text(); 602 clipper->AddData("text/plain", 603 B_MIME_TYPE, 604 expression.String(), 605 expression.Length()); 606 //clipper->PrintToStream(); 607 be_clipboard->Commit(); 608 be_clipboard->Unlock(); 609 } 610 } 611 612 613 void 614 CalcView::Paste(BMessage *message) 615 { 616 // handle color drops first 617 // read incoming color 618 const rgb_color* dropColor = NULL; 619 ssize_t dataSize; 620 if (message->FindData("RGBColor", 621 B_RGB_COLOR_TYPE, 622 (const void**)&dropColor, 623 &dataSize) == B_OK 624 && dataSize == sizeof(rgb_color)) { 625 626 // calculate view relative drop point 627 BPoint dropPoint = ConvertFromScreen(message->DropPoint()); 628 629 // calculate current keypad area 630 float sizeDisp = fHeight * K_DISPLAY_YPROP; 631 BRect keypadRect(0.0, sizeDisp, fWidth, fHeight); 632 633 // check location of color drop 634 if (keypadRect.Contains(dropPoint) && dropColor) { 635 fBaseColor = *dropColor; 636 _Colorize(); 637 // redraw keypad 638 Invalidate(keypadRect); 639 } 640 641 } else { 642 // look for text/plain MIME data 643 const char* text; 644 ssize_t numBytes; 645 if (message->FindData("text/plain", 646 B_MIME_TYPE, 647 (const void**)&text, 648 &numBytes) == B_OK) { 649 BString temp; 650 temp.Append(text, numBytes); 651 fExpressionTextView->Insert(temp.String()); 652 } 653 } 654 } 655 656 657 status_t 658 CalcView::LoadSettings(BMessage* archive) 659 { 660 if (!archive) 661 return B_BAD_VALUE; 662 663 // record calculator description 664 const char* calcDesc; 665 if (archive->FindString("calcDesc", &calcDesc) < B_OK) 666 calcDesc = kDefaultKeypadDescription; 667 668 // save calculator description for reference 669 free(fKeypadDescription); 670 fKeypadDescription = strdup(calcDesc); 671 672 // read grid dimensions 673 if (archive->FindInt16("cols", &fColums) < B_OK) 674 fColums = 5; 675 if (archive->FindInt16("rows", &fRows) < B_OK) 676 fRows = 4; 677 678 // read color scheme 679 const rgb_color* color; 680 ssize_t size; 681 if (archive->FindData("rgbBaseColor", B_RGB_COLOR_TYPE, 682 (const void**)&color, &size) < B_OK 683 || size != sizeof(rgb_color)) { 684 fBaseColor = (rgb_color){ 128, 128, 128, 255 }; 685 puts("Missing rgbBaseColor from CalcView archive!\n"); 686 } else { 687 fBaseColor = *color; 688 } 689 690 if (archive->FindData("rgbDisplay", B_RGB_COLOR_TYPE, 691 (const void**)&color, &size) < B_OK 692 || size != sizeof(rgb_color)) { 693 fExpressionBGColor = (rgb_color){ 0, 0, 0, 255 }; 694 puts("Missing rgbBaseColor from CalcView archive!\n"); 695 } else { 696 fExpressionBGColor = *color; 697 } 698 699 // load options 700 fOptions->LoadSettings(archive); 701 fShowKeypad = fOptions->show_keypad; 702 703 // load option window frame 704 BRect frame; 705 if (archive->FindRect("option window frame", &frame) == B_OK) 706 fOptionsWindowFrame = frame; 707 708 // load display text 709 const char* display; 710 if (archive->FindString("displayText", &display) < B_OK) { 711 puts("Missing expression text from CalcView archive.\n"); 712 } else { 713 // init expression text 714 fExpressionTextView->SetText(display); 715 } 716 717 // load expression history 718 fExpressionTextView->LoadSettings(archive); 719 720 // parse calculator description 721 _ParseCalcDesc(fKeypadDescription); 722 723 // colorize based on base color. 724 _Colorize(); 725 726 return B_OK; 727 } 728 729 730 status_t 731 CalcView::SaveSettings(BMessage* archive) const 732 { 733 status_t ret = archive ? B_OK : B_BAD_VALUE; 734 735 // record grid dimensions 736 if (ret == B_OK) 737 ret = archive->AddInt16("cols", fColums); 738 if (ret == B_OK) 739 ret = archive->AddInt16("rows", fRows); 740 741 // record color scheme 742 if (ret == B_OK) 743 ret = archive->AddData("rgbBaseColor", B_RGB_COLOR_TYPE, 744 &fBaseColor, sizeof(rgb_color)); 745 if (ret == B_OK) 746 ret = archive->AddData("rgbDisplay", B_RGB_COLOR_TYPE, 747 &fExpressionBGColor, sizeof(rgb_color)); 748 749 // record current options 750 if (ret == B_OK) 751 ret = fOptions->SaveSettings(archive); 752 753 // record option window frame 754 if (ret == B_OK) 755 ret = archive->AddRect("option window frame", fOptionsWindowFrame); 756 757 // record display text 758 if (ret == B_OK) 759 ret = archive->AddString("displayText", fExpressionTextView->Text()); 760 761 // record expression history 762 if (ret == B_OK) 763 ret = fExpressionTextView->SaveSettings(archive); 764 765 // record calculator description 766 if (ret == B_OK) 767 ret = archive->AddString("calcDesc", fKeypadDescription); 768 769 return ret; 770 } 771 772 773 void 774 CalcView::Evaluate() 775 { 776 const double EXP_SWITCH_HI = 1e12; // # digits to switch from std->exp form 777 const double EXP_SWITCH_LO = 1e-12; 778 779 BString expression = fExpressionTextView->Text(); 780 781 if (expression.Length() == 0) { 782 beep(); 783 return; 784 } 785 786 // audio feedback 787 if (fOptions->audio_feedback) { 788 BEntry zimp("zimp.AIFF"); 789 entry_ref zimp_ref; 790 zimp.GetRef(&zimp_ref); 791 play_sound(&zimp_ref, true, false, false); 792 } 793 794 //printf("evaluate: %s\n", expression.String()); 795 796 // evaluate expression 797 double value = 0.0; 798 799 try { 800 ExpressionParser parser; 801 value = parser.Evaluate(expression.String()); 802 } catch (ParseException e) { 803 BString error(e.message.String()); 804 error << " at " << (e.position + 1); 805 fExpressionTextView->SetText(error.String()); 806 return; 807 } 808 809 //printf(" -> value: %f\n", value); 810 811 // beautify the expression 812 // TODO: see if this is necessary at all 813 char buf[64]; 814 if (value == 0) { 815 strcpy(buf, "0"); 816 } else if (((value < EXP_SWITCH_HI) && (value > EXP_SWITCH_LO)) || 817 ((value > -EXP_SWITCH_HI) && (value < -EXP_SWITCH_LO))) { 818 // print in std form 819 sprintf(buf, "%.13f", value); 820 821 // hack to remove surplus zeros! 822 if (strchr(buf, '.')) { 823 int32 i = strlen(buf) - 1; 824 for (; i > 0; i--) { 825 if (buf[i] == '0') 826 buf[i] = '\0'; 827 else 828 break; 829 } 830 if (buf[i] == '.') 831 buf[i] = '\0'; 832 } 833 } else { 834 // print in exponential form 835 sprintf(buf, "%e", value); 836 } 837 838 // render new result to display 839 fExpressionTextView->SetExpression(buf); 840 } 841 842 843 void 844 CalcView::FlashKey(const char* bytes, int32 numBytes) 845 { 846 BString temp; 847 temp.Append(bytes, numBytes); 848 int32 key = _KeyForLabel(temp.String()); 849 if (key >= 0) 850 _FlashKey(key); 851 } 852 853 854 // #pragma mark - 855 856 857 void 858 CalcView::_ParseCalcDesc(const char* keypadDescription) 859 { 860 // TODO: should calculate dimensions from desc here! 861 fKeypad = new CalcKey[fRows * fColums]; 862 863 // scan through calculator description and assemble keypad 864 CalcKey *key = fKeypad; 865 const char *p = keypadDescription; 866 867 while (*p != 0) { 868 // copy label 869 char *l = key->label; 870 while (!isspace(*p)) 871 *l++ = *p++; 872 *l = '\0'; 873 874 // set code 875 if (strcmp(key->label, "=") == 0) 876 strcpy(key->code, "\n"); 877 else 878 strcpy(key->code, key->label); 879 880 // set keymap 881 if (strlen(key->label) == 1) { 882 strcpy(key->keymap, key->label); 883 } else { 884 *key->keymap = '\0'; 885 } // end if 886 887 // add this to the expression text view, so that it 888 // will forward the respective KeyDown event to us 889 fExpressionTextView->AddKeypadLabel(key->label); 890 891 // advance 892 while (isspace(*p)) 893 ++p; 894 key++; 895 } 896 } 897 898 899 void 900 CalcView::_PressKey(int key) 901 { 902 assert(key < (fRows * fColums)); 903 assert(key >= 0); 904 905 // check for backspace 906 if (strcmp(fKeypad[key].label, "BS") == 0) { 907 fExpressionTextView->BackSpace(); 908 } else if (strcmp(fKeypad[key].label, "C") == 0) { 909 // C means clear 910 fExpressionTextView->Clear(); 911 } else { 912 // check for evaluation order 913 if (fKeypad[key].code[0] == '\n') { 914 Evaluate(); 915 } else { 916 // insert into expression text 917 fExpressionTextView->Insert(fKeypad[key].code); 918 919 // audio feedback 920 if (fOptions->audio_feedback) { 921 BEntry zimp("key.AIFF"); 922 entry_ref zimp_ref; 923 zimp.GetRef(&zimp_ref); 924 play_sound(&zimp_ref, true, false, true); 925 } 926 } 927 } 928 929 // redraw display 930 // _InvalidateExpression(); 931 } 932 933 934 void 935 CalcView::_PressKey(const char *label) 936 { 937 int32 key = _KeyForLabel(label); 938 if (key >= 0) 939 _PressKey(key); 940 } 941 942 943 int32 944 CalcView::_KeyForLabel(const char *label) const 945 { 946 int keys = fRows * fColums; 947 for (int i = 0; i < keys; i++) { 948 if (strcmp(fKeypad[i].label, label) == 0) { 949 return i; 950 } 951 } 952 return -1; 953 } 954 955 956 void 957 CalcView::_FlashKey(int32 key) 958 { 959 // TODO ... 960 } 961 962 963 void 964 CalcView::_Colorize() 965 { 966 // calculate light and dark color from base color 967 fLightColor.red = (uint8)(fBaseColor.red * 1.25); 968 fLightColor.green = (uint8)(fBaseColor.green * 1.25); 969 fLightColor.blue = (uint8)(fBaseColor.blue * 1.25); 970 fLightColor.alpha = 255; 971 972 fDarkColor.red = (uint8)(fBaseColor.red * 0.75); 973 fDarkColor.green = (uint8)(fBaseColor.green * 0.75); 974 fDarkColor.blue = (uint8)(fBaseColor.blue * 0.75); 975 fDarkColor.alpha = 255; 976 977 // keypad text color 978 uint8 lightness = (fBaseColor.red + fBaseColor.green + fBaseColor.blue) / 3; 979 if (lightness > 200) 980 fButtonTextColor = (rgb_color){ 0, 0, 0, 255 }; 981 else 982 fButtonTextColor = (rgb_color){ 255, 255, 255, 255 }; 983 984 // expression text color 985 lightness = (fExpressionBGColor.red + 986 fExpressionBGColor.green + fExpressionBGColor.blue) / 3; 987 if (lightness > 200) 988 fExpressionTextColor = (rgb_color){ 0, 0, 0, 255 }; 989 else 990 fExpressionTextColor = (rgb_color){ 255, 255, 255, 255 }; 991 } 992 993 994 void 995 CalcView::_CreatePopUpMenu() 996 { 997 // construct items 998 fAboutItem = new BMenuItem("About Calculator" B_UTF8_ELLIPSIS, 999 new BMessage(B_ABOUT_REQUESTED)); 1000 fOptionsItem = new BMenuItem("Options" B_UTF8_ELLIPSIS, 1001 new BMessage(K_OPTIONS_REQUESTED)); 1002 1003 // construct menu 1004 fPopUpMenu = new BPopUpMenu("pop-up", false, false); 1005 fPopUpMenu->AddItem(fAboutItem); 1006 fPopUpMenu->AddItem(fOptionsItem); 1007 } 1008 1009 1010 BRect 1011 CalcView::_ExpressionRect() const 1012 { 1013 BRect r(0.0, 0.0, fWidth, fHeight); 1014 if (fShowKeypad) { 1015 r.bottom = floorf(fHeight * K_DISPLAY_YPROP); 1016 } 1017 return r; 1018 } 1019 1020 1021 BRect 1022 CalcView::_KeypadRect() const 1023 { 1024 BRect r(0.0, 0.0, -1.0, -1.0); 1025 if (fShowKeypad) { 1026 r.right = fWidth; 1027 r.bottom = fHeight; 1028 r.top = floorf(fHeight * K_DISPLAY_YPROP) + 1; 1029 } 1030 return r; 1031 } 1032 1033 1034 void 1035 CalcView::_ShowKeypad(bool show) 1036 { 1037 if (fShowKeypad == show) 1038 return; 1039 1040 fShowKeypad = show; 1041 1042 float height = fShowKeypad ? fHeight / K_DISPLAY_YPROP 1043 : fHeight * K_DISPLAY_YPROP; 1044 1045 BWindow* window = Window(); 1046 if (window->Bounds() == Frame()) 1047 window->ResizeTo(fWidth, height); 1048 else 1049 ResizeTo(fWidth, height); 1050 } 1051 1052 1053 void 1054 CalcView::_FetchAppIcon(BBitmap* into) 1055 { 1056 app_info info; 1057 be_roster->GetAppInfo(kAppSig, &info); 1058 BFile file(&info.ref, B_READ_ONLY); 1059 BAppFileInfo appInfo(&file); 1060 if (appInfo.GetIcon(into, B_MINI_ICON) < B_OK) 1061 memset(into->Bits(), 0, into->BitsLength()); 1062 } 1063