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