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 49const int32 msg_update_info = 'info'; 50const int32 msg_show_info = 'show'; 51const int32 msg_toggle_grid = 'grid'; 52const int32 msg_shrink = 'shnk'; 53const int32 msg_grow = 'grow'; 54const int32 msg_make_square = 'sqar'; 55const int32 msg_shrink_pixel = 'pshk'; 56const int32 msg_grow_pixel = 'pgrw'; 57 58const int32 msg_mouse_left = 'mslf'; 59const int32 msg_mouse_right = 'msrt'; 60const int32 msg_mouse_up = 'msup'; 61const int32 msg_mouse_down = 'msdn'; 62 63const int32 msg_new_color = 'colr'; 64const int32 msg_toggle_ruler = 'rulr'; 65const int32 msg_copy_image = 'copy'; 66const int32 msg_track_color = 'trak'; 67const int32 msg_freeze = 'frez'; 68const int32 msg_stick = 'stic'; 69const int32 msg_dump = 'dump'; 70const int32 msg_add_cross_hair = 'acrs'; 71const int32 msg_remove_cross_hair = 'rcrs'; 72const int32 msg_save = 'save'; 73 74const rgb_color kViewGray = { 216, 216, 216, 255}; 75const rgb_color kGridGray = {130, 130, 130, 255 }; 76const rgb_color kWhite = { 255, 255, 255, 255}; 77const rgb_color kBlack = { 0, 0, 0, 255}; 78const rgb_color kDarkGray = { 96, 96, 96, 255}; 79const rgb_color kRedColor = { 255, 10, 50, 255 }; 80const rgb_color kGreenColor = { 10, 255, 50, 255 }; 81const rgb_color kBlueColor = { 10, 50, 255, 255 }; 82 83const char* const kBitmapMimeType = "image/x-vnd.Be-bitmap"; 84 85const float kCurrentVersion = 1.2; 86const 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 95const char* const kAppName = "Magnify"; 96const bool kDefaultShowGrid = true; 97const bool kDefaultShowInfo = true; 98const int32 kDefaultPixelCount = 32; 99const int32 kDefaultPixelSize = 8; 100 101// each info region will be: 102// top-bottom: 5 fontheight 5 fontheight 5 103// left-right: 10 minwindowwidth 10 104const int32 kBorderSize = 10; 105 106 107static 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 141static float 142FontHeight(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 155static void 156BoundsSelection(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 174static void 175BuildInfoMenu(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 228static void 229UpdateInfoMenu(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 287TApp::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 298TWindow::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 346TWindow::~TWindow() 347{ 348} 349 350 351status_t 352TWindow::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 363BHandler* 364TWindow::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 375void 376TWindow::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 586bool 587TWindow::QuitRequested() 588{ 589 SetPrefs(); 590 be_app->PostMessage(B_QUIT_REQUESTED); 591 return true; 592} 593 594 595void 596TWindow::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 663ALMOST_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 677DONE: 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 687void 688TWindow::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 723void 724TWindow::FrameResized(float w, float h) 725{ 726 CalcViewablePixels(); 727 fFatBits->InitBuffers(fHPixelCount, fVPixelCount, fPixelSize, ShowGrid()); 728 UpdateInfoBarOnResize(); 729} 730 731 732void 733TWindow::ScreenChanged(BRect screenSize, color_space depth) 734{ 735 BWindow::ScreenChanged(screenSize, depth); 736 // reset all bitmaps 737 fFatBits->ScreenChanged(screenSize,depth); 738} 739 740 741void 742TWindow::Minimize(bool m) 743{ 744 BWindow::Minimize(m); 745} 746 747 748void 749TWindow::Zoom(BPoint /*position*/, float /*width*/, float /*height*/) 750{ 751 if (fFatBits->Active()) 752 ShowInfo(!fShowInfo); 753} 754 755 756void 757TWindow::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 784void 785TWindow::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 803void 804TWindow::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 816void 817TWindow::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 840void 841TWindow::SetGrid(bool s) 842{ 843 if (s == fShowGrid) 844 return; 845 846 fShowGrid = s; 847 fFatBits->SetUpdate(true); 848} 849 850 851bool 852TWindow::ShowGrid() 853{ 854 return fShowGrid; 855} 856 857 858void 859TWindow::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 879bool 880TWindow::InfoIsShowing() 881{ 882 return fShowInfo; 883} 884 885 886bool 887TWindow::InfoBarIsVisible() 888{ 889 return fInfoBarState; 890} 891 892 893void 894TWindow::UpdateInfo() 895{ 896 fInfo->Invalidate(); 897} 898 899 900void 901TWindow::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 915void 916TWindow::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 929void 930TWindow::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 943void 944TWindow::CrossHairsShowing(bool* ch1, bool* ch2) 945{ 946 fFatBits->CrossHairsShowing(ch1, ch2); 947} 948 949 950void 951TWindow::PixelCount(int32* h, int32 *v) 952{ 953 *h = fHPixelCount; 954 *v = fVPixelCount; 955} 956 957 958void 959TWindow::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 986void 987TWindow::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 1011int32 1012TWindow::PixelSize() 1013{ 1014 return fPixelSize; 1015} 1016 1017 1018#undef B_TRANSLATION_CONTEXT 1019#define B_TRANSLATION_CONTEXT "Magnify-Main" 1020 1021 1022bool 1023TWindow::IsActive() 1024{ 1025 return fFatBits->Active(); 1026} 1027 1028 1029bool 1030TWindow::IsSticked() 1031{ 1032 return fFatBits->Sticked(); 1033} 1034 1035 1036// #pragma mark - 1037 1038 1039TInfoView::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 1060TInfoView::~TInfoView() 1061{ 1062} 1063 1064 1065void 1066TInfoView::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 1077void 1078TInfoView::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 1178void 1179TInfoView::FrameResized(float width, float height) 1180{ 1181 BBox::FrameResized(width, height); 1182} 1183 1184 1185void 1186TInfoView::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 1198void 1199TInfoView::SetMagView(TMagnify* magView) 1200{ 1201 fMagView = magView; 1202} 1203 1204 1205// #pragma mark - 1206 1207 1208TMenu::TMenu(TWindow *mainWindow, const char *title, menu_layout layout) 1209 : BMenu(title, layout), 1210 fMainWindow(mainWindow) 1211{ 1212} 1213 1214 1215TMenu::~TMenu() 1216{ 1217} 1218 1219 1220void 1221TMenu::AttachedToWindow() 1222{ 1223 UpdateInfoMenu(this, fMainWindow); 1224 1225 BMenu::AttachedToWindow(); 1226} 1227 1228 1229void 1230TInfoView::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 1246bool 1247TInfoView::IsInfoTextVisible() 1248{ 1249 return fInfoTextVisible; 1250} 1251 1252 1253void 1254TInfoView::SetInfoTextVisible(bool visible) 1255{ 1256 fInfoTextVisible = visible; 1257 Draw(Bounds()); 1258} 1259 1260 1261// #pragma mark - 1262 1263 1264TMagnify::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 1285TMagnify::~TMagnify() 1286{ 1287 kill_thread(fThread); 1288 delete fImageBuf; 1289} 1290 1291 1292void 1293TMagnify::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 1309void 1310TMagnify::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 1337void 1338TMagnify::Draw(BRect) 1339{ 1340 BRect bounds(Bounds()); 1341 DrawBitmap(fImageBuf, bounds, bounds); 1342 static_cast<TWindow*>(Window())->UpdateInfo(); 1343} 1344 1345 1346void 1347TMagnify::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 1389void 1390TMagnify::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 1428void 1429TMagnify::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 1470void 1471TMagnify::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 1479void 1480TMagnify::SetSelection(bool state) 1481{ 1482 if (fShowSelection == state) 1483 return; 1484 1485 fShowSelection = state; 1486 fSelection = 0; 1487 Invalidate(); 1488} 1489 1490 1491void 1492TMagnify::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 1526void 1527TMagnify::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 1555void 1556TMagnify::ShowSelection() 1557{ 1558} 1559 1560 1561short 1562TMagnify::Selection() 1563{ 1564 return fSelection; 1565} 1566 1567 1568bool 1569TMagnify::SelectionIsShowing() 1570{ 1571 return fShowSelection; 1572} 1573 1574 1575void 1576TMagnify::SelectionLoc(float* x, float* y) 1577{ 1578 *x = fSelectionLoc.x; 1579 *y = fSelectionLoc.y; 1580} 1581 1582 1583void 1584TMagnify::SetSelectionLoc(float x, float y) 1585{ 1586 fSelectionLoc.x = x; 1587 fSelectionLoc.y = y; 1588} 1589 1590 1591rgb_color 1592TMagnify::SelectionColor() 1593{ 1594 return fImageView->ColorAtSelection(); 1595} 1596 1597 1598void 1599TMagnify::CrossHair1Loc(float* x, float* y) 1600{ 1601 *x = fCrossHair1.x; 1602 *y = fCrossHair1.y; 1603} 1604 1605 1606void 1607TMagnify::CrossHair2Loc(float* x, float* y) 1608{ 1609 *x = fCrossHair2.x; 1610 *y = fCrossHair2.y; 1611} 1612 1613 1614BPoint 1615TMagnify::CrossHair1Loc() 1616{ 1617 return fCrossHair1; 1618} 1619 1620 1621BPoint 1622TMagnify::CrossHair2Loc() 1623{ 1624 return fCrossHair2; 1625} 1626 1627 1628void 1629TMagnify::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 1643void 1644TMagnify::WindowActivated(bool active) 1645{ 1646 if (active) 1647 MakeFocus(); 1648} 1649 1650 1651status_t 1652TMagnify::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 1674void 1675TMagnify::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 1699bool 1700TMagnify::NeedToUpdate() 1701{ 1702 return fNeedToUpdate; 1703} 1704 1705 1706void 1707TMagnify::SetUpdate(bool s) 1708{ 1709 fNeedToUpdate = s; 1710} 1711 1712 1713void 1714TMagnify::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 1740void 1741TMagnify::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 1772void 1773TMagnify::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 1789void 1790TMagnify::SetCrossHairsShowing(bool ch1, bool ch2) 1791{ 1792 fShowCrossHair1 = ch1; 1793 fShowCrossHair2 = ch2; 1794} 1795 1796 1797void 1798TMagnify::CrossHairsShowing(bool* ch1, bool* ch2) 1799{ 1800 *ch1 = fShowCrossHair1; 1801 *ch2 = fShowCrossHair2; 1802} 1803 1804 1805void 1806TMagnify::MakeActive(bool s) 1807{ 1808 fActive = s; 1809} 1810 1811 1812void 1813TMagnify::MakeSticked(bool s) 1814{ 1815 fStickCoordinates = s; 1816} 1817 1818 1819void 1820TMagnify::PixelCount(int32* width, int32* height) 1821{ 1822 fParent->PixelCount(width, height); 1823} 1824 1825 1826int32 1827TMagnify::PixelSize() 1828{ 1829 return fParent->PixelSize(); 1830} 1831 1832 1833bool 1834TMagnify::ShowGrid() 1835{ 1836 return fParent->ShowGrid(); 1837} 1838 1839 1840void 1841TMagnify::StartSave() 1842{ 1843 fImageFrozenOnSave = Active(); 1844 if (fImageFrozenOnSave) 1845 MakeActive(false); 1846} 1847 1848 1849void 1850TMagnify::EndSave() 1851{ 1852 if (fImageFrozenOnSave) 1853 MakeActive(true); 1854} 1855 1856 1857void 1858TMagnify::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 1882TOSMagnify::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 1921TOSMagnify::~TOSMagnify() 1922{ 1923 delete fPixel; 1924 delete fBitmap; 1925 free(fOldBits); 1926} 1927 1928 1929void 1930TOSMagnify::SetSpace(color_space space) 1931{ 1932 fColorSpace = space; 1933 InitObject(); 1934}; 1935 1936 1937void 1938TOSMagnify::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 1964void 1965TOSMagnify::FrameResized(float width, float height) 1966{ 1967 BView::FrameResized(width, height); 1968 InitObject(); 1969} 1970 1971 1972void 1973TOSMagnify::Resize(int32 width, int32 height) 1974{ 1975 ResizeTo(width, height); 1976 InitObject(); 1977} 1978 1979 1980bool 1981TOSMagnify::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 2013bool 2014TOSMagnify::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 2040void 2041TOSMagnify::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 2063void 2064TOSMagnify::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 2140rgb_color 2141TOSMagnify::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 2166int 2167main(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