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