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