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