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