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