1 /* 2 * Copyright 2002-2009, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Updated by Sikosis (beos@gravity24hr.com) 6 * 7 * Copyright 1999, Be Incorporated. All Rights Reserved. 8 * This file may be used under the terms of the Be Sample Code License. 9 */ 10 11 #include "Magnify.h" 12 13 #include <Alert.h> 14 #include <Bitmap.h> 15 #include <BitmapStream.h> 16 #include <Catalog.h> 17 #include <Clipboard.h> 18 #include <Debug.h> 19 #include <Directory.h> 20 #include <File.h> 21 #include <FindDirectory.h> 22 #include <Locale.h> 23 #include <MenuItem.h> 24 #include <MenuField.h> 25 #include <MessageFormat.h> 26 #include <NodeInfo.h> 27 #include <Path.h> 28 #include <PopUpMenu.h> 29 #include <Screen.h> 30 #include <ScrollView.h> 31 #include <TextView.h> 32 #include <TranslationUtils.h> 33 #include <TranslatorRoster.h> 34 #include <WindowScreen.h> 35 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <fcntl.h> 40 #include <unistd.h> 41 #include <sys/stat.h> 42 43 44 #undef B_TRANSLATION_CONTEXT 45 #define B_TRANSLATION_CONTEXT "Magnify-Main" 46 47 48 const int32 msg_update_info = 'info'; 49 const int32 msg_show_info = 'show'; 50 const int32 msg_toggle_grid = 'grid'; 51 const int32 msg_shrink = 'shnk'; 52 const int32 msg_grow = 'grow'; 53 const int32 msg_make_square = 'sqar'; 54 const int32 msg_shrink_pixel = 'pshk'; 55 const int32 msg_grow_pixel = 'pgrw'; 56 57 const int32 msg_new_color = 'colr'; 58 const int32 msg_toggle_ruler = 'rulr'; 59 const int32 msg_copy_image = 'copy'; 60 const int32 msg_track_color = 'trak'; 61 const int32 msg_freeze = 'frez'; 62 const int32 msg_stick = 'stic'; 63 const int32 msg_dump = 'dump'; 64 const int32 msg_add_cross_hair = 'acrs'; 65 const int32 msg_remove_cross_hair = 'rcrs'; 66 const int32 msg_save = 'save'; 67 68 const rgb_color kViewGray = { 216, 216, 216, 255}; 69 const rgb_color kGridGray = {130, 130, 130, 255 }; 70 const rgb_color kWhite = { 255, 255, 255, 255}; 71 const rgb_color kBlack = { 0, 0, 0, 255}; 72 const rgb_color kDarkGray = { 96, 96, 96, 255}; 73 const rgb_color kRedColor = { 255, 10, 50, 255 }; 74 const rgb_color kGreenColor = { 10, 255, 50, 255 }; 75 const rgb_color kBlueColor = { 10, 50, 255, 255 }; 76 77 const char* const kBitmapMimeType = "image/x-vnd.Be-bitmap"; 78 79 const float kCurrentVersion = 1.2; 80 const char *kPrefsFileName = "Magnify_prefs"; 81 82 // prefs are: 83 // name = Magnify 84 // version 85 // show grid 86 // show info (rgb, location) 87 // pixel count 88 // pixel size 89 const char* const kAppName = "Magnify"; 90 const bool kDefaultShowGrid = true; 91 const bool kDefaultShowInfo = true; 92 const int32 kDefaultPixelCount = 32; 93 const int32 kDefaultPixelSize = 8; 94 95 // each info region will be: 96 // top-bottom: 5 fontheight 5 fontheight 5 97 // left-right: 10 minwindowwidth 10 98 const int32 kBorderSize = 10; 99 100 101 static float 102 FontHeight(BView* target, bool full) 103 { 104 font_height finfo; 105 target->GetFontHeight(&finfo); 106 float h = ceil(finfo.ascent) + ceil(finfo.descent); 107 108 if (full) 109 h += ceil(finfo.leading); 110 111 return h; 112 } 113 114 115 static void 116 BoundsSelection(int32 incX, int32 incY, float* x, float* y, 117 int32 xCount, int32 yCount) 118 { 119 *x += incX; 120 *y += incY; 121 122 if (*x < 0) 123 *x = xCount-1; 124 if (*x >= xCount) 125 *x = 0; 126 127 if (*y < 0) 128 *y = yCount-1; 129 if (*y >= yCount) 130 *y = 0; 131 } 132 133 134 static void 135 BuildInfoMenu(BMenu *menu) 136 { 137 BMenuItem* menuItem; 138 menuItem = new BMenuItem(B_TRANSLATE("Save image"), 139 new BMessage(msg_save), 'S'); 140 menu->AddItem(menuItem); 141 // menuItem = new BMenuItem(B_TRANSLATE("Save selection"), 142 // new BMessage(msg_save), 'S'); 143 // menu->AddItem(menuItem); 144 menuItem = new BMenuItem(B_TRANSLATE("Copy image"), 145 new BMessage(msg_copy_image), 'C'); 146 menu->AddItem(menuItem); 147 menu->AddSeparatorItem(); 148 149 menuItem = new BMenuItem(B_TRANSLATE("Hide/Show info"), 150 new BMessage(msg_show_info), 'T'); 151 menu->AddItem(menuItem); 152 menuItem = new BMenuItem(B_TRANSLATE("Add a crosshair"), 153 new BMessage(msg_add_cross_hair), 'H'); 154 menu->AddItem(menuItem); 155 menuItem = new BMenuItem(B_TRANSLATE("Remove a crosshair"), 156 new BMessage(msg_remove_cross_hair), 'H', B_SHIFT_KEY); 157 menu->AddItem(menuItem); 158 menuItem = new BMenuItem(B_TRANSLATE("Hide/Show grid"), 159 new BMessage(msg_toggle_grid), 'G'); 160 menu->AddItem(menuItem); 161 menu->AddSeparatorItem(); 162 163 menuItem = new BMenuItem(B_TRANSLATE("Freeze/Unfreeze image"), 164 new BMessage(msg_freeze), 'F'); 165 menu->AddItem(menuItem); 166 menuItem = new BMenuItem(B_TRANSLATE("Stick coordinates"), 167 new BMessage(msg_stick), 'I'); 168 menu->AddItem(menuItem); 169 menu->AddSeparatorItem(); 170 171 menuItem = new BMenuItem(B_TRANSLATE("Make square"), 172 new BMessage(msg_make_square), '/'); 173 menu->AddItem(menuItem); 174 menuItem = new BMenuItem(B_TRANSLATE("Decrease window size"), 175 new BMessage(msg_shrink), '-'); 176 menu->AddItem(menuItem); 177 menuItem = new BMenuItem(B_TRANSLATE("Increase window size"), 178 new BMessage(msg_grow), '+'); 179 menu->AddItem(menuItem); 180 menuItem = new BMenuItem(B_TRANSLATE("Decrease pixel size"), 181 new BMessage(msg_shrink_pixel), ','); 182 menu->AddItem(menuItem); 183 menuItem = new BMenuItem(B_TRANSLATE("Increase pixel size"), 184 new BMessage(msg_grow_pixel), '.'); 185 menu->AddItem(menuItem); 186 } 187 188 189 // #pragma mark - 190 191 192 // pass in pixelCount to maintain backward compatibility of setting 193 // the pixelcount from the command line 194 TApp::TApp(int32 pixelCount) 195 : BApplication("application/x-vnd.Haiku-Magnify") 196 { 197 TWindow* magWindow = new TWindow(pixelCount); 198 magWindow->Show(); 199 } 200 201 202 // #pragma mark - 203 204 205 TWindow::TWindow(int32 pixelCount) 206 : 207 BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Magnify"), 208 B_TITLED_WINDOW, B_OUTLINE_RESIZE) 209 { 210 GetPrefs(pixelCount); 211 212 // add info view 213 BRect infoRect(Bounds()); 214 infoRect.InsetBy(-1, -1); 215 fInfo = new TInfoView(infoRect); 216 AddChild(fInfo); 217 218 fFontHeight = FontHeight(fInfo, true); 219 fInfoHeight = (fFontHeight * 2) + (3 * 5); 220 221 BRect fbRect(0, 0, (fHPixelCount*fPixelSize), (fHPixelCount*fPixelSize)); 222 if (InfoIsShowing()) 223 fbRect.OffsetBy(10, fInfoHeight); 224 fFatBits = new TMagnify(fbRect, this); 225 fInfo->AddChild(fFatBits); 226 227 fFatBits->SetSelection(fShowInfo); 228 fInfo->SetMagView(fFatBits); 229 230 ResizeWindow(fHPixelCount, fVPixelCount); 231 UpdateInfoBarOnResize(); 232 233 AddShortcut('S', B_COMMAND_KEY, new BMessage(msg_save)); 234 AddShortcut('C', B_COMMAND_KEY, new BMessage(msg_copy_image)); 235 AddShortcut('T', B_COMMAND_KEY, new BMessage(msg_show_info)); 236 AddShortcut('H', B_COMMAND_KEY, new BMessage(msg_add_cross_hair)); 237 AddShortcut('H', B_SHIFT_KEY, new BMessage(msg_remove_cross_hair)); 238 AddShortcut('G', B_COMMAND_KEY, new BMessage(msg_toggle_grid)); 239 AddShortcut('F', B_COMMAND_KEY, new BMessage(msg_freeze)); 240 AddShortcut('I', B_COMMAND_KEY, new BMessage(msg_stick)); 241 AddShortcut('-', B_COMMAND_KEY, new BMessage(msg_shrink)); 242 AddShortcut('=', B_COMMAND_KEY, new BMessage(msg_grow)); 243 AddShortcut('/', B_COMMAND_KEY, new BMessage(msg_make_square)); 244 AddShortcut(',', B_COMMAND_KEY, new BMessage(msg_shrink_pixel)); 245 AddShortcut('.', B_COMMAND_KEY, new BMessage(msg_grow_pixel)); 246 } 247 248 249 TWindow::~TWindow() 250 { 251 } 252 253 254 void 255 TWindow::MessageReceived(BMessage* m) 256 { 257 bool active = fFatBits->Active(); 258 259 switch (m->what) { 260 case msg_show_info: 261 if (active) { 262 fInfoBarState = !fInfoBarState; 263 ShowInfo(!fShowInfo); 264 } 265 break; 266 267 case msg_toggle_grid: 268 if (active) 269 SetGrid(!fShowGrid); 270 break; 271 272 case msg_grow: 273 if (active) 274 ResizeWindow(true); 275 break; 276 case msg_shrink: 277 if (active) 278 ResizeWindow(false); 279 break; 280 case msg_make_square: 281 if (active) { 282 if (fHPixelCount == fVPixelCount) 283 break; 284 int32 big = (fHPixelCount > fVPixelCount) ? fHPixelCount : fVPixelCount; 285 ResizeWindow(big, big); 286 } 287 break; 288 289 case msg_shrink_pixel: 290 if (active) 291 SetPixelSize(false); 292 break; 293 case msg_grow_pixel: 294 if (active) 295 SetPixelSize(true); 296 break; 297 298 case msg_add_cross_hair: 299 if (active && fShowInfo) 300 AddCrossHair(); 301 break; 302 case msg_remove_cross_hair: 303 if (active && fShowInfo) 304 RemoveCrossHair(); 305 break; 306 307 case msg_freeze: 308 if (active) 309 SetFlags(B_OUTLINE_RESIZE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE); 310 else 311 SetFlags(B_OUTLINE_RESIZE | B_NOT_ZOOMABLE); 312 313 fFatBits->MakeActive(!fFatBits->Active()); 314 break; 315 316 case msg_stick: 317 fFatBits->MakeSticked(!fFatBits->Sticked()); 318 break; 319 320 case msg_save: { 321 // freeze the image here, unfreeze after dump or cancel 322 fFatBits->StartSave(); 323 324 BMessenger messenger(this); 325 BMessage message(msg_dump); 326 fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, 0, 0, false, 327 &message); 328 fSavePanel->SetSaveText("Bitmaps.png"); 329 fSavePanel->Show(); 330 } break; 331 case msg_dump: 332 { 333 delete fSavePanel; 334 335 entry_ref dirRef; 336 char* name; 337 m->FindRef("directory", &dirRef); 338 m->FindString((const char*)"name",(const char**) &name); 339 340 fFatBits->SaveImage(&dirRef, name); 341 } 342 break; 343 case B_CANCEL: 344 // image is frozen before the FilePanel is shown 345 fFatBits->EndSave(); 346 break; 347 348 case msg_copy_image: 349 fFatBits->CopyImage(); 350 break; 351 default: 352 BWindow::MessageReceived(m); 353 break; 354 } 355 } 356 357 358 bool 359 TWindow::QuitRequested() 360 { 361 SetPrefs(); 362 be_app->PostMessage(B_QUIT_REQUESTED); 363 return true; 364 } 365 366 367 void 368 TWindow::GetPrefs(int32 overridePixelCount) 369 { 370 BPath path; 371 char name[8]; 372 float version; 373 bool haveLoc=false; 374 BPoint loc; 375 bool showGrid = kDefaultShowGrid; 376 bool showInfo = kDefaultShowInfo; 377 bool ch1Showing=false; 378 bool ch2Showing=false; 379 int32 hPixelCount = kDefaultPixelCount; 380 int32 vPixelCount = kDefaultPixelCount; 381 int32 pixelSize = kDefaultPixelSize; 382 383 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 384 int ref = -1; 385 path.Append(kPrefsFileName); 386 if ((ref = open(path.Path(), 0)) >= 0) { 387 if (read(ref, name, 7) != 7) 388 goto ALMOST_DONE; 389 390 name[7] = 0; 391 if (strcmp(name, kAppName) != 0) 392 goto ALMOST_DONE; 393 394 read(ref, &version, sizeof(float)); 395 396 if (read(ref, &loc, sizeof(BPoint)) != sizeof(BPoint)) 397 goto ALMOST_DONE; 398 else 399 haveLoc = true; 400 401 if (read(ref, &showGrid, sizeof(bool)) != sizeof(bool)) { 402 showGrid = kDefaultShowGrid; 403 goto ALMOST_DONE; 404 } 405 406 if (read(ref, &showInfo, sizeof(bool)) != sizeof(bool)) { 407 showInfo = kDefaultShowInfo; 408 goto ALMOST_DONE; 409 } 410 411 if (read(ref, &ch1Showing, sizeof(bool)) != sizeof(bool)) { 412 ch1Showing = false; 413 goto ALMOST_DONE; 414 } 415 416 if (read(ref, &ch2Showing, sizeof(bool)) != sizeof(bool)) { 417 ch2Showing = false; 418 goto ALMOST_DONE; 419 } 420 421 if (read(ref, &hPixelCount, sizeof(int32)) != sizeof(int32)) { 422 hPixelCount = kDefaultPixelCount; 423 goto ALMOST_DONE; 424 } 425 if (read(ref, &vPixelCount, sizeof(int32)) != sizeof(int32)) { 426 vPixelCount = kDefaultPixelCount; 427 goto ALMOST_DONE; 428 } 429 430 if (read(ref, &pixelSize, sizeof(int32)) != sizeof(int32)) { 431 pixelSize = kDefaultPixelSize; 432 goto ALMOST_DONE; 433 } 434 435 ALMOST_DONE: // clean up and try to position the window 436 close(ref); 437 438 if (haveLoc && BScreen(B_MAIN_SCREEN_ID).Frame().Contains(loc)) { 439 MoveTo(loc); 440 goto DONE; 441 } 442 } 443 } 444 445 // if prefs dont yet exist or the window is not onscreen, center the window 446 CenterOnScreen(); 447 448 // set all the settings to defaults if we get here 449 DONE: 450 fShowGrid = showGrid; 451 fShowInfo = showInfo; 452 fInfoBarState = showInfo; 453 fHPixelCount = (overridePixelCount == -1) ? hPixelCount : overridePixelCount; 454 fVPixelCount = (overridePixelCount == -1) ? vPixelCount : overridePixelCount; 455 fPixelSize = pixelSize; 456 } 457 458 459 void 460 TWindow::SetPrefs() 461 { 462 BPath path; 463 464 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) == B_OK) { 465 long ref; 466 467 path.Append (kPrefsFileName); 468 if ((ref = creat(path.Path(), S_IRUSR | S_IWUSR)) >= 0) { 469 float version = kCurrentVersion; 470 471 lseek (ref, 0, SEEK_SET); 472 write(ref, kAppName, 7); 473 write(ref, &version, sizeof(float)); 474 475 BPoint loc = Frame().LeftTop(); 476 write(ref, &loc, sizeof(BPoint)); 477 478 write(ref, &fShowGrid, sizeof(bool)); 479 write(ref, &fShowInfo, sizeof(bool)); 480 bool ch1, ch2; 481 CrossHairsShowing(&ch1, &ch2); 482 write(ref, &ch1, sizeof(bool)); 483 write(ref, &ch2, sizeof(bool)); 484 485 write(ref, &fHPixelCount, sizeof(int32)); 486 write(ref, &fVPixelCount, sizeof(int32)); 487 write(ref, &fPixelSize, sizeof(int32)); 488 489 close(ref); 490 } 491 } 492 } 493 494 495 void 496 TWindow::FrameResized(float w, float h) 497 { 498 CalcViewablePixels(); 499 fFatBits->InitBuffers(fHPixelCount, fVPixelCount, fPixelSize, ShowGrid()); 500 UpdateInfoBarOnResize(); 501 } 502 503 504 void 505 TWindow::ScreenChanged(BRect screenSize, color_space depth) 506 { 507 BWindow::ScreenChanged(screenSize, depth); 508 // reset all bitmaps 509 fFatBits->ScreenChanged(screenSize,depth); 510 } 511 512 513 void 514 TWindow::Minimize(bool m) 515 { 516 BWindow::Minimize(m); 517 } 518 519 520 void 521 TWindow::Zoom(BPoint /*position*/, float /*width*/, float /*height*/) 522 { 523 if (fFatBits->Active()) 524 ShowInfo(!fShowInfo); 525 } 526 527 528 void 529 TWindow::CalcViewablePixels() 530 { 531 float w = Bounds().Width(); 532 float h = Bounds().Height(); 533 534 if (InfoIsShowing()) { 535 w -= 20; // remove the gutter 536 h = h-fInfoHeight-10; // remove info and gutter 537 } 538 539 bool ch1, ch2; 540 fFatBits->CrossHairsShowing(&ch1, &ch2); 541 if (ch1) 542 h -= fFontHeight; 543 if (ch2) 544 h -= fFontHeight + 5; 545 546 fHPixelCount = (int32)w / fPixelSize; // calc h pixels 547 if (fHPixelCount < 16) 548 fHPixelCount = 16; 549 550 fVPixelCount = (int32)h / fPixelSize; // calc v pixels 551 if (fVPixelCount < 4) 552 fVPixelCount = 4; 553 } 554 555 556 void 557 TWindow::GetPreferredSize(float* width, float* height) 558 { 559 *width = fHPixelCount * fPixelSize; // calc window width 560 *height = fVPixelCount * fPixelSize; // calc window height 561 if (InfoIsShowing()) { 562 *width += 20; 563 *height += fInfoHeight + 10; 564 } 565 566 bool ch1, ch2; 567 fFatBits->CrossHairsShowing(&ch1, &ch2); 568 if (ch1) 569 *height += fFontHeight; 570 if (ch2) 571 *height += fFontHeight + 5; 572 } 573 574 575 void 576 TWindow::ResizeWindow(int32 hPixelCount, int32 vPixelCount) 577 { 578 fHPixelCount = hPixelCount; 579 fVPixelCount = vPixelCount; 580 581 float width, height; 582 GetPreferredSize(&width, &height); 583 584 ResizeTo(width, height); 585 } 586 587 588 void 589 TWindow::ResizeWindow(bool direction) 590 { 591 int32 x = fHPixelCount; 592 int32 y = fVPixelCount; 593 594 if (direction) { 595 x += 4; 596 y += 4; 597 } else { 598 x -= 4; 599 y -= 4; 600 } 601 602 if (x < 4) 603 x = 4; 604 605 if (y < 4) 606 y = 4; 607 608 ResizeWindow(x, y); 609 } 610 611 612 void 613 TWindow::SetGrid(bool s) 614 { 615 if (s == fShowGrid) 616 return; 617 618 fShowGrid = s; 619 fFatBits->SetUpdate(true); 620 } 621 622 623 bool 624 TWindow::ShowGrid() 625 { 626 return fShowGrid; 627 } 628 629 630 void 631 TWindow::ShowInfo(bool i) 632 { 633 if (i == fShowInfo) 634 return; 635 636 fShowInfo = i; 637 638 if (fShowInfo) 639 fFatBits->MoveTo(10, fInfoHeight); 640 else { 641 fFatBits->MoveTo(1,1); 642 fFatBits->SetCrossHairsShowing(false, false); 643 } 644 645 fFatBits->SetSelection(fShowInfo); 646 ResizeWindow(fHPixelCount, fVPixelCount); 647 fInfo->SetInfoTextVisible(i); 648 } 649 650 651 bool 652 TWindow::InfoIsShowing() 653 { 654 return fShowInfo; 655 } 656 657 658 void 659 TWindow::UpdateInfo() 660 { 661 fInfo->Invalidate(); 662 } 663 664 665 void 666 TWindow::UpdateInfoBarOnResize() 667 { 668 float infoWidth, infoHeight; 669 fInfo->GetPreferredSize(&infoWidth, &infoHeight); 670 671 if (infoWidth > Bounds().Width() 672 || infoHeight > Bounds().Height()) { 673 ShowInfo(false); 674 } else { 675 ShowInfo(true); 676 } 677 } 678 679 680 void 681 TWindow::AddCrossHair() 682 { 683 fFatBits->AddCrossHair(); 684 685 // crosshair info needs to be added 686 // window resizes accordingly 687 float width; 688 float height; 689 GetPreferredSize(&width, &height); 690 ResizeTo(width, height); 691 } 692 693 694 void 695 TWindow::RemoveCrossHair() 696 { 697 fFatBits->RemoveCrossHair(); 698 699 // crosshair info needs to be removed 700 // window resizes accordingly 701 float width; 702 float height; 703 GetPreferredSize(&width, &height); 704 ResizeTo(width, height); 705 } 706 707 708 void 709 TWindow::CrossHairsShowing(bool* ch1, bool* ch2) 710 { 711 fFatBits->CrossHairsShowing(ch1, ch2); 712 } 713 714 715 void 716 TWindow::PixelCount(int32* h, int32 *v) 717 { 718 *h = fHPixelCount; 719 *v = fVPixelCount; 720 } 721 722 723 void 724 TWindow::SetPixelSize(int32 s) 725 { 726 if (s == fPixelSize) 727 return; 728 729 fPixelSize = s; 730 // resize window 731 // tell info that size has changed 732 // tell mag that size has changed 733 734 CalcViewablePixels(); 735 ResizeWindow(fHPixelCount, fVPixelCount); 736 } 737 738 739 void 740 TWindow::SetPixelSize(bool d) 741 { 742 if (d) { // grow 743 fPixelSize++; 744 if (fPixelSize > 16) 745 fPixelSize = 16; 746 } else { 747 fPixelSize--; 748 if (fPixelSize < 1) 749 fPixelSize = 1; 750 } 751 752 float w = Bounds().Width(); 753 float h = Bounds().Height(); 754 CalcViewablePixels(); 755 ResizeWindow(fHPixelCount, fVPixelCount); 756 757 // the window might not actually change in size 758 // in that case force the buffers to the new dimension 759 if (w == Bounds().Width() && h == Bounds().Height()) 760 fFatBits->InitBuffers(fHPixelCount, fVPixelCount, fPixelSize, ShowGrid()); 761 } 762 763 764 int32 765 TWindow::PixelSize() 766 { 767 return fPixelSize; 768 } 769 770 771 #undef B_TRANSLATION_CONTEXT 772 #define B_TRANSLATION_CONTEXT "Magnify-Main" 773 774 775 bool 776 TWindow::IsActive() 777 { 778 return fFatBits->Active(); 779 } 780 781 782 // #pragma mark - 783 784 785 TInfoView::TInfoView(BRect frame) 786 : BBox(frame, "rgb", B_FOLLOW_ALL, 787 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS, 788 B_NO_BORDER) 789 { 790 SetFont(be_plain_font); 791 fFontHeight = FontHeight(this, true); 792 fMagView = NULL; 793 794 fSelectionColor = kBlack; 795 fCH1Loc.x = fCH1Loc.y = fCH2Loc.x = fCH2Loc.y = 0; 796 797 fInfoStr[0] = 0; 798 fRGBStr[0] = 0; 799 fCH1Str[0] = 0; 800 fCH2Str[0] = 0; 801 802 fInfoTextVisible = true; 803 } 804 805 806 TInfoView::~TInfoView() 807 { 808 } 809 810 811 void 812 TInfoView::AttachedToWindow() 813 { 814 BBox::AttachedToWindow(); 815 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 816 dynamic_cast<TWindow*>(Window())->PixelCount(&fHPixelCount, &fVPixelCount); 817 fPixelSize = dynamic_cast<TWindow*>(Window())->PixelSize(); 818 819 AddMenu(); 820 } 821 822 823 void 824 TInfoView::Draw(BRect updateRect) 825 { 826 PushState(); 827 SetLowColor(ViewColor()); 828 829 BRect invalRect; 830 831 int32 hPixelCount, vPixelCount; 832 dynamic_cast<TWindow*>(Window())->PixelCount(&hPixelCount, &vPixelCount); 833 int32 pixelSize = dynamic_cast<TWindow*>(Window())->PixelSize(); 834 835 MovePenTo(15 + fPopUp->Bounds().Width(), fFontHeight + 5); 836 837 static BMessageFormat format(B_TRANSLATE("%width x %height @ {0, plural, " 838 "one{# pixel/pixel} other{# pixels/pixel}}")); 839 840 BString dimensionsInfo; 841 format.Format(dimensionsInfo, pixelSize); 842 843 BString rep; 844 rep << hPixelCount; 845 dimensionsInfo.ReplaceAll("%width", rep); 846 rep = ""; 847 rep << vPixelCount; 848 dimensionsInfo.ReplaceAll("%height", rep); 849 850 invalRect.Set(10, 5, 10 + StringWidth(fInfoStr), fFontHeight+7); 851 SetHighColor(ViewColor()); 852 FillRect(invalRect); 853 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 854 strcpy(fInfoStr, dimensionsInfo); 855 if (fInfoTextVisible) 856 DrawString(fInfoStr); 857 858 rgb_color color = { 0, 0, 0, 255 }; 859 if (fMagView) 860 color = fMagView->SelectionColor(); 861 char str[64]; 862 snprintf(str, sizeof(str), "R: %i G: %i B: %i (#%02x%02x%02x)", 863 color.red, color.green, color.blue, color.red, color.green, color.blue); 864 865 MovePenTo(15 + fPopUp->Bounds().Width(), fFontHeight*2+5); 866 invalRect.Set(10, fFontHeight+7, 10 + StringWidth(fRGBStr), fFontHeight*2+7); 867 SetHighColor(ViewColor()); 868 FillRect(invalRect); 869 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 870 strcpy(fRGBStr,str); 871 if (fInfoTextVisible) 872 DrawString(fRGBStr); 873 874 bool ch1Showing, ch2Showing; 875 dynamic_cast<TWindow*>(Window())->CrossHairsShowing(&ch1Showing, &ch2Showing); 876 877 if (fMagView) { 878 BPoint pt1(fMagView->CrossHair1Loc()); 879 BPoint pt2(fMagView->CrossHair2Loc()); 880 881 float h = Bounds().Height(); 882 if (ch2Showing) { 883 MovePenTo(10, h-12); 884 sprintf(str, "2) x: %" B_PRIi32 " y: %" B_PRIi32 " y: %d", 885 (int32)pt2.x, (int32)pt2.y, abs((int)(pt1.y - pt2.y))); 886 invalRect.Set(10, h-12-fFontHeight, 10 + StringWidth(fCH2Str), h-10); 887 SetHighColor(ViewColor()); 888 FillRect(invalRect); 889 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 890 strcpy(fCH2Str,str); 891 if (fInfoTextVisible) 892 DrawString(fCH2Str); 893 } 894 895 if (ch1Showing && ch2Showing) { 896 MovePenTo(10, h-10-fFontHeight-2); 897 sprintf(str, "1) x: %" B_PRIi32 " y: %" B_PRIi32 " x: %d", 898 (int32)pt1.x, (int32)pt1.y, abs((int)(pt1.x - pt2.x))); 899 invalRect.Set(10, h-10-2*fFontHeight-2, 10 + StringWidth(fCH1Str), h-10-fFontHeight); 900 SetHighColor(ViewColor()); 901 FillRect(invalRect); 902 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 903 strcpy(fCH1Str,str); 904 if (fInfoTextVisible) 905 DrawString(fCH1Str); 906 } else if (ch1Showing) { 907 MovePenTo(10, h-10); 908 sprintf(str, "x: %" B_PRIi32 " y: %" B_PRIi32, (int32)pt1.x, (int32)pt1.y); 909 invalRect.Set(10, h-10-fFontHeight, 10 + StringWidth(fCH1Str), h-8); 910 SetHighColor(ViewColor()); 911 FillRect(invalRect); 912 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 913 strcpy(fCH1Str,str); 914 if (fInfoTextVisible) 915 DrawString(fCH1Str); 916 } 917 } 918 919 PopState(); 920 } 921 922 923 void 924 TInfoView::FrameResized(float width, float height) 925 { 926 BBox::FrameResized(width, height); 927 } 928 929 930 void 931 TInfoView::AddMenu() 932 { 933 fMenu = new TMenu(dynamic_cast<TWindow*>(Window()), ""); 934 BuildInfoMenu(fMenu); 935 936 BRect r(9, 11, 22, 27); 937 fPopUp = new BMenuField( r, "region menu", NULL, fMenu, true, 938 B_FOLLOW_LEFT | B_FOLLOW_TOP); 939 AddChild(fPopUp); 940 } 941 942 943 void 944 TInfoView::SetMagView(TMagnify* magView) 945 { 946 fMagView = magView; 947 } 948 949 950 // #pragma mark - 951 952 953 TMenu::TMenu(TWindow *mainWindow, const char *title, menu_layout layout) 954 : BMenu(title, layout), 955 fMainWindow(mainWindow) 956 { 957 } 958 959 960 TMenu::~TMenu() 961 { 962 } 963 964 965 void 966 TMenu::AttachedToWindow() 967 { 968 bool state = true; 969 if (fMainWindow) 970 state = fMainWindow->IsActive(); 971 972 BMenuItem* menuItem = FindItem(B_TRANSLATE("Hide/Show info")); 973 if (menuItem) 974 menuItem->SetEnabled(state); 975 menuItem = FindItem(B_TRANSLATE("Add a crosshair")); 976 if (menuItem) 977 menuItem->SetEnabled(state); 978 menuItem = FindItem(B_TRANSLATE("Remove a crosshair")); 979 if (menuItem) 980 menuItem->SetEnabled(state); 981 menuItem = FindItem(B_TRANSLATE("Hide/Show grid")); 982 if (menuItem) 983 menuItem->SetEnabled(state); 984 menuItem = FindItem(B_TRANSLATE("Make square")); 985 if (menuItem) 986 menuItem->SetEnabled(state); 987 menuItem = FindItem(B_TRANSLATE("Decrease window size")); 988 if (menuItem) 989 menuItem->SetEnabled(state); 990 menuItem = FindItem(B_TRANSLATE("Increase window size")); 991 if (menuItem) 992 menuItem->SetEnabled(state); 993 menuItem = FindItem(B_TRANSLATE("Decrease pixel size")); 994 if (menuItem) 995 menuItem->SetEnabled(state); 996 menuItem = FindItem(B_TRANSLATE("Increase pixel size")); 997 if (menuItem) 998 menuItem->SetEnabled(state); 999 1000 BMenu::AttachedToWindow(); 1001 } 1002 1003 1004 void 1005 TInfoView::GetPreferredSize(float* _width, float* _height) 1006 { 1007 if (_width) { 1008 float str1Width = StringWidth(fCH1Str) 1009 + StringWidth(fCH2Str) 1010 + StringWidth(fRGBStr) 1011 + 30; 1012 float str2Width = StringWidth(fInfoStr) + 30; 1013 *_width = str1Width > str2Width ? str1Width : str2Width; 1014 } 1015 1016 if (_height) 1017 *_height = fFontHeight * 2 + 10; 1018 } 1019 1020 1021 bool 1022 TInfoView::IsInfoTextVisible() 1023 { 1024 return fInfoTextVisible; 1025 } 1026 1027 1028 void 1029 TInfoView::SetInfoTextVisible(bool visible) 1030 { 1031 fInfoTextVisible = visible; 1032 Draw(Bounds()); 1033 } 1034 1035 1036 // #pragma mark - 1037 1038 1039 TMagnify::TMagnify(BRect r, TWindow* parent) 1040 : BView(r, "MagView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS), 1041 fNeedToUpdate(true), 1042 fThread(-1), 1043 fActive(true), 1044 fImageBuf(NULL), 1045 fImageView(NULL), 1046 fLastLoc(-1, -1), 1047 fSelection(-1), 1048 fShowSelection(false), 1049 fSelectionLoc(0, 0), 1050 fShowCrossHair1(false), 1051 fCrossHair1(-1, -1), 1052 fShowCrossHair2(false), 1053 fCrossHair2(-1, -1), 1054 fParent(parent), 1055 fStickCoordinates(false) 1056 { 1057 } 1058 1059 1060 TMagnify::~TMagnify() 1061 { 1062 kill_thread(fThread); 1063 delete fImageBuf; 1064 } 1065 1066 1067 void 1068 TMagnify::AttachedToWindow() 1069 { 1070 int32 width, height; 1071 fParent->PixelCount(&width, &height); 1072 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid()); 1073 1074 fThread = spawn_thread(TMagnify::MagnifyTask, "MagnifyTask", 1075 B_NORMAL_PRIORITY, this); 1076 1077 resume_thread(fThread); 1078 1079 SetViewColor(B_TRANSPARENT_32_BIT); 1080 MakeFocus(); 1081 } 1082 1083 1084 void 1085 TMagnify::InitBuffers(int32 hPixelCount, int32 vPixelCount, 1086 int32 pixelSize, bool showGrid) 1087 { 1088 color_space colorSpace = BScreen(Window()).ColorSpace(); 1089 1090 BRect r(0, 0, (pixelSize * hPixelCount)-1, (pixelSize * vPixelCount)-1); 1091 if (Bounds().Width() != r.Width() || Bounds().Height() != r.Height()) 1092 ResizeTo(r.Width(), r.Height()); 1093 1094 if (fImageView) { 1095 fImageBuf->Lock(); 1096 fImageView->RemoveSelf(); 1097 fImageBuf->Unlock(); 1098 1099 fImageView->Resize((int32)r.Width(), (int32)r.Height()); 1100 fImageView->SetSpace(colorSpace); 1101 } else 1102 fImageView = new TOSMagnify(r, this, colorSpace); 1103 1104 delete fImageBuf; 1105 fImageBuf = new BBitmap(r, colorSpace, true); 1106 fImageBuf->Lock(); 1107 fImageBuf->AddChild(fImageView); 1108 fImageBuf->Unlock(); 1109 } 1110 1111 1112 void 1113 TMagnify::Draw(BRect) 1114 { 1115 BRect bounds(Bounds()); 1116 DrawBitmap(fImageBuf, bounds, bounds); 1117 static_cast<TWindow*>(Window())->UpdateInfo(); 1118 } 1119 1120 1121 void 1122 TMagnify::KeyDown(const char *key, int32 numBytes) 1123 { 1124 if (!fShowSelection) 1125 BView::KeyDown(key, numBytes); 1126 1127 uint32 mods = modifiers(); 1128 1129 switch (key[0]) { 1130 case B_TAB: 1131 if (fShowCrossHair1) { 1132 fSelection++; 1133 1134 if (fShowCrossHair2) { 1135 if (fSelection > 2) 1136 fSelection = 0; 1137 } else if (fShowCrossHair1) { 1138 if (fSelection > 1) 1139 fSelection = 0; 1140 } 1141 fNeedToUpdate = true; 1142 Invalidate(); 1143 } 1144 break; 1145 1146 case B_LEFT_ARROW: 1147 if (mods & B_OPTION_KEY) 1148 NudgeMouse(-1,0); 1149 else 1150 MoveSelection(-1,0); 1151 break; 1152 case B_RIGHT_ARROW: 1153 if (mods & B_OPTION_KEY) 1154 NudgeMouse(1, 0); 1155 else 1156 MoveSelection(1,0); 1157 break; 1158 case B_UP_ARROW: 1159 if (mods & B_OPTION_KEY) 1160 NudgeMouse(0, -1); 1161 else 1162 MoveSelection(0,-1); 1163 break; 1164 case B_DOWN_ARROW: 1165 if (mods & B_OPTION_KEY) 1166 NudgeMouse(0, 1); 1167 else 1168 MoveSelection(0,1); 1169 break; 1170 1171 default: 1172 BView::KeyDown(key,numBytes); 1173 break; 1174 } 1175 } 1176 1177 1178 void 1179 TMagnify::FrameResized(float newW, float newH) 1180 { 1181 int32 w, h; 1182 PixelCount(&w, &h); 1183 1184 if (fSelectionLoc.x >= w) 1185 fSelectionLoc.x = 0; 1186 if (fSelectionLoc.y >= h) 1187 fSelectionLoc.y = 0; 1188 1189 if (fShowCrossHair1) { 1190 if (fCrossHair1.x >= w) { 1191 fCrossHair1.x = fSelectionLoc.x + 2; 1192 if (fCrossHair1.x >= w) 1193 fCrossHair1.x = 0; 1194 } 1195 if (fCrossHair1.y >= h) { 1196 fCrossHair1.y = fSelectionLoc.y + 2; 1197 if (fCrossHair1.y >= h) 1198 fCrossHair1.y = 0; 1199 } 1200 1201 if (fShowCrossHair2) { 1202 if (fCrossHair2.x >= w) { 1203 fCrossHair2.x = fCrossHair1.x + 2; 1204 if (fCrossHair2.x >= w) 1205 fCrossHair2.x = 0; 1206 } 1207 if (fCrossHair2.y >= h) { 1208 fCrossHair2.y = fCrossHair1.y + 2; 1209 if (fCrossHair2.y >= h) 1210 fCrossHair2.y = 0; 1211 } 1212 } 1213 } 1214 } 1215 1216 1217 void 1218 TMagnify::MouseDown(BPoint where) 1219 { 1220 BMessage *currentMsg = Window()->CurrentMessage(); 1221 if (currentMsg->what == B_MOUSE_DOWN) { 1222 uint32 buttons = 0; 1223 currentMsg->FindInt32("buttons", (int32 *)&buttons); 1224 1225 uint32 modifiers = 0; 1226 currentMsg->FindInt32("modifiers", (int32 *)&modifiers); 1227 1228 if ((buttons & B_SECONDARY_MOUSE_BUTTON) || (modifiers & B_CONTROL_KEY)) { 1229 // secondary button was clicked or control key was down, show menu and return 1230 1231 BPopUpMenu *menu = new BPopUpMenu(B_TRANSLATE("Info")); 1232 menu->SetFont(be_plain_font); 1233 BuildInfoMenu(menu); 1234 1235 BMenuItem *selected = menu->Go(ConvertToScreen(where)); 1236 if (selected) 1237 Window()->PostMessage(selected->Message()->what); 1238 delete menu; 1239 return; 1240 } 1241 1242 // add a mousedown looper here 1243 1244 int32 pixelSize = PixelSize(); 1245 float x = where.x / pixelSize; 1246 float y = where.y / pixelSize; 1247 1248 MoveSelectionTo(x, y); 1249 1250 // draw the frozen image 1251 // update the info region 1252 1253 fNeedToUpdate = true; 1254 Invalidate(); 1255 } 1256 } 1257 1258 1259 void 1260 TMagnify::ScreenChanged(BRect, color_space) 1261 { 1262 int32 width, height; 1263 fParent->PixelCount(&width, &height); 1264 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid()); 1265 } 1266 1267 1268 void 1269 TMagnify::SetSelection(bool state) 1270 { 1271 if (fShowSelection == state) 1272 return; 1273 1274 fShowSelection = state; 1275 fSelection = 0; 1276 Invalidate(); 1277 } 1278 1279 1280 void 1281 TMagnify::MoveSelection(int32 x, int32 y) 1282 { 1283 if (!fShowSelection) 1284 return; 1285 1286 int32 xCount, yCount; 1287 PixelCount(&xCount, &yCount); 1288 1289 float xloc, yloc; 1290 if (fSelection == 0) { 1291 xloc = fSelectionLoc.x; 1292 yloc = fSelectionLoc.y; 1293 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); 1294 fSelectionLoc.x = xloc; 1295 fSelectionLoc.y = yloc; 1296 } else if (fSelection == 1) { 1297 xloc = fCrossHair1.x; 1298 yloc = fCrossHair1.y; 1299 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); 1300 fCrossHair1.x = xloc; 1301 fCrossHair1.y = yloc; 1302 } else if (fSelection == 2) { 1303 xloc = fCrossHair2.x; 1304 yloc = fCrossHair2.y; 1305 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); 1306 fCrossHair2.x = xloc; 1307 fCrossHair2.y = yloc; 1308 } 1309 1310 fNeedToUpdate = true; 1311 Invalidate(); 1312 } 1313 1314 1315 void 1316 TMagnify::MoveSelectionTo(int32 x, int32 y) 1317 { 1318 if (!fShowSelection) 1319 return; 1320 1321 int32 xCount, yCount; 1322 PixelCount(&xCount, &yCount); 1323 if (x >= xCount) 1324 x = 0; 1325 if (y >= yCount) 1326 y = 0; 1327 1328 if (fSelection == 0) { 1329 fSelectionLoc.x = x; 1330 fSelectionLoc.y = y; 1331 } else if (fSelection == 1) { 1332 fCrossHair1.x = x; 1333 fCrossHair1.y = y; 1334 } else if (fSelection == 2) { 1335 fCrossHair2.x = x; 1336 fCrossHair2.y = y; 1337 } 1338 1339 fNeedToUpdate = true; 1340 Invalidate(); //Draw(Bounds()); 1341 } 1342 1343 1344 void 1345 TMagnify::ShowSelection() 1346 { 1347 } 1348 1349 1350 short 1351 TMagnify::Selection() 1352 { 1353 return fSelection; 1354 } 1355 1356 1357 bool 1358 TMagnify::SelectionIsShowing() 1359 { 1360 return fShowSelection; 1361 } 1362 1363 1364 void 1365 TMagnify::SelectionLoc(float* x, float* y) 1366 { 1367 *x = fSelectionLoc.x; 1368 *y = fSelectionLoc.y; 1369 } 1370 1371 1372 void 1373 TMagnify::SetSelectionLoc(float x, float y) 1374 { 1375 fSelectionLoc.x = x; 1376 fSelectionLoc.y = y; 1377 } 1378 1379 1380 rgb_color 1381 TMagnify::SelectionColor() 1382 { 1383 return fImageView->ColorAtSelection(); 1384 } 1385 1386 1387 void 1388 TMagnify::CrossHair1Loc(float* x, float* y) 1389 { 1390 *x = fCrossHair1.x; 1391 *y = fCrossHair1.y; 1392 } 1393 1394 1395 void 1396 TMagnify::CrossHair2Loc(float* x, float* y) 1397 { 1398 *x = fCrossHair2.x; 1399 *y = fCrossHair2.y; 1400 } 1401 1402 1403 BPoint 1404 TMagnify::CrossHair1Loc() 1405 { 1406 return fCrossHair1; 1407 } 1408 1409 1410 BPoint 1411 TMagnify::CrossHair2Loc() 1412 { 1413 return fCrossHair2; 1414 } 1415 1416 1417 void 1418 TMagnify::NudgeMouse(float x, float y) 1419 { 1420 BPoint loc; 1421 uint32 button; 1422 1423 GetMouse(&loc, &button); 1424 ConvertToScreen(&loc); 1425 loc.x += x; 1426 loc.y += y; 1427 1428 set_mouse_position((int32)loc.x, (int32)loc.y); 1429 } 1430 1431 1432 void 1433 TMagnify::WindowActivated(bool active) 1434 { 1435 if (active) 1436 MakeFocus(); 1437 } 1438 1439 1440 status_t 1441 TMagnify::MagnifyTask(void *arg) 1442 { 1443 TMagnify* view = (TMagnify*)arg; 1444 1445 // static data members can't access members, methods without 1446 // a pointer to an instance of the class 1447 TWindow* window = (TWindow*)view->Window(); 1448 1449 while (true) { 1450 if (window->Lock()) { 1451 if (view->NeedToUpdate() || view->Active()) 1452 view->Update(view->NeedToUpdate()); 1453 1454 window->Unlock(); 1455 } 1456 snooze(35000); 1457 } 1458 1459 return B_NO_ERROR; 1460 } 1461 1462 1463 void 1464 TMagnify::Update(bool force) 1465 { 1466 BPoint loc; 1467 uint32 button; 1468 static long counter = 0; 1469 1470 if (!fStickCoordinates) { 1471 GetMouse(&loc, &button); 1472 ConvertToScreen(&loc); 1473 } else 1474 loc = fLastLoc; 1475 1476 if (force || fLastLoc != loc || counter++ % 35 == 0) { 1477 if (fImageView->CreateImage(loc, force)) 1478 Invalidate(); 1479 1480 counter = 0; 1481 if (force) 1482 SetUpdate(false); 1483 } 1484 fLastLoc = loc; 1485 } 1486 1487 1488 bool 1489 TMagnify::NeedToUpdate() 1490 { 1491 return fNeedToUpdate; 1492 } 1493 1494 1495 void 1496 TMagnify::SetUpdate(bool s) 1497 { 1498 fNeedToUpdate = s; 1499 } 1500 1501 1502 void 1503 TMagnify::CopyImage() 1504 { 1505 StartSave(); 1506 be_clipboard->Lock(); 1507 be_clipboard->Clear(); 1508 1509 BMessage *message = be_clipboard->Data(); 1510 if (!message) { 1511 printf(B_TRANSLATE_CONTEXT("no clip msg\n", 1512 "In console, when clipboard is empty after clicking Copy image")); 1513 return; 1514 } 1515 1516 BMessage *embeddedBitmap = new BMessage(); 1517 (fImageView->Bitmap())->Archive(embeddedBitmap,false); 1518 status_t err = message->AddMessage(kBitmapMimeType, embeddedBitmap); 1519 if (err == B_OK) 1520 err = message->AddRect("rect", fImageView->Bitmap()->Bounds()); 1521 if (err == B_OK) 1522 be_clipboard->Commit(); 1523 1524 be_clipboard->Unlock(); 1525 EndSave(); 1526 } 1527 1528 1529 void 1530 TMagnify::AddCrossHair() 1531 { 1532 if (fShowCrossHair1 && fShowCrossHair2) 1533 return; 1534 1535 int32 w, h; 1536 PixelCount(&w, &h); 1537 1538 if (fShowCrossHair1) { 1539 fSelection = 2; 1540 fShowCrossHair2 = true; 1541 fCrossHair2.x = fCrossHair1.x + 2; 1542 if (fCrossHair2.x >= w) 1543 fCrossHair2.x = 0; 1544 fCrossHair2.y = fCrossHair1.y + 2; 1545 if (fCrossHair2.y >= h) 1546 fCrossHair2.y = 0; 1547 } else { 1548 fSelection = 1; 1549 fShowCrossHair1 = true; 1550 fCrossHair1.x = fSelectionLoc.x + 2; 1551 if (fCrossHair1.x >= w) 1552 fCrossHair1.x = 0; 1553 fCrossHair1.y = fSelectionLoc.y + 2; 1554 if (fCrossHair1.y >= h) 1555 fCrossHair1.y = 0; 1556 } 1557 Invalidate(); 1558 } 1559 1560 1561 void 1562 TMagnify::RemoveCrossHair() 1563 { 1564 if (!fShowCrossHair1 && !fShowCrossHair2) 1565 return; 1566 1567 if (fShowCrossHair2) { 1568 fSelection = 1; 1569 fShowCrossHair2 = false; 1570 } else if (fShowCrossHair1) { 1571 fSelection = 0; 1572 fShowCrossHair1 = false; 1573 } 1574 Invalidate(); 1575 } 1576 1577 1578 void 1579 TMagnify::SetCrossHairsShowing(bool ch1, bool ch2) 1580 { 1581 fShowCrossHair1 = ch1; 1582 fShowCrossHair2 = ch2; 1583 } 1584 1585 1586 void 1587 TMagnify::CrossHairsShowing(bool* ch1, bool* ch2) 1588 { 1589 *ch1 = fShowCrossHair1; 1590 *ch2 = fShowCrossHair2; 1591 } 1592 1593 1594 void 1595 TMagnify::MakeActive(bool s) 1596 { 1597 fActive = s; 1598 } 1599 1600 1601 void 1602 TMagnify::MakeSticked(bool s) 1603 { 1604 fStickCoordinates = s; 1605 } 1606 1607 1608 void 1609 TMagnify::PixelCount(int32* width, int32* height) 1610 { 1611 fParent->PixelCount(width, height); 1612 } 1613 1614 1615 int32 1616 TMagnify::PixelSize() 1617 { 1618 return fParent->PixelSize(); 1619 } 1620 1621 1622 bool 1623 TMagnify::ShowGrid() 1624 { 1625 return fParent->ShowGrid(); 1626 } 1627 1628 1629 void 1630 TMagnify::StartSave() 1631 { 1632 fImageFrozenOnSave = Active(); 1633 if (fImageFrozenOnSave) 1634 MakeActive(false); 1635 } 1636 1637 1638 void 1639 TMagnify::EndSave() 1640 { 1641 if (fImageFrozenOnSave) 1642 MakeActive(true); 1643 } 1644 1645 1646 void 1647 TMagnify::SaveImage(entry_ref* ref, char* name) 1648 { 1649 // create a new file 1650 BFile file; 1651 BDirectory parentDir(ref); 1652 parentDir.CreateFile(name, &file); 1653 1654 // Write the screenshot bitmap to the file 1655 BBitmapStream stream(fImageView->Bitmap()); 1656 BTranslatorRoster* roster = BTranslatorRoster::Default(); 1657 roster->Translate(&stream, NULL, NULL, &file, B_PNG_FORMAT, 1658 B_TRANSLATOR_BITMAP); 1659 1660 BBitmap* bitmap; 1661 stream.DetachBitmap(&bitmap); 1662 // The stream takes over ownership of the bitmap 1663 1664 // unfreeze the image, image was frozen before invoke of FilePanel 1665 EndSave(); 1666 } 1667 1668 // #pragma mark - 1669 1670 1671 TOSMagnify::TOSMagnify(BRect r, TMagnify* parent, color_space space) 1672 : BView(r, "ImageView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS), 1673 fColorSpace(space), fParent(parent) 1674 { 1675 switch (space) { 1676 case B_CMAP8: 1677 fBytesPerPixel = 1; 1678 break; 1679 case B_RGB15: 1680 case B_RGBA15: 1681 case B_RGB15_BIG: 1682 case B_RGBA15_BIG: 1683 case B_RGB16: 1684 case B_RGB16_BIG: 1685 fBytesPerPixel = 2; 1686 break; 1687 case B_RGB24: 1688 fBytesPerPixel = 3; 1689 break; 1690 case B_RGB32: 1691 case B_RGBA32: 1692 case B_RGB32_BIG: 1693 case B_RGBA32_BIG: 1694 fBytesPerPixel = 4; 1695 break; 1696 default: 1697 // uh, oh -- a color space we don't support 1698 fprintf(stderr, "Tried to run in an unsupported color space; exiting\n"); 1699 exit(1); 1700 break; 1701 } 1702 1703 fPixel = NULL; 1704 fBitmap = NULL; 1705 fOldBits = NULL; 1706 InitObject(); 1707 } 1708 1709 1710 TOSMagnify::~TOSMagnify() 1711 { 1712 delete fPixel; 1713 delete fBitmap; 1714 free(fOldBits); 1715 } 1716 1717 1718 void 1719 TOSMagnify::SetSpace(color_space space) 1720 { 1721 fColorSpace = space; 1722 InitObject(); 1723 }; 1724 1725 1726 void 1727 TOSMagnify::InitObject() 1728 { 1729 int32 w, h; 1730 fParent->PixelCount(&w, &h); 1731 1732 delete fBitmap; 1733 BRect bitsRect(0, 0, w-1, h-1); 1734 fBitmap = new BBitmap(bitsRect, fColorSpace); 1735 1736 free(fOldBits); 1737 fOldBits = (char*)malloc(fBitmap->BitsLength()); 1738 1739 if (!fPixel) { 1740 #if B_HOST_IS_BENDIAN 1741 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32_BIG, true); 1742 #else 1743 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32, true); 1744 #endif 1745 fPixelView = new BView(BRect(0,0,0,0), NULL, 0, 0); 1746 fPixel->Lock(); 1747 fPixel->AddChild(fPixelView); 1748 fPixel->Unlock(); 1749 } 1750 } 1751 1752 1753 void 1754 TOSMagnify::FrameResized(float width, float height) 1755 { 1756 BView::FrameResized(width, height); 1757 InitObject(); 1758 } 1759 1760 1761 void 1762 TOSMagnify::Resize(int32 width, int32 height) 1763 { 1764 ResizeTo(width, height); 1765 InitObject(); 1766 } 1767 1768 1769 bool 1770 TOSMagnify::CreateImage(BPoint mouseLoc, bool force) 1771 { 1772 bool created = false; 1773 if (Window() && Window()->Lock()) { 1774 int32 width, height; 1775 fParent->PixelCount(&width, &height); 1776 int32 pixelSize = fParent->PixelSize(); 1777 1778 BRect srcRect(0, 0, width - 1, height - 1); 1779 srcRect.OffsetBy(mouseLoc.x - (width / 2), 1780 mouseLoc.y - (height / 2)); 1781 1782 if (force || CopyScreenRect(srcRect)) { 1783 srcRect.OffsetTo(BPoint(0, 0)); 1784 BRect destRect(Bounds()); 1785 1786 DrawBitmap(fBitmap, srcRect, destRect); 1787 1788 DrawGrid(width, height, destRect, pixelSize); 1789 DrawSelection(); 1790 1791 Sync(); 1792 created = true; 1793 } 1794 Window()->Unlock(); 1795 } else 1796 printf("window problem\n"); 1797 1798 return created; 1799 } 1800 1801 1802 bool 1803 TOSMagnify::CopyScreenRect(BRect srcRect) 1804 { 1805 // constrain src rect to legal screen rect 1806 BScreen screen(Window()); 1807 BRect scrnframe = screen.Frame(); 1808 1809 if (srcRect.right > scrnframe.right) 1810 srcRect.OffsetTo(scrnframe.right - srcRect.Width(), srcRect.top); 1811 if (srcRect.top < 0) 1812 srcRect.OffsetTo(srcRect.left, 0); 1813 1814 if (srcRect.bottom > scrnframe.bottom) 1815 srcRect.OffsetTo(srcRect.left, scrnframe.bottom - srcRect.Height()); 1816 if (srcRect.left < 0) 1817 srcRect.OffsetTo(0, srcRect.top); 1818 1819 // save a copy of the bits for comparison later 1820 memcpy(fOldBits, fBitmap->Bits(), fBitmap->BitsLength()); 1821 1822 screen.ReadBitmap(fBitmap, false, &srcRect); 1823 1824 // let caller know whether bits have actually changed 1825 return memcmp(fBitmap->Bits(), fOldBits, fBitmap->BitsLength()) != 0; 1826 } 1827 1828 1829 void 1830 TOSMagnify::DrawGrid(int32 width, int32 height, BRect destRect, int32 pixelSize) 1831 { 1832 // draw grid 1833 if (fParent->ShowGrid() && fParent->PixelSize() > 2) { 1834 BeginLineArray(width * height); 1835 1836 // horizontal lines 1837 for (int32 i = pixelSize; i < (height * pixelSize); i += pixelSize) 1838 AddLine(BPoint(0, i), BPoint(destRect.right, i), kGridGray); 1839 1840 // vertical lines 1841 for (int32 i = pixelSize; i < (width * pixelSize); i += pixelSize) 1842 AddLine(BPoint(i, 0), BPoint(i, destRect.bottom), kGridGray); 1843 1844 EndLineArray(); 1845 } 1846 1847 SetHighColor(kGridGray); 1848 StrokeRect(destRect); 1849 } 1850 1851 1852 void 1853 TOSMagnify::DrawSelection() 1854 { 1855 if (!fParent->SelectionIsShowing()) 1856 return; 1857 1858 float x, y; 1859 int32 pixelSize = fParent->PixelSize(); 1860 int32 squareSize = pixelSize - 2; 1861 1862 fParent->SelectionLoc(&x, &y); 1863 x *= pixelSize; x++; 1864 y *= pixelSize; y++; 1865 BRect selRect(x, y, x+squareSize, y+squareSize); 1866 1867 short selection = fParent->Selection(); 1868 1869 PushState(); 1870 SetLowColor(ViewColor()); 1871 SetHighColor(kRedColor); 1872 StrokeRect(selRect); 1873 if (selection == 0) { 1874 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); 1875 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); 1876 } 1877 1878 bool ch1Showing, ch2Showing; 1879 fParent->CrossHairsShowing(&ch1Showing, &ch2Showing); 1880 if (ch1Showing) { 1881 SetHighColor(kBlueColor); 1882 fParent->CrossHair1Loc(&x, &y); 1883 x *= pixelSize; x++; 1884 y *= pixelSize; y++; 1885 selRect.Set(x, y,x+squareSize, y+squareSize); 1886 StrokeRect(selRect); 1887 BeginLineArray(4); 1888 AddLine(BPoint(0, y+(squareSize/2)), 1889 BPoint(x, y+(squareSize/2)), kBlueColor); // left 1890 AddLine(BPoint(x+squareSize,y+(squareSize/2)), 1891 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right 1892 AddLine(BPoint(x+(squareSize/2), 0), 1893 BPoint(x+(squareSize/2), y), kBlueColor); // top 1894 AddLine(BPoint(x+(squareSize/2), y+squareSize), 1895 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom 1896 EndLineArray(); 1897 if (selection == 1) { 1898 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); 1899 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); 1900 } 1901 } 1902 if (ch2Showing) { 1903 SetHighColor(kBlueColor); 1904 fParent->CrossHair2Loc(&x, &y); 1905 x *= pixelSize; x++; 1906 y *= pixelSize; y++; 1907 selRect.Set(x, y,x+squareSize, y+squareSize); 1908 StrokeRect(selRect); 1909 BeginLineArray(4); 1910 AddLine(BPoint(0, y+(squareSize/2)), 1911 BPoint(x, y+(squareSize/2)), kBlueColor); // left 1912 AddLine(BPoint(x+squareSize,y+(squareSize/2)), 1913 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right 1914 AddLine(BPoint(x+(squareSize/2), 0), 1915 BPoint(x+(squareSize/2), y), kBlueColor); // top 1916 AddLine(BPoint(x+(squareSize/2), y+squareSize), 1917 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom 1918 EndLineArray(); 1919 if (selection == 2) { 1920 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); 1921 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); 1922 } 1923 } 1924 1925 PopState(); 1926 } 1927 1928 1929 rgb_color 1930 TOSMagnify::ColorAtSelection() 1931 { 1932 float x, y; 1933 fParent->SelectionLoc(&x, &y); 1934 BRect srcRect(x, y, x, y); 1935 BRect dstRect(0, 0, 0, 0); 1936 fPixel->Lock(); 1937 fPixelView->DrawBitmap(fBitmap, srcRect, dstRect); 1938 fPixelView->Sync(); 1939 fPixel->Unlock(); 1940 1941 uint32 pixel = *((uint32*)fPixel->Bits()); 1942 rgb_color c; 1943 c.alpha = pixel >> 24; 1944 c.red = (pixel >> 16) & 0xFF; 1945 c.green = (pixel >> 8) & 0xFF; 1946 c.blue = pixel & 0xFF; 1947 1948 return c; 1949 } 1950 1951 1952 // #pragma mark - 1953 1954 1955 int 1956 main(int argc, char* argv[]) 1957 { 1958 int32 pixelCount = -1; 1959 1960 if (argc > 2) { 1961 printf(B_TRANSLATE_CONTEXT( 1962 "usage: magnify [size] (magnify size * size pixels)\n", 1963 "Console")); 1964 exit(1); 1965 } else { 1966 if (argc == 2) { 1967 pixelCount = abs(atoi(argv[1])); 1968 1969 if ((pixelCount > 100) || (pixelCount < 4)) { 1970 printf(B_TRANSLATE_CONTEXT( 1971 "usage: magnify [size] (magnify size * size pixels)\n", 1972 "Console")); 1973 printf(B_TRANSLATE_CONTEXT( 1974 " size must be > 4 and a multiple of 4\n", 1975 "Console")); 1976 exit(1); 1977 } 1978 1979 if (pixelCount % 4) { 1980 printf(B_TRANSLATE_CONTEXT( 1981 "magnify: size must be a multiple of 4\n", 1982 "Console")); 1983 exit(1); 1984 } 1985 } 1986 } 1987 1988 TApp app(pixelCount); 1989 app.Run(); 1990 return 0; 1991 } 1992