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[0] = 0; 1054 fCH2Str[0] = 0; 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 (ch2Showing) { 1138 MovePenTo(10, h-12); 1139 sprintf(str, "2) x: %" B_PRIi32 " y: %" B_PRIi32 " y: %d", 1140 (int32)pt2.x, (int32)pt2.y, abs((int)(pt1.y - pt2.y))); 1141 invalRect.Set(10, h-12-fFontHeight, 10 + StringWidth(fCH2Str), h-10); 1142 SetHighColor(ViewColor()); 1143 FillRect(invalRect); 1144 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 1145 strcpy(fCH2Str,str); 1146 if (fInfoTextVisible) 1147 DrawString(fCH2Str); 1148 } 1149 1150 if (ch1Showing && ch2Showing) { 1151 MovePenTo(10, h-10-fFontHeight-2); 1152 sprintf(str, "1) x: %" B_PRIi32 " y: %" B_PRIi32 " x: %d", 1153 (int32)pt1.x, (int32)pt1.y, abs((int)(pt1.x - pt2.x))); 1154 invalRect.Set(10, h-10-2*fFontHeight-2, 10 + StringWidth(fCH1Str), h-10-fFontHeight); 1155 SetHighColor(ViewColor()); 1156 FillRect(invalRect); 1157 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 1158 strcpy(fCH1Str,str); 1159 if (fInfoTextVisible) 1160 DrawString(fCH1Str); 1161 } else if (ch1Showing) { 1162 MovePenTo(10, h-10); 1163 sprintf(str, "x: %" B_PRIi32 " y: %" B_PRIi32, (int32)pt1.x, (int32)pt1.y); 1164 invalRect.Set(10, h-10-fFontHeight, 10 + StringWidth(fCH1Str), h-8); 1165 SetHighColor(ViewColor()); 1166 FillRect(invalRect); 1167 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 1168 strcpy(fCH1Str,str); 1169 if (fInfoTextVisible) 1170 DrawString(fCH1Str); 1171 } 1172 } 1173 1174 PopState(); 1175 } 1176 1177 1178 void 1179 TInfoView::FrameResized(float width, float height) 1180 { 1181 BBox::FrameResized(width, height); 1182 } 1183 1184 1185 void 1186 TInfoView::AddMenu() 1187 { 1188 fMenu = new TMenu(dynamic_cast<TWindow*>(Window()), ""); 1189 BuildInfoMenu(fMenu); 1190 1191 BRect r(9, 11, 22, 27); 1192 fPopUp = new BMenuField( r, "region menu", NULL, fMenu, true, 1193 B_FOLLOW_LEFT | B_FOLLOW_TOP); 1194 AddChild(fPopUp); 1195 } 1196 1197 1198 void 1199 TInfoView::SetMagView(TMagnify* magView) 1200 { 1201 fMagView = magView; 1202 } 1203 1204 1205 // #pragma mark - 1206 1207 1208 TMenu::TMenu(TWindow *mainWindow, const char *title, menu_layout layout) 1209 : BMenu(title, layout), 1210 fMainWindow(mainWindow) 1211 { 1212 } 1213 1214 1215 TMenu::~TMenu() 1216 { 1217 } 1218 1219 1220 void 1221 TMenu::AttachedToWindow() 1222 { 1223 UpdateInfoMenu(this, fMainWindow); 1224 1225 BMenu::AttachedToWindow(); 1226 } 1227 1228 1229 void 1230 TInfoView::GetPreferredSize(float* _width, float* _height) 1231 { 1232 if (_width) { 1233 float str1Width = StringWidth(fCH1Str) 1234 + StringWidth(fCH2Str) 1235 + StringWidth(fRGBStr) 1236 + 30; 1237 float str2Width = StringWidth(fInfoStr) + 30; 1238 *_width = str1Width > str2Width ? str1Width : str2Width; 1239 } 1240 1241 if (_height) 1242 *_height = fFontHeight * 2 + 10; 1243 } 1244 1245 1246 bool 1247 TInfoView::IsInfoTextVisible() 1248 { 1249 return fInfoTextVisible; 1250 } 1251 1252 1253 void 1254 TInfoView::SetInfoTextVisible(bool visible) 1255 { 1256 fInfoTextVisible = visible; 1257 Draw(Bounds()); 1258 } 1259 1260 1261 // #pragma mark - 1262 1263 1264 TMagnify::TMagnify(BRect r, TWindow* parent) 1265 : BView(r, "MagView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS), 1266 fNeedToUpdate(true), 1267 fThread(-1), 1268 fActive(true), 1269 fImageBuf(NULL), 1270 fImageView(NULL), 1271 fLastLoc(-1, -1), 1272 fSelection(-1), 1273 fShowSelection(false), 1274 fSelectionLoc(0, 0), 1275 fShowCrossHair1(false), 1276 fCrossHair1(-1, -1), 1277 fShowCrossHair2(false), 1278 fCrossHair2(-1, -1), 1279 fParent(parent), 1280 fStickCoordinates(false) 1281 { 1282 } 1283 1284 1285 TMagnify::~TMagnify() 1286 { 1287 kill_thread(fThread); 1288 delete fImageBuf; 1289 } 1290 1291 1292 void 1293 TMagnify::AttachedToWindow() 1294 { 1295 int32 width, height; 1296 fParent->PixelCount(&width, &height); 1297 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid()); 1298 1299 fThread = spawn_thread(TMagnify::MagnifyTask, "MagnifyTask", 1300 B_NORMAL_PRIORITY, this); 1301 1302 resume_thread(fThread); 1303 1304 SetViewColor(B_TRANSPARENT_32_BIT); 1305 MakeFocus(); 1306 } 1307 1308 1309 void 1310 TMagnify::InitBuffers(int32 hPixelCount, int32 vPixelCount, 1311 int32 pixelSize, bool showGrid) 1312 { 1313 color_space colorSpace = BScreen(Window()).ColorSpace(); 1314 1315 BRect r(0, 0, (pixelSize * hPixelCount)-1, (pixelSize * vPixelCount)-1); 1316 if (Bounds().Width() != r.Width() || Bounds().Height() != r.Height()) 1317 ResizeTo(r.Width(), r.Height()); 1318 1319 if (fImageView) { 1320 fImageBuf->Lock(); 1321 fImageView->RemoveSelf(); 1322 fImageBuf->Unlock(); 1323 1324 fImageView->Resize((int32)r.Width(), (int32)r.Height()); 1325 fImageView->SetSpace(colorSpace); 1326 } else 1327 fImageView = new TOSMagnify(r, this, colorSpace); 1328 1329 delete fImageBuf; 1330 fImageBuf = new BBitmap(r, colorSpace, true); 1331 fImageBuf->Lock(); 1332 fImageBuf->AddChild(fImageView); 1333 fImageBuf->Unlock(); 1334 } 1335 1336 1337 void 1338 TMagnify::Draw(BRect) 1339 { 1340 BRect bounds(Bounds()); 1341 DrawBitmap(fImageBuf, bounds, bounds); 1342 static_cast<TWindow*>(Window())->UpdateInfo(); 1343 } 1344 1345 1346 void 1347 TMagnify::KeyDown(const char *key, int32 numBytes) 1348 { 1349 if (!fShowSelection) 1350 BView::KeyDown(key, numBytes); 1351 1352 switch (key[0]) { 1353 case B_TAB: 1354 if (fShowCrossHair1) { 1355 fSelection++; 1356 1357 if (fShowCrossHair2) { 1358 if (fSelection > 2) 1359 fSelection = 0; 1360 } else if (fShowCrossHair1) { 1361 if (fSelection > 1) 1362 fSelection = 0; 1363 } 1364 fNeedToUpdate = true; 1365 Invalidate(); 1366 } 1367 break; 1368 1369 case B_LEFT_ARROW: 1370 MoveSelection(-1,0); 1371 break; 1372 case B_RIGHT_ARROW: 1373 MoveSelection(1,0); 1374 break; 1375 case B_UP_ARROW: 1376 MoveSelection(0,-1); 1377 break; 1378 case B_DOWN_ARROW: 1379 MoveSelection(0,1); 1380 break; 1381 1382 default: 1383 BView::KeyDown(key,numBytes); 1384 break; 1385 } 1386 } 1387 1388 1389 void 1390 TMagnify::FrameResized(float newW, float newH) 1391 { 1392 int32 w, h; 1393 PixelCount(&w, &h); 1394 1395 if (fSelectionLoc.x >= w) 1396 fSelectionLoc.x = 0; 1397 if (fSelectionLoc.y >= h) 1398 fSelectionLoc.y = 0; 1399 1400 if (fShowCrossHair1) { 1401 if (fCrossHair1.x >= w) { 1402 fCrossHair1.x = fSelectionLoc.x + 2; 1403 if (fCrossHair1.x >= w) 1404 fCrossHair1.x = 0; 1405 } 1406 if (fCrossHair1.y >= h) { 1407 fCrossHair1.y = fSelectionLoc.y + 2; 1408 if (fCrossHair1.y >= h) 1409 fCrossHair1.y = 0; 1410 } 1411 1412 if (fShowCrossHair2) { 1413 if (fCrossHair2.x >= w) { 1414 fCrossHair2.x = fCrossHair1.x + 2; 1415 if (fCrossHair2.x >= w) 1416 fCrossHair2.x = 0; 1417 } 1418 if (fCrossHair2.y >= h) { 1419 fCrossHair2.y = fCrossHair1.y + 2; 1420 if (fCrossHair2.y >= h) 1421 fCrossHair2.y = 0; 1422 } 1423 } 1424 } 1425 } 1426 1427 1428 void 1429 TMagnify::MouseDown(BPoint where) 1430 { 1431 BMessage *currentMsg = Window()->CurrentMessage(); 1432 if (currentMsg->what == B_MOUSE_DOWN) { 1433 uint32 buttons = 0; 1434 currentMsg->FindInt32("buttons", (int32 *)&buttons); 1435 1436 uint32 modifiers = 0; 1437 currentMsg->FindInt32("modifiers", (int32 *)&modifiers); 1438 1439 if ((buttons & B_SECONDARY_MOUSE_BUTTON) || (modifiers & B_CONTROL_KEY)) { 1440 // secondary button was clicked or control key was down, show menu and return 1441 1442 BPopUpMenu *menu = new BPopUpMenu(B_TRANSLATE("Info"), false, false); 1443 menu->SetFont(be_plain_font); 1444 BuildInfoMenu(menu); 1445 UpdateInfoMenu(menu, dynamic_cast<TWindow*>(Window())); 1446 BMenuItem *selected = menu->Go(ConvertToScreen(where)); 1447 if (selected) 1448 Window()->PostMessage(selected->Message()->what); 1449 delete menu; 1450 return; 1451 } 1452 1453 // add a mousedown looper here 1454 1455 int32 pixelSize = PixelSize(); 1456 float x = where.x / pixelSize; 1457 float y = where.y / pixelSize; 1458 1459 MoveSelectionTo(x, y); 1460 1461 // draw the frozen image 1462 // update the info region 1463 1464 fNeedToUpdate = true; 1465 Invalidate(); 1466 } 1467 } 1468 1469 1470 void 1471 TMagnify::ScreenChanged(BRect, color_space) 1472 { 1473 int32 width, height; 1474 fParent->PixelCount(&width, &height); 1475 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid()); 1476 } 1477 1478 1479 void 1480 TMagnify::SetSelection(bool state) 1481 { 1482 if (fShowSelection == state) 1483 return; 1484 1485 fShowSelection = state; 1486 fSelection = 0; 1487 Invalidate(); 1488 } 1489 1490 1491 void 1492 TMagnify::MoveSelection(int32 x, int32 y) 1493 { 1494 if (!fShowSelection) 1495 return; 1496 1497 int32 xCount, yCount; 1498 PixelCount(&xCount, &yCount); 1499 1500 float xloc, yloc; 1501 if (fSelection == 0) { 1502 xloc = fSelectionLoc.x; 1503 yloc = fSelectionLoc.y; 1504 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); 1505 fSelectionLoc.x = xloc; 1506 fSelectionLoc.y = yloc; 1507 } else if (fSelection == 1) { 1508 xloc = fCrossHair1.x; 1509 yloc = fCrossHair1.y; 1510 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); 1511 fCrossHair1.x = xloc; 1512 fCrossHair1.y = yloc; 1513 } else if (fSelection == 2) { 1514 xloc = fCrossHair2.x; 1515 yloc = fCrossHair2.y; 1516 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); 1517 fCrossHair2.x = xloc; 1518 fCrossHair2.y = yloc; 1519 } 1520 1521 fNeedToUpdate = true; 1522 Invalidate(); 1523 } 1524 1525 1526 void 1527 TMagnify::MoveSelectionTo(int32 x, int32 y) 1528 { 1529 if (!fShowSelection) 1530 return; 1531 1532 int32 xCount, yCount; 1533 PixelCount(&xCount, &yCount); 1534 if (x >= xCount) 1535 x = 0; 1536 if (y >= yCount) 1537 y = 0; 1538 1539 if (fSelection == 0) { 1540 fSelectionLoc.x = x; 1541 fSelectionLoc.y = y; 1542 } else if (fSelection == 1) { 1543 fCrossHair1.x = x; 1544 fCrossHair1.y = y; 1545 } else if (fSelection == 2) { 1546 fCrossHair2.x = x; 1547 fCrossHair2.y = y; 1548 } 1549 1550 fNeedToUpdate = true; 1551 Invalidate(); //Draw(Bounds()); 1552 } 1553 1554 1555 void 1556 TMagnify::ShowSelection() 1557 { 1558 } 1559 1560 1561 short 1562 TMagnify::Selection() 1563 { 1564 return fSelection; 1565 } 1566 1567 1568 bool 1569 TMagnify::SelectionIsShowing() 1570 { 1571 return fShowSelection; 1572 } 1573 1574 1575 void 1576 TMagnify::SelectionLoc(float* x, float* y) 1577 { 1578 *x = fSelectionLoc.x; 1579 *y = fSelectionLoc.y; 1580 } 1581 1582 1583 void 1584 TMagnify::SetSelectionLoc(float x, float y) 1585 { 1586 fSelectionLoc.x = x; 1587 fSelectionLoc.y = y; 1588 } 1589 1590 1591 rgb_color 1592 TMagnify::SelectionColor() 1593 { 1594 return fImageView->ColorAtSelection(); 1595 } 1596 1597 1598 void 1599 TMagnify::CrossHair1Loc(float* x, float* y) 1600 { 1601 *x = fCrossHair1.x; 1602 *y = fCrossHair1.y; 1603 } 1604 1605 1606 void 1607 TMagnify::CrossHair2Loc(float* x, float* y) 1608 { 1609 *x = fCrossHair2.x; 1610 *y = fCrossHair2.y; 1611 } 1612 1613 1614 BPoint 1615 TMagnify::CrossHair1Loc() 1616 { 1617 return fCrossHair1; 1618 } 1619 1620 1621 BPoint 1622 TMagnify::CrossHair2Loc() 1623 { 1624 return fCrossHair2; 1625 } 1626 1627 1628 void 1629 TMagnify::NudgeMouse(float x, float y) 1630 { 1631 BPoint loc; 1632 uint32 button; 1633 1634 GetMouse(&loc, &button); 1635 ConvertToScreen(&loc); 1636 loc.x += x; 1637 loc.y += y; 1638 1639 set_mouse_position((int32)loc.x, (int32)loc.y); 1640 } 1641 1642 1643 void 1644 TMagnify::WindowActivated(bool active) 1645 { 1646 if (active) 1647 MakeFocus(); 1648 } 1649 1650 1651 status_t 1652 TMagnify::MagnifyTask(void *arg) 1653 { 1654 TMagnify* view = (TMagnify*)arg; 1655 1656 // static data members can't access members, methods without 1657 // a pointer to an instance of the class 1658 TWindow* window = (TWindow*)view->Window(); 1659 1660 while (true) { 1661 if (window->Lock()) { 1662 if (view->NeedToUpdate() || view->Active()) 1663 view->Update(view->NeedToUpdate()); 1664 1665 window->Unlock(); 1666 } 1667 snooze(35000); 1668 } 1669 1670 return B_NO_ERROR; 1671 } 1672 1673 1674 void 1675 TMagnify::Update(bool force) 1676 { 1677 BPoint loc; 1678 uint32 button; 1679 static long counter = 0; 1680 1681 if (!fStickCoordinates) { 1682 GetMouse(&loc, &button); 1683 ConvertToScreen(&loc); 1684 } else 1685 loc = fLastLoc; 1686 1687 if (force || fLastLoc != loc || counter++ % 35 == 0) { 1688 if (fImageView->CreateImage(loc, force)) 1689 Invalidate(); 1690 1691 counter = 0; 1692 if (force) 1693 SetUpdate(false); 1694 } 1695 fLastLoc = loc; 1696 } 1697 1698 1699 bool 1700 TMagnify::NeedToUpdate() 1701 { 1702 return fNeedToUpdate; 1703 } 1704 1705 1706 void 1707 TMagnify::SetUpdate(bool s) 1708 { 1709 fNeedToUpdate = s; 1710 } 1711 1712 1713 void 1714 TMagnify::CopyImage() 1715 { 1716 StartSave(); 1717 be_clipboard->Lock(); 1718 be_clipboard->Clear(); 1719 1720 BMessage *message = be_clipboard->Data(); 1721 if (!message) { 1722 puts(B_TRANSLATE_CONTEXT("no clip msg", 1723 "In console, when clipboard is empty after clicking Copy image")); 1724 return; 1725 } 1726 1727 BMessage *embeddedBitmap = new BMessage(); 1728 (fImageView->Bitmap())->Archive(embeddedBitmap,false); 1729 status_t err = message->AddMessage(kBitmapMimeType, embeddedBitmap); 1730 if (err == B_OK) 1731 err = message->AddRect("rect", fImageView->Bitmap()->Bounds()); 1732 if (err == B_OK) 1733 be_clipboard->Commit(); 1734 1735 be_clipboard->Unlock(); 1736 EndSave(); 1737 } 1738 1739 1740 void 1741 TMagnify::AddCrossHair() 1742 { 1743 if (fShowCrossHair1 && fShowCrossHair2) 1744 return; 1745 1746 int32 w, h; 1747 PixelCount(&w, &h); 1748 1749 if (fShowCrossHair1) { 1750 fSelection = 2; 1751 fShowCrossHair2 = true; 1752 fCrossHair2.x = fCrossHair1.x + 2; 1753 if (fCrossHair2.x >= w) 1754 fCrossHair2.x = 0; 1755 fCrossHair2.y = fCrossHair1.y + 2; 1756 if (fCrossHair2.y >= h) 1757 fCrossHair2.y = 0; 1758 } else { 1759 fSelection = 1; 1760 fShowCrossHair1 = true; 1761 fCrossHair1.x = fSelectionLoc.x + 2; 1762 if (fCrossHair1.x >= w) 1763 fCrossHair1.x = 0; 1764 fCrossHair1.y = fSelectionLoc.y + 2; 1765 if (fCrossHair1.y >= h) 1766 fCrossHair1.y = 0; 1767 } 1768 Invalidate(); 1769 } 1770 1771 1772 void 1773 TMagnify::RemoveCrossHair() 1774 { 1775 if (!fShowCrossHair1 && !fShowCrossHair2) 1776 return; 1777 1778 if (fShowCrossHair2) { 1779 fSelection = 1; 1780 fShowCrossHair2 = false; 1781 } else if (fShowCrossHair1) { 1782 fSelection = 0; 1783 fShowCrossHair1 = false; 1784 } 1785 Invalidate(); 1786 } 1787 1788 1789 void 1790 TMagnify::SetCrossHairsShowing(bool ch1, bool ch2) 1791 { 1792 fShowCrossHair1 = ch1; 1793 fShowCrossHair2 = ch2; 1794 } 1795 1796 1797 void 1798 TMagnify::CrossHairsShowing(bool* ch1, bool* ch2) 1799 { 1800 *ch1 = fShowCrossHair1; 1801 *ch2 = fShowCrossHair2; 1802 } 1803 1804 1805 void 1806 TMagnify::MakeActive(bool s) 1807 { 1808 fActive = s; 1809 } 1810 1811 1812 void 1813 TMagnify::MakeSticked(bool s) 1814 { 1815 fStickCoordinates = s; 1816 } 1817 1818 1819 void 1820 TMagnify::PixelCount(int32* width, int32* height) 1821 { 1822 fParent->PixelCount(width, height); 1823 } 1824 1825 1826 int32 1827 TMagnify::PixelSize() 1828 { 1829 return fParent->PixelSize(); 1830 } 1831 1832 1833 bool 1834 TMagnify::ShowGrid() 1835 { 1836 return fParent->ShowGrid(); 1837 } 1838 1839 1840 void 1841 TMagnify::StartSave() 1842 { 1843 fImageFrozenOnSave = Active(); 1844 if (fImageFrozenOnSave) 1845 MakeActive(false); 1846 } 1847 1848 1849 void 1850 TMagnify::EndSave() 1851 { 1852 if (fImageFrozenOnSave) 1853 MakeActive(true); 1854 } 1855 1856 1857 void 1858 TMagnify::SaveImage(entry_ref* ref, char* name) 1859 { 1860 // create a new file 1861 BFile file; 1862 BDirectory parentDir(ref); 1863 parentDir.CreateFile(name, &file); 1864 1865 // Write the screenshot bitmap to the file 1866 BBitmapStream stream(fImageView->Bitmap()); 1867 BTranslatorRoster* roster = BTranslatorRoster::Default(); 1868 roster->Translate(&stream, NULL, NULL, &file, B_PNG_FORMAT, 1869 B_TRANSLATOR_BITMAP); 1870 1871 BBitmap* bitmap; 1872 stream.DetachBitmap(&bitmap); 1873 // The stream takes over ownership of the bitmap 1874 1875 // unfreeze the image, image was frozen before invoke of FilePanel 1876 EndSave(); 1877 } 1878 1879 // #pragma mark - 1880 1881 1882 TOSMagnify::TOSMagnify(BRect r, TMagnify* parent, color_space space) 1883 : BView(r, "ImageView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS), 1884 fColorSpace(space), fParent(parent) 1885 { 1886 switch (space) { 1887 case B_CMAP8: 1888 fBytesPerPixel = 1; 1889 break; 1890 case B_RGB15: 1891 case B_RGBA15: 1892 case B_RGB15_BIG: 1893 case B_RGBA15_BIG: 1894 case B_RGB16: 1895 case B_RGB16_BIG: 1896 fBytesPerPixel = 2; 1897 break; 1898 case B_RGB24: 1899 fBytesPerPixel = 3; 1900 break; 1901 case B_RGB32: 1902 case B_RGBA32: 1903 case B_RGB32_BIG: 1904 case B_RGBA32_BIG: 1905 fBytesPerPixel = 4; 1906 break; 1907 default: 1908 // uh, oh -- a color space we don't support 1909 fprintf(stderr, "Tried to run in an unsupported color space; exiting\n"); 1910 exit(1); 1911 break; 1912 } 1913 1914 fPixel = NULL; 1915 fBitmap = NULL; 1916 fOldBits = NULL; 1917 InitObject(); 1918 } 1919 1920 1921 TOSMagnify::~TOSMagnify() 1922 { 1923 delete fPixel; 1924 delete fBitmap; 1925 free(fOldBits); 1926 } 1927 1928 1929 void 1930 TOSMagnify::SetSpace(color_space space) 1931 { 1932 fColorSpace = space; 1933 InitObject(); 1934 }; 1935 1936 1937 void 1938 TOSMagnify::InitObject() 1939 { 1940 int32 w, h; 1941 fParent->PixelCount(&w, &h); 1942 1943 delete fBitmap; 1944 BRect bitsRect(0, 0, w-1, h-1); 1945 fBitmap = new BBitmap(bitsRect, fColorSpace); 1946 1947 free(fOldBits); 1948 fOldBits = (char*)malloc(fBitmap->BitsLength()); 1949 1950 if (!fPixel) { 1951 #if B_HOST_IS_BENDIAN 1952 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32_BIG, true); 1953 #else 1954 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32, true); 1955 #endif 1956 fPixelView = new BView(BRect(0,0,0,0), NULL, 0, 0); 1957 fPixel->Lock(); 1958 fPixel->AddChild(fPixelView); 1959 fPixel->Unlock(); 1960 } 1961 } 1962 1963 1964 void 1965 TOSMagnify::FrameResized(float width, float height) 1966 { 1967 BView::FrameResized(width, height); 1968 InitObject(); 1969 } 1970 1971 1972 void 1973 TOSMagnify::Resize(int32 width, int32 height) 1974 { 1975 ResizeTo(width, height); 1976 InitObject(); 1977 } 1978 1979 1980 bool 1981 TOSMagnify::CreateImage(BPoint mouseLoc, bool force) 1982 { 1983 bool created = false; 1984 if (Window() && Window()->Lock()) { 1985 int32 width, height; 1986 fParent->PixelCount(&width, &height); 1987 int32 pixelSize = fParent->PixelSize(); 1988 1989 BRect srcRect(0, 0, width - 1, height - 1); 1990 srcRect.OffsetBy(mouseLoc.x - (width / 2), 1991 mouseLoc.y - (height / 2)); 1992 1993 if (force || CopyScreenRect(srcRect)) { 1994 srcRect.OffsetTo(BPoint(0, 0)); 1995 BRect destRect(Bounds()); 1996 1997 DrawBitmap(fBitmap, srcRect, destRect); 1998 1999 DrawGrid(width, height, destRect, pixelSize); 2000 DrawSelection(); 2001 2002 Sync(); 2003 created = true; 2004 } 2005 Window()->Unlock(); 2006 } else 2007 puts("window problem"); 2008 2009 return created; 2010 } 2011 2012 2013 bool 2014 TOSMagnify::CopyScreenRect(BRect srcRect) 2015 { 2016 // constrain src rect to legal screen rect 2017 BScreen screen(Window()); 2018 BRect scrnframe = screen.Frame(); 2019 2020 if (srcRect.right > scrnframe.right) 2021 srcRect.OffsetTo(scrnframe.right - srcRect.Width(), srcRect.top); 2022 if (srcRect.top < 0) 2023 srcRect.OffsetTo(srcRect.left, 0); 2024 2025 if (srcRect.bottom > scrnframe.bottom) 2026 srcRect.OffsetTo(srcRect.left, scrnframe.bottom - srcRect.Height()); 2027 if (srcRect.left < 0) 2028 srcRect.OffsetTo(0, srcRect.top); 2029 2030 // save a copy of the bits for comparison later 2031 memcpy(fOldBits, fBitmap->Bits(), fBitmap->BitsLength()); 2032 2033 screen.ReadBitmap(fBitmap, false, &srcRect); 2034 2035 // let caller know whether bits have actually changed 2036 return memcmp(fBitmap->Bits(), fOldBits, fBitmap->BitsLength()) != 0; 2037 } 2038 2039 2040 void 2041 TOSMagnify::DrawGrid(int32 width, int32 height, BRect destRect, int32 pixelSize) 2042 { 2043 // draw grid 2044 if (fParent->ShowGrid() && fParent->PixelSize() > 2) { 2045 BeginLineArray(width * height); 2046 2047 // horizontal lines 2048 for (int32 i = pixelSize; i < (height * pixelSize); i += pixelSize) 2049 AddLine(BPoint(0, i), BPoint(destRect.right, i), kGridGray); 2050 2051 // vertical lines 2052 for (int32 i = pixelSize; i < (width * pixelSize); i += pixelSize) 2053 AddLine(BPoint(i, 0), BPoint(i, destRect.bottom), kGridGray); 2054 2055 EndLineArray(); 2056 } 2057 2058 SetHighColor(kGridGray); 2059 StrokeRect(destRect); 2060 } 2061 2062 2063 void 2064 TOSMagnify::DrawSelection() 2065 { 2066 if (!fParent->SelectionIsShowing()) 2067 return; 2068 2069 float x, y; 2070 int32 pixelSize = fParent->PixelSize(); 2071 int32 squareSize = pixelSize - 2; 2072 2073 fParent->SelectionLoc(&x, &y); 2074 x *= pixelSize; x++; 2075 y *= pixelSize; y++; 2076 BRect selRect(x, y, x+squareSize, y+squareSize); 2077 2078 short selection = fParent->Selection(); 2079 2080 PushState(); 2081 SetLowColor(ViewColor()); 2082 SetHighColor(kRedColor); 2083 StrokeRect(selRect); 2084 if (selection == 0) { 2085 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); 2086 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); 2087 } 2088 2089 bool ch1Showing, ch2Showing; 2090 fParent->CrossHairsShowing(&ch1Showing, &ch2Showing); 2091 if (ch1Showing) { 2092 SetHighColor(kBlueColor); 2093 fParent->CrossHair1Loc(&x, &y); 2094 x *= pixelSize; x++; 2095 y *= pixelSize; y++; 2096 selRect.Set(x, y,x+squareSize, y+squareSize); 2097 StrokeRect(selRect); 2098 BeginLineArray(4); 2099 AddLine(BPoint(0, y+(squareSize/2)), 2100 BPoint(x, y+(squareSize/2)), kBlueColor); // left 2101 AddLine(BPoint(x+squareSize,y+(squareSize/2)), 2102 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right 2103 AddLine(BPoint(x+(squareSize/2), 0), 2104 BPoint(x+(squareSize/2), y), kBlueColor); // top 2105 AddLine(BPoint(x+(squareSize/2), y+squareSize), 2106 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom 2107 EndLineArray(); 2108 if (selection == 1) { 2109 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); 2110 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); 2111 } 2112 } 2113 if (ch2Showing) { 2114 SetHighColor(kBlueColor); 2115 fParent->CrossHair2Loc(&x, &y); 2116 x *= pixelSize; x++; 2117 y *= pixelSize; y++; 2118 selRect.Set(x, y,x+squareSize, y+squareSize); 2119 StrokeRect(selRect); 2120 BeginLineArray(4); 2121 AddLine(BPoint(0, y+(squareSize/2)), 2122 BPoint(x, y+(squareSize/2)), kBlueColor); // left 2123 AddLine(BPoint(x+squareSize,y+(squareSize/2)), 2124 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right 2125 AddLine(BPoint(x+(squareSize/2), 0), 2126 BPoint(x+(squareSize/2), y), kBlueColor); // top 2127 AddLine(BPoint(x+(squareSize/2), y+squareSize), 2128 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom 2129 EndLineArray(); 2130 if (selection == 2) { 2131 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); 2132 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); 2133 } 2134 } 2135 2136 PopState(); 2137 } 2138 2139 2140 rgb_color 2141 TOSMagnify::ColorAtSelection() 2142 { 2143 float x, y; 2144 fParent->SelectionLoc(&x, &y); 2145 BRect srcRect(x, y, x, y); 2146 BRect dstRect(0, 0, 0, 0); 2147 fPixel->Lock(); 2148 fPixelView->DrawBitmap(fBitmap, srcRect, dstRect); 2149 fPixelView->Sync(); 2150 fPixel->Unlock(); 2151 2152 uint32 pixel = *((uint32*)fPixel->Bits()); 2153 rgb_color c; 2154 c.alpha = pixel >> 24; 2155 c.red = (pixel >> 16) & 0xFF; 2156 c.green = (pixel >> 8) & 0xFF; 2157 c.blue = pixel & 0xFF; 2158 2159 return c; 2160 } 2161 2162 2163 // #pragma mark - 2164 2165 2166 int 2167 main(int argc, char* argv[]) 2168 { 2169 int32 pixelCount = -1; 2170 2171 if (argc > 2) { 2172 puts(B_TRANSLATE_CONTEXT( 2173 "usage: magnify [size] (magnify size * size pixels)", 2174 "Console")); 2175 exit(1); 2176 } else { 2177 if (argc == 2) { 2178 pixelCount = abs(atoi(argv[1])); 2179 2180 if ((pixelCount > 100) || (pixelCount < 4)) { 2181 puts(B_TRANSLATE_CONTEXT( 2182 "usage: magnify [size] (magnify size * size pixels)", 2183 "Console")); 2184 puts(B_TRANSLATE_CONTEXT( 2185 " size must be > 4 and a multiple of 4", 2186 "Console")); 2187 exit(1); 2188 } 2189 2190 if (pixelCount % 4) { 2191 puts(B_TRANSLATE_CONTEXT( 2192 "magnify: size must be a multiple of 4", 2193 "Console")); 2194 exit(1); 2195 } 2196 } 2197 } 2198 2199 TApp app(pixelCount); 2200 app.Run(); 2201 return 0; 2202 } 2203