1 /*
2 * Copyright 2003-2014, 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 * Stephan Aßmus <superstippi@gmx.de>
15 */
16
17
18 #include "ShowImageWindow.h"
19
20 #include <new>
21 #include <stdio.h>
22 #include <stdlib.h>
23
24 #include <Alert.h>
25 #include <Application.h>
26 #include <Bitmap.h>
27 #include <BitmapStream.h>
28 #include <Button.h>
29 #include <Catalog.h>
30 #include <Clipboard.h>
31 #include <ControlLook.h>
32 #include <DurationFormat.h>
33 #include <Entry.h>
34 #include <File.h>
35 #include <FilePanel.h>
36 #include <GridLayout.h>
37 #include <Locale.h>
38 #include <Menu.h>
39 #include <MenuBar.h>
40 #include <MenuItem.h>
41 #include <MessageRunner.h>
42 #include <Path.h>
43 #include <PrintJob.h>
44 #include <RecentItems.h>
45 #include <Roster.h>
46 #include <Screen.h>
47 #include <ScrollView.h>
48 #include <String.h>
49 #include <SupportDefs.h>
50 #include <TranslationDefs.h>
51 #include <TranslationUtils.h>
52 #include <TranslatorRoster.h>
53
54 #include "ImageCache.h"
55 #include "ProgressWindow.h"
56 #include "ShowImageApp.h"
57 #include "ShowImageConstants.h"
58 #include "ShowImageStatusView.h"
59 #include "ShowImageView.h"
60 #include "SupportingAppsMenu.h"
61 #include "ToolBarIcons.h"
62
63
64 // BMessage field names used in Save messages
65 const char* kTypeField = "be:type";
66 const char* kTranslatorField = "be:translator";
67
68 const bigtime_t kDefaultSlideShowDelay = 3000000;
69 // 3 seconds
70
71
72 // message constants
73 enum {
74 MSG_CAPTURE_MOUSE = 'mCPM',
75 MSG_CHANGE_FOCUS = 'mCFS',
76 MSG_WINDOW_QUIT = 'mWQT',
77 MSG_OUTPUT_TYPE = 'BTMN',
78 MSG_SAVE_PANEL = 'mFSP',
79 MSG_CLEAR_SELECT = 'mCSL',
80 MSG_SELECT_ALL = 'mSAL',
81 MSG_SELECTION_MODE = 'mSLM',
82 MSG_PAGE_FIRST = 'mPGF',
83 MSG_PAGE_LAST = 'mPGL',
84 MSG_PAGE_NEXT = 'mPGN',
85 MSG_PAGE_PREV = 'mPGP',
86 MSG_GOTO_PAGE = 'mGTP',
87 MSG_ZOOM_IN = 'mZIN',
88 MSG_ZOOM_OUT = 'mZOU',
89 MSG_SCALE_BILINEAR = 'mSBL',
90 MSG_DESKTOP_BACKGROUND = 'mDBG',
91 MSG_ROTATE_90 = 'mR90',
92 MSG_ROTATE_270 = 'mR27',
93 MSG_FLIP_LEFT_TO_RIGHT = 'mFLR',
94 MSG_FLIP_TOP_TO_BOTTOM = 'mFTB',
95 MSG_SLIDE_SHOW_DELAY = 'mSSD',
96 MSG_SHOW_CAPTION = 'mSCP',
97 MSG_PAGE_SETUP = 'mPSU',
98 MSG_PREPARE_PRINT = 'mPPT',
99 MSG_GET_INFO = 'mGFI',
100 MSG_SET_RATING = 'mSRT',
101 kMsgFitToWindow = 'mFtW',
102 kMsgOriginalSize = 'mOSZ',
103 kMsgStretchToWindow = 'mStW',
104 kMsgNextSlide = 'mNxS',
105 kMsgToggleToolBar = 'mTTB',
106 kMsgSlideToolBar = 'mSTB',
107 kMsgFinishSlidingToolBar = 'mFST'
108 };
109
110
111 // This is temporary solution for building BString with printf like format.
112 // will be removed in the future.
113 static void
bs_printf(BString * string,const char * format,...)114 bs_printf(BString* string, const char* format, ...)
115 {
116 va_list ap;
117 char* buf;
118
119 va_start(ap, format);
120 vasprintf(&buf, format, ap);
121 string->SetTo(buf);
122 free(buf);
123 va_end(ap);
124 }
125
126
127 // #pragma mark -- ShowImageWindow
128
129
130 #undef B_TRANSLATION_CONTEXT
131 #define B_TRANSLATION_CONTEXT "Menus"
132
133
ShowImageWindow(BRect frame,const entry_ref & ref,const BMessenger & trackerMessenger)134 ShowImageWindow::ShowImageWindow(BRect frame, const entry_ref& ref,
135 const BMessenger& trackerMessenger)
136 :
137 BWindow(frame, "", B_DOCUMENT_WINDOW, 0),
138 fNavigator(ref, trackerMessenger),
139 fSavePanel(NULL),
140 fBar(NULL),
141 fBrowseMenu(NULL),
142 fGoToPageMenu(NULL),
143 fSlideShowDelayMenu(NULL),
144 fToolBar(NULL),
145 fImageView(NULL),
146 fStatusView(NULL),
147 fProgressWindow(new ProgressWindow()),
148 fModified(false),
149 fFullScreen(false),
150 fShowCaption(true),
151 fShowToolBar(true),
152 fPrintSettings(NULL),
153 fSlideShowRunner(NULL),
154 fSlideShowDelay(kDefaultSlideShowDelay)
155 {
156 _ApplySettings();
157
158 SetLayout(new BGroupLayout(B_VERTICAL, 0));
159
160 // create menu bar
161 fBar = new BMenuBar("menu_bar");
162 _AddMenus(fBar);
163 float menuBarMinWidth = fBar->MinSize().width;
164 AddChild(fBar);
165
166 // Add a content view so the tool bar can be moved outside of the
167 // visible portion without colliding with the menu bar.
168
169 BView* contentView = new BView(BRect(), "content", B_FOLLOW_NONE, 0);
170 contentView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
171 contentView->SetExplicitMinSize(BSize(250, 100));
172 AddChild(contentView);
173
174 // Create the tool bar
175 BRect viewFrame = contentView->Bounds();
176 fToolBar = new BToolBar(viewFrame);
177
178 // Add the tool icons.
179
180 // fToolBar->AddAction(MSG_FILE_OPEN, be_app,
181 // tool_bar_icon(kIconDocumentOpen), B_TRANSLATE("Open" B_UTF8_ELLIPSIS));
182 fToolBar->AddAction(MSG_FILE_PREV, this,
183 tool_bar_icon(kIconGoPrevious), B_TRANSLATE("Previous file"));
184 fToolBar->AddAction(MSG_FILE_NEXT, this, tool_bar_icon(kIconGoNext),
185 B_TRANSLATE("Next file"));
186 BMessage* fullScreenSlideShow = new BMessage(MSG_SLIDE_SHOW);
187 fullScreenSlideShow->AddBool("full screen", true);
188 fToolBar->AddAction(fullScreenSlideShow, this,
189 tool_bar_icon(kIconMediaMovieLibrary), B_TRANSLATE("Slide show"));
190 fToolBar->AddSeparator();
191 fToolBar->AddAction(MSG_SELECTION_MODE, this,
192 tool_bar_icon(kIconDrawRectangularSelection),
193 B_TRANSLATE("Selection mode"));
194 fToolBar->AddSeparator();
195 fToolBar->AddAction(kMsgOriginalSize, this,
196 tool_bar_icon(kIconZoomOriginal), B_TRANSLATE("Original size"), NULL,
197 true);
198 fToolBar->AddAction(kMsgFitToWindow, this,
199 tool_bar_icon(kIconZoomFitBest), B_TRANSLATE("Fit to window"));
200 fToolBar->AddAction(MSG_ZOOM_IN, this, tool_bar_icon(kIconZoomIn),
201 B_TRANSLATE("Zoom in"));
202 fToolBar->AddAction(MSG_ZOOM_OUT, this, tool_bar_icon(kIconZoomOut),
203 B_TRANSLATE("Zoom out"));
204 fToolBar->AddSeparator();
205 fToolBar->AddAction(MSG_PAGE_PREV, this, tool_bar_icon(kIconPagePrevious),
206 B_TRANSLATE("Previous page"));
207 fToolBar->AddAction(MSG_PAGE_NEXT, this, tool_bar_icon(kIconPageNext),
208 B_TRANSLATE("Next page"));
209 fToolBar->AddGlue();
210 fToolBar->AddAction(MSG_FULL_SCREEN, this,
211 tool_bar_icon(kIconViewWindowed), B_TRANSLATE("Leave full screen"));
212 fToolBar->SetActionVisible(MSG_FULL_SCREEN, false);
213
214 fToolBar->ResizeTo(viewFrame.Width(), fToolBar->MinSize().height);
215
216 contentView->AddChild(fToolBar);
217
218 if (fShowToolBar)
219 viewFrame.top = fToolBar->Frame().bottom + 1;
220 else
221 fToolBar->Hide();
222
223 fToolBarVisible = fShowToolBar;
224
225 viewFrame.bottom = contentView->Bounds().bottom;
226
227 // create the scroll area
228 fScrollArea = new BScrollView("image_scroller", NULL, 0,
229 false, false, B_PLAIN_BORDER);
230 BGridLayout* gridLayout = new BGridLayout(0, 0);
231 fScrollArea->SetLayout(gridLayout);
232 gridLayout->SetInsets(0, 1, -1, -1);
233
234 fScrollArea->MoveTo(viewFrame.LeftTop());
235 fScrollArea->ResizeTo(viewFrame.Size());
236 fScrollArea->SetResizingMode(B_FOLLOW_ALL);
237 contentView->AddChild(fScrollArea);
238
239 // create the image view
240 fImageView = new ShowImageView("image_view",
241 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_PULSE_NEEDED
242 | B_FRAME_EVENTS);
243 fImageView->SetExplicitMinSize(BSize(0, 0));
244 gridLayout->AddView(fImageView, 0, 0, 2, 1);
245
246 // create the scroll bars (wrapped to avoid double borders)
247 fVScrollBar = new BScrollBar(NULL, NULL, 0, 0, B_VERTICAL); {
248 BGroupView* vScrollBarContainer = new BGroupView(B_VERTICAL, 0);
249 vScrollBarContainer->GroupLayout()->AddView(fVScrollBar);
250 vScrollBarContainer->GroupLayout()->SetInsets(0, -1, 0, -1);
251 gridLayout->AddView(vScrollBarContainer, 2, 0);
252 }
253
254 fHScrollBar = new BScrollBar(NULL, NULL, 0, 0, B_HORIZONTAL); {
255 BGroupView* hScrollBarContainer = new BGroupView(B_VERTICAL, 0);
256 hScrollBarContainer->GroupLayout()->AddView(fHScrollBar);
257 hScrollBarContainer->GroupLayout()->SetInsets(0, -1, -1, -1);
258 gridLayout->AddView(hScrollBarContainer, 1, 1);
259 }
260
261 fVScrollBar->SetTarget(fImageView);
262 fHScrollBar->SetTarget(fImageView);
263
264 fStatusView = new ShowImageStatusView;
265 gridLayout->AddView(fStatusView, 0, 1);
266
267 // Update minimum window size
268 float toolBarMinWidth = fToolBar->MinSize().width;
269 SetSizeLimits(std::max(menuBarMinWidth, toolBarMinWidth), 100000,
270 fBar->MinSize().height + gridLayout->MinSize().height, 100000);
271
272 // finish creating the window
273 if (_LoadImage() != B_OK) {
274 _LoadError(ref);
275 Quit();
276 return;
277 }
278
279 // add View menu here so it can access ShowImageView methods
280 BMenu* menu = new BMenu(B_TRANSLATE_CONTEXT("View", "Menus"));
281 _BuildViewMenu(menu, false);
282 fBar->AddItem(menu);
283
284 menu = new BMenu(B_TRANSLATE_CONTEXT("Attributes", "Menus"));
285 menu->AddItem(_BuildRatingMenu());
286 BMessage* message = new BMessage(MSG_SET_RATING);
287 message->AddInt32("rating", 0);
288 fResetRatingItem = new BMenuItem(B_TRANSLATE("Reset rating"), message);
289 menu->AddItem(fResetRatingItem);
290 fBar->AddItem(menu);
291
292 SetPulseRate(100000);
293 // every 1/10 second; ShowImageView needs it for marching ants
294
295 _MarkMenuItem(menu, MSG_SELECTION_MODE,
296 fImageView->IsSelectionModeEnabled());
297
298 // Tell application object to query the clipboard
299 // and tell this window if it contains interesting data or not
300 be_app_messenger.SendMessage(B_CLIPBOARD_CHANGED);
301
302 // The window will be shown on screen automatically
303 Run();
304 }
305
306
~ShowImageWindow()307 ShowImageWindow::~ShowImageWindow()
308 {
309 fProgressWindow->Lock();
310 fProgressWindow->Quit();
311
312 _StopSlideShow();
313 }
314
315
316 void
BuildContextMenu(BMenu * menu)317 ShowImageWindow::BuildContextMenu(BMenu* menu)
318 {
319 _BuildViewMenu(menu, true);
320 }
321
322
323 void
_BuildViewMenu(BMenu * menu,bool popupMenu)324 ShowImageWindow::_BuildViewMenu(BMenu* menu, bool popupMenu)
325 {
326 _AddItemMenu(menu, B_TRANSLATE("Slide show"), MSG_SLIDE_SHOW, 0, 0, this);
327 _MarkMenuItem(menu, MSG_SLIDE_SHOW, fSlideShowRunner != NULL);
328 BMenu* delayMenu = new BMenu(B_TRANSLATE("Slide delay"));
329 if (fSlideShowDelayMenu == NULL)
330 fSlideShowDelayMenu = delayMenu;
331
332 delayMenu->SetRadioMode(true);
333
334 int32 kDelays[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 20};
335 for (uint32 i = 0; i < sizeof(kDelays) / sizeof(kDelays[0]); i++) {
336 BString text;
337 BDurationFormat format;
338 text.Truncate(0);
339 format.Format(text, 0, kDelays[i] * 1000000LL);
340
341 _AddDelayItem(delayMenu, text.String(), kDelays[i] * 1000000LL);
342 }
343 menu->AddItem(delayMenu);
344
345 menu->AddSeparatorItem();
346
347 _AddItemMenu(menu, B_TRANSLATE("Original size"),
348 kMsgOriginalSize, '1', 0, this);
349 _AddItemMenu(menu, B_TRANSLATE("Fit to window"),
350 kMsgFitToWindow, '0', 0, this);
351 _AddItemMenu(menu, B_TRANSLATE("Zoom in"), MSG_ZOOM_IN, '+', 0, this);
352 _AddItemMenu(menu, B_TRANSLATE("Zoom out"), MSG_ZOOM_OUT, '-', 0, this);
353
354 menu->AddSeparatorItem();
355
356 if (!popupMenu || fFullScreen) {
357 _AddItemMenu(menu, B_TRANSLATE("High-quality zooming"),
358 MSG_SCALE_BILINEAR, 0, 0, this);
359 _AddItemMenu(menu, B_TRANSLATE("Stretch to window"),
360 kMsgStretchToWindow, 0, 0, this);
361
362 menu->AddSeparatorItem();
363 }
364
365 _AddItemMenu(menu, B_TRANSLATE("Full screen"),
366 MSG_FULL_SCREEN, B_ENTER, 0, this);
367 _MarkMenuItem(menu, MSG_FULL_SCREEN, fFullScreen);
368
369 _AddItemMenu(menu, B_TRANSLATE("Show caption in full screen mode"),
370 MSG_SHOW_CAPTION, 0, 0, this);
371 _MarkMenuItem(menu, MSG_SHOW_CAPTION, fShowCaption);
372
373 _MarkMenuItem(menu, MSG_SCALE_BILINEAR, fImageView->ScaleBilinear());
374 _MarkMenuItem(menu, kMsgStretchToWindow, fImageView->StretchesToBounds());
375
376 if (!popupMenu) {
377 _AddItemMenu(menu, B_TRANSLATE("Show tool bar"), kMsgToggleToolBar,
378 'B', 0, this);
379 _MarkMenuItem(menu, kMsgToggleToolBar,
380 !fToolBar->IsHidden(fToolBar));
381 }
382
383 if (popupMenu) {
384 menu->AddSeparatorItem();
385 _AddItemMenu(menu, B_TRANSLATE("Use as background" B_UTF8_ELLIPSIS),
386 MSG_DESKTOP_BACKGROUND, 0, 0, this);
387
388 BMenu* openWithMenu = new BMenu(B_TRANSLATE("Open with" B_UTF8_ELLIPSIS));
389 _UpdateOpenWithMenu(openWithMenu);
390 BMenuItem* item = new BMenuItem(openWithMenu, NULL);
391 menu->AddItem(item);
392 }
393 }
394
395
396 void
_UpdateOpenWithMenu(BMenu * menu)397 ShowImageWindow::_UpdateOpenWithMenu(BMenu* menu)
398 {
399 update_supporting_apps_menu(menu, fMimeType, MSG_OPEN_WITH, this);
400 }
401
402
403 BMenu*
_BuildRatingMenu()404 ShowImageWindow::_BuildRatingMenu()
405 {
406 fRatingMenu = new BMenu(B_TRANSLATE("Rating"));
407 for (int32 i = 1; i <= 10; i++) {
408 BMessage* message = new BMessage(MSG_SET_RATING);
409 BString label;
410 fNumberFormat.Format(label, i);
411 message->AddInt32("rating", i);
412 fRatingMenu->AddItem(new BMenuItem(label.String(), message));
413 }
414
415 return fRatingMenu;
416 }
417
418
419 void
_AddMenus(BMenuBar * bar)420 ShowImageWindow::_AddMenus(BMenuBar* bar)
421 {
422 BMenu* menu = new BMenu(B_TRANSLATE("File"));
423
424 // Add recent files to "Open File" entry as sub-menu.
425 BMenuItem* item = new BMenuItem(BRecentFilesList::NewFileListMenu(
426 B_TRANSLATE("Open" B_UTF8_ELLIPSIS), NULL, NULL, be_app, 10, true,
427 NULL, kApplicationSignature), new BMessage(MSG_FILE_OPEN));
428 item->SetShortcut('O', 0);
429 item->SetTarget(be_app);
430 menu->AddItem(item);
431
432 menu->AddSeparatorItem();
433
434 fOpenWithMenu = new BMenu(B_TRANSLATE("Open with" B_UTF8_ELLIPSIS));
435 item = new BMenuItem(fOpenWithMenu, NULL);
436 menu->AddItem(item);
437
438 BMenu* menuSaveAs = new BMenu(B_TRANSLATE("Save as" B_UTF8_ELLIPSIS),
439 B_ITEMS_IN_COLUMN);
440 BTranslationUtils::AddTranslationItems(menuSaveAs, B_TRANSLATOR_BITMAP);
441 // Fill Save As submenu with all types that can be converted
442 // to from the Be bitmap image format
443 menu->AddItem(menuSaveAs);
444 _AddItemMenu(menu, B_TRANSLATE("Move to Trash"), kMsgDeleteCurrentFile, 'T', 0, this);
445 _AddItemMenu(menu, B_TRANSLATE("Use as background" B_UTF8_ELLIPSIS),
446 MSG_DESKTOP_BACKGROUND, 0, 0, this);
447 _AddItemMenu(menu, B_TRANSLATE("Get info"), MSG_GET_INFO, 'I', 0, this);
448 menu->AddSeparatorItem();
449 _AddItemMenu(menu, B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS),
450 MSG_PAGE_SETUP, 0, 0, this);
451 _AddItemMenu(menu, B_TRANSLATE("Print" B_UTF8_ELLIPSIS),
452 MSG_PREPARE_PRINT, 'P', 0, this);
453 menu->AddSeparatorItem();
454 _AddItemMenu(menu, B_TRANSLATE("Close"), B_QUIT_REQUESTED, 'W', 0, this);
455 _AddItemMenu(menu, B_TRANSLATE("Quit"), B_QUIT_REQUESTED, 'Q', 0, be_app);
456 bar->AddItem(menu);
457
458 menu = new BMenu(B_TRANSLATE("Edit"));
459 _AddItemMenu(menu, B_TRANSLATE("Copy"), B_COPY, 'C', 0, this, false);
460 menu->AddSeparatorItem();
461 _AddItemMenu(menu, B_TRANSLATE("Selection mode"), MSG_SELECTION_MODE, 0, 0,
462 this);
463 _AddItemMenu(menu, B_TRANSLATE("Clear selection"),
464 MSG_CLEAR_SELECT, 0, 0, this, false);
465 _AddItemMenu(menu, B_TRANSLATE("Select all"),
466 MSG_SELECT_ALL, 'A', 0, this);
467 bar->AddItem(menu);
468
469 menu = fBrowseMenu = new BMenu(B_TRANSLATE("Browse"));
470 _AddItemMenu(menu, B_TRANSLATE("First page"),
471 MSG_PAGE_FIRST, B_LEFT_ARROW, B_SHIFT_KEY, this);
472 _AddItemMenu(menu, B_TRANSLATE("Last page"),
473 MSG_PAGE_LAST, B_RIGHT_ARROW, B_SHIFT_KEY, this);
474 _AddItemMenu(menu, B_TRANSLATE("Previous page"),
475 MSG_PAGE_PREV, B_LEFT_ARROW, 0, this);
476 _AddItemMenu(menu, B_TRANSLATE("Next page"),
477 MSG_PAGE_NEXT, B_RIGHT_ARROW, 0, this);
478 fGoToPageMenu = new BMenu(B_TRANSLATE("Go to page"));
479 fGoToPageMenu->SetRadioMode(true);
480 menu->AddItem(fGoToPageMenu);
481 menu->AddSeparatorItem();
482 _AddItemMenu(menu, B_TRANSLATE("Previous file"),
483 MSG_FILE_PREV, B_UP_ARROW, 0, this);
484 _AddItemMenu(menu, B_TRANSLATE("Next file"),
485 MSG_FILE_NEXT, B_DOWN_ARROW, 0, this);
486 bar->AddItem(menu);
487
488 menu = new BMenu(B_TRANSLATE("Image"));
489 _AddItemMenu(menu, B_TRANSLATE("Rotate clockwise"),
490 MSG_ROTATE_90, 'R', 0, this);
491 _AddItemMenu(menu, B_TRANSLATE("Rotate counterclockwise"),
492 MSG_ROTATE_270, 'R', B_SHIFT_KEY, this);
493 menu->AddSeparatorItem();
494 _AddItemMenu(menu, B_TRANSLATE("Flip left to right"),
495 MSG_FLIP_LEFT_TO_RIGHT, 0, 0, this);
496 _AddItemMenu(menu, B_TRANSLATE("Flip top to bottom"),
497 MSG_FLIP_TOP_TO_BOTTOM, 0, 0, this);
498
499 bar->AddItem(menu);
500 }
501
502
503 BMenuItem*
_AddItemMenu(BMenu * menu,const char * label,uint32 what,char shortcut,uint32 modifier,const BHandler * target,bool enabled)504 ShowImageWindow::_AddItemMenu(BMenu* menu, const char* label, uint32 what,
505 char shortcut, uint32 modifier, const BHandler* target, bool enabled)
506 {
507 BMenuItem* item = new BMenuItem(label, new BMessage(what), shortcut,
508 modifier);
509 menu->AddItem(item);
510
511 item->SetTarget(target);
512 item->SetEnabled(enabled);
513
514 return item;
515 }
516
517
518 BMenuItem*
_AddDelayItem(BMenu * menu,const char * label,bigtime_t delay)519 ShowImageWindow::_AddDelayItem(BMenu* menu, const char* label, bigtime_t delay)
520 {
521 BMessage* message = new BMessage(MSG_SLIDE_SHOW_DELAY);
522 message->AddInt64("delay", delay);
523
524 BMenuItem* item = new BMenuItem(label, message, 0);
525 item->SetTarget(this);
526
527 if (delay == fSlideShowDelay)
528 item->SetMarked(true);
529
530 menu->AddItem(item);
531 return item;
532 }
533
534
535 void
_ResizeWindowToImage()536 ShowImageWindow::_ResizeWindowToImage()
537 {
538 BBitmap* bitmap = fImageView->Bitmap();
539 BScreen screen;
540 if (bitmap == NULL || !screen.IsValid())
541 return;
542
543 // TODO: use View::GetPreferredSize() instead?
544 BRect r(bitmap->Bounds());
545 float width = r.Width() + be_control_look->GetScrollBarWidth(B_VERTICAL);
546 float height = r.Height() + 1 + fBar->Frame().Height()
547 + be_control_look->GetScrollBarWidth(B_HORIZONTAL);
548
549 BRect frame = screen.Frame();
550 const float windowBorder = 5;
551 // dimensions so that window does not reach outside of screen
552 float maxWidth = frame.Width() + 1 - windowBorder - Frame().left;
553 float maxHeight = frame.Height() + 1 - windowBorder - Frame().top;
554
555 // We have to check size limits manually, otherwise
556 // menu bar will be too short for small images.
557
558 float minW, maxW, minH, maxH;
559 GetSizeLimits(&minW, &maxW, &minH, &maxH);
560 if (maxWidth > maxW)
561 maxWidth = maxW;
562 if (maxHeight > maxH)
563 maxHeight = maxH;
564 if (width < minW)
565 width = minW;
566 if (height < minH)
567 height = minH;
568
569 if (width > maxWidth)
570 width = maxWidth;
571 if (height > maxHeight)
572 height = maxHeight;
573
574 ResizeTo(width, height);
575 }
576
577
578 bool
_ToggleMenuItem(uint32 what)579 ShowImageWindow::_ToggleMenuItem(uint32 what)
580 {
581 bool marked = false;
582 BMenuItem* item = fBar->FindItem(what);
583 if (item != NULL) {
584 marked = !item->IsMarked();
585 item->SetMarked(marked);
586 }
587 fToolBar->SetActionPressed(what, marked);
588 return marked;
589 }
590
591
592 void
_EnableMenuItem(BMenu * menu,uint32 what,bool enable)593 ShowImageWindow::_EnableMenuItem(BMenu* menu, uint32 what, bool enable)
594 {
595 BMenuItem* item = menu->FindItem(what);
596 if (item && item->IsEnabled() != enable)
597 item->SetEnabled(enable);
598 fToolBar->SetActionEnabled(what, enable);
599 }
600
601
602 void
_MarkMenuItem(BMenu * menu,uint32 what,bool marked)603 ShowImageWindow::_MarkMenuItem(BMenu* menu, uint32 what, bool marked)
604 {
605 BMenuItem* item = menu->FindItem(what);
606 if (item && item->IsMarked() != marked)
607 item->SetMarked(marked);
608 fToolBar->SetActionPressed(what, marked);
609 }
610
611
612 void
_MarkSlideShowDelay(bigtime_t delay)613 ShowImageWindow::_MarkSlideShowDelay(bigtime_t delay)
614 {
615 const int32 count = fSlideShowDelayMenu->CountItems();
616 for (int32 i = 0; i < count; i ++) {
617 BMenuItem* item = fSlideShowDelayMenu->ItemAt(i);
618 if (item != NULL) {
619 bigtime_t itemDelay;
620 if (item->Message()->FindInt64("delay", &itemDelay) == B_OK
621 && itemDelay == delay) {
622 item->SetMarked(true);
623 return;
624 }
625 }
626 }
627 }
628
629
630 void
Zoom(BPoint origin,float width,float height)631 ShowImageWindow::Zoom(BPoint origin, float width, float height)
632 {
633 _ToggleFullScreen();
634 }
635
636
637 void
MessageReceived(BMessage * message)638 ShowImageWindow::MessageReceived(BMessage* message)
639 {
640 if (message->WasDropped()) {
641 uint32 type;
642 int32 count;
643 status_t status = message->GetInfo("refs", &type, &count);
644 if (status == B_OK && type == B_REF_TYPE) {
645 message->what = B_REFS_RECEIVED;
646 be_app->PostMessage(message);
647 }
648 }
649
650 switch (message->what) {
651 case kMsgImageCacheImageLoaded:
652 {
653 fProgressWindow->Stop();
654
655 BitmapOwner* bitmapOwner = NULL;
656 message->FindPointer("bitmapOwner", (void**)&bitmapOwner);
657
658 bool first = fImageView->Bitmap() == NULL;
659 entry_ref ref;
660 message->FindRef("ref", &ref);
661 if (!first && ref != fNavigator.CurrentRef()) {
662 // ignore older images
663 if (bitmapOwner != NULL)
664 bitmapOwner->ReleaseReference();
665 break;
666 }
667
668 int32 page = message->FindInt32("page");
669 int32 pageCount = message->FindInt32("pageCount");
670 if (!first && page != fNavigator.CurrentPage()) {
671 // ignore older pages
672 if (bitmapOwner != NULL)
673 bitmapOwner->ReleaseReference();
674 break;
675 }
676
677 status_t status = fImageView->SetImage(message);
678 if (status != B_OK) {
679 if (bitmapOwner != NULL)
680 bitmapOwner->ReleaseReference();
681
682 _LoadError(ref);
683
684 // quit if file could not be opened
685 if (first)
686 Quit();
687 break;
688 }
689
690 fImageType = message->FindString("type");
691 fNavigator.SetTo(ref, page, pageCount);
692
693 fImageView->FitToBounds();
694 if (first) {
695 fImageView->MakeFocus(true);
696 // to receive key messages
697 Show();
698 }
699
700 fMimeType = new BMimeType(message->FindString("mime"));
701 _UpdateOpenWithMenu(fOpenWithMenu);
702 _UpdateRatingMenu();
703 // Set width and height attributes of the currently showed file.
704 // This should only be a temporary solution.
705 _SaveWidthAndHeight();
706 break;
707 }
708
709 case kMsgImageCacheProgressUpdate:
710 {
711 entry_ref ref;
712 if (message->FindRef("ref", &ref) == B_OK
713 && ref == fNavigator.CurrentRef()) {
714 message->what = kMsgProgressUpdate;
715 fProgressWindow->PostMessage(message);
716 }
717 break;
718 }
719
720 case MSG_MODIFIED:
721 // If image has been modified due to a Cut or Paste
722 fModified = true;
723 break;
724
725 case MSG_OUTPUT_TYPE:
726 // User clicked Save As then choose an output format
727 if (!fSavePanel)
728 // If user doesn't already have a save panel open
729 _SaveAs(message);
730 break;
731
732 case MSG_SAVE_PANEL:
733 // User specified where to save the output image
734 _SaveToFile(message);
735 break;
736
737 case B_CANCEL:
738 delete fSavePanel;
739 fSavePanel = NULL;
740 break;
741
742 case MSG_UPDATE_STATUS:
743 {
744 int32 pages = fNavigator.PageCount();
745 int32 currentPage = fNavigator.CurrentPage();
746
747 _EnableMenuItem(fBar, MSG_PAGE_FIRST,
748 fNavigator.HasPreviousPage());
749 _EnableMenuItem(fBar, MSG_PAGE_LAST, fNavigator.HasNextPage());
750 _EnableMenuItem(fBar, MSG_PAGE_NEXT, fNavigator.HasNextPage());
751 _EnableMenuItem(fBar, MSG_PAGE_PREV, fNavigator.HasPreviousPage());
752 fGoToPageMenu->SetEnabled(pages > 1);
753
754 _EnableMenuItem(fBar, MSG_FILE_NEXT, fNavigator.HasNextFile());
755 _EnableMenuItem(fBar, MSG_FILE_PREV, fNavigator.HasPreviousFile());
756
757 if (fGoToPageMenu->CountItems() != pages) {
758 // Only rebuild the submenu if the number of
759 // pages is different
760
761 while (fGoToPageMenu->CountItems() > 0) {
762 // Remove all page numbers
763 delete fGoToPageMenu->RemoveItem((int32)0);
764 }
765
766 for (int32 i = 1; i <= pages; i++) {
767 // Fill Go To page submenu with an entry for each page
768 BMessage* goTo = new BMessage(MSG_GOTO_PAGE);
769 goTo->AddInt32("page", i);
770
771 char shortcut = 0;
772 if (i < 10)
773 shortcut = '0' + i;
774
775 BString strCaption;
776 strCaption << i;
777
778 BMenuItem* item = new BMenuItem(strCaption.String(), goTo,
779 shortcut, B_SHIFT_KEY);
780 if (currentPage == i)
781 item->SetMarked(true);
782 fGoToPageMenu->AddItem(item);
783 }
784 } else {
785 // Make sure the correct page is marked
786 BMenuItem* currentItem = fGoToPageMenu->ItemAt(currentPage - 1);
787 if (currentItem != NULL && !currentItem->IsMarked())
788 currentItem->SetMarked(true);
789 }
790
791 _UpdateStatusText(message);
792
793 BPath path(fImageView->Image());
794 SetTitle(path.Path());
795 break;
796 }
797
798 case MSG_UPDATE_STATUS_TEXT:
799 {
800 _UpdateStatusText(message);
801 break;
802 }
803
804 case MSG_OPEN_WITH:
805 {
806 BString appSig = "";
807 message->FindString("signature", &appSig);
808 entry_ref ref = fNavigator.CurrentRef();
809 BMessage openMsg(B_REFS_RECEIVED);
810 openMsg.AddRef("refs", &ref);
811 be_roster->Launch(appSig.String(), &openMsg);
812 break;
813 }
814
815 case MSG_SELECTION:
816 {
817 // The view sends this message when a selection is
818 // made or the selection is cleared so that the window
819 // can update the state of the appropriate menu items
820 bool enable;
821 if (message->FindBool("has_selection", &enable) == B_OK) {
822 _EnableMenuItem(fBar, B_COPY, enable);
823 _EnableMenuItem(fBar, MSG_CLEAR_SELECT, enable);
824 }
825 break;
826 }
827
828 case B_COPY:
829 fImageView->CopySelectionToClipboard();
830 break;
831
832 case MSG_SELECTION_MODE:
833 {
834 bool selectionMode = _ToggleMenuItem(MSG_SELECTION_MODE);
835 fImageView->SetSelectionMode(selectionMode);
836 if (!selectionMode)
837 fImageView->ClearSelection();
838 break;
839 }
840
841 case MSG_CLEAR_SELECT:
842 fImageView->ClearSelection();
843 break;
844
845 case MSG_SELECT_ALL:
846 fImageView->SelectAll();
847 break;
848
849 case MSG_PAGE_FIRST:
850 if (_ClosePrompt() && fNavigator.FirstPage())
851 _LoadImage();
852 break;
853
854 case MSG_PAGE_LAST:
855 if (_ClosePrompt() && fNavigator.LastPage())
856 _LoadImage();
857 break;
858
859 case MSG_PAGE_NEXT:
860 if (_ClosePrompt() && fNavigator.NextPage())
861 _LoadImage();
862 break;
863
864 case MSG_PAGE_PREV:
865 if (_ClosePrompt() && fNavigator.PreviousPage())
866 _LoadImage();
867 break;
868
869 case MSG_GOTO_PAGE:
870 {
871 if (!_ClosePrompt())
872 break;
873
874 int32 newPage;
875 if (message->FindInt32("page", &newPage) != B_OK)
876 break;
877
878 int32 currentPage = fNavigator.CurrentPage();
879 int32 pages = fNavigator.PageCount();
880
881 // TODO: use radio mode instead!
882 if (newPage > 0 && newPage <= pages) {
883 BMenuItem* currentItem = fGoToPageMenu->ItemAt(currentPage - 1);
884 BMenuItem* newItem = fGoToPageMenu->ItemAt(newPage - 1);
885 if (currentItem != NULL && newItem != NULL) {
886 currentItem->SetMarked(false);
887 newItem->SetMarked(true);
888 if (fNavigator.GoToPage(newPage))
889 _LoadImage();
890 }
891 }
892 break;
893 }
894
895 case kMsgFitToWindow:
896 fImageView->FitToBounds();
897 break;
898
899 case kMsgStretchToWindow:
900 fImageView->SetStretchToBounds(
901 _ToggleMenuItem(kMsgStretchToWindow));
902 break;
903
904 case MSG_FILE_PREV:
905 if (_ClosePrompt() && fNavigator.PreviousFile())
906 _LoadImage(false);
907 break;
908
909 case MSG_FILE_NEXT:
910 case kMsgNextSlide:
911 if (_ClosePrompt()) {
912 if (!fNavigator.NextFile()) {
913 // Wrap back around
914 fNavigator.FirstFile();
915 }
916 _LoadImage();
917 }
918 break;
919
920 case kMsgDeleteCurrentFile:
921 {
922 if (fNavigator.MoveFileToTrash())
923 _LoadImage();
924 else
925 PostMessage(B_QUIT_REQUESTED);
926 break;
927 }
928
929 case MSG_ROTATE_90:
930 fImageView->Rotate(90);
931 break;
932
933 case MSG_ROTATE_270:
934 fImageView->Rotate(270);
935 break;
936
937 case MSG_FLIP_LEFT_TO_RIGHT:
938 fImageView->Flip(true);
939 break;
940
941 case MSG_FLIP_TOP_TO_BOTTOM:
942 fImageView->Flip(false);
943 break;
944
945 case MSG_GET_INFO:
946 _GetFileInfo(fNavigator.CurrentRef());
947 break;
948
949 case MSG_SLIDE_SHOW:
950 {
951 bool fullScreen = false;
952 message->FindBool("full screen", &fullScreen);
953
954 BMenuItem* item = fBar->FindItem(message->what);
955 if (item == NULL)
956 break;
957
958 if (item->IsMarked()) {
959 item->SetMarked(false);
960 _StopSlideShow();
961 fToolBar->SetActionPressed(MSG_SLIDE_SHOW, false);
962 } else if (_ClosePrompt()) {
963 item->SetMarked(true);
964 if (!fFullScreen && fullScreen)
965 _ToggleFullScreen();
966 _StartSlideShow();
967 fToolBar->SetActionPressed(MSG_SLIDE_SHOW, true);
968 }
969 break;
970 }
971
972 case kMsgStopSlideShow:
973 {
974 BMenuItem* item = fBar->FindItem(MSG_SLIDE_SHOW);
975 if (item != NULL)
976 item->SetMarked(false);
977
978 _StopSlideShow();
979 fToolBar->SetActionPressed(MSG_SLIDE_SHOW, false);
980 break;
981 }
982
983 case MSG_SLIDE_SHOW_DELAY:
984 {
985 bigtime_t delay;
986 if (message->FindInt64("delay", &delay) == B_OK) {
987 _SetSlideShowDelay(delay);
988 // in case message is sent from popup menu
989 _MarkSlideShowDelay(delay);
990 }
991 break;
992 }
993
994 case MSG_FULL_SCREEN:
995 _ToggleFullScreen();
996 break;
997
998 case MSG_EXIT_FULL_SCREEN:
999 if (fFullScreen)
1000 _ToggleFullScreen();
1001 break;
1002
1003 case MSG_SHOW_CAPTION:
1004 {
1005 fShowCaption = _ToggleMenuItem(message->what);
1006 ShowImageSettings* settings = my_app->Settings();
1007
1008 if (settings->Lock()) {
1009 settings->SetBool("ShowCaption", fShowCaption);
1010 settings->Unlock();
1011 }
1012 if (fFullScreen)
1013 fImageView->SetShowCaption(fShowCaption);
1014 } break;
1015
1016 case MSG_PAGE_SETUP:
1017 _PageSetup();
1018 break;
1019
1020 case MSG_PREPARE_PRINT:
1021 _PrepareForPrint();
1022 break;
1023
1024 case MSG_PRINT:
1025 _Print(message);
1026 break;
1027
1028 case MSG_ZOOM_IN:
1029 fImageView->ZoomIn();
1030 break;
1031
1032 case MSG_ZOOM_OUT:
1033 fImageView->ZoomOut();
1034 break;
1035
1036 case MSG_UPDATE_STATUS_ZOOM:
1037 fStatusView->SetZoom(fImageView->Zoom());
1038 break;
1039
1040 case kMsgOriginalSize:
1041 if (message->FindInt32("behavior") == BButton::B_TOGGLE_BEHAVIOR) {
1042 bool force = (message->FindInt32("be:value") == B_CONTROL_ON);
1043 fImageView->ForceOriginalSize(force);
1044 if (!force)
1045 break;
1046 }
1047 fImageView->SetZoom(1.0);
1048 break;
1049
1050 case MSG_SCALE_BILINEAR:
1051 fImageView->SetScaleBilinear(_ToggleMenuItem(message->what));
1052 break;
1053
1054 case MSG_DESKTOP_BACKGROUND:
1055 {
1056 BMessage backgroundsMessage(B_REFS_RECEIVED);
1057 backgroundsMessage.AddRef("refs", fImageView->Image());
1058 // This is used in the Backgrounds code for scaled placement
1059 backgroundsMessage.AddInt32("placement", 'scpl');
1060 be_roster->Launch("application/x-vnd.haiku-backgrounds",
1061 &backgroundsMessage);
1062 break;
1063 }
1064
1065 case MSG_SET_RATING:
1066 {
1067 int32 rating;
1068 if (message->FindInt32("rating", &rating) != B_OK)
1069 break;
1070 BFile file(&fNavigator.CurrentRef(), B_WRITE_ONLY);
1071 if (file.InitCheck() != B_OK)
1072 break;
1073 file.WriteAttr("Media:Rating", B_INT32_TYPE, 0, &rating,
1074 sizeof(rating));
1075 _UpdateRatingMenu();
1076 break;
1077 }
1078
1079 case kMsgToggleToolBar:
1080 {
1081 fShowToolBar = _ToggleMenuItem(message->what);
1082 _SetToolBarVisible(fShowToolBar, true);
1083
1084 ShowImageSettings* settings = my_app->Settings();
1085
1086 if (settings->Lock()) {
1087 settings->SetBool("ShowToolBar", fShowToolBar);
1088 settings->Unlock();
1089 }
1090 break;
1091 }
1092 case kShowToolBarIfEnabled:
1093 {
1094 bool show;
1095 if (message->FindBool("show", &show) != B_OK)
1096 break;
1097 _SetToolBarVisible(fShowToolBar && show, true);
1098 break;
1099 }
1100 case kMsgSlideToolBar:
1101 {
1102 float offset;
1103 if (message->FindFloat("offset", &offset) == B_OK) {
1104 fToolBar->MoveBy(0, offset);
1105 fScrollArea->ResizeBy(0, -offset);
1106 fScrollArea->MoveBy(0, offset);
1107 UpdateIfNeeded();
1108 snooze(15000);
1109 }
1110 break;
1111 }
1112 case kMsgFinishSlidingToolBar:
1113 {
1114 float offset;
1115 bool show;
1116 if (message->FindFloat("offset", &offset) == B_OK
1117 && message->FindBool("show", &show) == B_OK) {
1118 // Compensate rounding errors with the final placement
1119 fToolBar->MoveTo(fToolBar->Frame().left, offset);
1120 if (!show)
1121 fToolBar->Hide();
1122 BRect frame = fToolBar->Parent()->Bounds();
1123 frame.top = fToolBar->Frame().bottom + 1;
1124 fScrollArea->MoveTo(fScrollArea->Frame().left, frame.top);
1125 fScrollArea->ResizeTo(fScrollArea->Bounds().Width(),
1126 frame.Height() + 1);
1127 }
1128 break;
1129 }
1130
1131 default:
1132 BWindow::MessageReceived(message);
1133 break;
1134 }
1135 }
1136
1137
1138 void
_GetFileInfo(const entry_ref & ref)1139 ShowImageWindow::_GetFileInfo(const entry_ref& ref)
1140 {
1141 BMessage message('Tinf');
1142 BMessenger tracker("application/x-vnd.Be-TRAK");
1143 message.AddRef("refs", &ref);
1144 tracker.SendMessage(&message);
1145 }
1146
1147
1148 void
_UpdateStatusText(const BMessage * message)1149 ShowImageWindow::_UpdateStatusText(const BMessage* message)
1150 {
1151 BString frameText, height, width;
1152 if (fImageView->Bitmap() != NULL) {
1153 BRect bounds = fImageView->Bitmap()->Bounds();
1154 fNumberFormat.Format(width, bounds.IntegerWidth() + 1);
1155 fNumberFormat.Format(height, bounds.IntegerHeight() + 1);
1156 frameText.SetToFormat("%s × %s", width.String(), height.String());
1157 }
1158
1159 BString currentPage, pageCount, pages;
1160 if (fNavigator.PageCount() > 1) {
1161 fNumberFormat.Format(currentPage, fNavigator.CurrentPage());
1162 fNumberFormat.Format(pageCount, fNavigator.PageCount());
1163 pages.SetToFormat("%s / %s", currentPage.String(), pageCount.String());
1164 }
1165
1166 fStatusView->Update(fNavigator.CurrentRef(), frameText, pages, fImageType,
1167 fImageView->Zoom());
1168 }
1169
1170
1171 void
_LoadError(const entry_ref & ref)1172 ShowImageWindow::_LoadError(const entry_ref& ref)
1173 {
1174 // TODO: give a better error message!
1175 BAlert* alert = new BAlert(B_TRANSLATE_SYSTEM_NAME("ShowImage"),
1176 B_TRANSLATE_CONTEXT("Could not load image! Either the "
1177 "file or an image translator for it does not exist.",
1178 "LoadAlerts"),
1179 B_TRANSLATE_CONTEXT("OK", "Alerts"), NULL, NULL,
1180 B_WIDTH_AS_USUAL, B_STOP_ALERT);
1181 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1182 alert->Go();
1183 }
1184
1185
1186 void
_SaveAs(BMessage * message)1187 ShowImageWindow::_SaveAs(BMessage* message)
1188 {
1189 // Read the translator and output type the user chose
1190 int32 outTranslator;
1191 uint32 outType;
1192 if (message->FindInt32(kTranslatorField, &outTranslator) != B_OK
1193 || message->FindInt32(kTypeField,
1194 reinterpret_cast<int32 *>(&outType)) != B_OK)
1195 return;
1196
1197 // Add the chosen translator and output type to the
1198 // message that the save panel will send back
1199 BMessage panelMsg(MSG_SAVE_PANEL);
1200 panelMsg.AddInt32(kTranslatorField, outTranslator);
1201 panelMsg.AddInt32(kTypeField, outType);
1202
1203 // Create save panel and show it
1204 BMessenger target(this);
1205 fSavePanel = new (std::nothrow) BFilePanel(B_SAVE_PANEL,
1206 &target, NULL, 0, false, &panelMsg);
1207
1208 if (!fSavePanel)
1209 return;
1210
1211 // Retrieve save directory from settings;
1212 ShowImageSettings* settings = my_app->Settings();
1213 if (settings->Lock()) {
1214 fSavePanel->SetPanelDirectory(
1215 settings->GetString("SaveDirectory", NULL));
1216 settings->Unlock();
1217 }
1218
1219 // Prefill current image's file name in save dialog
1220 BEntry entry = fImageView->Image();
1221 BPath path(&entry);
1222 const char* filename = path.Leaf();
1223 fSavePanel->SetSaveText(filename);
1224
1225 fSavePanel->Window()->SetWorkspaces(B_CURRENT_WORKSPACE);
1226 fSavePanel->Show();
1227 }
1228
1229
1230 void
_SaveToFile(BMessage * message)1231 ShowImageWindow::_SaveToFile(BMessage* message)
1232 {
1233 // Read in where the file should be saved
1234 entry_ref dirRef;
1235 if (message->FindRef("directory", &dirRef) != B_OK)
1236 return;
1237
1238 const char* filename;
1239 if (message->FindString("name", &filename) != B_OK)
1240 return;
1241
1242 // Read in the translator and type to be used
1243 // to save the output image
1244 int32 outTranslator;
1245 uint32 outType;
1246 if (message->FindInt32(kTranslatorField, &outTranslator) != B_OK
1247 || message->FindInt32(kTypeField,
1248 reinterpret_cast<int32 *>(&outType)) != B_OK)
1249 return;
1250
1251 // Find the translator_format information needed to
1252 // write a MIME attribute for the image file
1253 BTranslatorRoster* roster = BTranslatorRoster::Default();
1254 const translation_format* outFormat = NULL;
1255 int32 outCount = 0;
1256 if (roster->GetOutputFormats(outTranslator, &outFormat, &outCount) != B_OK
1257 || outCount < 1)
1258 return;
1259
1260 int32 i;
1261 for (i = 0; i < outCount; i++) {
1262 if (outFormat[i].group == B_TRANSLATOR_BITMAP && outFormat[i].type
1263 == outType)
1264 break;
1265 }
1266 if (i == outCount)
1267 return;
1268
1269 // Write out the image file
1270 BDirectory dir(&dirRef);
1271 fImageView->SaveToFile(&dir, filename, NULL, &outFormat[i]);
1272
1273 // Store Save directory in settings;
1274 ShowImageSettings* settings = my_app->Settings();
1275 if (settings->Lock()) {
1276 BPath path(&dirRef);
1277 settings->SetString("SaveDirectory", path.Path());
1278 settings->Unlock();
1279 }
1280 }
1281
1282
1283 #undef B_TRANSLATION_CONTEXT
1284 #define B_TRANSLATION_CONTEXT "ClosePrompt"
1285
1286
1287 bool
_ClosePrompt()1288 ShowImageWindow::_ClosePrompt()
1289 {
1290 if (!fModified)
1291 return true;
1292
1293 int32 count = fNavigator.PageCount();
1294 int32 page = fNavigator.CurrentPage();
1295 BString prompt;
1296
1297 if (count > 1) {
1298 bs_printf(&prompt,
1299 B_TRANSLATE("The document '%s' (page %d) has been changed. Do you "
1300 "want to close the document?"),
1301 fImageView->Image()->name, page);
1302 } else {
1303 bs_printf(&prompt,
1304 B_TRANSLATE("The document '%s' has been changed. Do you want to "
1305 "close the document?"),
1306 fImageView->Image()->name);
1307 }
1308
1309 BAlert* alert = new BAlert(B_TRANSLATE("Close document"), prompt.String(),
1310 B_TRANSLATE("Cancel"), B_TRANSLATE("Close"));
1311 alert->SetShortcut(0, B_ESCAPE);
1312
1313 if (alert->Go() == 0) {
1314 // Cancel
1315 return false;
1316 }
1317
1318 // Close
1319 fModified = false;
1320 return true;
1321 }
1322
1323
1324 status_t
_LoadImage(bool forward)1325 ShowImageWindow::_LoadImage(bool forward)
1326 {
1327 // If the user triggered a _LoadImage while in a slide show,
1328 // make sure the new image is shown for the set delay:
1329 _ResetSlideShowDelay();
1330
1331 BMessenger us(this);
1332 status_t status = my_app->DefaultCache().RetrieveImage(
1333 fNavigator.CurrentRef(), fNavigator.CurrentPage(), &us);
1334 if (status != B_OK)
1335 return status;
1336
1337 fProgressWindow->Start(this);
1338
1339 // Preload previous/next images - two in the navigation direction, one
1340 // in the opposite direction.
1341
1342 entry_ref nextRef = fNavigator.CurrentRef();
1343 if (_PreloadImage(forward, nextRef))
1344 _PreloadImage(forward, nextRef);
1345
1346 entry_ref previousRef = fNavigator.CurrentRef();
1347 _PreloadImage(!forward, previousRef);
1348
1349 return B_OK;
1350 }
1351
1352
1353 bool
_PreloadImage(bool forward,entry_ref & ref)1354 ShowImageWindow::_PreloadImage(bool forward, entry_ref& ref)
1355 {
1356 entry_ref currentRef = ref;
1357 if ((forward && !fNavigator.GetNextFile(currentRef, ref))
1358 || (!forward && !fNavigator.GetPreviousFile(currentRef, ref)))
1359 return false;
1360
1361 return my_app->DefaultCache().RetrieveImage(ref) == B_OK;
1362 }
1363
1364
1365 void
_ToggleFullScreen()1366 ShowImageWindow::_ToggleFullScreen()
1367 {
1368 BRect frame;
1369 fFullScreen = !fFullScreen;
1370 if (fFullScreen) {
1371 BScreen screen;
1372 fWindowFrame = Frame();
1373 frame = screen.Frame();
1374 frame.top -= fBar->Bounds().Height() + 1;
1375 frame.right += be_control_look->GetScrollBarWidth(B_VERTICAL);
1376 frame.bottom += be_control_look->GetScrollBarWidth(B_HORIZONTAL);
1377
1378 SetFlags(Flags() | B_NOT_RESIZABLE | B_NOT_MOVABLE);
1379
1380 Activate();
1381 // make the window frontmost
1382 } else {
1383 frame = fWindowFrame;
1384
1385 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE));
1386 }
1387
1388 fToolBar->SetActionVisible(MSG_FULL_SCREEN, fFullScreen);
1389 _SetToolBarVisible(!fFullScreen && fShowToolBar);
1390 _SetToolBarBorder(!fFullScreen);
1391
1392 MoveTo(frame.left, frame.top);
1393 ResizeTo(frame.Width(), frame.Height());
1394
1395 fImageView->SetHideIdlingCursor(fFullScreen);
1396 fImageView->SetShowCaption(fFullScreen && fShowCaption);
1397
1398 Layout(false);
1399 // We need to manually relayout here, as the views are layouted
1400 // asynchronously, and FitToBounds() would still have the wrong size
1401 fImageView->FitToBounds();
1402 }
1403
1404
1405 void
_ApplySettings()1406 ShowImageWindow::_ApplySettings()
1407 {
1408 ShowImageSettings* settings = my_app->Settings();
1409
1410 if (settings->Lock()) {
1411 fShowCaption = settings->GetBool("ShowCaption", fShowCaption);
1412 fPrintOptions.SetBounds(BRect(0, 0, 1023, 767));
1413
1414 fSlideShowDelay = settings->GetTime("SlideShowDelay", fSlideShowDelay);
1415
1416 fPrintOptions.SetOption((enum PrintOptions::Option)settings->GetInt32(
1417 "PO:Option", fPrintOptions.Option()));
1418 fPrintOptions.SetZoomFactor(
1419 settings->GetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor()));
1420 fPrintOptions.SetDPI(settings->GetFloat("PO:DPI", fPrintOptions.DPI()));
1421 fPrintOptions.SetWidth(
1422 settings->GetFloat("PO:Width", fPrintOptions.Width()));
1423 fPrintOptions.SetHeight(
1424 settings->GetFloat("PO:Height", fPrintOptions.Height()));
1425
1426 fShowToolBar = settings->GetBool("ShowToolBar", fShowToolBar);
1427
1428 settings->Unlock();
1429 }
1430 }
1431
1432
1433 void
_SavePrintOptions()1434 ShowImageWindow::_SavePrintOptions()
1435 {
1436 ShowImageSettings* settings = my_app->Settings();
1437
1438 if (settings->Lock()) {
1439 settings->SetInt32("PO:Option", fPrintOptions.Option());
1440 settings->SetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor());
1441 settings->SetFloat("PO:DPI", fPrintOptions.DPI());
1442 settings->SetFloat("PO:Width", fPrintOptions.Width());
1443 settings->SetFloat("PO:Height", fPrintOptions.Height());
1444 settings->Unlock();
1445 }
1446 }
1447
1448
1449 bool
_PageSetup()1450 ShowImageWindow::_PageSetup()
1451 {
1452 BPrintJob printJob(fImageView->Image()->name);
1453 if (fPrintSettings != NULL)
1454 printJob.SetSettings(new BMessage(*fPrintSettings));
1455
1456 status_t status = printJob.ConfigPage();
1457 if (status == B_OK) {
1458 delete fPrintSettings;
1459 fPrintSettings = printJob.Settings();
1460 }
1461
1462 return status == B_OK;
1463 }
1464
1465
1466 void
_PrepareForPrint()1467 ShowImageWindow::_PrepareForPrint()
1468 {
1469 if (fPrintSettings == NULL) {
1470 BPrintJob printJob(fImageView->Image()->name);
1471 if (printJob.ConfigJob() == B_OK)
1472 fPrintSettings = printJob.Settings();
1473 }
1474
1475 fPrintOptions.SetBounds(fImageView->Bitmap()->Bounds());
1476 fPrintOptions.SetWidth(fImageView->Bitmap()->Bounds().Width() + 1);
1477
1478 new PrintOptionsWindow(BPoint(Frame().left + 30, Frame().top + 50),
1479 &fPrintOptions, this);
1480 }
1481
1482
1483 void
_Print(BMessage * msg)1484 ShowImageWindow::_Print(BMessage* msg)
1485 {
1486 status_t st;
1487 if (msg->FindInt32("status", &st) != B_OK || st != B_OK)
1488 return;
1489
1490 _SavePrintOptions();
1491
1492 BPrintJob printJob(fImageView->Image()->name);
1493 if (fPrintSettings)
1494 printJob.SetSettings(new BMessage(*fPrintSettings));
1495
1496 if (printJob.ConfigJob() == B_OK) {
1497 delete fPrintSettings;
1498 fPrintSettings = printJob.Settings();
1499
1500 // first/lastPage is unused for now
1501 int32 firstPage = printJob.FirstPage();
1502 int32 lastPage = printJob.LastPage();
1503 BRect printableRect = printJob.PrintableRect();
1504
1505 if (firstPage < 1)
1506 firstPage = 1;
1507 if (lastPage < firstPage)
1508 lastPage = firstPage;
1509
1510 BBitmap* bitmap = fImageView->Bitmap();
1511 float imageWidth = bitmap->Bounds().Width() + 1.0;
1512 float imageHeight = bitmap->Bounds().Height() + 1.0;
1513
1514 float width;
1515 switch (fPrintOptions.Option()) {
1516 case PrintOptions::kFitToPage: {
1517 float w1 = printableRect.Width() + 1;
1518 float w2 = imageWidth * (printableRect.Height() + 1)
1519 / imageHeight;
1520 if (w2 < w1)
1521 width = w2;
1522 else
1523 width = w1;
1524 } break;
1525 case PrintOptions::kZoomFactor:
1526 width = imageWidth * fPrintOptions.ZoomFactor();
1527 break;
1528 case PrintOptions::kDPI:
1529 width = imageWidth * 72.0 / fPrintOptions.DPI();
1530 break;
1531 case PrintOptions::kWidth:
1532 case PrintOptions::kHeight:
1533 width = fPrintOptions.Width();
1534 break;
1535
1536 default:
1537 // keep compiler silent; should not reach here
1538 width = imageWidth;
1539 }
1540
1541 // TODO: eventually print large images on several pages
1542 printJob.BeginJob();
1543 fImageView->SetScale(width / imageWidth);
1544 // coordinates are relative to printable rectangle
1545 BRect bounds(bitmap->Bounds());
1546 printJob.DrawView(fImageView, bounds, BPoint(0, 0));
1547 fImageView->SetScale(1.0);
1548 printJob.SpoolPage();
1549 printJob.CommitJob();
1550 }
1551 }
1552
1553
1554 void
_SetSlideShowDelay(bigtime_t delay)1555 ShowImageWindow::_SetSlideShowDelay(bigtime_t delay)
1556 {
1557 if (fSlideShowDelay == delay)
1558 return;
1559
1560 fSlideShowDelay = delay;
1561
1562 ShowImageSettings* settings = my_app->Settings();
1563 if (settings->Lock()) {
1564 settings->SetTime("SlideShowDelay", fSlideShowDelay);
1565 settings->Unlock();
1566 }
1567
1568 if (fSlideShowRunner != NULL)
1569 _StartSlideShow();
1570 }
1571
1572
1573 void
_StartSlideShow()1574 ShowImageWindow::_StartSlideShow()
1575 {
1576 _StopSlideShow();
1577
1578 BMessage nextSlide(kMsgNextSlide);
1579 fSlideShowRunner = new BMessageRunner(this, &nextSlide, fSlideShowDelay);
1580 }
1581
1582
1583 void
_StopSlideShow()1584 ShowImageWindow::_StopSlideShow()
1585 {
1586 if (fSlideShowRunner != NULL) {
1587 delete fSlideShowRunner;
1588 fSlideShowRunner = NULL;
1589 }
1590 }
1591
1592
1593 void
_ResetSlideShowDelay()1594 ShowImageWindow::_ResetSlideShowDelay()
1595 {
1596 if (fSlideShowRunner != NULL)
1597 fSlideShowRunner->SetInterval(fSlideShowDelay);
1598 }
1599
1600
1601 void
_UpdateRatingMenu()1602 ShowImageWindow::_UpdateRatingMenu()
1603 {
1604 BFile file(&fNavigator.CurrentRef(), B_READ_ONLY);
1605 if (file.InitCheck() != B_OK)
1606 return;
1607 int32 rating;
1608 ssize_t size = sizeof(rating);
1609 if (file.ReadAttr("Media:Rating", B_INT32_TYPE, 0, &rating, size) != size)
1610 rating = 0;
1611 // TODO: Finding the correct item could be more robust, like by looking
1612 // at the message of each item.
1613 for (int32 i = 1; i <= 10; i++) {
1614 BMenuItem* item = fRatingMenu->ItemAt(i - 1);
1615 if (item == NULL)
1616 break;
1617 item->SetMarked(i == rating);
1618 }
1619 fResetRatingItem->SetEnabled(rating > 0);
1620 }
1621
1622
1623 void
_SaveWidthAndHeight()1624 ShowImageWindow::_SaveWidthAndHeight()
1625 {
1626 if (fNavigator.CurrentPage() != 1)
1627 return;
1628
1629 if (fImageView->Bitmap() == NULL)
1630 return;
1631
1632 BRect bounds = fImageView->Bitmap()->Bounds();
1633 int32 width = bounds.IntegerWidth() + 1;
1634 int32 height = bounds.IntegerHeight() + 1;
1635
1636 BNode node(&fNavigator.CurrentRef());
1637 if (node.InitCheck() != B_OK)
1638 return;
1639
1640 const char* kWidthAttrName = "Media:Width";
1641 const char* kHeightAttrName = "Media:Height";
1642
1643 int32 widthAttr;
1644 ssize_t attrSize = node.ReadAttr(kWidthAttrName, B_INT32_TYPE, 0,
1645 &widthAttr, sizeof(widthAttr));
1646 if (attrSize <= 0 || widthAttr != width)
1647 node.WriteAttr(kWidthAttrName, B_INT32_TYPE, 0, &width, sizeof(width));
1648
1649 int32 heightAttr;
1650 attrSize = node.ReadAttr(kHeightAttrName, B_INT32_TYPE, 0,
1651 &heightAttr, sizeof(heightAttr));
1652 if (attrSize <= 0 || heightAttr != height)
1653 node.WriteAttr(kHeightAttrName, B_INT32_TYPE, 0, &height, sizeof(height));
1654 }
1655
1656
1657 void
_SetToolBarVisible(bool visible,bool animate)1658 ShowImageWindow::_SetToolBarVisible(bool visible, bool animate)
1659 {
1660 if (visible == fToolBarVisible)
1661 return;
1662
1663 fToolBarVisible = visible;
1664 float diff = fToolBar->Bounds().Height() + 2;
1665 if (!visible)
1666 diff = -diff;
1667 else
1668 fToolBar->Show();
1669
1670 if (animate) {
1671 // Slide the controls into view. We do this with messages in order
1672 // not to block the window thread.
1673 const float kAnimationOffsets[] = { 0.05, 0.2, 0.5, 0.2, 0.05 };
1674 const int32 steps = sizeof(kAnimationOffsets) / sizeof(float);
1675 for (int32 i = 0; i < steps; i++) {
1676 BMessage message(kMsgSlideToolBar);
1677 message.AddFloat("offset", floorf(diff * kAnimationOffsets[i]));
1678 PostMessage(&message, this);
1679 }
1680 BMessage finalMessage(kMsgFinishSlidingToolBar);
1681 finalMessage.AddFloat("offset", visible ? 0 : diff);
1682 finalMessage.AddBool("show", visible);
1683 PostMessage(&finalMessage, this);
1684 } else {
1685 fScrollArea->ResizeBy(0, -diff);
1686 fScrollArea->MoveBy(0, diff);
1687 fToolBar->MoveBy(0, diff);
1688 if (!visible)
1689 fToolBar->Hide();
1690 }
1691 }
1692
1693
1694 void
_SetToolBarBorder(bool visible)1695 ShowImageWindow::_SetToolBarBorder(bool visible)
1696 {
1697 float inset = visible
1698 ? ceilf(be_control_look->DefaultItemSpacing() / 2) : 0;
1699
1700 fToolBar->GroupLayout()->SetInsets(inset, 0, inset, 0);
1701 }
1702
1703
1704 bool
QuitRequested()1705 ShowImageWindow::QuitRequested()
1706 {
1707 if (fSavePanel) {
1708 // Don't allow this window to be closed if a save panel is open
1709 return false;
1710 }
1711
1712 if (!_ClosePrompt())
1713 return false;
1714
1715 ShowImageSettings* settings = my_app->Settings();
1716 if (settings->Lock()) {
1717 if (fFullScreen)
1718 settings->SetRect("WindowFrame", fWindowFrame);
1719 else
1720 settings->SetRect("WindowFrame", Frame());
1721 settings->Unlock();
1722 }
1723
1724 be_app->PostMessage(MSG_WINDOW_HAS_QUIT);
1725
1726 return true;
1727 }
1728