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