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