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