xref: /haiku/src/apps/showimage/ShowImageWindow.cpp (revision 45bd7bb3db9d9e4dcb02b89a3e7c2bf382c0a88c)
1 /*
2  * Copyright 2003-2009, Haiku, Inc. All Rights Reserved.
3  * Copyright 2004-2005 yellowTAB GmbH. All Rights Reserverd.
4  * Copyright 2006 Bernd Korz. All Rights Reserved
5  * Distributed under the terms of the MIT License.
6  *
7  * Authors:
8  *		Fernando Francisco de Oliveira
9  *		Michael Wilber
10  *		Michael Pfeiffer
11  *		yellowTAB GmbH
12  *		Bernd Korz
13  */
14 
15 #include "ShowImageWindow.h"
16 
17 #include <new>
18 #include <stdio.h>
19 
20 #include <Alert.h>
21 #include <Application.h>
22 #include <Bitmap.h>
23 #include <BitmapStream.h>
24 #include <Catalog.h>
25 #include <Clipboard.h>
26 #include <Entry.h>
27 #include <File.h>
28 #include <FilePanel.h>
29 #include <Locale.h>
30 #include <Menu.h>
31 #include <MenuBar.h>
32 #include <MenuItem.h>
33 #include <Path.h>
34 #include <PrintJob.h>
35 #include <Roster.h>
36 #include <Screen.h>
37 #include <ScrollView.h>
38 #include <String.h>
39 #include <SupportDefs.h>
40 #include <TranslationDefs.h>
41 #include <TranslationUtils.h>
42 #include <TranslatorRoster.h>
43 #include <stdlib.h> // for bs_printf()
44 
45 #include "EntryMenuItem.h"
46 #include "ResizerWindow.h"
47 #include "ShowImageApp.h"
48 #include "ShowImageConstants.h"
49 #include "ShowImageStatusView.h"
50 #include "ShowImageView.h"
51 
52 
53 // #pragma mark -- ShowImageWindow::RecentDocumentsMenu
54 
55 class ShowImageWindow::RecentDocumentsMenu : public BMenu {
56 public:
57 			RecentDocumentsMenu(const char* title,
58 				menu_layout layout = B_ITEMS_IN_COLUMN);
59 	bool	AddDynamicItem(add_state addState);
60 
61 private:
62 	void	UpdateRecentDocumentsMenu();
63 };
64 
65 
66 ShowImageWindow::RecentDocumentsMenu::RecentDocumentsMenu(const char* title,
67 	menu_layout layout)
68 	:
69 	BMenu(title, layout)
70 {
71 }
72 
73 
74 bool
75 ShowImageWindow::RecentDocumentsMenu::AddDynamicItem(add_state addState)
76 {
77 	if (addState != B_INITIAL_ADD)
78 		return false;
79 
80 	while (CountItems() > 0)
81 		delete RemoveItem(0L);
82 
83 	BMenuItem* item;
84 	BMessage list, *msg;
85 	entry_ref ref;
86 	char name[B_FILE_NAME_LENGTH];
87 
88 	be_roster->GetRecentDocuments(&list, 20, NULL, kApplicationSignature);
89 	for (int i = 0; list.FindRef("refs", i, &ref) == B_OK; i++) {
90 		BEntry entry(&ref);
91 		if (entry.Exists() && entry.GetName(name) == B_OK) {
92 			msg = new BMessage(B_REFS_RECEIVED);
93 			msg->AddRef("refs", &ref);
94 			item = new EntryMenuItem(&ref, name, msg, 0, 0);
95 			AddItem(item);
96 			item->SetTarget(be_app, NULL);
97 		}
98 	}
99 
100 	return false;
101 }
102 
103 
104 //	#pragma mark -- ShowImageWindow
105 
106 
107 // BMessage field names used in Save messages
108 const char* kTypeField = "be:type";
109 const char* kTranslatorField = "be:translator";
110 
111 
112 ShowImageWindow::ShowImageWindow(const entry_ref* ref,
113 	const BMessenger& trackerMessenger)
114 	:
115 	BWindow(BRect(5, 24, 250, 100), "", B_DOCUMENT_WINDOW, 0),
116 	fSavePanel(NULL),
117 	fBar(NULL),
118 	fOpenMenu(NULL),
119 	fBrowseMenu(NULL),
120 	fGoToPageMenu(NULL),
121 	fSlideShowDelay(NULL),
122 	fImageView(NULL),
123 	fStatusView(NULL),
124 	fModified(false),
125 	fFullScreen(false),
126 	fShowCaption(true),
127 	fPrintSettings(NULL),
128 	fResizerWindowMessenger(NULL),
129 	fResizeItem(NULL),
130 	fHeight(0),
131 	fWidth(0)
132 {
133 	_LoadSettings();
134 
135 	// create menu bar
136 	fBar = new BMenuBar(BRect(0, 0, Bounds().right, 1), "menu_bar");
137 	AddMenus(fBar);
138 	AddChild(fBar);
139 
140 	BRect viewFrame = Bounds();
141 	viewFrame.top = fBar->Bounds().Height() + 1;
142 	viewFrame.right -= B_V_SCROLL_BAR_WIDTH;
143 	viewFrame.bottom -= B_H_SCROLL_BAR_HEIGHT;
144 
145 	// create the image view
146 	fImageView = new ShowImageView(viewFrame, "image_view", B_FOLLOW_ALL,
147 		B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE | B_PULSE_NEEDED);
148 	// wrap a scroll view around the view
149 	BScrollView* scrollView = new BScrollView("image_scroller", fImageView,
150 		B_FOLLOW_ALL, 0, false, false, B_PLAIN_BORDER);
151 	AddChild(scrollView);
152 
153 	const int32 kstatusWidth = 190;
154 	BRect rect;
155 	rect = Bounds();
156 	rect.top	= viewFrame.bottom + 1;
157 	rect.left 	= viewFrame.left + kstatusWidth;
158 	rect.right	= viewFrame.right + 1;
159 	rect.bottom += 1;
160 	BScrollBar* horizontalScrollBar = new BScrollBar(rect, "hscroll",
161 		fImageView, 0, 150, B_HORIZONTAL);
162 	AddChild(horizontalScrollBar);
163 
164 	rect.left = 0;
165 	rect.right = kstatusWidth - 1;
166 	rect.bottom -= 1;
167 	fStatusView = new ShowImageStatusView(rect, "status_view", B_FOLLOW_BOTTOM,
168 		B_WILL_DRAW);
169 	AddChild(fStatusView);
170 
171 	rect = Bounds();
172 	rect.top	= viewFrame.top - 1;
173 	rect.left	= viewFrame.right + 1;
174 	rect.bottom	= viewFrame.bottom + 1;
175 	rect.right	+= 1;
176 	BScrollBar* verticalScrollBar = new BScrollBar(rect, "vscroll", fImageView,
177 		0, 150, B_VERTICAL);
178 	AddChild(verticalScrollBar);
179 
180 	SetSizeLimits(250, 100000, 100, 100000);
181 
182 	// finish creating the window
183 	fImageView->SetImage(ref);
184 	fImageView->SetTrackerMessenger(trackerMessenger);
185 
186 #undef B_TRANSLATE_CONTEXT
187 #define B_TRANSLATE_CONTEXT "LoadAlerts"
188 
189 	if (InitCheck() != B_OK) {
190 		BAlert* alert;
191 		alert = new BAlert(
192 			B_TRANSLATE("ShowImage"),
193 			B_TRANSLATE("Could not load image! Either the file or an image "
194 				"translator for it does not exist."),
195 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_INFO_ALERT);
196 		alert->Go();
197 
198 		// quit if file could not be opened
199 		Quit();
200 		return;
201 	}
202 
203 #undef B_TRANSLATE_CONTEXT
204 #define B_TRANSLATE_CONTEXT "Menus"
205 
206 	// add View menu here so it can access ShowImageView methods
207 	BMenu* menu = new BMenu(B_TRANSLATE("View"));
208 	_BuildViewMenu(menu, false);
209 	fBar->AddItem(menu);
210 	_MarkMenuItem(fBar, MSG_DITHER_IMAGE, fImageView->GetDither());
211 	UpdateTitle();
212 
213 	SetPulseRate(100000);
214 		// every 1/10 second; ShowImageView needs it for marching ants
215 
216 	WindowRedimension(fImageView->GetBitmap());
217 	fImageView->MakeFocus(true); // to receive KeyDown messages
218 	Show();
219 
220 	// Tell application object to query the clipboard
221 	// and tell this window if it contains interesting data or not
222 	be_app_messenger.SendMessage(B_CLIPBOARD_CHANGED);
223 }
224 
225 
226 ShowImageWindow::~ShowImageWindow()
227 {
228 	delete fResizerWindowMessenger;
229 }
230 
231 
232 status_t
233 ShowImageWindow::InitCheck()
234 {
235 	if (!fImageView || fImageView->GetBitmap() == NULL)
236 		return B_ERROR;
237 
238 	return B_OK;
239 }
240 
241 
242 void
243 ShowImageWindow::UpdateTitle()
244 {
245 	BString path;
246 	fImageView->GetPath(&path);
247 	SetTitle(path.String());
248 }
249 
250 
251 void
252 ShowImageWindow::BuildContextMenu(BMenu* menu)
253 {
254 	_BuildViewMenu(menu, true);
255 }
256 
257 
258 #undef B_TRANSLATE_CONTEXT
259 #define B_TRANSLATE_CONTEXT "Menus"
260 
261 void
262 ShowImageWindow::_BuildViewMenu(BMenu* menu, bool popupMenu)
263 {
264 	_AddItemMenu(menu, B_TRANSLATE("Slide show"), MSG_SLIDE_SHOW, 0, 0, this);
265 	_MarkMenuItem(menu, MSG_SLIDE_SHOW, fImageView->SlideShowStarted());
266 	BMenu* delayMenu = new BMenu(B_TRANSLATE("Slide delay"));
267 	if (fSlideShowDelay == NULL)
268 		fSlideShowDelay = delayMenu;
269 
270 	delayMenu->SetRadioMode(true);
271 	// Note: ShowImage loads images in window thread so it becomes unresponsive
272 	//		 if slide show delay is too short! (Especially if loading the image
273 	//		 takes as long as or longer than the slide show delay). Should load
274 	//		 in background thread!
275 	_AddDelayItem(delayMenu, B_TRANSLATE("3 seconds"), 3);
276 	_AddDelayItem(delayMenu, B_TRANSLATE("4 seconds"), 4);
277 	_AddDelayItem(delayMenu, B_TRANSLATE("5 seconds"), 5);
278 	_AddDelayItem(delayMenu, B_TRANSLATE("6 seconds"), 6);
279 	_AddDelayItem(delayMenu, B_TRANSLATE("7 seconds"), 7);
280 	_AddDelayItem(delayMenu, B_TRANSLATE("8 seconds"), 8);
281 	_AddDelayItem(delayMenu, B_TRANSLATE("9 seconds"), 9);
282 	_AddDelayItem(delayMenu, B_TRANSLATE("10 seconds"), 10);
283 	_AddDelayItem(delayMenu, B_TRANSLATE("20 seconds"), 20);
284 	menu->AddItem(delayMenu);
285 
286 	menu->AddSeparatorItem();
287 
288 	_AddItemMenu(menu, B_TRANSLATE("Original size"),
289 		MSG_ORIGINAL_SIZE, '1', 0, this);
290 	_AddItemMenu(menu, B_TRANSLATE("Zoom in"), MSG_ZOOM_IN, '+', 0, this);
291 	_AddItemMenu(menu, B_TRANSLATE("Zoom out"), MSG_ZOOM_OUT, '-', 0, this);
292 
293 	menu->AddSeparatorItem();
294 
295 	_AddItemMenu(menu, B_TRANSLATE("High-quality zooming"),
296 		MSG_SCALE_BILINEAR, 0, 0, this);
297 
298 	menu->AddSeparatorItem();
299 
300 	_AddItemMenu(menu, B_TRANSLATE("Shrink to window"),
301 		MSG_SHRINK_TO_WINDOW, 0, 0, this);
302 	_AddItemMenu(menu, B_TRANSLATE("Zoom to window"),
303 		MSG_ZOOM_TO_WINDOW, 0, 0, this);
304 
305 	menu->AddSeparatorItem();
306 
307 	_AddItemMenu(menu, B_TRANSLATE("Full screen"),
308 		MSG_FULL_SCREEN, B_ENTER, 0, this);
309 	_MarkMenuItem(menu, MSG_FULL_SCREEN, fFullScreen);
310 
311 	_AddItemMenu(menu, B_TRANSLATE("Show caption in full screen mode"),
312 		MSG_SHOW_CAPTION, 0, 0, this);
313 	_MarkMenuItem(menu, MSG_SHOW_CAPTION, fShowCaption);
314 
315 	_MarkMenuItem(menu, MSG_SCALE_BILINEAR, fImageView->GetScaleBilinear());
316 
317 	bool shrink, zoom, enabled;
318 
319 	shrink = fImageView->GetShrinkToBounds();
320 	zoom = fImageView->GetZoomToBounds();
321 	_MarkMenuItem(menu, MSG_SHRINK_TO_WINDOW, shrink);
322 	_MarkMenuItem(menu, MSG_ZOOM_TO_WINDOW, zoom);
323 
324 	enabled = !(shrink || zoom);
325 	_EnableMenuItem(menu, MSG_ORIGINAL_SIZE, enabled);
326 	_EnableMenuItem(menu, MSG_ZOOM_IN, enabled);
327 	_EnableMenuItem(menu, MSG_ZOOM_OUT, enabled);
328 
329 	if (popupMenu) {
330 		menu->AddSeparatorItem();
331 		_AddItemMenu(menu, B_TRANSLATE("Use as background" B_UTF8_ELLIPSIS),
332 			MSG_DESKTOP_BACKGROUND, 0, 0, this);
333 	}
334 }
335 
336 
337 void
338 ShowImageWindow::AddMenus(BMenuBar* bar)
339 {
340 	BMenu* menu = new BMenu(B_TRANSLATE("File"));
341 	fOpenMenu = new RecentDocumentsMenu(B_TRANSLATE("Open"));
342 	menu->AddItem(fOpenMenu);
343 	fOpenMenu->Superitem()->SetTrigger('O');
344 	fOpenMenu->Superitem()->SetMessage(new BMessage(MSG_FILE_OPEN));
345 	fOpenMenu->Superitem()->SetTarget(be_app);
346 	fOpenMenu->Superitem()->SetShortcut('O', 0);
347 	menu->AddSeparatorItem();
348 	BMenu *pmenuSaveAs = new BMenu(B_TRANSLATE("Save as" B_UTF8_ELLIPSIS),
349 		B_ITEMS_IN_COLUMN);
350 	BTranslationUtils::AddTranslationItems(pmenuSaveAs, B_TRANSLATOR_BITMAP);
351 		// Fill Save As submenu with all types that can be converted
352 		// to from the Be bitmap image format
353 	menu->AddItem(pmenuSaveAs);
354 	_AddItemMenu(menu, B_TRANSLATE("Close"), B_QUIT_REQUESTED, 'W', 0, this);
355 	menu->AddSeparatorItem();
356 	_AddItemMenu(menu, B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS),
357 		MSG_PAGE_SETUP, 0, 0, this);
358 	_AddItemMenu(menu, B_TRANSLATE("Print" B_UTF8_ELLIPSIS),
359 		MSG_PREPARE_PRINT, 'P', 0, this);
360 	menu->AddSeparatorItem();
361 	_AddItemMenu(menu, B_TRANSLATE("About ShowImage" B_UTF8_ELLIPSIS),
362 		B_ABOUT_REQUESTED, 0, 0, 	be_app);
363 	menu->AddSeparatorItem();
364 	_AddItemMenu(menu, B_TRANSLATE("Quit"), B_QUIT_REQUESTED, 'Q', 0, be_app);
365 	bar->AddItem(menu);
366 
367 	menu = new BMenu(B_TRANSLATE("Edit"));
368 	_AddItemMenu(menu, B_TRANSLATE("Undo"), B_UNDO, 'Z', 0, this, false);
369 	menu->AddSeparatorItem();
370 	_AddItemMenu(menu, B_TRANSLATE("Cut"), B_CUT, 'X', 0, this, false);
371 	_AddItemMenu(menu, B_TRANSLATE("Copy"), B_COPY, 'C', 0, this, false);
372 	_AddItemMenu(menu, B_TRANSLATE("Paste"), B_PASTE, 'V', 0, this, false);
373 	_AddItemMenu(menu, B_TRANSLATE("Clear"),
374 		MSG_CLEAR_SELECT, 0, 0, this, false);
375 	menu->AddSeparatorItem();
376 	_AddItemMenu(menu, B_TRANSLATE("Select all"),
377 		MSG_SELECT_ALL, 'A', 0, this);
378 	bar->AddItem(menu);
379 
380 	menu = fBrowseMenu = new BMenu(B_TRANSLATE("Browse"));
381 	_AddItemMenu(menu, B_TRANSLATE("First page"),
382 		MSG_PAGE_FIRST, B_LEFT_ARROW, B_SHIFT_KEY, this);
383 	_AddItemMenu(menu, B_TRANSLATE("Last page"),
384 		MSG_PAGE_LAST, B_RIGHT_ARROW, B_SHIFT_KEY, this);
385 	_AddItemMenu(menu, B_TRANSLATE("Previous page"),
386 		MSG_PAGE_PREV, B_LEFT_ARROW, 0, this);
387 	_AddItemMenu(menu, B_TRANSLATE("Next page"),
388 		MSG_PAGE_NEXT, B_RIGHT_ARROW, 0, this);
389 	fGoToPageMenu = new BMenu(B_TRANSLATE("Go to page"));
390 	fGoToPageMenu->SetRadioMode(true);
391 	menu->AddItem(fGoToPageMenu);
392 	menu->AddSeparatorItem();
393 	_AddItemMenu(menu, B_TRANSLATE("Previous file"),
394 		MSG_FILE_PREV, B_UP_ARROW, 0, this);
395 	_AddItemMenu(menu, B_TRANSLATE("Next file"),
396 		MSG_FILE_NEXT, B_DOWN_ARROW, 0, this);
397 	bar->AddItem(menu);
398 
399 	menu = new BMenu(B_TRANSLATE("Image"));
400 	_AddItemMenu(menu, B_TRANSLATE("Rotate clockwise"),
401 		MSG_ROTATE_90, 'R', 0, this);
402 	_AddItemMenu(menu, B_TRANSLATE("Rotate counterclockwise"),
403 		MSG_ROTATE_270, 'R', B_SHIFT_KEY, this);
404 	menu->AddSeparatorItem();
405 	_AddItemMenu(menu, B_TRANSLATE("Flip left to right"),
406 		MSG_FLIP_LEFT_TO_RIGHT, 0, 0, this);
407 	_AddItemMenu(menu, B_TRANSLATE("Flip top to bottom"),
408 		MSG_FLIP_TOP_TO_BOTTOM, 0, 0, this);
409 	menu->AddSeparatorItem();
410 	_AddItemMenu(menu, B_TRANSLATE("Invert colors"), MSG_INVERT, 0, 0, this);
411 	menu->AddSeparatorItem();
412 	fResizeItem = _AddItemMenu(menu, B_TRANSLATE("Resize" B_UTF8_ELLIPSIS),
413 		MSG_OPEN_RESIZER_WINDOW, 0, 0, this);
414 	bar->AddItem(menu);
415 	menu->AddSeparatorItem();
416 	_AddItemMenu(menu, B_TRANSLATE("Use as background" B_UTF8_ELLIPSIS),
417 		MSG_DESKTOP_BACKGROUND, 0, 0, this);
418 }
419 
420 
421 BMenuItem*
422 ShowImageWindow::_AddItemMenu(BMenu* menu, const char* label, uint32 what,
423 	const char shortcut, uint32 modifier, const BHandler* target, bool enabled)
424 {
425 	BMenuItem* item = new BMenuItem(label, new BMessage(what), shortcut, modifier);
426 	menu->AddItem(item);
427 
428 	item->SetTarget(target);
429 	item->SetEnabled(enabled);
430 
431 	return item;
432 }
433 
434 
435 BMenuItem*
436 ShowImageWindow::_AddDelayItem(BMenu* menu, const char* label, float value)
437 {
438 	BMessage* message = new BMessage(MSG_SLIDE_SHOW_DELAY);
439 	message->AddFloat("value", value);
440 
441 	BMenuItem* item = new BMenuItem(label, message, 0);
442 	item->SetTarget(this);
443 
444 	bool marked = fImageView->GetSlideShowDelay() == value;
445 	if (marked)
446 		item->SetMarked(true);
447 
448 	menu->AddItem(item);
449 	return item;
450 }
451 
452 
453 void
454 ShowImageWindow::WindowRedimension(BBitmap* pbitmap)
455 {
456 	BScreen screen;
457 	if (!screen.IsValid())
458 		return;
459 
460 	BRect r(pbitmap->Bounds());
461 	float width = r.Width() + 2 * PEN_SIZE + B_V_SCROLL_BAR_WIDTH;
462 	float height = r.Height() + 2 * PEN_SIZE + 1 + fBar->Frame().Height() +
463 		B_H_SCROLL_BAR_HEIGHT;
464 
465 	BRect frame = screen.Frame();
466 	const float windowBorder = 5;
467 	// dimensions so that window does not reach outside of screen
468 	float maxWidth = frame.Width() + 1 - windowBorder - Frame().left;
469 	float maxHeight = frame.Height() + 1 - windowBorder - Frame().top;
470 
471 	// We have to check size limits manually, otherwise
472 	// menu bar will be too short for small images.
473 
474 	float minW, maxW, minH, maxH;
475 	GetSizeLimits(&minW, &maxW, &minH, &maxH);
476 	if (maxWidth > maxW)
477 		maxWidth = maxW;
478 	if (maxHeight > maxH)
479 		maxHeight = maxH;
480 	if (width < minW)
481 		width = minW;
482 	if (height < minH)
483 		height = minH;
484 
485 	if (width > maxWidth)
486 		width = maxWidth;
487 	if (height > maxHeight)
488 		height = maxHeight;
489 
490 	ResizeTo(width, height);
491 }
492 
493 
494 void
495 ShowImageWindow::FrameResized(float width, float height)
496 {
497 	BWindow::FrameResized(width, height);
498 }
499 
500 
501 bool
502 ShowImageWindow::_ToggleMenuItem(uint32 what)
503 {
504 	bool marked = false;
505 	BMenuItem* item = fBar->FindItem(what);
506 	if (item != NULL) {
507 		marked = !item->IsMarked();
508 		item->SetMarked(marked);
509 	}
510 	return marked;
511 }
512 
513 
514 void
515 ShowImageWindow::_EnableMenuItem(BMenu* menu, uint32 what, bool enable)
516 {
517 	BMenuItem* item = menu->FindItem(what);
518 	if (item && item->IsEnabled() != enable)
519 		item->SetEnabled(enable);
520 }
521 
522 
523 void
524 ShowImageWindow::_MarkMenuItem(BMenu* menu, uint32 what, bool marked)
525 {
526 	BMenuItem* item = menu->FindItem(what);
527 	if (item && item->IsMarked() != marked)
528 		item->SetMarked(marked);
529 }
530 
531 
532 void
533 ShowImageWindow::_MarkSlideShowDelay(float value)
534 {
535 	const int32 n = fSlideShowDelay->CountItems();
536 	float v;
537 	for (int32 i = 0; i < n; i ++) {
538 		BMenuItem* item = fSlideShowDelay->ItemAt(i);
539 		if (item) {
540 			if (item->Message()->FindFloat("value", &v) == B_OK && v == value) {
541 				if (!item->IsMarked())
542 					item->SetMarked(true);
543 				return;
544 			}
545 		}
546 	}
547 }
548 
549 
550 void
551 ShowImageWindow::_ResizeToWindow(bool shrink, uint32 what)
552 {
553 	bool enabled = _ToggleMenuItem(what);
554 	if (shrink)
555 		fImageView->SetShrinkToBounds(enabled);
556 	else
557 		fImageView->SetZoomToBounds(enabled);
558 
559 	enabled = !(fImageView->GetShrinkToBounds() || fImageView->GetZoomToBounds());
560 	_EnableMenuItem(fBar, MSG_ORIGINAL_SIZE, enabled);
561 	_EnableMenuItem(fBar, MSG_ZOOM_IN, enabled);
562 	_EnableMenuItem(fBar, MSG_ZOOM_OUT, enabled);
563 }
564 
565 
566 void
567 ShowImageWindow::MessageReceived(BMessage* message)
568 {
569 	switch (message->what) {
570 		case MSG_MODIFIED:
571 			// If image has been modified due to a Cut or Paste
572 			fModified = true;
573 			break;
574 
575 		case MSG_OUTPUT_TYPE:
576 			// User clicked Save As then choose an output format
577 			if (!fSavePanel)
578 				// If user doesn't already have a save panel open
579 				_SaveAs(message);
580 			break;
581 
582 		case MSG_SAVE_PANEL:
583 			// User specified where to save the output image
584 			_SaveToFile(message);
585 			break;
586 
587 		case B_CANCEL:
588 			delete fSavePanel;
589 			fSavePanel = NULL;
590 			break;
591 
592 		case MSG_UPDATE_STATUS: {
593 			int32 pages = fImageView->PageCount();
594 			int32 curPage = fImageView->CurrentPage();
595 
596 			bool enable = (pages > 1) ? true : false;
597 			_EnableMenuItem(fBar, MSG_PAGE_FIRST, enable);
598 			_EnableMenuItem(fBar, MSG_PAGE_LAST, enable);
599 			_EnableMenuItem(fBar, MSG_PAGE_NEXT, enable);
600 			_EnableMenuItem(fBar, MSG_PAGE_PREV, enable);
601 			fGoToPageMenu->SetEnabled(enable);
602 
603 			_EnableMenuItem(fBar, MSG_FILE_NEXT, fImageView->HasNextFile());
604 			_EnableMenuItem(fBar, MSG_FILE_PREV, fImageView->HasPrevFile());
605 
606 			if (fGoToPageMenu->CountItems() != pages) {
607 				// Only rebuild the submenu if the number of
608 				// pages is different
609 
610 				while (fGoToPageMenu->CountItems() > 0)
611 					// Remove all page numbers
612 					delete fGoToPageMenu->RemoveItem(0L);
613 
614 				for (int32 i = 1; i <= pages; i++) {
615 					// Fill Go To page submenu with an entry for each page
616 					BMessage* pgomsg = new BMessage(MSG_GOTO_PAGE);
617 					pgomsg->AddInt32("page", i);
618 
619 					char shortcut = 0;
620 					if (i < 10) {
621 						shortcut = '0' + i;
622 					} else if (i == 10) {
623 						shortcut = '0';
624 					}
625 
626 					BString strCaption;
627 					strCaption << i;
628 
629 					BMenuItem* item = new BMenuItem(strCaption.String(), pgomsg,
630 						shortcut);
631 					if (curPage == i)
632 						item->SetMarked(true);
633 					fGoToPageMenu->AddItem(item);
634 				}
635 			} else {
636 				// Make sure the correct page is marked
637 				BMenuItem *pcurItem;
638 				pcurItem = fGoToPageMenu->ItemAt(curPage - 1);
639 				if (!pcurItem->IsMarked()) {
640 					pcurItem->SetMarked(true);
641 				}
642 			}
643 
644 			// Disable the Invert menu item if the bitmap color space
645 			// is B_CMAP8. (B_CMAP8 is currently unsupported by the
646 			// invert algorithm)
647 			color_space colors = B_NO_COLOR_SPACE;
648 			message->FindInt32("colors", reinterpret_cast<int32 *>(&colors));
649 			_EnableMenuItem(fBar, MSG_INVERT, (colors != B_CMAP8));
650 
651 			BString status;
652 			bool messageProvidesSize = false;
653 			if (message->FindInt32("width", &fWidth) >= B_OK
654 				&& message->FindInt32("height", &fHeight) >= B_OK) {
655 				status << fWidth << "x" << fHeight;
656 				messageProvidesSize = true;
657 			}
658 
659 			BString str;
660 			if (message->FindString("status", &str) == B_OK && str.Length() > 0) {
661 				if (status.Length() > 0)
662 					status << ", ";
663 				status << str;
664 			}
665 
666 			if (messageProvidesSize) {
667 				_UpdateResizerWindow(fWidth, fHeight);
668 				if (!fImageView->GetZoomToBounds()
669 					&& !fImageView->GetShrinkToBounds()
670 					&& !fFullScreen)
671 					WindowRedimension(fImageView->GetBitmap());
672 			}
673 
674 			fStatusView->SetText(status);
675 
676 			UpdateTitle();
677 		}	break;
678 
679 		case MSG_UPDATE_STATUS_TEXT: {
680 			BString status;
681 			status << fWidth << "x" << fHeight;
682 			BString str;
683 			if (message->FindString("status", &str) == B_OK && str.Length() > 0) {
684 				status << ", " << str;
685 				fStatusView->SetText(status);
686 			}
687 		}	break;
688 
689 		case MSG_SELECTION: {
690 			// The view sends this message when a selection is
691 			// made or the selection is cleared so that the window
692 			// can update the state of the appropriate menu items
693 			bool benable;
694 			if (message->FindBool("has_selection", &benable) == B_OK) {
695 				_EnableMenuItem(fBar, B_CUT, benable);
696 				_EnableMenuItem(fBar, B_COPY, benable);
697 				_EnableMenuItem(fBar, MSG_CLEAR_SELECT, benable);
698 			}
699 		}	break;
700 
701 		case MSG_UNDO_STATE: {
702 			bool benable;
703 			if (message->FindBool("can_undo", &benable) == B_OK)
704 				_EnableMenuItem(fBar, B_UNDO, benable);
705 		}	break;
706 
707 		case MSG_CLIPBOARD_CHANGED: {
708 			// The app sends this message after it examines the clipboard in
709 			// response to a B_CLIPBOARD_CHANGED message
710 			bool bdata;
711 			if (message->FindBool("data_available", &bdata) == B_OK)
712 				_EnableMenuItem(fBar, B_PASTE, bdata);
713 		}	break;
714 
715 		case B_UNDO:
716 			fImageView->Undo();
717 			break;
718 
719 		case B_CUT:
720 			fImageView->Cut();
721 			break;
722 
723 		case B_COPY:
724 			fImageView->CopySelectionToClipboard();
725 			break;
726 
727 		case B_PASTE:
728 			fImageView->Paste();
729 			break;
730 
731 		case MSG_CLEAR_SELECT:
732 			fImageView->ClearSelection();
733 			break;
734 
735 		case MSG_SELECT_ALL:
736 			fImageView->SelectAll();
737 			break;
738 
739 		case MSG_PAGE_FIRST:
740 			if (_ClosePrompt())
741 				fImageView->FirstPage();
742 			break;
743 
744 		case MSG_PAGE_LAST:
745 			if (_ClosePrompt())
746 				fImageView->LastPage();
747 			break;
748 
749 		case MSG_PAGE_NEXT:
750 			if (_ClosePrompt())
751 				fImageView->NextPage();
752 			break;
753 
754 		case MSG_PAGE_PREV:
755 			if (_ClosePrompt())
756 				fImageView->PrevPage();
757 			break;
758 
759 		case MSG_GOTO_PAGE: {
760 			if (!_ClosePrompt())
761 				break;
762 
763 			int32 newPage;
764 			if (message->FindInt32("page", &newPage) != B_OK)
765 				break;
766 
767 			int32 curPage = fImageView->CurrentPage();
768 			int32 pages = fImageView->PageCount();
769 
770 			if (newPage > 0 && newPage <= pages) {
771 				BMenuItem* pcurItem = fGoToPageMenu->ItemAt(curPage - 1);
772 				BMenuItem* pnewItem = fGoToPageMenu->ItemAt(newPage - 1);
773 				if (pcurItem && pnewItem) {
774 					pcurItem->SetMarked(false);
775 					pnewItem->SetMarked(true);
776 					fImageView->GoToPage(newPage);
777 				}
778 			}
779 		}	break;
780 
781 		case MSG_DITHER_IMAGE:
782 			fImageView->SetDither(_ToggleMenuItem(message->what));
783 			break;
784 
785 		case MSG_SHRINK_TO_WINDOW:
786 			_ResizeToWindow(true, message->what);
787 			break;
788 
789 		case MSG_ZOOM_TO_WINDOW:
790 			_ResizeToWindow(false, message->what);
791 			break;
792 
793 		case MSG_FILE_PREV:
794 			if (_ClosePrompt())
795 				fImageView->PrevFile();
796 			break;
797 
798 		case MSG_FILE_NEXT:
799 			if (_ClosePrompt())
800 				fImageView->NextFile();
801 			break;
802 
803 		case MSG_ROTATE_90:
804 			fImageView->Rotate(90);
805 			break;
806 
807 		case MSG_ROTATE_270:
808 			fImageView->Rotate(270);
809 			break;
810 
811 		case MSG_FLIP_LEFT_TO_RIGHT:
812 			fImageView->Flip(true);
813 			break;
814 
815 		case MSG_FLIP_TOP_TO_BOTTOM:
816 			fImageView->Flip(false);
817 			break;
818 
819 		case MSG_INVERT:
820 			fImageView->Invert();
821 			break;
822 
823 		case MSG_SLIDE_SHOW: {
824 			BMenuItem* item = fBar->FindItem(message->what);
825 			if (!item)
826 				break;
827 			if (item->IsMarked()) {
828 				item->SetMarked(false);
829 				fResizeItem->SetEnabled(true);
830 				fImageView->StopSlideShow();
831 			} else if (_ClosePrompt()) {
832 				item->SetMarked(true);
833 				fResizeItem->SetEnabled(false);
834 				fImageView->StartSlideShow();
835 			}
836 		}	break;
837 
838 		case MSG_SLIDE_SHOW_DELAY: {
839 			float value;
840 			if (message->FindFloat("value", &value) == B_OK) {
841 				fImageView->SetSlideShowDelay(value);
842 				// in case message is sent from popup menu
843 				_MarkSlideShowDelay(value);
844 			}
845 		}	break;
846 
847 		case MSG_FULL_SCREEN:
848 			_ToggleFullScreen();
849 			break;
850 
851 		case MSG_EXIT_FULL_SCREEN:
852 			if (fFullScreen)
853 				_ToggleFullScreen();
854 			break;
855 
856 		case MSG_SHOW_CAPTION: {
857 			fShowCaption = _ToggleMenuItem(message->what);
858 			ShowImageSettings* settings = my_app->Settings();
859 
860 			if (settings->Lock()) {
861 				settings->SetBool("ShowCaption", fShowCaption);
862 				settings->Unlock();
863 			}
864 			if (fFullScreen)
865 				fImageView->SetShowCaption(fShowCaption);
866 		}	break;
867 
868 		case MSG_PAGE_SETUP:
869 			_PageSetup();
870 			break;
871 
872 		case MSG_PREPARE_PRINT:
873 			_PrepareForPrint();
874 			break;
875 
876 		case MSG_PRINT:
877 			_Print(message);
878 			break;
879 
880 		case MSG_ZOOM_IN:
881 			fImageView->ZoomIn();
882 			break;
883 
884 		case MSG_ZOOM_OUT:
885 			fImageView->ZoomOut();
886 			break;
887 
888 		case MSG_ORIGINAL_SIZE:
889 			fImageView->SetZoom(1.0);
890 			break;
891 
892 		case MSG_SCALE_BILINEAR:
893 			fImageView->SetScaleBilinear(_ToggleMenuItem(message->what));
894 			break;
895 
896 		case MSG_OPEN_RESIZER_WINDOW: {
897 			if (fImageView->GetBitmap() != NULL) {
898 				BRect rect = fImageView->GetBitmap()->Bounds();
899 				_OpenResizerWindow(rect.IntegerWidth()+1, rect.IntegerHeight()+1);
900 			}
901 		}	break;
902 
903 		case MSG_RESIZE: {
904 			int w = message->FindInt32("w");
905 			int h = message->FindInt32("h");
906 			fImageView->ResizeImage(w, h);
907 		} break;
908 
909 		case MSG_RESIZER_WINDOW_QUIT:
910 			delete fResizerWindowMessenger;
911 			fResizerWindowMessenger = NULL;
912 			break;
913 
914 		case MSG_DESKTOP_BACKGROUND: {
915 			BMessage message(B_REFS_RECEIVED);
916 			message.AddRef("refs", fImageView->Image());
917 			// This is used in the Backgrounds code for scaled placement
918 			message.AddInt32("placement", 'scpl');
919 			be_roster->Launch("application/x-vnd.haiku-backgrounds", &message);
920 		}	break;
921 
922 		default:
923 			BWindow::MessageReceived(message);
924 			break;
925 	}
926 }
927 
928 
929 void
930 ShowImageWindow::_SaveAs(BMessage* message)
931 {
932 	// Read the translator and output type the user chose
933 	translator_id outTranslator;
934 	uint32 outType;
935 	if (message->FindInt32(kTranslatorField,
936 			reinterpret_cast<int32 *>(&outTranslator)) != B_OK
937 		|| message->FindInt32(kTypeField,
938 			reinterpret_cast<int32 *>(&outType)) != B_OK)
939 		return;
940 
941 	// Add the chosen translator and output type to the
942 	// message that the save panel will send back
943 	BMessage panelMsg(MSG_SAVE_PANEL);
944 	panelMsg.AddInt32(kTranslatorField, outTranslator);
945 	panelMsg.AddInt32(kTypeField, outType);
946 
947 	// Create save panel and show it
948 	BMessenger target(this);
949 	fSavePanel = new (std::nothrow) BFilePanel(B_SAVE_PANEL,
950 		&target, NULL, 0, false, &panelMsg);
951 	if (!fSavePanel)
952 		return;
953 
954 	fSavePanel->Window()->SetWorkspaces(B_CURRENT_WORKSPACE);
955 	fSavePanel->Show();
956 }
957 
958 
959 void
960 ShowImageWindow::_SaveToFile(BMessage* message)
961 {
962 	// Read in where the file should be saved
963 	entry_ref dirRef;
964 	if (message->FindRef("directory", &dirRef) != B_OK)
965 		return;
966 
967 	const char* filename;
968 	if (message->FindString("name", &filename) != B_OK)
969 		return;
970 
971 	// Read in the translator and type to be used
972 	// to save the output image
973 	translator_id outTranslator;
974 	uint32 outType;
975 	if (message->FindInt32(kTranslatorField,
976 			reinterpret_cast<int32 *>(&outTranslator)) != B_OK
977 		|| message->FindInt32(kTypeField,
978 			reinterpret_cast<int32 *>(&outType)) != B_OK)
979 		return;
980 
981 	// Find the translator_format information needed to
982 	// write a MIME attribute for the image file
983 	BTranslatorRoster* roster = BTranslatorRoster::Default();
984 	const translation_format* outFormat = NULL;
985 	int32 outCount = 0;
986 	if (roster->GetOutputFormats(outTranslator, &outFormat, &outCount) != B_OK
987 		|| outCount < 1)
988 		return;
989 
990 	int32 i;
991 	for (i = 0; i < outCount; i++) {
992 		if (outFormat[i].group == B_TRANSLATOR_BITMAP && outFormat[i].type == outType)
993 			break;
994 	}
995 	if (i == outCount)
996 		return;
997 
998 	// Write out the image file
999 	BDirectory dir(&dirRef);
1000 	fImageView->SaveToFile(&dir, filename, NULL, &outFormat[i]);
1001 }
1002 
1003 
1004 // This is temporary solution for building BString with printf like format.
1005 // will be removed in the future.
1006 static void
1007 bs_printf(BString* string, const char* format, ...)
1008 {
1009 	va_list ap;
1010 	char* buf;
1011 
1012 	va_start(ap, format);
1013 	vasprintf(&buf, format, ap);
1014 	string->SetTo(buf);
1015 	free(buf);
1016 	va_end(ap);
1017 }
1018 
1019 
1020 #undef B_TRANSLATE_CONTEXT
1021 #define B_TRANSLATE_CONTEXT "ClosePrompt"
1022 
1023 bool
1024 ShowImageWindow::_ClosePrompt()
1025 {
1026 	if (!fModified)
1027 		return true;
1028 
1029 	int32 page, count;
1030 	count = fImageView->PageCount();
1031 	page = fImageView->CurrentPage();
1032 	BString prompt, name;
1033 	fImageView->GetName(&name);
1034 
1035 	if (count > 1) {
1036 		bs_printf(&prompt,
1037 			B_TRANSLATE("The document '%s' (page %d) has been changed. Do you "
1038 				"want to close the document?"),
1039 			name.String(), page);
1040 	} else {
1041 		bs_printf(&prompt,
1042 			B_TRANSLATE("The document '%s' has been changed. Do you want to "
1043 				"close the document?"),
1044 			name.String());
1045 	}
1046 
1047 	BAlert* pAlert = new BAlert(B_TRANSLATE("Close document"), prompt.String(),
1048 		B_TRANSLATE("Cancel"), B_TRANSLATE("Close"));
1049 	if (pAlert->Go() == 0) {
1050 		// Cancel
1051 		return false;
1052 	} else {
1053 		// Close
1054 		fModified = false;
1055 		return true;
1056 	}
1057 }
1058 
1059 
1060 void
1061 ShowImageWindow::_ToggleFullScreen()
1062 {
1063 	BRect frame;
1064 	fFullScreen = !fFullScreen;
1065 	if (fFullScreen) {
1066 		BScreen screen;
1067 		fWindowFrame = Frame();
1068 		frame = screen.Frame();
1069 		frame.top -= fBar->Bounds().Height()+1;
1070 		frame.right += B_V_SCROLL_BAR_WIDTH;
1071 		frame.bottom += B_H_SCROLL_BAR_HEIGHT;
1072 		frame.InsetBy(-1, -1); // PEN_SIZE in ShowImageView
1073 
1074 		SetFlags(Flags() | B_NOT_RESIZABLE | B_NOT_MOVABLE);
1075 
1076 		Activate();
1077 			// make the window frontmost
1078 	} else {
1079 		frame = fWindowFrame;
1080 
1081 		SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE));
1082 	}
1083 
1084 	fImageView->SetFullScreen(fFullScreen);
1085 	fImageView->SetShowCaption(fFullScreen && fShowCaption);
1086 	MoveTo(frame.left, frame.top);
1087 	ResizeTo(frame.Width(), frame.Height());
1088 }
1089 
1090 
1091 void
1092 ShowImageWindow::_LoadSettings()
1093 {
1094 	ShowImageSettings* settings = my_app->Settings();
1095 
1096 	if (settings->Lock()) {
1097 		fShowCaption = settings->GetBool("ShowCaption", fShowCaption);
1098 		fPrintOptions.SetBounds(BRect(0, 0, 1023, 767));
1099 
1100 		int32 op = settings->GetInt32("PO:Option", fPrintOptions.Option());
1101 		fPrintOptions.SetOption((enum PrintOptions::Option)op);
1102 
1103 		float f = settings->GetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor());
1104 		fPrintOptions.SetZoomFactor(f);
1105 
1106 		f = settings->GetFloat("PO:DPI", fPrintOptions.DPI());
1107 		fPrintOptions.SetDPI(f);
1108 
1109 		f = settings->GetFloat("PO:Width", fPrintOptions.Width());
1110 		fPrintOptions.SetWidth(f);
1111 
1112 		f = settings->GetFloat("PO:Height", fPrintOptions.Height());
1113 		fPrintOptions.SetHeight(f);
1114 
1115 		settings->Unlock();
1116 	}
1117 }
1118 
1119 
1120 void
1121 ShowImageWindow::_SavePrintOptions()
1122 {
1123 	ShowImageSettings* settings = my_app->Settings();
1124 
1125 	if (settings->Lock()) {
1126 		settings->SetInt32("PO:Option", fPrintOptions.Option());
1127 		settings->SetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor());
1128 		settings->SetFloat("PO:DPI", fPrintOptions.DPI());
1129 		settings->SetFloat("PO:Width", fPrintOptions.Width());
1130 		settings->SetFloat("PO:Height", fPrintOptions.Height());
1131 		settings->Unlock();
1132 	}
1133 }
1134 
1135 
1136 bool
1137 ShowImageWindow::_PageSetup()
1138 {
1139 	BString name;
1140 	fImageView->GetName(&name);
1141 	BPrintJob printJob(name.String());
1142 	if (fPrintSettings != NULL)
1143 		printJob.SetSettings(new BMessage(*fPrintSettings));
1144 
1145 	status_t status = printJob.ConfigPage();
1146 	if (status == B_OK) {
1147 		delete fPrintSettings;
1148 		fPrintSettings = printJob.Settings();
1149 	}
1150 
1151 	return status == B_OK;
1152 }
1153 
1154 
1155 void
1156 ShowImageWindow::_PrepareForPrint()
1157 {
1158 	if (fPrintSettings == NULL) {
1159 		BString name;
1160 		fImageView->GetName(&name);
1161 
1162 		BPrintJob printJob("");
1163 		if (printJob.ConfigJob() == B_OK)
1164 			fPrintSettings = printJob.Settings();
1165 	}
1166 
1167 	fPrintOptions.SetBounds(fImageView->GetBitmap()->Bounds());
1168 	fPrintOptions.SetWidth(fImageView->GetBitmap()->Bounds().Width() + 1);
1169 
1170 	new PrintOptionsWindow(BPoint(Frame().left + 30, Frame().top + 50),
1171 		&fPrintOptions, this);
1172 }
1173 
1174 
1175 void
1176 ShowImageWindow::_Print(BMessage* msg)
1177 {
1178 	status_t st;
1179 	if (msg->FindInt32("status", &st) != B_OK || st != B_OK)
1180 		return;
1181 
1182 	_SavePrintOptions();
1183 
1184 	BString name;
1185 	fImageView->GetName(&name);
1186 
1187 	BPrintJob printJob(name.String());
1188 	if (fPrintSettings)
1189 		printJob.SetSettings(new BMessage(*fPrintSettings));
1190 
1191 	if (printJob.ConfigJob() == B_OK) {
1192 		delete fPrintSettings;
1193 		fPrintSettings = printJob.Settings();
1194 
1195 		// first/lastPage is unused for now
1196 		int32 firstPage = printJob.FirstPage();
1197 		int32 lastPage = printJob.LastPage();
1198 		BRect printableRect = printJob.PrintableRect();
1199 
1200 		if (firstPage < 1)
1201 			firstPage = 1;
1202 		if (lastPage < firstPage)
1203 			lastPage = firstPage;
1204 
1205 		BBitmap* bitmap = fImageView->GetBitmap();
1206 		float imageWidth = bitmap->Bounds().Width() + 1.0;
1207 		float imageHeight = bitmap->Bounds().Height() + 1.0;
1208 
1209 		float width;
1210 		switch (fPrintOptions.Option()) {
1211 			case PrintOptions::kFitToPage: {
1212 				float w1 = printableRect.Width()+1;
1213 				float w2 = imageWidth * (printableRect.Height() + 1) / imageHeight;
1214 				if (w2 < w1)
1215 					width = w2;
1216 				else
1217 					width = w1;
1218 			}	break;
1219 			case PrintOptions::kZoomFactor:
1220 				width = imageWidth * fPrintOptions.ZoomFactor();
1221 				break;
1222 			case PrintOptions::kDPI:
1223 				width = imageWidth * 72.0 / fPrintOptions.DPI();
1224 				break;
1225 			case PrintOptions::kWidth:
1226 			case PrintOptions::kHeight:
1227 				width = fPrintOptions.Width();
1228 				break;
1229 
1230 			default:
1231 				// keep compiler silent; should not reach here
1232 				width = imageWidth;
1233 		}
1234 
1235 		// TODO: eventually print large images on several pages
1236 		printJob.BeginJob();
1237 		fImageView->SetScale(width / imageWidth);
1238 		// coordinates are relative to printable rectangle
1239 		BRect bounds(bitmap->Bounds());
1240 		printJob.DrawView(fImageView, bounds, BPoint(0, 0));
1241 		fImageView->SetScale(1.0);
1242 		printJob.SpoolPage();
1243 		printJob.CommitJob();
1244 	}
1245 }
1246 
1247 
1248 void
1249 ShowImageWindow::_OpenResizerWindow(int32 width, int32 height)
1250 {
1251 	if (fResizerWindowMessenger == NULL) {
1252 		// open window if it is not already opened
1253 		BWindow* window = new ResizerWindow(this, width, height);
1254 		fResizerWindowMessenger = new BMessenger(window);
1255 		window->Show();
1256 	} else {
1257 		fResizerWindowMessenger->SendMessage(ResizerWindow::kActivateMsg);
1258 	}
1259 }
1260 
1261 
1262 void
1263 ShowImageWindow::_UpdateResizerWindow(int32 width, int32 height)
1264 {
1265 	if (fResizerWindowMessenger == NULL)
1266 		return;
1267 
1268 	BMessage updateMsg(ResizerWindow::kUpdateMsg);
1269 	updateMsg.AddInt32("width", width);
1270 	updateMsg.AddInt32("height", height);
1271 	fResizerWindowMessenger->SendMessage(&updateMsg);
1272 }
1273 
1274 
1275 void
1276 ShowImageWindow::_CloseResizerWindow()
1277 {
1278 	if (fResizerWindowMessenger == NULL)
1279 		return;
1280 
1281 	fResizerWindowMessenger->SendMessage(B_QUIT_REQUESTED);
1282 	delete fResizerWindowMessenger;
1283 	fResizerWindowMessenger = NULL;
1284 }
1285 
1286 
1287 bool
1288 ShowImageWindow::QuitRequested()
1289 {
1290 	if (fSavePanel) {
1291 		// Don't allow this window to be closed if a save panel is open
1292 		return false;
1293 	}
1294 
1295 	bool quit = _ClosePrompt();
1296 
1297 	if (quit) {
1298 		_CloseResizerWindow();
1299 
1300 		// tell the app to forget about this window
1301 		be_app->PostMessage(MSG_WINDOW_QUIT);
1302 	}
1303 
1304 	return quit;
1305 }
1306 
1307 
1308 void
1309 ShowImageWindow::ScreenChanged(BRect frame, color_space mode)
1310 {
1311 	fImageView->SetDither(mode == B_CMAP8);
1312 }
1313