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