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