HaikuDepot: notify work status from main window
[haiku.git] / src / apps / magnify / Magnify.cpp
blobc411a81b983aa2888cb141efab8948212415d2c8
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 <PropertyInfo.h>
30 #include <Screen.h>
31 #include <ScrollView.h>
32 #include <TextView.h>
33 #include <TranslationUtils.h>
34 #include <TranslatorRoster.h>
35 #include <WindowScreen.h>
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>
45 #undef B_TRANSLATION_CONTEXT
46 #define B_TRANSLATION_CONTEXT "Magnify-Main"
49 const int32 msg_update_info = 'info';
50 const int32 msg_show_info = 'show';
51 const int32 msg_toggle_grid = 'grid';
52 const int32 msg_shrink = 'shnk';
53 const int32 msg_grow = 'grow';
54 const int32 msg_make_square = 'sqar';
55 const int32 msg_shrink_pixel = 'pshk';
56 const int32 msg_grow_pixel = 'pgrw';
58 const int32 msg_new_color = 'colr';
59 const int32 msg_toggle_ruler = 'rulr';
60 const int32 msg_copy_image = 'copy';
61 const int32 msg_track_color = 'trak';
62 const int32 msg_freeze = 'frez';
63 const int32 msg_stick = 'stic';
64 const int32 msg_dump = 'dump';
65 const int32 msg_add_cross_hair = 'acrs';
66 const int32 msg_remove_cross_hair = 'rcrs';
67 const int32 msg_save = 'save';
69 const rgb_color kViewGray = { 216, 216, 216, 255};
70 const rgb_color kGridGray = {130, 130, 130, 255 };
71 const rgb_color kWhite = { 255, 255, 255, 255};
72 const rgb_color kBlack = { 0, 0, 0, 255};
73 const rgb_color kDarkGray = { 96, 96, 96, 255};
74 const rgb_color kRedColor = { 255, 10, 50, 255 };
75 const rgb_color kGreenColor = { 10, 255, 50, 255 };
76 const rgb_color kBlueColor = { 10, 50, 255, 255 };
78 const char* const kBitmapMimeType = "image/x-vnd.Be-bitmap";
80 const float kCurrentVersion = 1.2;
81 const char *kPrefsFileName = "Magnify_prefs";
83 // prefs are:
84 // name = Magnify
85 // version
86 // show grid
87 // show info (rgb, location)
88 // pixel count
89 // pixel size
90 const char* const kAppName = "Magnify";
91 const bool kDefaultShowGrid = true;
92 const bool kDefaultShowInfo = true;
93 const int32 kDefaultPixelCount = 32;
94 const int32 kDefaultPixelSize = 8;
96 // each info region will be:
97 // top-bottom: 5 fontheight 5 fontheight 5
98 // left-right: 10 minwindowwidth 10
99 const int32 kBorderSize = 10;
102 static property_info sProperties[] = {
103 { "Info", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
104 { B_DIRECT_SPECIFIER, 0 },
105 "Show/hide info.", 0,
106 { B_BOOL_TYPE }
108 { "Grid", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
109 { B_DIRECT_SPECIFIER, 0 },
110 "Show/hide grid.", 0,
111 { B_BOOL_TYPE }
113 { "MakeSquare", { B_EXECUTE_PROPERTY, 0 },
114 { B_DIRECT_SPECIFIER, 0 },
115 "Make the view square.", 0,
117 { "Zoom", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
118 { B_DIRECT_SPECIFIER, 0 },
119 "Gets/sets the zoom factor (1-16).", 0,
120 { B_INT32_TYPE }
122 { "Stick", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
123 { B_DIRECT_SPECIFIER, 0 },
124 "Stick/unstick coordinates.", 0,
125 { B_BOOL_TYPE }
127 { "CopyImage", { B_EXECUTE_PROPERTY, 0 },
128 { B_DIRECT_SPECIFIER, 0 },
129 "Copy image to clipboard.", 0,
132 { 0 }
136 static float
137 FontHeight(BView* target, bool full)
139 font_height finfo;
140 target->GetFontHeight(&finfo);
141 float h = ceil(finfo.ascent) + ceil(finfo.descent);
143 if (full)
144 h += ceil(finfo.leading);
146 return h;
150 static void
151 BoundsSelection(int32 incX, int32 incY, float* x, float* y,
152 int32 xCount, int32 yCount)
154 *x += incX;
155 *y += incY;
157 if (*x < 0)
158 *x = xCount-1;
159 if (*x >= xCount)
160 *x = 0;
162 if (*y < 0)
163 *y = yCount-1;
164 if (*y >= yCount)
165 *y = 0;
169 static void
170 BuildInfoMenu(BMenu *menu)
172 BMenuItem* menuItem;
173 menuItem = new BMenuItem(B_TRANSLATE("Save image"),
174 new BMessage(msg_save), 'S');
175 menu->AddItem(menuItem);
176 // menuItem = new BMenuItem(B_TRANSLATE("Save selection"),
177 // new BMessage(msg_save), 'S');
178 // menu->AddItem(menuItem);
179 menuItem = new BMenuItem(B_TRANSLATE("Copy image"),
180 new BMessage(msg_copy_image), 'C');
181 menu->AddItem(menuItem);
182 menu->AddSeparatorItem();
184 menuItem = new BMenuItem(B_TRANSLATE("Show info"),
185 new BMessage(msg_show_info), 'T');
186 menu->AddItem(menuItem);
187 menuItem = new BMenuItem(B_TRANSLATE("Add a crosshair"),
188 new BMessage(msg_add_cross_hair), 'H');
189 menu->AddItem(menuItem);
190 menuItem = new BMenuItem(B_TRANSLATE("Remove a crosshair"),
191 new BMessage(msg_remove_cross_hair), 'H', B_SHIFT_KEY);
192 menu->AddItem(menuItem);
193 menuItem = new BMenuItem(B_TRANSLATE("Show grid"),
194 new BMessage(msg_toggle_grid), 'G');
195 menu->AddItem(menuItem);
196 menu->AddSeparatorItem();
198 menuItem = new BMenuItem(B_TRANSLATE("Freeze image"),
199 new BMessage(msg_freeze), 'F');
200 menu->AddItem(menuItem);
201 menuItem = new BMenuItem(B_TRANSLATE("Stick coordinates"),
202 new BMessage(msg_stick), 'I');
203 menu->AddItem(menuItem);
204 menu->AddSeparatorItem();
206 menuItem = new BMenuItem(B_TRANSLATE("Make square"),
207 new BMessage(msg_make_square), '/');
208 menu->AddItem(menuItem);
209 menuItem = new BMenuItem(B_TRANSLATE("Decrease window size"),
210 new BMessage(msg_shrink), '-');
211 menu->AddItem(menuItem);
212 menuItem = new BMenuItem(B_TRANSLATE("Increase window size"),
213 new BMessage(msg_grow), '+');
214 menu->AddItem(menuItem);
215 menuItem = new BMenuItem(B_TRANSLATE("Decrease pixel size"),
216 new BMessage(msg_shrink_pixel), ',');
217 menu->AddItem(menuItem);
218 menuItem = new BMenuItem(B_TRANSLATE("Increase pixel size"),
219 new BMessage(msg_grow_pixel), '.');
220 menu->AddItem(menuItem);
223 static void
224 UpdateInfoMenu(BMenu *menu, TWindow *window)
226 bool state = true;
227 bool showGrid = true;
228 bool infoBarIsVisible = true;
229 bool stickCordinates = true;
230 if (window) {
231 state = window->IsActive();
232 showGrid = window->ShowGrid();
233 infoBarIsVisible = window->InfoBarIsVisible();
234 stickCordinates = window->IsSticked();
236 BMenuItem* menuItem = menu->FindItem(B_TRANSLATE("Show info"));
237 if (menuItem) {
238 menuItem->SetEnabled(state);
239 menuItem->SetMarked(infoBarIsVisible);
241 menuItem = menu->FindItem(B_TRANSLATE("Add a crosshair"));
242 if (menuItem)
243 menuItem->SetEnabled(state);
244 menuItem = menu->FindItem(B_TRANSLATE("Remove a crosshair"));
245 if (menuItem)
246 menuItem->SetEnabled(state);
247 menuItem = menu->FindItem(B_TRANSLATE("Show grid"));
248 if (menuItem) {
249 menuItem->SetEnabled(state);
250 menuItem->SetMarked(showGrid);
252 menuItem = menu->FindItem(B_TRANSLATE("Freeze image"));
253 if (menuItem) {
254 menuItem->SetMarked(!state);
256 menuItem = menu->FindItem(B_TRANSLATE("Stick coordinates"));
257 if (menuItem) {
258 menuItem->SetMarked(stickCordinates);
260 menuItem = menu->FindItem(B_TRANSLATE("Make square"));
261 if (menuItem)
262 menuItem->SetEnabled(state);
263 menuItem = menu->FindItem(B_TRANSLATE("Decrease window size"));
264 if (menuItem)
265 menuItem->SetEnabled(state);
266 menuItem = menu->FindItem(B_TRANSLATE("Increase window size"));
267 if (menuItem)
268 menuItem->SetEnabled(state);
269 menuItem = menu->FindItem(B_TRANSLATE("Decrease pixel size"));
270 if (menuItem)
271 menuItem->SetEnabled(state);
272 menuItem = menu->FindItem(B_TRANSLATE("Increase pixel size"));
273 if (menuItem)
274 menuItem->SetEnabled(state);
277 // #pragma mark -
280 // pass in pixelCount to maintain backward compatibility of setting
281 // the pixelcount from the command line
282 TApp::TApp(int32 pixelCount)
283 : BApplication("application/x-vnd.Haiku-Magnify")
285 TWindow* magWindow = new TWindow(pixelCount);
286 magWindow->Show();
290 // #pragma mark -
293 TWindow::TWindow(int32 pixelCount)
295 BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Magnify"),
296 B_TITLED_WINDOW, B_OUTLINE_RESIZE)
298 GetPrefs(pixelCount);
300 // add info view
301 BRect infoRect(Bounds());
302 infoRect.InsetBy(-1, -1);
303 fInfo = new TInfoView(infoRect);
304 AddChild(fInfo);
306 fFontHeight = FontHeight(fInfo, true);
307 fInfoHeight = (fFontHeight * 2) + (3 * 5);
309 BRect fbRect(0, 0, (fHPixelCount*fPixelSize), (fHPixelCount*fPixelSize));
310 if (InfoIsShowing())
311 fbRect.OffsetBy(10, fInfoHeight);
312 fFatBits = new TMagnify(fbRect, this);
313 fInfo->AddChild(fFatBits);
315 fFatBits->SetSelection(fShowInfo);
316 fInfo->SetMagView(fFatBits);
318 ResizeWindow(fHPixelCount, fVPixelCount);
319 UpdateInfoBarOnResize();
321 AddShortcut('S', B_COMMAND_KEY, new BMessage(msg_save));
322 AddShortcut('C', B_COMMAND_KEY, new BMessage(msg_copy_image));
323 AddShortcut('T', B_COMMAND_KEY, new BMessage(msg_show_info));
324 AddShortcut('H', B_COMMAND_KEY, new BMessage(msg_add_cross_hair));
325 AddShortcut('H', B_SHIFT_KEY, new BMessage(msg_remove_cross_hair));
326 AddShortcut('G', B_COMMAND_KEY, new BMessage(msg_toggle_grid));
327 AddShortcut('F', B_COMMAND_KEY, new BMessage(msg_freeze));
328 AddShortcut('I', B_COMMAND_KEY, new BMessage(msg_stick));
329 AddShortcut('-', B_COMMAND_KEY, new BMessage(msg_shrink));
330 AddShortcut('=', B_COMMAND_KEY, new BMessage(msg_grow));
331 AddShortcut('/', B_COMMAND_KEY, new BMessage(msg_make_square));
332 AddShortcut(',', B_COMMAND_KEY, new BMessage(msg_shrink_pixel));
333 AddShortcut('.', B_COMMAND_KEY, new BMessage(msg_grow_pixel));
337 TWindow::~TWindow()
342 status_t
343 TWindow::GetSupportedSuites(BMessage* msg)
345 msg->AddString("suites", "suite/x-vnd.Haiku-Magnify");
347 BPropertyInfo propertyInfo(sProperties);
348 msg->AddFlat("messages", &propertyInfo);
350 return BHandler::GetSupportedSuites(msg);
354 BHandler*
355 TWindow::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier,
356 int32 what, const char* property)
358 BPropertyInfo propertyInfo(sProperties);
359 if (propertyInfo.FindMatch(msg, index, specifier, what, property) >= 0)
360 return this;
362 return BHandler::ResolveSpecifier(msg, index, specifier, what, property);
366 void
367 TWindow::MessageReceived(BMessage* m)
369 bool active = fFatBits->Active();
371 switch (m->what) {
372 case B_EXECUTE_PROPERTY:
373 case B_GET_PROPERTY:
374 case B_SET_PROPERTY:
376 int32 index;
377 BMessage specifier;
378 int32 what;
379 const char* property;
380 if (m->GetCurrentSpecifier(&index, &specifier, &what, &property)
381 != B_OK)
382 return BWindow::MessageReceived(m);
384 status_t result = B_OK;
385 BMessage reply(B_REPLY);
387 BPropertyInfo propertyInfo(sProperties);
388 switch (propertyInfo.FindMatch(m, index, &specifier, what,
389 property)) {
390 case 0:
391 if (m->what == B_GET_PROPERTY)
392 result = reply.AddBool("result", fInfoBarState);
393 else if (m->what == B_SET_PROPERTY) {
394 bool showInfo;
395 result = m->FindBool("data", &showInfo);
396 if (result == B_OK) {
397 fInfoBarState = showInfo;
398 ShowInfo(fInfoBarState);
401 break;
403 case 1:
404 if (m->what == B_GET_PROPERTY)
405 result = reply.AddBool("result", fShowGrid);
406 else if (m->what == B_SET_PROPERTY) {
407 bool showGrid;
408 result = m->FindBool("data", &showGrid);
409 if (result == B_OK)
410 SetGrid(showGrid);
412 break;
414 case 2:
415 if (fHPixelCount != fVPixelCount) {
416 int32 big = fHPixelCount > fVPixelCount ? fHPixelCount
417 : fVPixelCount;
418 ResizeWindow(big, big);
420 break;
422 case 3:
423 if (m->what == B_GET_PROPERTY)
424 result = reply.AddInt32("result", fPixelSize);
425 else if (m->what == B_SET_PROPERTY) {
426 int32 zoom;
427 result = m->FindInt32("data", &zoom);
428 if (result == B_OK)
429 SetPixelSize(zoom);
431 break;
433 case 4:
434 if (m->what == B_GET_PROPERTY)
435 result = reply.AddBool("result", fFatBits->Sticked());
436 else if (m->what == B_SET_PROPERTY) {
437 bool stick;
438 result = m->FindBool("data", &stick);
439 if (result == B_OK)
440 fFatBits->MakeSticked(stick);
442 break;
444 case 5:
445 fFatBits->CopyImage();
446 break;
448 default:
449 return BWindow::MessageReceived(m);
452 if (result != B_OK) {
453 reply.what = B_MESSAGE_NOT_UNDERSTOOD;
454 reply.AddString("message", strerror(result));
455 reply.AddInt32("error", result);
458 m->SendReply(&reply);
459 break;
462 case msg_show_info:
463 if (active) {
464 fInfoBarState = !fInfoBarState;
465 ShowInfo(!fShowInfo);
467 break;
469 case msg_toggle_grid:
470 if (active)
471 SetGrid(!fShowGrid);
472 break;
474 case msg_grow:
475 if (active)
476 ResizeWindow(true);
477 break;
478 case msg_shrink:
479 if (active)
480 ResizeWindow(false);
481 break;
482 case msg_make_square:
483 if (active) {
484 if (fHPixelCount == fVPixelCount)
485 break;
486 int32 big = (fHPixelCount > fVPixelCount) ? fHPixelCount : fVPixelCount;
487 ResizeWindow(big, big);
489 break;
491 case msg_shrink_pixel:
492 if (active)
493 SetPixelSize(false);
494 break;
495 case msg_grow_pixel:
496 if (active)
497 SetPixelSize(true);
498 break;
500 case msg_add_cross_hair:
501 if (active && fShowInfo)
502 AddCrossHair();
503 break;
504 case msg_remove_cross_hair:
505 if (active && fShowInfo)
506 RemoveCrossHair();
507 break;
509 case msg_freeze:
510 if (active)
511 SetFlags(B_OUTLINE_RESIZE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE);
512 else
513 SetFlags(B_OUTLINE_RESIZE | B_NOT_ZOOMABLE);
515 fFatBits->MakeActive(!fFatBits->Active());
516 break;
518 case msg_stick:
519 fFatBits->MakeSticked(!fFatBits->Sticked());
520 break;
522 case msg_save: {
523 // freeze the image here, unfreeze after dump or cancel
524 fFatBits->StartSave();
526 BMessenger messenger(this);
527 BMessage message(msg_dump);
528 fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, 0, 0, false,
529 &message);
530 fSavePanel->SetSaveText("Bitmaps.png");
531 fSavePanel->Show();
532 } break;
533 case msg_dump:
535 delete fSavePanel;
537 entry_ref dirRef;
538 char* name;
539 m->FindRef("directory", &dirRef);
540 m->FindString((const char*)"name",(const char**) &name);
542 fFatBits->SaveImage(&dirRef, name);
544 break;
545 case B_CANCEL:
546 // image is frozen before the FilePanel is shown
547 fFatBits->EndSave();
548 break;
550 case msg_copy_image:
551 fFatBits->CopyImage();
552 break;
553 default:
554 BWindow::MessageReceived(m);
555 break;
560 bool
561 TWindow::QuitRequested()
563 SetPrefs();
564 be_app->PostMessage(B_QUIT_REQUESTED);
565 return true;
569 void
570 TWindow::GetPrefs(int32 overridePixelCount)
572 BPath path;
573 char name[8];
574 float version;
575 bool haveLoc=false;
576 BPoint loc;
577 bool showGrid = kDefaultShowGrid;
578 bool showInfo = kDefaultShowInfo;
579 bool ch1Showing=false;
580 bool ch2Showing=false;
581 int32 hPixelCount = kDefaultPixelCount;
582 int32 vPixelCount = kDefaultPixelCount;
583 int32 pixelSize = kDefaultPixelSize;
585 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
586 int ref = -1;
587 path.Append(kPrefsFileName);
588 if ((ref = open(path.Path(), 0)) >= 0) {
589 if (read(ref, name, 7) != 7)
590 goto ALMOST_DONE;
592 name[7] = 0;
593 if (strcmp(name, kAppName) != 0)
594 goto ALMOST_DONE;
596 read(ref, &version, sizeof(float));
598 if (read(ref, &loc, sizeof(BPoint)) != sizeof(BPoint))
599 goto ALMOST_DONE;
600 else
601 haveLoc = true;
603 if (read(ref, &showGrid, sizeof(bool)) != sizeof(bool)) {
604 showGrid = kDefaultShowGrid;
605 goto ALMOST_DONE;
608 if (read(ref, &showInfo, sizeof(bool)) != sizeof(bool)) {
609 showInfo = kDefaultShowInfo;
610 goto ALMOST_DONE;
613 if (read(ref, &ch1Showing, sizeof(bool)) != sizeof(bool)) {
614 ch1Showing = false;
615 goto ALMOST_DONE;
618 if (read(ref, &ch2Showing, sizeof(bool)) != sizeof(bool)) {
619 ch2Showing = false;
620 goto ALMOST_DONE;
623 if (read(ref, &hPixelCount, sizeof(int32)) != sizeof(int32)) {
624 hPixelCount = kDefaultPixelCount;
625 goto ALMOST_DONE;
627 if (read(ref, &vPixelCount, sizeof(int32)) != sizeof(int32)) {
628 vPixelCount = kDefaultPixelCount;
629 goto ALMOST_DONE;
632 if (read(ref, &pixelSize, sizeof(int32)) != sizeof(int32)) {
633 pixelSize = kDefaultPixelSize;
634 goto ALMOST_DONE;
637 ALMOST_DONE: // clean up and try to position the window
638 close(ref);
640 if (haveLoc && BScreen(B_MAIN_SCREEN_ID).Frame().Contains(loc)) {
641 MoveTo(loc);
642 goto DONE;
647 // if prefs dont yet exist or the window is not onscreen, center the window
648 CenterOnScreen();
650 // set all the settings to defaults if we get here
651 DONE:
652 fShowGrid = showGrid;
653 fShowInfo = showInfo;
654 fInfoBarState = showInfo;
655 fHPixelCount = (overridePixelCount == -1) ? hPixelCount : overridePixelCount;
656 fVPixelCount = (overridePixelCount == -1) ? vPixelCount : overridePixelCount;
657 fPixelSize = pixelSize;
661 void
662 TWindow::SetPrefs()
664 BPath path;
666 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) == B_OK) {
667 long ref;
669 path.Append (kPrefsFileName);
670 if ((ref = creat(path.Path(), S_IRUSR | S_IWUSR)) >= 0) {
671 float version = kCurrentVersion;
673 lseek (ref, 0, SEEK_SET);
674 write(ref, kAppName, 7);
675 write(ref, &version, sizeof(float));
677 BPoint loc = Frame().LeftTop();
678 write(ref, &loc, sizeof(BPoint));
680 write(ref, &fShowGrid, sizeof(bool));
681 write(ref, &fShowInfo, sizeof(bool));
682 bool ch1, ch2;
683 CrossHairsShowing(&ch1, &ch2);
684 write(ref, &ch1, sizeof(bool));
685 write(ref, &ch2, sizeof(bool));
687 write(ref, &fHPixelCount, sizeof(int32));
688 write(ref, &fVPixelCount, sizeof(int32));
689 write(ref, &fPixelSize, sizeof(int32));
691 close(ref);
697 void
698 TWindow::FrameResized(float w, float h)
700 CalcViewablePixels();
701 fFatBits->InitBuffers(fHPixelCount, fVPixelCount, fPixelSize, ShowGrid());
702 UpdateInfoBarOnResize();
706 void
707 TWindow::ScreenChanged(BRect screenSize, color_space depth)
709 BWindow::ScreenChanged(screenSize, depth);
710 // reset all bitmaps
711 fFatBits->ScreenChanged(screenSize,depth);
715 void
716 TWindow::Minimize(bool m)
718 BWindow::Minimize(m);
722 void
723 TWindow::Zoom(BPoint /*position*/, float /*width*/, float /*height*/)
725 if (fFatBits->Active())
726 ShowInfo(!fShowInfo);
730 void
731 TWindow::CalcViewablePixels()
733 float w = Bounds().Width();
734 float h = Bounds().Height();
736 if (InfoIsShowing()) {
737 w -= 20; // remove the gutter
738 h = h-fInfoHeight-10; // remove info and gutter
741 bool ch1, ch2;
742 fFatBits->CrossHairsShowing(&ch1, &ch2);
743 if (ch1)
744 h -= fFontHeight;
745 if (ch2)
746 h -= fFontHeight + 5;
748 fHPixelCount = (int32)w / fPixelSize; // calc h pixels
749 if (fHPixelCount < 16)
750 fHPixelCount = 16;
752 fVPixelCount = (int32)h / fPixelSize; // calc v pixels
753 if (fVPixelCount < 4)
754 fVPixelCount = 4;
758 void
759 TWindow::GetPreferredSize(float* width, float* height)
761 *width = fHPixelCount * fPixelSize; // calc window width
762 *height = fVPixelCount * fPixelSize; // calc window height
763 if (InfoIsShowing()) {
764 *width += 20;
765 *height += fInfoHeight + 10;
768 bool ch1, ch2;
769 fFatBits->CrossHairsShowing(&ch1, &ch2);
770 if (ch1)
771 *height += fFontHeight;
772 if (ch2)
773 *height += fFontHeight + 5;
777 void
778 TWindow::ResizeWindow(int32 hPixelCount, int32 vPixelCount)
780 fHPixelCount = hPixelCount;
781 fVPixelCount = vPixelCount;
783 float width, height;
784 GetPreferredSize(&width, &height);
786 ResizeTo(width, height);
790 void
791 TWindow::ResizeWindow(bool direction)
793 int32 x = fHPixelCount;
794 int32 y = fVPixelCount;
796 if (direction) {
797 x += 4;
798 y += 4;
799 } else {
800 x -= 4;
801 y -= 4;
804 if (x < 4)
805 x = 4;
807 if (y < 4)
808 y = 4;
810 ResizeWindow(x, y);
814 void
815 TWindow::SetGrid(bool s)
817 if (s == fShowGrid)
818 return;
820 fShowGrid = s;
821 fFatBits->SetUpdate(true);
825 bool
826 TWindow::ShowGrid()
828 return fShowGrid;
832 void
833 TWindow::ShowInfo(bool i)
835 if (i == fShowInfo)
836 return;
838 fShowInfo = i;
840 if (fShowInfo)
841 fFatBits->MoveTo(10, fInfoHeight);
842 else {
843 fFatBits->MoveTo(1,1);
844 fFatBits->SetCrossHairsShowing(false, false);
847 fFatBits->SetSelection(fShowInfo);
848 ResizeWindow(fHPixelCount, fVPixelCount);
849 fInfo->SetInfoTextVisible(i);
853 bool
854 TWindow::InfoIsShowing()
856 return fShowInfo;
860 bool
861 TWindow::InfoBarIsVisible()
863 return fInfoBarState;
867 void
868 TWindow::UpdateInfo()
870 fInfo->Invalidate();
874 void
875 TWindow::UpdateInfoBarOnResize()
877 float infoWidth, infoHeight;
878 fInfo->GetPreferredSize(&infoWidth, &infoHeight);
880 if (infoWidth > Bounds().Width()
881 || infoHeight > Bounds().Height()) {
882 ShowInfo(false);
883 } else {
884 ShowInfo(fInfoBarState);
889 void
890 TWindow::AddCrossHair()
892 fFatBits->AddCrossHair();
894 // crosshair info needs to be added
895 // window resizes accordingly
896 float width;
897 float height;
898 GetPreferredSize(&width, &height);
899 ResizeTo(width, height);
903 void
904 TWindow::RemoveCrossHair()
906 fFatBits->RemoveCrossHair();
908 // crosshair info needs to be removed
909 // window resizes accordingly
910 float width;
911 float height;
912 GetPreferredSize(&width, &height);
913 ResizeTo(width, height);
917 void
918 TWindow::CrossHairsShowing(bool* ch1, bool* ch2)
920 fFatBits->CrossHairsShowing(ch1, ch2);
924 void
925 TWindow::PixelCount(int32* h, int32 *v)
927 *h = fHPixelCount;
928 *v = fVPixelCount;
932 void
933 TWindow::SetPixelSize(int32 s)
935 if (s > 100)
936 s = 100;
937 else if (s < 1)
938 s = 1;
940 if (s == fPixelSize)
941 return;
943 fPixelSize = s;
944 // resize window
945 // tell info that size has changed
946 // tell mag that size has changed
948 float w = Bounds().Width();
949 float h = Bounds().Height();
950 CalcViewablePixels();
951 ResizeWindow(fHPixelCount, fVPixelCount);
953 // the window might not actually change in size
954 // in that case force the buffers to the new dimension
955 if (w == Bounds().Width() && h == Bounds().Height())
956 fFatBits->InitBuffers(fHPixelCount, fVPixelCount, fPixelSize, ShowGrid());
960 void
961 TWindow::SetPixelSize(bool plus)
963 int32 pixelSize;
965 if (plus) {
966 if (fPixelSize >= 16)
967 return;
969 pixelSize = fPixelSize + 1;
970 } else {
971 pixelSize = fPixelSize / 2;
973 if (pixelSize < 16) {
974 if (fPixelSize > 16)
975 pixelSize = (fPixelSize + 16) / 2;
976 else
977 pixelSize = fPixelSize - 1;
981 SetPixelSize(pixelSize);
985 int32
986 TWindow::PixelSize()
988 return fPixelSize;
992 #undef B_TRANSLATION_CONTEXT
993 #define B_TRANSLATION_CONTEXT "Magnify-Main"
996 bool
997 TWindow::IsActive()
999 return fFatBits->Active();
1003 bool
1004 TWindow::IsSticked()
1006 return fFatBits->Sticked();
1010 // #pragma mark -
1013 TInfoView::TInfoView(BRect frame)
1014 : BBox(frame, "rgb", B_FOLLOW_ALL,
1015 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS,
1016 B_NO_BORDER)
1018 SetFont(be_plain_font);
1019 fFontHeight = FontHeight(this, true);
1020 fMagView = NULL;
1022 fSelectionColor = kBlack;
1023 fCH1Loc.x = fCH1Loc.y = fCH2Loc.x = fCH2Loc.y = 0;
1025 fInfoStr[0] = 0;
1026 fRGBStr[0] = 0;
1027 fCH1Str[0] = 0;
1028 fCH2Str[0] = 0;
1030 fInfoTextVisible = true;
1034 TInfoView::~TInfoView()
1039 void
1040 TInfoView::AttachedToWindow()
1042 BBox::AttachedToWindow();
1043 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1044 dynamic_cast<TWindow*>(Window())->PixelCount(&fHPixelCount, &fVPixelCount);
1045 fPixelSize = dynamic_cast<TWindow*>(Window())->PixelSize();
1047 AddMenu();
1051 void
1052 TInfoView::Draw(BRect updateRect)
1054 PushState();
1055 SetLowColor(ViewColor());
1057 BRect invalRect;
1059 int32 hPixelCount, vPixelCount;
1060 dynamic_cast<TWindow*>(Window())->PixelCount(&hPixelCount, &vPixelCount);
1061 int32 pixelSize = dynamic_cast<TWindow*>(Window())->PixelSize();
1063 MovePenTo(15 + fPopUp->Bounds().Width(), fFontHeight + 5);
1065 static BMessageFormat format(B_TRANSLATE("%width x %height @ {0, plural, "
1066 "one{# pixel/pixel} other{# pixels/pixel}}"));
1068 BString dimensionsInfo;
1069 format.Format(dimensionsInfo, pixelSize);
1071 BString rep;
1072 rep << hPixelCount;
1073 dimensionsInfo.ReplaceAll("%width", rep);
1074 rep = "";
1075 rep << vPixelCount;
1076 dimensionsInfo.ReplaceAll("%height", rep);
1078 invalRect.Set(10, 5, 10 + StringWidth(fInfoStr), fFontHeight+7);
1079 SetHighColor(ViewColor());
1080 FillRect(invalRect);
1081 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
1082 strcpy(fInfoStr, dimensionsInfo);
1083 if (fInfoTextVisible)
1084 DrawString(fInfoStr);
1086 rgb_color color = { 0, 0, 0, 255 };
1087 if (fMagView)
1088 color = fMagView->SelectionColor();
1089 char str[64];
1090 snprintf(str, sizeof(str), "R: %i G: %i B: %i (#%02x%02x%02x)",
1091 color.red, color.green, color.blue, color.red, color.green, color.blue);
1093 MovePenTo(15 + fPopUp->Bounds().Width(), fFontHeight*2+5);
1094 invalRect.Set(10, fFontHeight+7, 10 + StringWidth(fRGBStr), fFontHeight*2+7);
1095 SetHighColor(ViewColor());
1096 FillRect(invalRect);
1097 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
1098 strcpy(fRGBStr,str);
1099 if (fInfoTextVisible)
1100 DrawString(fRGBStr);
1102 bool ch1Showing, ch2Showing;
1103 dynamic_cast<TWindow*>(Window())->CrossHairsShowing(&ch1Showing, &ch2Showing);
1105 if (fMagView) {
1106 BPoint pt1(fMagView->CrossHair1Loc());
1107 BPoint pt2(fMagView->CrossHair2Loc());
1109 float h = Bounds().Height();
1110 if (ch2Showing) {
1111 MovePenTo(10, h-12);
1112 sprintf(str, "2) x: %" B_PRIi32 " y: %" B_PRIi32 " y: %d",
1113 (int32)pt2.x, (int32)pt2.y, abs((int)(pt1.y - pt2.y)));
1114 invalRect.Set(10, h-12-fFontHeight, 10 + StringWidth(fCH2Str), h-10);
1115 SetHighColor(ViewColor());
1116 FillRect(invalRect);
1117 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
1118 strcpy(fCH2Str,str);
1119 if (fInfoTextVisible)
1120 DrawString(fCH2Str);
1123 if (ch1Showing && ch2Showing) {
1124 MovePenTo(10, h-10-fFontHeight-2);
1125 sprintf(str, "1) x: %" B_PRIi32 " y: %" B_PRIi32 " x: %d",
1126 (int32)pt1.x, (int32)pt1.y, abs((int)(pt1.x - pt2.x)));
1127 invalRect.Set(10, h-10-2*fFontHeight-2, 10 + StringWidth(fCH1Str), h-10-fFontHeight);
1128 SetHighColor(ViewColor());
1129 FillRect(invalRect);
1130 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
1131 strcpy(fCH1Str,str);
1132 if (fInfoTextVisible)
1133 DrawString(fCH1Str);
1134 } else if (ch1Showing) {
1135 MovePenTo(10, h-10);
1136 sprintf(str, "x: %" B_PRIi32 " y: %" B_PRIi32, (int32)pt1.x, (int32)pt1.y);
1137 invalRect.Set(10, h-10-fFontHeight, 10 + StringWidth(fCH1Str), h-8);
1138 SetHighColor(ViewColor());
1139 FillRect(invalRect);
1140 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
1141 strcpy(fCH1Str,str);
1142 if (fInfoTextVisible)
1143 DrawString(fCH1Str);
1147 PopState();
1151 void
1152 TInfoView::FrameResized(float width, float height)
1154 BBox::FrameResized(width, height);
1158 void
1159 TInfoView::AddMenu()
1161 fMenu = new TMenu(dynamic_cast<TWindow*>(Window()), "");
1162 BuildInfoMenu(fMenu);
1164 BRect r(9, 11, 22, 27);
1165 fPopUp = new BMenuField( r, "region menu", NULL, fMenu, true,
1166 B_FOLLOW_LEFT | B_FOLLOW_TOP);
1167 AddChild(fPopUp);
1171 void
1172 TInfoView::SetMagView(TMagnify* magView)
1174 fMagView = magView;
1178 // #pragma mark -
1181 TMenu::TMenu(TWindow *mainWindow, const char *title, menu_layout layout)
1182 : BMenu(title, layout),
1183 fMainWindow(mainWindow)
1188 TMenu::~TMenu()
1193 void
1194 TMenu::AttachedToWindow()
1196 UpdateInfoMenu(this, fMainWindow);
1198 BMenu::AttachedToWindow();
1202 void
1203 TInfoView::GetPreferredSize(float* _width, float* _height)
1205 if (_width) {
1206 float str1Width = StringWidth(fCH1Str)
1207 + StringWidth(fCH2Str)
1208 + StringWidth(fRGBStr)
1209 + 30;
1210 float str2Width = StringWidth(fInfoStr) + 30;
1211 *_width = str1Width > str2Width ? str1Width : str2Width;
1214 if (_height)
1215 *_height = fFontHeight * 2 + 10;
1219 bool
1220 TInfoView::IsInfoTextVisible()
1222 return fInfoTextVisible;
1226 void
1227 TInfoView::SetInfoTextVisible(bool visible)
1229 fInfoTextVisible = visible;
1230 Draw(Bounds());
1234 // #pragma mark -
1237 TMagnify::TMagnify(BRect r, TWindow* parent)
1238 : BView(r, "MagView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS),
1239 fNeedToUpdate(true),
1240 fThread(-1),
1241 fActive(true),
1242 fImageBuf(NULL),
1243 fImageView(NULL),
1244 fLastLoc(-1, -1),
1245 fSelection(-1),
1246 fShowSelection(false),
1247 fSelectionLoc(0, 0),
1248 fShowCrossHair1(false),
1249 fCrossHair1(-1, -1),
1250 fShowCrossHair2(false),
1251 fCrossHair2(-1, -1),
1252 fParent(parent),
1253 fStickCoordinates(false)
1258 TMagnify::~TMagnify()
1260 kill_thread(fThread);
1261 delete fImageBuf;
1265 void
1266 TMagnify::AttachedToWindow()
1268 int32 width, height;
1269 fParent->PixelCount(&width, &height);
1270 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid());
1272 fThread = spawn_thread(TMagnify::MagnifyTask, "MagnifyTask",
1273 B_NORMAL_PRIORITY, this);
1275 resume_thread(fThread);
1277 SetViewColor(B_TRANSPARENT_32_BIT);
1278 MakeFocus();
1282 void
1283 TMagnify::InitBuffers(int32 hPixelCount, int32 vPixelCount,
1284 int32 pixelSize, bool showGrid)
1286 color_space colorSpace = BScreen(Window()).ColorSpace();
1288 BRect r(0, 0, (pixelSize * hPixelCount)-1, (pixelSize * vPixelCount)-1);
1289 if (Bounds().Width() != r.Width() || Bounds().Height() != r.Height())
1290 ResizeTo(r.Width(), r.Height());
1292 if (fImageView) {
1293 fImageBuf->Lock();
1294 fImageView->RemoveSelf();
1295 fImageBuf->Unlock();
1297 fImageView->Resize((int32)r.Width(), (int32)r.Height());
1298 fImageView->SetSpace(colorSpace);
1299 } else
1300 fImageView = new TOSMagnify(r, this, colorSpace);
1302 delete fImageBuf;
1303 fImageBuf = new BBitmap(r, colorSpace, true);
1304 fImageBuf->Lock();
1305 fImageBuf->AddChild(fImageView);
1306 fImageBuf->Unlock();
1310 void
1311 TMagnify::Draw(BRect)
1313 BRect bounds(Bounds());
1314 DrawBitmap(fImageBuf, bounds, bounds);
1315 static_cast<TWindow*>(Window())->UpdateInfo();
1319 void
1320 TMagnify::KeyDown(const char *key, int32 numBytes)
1322 if (!fShowSelection)
1323 BView::KeyDown(key, numBytes);
1325 uint32 mods = modifiers();
1327 switch (key[0]) {
1328 case B_TAB:
1329 if (fShowCrossHair1) {
1330 fSelection++;
1332 if (fShowCrossHair2) {
1333 if (fSelection > 2)
1334 fSelection = 0;
1335 } else if (fShowCrossHair1) {
1336 if (fSelection > 1)
1337 fSelection = 0;
1339 fNeedToUpdate = true;
1340 Invalidate();
1342 break;
1344 case B_LEFT_ARROW:
1345 if (mods & B_OPTION_KEY)
1346 NudgeMouse(-1,0);
1347 else
1348 MoveSelection(-1,0);
1349 break;
1350 case B_RIGHT_ARROW:
1351 if (mods & B_OPTION_KEY)
1352 NudgeMouse(1, 0);
1353 else
1354 MoveSelection(1,0);
1355 break;
1356 case B_UP_ARROW:
1357 if (mods & B_OPTION_KEY)
1358 NudgeMouse(0, -1);
1359 else
1360 MoveSelection(0,-1);
1361 break;
1362 case B_DOWN_ARROW:
1363 if (mods & B_OPTION_KEY)
1364 NudgeMouse(0, 1);
1365 else
1366 MoveSelection(0,1);
1367 break;
1369 default:
1370 BView::KeyDown(key,numBytes);
1371 break;
1376 void
1377 TMagnify::FrameResized(float newW, float newH)
1379 int32 w, h;
1380 PixelCount(&w, &h);
1382 if (fSelectionLoc.x >= w)
1383 fSelectionLoc.x = 0;
1384 if (fSelectionLoc.y >= h)
1385 fSelectionLoc.y = 0;
1387 if (fShowCrossHair1) {
1388 if (fCrossHair1.x >= w) {
1389 fCrossHair1.x = fSelectionLoc.x + 2;
1390 if (fCrossHair1.x >= w)
1391 fCrossHair1.x = 0;
1393 if (fCrossHair1.y >= h) {
1394 fCrossHair1.y = fSelectionLoc.y + 2;
1395 if (fCrossHair1.y >= h)
1396 fCrossHair1.y = 0;
1399 if (fShowCrossHair2) {
1400 if (fCrossHair2.x >= w) {
1401 fCrossHair2.x = fCrossHair1.x + 2;
1402 if (fCrossHair2.x >= w)
1403 fCrossHair2.x = 0;
1405 if (fCrossHair2.y >= h) {
1406 fCrossHair2.y = fCrossHair1.y + 2;
1407 if (fCrossHair2.y >= h)
1408 fCrossHair2.y = 0;
1415 void
1416 TMagnify::MouseDown(BPoint where)
1418 BMessage *currentMsg = Window()->CurrentMessage();
1419 if (currentMsg->what == B_MOUSE_DOWN) {
1420 uint32 buttons = 0;
1421 currentMsg->FindInt32("buttons", (int32 *)&buttons);
1423 uint32 modifiers = 0;
1424 currentMsg->FindInt32("modifiers", (int32 *)&modifiers);
1426 if ((buttons & B_SECONDARY_MOUSE_BUTTON) || (modifiers & B_CONTROL_KEY)) {
1427 // secondary button was clicked or control key was down, show menu and return
1429 BPopUpMenu *menu = new BPopUpMenu(B_TRANSLATE("Info"), false, false);
1430 menu->SetFont(be_plain_font);
1431 BuildInfoMenu(menu);
1432 UpdateInfoMenu(menu, dynamic_cast<TWindow*>(Window()));
1433 BMenuItem *selected = menu->Go(ConvertToScreen(where));
1434 if (selected)
1435 Window()->PostMessage(selected->Message()->what);
1436 delete menu;
1437 return;
1440 // add a mousedown looper here
1442 int32 pixelSize = PixelSize();
1443 float x = where.x / pixelSize;
1444 float y = where.y / pixelSize;
1446 MoveSelectionTo(x, y);
1448 // draw the frozen image
1449 // update the info region
1451 fNeedToUpdate = true;
1452 Invalidate();
1457 void
1458 TMagnify::ScreenChanged(BRect, color_space)
1460 int32 width, height;
1461 fParent->PixelCount(&width, &height);
1462 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid());
1466 void
1467 TMagnify::SetSelection(bool state)
1469 if (fShowSelection == state)
1470 return;
1472 fShowSelection = state;
1473 fSelection = 0;
1474 Invalidate();
1478 void
1479 TMagnify::MoveSelection(int32 x, int32 y)
1481 if (!fShowSelection)
1482 return;
1484 int32 xCount, yCount;
1485 PixelCount(&xCount, &yCount);
1487 float xloc, yloc;
1488 if (fSelection == 0) {
1489 xloc = fSelectionLoc.x;
1490 yloc = fSelectionLoc.y;
1491 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount);
1492 fSelectionLoc.x = xloc;
1493 fSelectionLoc.y = yloc;
1494 } else if (fSelection == 1) {
1495 xloc = fCrossHair1.x;
1496 yloc = fCrossHair1.y;
1497 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount);
1498 fCrossHair1.x = xloc;
1499 fCrossHair1.y = yloc;
1500 } else if (fSelection == 2) {
1501 xloc = fCrossHair2.x;
1502 yloc = fCrossHair2.y;
1503 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount);
1504 fCrossHair2.x = xloc;
1505 fCrossHair2.y = yloc;
1508 fNeedToUpdate = true;
1509 Invalidate();
1513 void
1514 TMagnify::MoveSelectionTo(int32 x, int32 y)
1516 if (!fShowSelection)
1517 return;
1519 int32 xCount, yCount;
1520 PixelCount(&xCount, &yCount);
1521 if (x >= xCount)
1522 x = 0;
1523 if (y >= yCount)
1524 y = 0;
1526 if (fSelection == 0) {
1527 fSelectionLoc.x = x;
1528 fSelectionLoc.y = y;
1529 } else if (fSelection == 1) {
1530 fCrossHair1.x = x;
1531 fCrossHair1.y = y;
1532 } else if (fSelection == 2) {
1533 fCrossHair2.x = x;
1534 fCrossHair2.y = y;
1537 fNeedToUpdate = true;
1538 Invalidate(); //Draw(Bounds());
1542 void
1543 TMagnify::ShowSelection()
1548 short
1549 TMagnify::Selection()
1551 return fSelection;
1555 bool
1556 TMagnify::SelectionIsShowing()
1558 return fShowSelection;
1562 void
1563 TMagnify::SelectionLoc(float* x, float* y)
1565 *x = fSelectionLoc.x;
1566 *y = fSelectionLoc.y;
1570 void
1571 TMagnify::SetSelectionLoc(float x, float y)
1573 fSelectionLoc.x = x;
1574 fSelectionLoc.y = y;
1578 rgb_color
1579 TMagnify::SelectionColor()
1581 return fImageView->ColorAtSelection();
1585 void
1586 TMagnify::CrossHair1Loc(float* x, float* y)
1588 *x = fCrossHair1.x;
1589 *y = fCrossHair1.y;
1593 void
1594 TMagnify::CrossHair2Loc(float* x, float* y)
1596 *x = fCrossHair2.x;
1597 *y = fCrossHair2.y;
1601 BPoint
1602 TMagnify::CrossHair1Loc()
1604 return fCrossHair1;
1608 BPoint
1609 TMagnify::CrossHair2Loc()
1611 return fCrossHair2;
1615 void
1616 TMagnify::NudgeMouse(float x, float y)
1618 BPoint loc;
1619 uint32 button;
1621 GetMouse(&loc, &button);
1622 ConvertToScreen(&loc);
1623 loc.x += x;
1624 loc.y += y;
1626 set_mouse_position((int32)loc.x, (int32)loc.y);
1630 void
1631 TMagnify::WindowActivated(bool active)
1633 if (active)
1634 MakeFocus();
1638 status_t
1639 TMagnify::MagnifyTask(void *arg)
1641 TMagnify* view = (TMagnify*)arg;
1643 // static data members can't access members, methods without
1644 // a pointer to an instance of the class
1645 TWindow* window = (TWindow*)view->Window();
1647 while (true) {
1648 if (window->Lock()) {
1649 if (view->NeedToUpdate() || view->Active())
1650 view->Update(view->NeedToUpdate());
1652 window->Unlock();
1654 snooze(35000);
1657 return B_NO_ERROR;
1661 void
1662 TMagnify::Update(bool force)
1664 BPoint loc;
1665 uint32 button;
1666 static long counter = 0;
1668 if (!fStickCoordinates) {
1669 GetMouse(&loc, &button);
1670 ConvertToScreen(&loc);
1671 } else
1672 loc = fLastLoc;
1674 if (force || fLastLoc != loc || counter++ % 35 == 0) {
1675 if (fImageView->CreateImage(loc, force))
1676 Invalidate();
1678 counter = 0;
1679 if (force)
1680 SetUpdate(false);
1682 fLastLoc = loc;
1686 bool
1687 TMagnify::NeedToUpdate()
1689 return fNeedToUpdate;
1693 void
1694 TMagnify::SetUpdate(bool s)
1696 fNeedToUpdate = s;
1700 void
1701 TMagnify::CopyImage()
1703 StartSave();
1704 be_clipboard->Lock();
1705 be_clipboard->Clear();
1707 BMessage *message = be_clipboard->Data();
1708 if (!message) {
1709 printf(B_TRANSLATE_CONTEXT("no clip msg\n",
1710 "In console, when clipboard is empty after clicking Copy image"));
1711 return;
1714 BMessage *embeddedBitmap = new BMessage();
1715 (fImageView->Bitmap())->Archive(embeddedBitmap,false);
1716 status_t err = message->AddMessage(kBitmapMimeType, embeddedBitmap);
1717 if (err == B_OK)
1718 err = message->AddRect("rect", fImageView->Bitmap()->Bounds());
1719 if (err == B_OK)
1720 be_clipboard->Commit();
1722 be_clipboard->Unlock();
1723 EndSave();
1727 void
1728 TMagnify::AddCrossHair()
1730 if (fShowCrossHair1 && fShowCrossHair2)
1731 return;
1733 int32 w, h;
1734 PixelCount(&w, &h);
1736 if (fShowCrossHair1) {
1737 fSelection = 2;
1738 fShowCrossHair2 = true;
1739 fCrossHair2.x = fCrossHair1.x + 2;
1740 if (fCrossHair2.x >= w)
1741 fCrossHair2.x = 0;
1742 fCrossHair2.y = fCrossHair1.y + 2;
1743 if (fCrossHair2.y >= h)
1744 fCrossHair2.y = 0;
1745 } else {
1746 fSelection = 1;
1747 fShowCrossHair1 = true;
1748 fCrossHair1.x = fSelectionLoc.x + 2;
1749 if (fCrossHair1.x >= w)
1750 fCrossHair1.x = 0;
1751 fCrossHair1.y = fSelectionLoc.y + 2;
1752 if (fCrossHair1.y >= h)
1753 fCrossHair1.y = 0;
1755 Invalidate();
1759 void
1760 TMagnify::RemoveCrossHair()
1762 if (!fShowCrossHair1 && !fShowCrossHair2)
1763 return;
1765 if (fShowCrossHair2) {
1766 fSelection = 1;
1767 fShowCrossHair2 = false;
1768 } else if (fShowCrossHair1) {
1769 fSelection = 0;
1770 fShowCrossHair1 = false;
1772 Invalidate();
1776 void
1777 TMagnify::SetCrossHairsShowing(bool ch1, bool ch2)
1779 fShowCrossHair1 = ch1;
1780 fShowCrossHair2 = ch2;
1784 void
1785 TMagnify::CrossHairsShowing(bool* ch1, bool* ch2)
1787 *ch1 = fShowCrossHair1;
1788 *ch2 = fShowCrossHair2;
1792 void
1793 TMagnify::MakeActive(bool s)
1795 fActive = s;
1799 void
1800 TMagnify::MakeSticked(bool s)
1802 fStickCoordinates = s;
1806 void
1807 TMagnify::PixelCount(int32* width, int32* height)
1809 fParent->PixelCount(width, height);
1813 int32
1814 TMagnify::PixelSize()
1816 return fParent->PixelSize();
1820 bool
1821 TMagnify::ShowGrid()
1823 return fParent->ShowGrid();
1827 void
1828 TMagnify::StartSave()
1830 fImageFrozenOnSave = Active();
1831 if (fImageFrozenOnSave)
1832 MakeActive(false);
1836 void
1837 TMagnify::EndSave()
1839 if (fImageFrozenOnSave)
1840 MakeActive(true);
1844 void
1845 TMagnify::SaveImage(entry_ref* ref, char* name)
1847 // create a new file
1848 BFile file;
1849 BDirectory parentDir(ref);
1850 parentDir.CreateFile(name, &file);
1852 // Write the screenshot bitmap to the file
1853 BBitmapStream stream(fImageView->Bitmap());
1854 BTranslatorRoster* roster = BTranslatorRoster::Default();
1855 roster->Translate(&stream, NULL, NULL, &file, B_PNG_FORMAT,
1856 B_TRANSLATOR_BITMAP);
1858 BBitmap* bitmap;
1859 stream.DetachBitmap(&bitmap);
1860 // The stream takes over ownership of the bitmap
1862 // unfreeze the image, image was frozen before invoke of FilePanel
1863 EndSave();
1866 // #pragma mark -
1869 TOSMagnify::TOSMagnify(BRect r, TMagnify* parent, color_space space)
1870 : BView(r, "ImageView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS),
1871 fColorSpace(space), fParent(parent)
1873 switch (space) {
1874 case B_CMAP8:
1875 fBytesPerPixel = 1;
1876 break;
1877 case B_RGB15:
1878 case B_RGBA15:
1879 case B_RGB15_BIG:
1880 case B_RGBA15_BIG:
1881 case B_RGB16:
1882 case B_RGB16_BIG:
1883 fBytesPerPixel = 2;
1884 break;
1885 case B_RGB24:
1886 fBytesPerPixel = 3;
1887 break;
1888 case B_RGB32:
1889 case B_RGBA32:
1890 case B_RGB32_BIG:
1891 case B_RGBA32_BIG:
1892 fBytesPerPixel = 4;
1893 break;
1894 default:
1895 // uh, oh -- a color space we don't support
1896 fprintf(stderr, "Tried to run in an unsupported color space; exiting\n");
1897 exit(1);
1898 break;
1901 fPixel = NULL;
1902 fBitmap = NULL;
1903 fOldBits = NULL;
1904 InitObject();
1908 TOSMagnify::~TOSMagnify()
1910 delete fPixel;
1911 delete fBitmap;
1912 free(fOldBits);
1916 void
1917 TOSMagnify::SetSpace(color_space space)
1919 fColorSpace = space;
1920 InitObject();
1924 void
1925 TOSMagnify::InitObject()
1927 int32 w, h;
1928 fParent->PixelCount(&w, &h);
1930 delete fBitmap;
1931 BRect bitsRect(0, 0, w-1, h-1);
1932 fBitmap = new BBitmap(bitsRect, fColorSpace);
1934 free(fOldBits);
1935 fOldBits = (char*)malloc(fBitmap->BitsLength());
1937 if (!fPixel) {
1938 #if B_HOST_IS_BENDIAN
1939 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32_BIG, true);
1940 #else
1941 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32, true);
1942 #endif
1943 fPixelView = new BView(BRect(0,0,0,0), NULL, 0, 0);
1944 fPixel->Lock();
1945 fPixel->AddChild(fPixelView);
1946 fPixel->Unlock();
1951 void
1952 TOSMagnify::FrameResized(float width, float height)
1954 BView::FrameResized(width, height);
1955 InitObject();
1959 void
1960 TOSMagnify::Resize(int32 width, int32 height)
1962 ResizeTo(width, height);
1963 InitObject();
1967 bool
1968 TOSMagnify::CreateImage(BPoint mouseLoc, bool force)
1970 bool created = false;
1971 if (Window() && Window()->Lock()) {
1972 int32 width, height;
1973 fParent->PixelCount(&width, &height);
1974 int32 pixelSize = fParent->PixelSize();
1976 BRect srcRect(0, 0, width - 1, height - 1);
1977 srcRect.OffsetBy(mouseLoc.x - (width / 2),
1978 mouseLoc.y - (height / 2));
1980 if (force || CopyScreenRect(srcRect)) {
1981 srcRect.OffsetTo(BPoint(0, 0));
1982 BRect destRect(Bounds());
1984 DrawBitmap(fBitmap, srcRect, destRect);
1986 DrawGrid(width, height, destRect, pixelSize);
1987 DrawSelection();
1989 Sync();
1990 created = true;
1992 Window()->Unlock();
1993 } else
1994 printf("window problem\n");
1996 return created;
2000 bool
2001 TOSMagnify::CopyScreenRect(BRect srcRect)
2003 // constrain src rect to legal screen rect
2004 BScreen screen(Window());
2005 BRect scrnframe = screen.Frame();
2007 if (srcRect.right > scrnframe.right)
2008 srcRect.OffsetTo(scrnframe.right - srcRect.Width(), srcRect.top);
2009 if (srcRect.top < 0)
2010 srcRect.OffsetTo(srcRect.left, 0);
2012 if (srcRect.bottom > scrnframe.bottom)
2013 srcRect.OffsetTo(srcRect.left, scrnframe.bottom - srcRect.Height());
2014 if (srcRect.left < 0)
2015 srcRect.OffsetTo(0, srcRect.top);
2017 // save a copy of the bits for comparison later
2018 memcpy(fOldBits, fBitmap->Bits(), fBitmap->BitsLength());
2020 screen.ReadBitmap(fBitmap, false, &srcRect);
2022 // let caller know whether bits have actually changed
2023 return memcmp(fBitmap->Bits(), fOldBits, fBitmap->BitsLength()) != 0;
2027 void
2028 TOSMagnify::DrawGrid(int32 width, int32 height, BRect destRect, int32 pixelSize)
2030 // draw grid
2031 if (fParent->ShowGrid() && fParent->PixelSize() > 2) {
2032 BeginLineArray(width * height);
2034 // horizontal lines
2035 for (int32 i = pixelSize; i < (height * pixelSize); i += pixelSize)
2036 AddLine(BPoint(0, i), BPoint(destRect.right, i), kGridGray);
2038 // vertical lines
2039 for (int32 i = pixelSize; i < (width * pixelSize); i += pixelSize)
2040 AddLine(BPoint(i, 0), BPoint(i, destRect.bottom), kGridGray);
2042 EndLineArray();
2045 SetHighColor(kGridGray);
2046 StrokeRect(destRect);
2050 void
2051 TOSMagnify::DrawSelection()
2053 if (!fParent->SelectionIsShowing())
2054 return;
2056 float x, y;
2057 int32 pixelSize = fParent->PixelSize();
2058 int32 squareSize = pixelSize - 2;
2060 fParent->SelectionLoc(&x, &y);
2061 x *= pixelSize; x++;
2062 y *= pixelSize; y++;
2063 BRect selRect(x, y, x+squareSize, y+squareSize);
2065 short selection = fParent->Selection();
2067 PushState();
2068 SetLowColor(ViewColor());
2069 SetHighColor(kRedColor);
2070 StrokeRect(selRect);
2071 if (selection == 0) {
2072 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize));
2073 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y));
2076 bool ch1Showing, ch2Showing;
2077 fParent->CrossHairsShowing(&ch1Showing, &ch2Showing);
2078 if (ch1Showing) {
2079 SetHighColor(kBlueColor);
2080 fParent->CrossHair1Loc(&x, &y);
2081 x *= pixelSize; x++;
2082 y *= pixelSize; y++;
2083 selRect.Set(x, y,x+squareSize, y+squareSize);
2084 StrokeRect(selRect);
2085 BeginLineArray(4);
2086 AddLine(BPoint(0, y+(squareSize/2)),
2087 BPoint(x, y+(squareSize/2)), kBlueColor); // left
2088 AddLine(BPoint(x+squareSize,y+(squareSize/2)),
2089 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right
2090 AddLine(BPoint(x+(squareSize/2), 0),
2091 BPoint(x+(squareSize/2), y), kBlueColor); // top
2092 AddLine(BPoint(x+(squareSize/2), y+squareSize),
2093 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom
2094 EndLineArray();
2095 if (selection == 1) {
2096 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize));
2097 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y));
2100 if (ch2Showing) {
2101 SetHighColor(kBlueColor);
2102 fParent->CrossHair2Loc(&x, &y);
2103 x *= pixelSize; x++;
2104 y *= pixelSize; y++;
2105 selRect.Set(x, y,x+squareSize, y+squareSize);
2106 StrokeRect(selRect);
2107 BeginLineArray(4);
2108 AddLine(BPoint(0, y+(squareSize/2)),
2109 BPoint(x, y+(squareSize/2)), kBlueColor); // left
2110 AddLine(BPoint(x+squareSize,y+(squareSize/2)),
2111 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right
2112 AddLine(BPoint(x+(squareSize/2), 0),
2113 BPoint(x+(squareSize/2), y), kBlueColor); // top
2114 AddLine(BPoint(x+(squareSize/2), y+squareSize),
2115 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom
2116 EndLineArray();
2117 if (selection == 2) {
2118 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize));
2119 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y));
2123 PopState();
2127 rgb_color
2128 TOSMagnify::ColorAtSelection()
2130 float x, y;
2131 fParent->SelectionLoc(&x, &y);
2132 BRect srcRect(x, y, x, y);
2133 BRect dstRect(0, 0, 0, 0);
2134 fPixel->Lock();
2135 fPixelView->DrawBitmap(fBitmap, srcRect, dstRect);
2136 fPixelView->Sync();
2137 fPixel->Unlock();
2139 uint32 pixel = *((uint32*)fPixel->Bits());
2140 rgb_color c;
2141 c.alpha = pixel >> 24;
2142 c.red = (pixel >> 16) & 0xFF;
2143 c.green = (pixel >> 8) & 0xFF;
2144 c.blue = pixel & 0xFF;
2146 return c;
2150 // #pragma mark -
2154 main(int argc, char* argv[])
2156 int32 pixelCount = -1;
2158 if (argc > 2) {
2159 printf(B_TRANSLATE_CONTEXT(
2160 "usage: magnify [size] (magnify size * size pixels)\n",
2161 "Console"));
2162 exit(1);
2163 } else {
2164 if (argc == 2) {
2165 pixelCount = abs(atoi(argv[1]));
2167 if ((pixelCount > 100) || (pixelCount < 4)) {
2168 printf(B_TRANSLATE_CONTEXT(
2169 "usage: magnify [size] (magnify size * size pixels)\n",
2170 "Console"));
2171 printf(B_TRANSLATE_CONTEXT(
2172 " size must be > 4 and a multiple of 4\n",
2173 "Console"));
2174 exit(1);
2177 if (pixelCount % 4) {
2178 printf(B_TRANSLATE_CONTEXT(
2179 "magnify: size must be a multiple of 4\n",
2180 "Console"));
2181 exit(1);
2186 TApp app(pixelCount);
2187 app.Run();
2188 return 0;