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