RemoteDrawingEngine: Reduce RP_READ_BITMAP result timeout.
[haiku.git] / src / apps / magnify / Magnify.cpp
blob02d7c4a7311566bc8ea3105929fab82913486e19
1 /*
2 * Copyright 2002-2009, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Updated by Sikosis (beos@gravity24hr.com)
7 * Copyright 1999, Be Incorporated. All Rights Reserved.
8 * This file may be used under the terms of the Be Sample Code License.
9 */
11 #include "Magnify.h"
13 #include <Alert.h>
14 #include <Bitmap.h>
15 #include <BitmapStream.h>
16 #include <Catalog.h>
17 #include <Clipboard.h>
18 #include <Debug.h>
19 #include <Directory.h>
20 #include <File.h>
21 #include <FindDirectory.h>
22 #include <Locale.h>
23 #include <MenuItem.h>
24 #include <MenuField.h>
25 #include <MessageFormat.h>
26 #include <NodeInfo.h>
27 #include <Path.h>
28 #include <PopUpMenu.h>
29 #include <Screen.h>
30 #include <ScrollView.h>
31 #include <TextView.h>
32 #include <TranslationUtils.h>
33 #include <TranslatorRoster.h>
34 #include <WindowScreen.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <sys/stat.h>
44 #undef B_TRANSLATION_CONTEXT
45 #define B_TRANSLATION_CONTEXT "Magnify-Main"
48 const int32 msg_update_info = 'info';
49 const int32 msg_show_info = 'show';
50 const int32 msg_toggle_grid = 'grid';
51 const int32 msg_shrink = 'shnk';
52 const int32 msg_grow = 'grow';
53 const int32 msg_make_square = 'sqar';
54 const int32 msg_shrink_pixel = 'pshk';
55 const int32 msg_grow_pixel = 'pgrw';
57 const int32 msg_new_color = 'colr';
58 const int32 msg_toggle_ruler = 'rulr';
59 const int32 msg_copy_image = 'copy';
60 const int32 msg_track_color = 'trak';
61 const int32 msg_freeze = 'frez';
62 const int32 msg_stick = 'stic';
63 const int32 msg_dump = 'dump';
64 const int32 msg_add_cross_hair = 'acrs';
65 const int32 msg_remove_cross_hair = 'rcrs';
66 const int32 msg_save = 'save';
68 const rgb_color kViewGray = { 216, 216, 216, 255};
69 const rgb_color kGridGray = {130, 130, 130, 255 };
70 const rgb_color kWhite = { 255, 255, 255, 255};
71 const rgb_color kBlack = { 0, 0, 0, 255};
72 const rgb_color kDarkGray = { 96, 96, 96, 255};
73 const rgb_color kRedColor = { 255, 10, 50, 255 };
74 const rgb_color kGreenColor = { 10, 255, 50, 255 };
75 const rgb_color kBlueColor = { 10, 50, 255, 255 };
77 const char* const kBitmapMimeType = "image/x-vnd.Be-bitmap";
79 const float kCurrentVersion = 1.2;
80 const char *kPrefsFileName = "Magnify_prefs";
82 // prefs are:
83 // name = Magnify
84 // version
85 // show grid
86 // show info (rgb, location)
87 // pixel count
88 // pixel size
89 const char* const kAppName = "Magnify";
90 const bool kDefaultShowGrid = true;
91 const bool kDefaultShowInfo = true;
92 const int32 kDefaultPixelCount = 32;
93 const int32 kDefaultPixelSize = 8;
95 // each info region will be:
96 // top-bottom: 5 fontheight 5 fontheight 5
97 // left-right: 10 minwindowwidth 10
98 const int32 kBorderSize = 10;
101 static float
102 FontHeight(BView* target, bool full)
104 font_height finfo;
105 target->GetFontHeight(&finfo);
106 float h = ceil(finfo.ascent) + ceil(finfo.descent);
108 if (full)
109 h += ceil(finfo.leading);
111 return h;
115 static void
116 BoundsSelection(int32 incX, int32 incY, float* x, float* y,
117 int32 xCount, int32 yCount)
119 *x += incX;
120 *y += incY;
122 if (*x < 0)
123 *x = xCount-1;
124 if (*x >= xCount)
125 *x = 0;
127 if (*y < 0)
128 *y = yCount-1;
129 if (*y >= yCount)
130 *y = 0;
134 static void
135 BuildInfoMenu(BMenu *menu)
137 BMenuItem* menuItem;
138 menuItem = new BMenuItem(B_TRANSLATE("Save image"),
139 new BMessage(msg_save), 'S');
140 menu->AddItem(menuItem);
141 // menuItem = new BMenuItem(B_TRANSLATE("Save selection"),
142 // new BMessage(msg_save), 'S');
143 // menu->AddItem(menuItem);
144 menuItem = new BMenuItem(B_TRANSLATE("Copy image"),
145 new BMessage(msg_copy_image), 'C');
146 menu->AddItem(menuItem);
147 menu->AddSeparatorItem();
149 menuItem = new BMenuItem(B_TRANSLATE("Show info"),
150 new BMessage(msg_show_info), 'T');
151 menu->AddItem(menuItem);
152 menuItem = new BMenuItem(B_TRANSLATE("Add a crosshair"),
153 new BMessage(msg_add_cross_hair), 'H');
154 menu->AddItem(menuItem);
155 menuItem = new BMenuItem(B_TRANSLATE("Remove a crosshair"),
156 new BMessage(msg_remove_cross_hair), 'H', B_SHIFT_KEY);
157 menu->AddItem(menuItem);
158 menuItem = new BMenuItem(B_TRANSLATE("Show grid"),
159 new BMessage(msg_toggle_grid), 'G');
160 menu->AddItem(menuItem);
161 menu->AddSeparatorItem();
163 menuItem = new BMenuItem(B_TRANSLATE("Freeze image"),
164 new BMessage(msg_freeze), 'F');
165 menu->AddItem(menuItem);
166 menuItem = new BMenuItem(B_TRANSLATE("Stick coordinates"),
167 new BMessage(msg_stick), 'I');
168 menu->AddItem(menuItem);
169 menu->AddSeparatorItem();
171 menuItem = new BMenuItem(B_TRANSLATE("Make square"),
172 new BMessage(msg_make_square), '/');
173 menu->AddItem(menuItem);
174 menuItem = new BMenuItem(B_TRANSLATE("Decrease window size"),
175 new BMessage(msg_shrink), '-');
176 menu->AddItem(menuItem);
177 menuItem = new BMenuItem(B_TRANSLATE("Increase window size"),
178 new BMessage(msg_grow), '+');
179 menu->AddItem(menuItem);
180 menuItem = new BMenuItem(B_TRANSLATE("Decrease pixel size"),
181 new BMessage(msg_shrink_pixel), ',');
182 menu->AddItem(menuItem);
183 menuItem = new BMenuItem(B_TRANSLATE("Increase pixel size"),
184 new BMessage(msg_grow_pixel), '.');
185 menu->AddItem(menuItem);
188 static void
189 UpdateInfoMenu(BMenu *menu, TWindow *window)
191 bool state = true;
192 bool showGrid = true;
193 bool infoBarIsVisible = true;
194 bool stickCordinates = true;
195 if (window) {
196 state = window->IsActive();
197 showGrid = window->ShowGrid();
198 infoBarIsVisible = window->InfoBarIsVisible();
199 stickCordinates = window->IsSticked();
201 BMenuItem* menuItem = menu->FindItem(B_TRANSLATE("Show info"));
202 if (menuItem) {
203 menuItem->SetEnabled(state);
204 menuItem->SetMarked(infoBarIsVisible);
206 menuItem = menu->FindItem(B_TRANSLATE("Add a crosshair"));
207 if (menuItem)
208 menuItem->SetEnabled(state);
209 menuItem = menu->FindItem(B_TRANSLATE("Remove a crosshair"));
210 if (menuItem)
211 menuItem->SetEnabled(state);
212 menuItem = menu->FindItem(B_TRANSLATE("Show grid"));
213 if (menuItem) {
214 menuItem->SetEnabled(state);
215 menuItem->SetMarked(showGrid);
217 menuItem = menu->FindItem(B_TRANSLATE("Freeze image"));
218 if (menuItem) {
219 menuItem->SetMarked(!state);
221 menuItem = menu->FindItem(B_TRANSLATE("Stick coordinates"));
222 if (menuItem) {
223 menuItem->SetMarked(stickCordinates);
225 menuItem = menu->FindItem(B_TRANSLATE("Make square"));
226 if (menuItem)
227 menuItem->SetEnabled(state);
228 menuItem = menu->FindItem(B_TRANSLATE("Decrease window size"));
229 if (menuItem)
230 menuItem->SetEnabled(state);
231 menuItem = menu->FindItem(B_TRANSLATE("Increase window size"));
232 if (menuItem)
233 menuItem->SetEnabled(state);
234 menuItem = menu->FindItem(B_TRANSLATE("Decrease pixel size"));
235 if (menuItem)
236 menuItem->SetEnabled(state);
237 menuItem = menu->FindItem(B_TRANSLATE("Increase pixel size"));
238 if (menuItem)
239 menuItem->SetEnabled(state);
242 // #pragma mark -
245 // pass in pixelCount to maintain backward compatibility of setting
246 // the pixelcount from the command line
247 TApp::TApp(int32 pixelCount)
248 : BApplication("application/x-vnd.Haiku-Magnify")
250 TWindow* magWindow = new TWindow(pixelCount);
251 magWindow->Show();
255 // #pragma mark -
258 TWindow::TWindow(int32 pixelCount)
260 BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Magnify"),
261 B_TITLED_WINDOW, B_OUTLINE_RESIZE)
263 GetPrefs(pixelCount);
265 // add info view
266 BRect infoRect(Bounds());
267 infoRect.InsetBy(-1, -1);
268 fInfo = new TInfoView(infoRect);
269 AddChild(fInfo);
271 fFontHeight = FontHeight(fInfo, true);
272 fInfoHeight = (fFontHeight * 2) + (3 * 5);
274 BRect fbRect(0, 0, (fHPixelCount*fPixelSize), (fHPixelCount*fPixelSize));
275 if (InfoIsShowing())
276 fbRect.OffsetBy(10, fInfoHeight);
277 fFatBits = new TMagnify(fbRect, this);
278 fInfo->AddChild(fFatBits);
280 fFatBits->SetSelection(fShowInfo);
281 fInfo->SetMagView(fFatBits);
283 ResizeWindow(fHPixelCount, fVPixelCount);
284 UpdateInfoBarOnResize();
286 AddShortcut('S', B_COMMAND_KEY, new BMessage(msg_save));
287 AddShortcut('C', B_COMMAND_KEY, new BMessage(msg_copy_image));
288 AddShortcut('T', B_COMMAND_KEY, new BMessage(msg_show_info));
289 AddShortcut('H', B_COMMAND_KEY, new BMessage(msg_add_cross_hair));
290 AddShortcut('H', B_SHIFT_KEY, new BMessage(msg_remove_cross_hair));
291 AddShortcut('G', B_COMMAND_KEY, new BMessage(msg_toggle_grid));
292 AddShortcut('F', B_COMMAND_KEY, new BMessage(msg_freeze));
293 AddShortcut('I', B_COMMAND_KEY, new BMessage(msg_stick));
294 AddShortcut('-', B_COMMAND_KEY, new BMessage(msg_shrink));
295 AddShortcut('=', B_COMMAND_KEY, new BMessage(msg_grow));
296 AddShortcut('/', B_COMMAND_KEY, new BMessage(msg_make_square));
297 AddShortcut(',', B_COMMAND_KEY, new BMessage(msg_shrink_pixel));
298 AddShortcut('.', B_COMMAND_KEY, new BMessage(msg_grow_pixel));
302 TWindow::~TWindow()
307 void
308 TWindow::MessageReceived(BMessage* m)
310 bool active = fFatBits->Active();
312 switch (m->what) {
313 case msg_show_info:
314 if (active) {
315 fInfoBarState = !fInfoBarState;
316 ShowInfo(!fShowInfo);
318 break;
320 case msg_toggle_grid:
321 if (active)
322 SetGrid(!fShowGrid);
323 break;
325 case msg_grow:
326 if (active)
327 ResizeWindow(true);
328 break;
329 case msg_shrink:
330 if (active)
331 ResizeWindow(false);
332 break;
333 case msg_make_square:
334 if (active) {
335 if (fHPixelCount == fVPixelCount)
336 break;
337 int32 big = (fHPixelCount > fVPixelCount) ? fHPixelCount : fVPixelCount;
338 ResizeWindow(big, big);
340 break;
342 case msg_shrink_pixel:
343 if (active)
344 SetPixelSize(false);
345 break;
346 case msg_grow_pixel:
347 if (active)
348 SetPixelSize(true);
349 break;
351 case msg_add_cross_hair:
352 if (active && fShowInfo)
353 AddCrossHair();
354 break;
355 case msg_remove_cross_hair:
356 if (active && fShowInfo)
357 RemoveCrossHair();
358 break;
360 case msg_freeze:
361 if (active)
362 SetFlags(B_OUTLINE_RESIZE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE);
363 else
364 SetFlags(B_OUTLINE_RESIZE | B_NOT_ZOOMABLE);
366 fFatBits->MakeActive(!fFatBits->Active());
367 break;
369 case msg_stick:
370 fFatBits->MakeSticked(!fFatBits->Sticked());
371 break;
373 case msg_save: {
374 // freeze the image here, unfreeze after dump or cancel
375 fFatBits->StartSave();
377 BMessenger messenger(this);
378 BMessage message(msg_dump);
379 fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, 0, 0, false,
380 &message);
381 fSavePanel->SetSaveText("Bitmaps.png");
382 fSavePanel->Show();
383 } break;
384 case msg_dump:
386 delete fSavePanel;
388 entry_ref dirRef;
389 char* name;
390 m->FindRef("directory", &dirRef);
391 m->FindString((const char*)"name",(const char**) &name);
393 fFatBits->SaveImage(&dirRef, name);
395 break;
396 case B_CANCEL:
397 // image is frozen before the FilePanel is shown
398 fFatBits->EndSave();
399 break;
401 case msg_copy_image:
402 fFatBits->CopyImage();
403 break;
404 default:
405 BWindow::MessageReceived(m);
406 break;
411 bool
412 TWindow::QuitRequested()
414 SetPrefs();
415 be_app->PostMessage(B_QUIT_REQUESTED);
416 return true;
420 void
421 TWindow::GetPrefs(int32 overridePixelCount)
423 BPath path;
424 char name[8];
425 float version;
426 bool haveLoc=false;
427 BPoint loc;
428 bool showGrid = kDefaultShowGrid;
429 bool showInfo = kDefaultShowInfo;
430 bool ch1Showing=false;
431 bool ch2Showing=false;
432 int32 hPixelCount = kDefaultPixelCount;
433 int32 vPixelCount = kDefaultPixelCount;
434 int32 pixelSize = kDefaultPixelSize;
436 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
437 int ref = -1;
438 path.Append(kPrefsFileName);
439 if ((ref = open(path.Path(), 0)) >= 0) {
440 if (read(ref, name, 7) != 7)
441 goto ALMOST_DONE;
443 name[7] = 0;
444 if (strcmp(name, kAppName) != 0)
445 goto ALMOST_DONE;
447 read(ref, &version, sizeof(float));
449 if (read(ref, &loc, sizeof(BPoint)) != sizeof(BPoint))
450 goto ALMOST_DONE;
451 else
452 haveLoc = true;
454 if (read(ref, &showGrid, sizeof(bool)) != sizeof(bool)) {
455 showGrid = kDefaultShowGrid;
456 goto ALMOST_DONE;
459 if (read(ref, &showInfo, sizeof(bool)) != sizeof(bool)) {
460 showInfo = kDefaultShowInfo;
461 goto ALMOST_DONE;
464 if (read(ref, &ch1Showing, sizeof(bool)) != sizeof(bool)) {
465 ch1Showing = false;
466 goto ALMOST_DONE;
469 if (read(ref, &ch2Showing, sizeof(bool)) != sizeof(bool)) {
470 ch2Showing = false;
471 goto ALMOST_DONE;
474 if (read(ref, &hPixelCount, sizeof(int32)) != sizeof(int32)) {
475 hPixelCount = kDefaultPixelCount;
476 goto ALMOST_DONE;
478 if (read(ref, &vPixelCount, sizeof(int32)) != sizeof(int32)) {
479 vPixelCount = kDefaultPixelCount;
480 goto ALMOST_DONE;
483 if (read(ref, &pixelSize, sizeof(int32)) != sizeof(int32)) {
484 pixelSize = kDefaultPixelSize;
485 goto ALMOST_DONE;
488 ALMOST_DONE: // clean up and try to position the window
489 close(ref);
491 if (haveLoc && BScreen(B_MAIN_SCREEN_ID).Frame().Contains(loc)) {
492 MoveTo(loc);
493 goto DONE;
498 // if prefs dont yet exist or the window is not onscreen, center the window
499 CenterOnScreen();
501 // set all the settings to defaults if we get here
502 DONE:
503 fShowGrid = showGrid;
504 fShowInfo = showInfo;
505 fInfoBarState = showInfo;
506 fHPixelCount = (overridePixelCount == -1) ? hPixelCount : overridePixelCount;
507 fVPixelCount = (overridePixelCount == -1) ? vPixelCount : overridePixelCount;
508 fPixelSize = pixelSize;
512 void
513 TWindow::SetPrefs()
515 BPath path;
517 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) == B_OK) {
518 long ref;
520 path.Append (kPrefsFileName);
521 if ((ref = creat(path.Path(), S_IRUSR | S_IWUSR)) >= 0) {
522 float version = kCurrentVersion;
524 lseek (ref, 0, SEEK_SET);
525 write(ref, kAppName, 7);
526 write(ref, &version, sizeof(float));
528 BPoint loc = Frame().LeftTop();
529 write(ref, &loc, sizeof(BPoint));
531 write(ref, &fShowGrid, sizeof(bool));
532 write(ref, &fShowInfo, sizeof(bool));
533 bool ch1, ch2;
534 CrossHairsShowing(&ch1, &ch2);
535 write(ref, &ch1, sizeof(bool));
536 write(ref, &ch2, sizeof(bool));
538 write(ref, &fHPixelCount, sizeof(int32));
539 write(ref, &fVPixelCount, sizeof(int32));
540 write(ref, &fPixelSize, sizeof(int32));
542 close(ref);
548 void
549 TWindow::FrameResized(float w, float h)
551 CalcViewablePixels();
552 fFatBits->InitBuffers(fHPixelCount, fVPixelCount, fPixelSize, ShowGrid());
553 UpdateInfoBarOnResize();
557 void
558 TWindow::ScreenChanged(BRect screenSize, color_space depth)
560 BWindow::ScreenChanged(screenSize, depth);
561 // reset all bitmaps
562 fFatBits->ScreenChanged(screenSize,depth);
566 void
567 TWindow::Minimize(bool m)
569 BWindow::Minimize(m);
573 void
574 TWindow::Zoom(BPoint /*position*/, float /*width*/, float /*height*/)
576 if (fFatBits->Active())
577 ShowInfo(!fShowInfo);
581 void
582 TWindow::CalcViewablePixels()
584 float w = Bounds().Width();
585 float h = Bounds().Height();
587 if (InfoIsShowing()) {
588 w -= 20; // remove the gutter
589 h = h-fInfoHeight-10; // remove info and gutter
592 bool ch1, ch2;
593 fFatBits->CrossHairsShowing(&ch1, &ch2);
594 if (ch1)
595 h -= fFontHeight;
596 if (ch2)
597 h -= fFontHeight + 5;
599 fHPixelCount = (int32)w / fPixelSize; // calc h pixels
600 if (fHPixelCount < 16)
601 fHPixelCount = 16;
603 fVPixelCount = (int32)h / fPixelSize; // calc v pixels
604 if (fVPixelCount < 4)
605 fVPixelCount = 4;
609 void
610 TWindow::GetPreferredSize(float* width, float* height)
612 *width = fHPixelCount * fPixelSize; // calc window width
613 *height = fVPixelCount * fPixelSize; // calc window height
614 if (InfoIsShowing()) {
615 *width += 20;
616 *height += fInfoHeight + 10;
619 bool ch1, ch2;
620 fFatBits->CrossHairsShowing(&ch1, &ch2);
621 if (ch1)
622 *height += fFontHeight;
623 if (ch2)
624 *height += fFontHeight + 5;
628 void
629 TWindow::ResizeWindow(int32 hPixelCount, int32 vPixelCount)
631 fHPixelCount = hPixelCount;
632 fVPixelCount = vPixelCount;
634 float width, height;
635 GetPreferredSize(&width, &height);
637 ResizeTo(width, height);
641 void
642 TWindow::ResizeWindow(bool direction)
644 int32 x = fHPixelCount;
645 int32 y = fVPixelCount;
647 if (direction) {
648 x += 4;
649 y += 4;
650 } else {
651 x -= 4;
652 y -= 4;
655 if (x < 4)
656 x = 4;
658 if (y < 4)
659 y = 4;
661 ResizeWindow(x, y);
665 void
666 TWindow::SetGrid(bool s)
668 if (s == fShowGrid)
669 return;
671 fShowGrid = s;
672 fFatBits->SetUpdate(true);
676 bool
677 TWindow::ShowGrid()
679 return fShowGrid;
683 void
684 TWindow::ShowInfo(bool i)
686 if (i == fShowInfo)
687 return;
689 fShowInfo = i;
691 if (fShowInfo)
692 fFatBits->MoveTo(10, fInfoHeight);
693 else {
694 fFatBits->MoveTo(1,1);
695 fFatBits->SetCrossHairsShowing(false, false);
698 fFatBits->SetSelection(fShowInfo);
699 ResizeWindow(fHPixelCount, fVPixelCount);
700 fInfo->SetInfoTextVisible(i);
704 bool
705 TWindow::InfoIsShowing()
707 return fShowInfo;
711 bool
712 TWindow::InfoBarIsVisible()
714 return fInfoBarState;
718 void
719 TWindow::UpdateInfo()
721 fInfo->Invalidate();
725 void
726 TWindow::UpdateInfoBarOnResize()
728 float infoWidth, infoHeight;
729 fInfo->GetPreferredSize(&infoWidth, &infoHeight);
731 if (infoWidth > Bounds().Width()
732 || infoHeight > Bounds().Height()) {
733 ShowInfo(false);
734 } else {
735 ShowInfo(fInfoBarState);
740 void
741 TWindow::AddCrossHair()
743 fFatBits->AddCrossHair();
745 // crosshair info needs to be added
746 // window resizes accordingly
747 float width;
748 float height;
749 GetPreferredSize(&width, &height);
750 ResizeTo(width, height);
754 void
755 TWindow::RemoveCrossHair()
757 fFatBits->RemoveCrossHair();
759 // crosshair info needs to be removed
760 // window resizes accordingly
761 float width;
762 float height;
763 GetPreferredSize(&width, &height);
764 ResizeTo(width, height);
768 void
769 TWindow::CrossHairsShowing(bool* ch1, bool* ch2)
771 fFatBits->CrossHairsShowing(ch1, ch2);
775 void
776 TWindow::PixelCount(int32* h, int32 *v)
778 *h = fHPixelCount;
779 *v = fVPixelCount;
783 void
784 TWindow::SetPixelSize(int32 s)
786 if (s == fPixelSize)
787 return;
789 fPixelSize = s;
790 // resize window
791 // tell info that size has changed
792 // tell mag that size has changed
794 CalcViewablePixels();
795 ResizeWindow(fHPixelCount, fVPixelCount);
799 void
800 TWindow::SetPixelSize(bool d)
802 if (d) { // grow
803 fPixelSize++;
804 if (fPixelSize > 16)
805 fPixelSize = 16;
806 } else {
807 fPixelSize--;
808 if (fPixelSize < 1)
809 fPixelSize = 1;
812 float w = Bounds().Width();
813 float h = Bounds().Height();
814 CalcViewablePixels();
815 ResizeWindow(fHPixelCount, fVPixelCount);
817 // the window might not actually change in size
818 // in that case force the buffers to the new dimension
819 if (w == Bounds().Width() && h == Bounds().Height())
820 fFatBits->InitBuffers(fHPixelCount, fVPixelCount, fPixelSize, ShowGrid());
824 int32
825 TWindow::PixelSize()
827 return fPixelSize;
831 #undef B_TRANSLATION_CONTEXT
832 #define B_TRANSLATION_CONTEXT "Magnify-Main"
835 bool
836 TWindow::IsActive()
838 return fFatBits->Active();
842 bool
843 TWindow::IsSticked()
845 return fFatBits->Sticked();
849 // #pragma mark -
852 TInfoView::TInfoView(BRect frame)
853 : BBox(frame, "rgb", B_FOLLOW_ALL,
854 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS,
855 B_NO_BORDER)
857 SetFont(be_plain_font);
858 fFontHeight = FontHeight(this, true);
859 fMagView = NULL;
861 fSelectionColor = kBlack;
862 fCH1Loc.x = fCH1Loc.y = fCH2Loc.x = fCH2Loc.y = 0;
864 fInfoStr[0] = 0;
865 fRGBStr[0] = 0;
866 fCH1Str[0] = 0;
867 fCH2Str[0] = 0;
869 fInfoTextVisible = true;
873 TInfoView::~TInfoView()
878 void
879 TInfoView::AttachedToWindow()
881 BBox::AttachedToWindow();
882 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
883 dynamic_cast<TWindow*>(Window())->PixelCount(&fHPixelCount, &fVPixelCount);
884 fPixelSize = dynamic_cast<TWindow*>(Window())->PixelSize();
886 AddMenu();
890 void
891 TInfoView::Draw(BRect updateRect)
893 PushState();
894 SetLowColor(ViewColor());
896 BRect invalRect;
898 int32 hPixelCount, vPixelCount;
899 dynamic_cast<TWindow*>(Window())->PixelCount(&hPixelCount, &vPixelCount);
900 int32 pixelSize = dynamic_cast<TWindow*>(Window())->PixelSize();
902 MovePenTo(15 + fPopUp->Bounds().Width(), fFontHeight + 5);
904 static BMessageFormat format(B_TRANSLATE("%width x %height @ {0, plural, "
905 "one{# pixel/pixel} other{# pixels/pixel}}"));
907 BString dimensionsInfo;
908 format.Format(dimensionsInfo, pixelSize);
910 BString rep;
911 rep << hPixelCount;
912 dimensionsInfo.ReplaceAll("%width", rep);
913 rep = "";
914 rep << vPixelCount;
915 dimensionsInfo.ReplaceAll("%height", rep);
917 invalRect.Set(10, 5, 10 + StringWidth(fInfoStr), fFontHeight+7);
918 SetHighColor(ViewColor());
919 FillRect(invalRect);
920 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
921 strcpy(fInfoStr, dimensionsInfo);
922 if (fInfoTextVisible)
923 DrawString(fInfoStr);
925 rgb_color color = { 0, 0, 0, 255 };
926 if (fMagView)
927 color = fMagView->SelectionColor();
928 char str[64];
929 snprintf(str, sizeof(str), "R: %i G: %i B: %i (#%02x%02x%02x)",
930 color.red, color.green, color.blue, color.red, color.green, color.blue);
932 MovePenTo(15 + fPopUp->Bounds().Width(), fFontHeight*2+5);
933 invalRect.Set(10, fFontHeight+7, 10 + StringWidth(fRGBStr), fFontHeight*2+7);
934 SetHighColor(ViewColor());
935 FillRect(invalRect);
936 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
937 strcpy(fRGBStr,str);
938 if (fInfoTextVisible)
939 DrawString(fRGBStr);
941 bool ch1Showing, ch2Showing;
942 dynamic_cast<TWindow*>(Window())->CrossHairsShowing(&ch1Showing, &ch2Showing);
944 if (fMagView) {
945 BPoint pt1(fMagView->CrossHair1Loc());
946 BPoint pt2(fMagView->CrossHair2Loc());
948 float h = Bounds().Height();
949 if (ch2Showing) {
950 MovePenTo(10, h-12);
951 sprintf(str, "2) x: %" B_PRIi32 " y: %" B_PRIi32 " y: %d",
952 (int32)pt2.x, (int32)pt2.y, abs((int)(pt1.y - pt2.y)));
953 invalRect.Set(10, h-12-fFontHeight, 10 + StringWidth(fCH2Str), h-10);
954 SetHighColor(ViewColor());
955 FillRect(invalRect);
956 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
957 strcpy(fCH2Str,str);
958 if (fInfoTextVisible)
959 DrawString(fCH2Str);
962 if (ch1Showing && ch2Showing) {
963 MovePenTo(10, h-10-fFontHeight-2);
964 sprintf(str, "1) x: %" B_PRIi32 " y: %" B_PRIi32 " x: %d",
965 (int32)pt1.x, (int32)pt1.y, abs((int)(pt1.x - pt2.x)));
966 invalRect.Set(10, h-10-2*fFontHeight-2, 10 + StringWidth(fCH1Str), h-10-fFontHeight);
967 SetHighColor(ViewColor());
968 FillRect(invalRect);
969 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
970 strcpy(fCH1Str,str);
971 if (fInfoTextVisible)
972 DrawString(fCH1Str);
973 } else if (ch1Showing) {
974 MovePenTo(10, h-10);
975 sprintf(str, "x: %" B_PRIi32 " y: %" B_PRIi32, (int32)pt1.x, (int32)pt1.y);
976 invalRect.Set(10, h-10-fFontHeight, 10 + StringWidth(fCH1Str), h-8);
977 SetHighColor(ViewColor());
978 FillRect(invalRect);
979 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
980 strcpy(fCH1Str,str);
981 if (fInfoTextVisible)
982 DrawString(fCH1Str);
986 PopState();
990 void
991 TInfoView::FrameResized(float width, float height)
993 BBox::FrameResized(width, height);
997 void
998 TInfoView::AddMenu()
1000 fMenu = new TMenu(dynamic_cast<TWindow*>(Window()), "");
1001 BuildInfoMenu(fMenu);
1003 BRect r(9, 11, 22, 27);
1004 fPopUp = new BMenuField( r, "region menu", NULL, fMenu, true,
1005 B_FOLLOW_LEFT | B_FOLLOW_TOP);
1006 AddChild(fPopUp);
1010 void
1011 TInfoView::SetMagView(TMagnify* magView)
1013 fMagView = magView;
1017 // #pragma mark -
1020 TMenu::TMenu(TWindow *mainWindow, const char *title, menu_layout layout)
1021 : BMenu(title, layout),
1022 fMainWindow(mainWindow)
1027 TMenu::~TMenu()
1032 void
1033 TMenu::AttachedToWindow()
1035 UpdateInfoMenu(this, fMainWindow);
1037 BMenu::AttachedToWindow();
1041 void
1042 TInfoView::GetPreferredSize(float* _width, float* _height)
1044 if (_width) {
1045 float str1Width = StringWidth(fCH1Str)
1046 + StringWidth(fCH2Str)
1047 + StringWidth(fRGBStr)
1048 + 30;
1049 float str2Width = StringWidth(fInfoStr) + 30;
1050 *_width = str1Width > str2Width ? str1Width : str2Width;
1053 if (_height)
1054 *_height = fFontHeight * 2 + 10;
1058 bool
1059 TInfoView::IsInfoTextVisible()
1061 return fInfoTextVisible;
1065 void
1066 TInfoView::SetInfoTextVisible(bool visible)
1068 fInfoTextVisible = visible;
1069 Draw(Bounds());
1073 // #pragma mark -
1076 TMagnify::TMagnify(BRect r, TWindow* parent)
1077 : BView(r, "MagView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS),
1078 fNeedToUpdate(true),
1079 fThread(-1),
1080 fActive(true),
1081 fImageBuf(NULL),
1082 fImageView(NULL),
1083 fLastLoc(-1, -1),
1084 fSelection(-1),
1085 fShowSelection(false),
1086 fSelectionLoc(0, 0),
1087 fShowCrossHair1(false),
1088 fCrossHair1(-1, -1),
1089 fShowCrossHair2(false),
1090 fCrossHair2(-1, -1),
1091 fParent(parent),
1092 fStickCoordinates(false)
1097 TMagnify::~TMagnify()
1099 kill_thread(fThread);
1100 delete fImageBuf;
1104 void
1105 TMagnify::AttachedToWindow()
1107 int32 width, height;
1108 fParent->PixelCount(&width, &height);
1109 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid());
1111 fThread = spawn_thread(TMagnify::MagnifyTask, "MagnifyTask",
1112 B_NORMAL_PRIORITY, this);
1114 resume_thread(fThread);
1116 SetViewColor(B_TRANSPARENT_32_BIT);
1117 MakeFocus();
1121 void
1122 TMagnify::InitBuffers(int32 hPixelCount, int32 vPixelCount,
1123 int32 pixelSize, bool showGrid)
1125 color_space colorSpace = BScreen(Window()).ColorSpace();
1127 BRect r(0, 0, (pixelSize * hPixelCount)-1, (pixelSize * vPixelCount)-1);
1128 if (Bounds().Width() != r.Width() || Bounds().Height() != r.Height())
1129 ResizeTo(r.Width(), r.Height());
1131 if (fImageView) {
1132 fImageBuf->Lock();
1133 fImageView->RemoveSelf();
1134 fImageBuf->Unlock();
1136 fImageView->Resize((int32)r.Width(), (int32)r.Height());
1137 fImageView->SetSpace(colorSpace);
1138 } else
1139 fImageView = new TOSMagnify(r, this, colorSpace);
1141 delete fImageBuf;
1142 fImageBuf = new BBitmap(r, colorSpace, true);
1143 fImageBuf->Lock();
1144 fImageBuf->AddChild(fImageView);
1145 fImageBuf->Unlock();
1149 void
1150 TMagnify::Draw(BRect)
1152 BRect bounds(Bounds());
1153 DrawBitmap(fImageBuf, bounds, bounds);
1154 static_cast<TWindow*>(Window())->UpdateInfo();
1158 void
1159 TMagnify::KeyDown(const char *key, int32 numBytes)
1161 if (!fShowSelection)
1162 BView::KeyDown(key, numBytes);
1164 uint32 mods = modifiers();
1166 switch (key[0]) {
1167 case B_TAB:
1168 if (fShowCrossHair1) {
1169 fSelection++;
1171 if (fShowCrossHair2) {
1172 if (fSelection > 2)
1173 fSelection = 0;
1174 } else if (fShowCrossHair1) {
1175 if (fSelection > 1)
1176 fSelection = 0;
1178 fNeedToUpdate = true;
1179 Invalidate();
1181 break;
1183 case B_LEFT_ARROW:
1184 if (mods & B_OPTION_KEY)
1185 NudgeMouse(-1,0);
1186 else
1187 MoveSelection(-1,0);
1188 break;
1189 case B_RIGHT_ARROW:
1190 if (mods & B_OPTION_KEY)
1191 NudgeMouse(1, 0);
1192 else
1193 MoveSelection(1,0);
1194 break;
1195 case B_UP_ARROW:
1196 if (mods & B_OPTION_KEY)
1197 NudgeMouse(0, -1);
1198 else
1199 MoveSelection(0,-1);
1200 break;
1201 case B_DOWN_ARROW:
1202 if (mods & B_OPTION_KEY)
1203 NudgeMouse(0, 1);
1204 else
1205 MoveSelection(0,1);
1206 break;
1208 default:
1209 BView::KeyDown(key,numBytes);
1210 break;
1215 void
1216 TMagnify::FrameResized(float newW, float newH)
1218 int32 w, h;
1219 PixelCount(&w, &h);
1221 if (fSelectionLoc.x >= w)
1222 fSelectionLoc.x = 0;
1223 if (fSelectionLoc.y >= h)
1224 fSelectionLoc.y = 0;
1226 if (fShowCrossHair1) {
1227 if (fCrossHair1.x >= w) {
1228 fCrossHair1.x = fSelectionLoc.x + 2;
1229 if (fCrossHair1.x >= w)
1230 fCrossHair1.x = 0;
1232 if (fCrossHair1.y >= h) {
1233 fCrossHair1.y = fSelectionLoc.y + 2;
1234 if (fCrossHair1.y >= h)
1235 fCrossHair1.y = 0;
1238 if (fShowCrossHair2) {
1239 if (fCrossHair2.x >= w) {
1240 fCrossHair2.x = fCrossHair1.x + 2;
1241 if (fCrossHair2.x >= w)
1242 fCrossHair2.x = 0;
1244 if (fCrossHair2.y >= h) {
1245 fCrossHair2.y = fCrossHair1.y + 2;
1246 if (fCrossHair2.y >= h)
1247 fCrossHair2.y = 0;
1254 void
1255 TMagnify::MouseDown(BPoint where)
1257 BMessage *currentMsg = Window()->CurrentMessage();
1258 if (currentMsg->what == B_MOUSE_DOWN) {
1259 uint32 buttons = 0;
1260 currentMsg->FindInt32("buttons", (int32 *)&buttons);
1262 uint32 modifiers = 0;
1263 currentMsg->FindInt32("modifiers", (int32 *)&modifiers);
1265 if ((buttons & B_SECONDARY_MOUSE_BUTTON) || (modifiers & B_CONTROL_KEY)) {
1266 // secondary button was clicked or control key was down, show menu and return
1268 BPopUpMenu *menu = new BPopUpMenu(B_TRANSLATE("Info"), false, false);
1269 menu->SetFont(be_plain_font);
1270 BuildInfoMenu(menu);
1271 UpdateInfoMenu(menu, dynamic_cast<TWindow*>(Window()));
1272 BMenuItem *selected = menu->Go(ConvertToScreen(where));
1273 if (selected)
1274 Window()->PostMessage(selected->Message()->what);
1275 delete menu;
1276 return;
1279 // add a mousedown looper here
1281 int32 pixelSize = PixelSize();
1282 float x = where.x / pixelSize;
1283 float y = where.y / pixelSize;
1285 MoveSelectionTo(x, y);
1287 // draw the frozen image
1288 // update the info region
1290 fNeedToUpdate = true;
1291 Invalidate();
1296 void
1297 TMagnify::ScreenChanged(BRect, color_space)
1299 int32 width, height;
1300 fParent->PixelCount(&width, &height);
1301 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid());
1305 void
1306 TMagnify::SetSelection(bool state)
1308 if (fShowSelection == state)
1309 return;
1311 fShowSelection = state;
1312 fSelection = 0;
1313 Invalidate();
1317 void
1318 TMagnify::MoveSelection(int32 x, int32 y)
1320 if (!fShowSelection)
1321 return;
1323 int32 xCount, yCount;
1324 PixelCount(&xCount, &yCount);
1326 float xloc, yloc;
1327 if (fSelection == 0) {
1328 xloc = fSelectionLoc.x;
1329 yloc = fSelectionLoc.y;
1330 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount);
1331 fSelectionLoc.x = xloc;
1332 fSelectionLoc.y = yloc;
1333 } else if (fSelection == 1) {
1334 xloc = fCrossHair1.x;
1335 yloc = fCrossHair1.y;
1336 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount);
1337 fCrossHair1.x = xloc;
1338 fCrossHair1.y = yloc;
1339 } else if (fSelection == 2) {
1340 xloc = fCrossHair2.x;
1341 yloc = fCrossHair2.y;
1342 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount);
1343 fCrossHair2.x = xloc;
1344 fCrossHair2.y = yloc;
1347 fNeedToUpdate = true;
1348 Invalidate();
1352 void
1353 TMagnify::MoveSelectionTo(int32 x, int32 y)
1355 if (!fShowSelection)
1356 return;
1358 int32 xCount, yCount;
1359 PixelCount(&xCount, &yCount);
1360 if (x >= xCount)
1361 x = 0;
1362 if (y >= yCount)
1363 y = 0;
1365 if (fSelection == 0) {
1366 fSelectionLoc.x = x;
1367 fSelectionLoc.y = y;
1368 } else if (fSelection == 1) {
1369 fCrossHair1.x = x;
1370 fCrossHair1.y = y;
1371 } else if (fSelection == 2) {
1372 fCrossHair2.x = x;
1373 fCrossHair2.y = y;
1376 fNeedToUpdate = true;
1377 Invalidate(); //Draw(Bounds());
1381 void
1382 TMagnify::ShowSelection()
1387 short
1388 TMagnify::Selection()
1390 return fSelection;
1394 bool
1395 TMagnify::SelectionIsShowing()
1397 return fShowSelection;
1401 void
1402 TMagnify::SelectionLoc(float* x, float* y)
1404 *x = fSelectionLoc.x;
1405 *y = fSelectionLoc.y;
1409 void
1410 TMagnify::SetSelectionLoc(float x, float y)
1412 fSelectionLoc.x = x;
1413 fSelectionLoc.y = y;
1417 rgb_color
1418 TMagnify::SelectionColor()
1420 return fImageView->ColorAtSelection();
1424 void
1425 TMagnify::CrossHair1Loc(float* x, float* y)
1427 *x = fCrossHair1.x;
1428 *y = fCrossHair1.y;
1432 void
1433 TMagnify::CrossHair2Loc(float* x, float* y)
1435 *x = fCrossHair2.x;
1436 *y = fCrossHair2.y;
1440 BPoint
1441 TMagnify::CrossHair1Loc()
1443 return fCrossHair1;
1447 BPoint
1448 TMagnify::CrossHair2Loc()
1450 return fCrossHair2;
1454 void
1455 TMagnify::NudgeMouse(float x, float y)
1457 BPoint loc;
1458 uint32 button;
1460 GetMouse(&loc, &button);
1461 ConvertToScreen(&loc);
1462 loc.x += x;
1463 loc.y += y;
1465 set_mouse_position((int32)loc.x, (int32)loc.y);
1469 void
1470 TMagnify::WindowActivated(bool active)
1472 if (active)
1473 MakeFocus();
1477 status_t
1478 TMagnify::MagnifyTask(void *arg)
1480 TMagnify* view = (TMagnify*)arg;
1482 // static data members can't access members, methods without
1483 // a pointer to an instance of the class
1484 TWindow* window = (TWindow*)view->Window();
1486 while (true) {
1487 if (window->Lock()) {
1488 if (view->NeedToUpdate() || view->Active())
1489 view->Update(view->NeedToUpdate());
1491 window->Unlock();
1493 snooze(35000);
1496 return B_NO_ERROR;
1500 void
1501 TMagnify::Update(bool force)
1503 BPoint loc;
1504 uint32 button;
1505 static long counter = 0;
1507 if (!fStickCoordinates) {
1508 GetMouse(&loc, &button);
1509 ConvertToScreen(&loc);
1510 } else
1511 loc = fLastLoc;
1513 if (force || fLastLoc != loc || counter++ % 35 == 0) {
1514 if (fImageView->CreateImage(loc, force))
1515 Invalidate();
1517 counter = 0;
1518 if (force)
1519 SetUpdate(false);
1521 fLastLoc = loc;
1525 bool
1526 TMagnify::NeedToUpdate()
1528 return fNeedToUpdate;
1532 void
1533 TMagnify::SetUpdate(bool s)
1535 fNeedToUpdate = s;
1539 void
1540 TMagnify::CopyImage()
1542 StartSave();
1543 be_clipboard->Lock();
1544 be_clipboard->Clear();
1546 BMessage *message = be_clipboard->Data();
1547 if (!message) {
1548 printf(B_TRANSLATE_CONTEXT("no clip msg\n",
1549 "In console, when clipboard is empty after clicking Copy image"));
1550 return;
1553 BMessage *embeddedBitmap = new BMessage();
1554 (fImageView->Bitmap())->Archive(embeddedBitmap,false);
1555 status_t err = message->AddMessage(kBitmapMimeType, embeddedBitmap);
1556 if (err == B_OK)
1557 err = message->AddRect("rect", fImageView->Bitmap()->Bounds());
1558 if (err == B_OK)
1559 be_clipboard->Commit();
1561 be_clipboard->Unlock();
1562 EndSave();
1566 void
1567 TMagnify::AddCrossHair()
1569 if (fShowCrossHair1 && fShowCrossHair2)
1570 return;
1572 int32 w, h;
1573 PixelCount(&w, &h);
1575 if (fShowCrossHair1) {
1576 fSelection = 2;
1577 fShowCrossHair2 = true;
1578 fCrossHair2.x = fCrossHair1.x + 2;
1579 if (fCrossHair2.x >= w)
1580 fCrossHair2.x = 0;
1581 fCrossHair2.y = fCrossHair1.y + 2;
1582 if (fCrossHair2.y >= h)
1583 fCrossHair2.y = 0;
1584 } else {
1585 fSelection = 1;
1586 fShowCrossHair1 = true;
1587 fCrossHair1.x = fSelectionLoc.x + 2;
1588 if (fCrossHair1.x >= w)
1589 fCrossHair1.x = 0;
1590 fCrossHair1.y = fSelectionLoc.y + 2;
1591 if (fCrossHair1.y >= h)
1592 fCrossHair1.y = 0;
1594 Invalidate();
1598 void
1599 TMagnify::RemoveCrossHair()
1601 if (!fShowCrossHair1 && !fShowCrossHair2)
1602 return;
1604 if (fShowCrossHair2) {
1605 fSelection = 1;
1606 fShowCrossHair2 = false;
1607 } else if (fShowCrossHair1) {
1608 fSelection = 0;
1609 fShowCrossHair1 = false;
1611 Invalidate();
1615 void
1616 TMagnify::SetCrossHairsShowing(bool ch1, bool ch2)
1618 fShowCrossHair1 = ch1;
1619 fShowCrossHair2 = ch2;
1623 void
1624 TMagnify::CrossHairsShowing(bool* ch1, bool* ch2)
1626 *ch1 = fShowCrossHair1;
1627 *ch2 = fShowCrossHair2;
1631 void
1632 TMagnify::MakeActive(bool s)
1634 fActive = s;
1638 void
1639 TMagnify::MakeSticked(bool s)
1641 fStickCoordinates = s;
1645 void
1646 TMagnify::PixelCount(int32* width, int32* height)
1648 fParent->PixelCount(width, height);
1652 int32
1653 TMagnify::PixelSize()
1655 return fParent->PixelSize();
1659 bool
1660 TMagnify::ShowGrid()
1662 return fParent->ShowGrid();
1666 void
1667 TMagnify::StartSave()
1669 fImageFrozenOnSave = Active();
1670 if (fImageFrozenOnSave)
1671 MakeActive(false);
1675 void
1676 TMagnify::EndSave()
1678 if (fImageFrozenOnSave)
1679 MakeActive(true);
1683 void
1684 TMagnify::SaveImage(entry_ref* ref, char* name)
1686 // create a new file
1687 BFile file;
1688 BDirectory parentDir(ref);
1689 parentDir.CreateFile(name, &file);
1691 // Write the screenshot bitmap to the file
1692 BBitmapStream stream(fImageView->Bitmap());
1693 BTranslatorRoster* roster = BTranslatorRoster::Default();
1694 roster->Translate(&stream, NULL, NULL, &file, B_PNG_FORMAT,
1695 B_TRANSLATOR_BITMAP);
1697 BBitmap* bitmap;
1698 stream.DetachBitmap(&bitmap);
1699 // The stream takes over ownership of the bitmap
1701 // unfreeze the image, image was frozen before invoke of FilePanel
1702 EndSave();
1705 // #pragma mark -
1708 TOSMagnify::TOSMagnify(BRect r, TMagnify* parent, color_space space)
1709 : BView(r, "ImageView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS),
1710 fColorSpace(space), fParent(parent)
1712 switch (space) {
1713 case B_CMAP8:
1714 fBytesPerPixel = 1;
1715 break;
1716 case B_RGB15:
1717 case B_RGBA15:
1718 case B_RGB15_BIG:
1719 case B_RGBA15_BIG:
1720 case B_RGB16:
1721 case B_RGB16_BIG:
1722 fBytesPerPixel = 2;
1723 break;
1724 case B_RGB24:
1725 fBytesPerPixel = 3;
1726 break;
1727 case B_RGB32:
1728 case B_RGBA32:
1729 case B_RGB32_BIG:
1730 case B_RGBA32_BIG:
1731 fBytesPerPixel = 4;
1732 break;
1733 default:
1734 // uh, oh -- a color space we don't support
1735 fprintf(stderr, "Tried to run in an unsupported color space; exiting\n");
1736 exit(1);
1737 break;
1740 fPixel = NULL;
1741 fBitmap = NULL;
1742 fOldBits = NULL;
1743 InitObject();
1747 TOSMagnify::~TOSMagnify()
1749 delete fPixel;
1750 delete fBitmap;
1751 free(fOldBits);
1755 void
1756 TOSMagnify::SetSpace(color_space space)
1758 fColorSpace = space;
1759 InitObject();
1763 void
1764 TOSMagnify::InitObject()
1766 int32 w, h;
1767 fParent->PixelCount(&w, &h);
1769 delete fBitmap;
1770 BRect bitsRect(0, 0, w-1, h-1);
1771 fBitmap = new BBitmap(bitsRect, fColorSpace);
1773 free(fOldBits);
1774 fOldBits = (char*)malloc(fBitmap->BitsLength());
1776 if (!fPixel) {
1777 #if B_HOST_IS_BENDIAN
1778 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32_BIG, true);
1779 #else
1780 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32, true);
1781 #endif
1782 fPixelView = new BView(BRect(0,0,0,0), NULL, 0, 0);
1783 fPixel->Lock();
1784 fPixel->AddChild(fPixelView);
1785 fPixel->Unlock();
1790 void
1791 TOSMagnify::FrameResized(float width, float height)
1793 BView::FrameResized(width, height);
1794 InitObject();
1798 void
1799 TOSMagnify::Resize(int32 width, int32 height)
1801 ResizeTo(width, height);
1802 InitObject();
1806 bool
1807 TOSMagnify::CreateImage(BPoint mouseLoc, bool force)
1809 bool created = false;
1810 if (Window() && Window()->Lock()) {
1811 int32 width, height;
1812 fParent->PixelCount(&width, &height);
1813 int32 pixelSize = fParent->PixelSize();
1815 BRect srcRect(0, 0, width - 1, height - 1);
1816 srcRect.OffsetBy(mouseLoc.x - (width / 2),
1817 mouseLoc.y - (height / 2));
1819 if (force || CopyScreenRect(srcRect)) {
1820 srcRect.OffsetTo(BPoint(0, 0));
1821 BRect destRect(Bounds());
1823 DrawBitmap(fBitmap, srcRect, destRect);
1825 DrawGrid(width, height, destRect, pixelSize);
1826 DrawSelection();
1828 Sync();
1829 created = true;
1831 Window()->Unlock();
1832 } else
1833 printf("window problem\n");
1835 return created;
1839 bool
1840 TOSMagnify::CopyScreenRect(BRect srcRect)
1842 // constrain src rect to legal screen rect
1843 BScreen screen(Window());
1844 BRect scrnframe = screen.Frame();
1846 if (srcRect.right > scrnframe.right)
1847 srcRect.OffsetTo(scrnframe.right - srcRect.Width(), srcRect.top);
1848 if (srcRect.top < 0)
1849 srcRect.OffsetTo(srcRect.left, 0);
1851 if (srcRect.bottom > scrnframe.bottom)
1852 srcRect.OffsetTo(srcRect.left, scrnframe.bottom - srcRect.Height());
1853 if (srcRect.left < 0)
1854 srcRect.OffsetTo(0, srcRect.top);
1856 // save a copy of the bits for comparison later
1857 memcpy(fOldBits, fBitmap->Bits(), fBitmap->BitsLength());
1859 screen.ReadBitmap(fBitmap, false, &srcRect);
1861 // let caller know whether bits have actually changed
1862 return memcmp(fBitmap->Bits(), fOldBits, fBitmap->BitsLength()) != 0;
1866 void
1867 TOSMagnify::DrawGrid(int32 width, int32 height, BRect destRect, int32 pixelSize)
1869 // draw grid
1870 if (fParent->ShowGrid() && fParent->PixelSize() > 2) {
1871 BeginLineArray(width * height);
1873 // horizontal lines
1874 for (int32 i = pixelSize; i < (height * pixelSize); i += pixelSize)
1875 AddLine(BPoint(0, i), BPoint(destRect.right, i), kGridGray);
1877 // vertical lines
1878 for (int32 i = pixelSize; i < (width * pixelSize); i += pixelSize)
1879 AddLine(BPoint(i, 0), BPoint(i, destRect.bottom), kGridGray);
1881 EndLineArray();
1884 SetHighColor(kGridGray);
1885 StrokeRect(destRect);
1889 void
1890 TOSMagnify::DrawSelection()
1892 if (!fParent->SelectionIsShowing())
1893 return;
1895 float x, y;
1896 int32 pixelSize = fParent->PixelSize();
1897 int32 squareSize = pixelSize - 2;
1899 fParent->SelectionLoc(&x, &y);
1900 x *= pixelSize; x++;
1901 y *= pixelSize; y++;
1902 BRect selRect(x, y, x+squareSize, y+squareSize);
1904 short selection = fParent->Selection();
1906 PushState();
1907 SetLowColor(ViewColor());
1908 SetHighColor(kRedColor);
1909 StrokeRect(selRect);
1910 if (selection == 0) {
1911 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize));
1912 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y));
1915 bool ch1Showing, ch2Showing;
1916 fParent->CrossHairsShowing(&ch1Showing, &ch2Showing);
1917 if (ch1Showing) {
1918 SetHighColor(kBlueColor);
1919 fParent->CrossHair1Loc(&x, &y);
1920 x *= pixelSize; x++;
1921 y *= pixelSize; y++;
1922 selRect.Set(x, y,x+squareSize, y+squareSize);
1923 StrokeRect(selRect);
1924 BeginLineArray(4);
1925 AddLine(BPoint(0, y+(squareSize/2)),
1926 BPoint(x, y+(squareSize/2)), kBlueColor); // left
1927 AddLine(BPoint(x+squareSize,y+(squareSize/2)),
1928 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right
1929 AddLine(BPoint(x+(squareSize/2), 0),
1930 BPoint(x+(squareSize/2), y), kBlueColor); // top
1931 AddLine(BPoint(x+(squareSize/2), y+squareSize),
1932 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom
1933 EndLineArray();
1934 if (selection == 1) {
1935 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize));
1936 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y));
1939 if (ch2Showing) {
1940 SetHighColor(kBlueColor);
1941 fParent->CrossHair2Loc(&x, &y);
1942 x *= pixelSize; x++;
1943 y *= pixelSize; y++;
1944 selRect.Set(x, y,x+squareSize, y+squareSize);
1945 StrokeRect(selRect);
1946 BeginLineArray(4);
1947 AddLine(BPoint(0, y+(squareSize/2)),
1948 BPoint(x, y+(squareSize/2)), kBlueColor); // left
1949 AddLine(BPoint(x+squareSize,y+(squareSize/2)),
1950 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right
1951 AddLine(BPoint(x+(squareSize/2), 0),
1952 BPoint(x+(squareSize/2), y), kBlueColor); // top
1953 AddLine(BPoint(x+(squareSize/2), y+squareSize),
1954 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom
1955 EndLineArray();
1956 if (selection == 2) {
1957 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize));
1958 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y));
1962 PopState();
1966 rgb_color
1967 TOSMagnify::ColorAtSelection()
1969 float x, y;
1970 fParent->SelectionLoc(&x, &y);
1971 BRect srcRect(x, y, x, y);
1972 BRect dstRect(0, 0, 0, 0);
1973 fPixel->Lock();
1974 fPixelView->DrawBitmap(fBitmap, srcRect, dstRect);
1975 fPixelView->Sync();
1976 fPixel->Unlock();
1978 uint32 pixel = *((uint32*)fPixel->Bits());
1979 rgb_color c;
1980 c.alpha = pixel >> 24;
1981 c.red = (pixel >> 16) & 0xFF;
1982 c.green = (pixel >> 8) & 0xFF;
1983 c.blue = pixel & 0xFF;
1985 return c;
1989 // #pragma mark -
1993 main(int argc, char* argv[])
1995 int32 pixelCount = -1;
1997 if (argc > 2) {
1998 printf(B_TRANSLATE_CONTEXT(
1999 "usage: magnify [size] (magnify size * size pixels)\n",
2000 "Console"));
2001 exit(1);
2002 } else {
2003 if (argc == 2) {
2004 pixelCount = abs(atoi(argv[1]));
2006 if ((pixelCount > 100) || (pixelCount < 4)) {
2007 printf(B_TRANSLATE_CONTEXT(
2008 "usage: magnify [size] (magnify size * size pixels)\n",
2009 "Console"));
2010 printf(B_TRANSLATE_CONTEXT(
2011 " size must be > 4 and a multiple of 4\n",
2012 "Console"));
2013 exit(1);
2016 if (pixelCount % 4) {
2017 printf(B_TRANSLATE_CONTEXT(
2018 "magnify: size must be a multiple of 4\n",
2019 "Console"));
2020 exit(1);
2025 TApp app(pixelCount);
2026 app.Run();
2027 return 0;