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