1 /*
2 * Copyright 2002-2009, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Updated by Sikosis (beos@gravity24hr.com)
6 *
7 * Copyright 1999, Be Incorporated. All Rights Reserved.
8 * This file may be used under the terms of the Be Sample Code License.
9 */
10
11 #include "Magnify.h"
12
13 #include <Alert.h>
14 #include <Bitmap.h>
15 #include <BitmapStream.h>
16 #include <Catalog.h>
17 #include <Clipboard.h>
18 #include <Debug.h>
19 #include <Directory.h>
20 #include <File.h>
21 #include <FindDirectory.h>
22 #include <Locale.h>
23 #include <MenuItem.h>
24 #include <MenuField.h>
25 #include <NodeInfo.h>
26 #include <Path.h>
27 #include <PopUpMenu.h>
28 #include <PropertyInfo.h>
29 #include <Screen.h>
30 #include <ScrollView.h>
31 #include <StringFormat.h>
32 #include <TextView.h>
33 #include <TranslationUtils.h>
34 #include <TranslatorRoster.h>
35 #include <WindowScreen.h>
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <sys/stat.h>
43
44
45 #undef B_TRANSLATION_CONTEXT
46 #define B_TRANSLATION_CONTEXT "Magnify-Main"
47
48
49 const int32 msg_update_info = 'info';
50 const int32 msg_show_info = 'show';
51 const int32 msg_toggle_grid = 'grid';
52 const int32 msg_shrink = 'shnk';
53 const int32 msg_grow = 'grow';
54 const int32 msg_make_square = 'sqar';
55 const int32 msg_shrink_pixel = 'pshk';
56 const int32 msg_grow_pixel = 'pgrw';
57
58 const int32 msg_mouse_left = 'mslf';
59 const int32 msg_mouse_right = 'msrt';
60 const int32 msg_mouse_up = 'msup';
61 const int32 msg_mouse_down = 'msdn';
62
63 const int32 msg_new_color = 'colr';
64 const int32 msg_toggle_ruler = 'rulr';
65 const int32 msg_copy_image = 'copy';
66 const int32 msg_track_color = 'trak';
67 const int32 msg_freeze = 'frez';
68 const int32 msg_stick = 'stic';
69 const int32 msg_dump = 'dump';
70 const int32 msg_add_cross_hair = 'acrs';
71 const int32 msg_remove_cross_hair = 'rcrs';
72 const int32 msg_save = 'save';
73
74 const rgb_color kViewGray = { 216, 216, 216, 255};
75 const rgb_color kGridGray = {130, 130, 130, 255 };
76 const rgb_color kWhite = { 255, 255, 255, 255};
77 const rgb_color kBlack = { 0, 0, 0, 255};
78 const rgb_color kDarkGray = { 96, 96, 96, 255};
79 const rgb_color kRedColor = { 255, 10, 50, 255 };
80 const rgb_color kGreenColor = { 10, 255, 50, 255 };
81 const rgb_color kBlueColor = { 10, 50, 255, 255 };
82
83 const char* const kBitmapMimeType = "image/x-vnd.Be-bitmap";
84
85 const float kCurrentVersion = 1.2;
86 const char *kPrefsFileName = "Magnify_prefs";
87
88 // prefs are:
89 // name = Magnify
90 // version
91 // show grid
92 // show info (rgb, location)
93 // pixel count
94 // pixel size
95 const char* const kAppName = "Magnify";
96 const bool kDefaultShowGrid = true;
97 const bool kDefaultShowInfo = true;
98 const int32 kDefaultPixelCount = 32;
99 const int32 kDefaultPixelSize = 8;
100
101 // each info region will be:
102 // top-bottom: 5 fontheight 5 fontheight 5
103 // left-right: 10 minwindowwidth 10
104 const int32 kBorderSize = 10;
105
106
107 static property_info sProperties[] = {
108 { "Info", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
109 { B_DIRECT_SPECIFIER, 0 },
110 "Show/hide info.", 0,
111 { B_BOOL_TYPE }
112 },
113 { "Grid", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
114 { B_DIRECT_SPECIFIER, 0 },
115 "Show/hide grid.", 0,
116 { B_BOOL_TYPE }
117 },
118 { "MakeSquare", { B_EXECUTE_PROPERTY, 0 },
119 { B_DIRECT_SPECIFIER, 0 },
120 "Make the view square.", 0,
121 },
122 { "Zoom", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
123 { B_DIRECT_SPECIFIER, 0 },
124 "Gets/sets the zoom factor (1-16).", 0,
125 { B_INT32_TYPE }
126 },
127 { "Stick", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
128 { B_DIRECT_SPECIFIER, 0 },
129 "Stick/unstick coordinates.", 0,
130 { B_BOOL_TYPE }
131 },
132 { "CopyImage", { B_EXECUTE_PROPERTY, 0 },
133 { B_DIRECT_SPECIFIER, 0 },
134 "Copy image to clipboard.", 0,
135 },
136
137 { 0 }
138 };
139
140
141 static float
FontHeight(BView * target,bool full)142 FontHeight(BView* target, bool full)
143 {
144 font_height finfo;
145 target->GetFontHeight(&finfo);
146 float h = ceil(finfo.ascent) + ceil(finfo.descent);
147
148 if (full)
149 h += ceil(finfo.leading);
150
151 return h;
152 }
153
154
155 static void
BoundsSelection(int32 incX,int32 incY,float * x,float * y,int32 xCount,int32 yCount)156 BoundsSelection(int32 incX, int32 incY, float* x, float* y,
157 int32 xCount, int32 yCount)
158 {
159 *x += incX;
160 *y += incY;
161
162 if (*x < 0)
163 *x = xCount-1;
164 if (*x >= xCount)
165 *x = 0;
166
167 if (*y < 0)
168 *y = yCount-1;
169 if (*y >= yCount)
170 *y = 0;
171 }
172
173
174 static void
BuildInfoMenu(BMenu * menu)175 BuildInfoMenu(BMenu *menu)
176 {
177 BMenuItem* menuItem;
178 menuItem = new BMenuItem(B_TRANSLATE("Save image"),
179 new BMessage(msg_save), 'S');
180 menu->AddItem(menuItem);
181 // menuItem = new BMenuItem(B_TRANSLATE("Save selection"),
182 // new BMessage(msg_save), 'S');
183 // menu->AddItem(menuItem);
184 menuItem = new BMenuItem(B_TRANSLATE("Copy image"),
185 new BMessage(msg_copy_image), 'C');
186 menu->AddItem(menuItem);
187 menu->AddSeparatorItem();
188
189 menuItem = new BMenuItem(B_TRANSLATE("Show info"),
190 new BMessage(msg_show_info), 'T');
191 menu->AddItem(menuItem);
192 menuItem = new BMenuItem(B_TRANSLATE("Add a crosshair"),
193 new BMessage(msg_add_cross_hair), 'H');
194 menu->AddItem(menuItem);
195 menuItem = new BMenuItem(B_TRANSLATE("Remove a crosshair"),
196 new BMessage(msg_remove_cross_hair), 'H', B_SHIFT_KEY);
197 menu->AddItem(menuItem);
198 menuItem = new BMenuItem(B_TRANSLATE("Show grid"),
199 new BMessage(msg_toggle_grid), 'G');
200 menu->AddItem(menuItem);
201 menu->AddSeparatorItem();
202
203 menuItem = new BMenuItem(B_TRANSLATE("Freeze image"),
204 new BMessage(msg_freeze), 'F');
205 menu->AddItem(menuItem);
206 menuItem = new BMenuItem(B_TRANSLATE("Stick coordinates"),
207 new BMessage(msg_stick), 'I');
208 menu->AddItem(menuItem);
209 menu->AddSeparatorItem();
210
211 menuItem = new BMenuItem(B_TRANSLATE("Make square"),
212 new BMessage(msg_make_square), '/');
213 menu->AddItem(menuItem);
214 menuItem = new BMenuItem(B_TRANSLATE("Decrease window size"),
215 new BMessage(msg_shrink), '-');
216 menu->AddItem(menuItem);
217 menuItem = new BMenuItem(B_TRANSLATE("Increase window size"),
218 new BMessage(msg_grow), '+');
219 menu->AddItem(menuItem);
220 menuItem = new BMenuItem(B_TRANSLATE("Decrease pixel size"),
221 new BMessage(msg_shrink_pixel), ',');
222 menu->AddItem(menuItem);
223 menuItem = new BMenuItem(B_TRANSLATE("Increase pixel size"),
224 new BMessage(msg_grow_pixel), '.');
225 menu->AddItem(menuItem);
226 }
227
228 static void
UpdateInfoMenu(BMenu * menu,TWindow * window)229 UpdateInfoMenu(BMenu *menu, TWindow *window)
230 {
231 bool state = true;
232 bool showGrid = true;
233 bool infoBarIsVisible = true;
234 bool stickCordinates = true;
235 if (window) {
236 state = window->IsActive();
237 showGrid = window->ShowGrid();
238 infoBarIsVisible = window->InfoBarIsVisible();
239 stickCordinates = window->IsSticked();
240 }
241 BMenuItem* menuItem = menu->FindItem(B_TRANSLATE("Show info"));
242 if (menuItem) {
243 menuItem->SetEnabled(state);
244 menuItem->SetMarked(infoBarIsVisible);
245 }
246 menuItem = menu->FindItem(B_TRANSLATE("Add a crosshair"));
247 if (menuItem)
248 menuItem->SetEnabled(state);
249 menuItem = menu->FindItem(B_TRANSLATE("Remove a crosshair"));
250 if (menuItem)
251 menuItem->SetEnabled(state);
252 menuItem = menu->FindItem(B_TRANSLATE("Show grid"));
253 if (menuItem) {
254 menuItem->SetEnabled(state);
255 menuItem->SetMarked(showGrid);
256 }
257 menuItem = menu->FindItem(B_TRANSLATE("Freeze image"));
258 if (menuItem) {
259 menuItem->SetMarked(!state);
260 }
261 menuItem = menu->FindItem(B_TRANSLATE("Stick coordinates"));
262 if (menuItem) {
263 menuItem->SetMarked(stickCordinates);
264 }
265 menuItem = menu->FindItem(B_TRANSLATE("Make square"));
266 if (menuItem)
267 menuItem->SetEnabled(state);
268 menuItem = menu->FindItem(B_TRANSLATE("Decrease window size"));
269 if (menuItem)
270 menuItem->SetEnabled(state);
271 menuItem = menu->FindItem(B_TRANSLATE("Increase window size"));
272 if (menuItem)
273 menuItem->SetEnabled(state);
274 menuItem = menu->FindItem(B_TRANSLATE("Decrease pixel size"));
275 if (menuItem)
276 menuItem->SetEnabled(state);
277 menuItem = menu->FindItem(B_TRANSLATE("Increase pixel size"));
278 if (menuItem)
279 menuItem->SetEnabled(state);
280 }
281
282 // #pragma mark -
283
284
285 // pass in pixelCount to maintain backward compatibility of setting
286 // the pixelcount from the command line
TApp(int32 pixelCount)287 TApp::TApp(int32 pixelCount)
288 : BApplication("application/x-vnd.Haiku-Magnify")
289 {
290 TWindow* magWindow = new TWindow(pixelCount);
291 magWindow->Show();
292 }
293
294
295 // #pragma mark -
296
297
TWindow(int32 pixelCount)298 TWindow::TWindow(int32 pixelCount)
299 :
300 BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Magnify"),
301 B_TITLED_WINDOW, B_OUTLINE_RESIZE)
302 {
303 GetPrefs(pixelCount);
304
305 // add info view
306 BRect infoRect(Bounds());
307 infoRect.InsetBy(-1, -1);
308 fInfo = new TInfoView(infoRect);
309 AddChild(fInfo);
310
311 fFontHeight = FontHeight(fInfo, true);
312 fInfoHeight = (fFontHeight * 2) + (3 * 5);
313
314 BRect fbRect(0, 0, (fHPixelCount*fPixelSize), (fHPixelCount*fPixelSize));
315 if (InfoIsShowing())
316 fbRect.OffsetBy(10, fInfoHeight);
317 fFatBits = new TMagnify(fbRect, this);
318 fInfo->AddChild(fFatBits);
319
320 fFatBits->SetSelection(fShowInfo);
321 fInfo->SetMagView(fFatBits);
322
323 ResizeWindow(fHPixelCount, fVPixelCount);
324 UpdateInfoBarOnResize();
325
326 AddShortcut('S', B_COMMAND_KEY, new BMessage(msg_save));
327 AddShortcut('C', B_COMMAND_KEY, new BMessage(msg_copy_image));
328 AddShortcut('T', B_COMMAND_KEY, new BMessage(msg_show_info));
329 AddShortcut('H', B_COMMAND_KEY, new BMessage(msg_add_cross_hair));
330 AddShortcut('H', B_SHIFT_KEY, new BMessage(msg_remove_cross_hair));
331 AddShortcut('G', B_COMMAND_KEY, new BMessage(msg_toggle_grid));
332 AddShortcut('F', B_COMMAND_KEY, new BMessage(msg_freeze));
333 AddShortcut('I', B_COMMAND_KEY, new BMessage(msg_stick));
334 AddShortcut('-', B_COMMAND_KEY, new BMessage(msg_shrink));
335 AddShortcut('=', B_COMMAND_KEY, new BMessage(msg_grow));
336 AddShortcut('/', B_COMMAND_KEY, new BMessage(msg_make_square));
337 AddShortcut(',', B_COMMAND_KEY, new BMessage(msg_shrink_pixel));
338 AddShortcut('.', B_COMMAND_KEY, new BMessage(msg_grow_pixel));
339 AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY, new BMessage(msg_mouse_left));
340 AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY, new BMessage(msg_mouse_right));
341 AddShortcut(B_UP_ARROW, B_COMMAND_KEY, new BMessage(msg_mouse_up));
342 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY, new BMessage(msg_mouse_down));
343 }
344
345
~TWindow()346 TWindow::~TWindow()
347 {
348 }
349
350
351 status_t
GetSupportedSuites(BMessage * msg)352 TWindow::GetSupportedSuites(BMessage* msg)
353 {
354 msg->AddString("suites", "suite/x-vnd.Haiku-Magnify");
355
356 BPropertyInfo propertyInfo(sProperties);
357 msg->AddFlat("messages", &propertyInfo);
358
359 return BWindow::GetSupportedSuites(msg);
360 }
361
362
363 BHandler*
ResolveSpecifier(BMessage * msg,int32 index,BMessage * specifier,int32 what,const char * property)364 TWindow::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier,
365 int32 what, const char* property)
366 {
367 BPropertyInfo propertyInfo(sProperties);
368 if (propertyInfo.FindMatch(msg, index, specifier, what, property) >= 0)
369 return this;
370
371 return BWindow::ResolveSpecifier(msg, index, specifier, what, property);
372 }
373
374
375 void
MessageReceived(BMessage * m)376 TWindow::MessageReceived(BMessage* m)
377 {
378 bool active = fFatBits->Active();
379
380 switch (m->what) {
381 case B_EXECUTE_PROPERTY:
382 case B_GET_PROPERTY:
383 case B_SET_PROPERTY:
384 {
385 int32 index;
386 BMessage specifier;
387 int32 what;
388 const char* property;
389 if (m->GetCurrentSpecifier(&index, &specifier, &what, &property)
390 != B_OK)
391 return BWindow::MessageReceived(m);
392
393 status_t result = B_OK;
394 BMessage reply(B_REPLY);
395
396 BPropertyInfo propertyInfo(sProperties);
397 switch (propertyInfo.FindMatch(m, index, &specifier, what,
398 property)) {
399 case 0:
400 if (m->what == B_GET_PROPERTY)
401 result = reply.AddBool("result", fInfoBarState);
402 else if (m->what == B_SET_PROPERTY) {
403 bool showInfo;
404 result = m->FindBool("data", &showInfo);
405 if (result == B_OK) {
406 fInfoBarState = showInfo;
407 ShowInfo(fInfoBarState);
408 }
409 }
410 break;
411
412 case 1:
413 if (m->what == B_GET_PROPERTY)
414 result = reply.AddBool("result", fShowGrid);
415 else if (m->what == B_SET_PROPERTY) {
416 bool showGrid;
417 result = m->FindBool("data", &showGrid);
418 if (result == B_OK)
419 SetGrid(showGrid);
420 }
421 break;
422
423 case 2:
424 if (fHPixelCount != fVPixelCount) {
425 int32 big = fHPixelCount > fVPixelCount ? fHPixelCount
426 : fVPixelCount;
427 ResizeWindow(big, big);
428 }
429 break;
430
431 case 3:
432 if (m->what == B_GET_PROPERTY)
433 result = reply.AddInt32("result", fPixelSize);
434 else if (m->what == B_SET_PROPERTY) {
435 int32 zoom;
436 result = m->FindInt32("data", &zoom);
437 if (result == B_OK)
438 SetPixelSize(zoom);
439 }
440 break;
441
442 case 4:
443 if (m->what == B_GET_PROPERTY)
444 result = reply.AddBool("result", fFatBits->Sticked());
445 else if (m->what == B_SET_PROPERTY) {
446 bool stick;
447 result = m->FindBool("data", &stick);
448 if (result == B_OK)
449 fFatBits->MakeSticked(stick);
450 }
451 break;
452
453 case 5:
454 fFatBits->CopyImage();
455 break;
456
457 default:
458 return BWindow::MessageReceived(m);
459 }
460
461 if (result != B_OK) {
462 reply.what = B_MESSAGE_NOT_UNDERSTOOD;
463 reply.AddString("message", strerror(result));
464 reply.AddInt32("error", result);
465 }
466
467 m->SendReply(&reply);
468 break;
469 }
470
471 case msg_show_info:
472 if (active) {
473 fInfoBarState = !fInfoBarState;
474 ShowInfo(!fShowInfo);
475 }
476 break;
477
478 case msg_toggle_grid:
479 if (active)
480 SetGrid(!fShowGrid);
481 break;
482
483 case msg_grow:
484 if (active)
485 ResizeWindow(true);
486 break;
487 case msg_shrink:
488 if (active)
489 ResizeWindow(false);
490 break;
491 case msg_make_square:
492 if (active) {
493 if (fHPixelCount == fVPixelCount)
494 break;
495 int32 big = (fHPixelCount > fVPixelCount) ? fHPixelCount : fVPixelCount;
496 ResizeWindow(big, big);
497 }
498 break;
499
500 case msg_shrink_pixel:
501 if (active)
502 SetPixelSize(false);
503 break;
504 case msg_grow_pixel:
505 if (active)
506 SetPixelSize(true);
507 break;
508
509 case msg_mouse_left:
510 if (active)
511 fFatBits->NudgeMouse(-1, 0);
512 break;
513 case msg_mouse_right:
514 if (active)
515 fFatBits->NudgeMouse(1, 0);
516 break;
517 case msg_mouse_up:
518 if (active)
519 fFatBits->NudgeMouse(0, -1);
520 break;
521 case msg_mouse_down:
522 if (active)
523 fFatBits->NudgeMouse(0, 1);
524 break;
525
526 case msg_add_cross_hair:
527 if (active && fShowInfo)
528 AddCrossHair();
529 break;
530 case msg_remove_cross_hair:
531 if (active && fShowInfo)
532 RemoveCrossHair();
533 break;
534
535 case msg_freeze:
536 if (active)
537 SetFlags(B_OUTLINE_RESIZE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE);
538 else
539 SetFlags(B_OUTLINE_RESIZE | B_NOT_ZOOMABLE);
540
541 fFatBits->MakeActive(!fFatBits->Active());
542 break;
543
544 case msg_stick:
545 fFatBits->MakeSticked(!fFatBits->Sticked());
546 break;
547
548 case msg_save: {
549 // freeze the image here, unfreeze after dump or cancel
550 fFatBits->StartSave();
551
552 BMessenger messenger(this);
553 BMessage message(msg_dump);
554 fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, 0, 0, false,
555 &message);
556 fSavePanel->SetSaveText("Bitmaps.png");
557 fSavePanel->Show();
558 } break;
559 case msg_dump:
560 {
561 delete fSavePanel;
562
563 entry_ref dirRef;
564 char* name;
565 m->FindRef("directory", &dirRef);
566 m->FindString((const char*)"name",(const char**) &name);
567
568 fFatBits->SaveImage(&dirRef, name);
569 }
570 break;
571 case B_CANCEL:
572 // image is frozen before the FilePanel is shown
573 fFatBits->EndSave();
574 break;
575
576 case msg_copy_image:
577 fFatBits->CopyImage();
578 break;
579 default:
580 BWindow::MessageReceived(m);
581 break;
582 }
583 }
584
585
586 bool
QuitRequested()587 TWindow::QuitRequested()
588 {
589 SetPrefs();
590 be_app->PostMessage(B_QUIT_REQUESTED);
591 return true;
592 }
593
594
595 void
GetPrefs(int32 overridePixelCount)596 TWindow::GetPrefs(int32 overridePixelCount)
597 {
598 BPath path;
599 char name[8];
600 float version;
601 bool haveLoc=false;
602 BPoint loc;
603 bool showGrid = kDefaultShowGrid;
604 bool showInfo = kDefaultShowInfo;
605 bool ch1Showing=false;
606 bool ch2Showing=false;
607 int32 hPixelCount = kDefaultPixelCount;
608 int32 vPixelCount = kDefaultPixelCount;
609 int32 pixelSize = kDefaultPixelSize;
610
611 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
612 int ref = -1;
613 path.Append(kPrefsFileName);
614 if ((ref = open(path.Path(), 0)) >= 0) {
615 if (read(ref, name, 7) != 7)
616 goto ALMOST_DONE;
617
618 name[7] = 0;
619 if (strcmp(name, kAppName) != 0)
620 goto ALMOST_DONE;
621
622 read(ref, &version, sizeof(float));
623
624 if (read(ref, &loc, sizeof(BPoint)) != sizeof(BPoint))
625 goto ALMOST_DONE;
626 else
627 haveLoc = true;
628
629 if (read(ref, &showGrid, sizeof(bool)) != sizeof(bool)) {
630 showGrid = kDefaultShowGrid;
631 goto ALMOST_DONE;
632 }
633
634 if (read(ref, &showInfo, sizeof(bool)) != sizeof(bool)) {
635 showInfo = kDefaultShowInfo;
636 goto ALMOST_DONE;
637 }
638
639 if (read(ref, &ch1Showing, sizeof(bool)) != sizeof(bool)) {
640 ch1Showing = false;
641 goto ALMOST_DONE;
642 }
643
644 if (read(ref, &ch2Showing, sizeof(bool)) != sizeof(bool)) {
645 ch2Showing = false;
646 goto ALMOST_DONE;
647 }
648
649 if (read(ref, &hPixelCount, sizeof(int32)) != sizeof(int32)) {
650 hPixelCount = kDefaultPixelCount;
651 goto ALMOST_DONE;
652 }
653 if (read(ref, &vPixelCount, sizeof(int32)) != sizeof(int32)) {
654 vPixelCount = kDefaultPixelCount;
655 goto ALMOST_DONE;
656 }
657
658 if (read(ref, &pixelSize, sizeof(int32)) != sizeof(int32)) {
659 pixelSize = kDefaultPixelSize;
660 goto ALMOST_DONE;
661 }
662
663 ALMOST_DONE: // clean up and try to position the window
664 close(ref);
665
666 if (haveLoc && BScreen(B_MAIN_SCREEN_ID).Frame().Contains(loc)) {
667 MoveTo(loc);
668 goto DONE;
669 }
670 }
671 }
672
673 // if prefs dont yet exist or the window is not onscreen, center the window
674 CenterOnScreen();
675
676 // set all the settings to defaults if we get here
677 DONE:
678 fShowGrid = showGrid;
679 fShowInfo = showInfo;
680 fInfoBarState = showInfo;
681 fHPixelCount = (overridePixelCount == -1) ? hPixelCount : overridePixelCount;
682 fVPixelCount = (overridePixelCount == -1) ? vPixelCount : overridePixelCount;
683 fPixelSize = pixelSize;
684 }
685
686
687 void
SetPrefs()688 TWindow::SetPrefs()
689 {
690 BPath path;
691
692 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) == B_OK) {
693 long ref;
694
695 path.Append (kPrefsFileName);
696 if ((ref = creat(path.Path(), S_IRUSR | S_IWUSR)) >= 0) {
697 float version = kCurrentVersion;
698
699 lseek (ref, 0, SEEK_SET);
700 write(ref, kAppName, 7);
701 write(ref, &version, sizeof(float));
702
703 BPoint loc = Frame().LeftTop();
704 write(ref, &loc, sizeof(BPoint));
705
706 write(ref, &fShowGrid, sizeof(bool));
707 write(ref, &fShowInfo, sizeof(bool));
708 bool ch1, ch2;
709 CrossHairsShowing(&ch1, &ch2);
710 write(ref, &ch1, sizeof(bool));
711 write(ref, &ch2, sizeof(bool));
712
713 write(ref, &fHPixelCount, sizeof(int32));
714 write(ref, &fVPixelCount, sizeof(int32));
715 write(ref, &fPixelSize, sizeof(int32));
716
717 close(ref);
718 }
719 }
720 }
721
722
723 void
FrameResized(float w,float h)724 TWindow::FrameResized(float w, float h)
725 {
726 CalcViewablePixels();
727 fFatBits->InitBuffers(fHPixelCount, fVPixelCount, fPixelSize, ShowGrid());
728 UpdateInfoBarOnResize();
729 }
730
731
732 void
ScreenChanged(BRect screenSize,color_space depth)733 TWindow::ScreenChanged(BRect screenSize, color_space depth)
734 {
735 BWindow::ScreenChanged(screenSize, depth);
736 // reset all bitmaps
737 fFatBits->ScreenChanged(screenSize,depth);
738 }
739
740
741 void
Minimize(bool m)742 TWindow::Minimize(bool m)
743 {
744 BWindow::Minimize(m);
745 }
746
747
748 void
Zoom(BPoint,float,float)749 TWindow::Zoom(BPoint /*position*/, float /*width*/, float /*height*/)
750 {
751 if (fFatBits->Active())
752 ShowInfo(!fShowInfo);
753 }
754
755
756 void
CalcViewablePixels()757 TWindow::CalcViewablePixels()
758 {
759 float w = Bounds().Width();
760 float h = Bounds().Height();
761
762 if (InfoIsShowing()) {
763 w -= 20; // remove the gutter
764 h = h-fInfoHeight-10; // remove info and gutter
765 }
766
767 bool ch1, ch2;
768 fFatBits->CrossHairsShowing(&ch1, &ch2);
769 if (ch1)
770 h -= fFontHeight;
771 if (ch2)
772 h -= fFontHeight + 5;
773
774 fHPixelCount = (int32)w / fPixelSize; // calc h pixels
775 if (fHPixelCount < 16)
776 fHPixelCount = 16;
777
778 fVPixelCount = (int32)h / fPixelSize; // calc v pixels
779 if (fVPixelCount < 4)
780 fVPixelCount = 4;
781 }
782
783
784 void
GetPreferredSize(float * width,float * height)785 TWindow::GetPreferredSize(float* width, float* height)
786 {
787 *width = fHPixelCount * fPixelSize; // calc window width
788 *height = fVPixelCount * fPixelSize; // calc window height
789 if (InfoIsShowing()) {
790 *width += 20;
791 *height += fInfoHeight + 10;
792 }
793
794 bool ch1, ch2;
795 fFatBits->CrossHairsShowing(&ch1, &ch2);
796 if (ch1)
797 *height += fFontHeight;
798 if (ch2)
799 *height += fFontHeight + 5;
800 }
801
802
803 void
ResizeWindow(int32 hPixelCount,int32 vPixelCount)804 TWindow::ResizeWindow(int32 hPixelCount, int32 vPixelCount)
805 {
806 fHPixelCount = hPixelCount;
807 fVPixelCount = vPixelCount;
808
809 float width, height;
810 GetPreferredSize(&width, &height);
811
812 ResizeTo(width, height);
813 }
814
815
816 void
ResizeWindow(bool direction)817 TWindow::ResizeWindow(bool direction)
818 {
819 int32 x = fHPixelCount;
820 int32 y = fVPixelCount;
821
822 if (direction) {
823 x += 4;
824 y += 4;
825 } else {
826 x -= 4;
827 y -= 4;
828 }
829
830 if (x < 4)
831 x = 4;
832
833 if (y < 4)
834 y = 4;
835
836 ResizeWindow(x, y);
837 }
838
839
840 void
SetGrid(bool s)841 TWindow::SetGrid(bool s)
842 {
843 if (s == fShowGrid)
844 return;
845
846 fShowGrid = s;
847 fFatBits->SetUpdate(true);
848 }
849
850
851 bool
ShowGrid()852 TWindow::ShowGrid()
853 {
854 return fShowGrid;
855 }
856
857
858 void
ShowInfo(bool i)859 TWindow::ShowInfo(bool i)
860 {
861 if (i == fShowInfo)
862 return;
863
864 fShowInfo = i;
865
866 if (fShowInfo)
867 fFatBits->MoveTo(10, fInfoHeight);
868 else {
869 fFatBits->MoveTo(1,1);
870 fFatBits->SetCrossHairsShowing(false, false);
871 }
872
873 fFatBits->SetSelection(fShowInfo);
874 ResizeWindow(fHPixelCount, fVPixelCount);
875 fInfo->SetInfoTextVisible(i);
876 }
877
878
879 bool
InfoIsShowing()880 TWindow::InfoIsShowing()
881 {
882 return fShowInfo;
883 }
884
885
886 bool
InfoBarIsVisible()887 TWindow::InfoBarIsVisible()
888 {
889 return fInfoBarState;
890 }
891
892
893 void
UpdateInfo()894 TWindow::UpdateInfo()
895 {
896 fInfo->Invalidate();
897 }
898
899
900 void
UpdateInfoBarOnResize()901 TWindow::UpdateInfoBarOnResize()
902 {
903 float infoWidth, infoHeight;
904 fInfo->GetPreferredSize(&infoWidth, &infoHeight);
905
906 if (infoWidth > Bounds().Width()
907 || infoHeight > Bounds().Height()) {
908 ShowInfo(false);
909 } else {
910 ShowInfo(fInfoBarState);
911 }
912 }
913
914
915 void
AddCrossHair()916 TWindow::AddCrossHair()
917 {
918 fFatBits->AddCrossHair();
919
920 // crosshair info needs to be added
921 // window resizes accordingly
922 float width;
923 float height;
924 GetPreferredSize(&width, &height);
925 ResizeTo(width, height);
926 }
927
928
929 void
RemoveCrossHair()930 TWindow::RemoveCrossHair()
931 {
932 fFatBits->RemoveCrossHair();
933
934 // crosshair info needs to be removed
935 // window resizes accordingly
936 float width;
937 float height;
938 GetPreferredSize(&width, &height);
939 ResizeTo(width, height);
940 }
941
942
943 void
CrossHairsShowing(bool * ch1,bool * ch2)944 TWindow::CrossHairsShowing(bool* ch1, bool* ch2)
945 {
946 fFatBits->CrossHairsShowing(ch1, ch2);
947 }
948
949
950 void
PixelCount(int32 * h,int32 * v)951 TWindow::PixelCount(int32* h, int32 *v)
952 {
953 *h = fHPixelCount;
954 *v = fVPixelCount;
955 }
956
957
958 void
SetPixelSize(int32 s)959 TWindow::SetPixelSize(int32 s)
960 {
961 if (s > 100)
962 s = 100;
963 else if (s < 1)
964 s = 1;
965
966 if (s == fPixelSize)
967 return;
968
969 fPixelSize = s;
970 // resize window
971 // tell info that size has changed
972 // tell mag that size has changed
973
974 float w = Bounds().Width();
975 float h = Bounds().Height();
976 CalcViewablePixels();
977 ResizeWindow(fHPixelCount, fVPixelCount);
978
979 // the window might not actually change in size
980 // in that case force the buffers to the new dimension
981 if (w == Bounds().Width() && h == Bounds().Height())
982 fFatBits->InitBuffers(fHPixelCount, fVPixelCount, fPixelSize, ShowGrid());
983 }
984
985
986 void
SetPixelSize(bool plus)987 TWindow::SetPixelSize(bool plus)
988 {
989 int32 pixelSize;
990
991 if (plus) {
992 if (fPixelSize >= 16)
993 return;
994
995 pixelSize = fPixelSize + 1;
996 } else {
997 pixelSize = fPixelSize / 2;
998
999 if (pixelSize < 16) {
1000 if (fPixelSize > 16)
1001 pixelSize = (fPixelSize + 16) / 2;
1002 else
1003 pixelSize = fPixelSize - 1;
1004 }
1005 }
1006
1007 SetPixelSize(pixelSize);
1008 }
1009
1010
1011 int32
PixelSize()1012 TWindow::PixelSize()
1013 {
1014 return fPixelSize;
1015 }
1016
1017
1018 #undef B_TRANSLATION_CONTEXT
1019 #define B_TRANSLATION_CONTEXT "Magnify-Main"
1020
1021
1022 bool
IsActive()1023 TWindow::IsActive()
1024 {
1025 return fFatBits->Active();
1026 }
1027
1028
1029 bool
IsSticked()1030 TWindow::IsSticked()
1031 {
1032 return fFatBits->Sticked();
1033 }
1034
1035
1036 // #pragma mark -
1037
1038
TInfoView(BRect frame)1039 TInfoView::TInfoView(BRect frame)
1040 : BBox(frame, "rgb", B_FOLLOW_ALL,
1041 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS,
1042 B_NO_BORDER)
1043 {
1044 SetFont(be_plain_font);
1045 fFontHeight = FontHeight(this, true);
1046 fMagView = NULL;
1047
1048 fSelectionColor = kBlack;
1049 fCH1Loc.x = fCH1Loc.y = fCH2Loc.x = fCH2Loc.y = 0;
1050
1051 fInfoStr[0] = 0;
1052 fRGBStr[0] = 0;
1053 fCH1Str = "";
1054 fCH2Str = "";
1055
1056 fInfoTextVisible = true;
1057 }
1058
1059
~TInfoView()1060 TInfoView::~TInfoView()
1061 {
1062 }
1063
1064
1065 void
AttachedToWindow()1066 TInfoView::AttachedToWindow()
1067 {
1068 BBox::AttachedToWindow();
1069 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1070 dynamic_cast<TWindow*>(Window())->PixelCount(&fHPixelCount, &fVPixelCount);
1071 fPixelSize = dynamic_cast<TWindow*>(Window())->PixelSize();
1072
1073 AddMenu();
1074 }
1075
1076
1077 void
Draw(BRect updateRect)1078 TInfoView::Draw(BRect updateRect)
1079 {
1080 PushState();
1081 SetLowColor(ViewColor());
1082
1083 BRect invalRect;
1084
1085 int32 hPixelCount, vPixelCount;
1086 dynamic_cast<TWindow*>(Window())->PixelCount(&hPixelCount, &vPixelCount);
1087 int32 pixelSize = dynamic_cast<TWindow*>(Window())->PixelSize();
1088
1089 MovePenTo(15 + fPopUp->Bounds().Width(), fFontHeight + 5);
1090
1091 static BStringFormat format(B_TRANSLATE_COMMENT("%width × %height @ {0, plural, "
1092 "one{# pixel/pixel} other{# pixels/pixel}}",
1093 "The '×' is the Unicode multiplication sign U+00D7"));
1094
1095 BString dimensionsInfo;
1096 format.Format(dimensionsInfo, pixelSize);
1097
1098 BString rep;
1099 rep << hPixelCount;
1100 dimensionsInfo.ReplaceAll("%width", rep);
1101 rep = "";
1102 rep << vPixelCount;
1103 dimensionsInfo.ReplaceAll("%height", rep);
1104
1105 invalRect.Set(10, 5, 10 + StringWidth(fInfoStr), fFontHeight+7);
1106 SetHighColor(ViewColor());
1107 FillRect(invalRect);
1108 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
1109 strcpy(fInfoStr, dimensionsInfo);
1110 if (fInfoTextVisible)
1111 DrawString(fInfoStr);
1112
1113 rgb_color color = { 0, 0, 0, 255 };
1114 if (fMagView)
1115 color = fMagView->SelectionColor();
1116 char str[64];
1117 snprintf(str, sizeof(str), "R: %i G: %i B: %i (#%02x%02x%02x)",
1118 color.red, color.green, color.blue, color.red, color.green, color.blue);
1119
1120 MovePenTo(15 + fPopUp->Bounds().Width(), fFontHeight*2+5);
1121 invalRect.Set(10, fFontHeight+7, 10 + StringWidth(fRGBStr), fFontHeight*2+7);
1122 SetHighColor(ViewColor());
1123 FillRect(invalRect);
1124 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
1125 strcpy(fRGBStr,str);
1126 if (fInfoTextVisible)
1127 DrawString(fRGBStr);
1128
1129 bool ch1Showing, ch2Showing;
1130 dynamic_cast<TWindow*>(Window())->CrossHairsShowing(&ch1Showing, &ch2Showing);
1131
1132 if (fMagView) {
1133 BPoint pt1(fMagView->CrossHair1Loc());
1134 BPoint pt2(fMagView->CrossHair2Loc());
1135
1136 float h = Bounds().Height();
1137 if (ch1Showing && ch2Showing) {
1138 MovePenTo(10, h-10-fFontHeight-2);
1139 fCH1Str.SetToFormat("➀ x: %" B_PRIi32 ", y: %" B_PRIi32,
1140 (int32)pt1.x, (int32)pt1.y);
1141 fCH2Str.SetToFormat("➁ x: %" B_PRIi32 ", y: %" B_PRIi32,
1142 (int32)pt2.x, (int32)pt2.y);
1143
1144 BString dimensions;
1145 dimensions.SetToFormat("width: %d, height: %d",
1146 abs((int)(pt1.x - pt2.x)), abs((int)(pt1.y - pt2.y)));
1147 dimensions.ReplaceFirst("width", B_TRANSLATE("width"));
1148 dimensions.ReplaceFirst("height", B_TRANSLATE("height"));
1149
1150 float width = StringWidth(fCH2Str) + StringWidth(dimensions) + 30;
1151 invalRect.Set(10, h-10-2*fFontHeight-2, width, h-10-fFontHeight);
1152 SetHighColor(ViewColor());
1153 FillRect(invalRect);
1154 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
1155
1156 if (fInfoTextVisible) {
1157 DrawString(fCH1Str);
1158 MovePenTo(10, h-12);
1159 DrawString(fCH2Str);
1160 MovePenTo(StringWidth(fCH2Str) + 30, h-10-fFontHeight/2-4);
1161 DrawString(dimensions);
1162 }
1163 } else if (ch1Showing) {
1164 MovePenTo(10, h-10);
1165 fCH1Str.SetToFormat("x: %" B_PRIi32 ", y: %" B_PRIi32, (int32)pt1.x, (int32)pt1.y);
1166 invalRect.Set(10, h-10-fFontHeight, 10 + StringWidth(fCH1Str), h-8);
1167 SetHighColor(ViewColor());
1168 FillRect(invalRect);
1169 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
1170 if (fInfoTextVisible)
1171 DrawString(fCH1Str);
1172 }
1173 }
1174
1175 PopState();
1176 }
1177
1178
1179 void
FrameResized(float width,float height)1180 TInfoView::FrameResized(float width, float height)
1181 {
1182 BBox::FrameResized(width, height);
1183 }
1184
1185
1186 void
AddMenu()1187 TInfoView::AddMenu()
1188 {
1189 fMenu = new TMenu(dynamic_cast<TWindow*>(Window()), "");
1190 BuildInfoMenu(fMenu);
1191
1192 BRect r(9, 11, 22, 27);
1193 fPopUp = new BMenuField( r, "region menu", NULL, fMenu, true,
1194 B_FOLLOW_LEFT | B_FOLLOW_TOP);
1195 AddChild(fPopUp);
1196 }
1197
1198
1199 void
SetMagView(TMagnify * magView)1200 TInfoView::SetMagView(TMagnify* magView)
1201 {
1202 fMagView = magView;
1203 }
1204
1205
1206 // #pragma mark -
1207
1208
TMenu(TWindow * mainWindow,const char * title,menu_layout layout)1209 TMenu::TMenu(TWindow *mainWindow, const char *title, menu_layout layout)
1210 : BMenu(title, layout),
1211 fMainWindow(mainWindow)
1212 {
1213 }
1214
1215
~TMenu()1216 TMenu::~TMenu()
1217 {
1218 }
1219
1220
1221 void
AttachedToWindow()1222 TMenu::AttachedToWindow()
1223 {
1224 UpdateInfoMenu(this, fMainWindow);
1225
1226 BMenu::AttachedToWindow();
1227 }
1228
1229
1230 void
GetPreferredSize(float * _width,float * _height)1231 TInfoView::GetPreferredSize(float* _width, float* _height)
1232 {
1233 if (_width) {
1234 float str1Width = StringWidth(fCH1Str)
1235 + StringWidth(fCH2Str)
1236 + StringWidth(fRGBStr)
1237 + 30;
1238 float str2Width = StringWidth(fInfoStr) + 30;
1239 *_width = str1Width > str2Width ? str1Width : str2Width;
1240 }
1241
1242 if (_height)
1243 *_height = fFontHeight * 2 + 10;
1244 }
1245
1246
1247 bool
IsInfoTextVisible()1248 TInfoView::IsInfoTextVisible()
1249 {
1250 return fInfoTextVisible;
1251 }
1252
1253
1254 void
SetInfoTextVisible(bool visible)1255 TInfoView::SetInfoTextVisible(bool visible)
1256 {
1257 fInfoTextVisible = visible;
1258 Draw(Bounds());
1259 }
1260
1261
1262 // #pragma mark -
1263
1264
TMagnify(BRect r,TWindow * parent)1265 TMagnify::TMagnify(BRect r, TWindow* parent)
1266 : BView(r, "MagView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS),
1267 fNeedToUpdate(true),
1268 fThread(-1),
1269 fActive(true),
1270 fImageBuf(NULL),
1271 fImageView(NULL),
1272 fLastLoc(-1, -1),
1273 fSelection(-1),
1274 fShowSelection(false),
1275 fSelectionLoc(0, 0),
1276 fShowCrossHair1(false),
1277 fCrossHair1(-1, -1),
1278 fShowCrossHair2(false),
1279 fCrossHair2(-1, -1),
1280 fParent(parent),
1281 fStickCoordinates(false)
1282 {
1283 }
1284
1285
~TMagnify()1286 TMagnify::~TMagnify()
1287 {
1288 kill_thread(fThread);
1289 delete fImageBuf;
1290 }
1291
1292
1293 void
AttachedToWindow()1294 TMagnify::AttachedToWindow()
1295 {
1296 int32 width, height;
1297 fParent->PixelCount(&width, &height);
1298 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid());
1299
1300 fThread = spawn_thread(TMagnify::MagnifyTask, "MagnifyTask",
1301 B_NORMAL_PRIORITY, this);
1302
1303 resume_thread(fThread);
1304
1305 SetViewColor(B_TRANSPARENT_32_BIT);
1306 MakeFocus();
1307 }
1308
1309
1310 void
InitBuffers(int32 hPixelCount,int32 vPixelCount,int32 pixelSize,bool showGrid)1311 TMagnify::InitBuffers(int32 hPixelCount, int32 vPixelCount,
1312 int32 pixelSize, bool showGrid)
1313 {
1314 color_space colorSpace = BScreen(Window()).ColorSpace();
1315
1316 BRect r(0, 0, (pixelSize * hPixelCount)-1, (pixelSize * vPixelCount)-1);
1317 if (Bounds().Width() != r.Width() || Bounds().Height() != r.Height())
1318 ResizeTo(r.Width(), r.Height());
1319
1320 if (fImageView) {
1321 fImageBuf->Lock();
1322 fImageView->RemoveSelf();
1323 fImageBuf->Unlock();
1324
1325 fImageView->Resize((int32)r.Width(), (int32)r.Height());
1326 fImageView->SetSpace(colorSpace);
1327 } else
1328 fImageView = new TOSMagnify(r, this, colorSpace);
1329
1330 delete fImageBuf;
1331 fImageBuf = new BBitmap(r, colorSpace, true);
1332 fImageBuf->Lock();
1333 fImageBuf->AddChild(fImageView);
1334 fImageBuf->Unlock();
1335 }
1336
1337
1338 void
Draw(BRect)1339 TMagnify::Draw(BRect)
1340 {
1341 BRect bounds(Bounds());
1342 DrawBitmap(fImageBuf, bounds, bounds);
1343 static_cast<TWindow*>(Window())->UpdateInfo();
1344 }
1345
1346
1347 void
KeyDown(const char * key,int32 numBytes)1348 TMagnify::KeyDown(const char *key, int32 numBytes)
1349 {
1350 if (!fShowSelection)
1351 BView::KeyDown(key, numBytes);
1352
1353 switch (key[0]) {
1354 case B_TAB:
1355 if (fShowCrossHair1) {
1356 fSelection++;
1357
1358 if (fShowCrossHair2) {
1359 if (fSelection > 2)
1360 fSelection = 0;
1361 } else if (fShowCrossHair1) {
1362 if (fSelection > 1)
1363 fSelection = 0;
1364 }
1365 fNeedToUpdate = true;
1366 Invalidate();
1367 }
1368 break;
1369
1370 case B_LEFT_ARROW:
1371 MoveSelection(-1,0);
1372 break;
1373 case B_RIGHT_ARROW:
1374 MoveSelection(1,0);
1375 break;
1376 case B_UP_ARROW:
1377 MoveSelection(0,-1);
1378 break;
1379 case B_DOWN_ARROW:
1380 MoveSelection(0,1);
1381 break;
1382
1383 default:
1384 BView::KeyDown(key,numBytes);
1385 break;
1386 }
1387 }
1388
1389
1390 void
FrameResized(float newW,float newH)1391 TMagnify::FrameResized(float newW, float newH)
1392 {
1393 int32 w, h;
1394 PixelCount(&w, &h);
1395
1396 if (fSelectionLoc.x >= w)
1397 fSelectionLoc.x = 0;
1398 if (fSelectionLoc.y >= h)
1399 fSelectionLoc.y = 0;
1400
1401 if (fShowCrossHair1) {
1402 if (fCrossHair1.x >= w) {
1403 fCrossHair1.x = fSelectionLoc.x + 2;
1404 if (fCrossHair1.x >= w)
1405 fCrossHair1.x = 0;
1406 }
1407 if (fCrossHair1.y >= h) {
1408 fCrossHair1.y = fSelectionLoc.y + 2;
1409 if (fCrossHair1.y >= h)
1410 fCrossHair1.y = 0;
1411 }
1412
1413 if (fShowCrossHair2) {
1414 if (fCrossHair2.x >= w) {
1415 fCrossHair2.x = fCrossHair1.x + 2;
1416 if (fCrossHair2.x >= w)
1417 fCrossHair2.x = 0;
1418 }
1419 if (fCrossHair2.y >= h) {
1420 fCrossHair2.y = fCrossHair1.y + 2;
1421 if (fCrossHair2.y >= h)
1422 fCrossHair2.y = 0;
1423 }
1424 }
1425 }
1426 }
1427
1428
1429 void
MouseDown(BPoint where)1430 TMagnify::MouseDown(BPoint where)
1431 {
1432 BMessage *currentMsg = Window()->CurrentMessage();
1433 if (currentMsg->what == B_MOUSE_DOWN) {
1434 uint32 buttons = 0;
1435 currentMsg->FindInt32("buttons", (int32 *)&buttons);
1436
1437 uint32 modifiers = 0;
1438 currentMsg->FindInt32("modifiers", (int32 *)&modifiers);
1439
1440 if ((buttons & B_SECONDARY_MOUSE_BUTTON) || (modifiers & B_CONTROL_KEY)) {
1441 // secondary button was clicked or control key was down, show menu and return
1442
1443 BPopUpMenu *menu = new BPopUpMenu(B_TRANSLATE("Info"), false, false);
1444 menu->SetFont(be_plain_font);
1445 BuildInfoMenu(menu);
1446 UpdateInfoMenu(menu, dynamic_cast<TWindow*>(Window()));
1447 BMenuItem *selected = menu->Go(ConvertToScreen(where));
1448 if (selected)
1449 Window()->PostMessage(selected->Message()->what);
1450 delete menu;
1451 return;
1452 }
1453
1454 // add a mousedown looper here
1455
1456 int32 pixelSize = PixelSize();
1457 float x = where.x / pixelSize;
1458 float y = where.y / pixelSize;
1459
1460 MoveSelectionTo(x, y);
1461
1462 // draw the frozen image
1463 // update the info region
1464
1465 fNeedToUpdate = true;
1466 Invalidate();
1467 }
1468 }
1469
1470
1471 void
ScreenChanged(BRect,color_space)1472 TMagnify::ScreenChanged(BRect, color_space)
1473 {
1474 int32 width, height;
1475 fParent->PixelCount(&width, &height);
1476 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid());
1477 }
1478
1479
1480 void
SetSelection(bool state)1481 TMagnify::SetSelection(bool state)
1482 {
1483 if (fShowSelection == state)
1484 return;
1485
1486 fShowSelection = state;
1487 fSelection = 0;
1488 Invalidate();
1489 }
1490
1491
1492 void
MoveSelection(int32 x,int32 y)1493 TMagnify::MoveSelection(int32 x, int32 y)
1494 {
1495 if (!fShowSelection)
1496 return;
1497
1498 int32 xCount, yCount;
1499 PixelCount(&xCount, &yCount);
1500
1501 float xloc, yloc;
1502 if (fSelection == 0) {
1503 xloc = fSelectionLoc.x;
1504 yloc = fSelectionLoc.y;
1505 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount);
1506 fSelectionLoc.x = xloc;
1507 fSelectionLoc.y = yloc;
1508 } else if (fSelection == 1) {
1509 xloc = fCrossHair1.x;
1510 yloc = fCrossHair1.y;
1511 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount);
1512 fCrossHair1.x = xloc;
1513 fCrossHair1.y = yloc;
1514 } else if (fSelection == 2) {
1515 xloc = fCrossHair2.x;
1516 yloc = fCrossHair2.y;
1517 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount);
1518 fCrossHair2.x = xloc;
1519 fCrossHair2.y = yloc;
1520 }
1521
1522 fNeedToUpdate = true;
1523 Invalidate();
1524 }
1525
1526
1527 void
MoveSelectionTo(int32 x,int32 y)1528 TMagnify::MoveSelectionTo(int32 x, int32 y)
1529 {
1530 if (!fShowSelection)
1531 return;
1532
1533 int32 xCount, yCount;
1534 PixelCount(&xCount, &yCount);
1535 if (x >= xCount)
1536 x = 0;
1537 if (y >= yCount)
1538 y = 0;
1539
1540 if (fSelection == 0) {
1541 fSelectionLoc.x = x;
1542 fSelectionLoc.y = y;
1543 } else if (fSelection == 1) {
1544 fCrossHair1.x = x;
1545 fCrossHair1.y = y;
1546 } else if (fSelection == 2) {
1547 fCrossHair2.x = x;
1548 fCrossHair2.y = y;
1549 }
1550
1551 fNeedToUpdate = true;
1552 Invalidate(); //Draw(Bounds());
1553 }
1554
1555
1556 void
ShowSelection()1557 TMagnify::ShowSelection()
1558 {
1559 }
1560
1561
1562 short
Selection()1563 TMagnify::Selection()
1564 {
1565 return fSelection;
1566 }
1567
1568
1569 bool
SelectionIsShowing()1570 TMagnify::SelectionIsShowing()
1571 {
1572 return fShowSelection;
1573 }
1574
1575
1576 void
SelectionLoc(float * x,float * y)1577 TMagnify::SelectionLoc(float* x, float* y)
1578 {
1579 *x = fSelectionLoc.x;
1580 *y = fSelectionLoc.y;
1581 }
1582
1583
1584 void
SetSelectionLoc(float x,float y)1585 TMagnify::SetSelectionLoc(float x, float y)
1586 {
1587 fSelectionLoc.x = x;
1588 fSelectionLoc.y = y;
1589 }
1590
1591
1592 rgb_color
SelectionColor()1593 TMagnify::SelectionColor()
1594 {
1595 return fImageView->ColorAtSelection();
1596 }
1597
1598
1599 void
CrossHair1Loc(float * x,float * y)1600 TMagnify::CrossHair1Loc(float* x, float* y)
1601 {
1602 *x = fCrossHair1.x;
1603 *y = fCrossHair1.y;
1604 }
1605
1606
1607 void
CrossHair2Loc(float * x,float * y)1608 TMagnify::CrossHair2Loc(float* x, float* y)
1609 {
1610 *x = fCrossHair2.x;
1611 *y = fCrossHair2.y;
1612 }
1613
1614
1615 BPoint
CrossHair1Loc()1616 TMagnify::CrossHair1Loc()
1617 {
1618 return fCrossHair1;
1619 }
1620
1621
1622 BPoint
CrossHair2Loc()1623 TMagnify::CrossHair2Loc()
1624 {
1625 return fCrossHair2;
1626 }
1627
1628
1629 void
NudgeMouse(float x,float y)1630 TMagnify::NudgeMouse(float x, float y)
1631 {
1632 BPoint loc;
1633 uint32 button;
1634
1635 GetMouse(&loc, &button);
1636 ConvertToScreen(&loc);
1637 loc.x += x;
1638 loc.y += y;
1639
1640 set_mouse_position((int32)loc.x, (int32)loc.y);
1641 }
1642
1643
1644 void
WindowActivated(bool active)1645 TMagnify::WindowActivated(bool active)
1646 {
1647 if (active)
1648 MakeFocus();
1649 }
1650
1651
1652 status_t
MagnifyTask(void * arg)1653 TMagnify::MagnifyTask(void *arg)
1654 {
1655 TMagnify* view = (TMagnify*)arg;
1656
1657 // static data members can't access members, methods without
1658 // a pointer to an instance of the class
1659 TWindow* window = (TWindow*)view->Window();
1660
1661 while (true) {
1662 if (window->Lock()) {
1663 if (view->NeedToUpdate() || view->Active())
1664 view->Update(view->NeedToUpdate());
1665
1666 window->Unlock();
1667 }
1668 snooze(35000);
1669 }
1670
1671 return B_NO_ERROR;
1672 }
1673
1674
1675 void
Update(bool force)1676 TMagnify::Update(bool force)
1677 {
1678 BPoint loc;
1679 uint32 button;
1680 static long counter = 0;
1681
1682 if (!fStickCoordinates) {
1683 GetMouse(&loc, &button);
1684 ConvertToScreen(&loc);
1685 } else
1686 loc = fLastLoc;
1687
1688 if (force || fLastLoc != loc || counter++ % 35 == 0) {
1689 if (fImageView->CreateImage(loc, force))
1690 Invalidate();
1691
1692 counter = 0;
1693 if (force)
1694 SetUpdate(false);
1695 }
1696 fLastLoc = loc;
1697 }
1698
1699
1700 bool
NeedToUpdate()1701 TMagnify::NeedToUpdate()
1702 {
1703 return fNeedToUpdate;
1704 }
1705
1706
1707 void
SetUpdate(bool s)1708 TMagnify::SetUpdate(bool s)
1709 {
1710 fNeedToUpdate = s;
1711 }
1712
1713
1714 void
CopyImage()1715 TMagnify::CopyImage()
1716 {
1717 StartSave();
1718 be_clipboard->Lock();
1719 be_clipboard->Clear();
1720
1721 BMessage *message = be_clipboard->Data();
1722 if (!message) {
1723 puts(B_TRANSLATE_CONTEXT("no clip msg",
1724 "In console, when clipboard is empty after clicking Copy image"));
1725 return;
1726 }
1727
1728 BMessage *embeddedBitmap = new BMessage();
1729 (fImageView->Bitmap())->Archive(embeddedBitmap,false);
1730 status_t err = message->AddMessage(kBitmapMimeType, embeddedBitmap);
1731 if (err == B_OK)
1732 err = message->AddRect("rect", fImageView->Bitmap()->Bounds());
1733 if (err == B_OK)
1734 be_clipboard->Commit();
1735
1736 be_clipboard->Unlock();
1737 EndSave();
1738 }
1739
1740
1741 void
AddCrossHair()1742 TMagnify::AddCrossHair()
1743 {
1744 if (fShowCrossHair1 && fShowCrossHair2)
1745 return;
1746
1747 int32 w, h;
1748 PixelCount(&w, &h);
1749
1750 if (fShowCrossHair1) {
1751 fSelection = 2;
1752 fShowCrossHair2 = true;
1753 fCrossHair2.x = fCrossHair1.x + 2;
1754 if (fCrossHair2.x >= w)
1755 fCrossHair2.x = 0;
1756 fCrossHair2.y = fCrossHair1.y + 2;
1757 if (fCrossHair2.y >= h)
1758 fCrossHair2.y = 0;
1759 } else {
1760 fSelection = 1;
1761 fShowCrossHair1 = true;
1762 fCrossHair1.x = fSelectionLoc.x + 2;
1763 if (fCrossHair1.x >= w)
1764 fCrossHair1.x = 0;
1765 fCrossHair1.y = fSelectionLoc.y + 2;
1766 if (fCrossHair1.y >= h)
1767 fCrossHair1.y = 0;
1768 }
1769 Invalidate();
1770 }
1771
1772
1773 void
RemoveCrossHair()1774 TMagnify::RemoveCrossHair()
1775 {
1776 if (!fShowCrossHair1 && !fShowCrossHair2)
1777 return;
1778
1779 if (fShowCrossHair2) {
1780 fSelection = 1;
1781 fShowCrossHair2 = false;
1782 } else if (fShowCrossHair1) {
1783 fSelection = 0;
1784 fShowCrossHair1 = false;
1785 }
1786 Invalidate();
1787 }
1788
1789
1790 void
SetCrossHairsShowing(bool ch1,bool ch2)1791 TMagnify::SetCrossHairsShowing(bool ch1, bool ch2)
1792 {
1793 fShowCrossHair1 = ch1;
1794 fShowCrossHair2 = ch2;
1795 }
1796
1797
1798 void
CrossHairsShowing(bool * ch1,bool * ch2)1799 TMagnify::CrossHairsShowing(bool* ch1, bool* ch2)
1800 {
1801 *ch1 = fShowCrossHair1;
1802 *ch2 = fShowCrossHair2;
1803 }
1804
1805
1806 void
MakeActive(bool s)1807 TMagnify::MakeActive(bool s)
1808 {
1809 fActive = s;
1810 }
1811
1812
1813 void
MakeSticked(bool s)1814 TMagnify::MakeSticked(bool s)
1815 {
1816 fStickCoordinates = s;
1817 }
1818
1819
1820 void
PixelCount(int32 * width,int32 * height)1821 TMagnify::PixelCount(int32* width, int32* height)
1822 {
1823 fParent->PixelCount(width, height);
1824 }
1825
1826
1827 int32
PixelSize()1828 TMagnify::PixelSize()
1829 {
1830 return fParent->PixelSize();
1831 }
1832
1833
1834 bool
ShowGrid()1835 TMagnify::ShowGrid()
1836 {
1837 return fParent->ShowGrid();
1838 }
1839
1840
1841 void
StartSave()1842 TMagnify::StartSave()
1843 {
1844 fImageFrozenOnSave = Active();
1845 if (fImageFrozenOnSave)
1846 MakeActive(false);
1847 }
1848
1849
1850 void
EndSave()1851 TMagnify::EndSave()
1852 {
1853 if (fImageFrozenOnSave)
1854 MakeActive(true);
1855 }
1856
1857
1858 void
SaveImage(entry_ref * ref,char * name)1859 TMagnify::SaveImage(entry_ref* ref, char* name)
1860 {
1861 // create a new file
1862 BFile file;
1863 BDirectory parentDir(ref);
1864 parentDir.CreateFile(name, &file);
1865
1866 // Write the screenshot bitmap to the file
1867 BBitmapStream stream(fImageView->Bitmap());
1868 BTranslatorRoster* roster = BTranslatorRoster::Default();
1869 roster->Translate(&stream, NULL, NULL, &file, B_PNG_FORMAT,
1870 B_TRANSLATOR_BITMAP);
1871
1872 BBitmap* bitmap;
1873 stream.DetachBitmap(&bitmap);
1874 // The stream takes over ownership of the bitmap
1875
1876 // unfreeze the image, image was frozen before invoke of FilePanel
1877 EndSave();
1878 }
1879
1880 // #pragma mark -
1881
1882
TOSMagnify(BRect r,TMagnify * parent,color_space space)1883 TOSMagnify::TOSMagnify(BRect r, TMagnify* parent, color_space space)
1884 : BView(r, "ImageView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS),
1885 fColorSpace(space), fParent(parent)
1886 {
1887 switch (space) {
1888 case B_CMAP8:
1889 fBytesPerPixel = 1;
1890 break;
1891 case B_RGB15:
1892 case B_RGBA15:
1893 case B_RGB15_BIG:
1894 case B_RGBA15_BIG:
1895 case B_RGB16:
1896 case B_RGB16_BIG:
1897 fBytesPerPixel = 2;
1898 break;
1899 case B_RGB24:
1900 fBytesPerPixel = 3;
1901 break;
1902 case B_RGB32:
1903 case B_RGBA32:
1904 case B_RGB32_BIG:
1905 case B_RGBA32_BIG:
1906 fBytesPerPixel = 4;
1907 break;
1908 default:
1909 // uh, oh -- a color space we don't support
1910 fprintf(stderr, "Tried to run in an unsupported color space; exiting\n");
1911 exit(1);
1912 break;
1913 }
1914
1915 fPixel = NULL;
1916 fBitmap = NULL;
1917 fOldBits = NULL;
1918 InitObject();
1919 }
1920
1921
~TOSMagnify()1922 TOSMagnify::~TOSMagnify()
1923 {
1924 delete fPixel;
1925 delete fBitmap;
1926 free(fOldBits);
1927 }
1928
1929
1930 void
SetSpace(color_space space)1931 TOSMagnify::SetSpace(color_space space)
1932 {
1933 fColorSpace = space;
1934 InitObject();
1935 };
1936
1937
1938 void
InitObject()1939 TOSMagnify::InitObject()
1940 {
1941 int32 w, h;
1942 fParent->PixelCount(&w, &h);
1943
1944 delete fBitmap;
1945 BRect bitsRect(0, 0, w-1, h-1);
1946 fBitmap = new BBitmap(bitsRect, fColorSpace);
1947
1948 free(fOldBits);
1949 fOldBits = (char*)malloc(fBitmap->BitsLength());
1950
1951 if (!fPixel) {
1952 #if B_HOST_IS_BENDIAN
1953 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32_BIG, true);
1954 #else
1955 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32, true);
1956 #endif
1957 fPixelView = new BView(BRect(0,0,0,0), NULL, 0, 0);
1958 fPixel->Lock();
1959 fPixel->AddChild(fPixelView);
1960 fPixel->Unlock();
1961 }
1962 }
1963
1964
1965 void
FrameResized(float width,float height)1966 TOSMagnify::FrameResized(float width, float height)
1967 {
1968 BView::FrameResized(width, height);
1969 InitObject();
1970 }
1971
1972
1973 void
Resize(int32 width,int32 height)1974 TOSMagnify::Resize(int32 width, int32 height)
1975 {
1976 ResizeTo(width, height);
1977 InitObject();
1978 }
1979
1980
1981 bool
CreateImage(BPoint mouseLoc,bool force)1982 TOSMagnify::CreateImage(BPoint mouseLoc, bool force)
1983 {
1984 bool created = false;
1985 if (Window() && Window()->Lock()) {
1986 int32 width, height;
1987 fParent->PixelCount(&width, &height);
1988 int32 pixelSize = fParent->PixelSize();
1989
1990 BRect srcRect(0, 0, width - 1, height - 1);
1991 srcRect.OffsetBy(mouseLoc.x - (width / 2),
1992 mouseLoc.y - (height / 2));
1993
1994 if (force || CopyScreenRect(srcRect)) {
1995 srcRect.OffsetTo(BPoint(0, 0));
1996 BRect destRect(Bounds());
1997
1998 DrawBitmap(fBitmap, srcRect, destRect);
1999
2000 DrawGrid(width, height, destRect, pixelSize);
2001 DrawSelection();
2002
2003 Sync();
2004 created = true;
2005 }
2006 Window()->Unlock();
2007 } else
2008 puts("window problem");
2009
2010 return created;
2011 }
2012
2013
2014 bool
CopyScreenRect(BRect srcRect)2015 TOSMagnify::CopyScreenRect(BRect srcRect)
2016 {
2017 // constrain src rect to legal screen rect
2018 BScreen screen(Window());
2019 BRect scrnframe = screen.Frame();
2020
2021 if (srcRect.right > scrnframe.right)
2022 srcRect.OffsetTo(scrnframe.right - srcRect.Width(), srcRect.top);
2023 if (srcRect.top < 0)
2024 srcRect.OffsetTo(srcRect.left, 0);
2025
2026 if (srcRect.bottom > scrnframe.bottom)
2027 srcRect.OffsetTo(srcRect.left, scrnframe.bottom - srcRect.Height());
2028 if (srcRect.left < 0)
2029 srcRect.OffsetTo(0, srcRect.top);
2030
2031 // save a copy of the bits for comparison later
2032 memcpy(fOldBits, fBitmap->Bits(), fBitmap->BitsLength());
2033
2034 screen.ReadBitmap(fBitmap, false, &srcRect);
2035
2036 // let caller know whether bits have actually changed
2037 return memcmp(fBitmap->Bits(), fOldBits, fBitmap->BitsLength()) != 0;
2038 }
2039
2040
2041 void
DrawGrid(int32 width,int32 height,BRect destRect,int32 pixelSize)2042 TOSMagnify::DrawGrid(int32 width, int32 height, BRect destRect, int32 pixelSize)
2043 {
2044 // draw grid
2045 if (fParent->ShowGrid() && fParent->PixelSize() > 2) {
2046 BeginLineArray(width * height);
2047
2048 // horizontal lines
2049 for (int32 i = pixelSize; i < (height * pixelSize); i += pixelSize)
2050 AddLine(BPoint(0, i), BPoint(destRect.right, i), kGridGray);
2051
2052 // vertical lines
2053 for (int32 i = pixelSize; i < (width * pixelSize); i += pixelSize)
2054 AddLine(BPoint(i, 0), BPoint(i, destRect.bottom), kGridGray);
2055
2056 EndLineArray();
2057 }
2058
2059 SetHighColor(kGridGray);
2060 StrokeRect(destRect);
2061 }
2062
2063
2064 void
DrawSelection()2065 TOSMagnify::DrawSelection()
2066 {
2067 if (!fParent->SelectionIsShowing())
2068 return;
2069
2070 float x, y;
2071 int32 pixelSize = fParent->PixelSize();
2072 int32 squareSize = pixelSize - 2;
2073
2074 fParent->SelectionLoc(&x, &y);
2075 x *= pixelSize; x++;
2076 y *= pixelSize; y++;
2077 BRect selRect(x, y, x+squareSize, y+squareSize);
2078
2079 short selection = fParent->Selection();
2080
2081 PushState();
2082 SetLowColor(ViewColor());
2083 SetHighColor(kRedColor);
2084 StrokeRect(selRect);
2085 if (selection == 0) {
2086 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize));
2087 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y));
2088 }
2089
2090 bool ch1Showing, ch2Showing;
2091 fParent->CrossHairsShowing(&ch1Showing, &ch2Showing);
2092 if (ch1Showing) {
2093 SetHighColor(kBlueColor);
2094 fParent->CrossHair1Loc(&x, &y);
2095 x *= pixelSize; x++;
2096 y *= pixelSize; y++;
2097 selRect.Set(x, y,x+squareSize, y+squareSize);
2098 StrokeRect(selRect);
2099 BeginLineArray(4);
2100 AddLine(BPoint(0, y+(squareSize/2)),
2101 BPoint(x, y+(squareSize/2)), kBlueColor); // left
2102 AddLine(BPoint(x+squareSize,y+(squareSize/2)),
2103 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right
2104 AddLine(BPoint(x+(squareSize/2), 0),
2105 BPoint(x+(squareSize/2), y), kBlueColor); // top
2106 AddLine(BPoint(x+(squareSize/2), y+squareSize),
2107 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom
2108 EndLineArray();
2109 if (selection == 1) {
2110 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize));
2111 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y));
2112 }
2113 }
2114 if (ch2Showing) {
2115 SetHighColor(kBlueColor);
2116 fParent->CrossHair2Loc(&x, &y);
2117 x *= pixelSize; x++;
2118 y *= pixelSize; y++;
2119 selRect.Set(x, y,x+squareSize, y+squareSize);
2120 StrokeRect(selRect);
2121 BeginLineArray(4);
2122 AddLine(BPoint(0, y+(squareSize/2)),
2123 BPoint(x, y+(squareSize/2)), kBlueColor); // left
2124 AddLine(BPoint(x+squareSize,y+(squareSize/2)),
2125 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right
2126 AddLine(BPoint(x+(squareSize/2), 0),
2127 BPoint(x+(squareSize/2), y), kBlueColor); // top
2128 AddLine(BPoint(x+(squareSize/2), y+squareSize),
2129 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom
2130 EndLineArray();
2131 if (selection == 2) {
2132 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize));
2133 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y));
2134 }
2135 }
2136
2137 PopState();
2138 }
2139
2140
2141 rgb_color
ColorAtSelection()2142 TOSMagnify::ColorAtSelection()
2143 {
2144 float x, y;
2145 fParent->SelectionLoc(&x, &y);
2146 BRect srcRect(x, y, x, y);
2147 BRect dstRect(0, 0, 0, 0);
2148 fPixel->Lock();
2149 fPixelView->DrawBitmap(fBitmap, srcRect, dstRect);
2150 fPixelView->Sync();
2151 fPixel->Unlock();
2152
2153 uint32 pixel = *((uint32*)fPixel->Bits());
2154 rgb_color c;
2155 c.alpha = pixel >> 24;
2156 c.red = (pixel >> 16) & 0xFF;
2157 c.green = (pixel >> 8) & 0xFF;
2158 c.blue = pixel & 0xFF;
2159
2160 return c;
2161 }
2162
2163
2164 // #pragma mark -
2165
2166
2167 int
main(int argc,char * argv[])2168 main(int argc, char* argv[])
2169 {
2170 int32 pixelCount = -1;
2171
2172 if (argc > 2) {
2173 puts(B_TRANSLATE_CONTEXT(
2174 "usage: magnify [size] (magnify size * size pixels)",
2175 "Console"));
2176 exit(1);
2177 } else {
2178 if (argc == 2) {
2179 pixelCount = abs(atoi(argv[1]));
2180
2181 if ((pixelCount > 100) || (pixelCount < 4)) {
2182 puts(B_TRANSLATE_CONTEXT(
2183 "usage: magnify [size] (magnify size * size pixels)",
2184 "Console"));
2185 puts(B_TRANSLATE_CONTEXT(
2186 " size must be > 4 and a multiple of 4",
2187 "Console"));
2188 exit(1);
2189 }
2190
2191 if (pixelCount % 4) {
2192 puts(B_TRANSLATE_CONTEXT(
2193 "magnify: size must be a multiple of 4",
2194 "Console"));
2195 exit(1);
2196 }
2197 }
2198 }
2199
2200 TApp app(pixelCount);
2201 app.Run();
2202 return 0;
2203 }
2204