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 viewFrame.bottom = contentView->Bounds().bottom; 214 viewFrame.bottom -= B_H_SCROLL_BAR_HEIGHT; 215 216 // create the image view 217 fImageView = new ShowImageView(viewFrame, "image_view", B_FOLLOW_ALL, 218 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_PULSE_NEEDED 219 | B_FRAME_EVENTS); 220 // wrap a scroll view around the view 221 fScrollView = new BScrollView("image_scroller", fImageView, 222 B_FOLLOW_ALL, 0, false, false, B_PLAIN_BORDER); 223 contentView->AddChild(fScrollView); 224 225 const int32 kstatusWidth = 190; 226 BRect rect; 227 rect = contentView->Bounds(); 228 rect.top = viewFrame.bottom + 1; 229 rect.left = viewFrame.left + kstatusWidth; 230 rect.right = viewFrame.right + 1; 231 rect.bottom += 1; 232 BScrollBar* horizontalScrollBar = new BScrollBar(rect, "hscroll", 233 fImageView, 0, 150, B_HORIZONTAL); 234 contentView->AddChild(horizontalScrollBar); 235 236 rect.left = 0; 237 rect.right = kstatusWidth - 1; 238 rect.bottom -= 1; 239 fStatusView = new ShowImageStatusView(rect, "status_view", B_FOLLOW_BOTTOM, 240 B_WILL_DRAW); 241 contentView->AddChild(fStatusView); 242 243 rect = contentView->Bounds(); 244 rect.top = viewFrame.top - 1; 245 rect.left = viewFrame.right + 1; 246 rect.bottom = viewFrame.bottom + 1; 247 rect.right += 1; 248 fVerticalScrollBar = new BScrollBar(rect, "vscroll", fImageView, 249 0, 150, B_VERTICAL); 250 contentView->AddChild(fVerticalScrollBar); 251 252 // Update minimum window size 253 float toolBarMinWidth = fToolBarView->MinSize().width; 254 SetSizeLimits(std::max(menuBarMinWidth, toolBarMinWidth), 100000, 100, 255 100000); 256 257 // finish creating the window 258 if (_LoadImage() != B_OK) { 259 _LoadError(ref); 260 Quit(); 261 return; 262 } 263 264 // add View menu here so it can access ShowImageView methods 265 BMenu* menu = new BMenu(B_TRANSLATE_CONTEXT("View", "Menus")); 266 _BuildViewMenu(menu, false); 267 fBar->AddItem(menu); 268 269 fBar->AddItem(_BuildRatingMenu()); 270 271 SetPulseRate(100000); 272 // every 1/10 second; ShowImageView needs it for marching ants 273 274 _MarkMenuItem(menu, MSG_SELECTION_MODE, 275 fImageView->IsSelectionModeEnabled()); 276 277 // Tell application object to query the clipboard 278 // and tell this window if it contains interesting data or not 279 be_app_messenger.SendMessage(B_CLIPBOARD_CHANGED); 280 281 // The window will be shown on screen automatically 282 Run(); 283 } 284 285 286 ShowImageWindow::~ShowImageWindow() 287 { 288 fProgressWindow->Lock(); 289 fProgressWindow->Quit(); 290 291 _StopSlideShow(); 292 } 293 294 295 void 296 ShowImageWindow::BuildContextMenu(BMenu* menu) 297 { 298 _BuildViewMenu(menu, true); 299 } 300 301 302 void 303 ShowImageWindow::_BuildViewMenu(BMenu* menu, bool popupMenu) 304 { 305 _AddItemMenu(menu, B_TRANSLATE("Slide show"), MSG_SLIDE_SHOW, 0, 0, this); 306 _MarkMenuItem(menu, MSG_SLIDE_SHOW, fSlideShowRunner != NULL); 307 BMenu* delayMenu = new BMenu(B_TRANSLATE("Slide delay")); 308 if (fSlideShowDelayMenu == NULL) 309 fSlideShowDelayMenu = delayMenu; 310 311 delayMenu->SetRadioMode(true); 312 313 int32 kDelays[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 20}; 314 for (uint32 i = 0; i < sizeof(kDelays) / sizeof(kDelays[0]); i++) { 315 BString text(B_TRANSLATE_COMMENT("%SECONDS seconds", 316 "Don't translate %SECONDS")); 317 char seconds[32]; 318 snprintf(seconds, sizeof(seconds), "%" B_PRIi32, kDelays[i]); 319 text.ReplaceFirst("%SECONDS", seconds); 320 321 _AddDelayItem(delayMenu, text.String(), kDelays[i] * 1000000LL); 322 } 323 menu->AddItem(delayMenu); 324 325 menu->AddSeparatorItem(); 326 327 _AddItemMenu(menu, B_TRANSLATE("Original size"), 328 kMsgOriginalSize, '1', 0, this); 329 _AddItemMenu(menu, B_TRANSLATE("Fit to window"), 330 kMsgFitToWindow, '0', 0, this); 331 _AddItemMenu(menu, B_TRANSLATE("Zoom in"), MSG_ZOOM_IN, '+', 0, this); 332 _AddItemMenu(menu, B_TRANSLATE("Zoom out"), MSG_ZOOM_OUT, '-', 0, this); 333 334 menu->AddSeparatorItem(); 335 336 if (!popupMenu || fFullScreen) { 337 _AddItemMenu(menu, B_TRANSLATE("High-quality zooming"), 338 MSG_SCALE_BILINEAR, 0, 0, this); 339 _AddItemMenu(menu, B_TRANSLATE("Stretch to window"), 340 kMsgStretchToWindow, 0, 0, this); 341 342 menu->AddSeparatorItem(); 343 } 344 345 _AddItemMenu(menu, B_TRANSLATE("Full screen"), 346 MSG_FULL_SCREEN, B_ENTER, 0, this); 347 _MarkMenuItem(menu, MSG_FULL_SCREEN, fFullScreen); 348 349 _AddItemMenu(menu, B_TRANSLATE("Show caption in full screen mode"), 350 MSG_SHOW_CAPTION, 0, 0, this); 351 _MarkMenuItem(menu, MSG_SHOW_CAPTION, fShowCaption); 352 353 _MarkMenuItem(menu, MSG_SCALE_BILINEAR, fImageView->ScaleBilinear()); 354 _MarkMenuItem(menu, kMsgStretchToWindow, fImageView->StretchesToBounds()); 355 356 if (!popupMenu) { 357 _AddItemMenu(menu, B_TRANSLATE("Show tool bar"), kMsgToggleToolBar, 358 'T', 0, this); 359 _MarkMenuItem(menu, kMsgToggleToolBar, 360 !fToolBarView->IsHidden(fToolBarView)); 361 } 362 363 if (popupMenu) { 364 menu->AddSeparatorItem(); 365 _AddItemMenu(menu, B_TRANSLATE("Use as background" B_UTF8_ELLIPSIS), 366 MSG_DESKTOP_BACKGROUND, 0, 0, this); 367 } 368 } 369 370 371 BMenu* 372 ShowImageWindow::_BuildRatingMenu() 373 { 374 fRatingMenu = new BMenu(B_TRANSLATE("Rating")); 375 for (int32 i = 1; i <= 10; i++) { 376 BString label; 377 label << i; 378 BMessage* message = new BMessage(MSG_SET_RATING); 379 message->AddInt32("rating", i); 380 fRatingMenu->AddItem(new BMenuItem(label.String(), message)); 381 } 382 // NOTE: We may want to encapsulate the Rating menu within a more 383 // general "Attributes" menu. 384 return fRatingMenu; 385 } 386 387 388 void 389 ShowImageWindow::_AddMenus(BMenuBar* bar) 390 { 391 BMenu* menu = new BMenu(B_TRANSLATE("File")); 392 393 // Add recent files to "Open File" entry as sub-menu. 394 BMenuItem* item = new BMenuItem(BRecentFilesList::NewFileListMenu( 395 B_TRANSLATE("Open"B_UTF8_ELLIPSIS), NULL, NULL, be_app, 10, true, 396 NULL, kApplicationSignature), new BMessage(MSG_FILE_OPEN)); 397 item->SetShortcut('O', 0); 398 item->SetTarget(be_app); 399 menu->AddItem(item); 400 menu->AddSeparatorItem(); 401 402 BMenu* menuSaveAs = new BMenu(B_TRANSLATE("Save as" B_UTF8_ELLIPSIS), 403 B_ITEMS_IN_COLUMN); 404 BTranslationUtils::AddTranslationItems(menuSaveAs, B_TRANSLATOR_BITMAP); 405 // Fill Save As submenu with all types that can be converted 406 // to from the Be bitmap image format 407 menu->AddItem(menuSaveAs); 408 _AddItemMenu(menu, B_TRANSLATE("Close"), B_QUIT_REQUESTED, 'W', 0, this); 409 menu->AddSeparatorItem(); 410 _AddItemMenu(menu, B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS), 411 MSG_PAGE_SETUP, 0, 0, this); 412 _AddItemMenu(menu, B_TRANSLATE("Print" B_UTF8_ELLIPSIS), 413 MSG_PREPARE_PRINT, 'P', 0, this); 414 menu->AddSeparatorItem(); 415 _AddItemMenu(menu, B_TRANSLATE("Quit"), B_QUIT_REQUESTED, 'Q', 0, be_app); 416 bar->AddItem(menu); 417 418 menu = new BMenu(B_TRANSLATE("Edit")); 419 _AddItemMenu(menu, B_TRANSLATE("Undo"), B_UNDO, 'Z', 0, this, false); 420 menu->AddSeparatorItem(); 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(0L); 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 B_SHIFT_KEY, shortcut); 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 MSG_UNDO_STATE: 769 { 770 bool enable; 771 if (message->FindBool("can_undo", &enable) == B_OK) 772 _EnableMenuItem(fBar, B_UNDO, enable); 773 break; 774 } 775 776 case B_UNDO: 777 fImageView->Undo(); 778 break; 779 780 case B_COPY: 781 fImageView->CopySelectionToClipboard(); 782 break; 783 784 case MSG_SELECTION_MODE: 785 { 786 bool selectionMode = _ToggleMenuItem(MSG_SELECTION_MODE); 787 fImageView->SetSelectionMode(selectionMode); 788 if (!selectionMode) 789 fImageView->ClearSelection(); 790 break; 791 } 792 793 case MSG_CLEAR_SELECT: 794 fImageView->ClearSelection(); 795 break; 796 797 case MSG_SELECT_ALL: 798 fImageView->SelectAll(); 799 break; 800 801 case MSG_PAGE_FIRST: 802 if (_ClosePrompt() && fNavigator.FirstPage()) 803 _LoadImage(); 804 break; 805 806 case MSG_PAGE_LAST: 807 if (_ClosePrompt() && fNavigator.LastPage()) 808 _LoadImage(); 809 break; 810 811 case MSG_PAGE_NEXT: 812 if (_ClosePrompt() && fNavigator.NextPage()) 813 _LoadImage(); 814 break; 815 816 case MSG_PAGE_PREV: 817 if (_ClosePrompt() && fNavigator.PreviousPage()) 818 _LoadImage(); 819 break; 820 821 case MSG_GOTO_PAGE: 822 { 823 if (!_ClosePrompt()) 824 break; 825 826 int32 newPage; 827 if (message->FindInt32("page", &newPage) != B_OK) 828 break; 829 830 int32 currentPage = fNavigator.CurrentPage(); 831 int32 pages = fNavigator.PageCount(); 832 833 // TODO: use radio mode instead! 834 if (newPage > 0 && newPage <= pages) { 835 BMenuItem* currentItem = fGoToPageMenu->ItemAt(currentPage - 1); 836 BMenuItem* newItem = fGoToPageMenu->ItemAt(newPage - 1); 837 if (currentItem != NULL && newItem != NULL) { 838 currentItem->SetMarked(false); 839 newItem->SetMarked(true); 840 if (fNavigator.GoToPage(newPage)) 841 _LoadImage(); 842 } 843 } 844 break; 845 } 846 847 case kMsgFitToWindow: 848 fImageView->FitToBounds(); 849 break; 850 851 case kMsgStretchToWindow: 852 fImageView->SetStretchToBounds( 853 _ToggleMenuItem(kMsgStretchToWindow)); 854 break; 855 856 case MSG_FILE_PREV: 857 if (_ClosePrompt() && fNavigator.PreviousFile()) 858 _LoadImage(false); 859 break; 860 861 case MSG_FILE_NEXT: 862 case kMsgNextSlide: 863 if (_ClosePrompt() && fNavigator.NextFile()) 864 _LoadImage(); 865 break; 866 867 case kMsgDeleteCurrentFile: 868 { 869 if (fNavigator.MoveFileToTrash()) 870 _LoadImage(); 871 else 872 PostMessage(B_QUIT_REQUESTED); 873 break; 874 } 875 876 case MSG_ROTATE_90: 877 fImageView->Rotate(90); 878 break; 879 880 case MSG_ROTATE_270: 881 fImageView->Rotate(270); 882 break; 883 884 case MSG_FLIP_LEFT_TO_RIGHT: 885 fImageView->Flip(true); 886 break; 887 888 case MSG_FLIP_TOP_TO_BOTTOM: 889 fImageView->Flip(false); 890 break; 891 892 case MSG_SLIDE_SHOW: 893 { 894 bool fullScreen = false; 895 message->FindBool("full screen", &fullScreen); 896 897 BMenuItem* item = fBar->FindItem(message->what); 898 if (item == NULL) 899 break; 900 901 if (item->IsMarked()) { 902 item->SetMarked(false); 903 _StopSlideShow(); 904 fToolBarView->SetActionPressed(MSG_SLIDE_SHOW, false); 905 } else if (_ClosePrompt()) { 906 item->SetMarked(true); 907 if (!fFullScreen && fullScreen) 908 _ToggleFullScreen(); 909 _StartSlideShow(); 910 fToolBarView->SetActionPressed(MSG_SLIDE_SHOW, true); 911 } 912 break; 913 } 914 915 case kMsgStopSlideShow: 916 { 917 BMenuItem* item = fBar->FindItem(MSG_SLIDE_SHOW); 918 if (item != NULL) 919 item->SetMarked(false); 920 921 _StopSlideShow(); 922 fToolBarView->SetActionPressed(MSG_SLIDE_SHOW, false); 923 break; 924 } 925 926 case MSG_SLIDE_SHOW_DELAY: 927 { 928 bigtime_t delay; 929 if (message->FindInt64("delay", &delay) == B_OK) { 930 _SetSlideShowDelay(delay); 931 // in case message is sent from popup menu 932 _MarkSlideShowDelay(delay); 933 } 934 break; 935 } 936 937 case MSG_FULL_SCREEN: 938 _ToggleFullScreen(); 939 break; 940 941 case MSG_EXIT_FULL_SCREEN: 942 if (fFullScreen) 943 _ToggleFullScreen(); 944 break; 945 946 case MSG_SHOW_CAPTION: 947 { 948 fShowCaption = _ToggleMenuItem(message->what); 949 ShowImageSettings* settings = my_app->Settings(); 950 951 if (settings->Lock()) { 952 settings->SetBool("ShowCaption", fShowCaption); 953 settings->Unlock(); 954 } 955 if (fFullScreen) 956 fImageView->SetShowCaption(fShowCaption); 957 } break; 958 959 case MSG_PAGE_SETUP: 960 _PageSetup(); 961 break; 962 963 case MSG_PREPARE_PRINT: 964 _PrepareForPrint(); 965 break; 966 967 case MSG_PRINT: 968 _Print(message); 969 break; 970 971 case MSG_ZOOM_IN: 972 fImageView->ZoomIn(); 973 break; 974 975 case MSG_ZOOM_OUT: 976 fImageView->ZoomOut(); 977 break; 978 979 case kMsgOriginalSize: 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 fToolBarView->MoveBy(0, offset); 1038 fScrollView->ResizeBy(0, -offset); 1039 fScrollView->MoveBy(0, offset); 1040 fVerticalScrollBar->ResizeBy(0, -offset); 1041 fVerticalScrollBar->MoveBy(0, offset); 1042 UpdateIfNeeded(); 1043 snooze(15000); 1044 } 1045 break; 1046 } 1047 case kMsgFinishSlidingToolBar: 1048 { 1049 float offset; 1050 bool show; 1051 if (message->FindFloat("offset", &offset) == B_OK 1052 && message->FindBool("show", &show) == B_OK) { 1053 // Compensate rounding errors with the final placement 1054 if (show) 1055 fToolBarView->MoveTo(fToolBarView->Frame().left, 0); 1056 else { 1057 fToolBarView->MoveTo(fToolBarView->Frame().left, offset); 1058 fToolBarView->Hide(); 1059 } 1060 BRect frame = fToolBarView->Parent()->Bounds(); 1061 frame.top = fToolBarView->Frame().bottom + 1; 1062 fScrollView->MoveTo(fScrollView->Frame().left, frame.top); 1063 fScrollView->ResizeTo(fScrollView->Bounds().Width(), 1064 frame.Height() - B_H_SCROLL_BAR_HEIGHT + 1); 1065 fVerticalScrollBar->MoveTo( 1066 frame.right - B_V_SCROLL_BAR_WIDTH + 1, frame.top); 1067 fVerticalScrollBar->ResizeTo( 1068 fVerticalScrollBar->Bounds().Width(), 1069 frame.Height() - B_H_SCROLL_BAR_HEIGHT + 1); 1070 } 1071 break; 1072 } 1073 1074 default: 1075 BWindow::MessageReceived(message); 1076 break; 1077 } 1078 } 1079 1080 1081 void 1082 ShowImageWindow::_UpdateStatusText(const BMessage* message) 1083 { 1084 BString status; 1085 if (fImageView->Bitmap() != NULL) { 1086 BRect bounds = fImageView->Bitmap()->Bounds(); 1087 status << bounds.IntegerWidth() + 1 1088 << "x" << bounds.IntegerHeight() + 1 << ", " << fImageType; 1089 } 1090 1091 BString text; 1092 if (message != NULL && message->FindString("status", &text) == B_OK 1093 && text.Length() > 0) { 1094 status << ", " << text; 1095 } 1096 1097 fStatusView->Update(fNavigator.CurrentRef(), status); 1098 } 1099 1100 1101 void 1102 ShowImageWindow::_LoadError(const entry_ref& ref) 1103 { 1104 // TODO: give a better error message! 1105 BAlert* alert = new BAlert(B_TRANSLATE_SYSTEM_NAME("ShowImage"), 1106 B_TRANSLATE_CONTEXT("Could not load image! Either the " 1107 "file or an image translator for it does not exist.", 1108 "LoadAlerts"), 1109 B_TRANSLATE_CONTEXT("OK", "Alerts"), NULL, NULL, 1110 B_WIDTH_AS_USUAL, B_INFO_ALERT); 1111 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1112 alert->Go(); 1113 } 1114 1115 1116 void 1117 ShowImageWindow::_SaveAs(BMessage* message) 1118 { 1119 // Read the translator and output type the user chose 1120 translator_id outTranslator; 1121 uint32 outType; 1122 if (message->FindInt32(kTranslatorField, 1123 reinterpret_cast<int32 *>(&outTranslator)) != B_OK 1124 || message->FindInt32(kTypeField, 1125 reinterpret_cast<int32 *>(&outType)) != B_OK) 1126 return; 1127 1128 // Add the chosen translator and output type to the 1129 // message that the save panel will send back 1130 BMessage panelMsg(MSG_SAVE_PANEL); 1131 panelMsg.AddInt32(kTranslatorField, outTranslator); 1132 panelMsg.AddInt32(kTypeField, outType); 1133 1134 // Create save panel and show it 1135 BMessenger target(this); 1136 fSavePanel = new (std::nothrow) BFilePanel(B_SAVE_PANEL, 1137 &target, NULL, 0, false, &panelMsg); 1138 if (!fSavePanel) 1139 return; 1140 1141 fSavePanel->Window()->SetWorkspaces(B_CURRENT_WORKSPACE); 1142 fSavePanel->Show(); 1143 } 1144 1145 1146 void 1147 ShowImageWindow::_SaveToFile(BMessage* message) 1148 { 1149 // Read in where the file should be saved 1150 entry_ref dirRef; 1151 if (message->FindRef("directory", &dirRef) != B_OK) 1152 return; 1153 1154 const char* filename; 1155 if (message->FindString("name", &filename) != B_OK) 1156 return; 1157 1158 // Read in the translator and type to be used 1159 // to save the output image 1160 translator_id outTranslator; 1161 uint32 outType; 1162 if (message->FindInt32(kTranslatorField, 1163 reinterpret_cast<int32 *>(&outTranslator)) != B_OK 1164 || message->FindInt32(kTypeField, 1165 reinterpret_cast<int32 *>(&outType)) != B_OK) 1166 return; 1167 1168 // Find the translator_format information needed to 1169 // write a MIME attribute for the image file 1170 BTranslatorRoster* roster = BTranslatorRoster::Default(); 1171 const translation_format* outFormat = NULL; 1172 int32 outCount = 0; 1173 if (roster->GetOutputFormats(outTranslator, &outFormat, &outCount) != B_OK 1174 || outCount < 1) 1175 return; 1176 1177 int32 i; 1178 for (i = 0; i < outCount; i++) { 1179 if (outFormat[i].group == B_TRANSLATOR_BITMAP && outFormat[i].type 1180 == outType) 1181 break; 1182 } 1183 if (i == outCount) 1184 return; 1185 1186 // Write out the image file 1187 BDirectory dir(&dirRef); 1188 fImageView->SaveToFile(&dir, filename, NULL, &outFormat[i]); 1189 } 1190 1191 1192 #undef B_TRANSLATION_CONTEXT 1193 #define B_TRANSLATION_CONTEXT "ClosePrompt" 1194 1195 1196 bool 1197 ShowImageWindow::_ClosePrompt() 1198 { 1199 if (!fModified) 1200 return true; 1201 1202 int32 count = fNavigator.PageCount(); 1203 int32 page = fNavigator.CurrentPage(); 1204 BString prompt; 1205 1206 if (count > 1) { 1207 bs_printf(&prompt, 1208 B_TRANSLATE("The document '%s' (page %d) has been changed. Do you " 1209 "want to close the document?"), 1210 fImageView->Image()->name, page); 1211 } else { 1212 bs_printf(&prompt, 1213 B_TRANSLATE("The document '%s' has been changed. Do you want to " 1214 "close the document?"), 1215 fImageView->Image()->name); 1216 } 1217 1218 BAlert* alert = new BAlert(B_TRANSLATE("Close document"), prompt.String(), 1219 B_TRANSLATE("Cancel"), B_TRANSLATE("Close")); 1220 alert->SetShortcut(0, B_ESCAPE); 1221 1222 if (alert->Go() == 0) { 1223 // Cancel 1224 return false; 1225 } 1226 1227 // Close 1228 fModified = false; 1229 return true; 1230 } 1231 1232 1233 status_t 1234 ShowImageWindow::_LoadImage(bool forward) 1235 { 1236 BMessenger us(this); 1237 status_t status = ImageCache::Default().RetrieveImage( 1238 fNavigator.CurrentRef(), fNavigator.CurrentPage(), &us); 1239 if (status != B_OK) 1240 return status; 1241 1242 fProgressWindow->Start(this); 1243 1244 // Preload previous/next images - two in the navigation direction, one 1245 // in the opposite direction. 1246 1247 entry_ref nextRef = fNavigator.CurrentRef(); 1248 if (_PreloadImage(forward, nextRef)) 1249 _PreloadImage(forward, nextRef); 1250 1251 entry_ref previousRef = fNavigator.CurrentRef(); 1252 _PreloadImage(!forward, previousRef); 1253 1254 return B_OK; 1255 } 1256 1257 1258 bool 1259 ShowImageWindow::_PreloadImage(bool forward, entry_ref& ref) 1260 { 1261 entry_ref currentRef = ref; 1262 if ((forward && !fNavigator.GetNextFile(currentRef, ref)) 1263 || (!forward && !fNavigator.GetPreviousFile(currentRef, ref))) 1264 return false; 1265 1266 return ImageCache::Default().RetrieveImage(ref) == B_OK; 1267 } 1268 1269 1270 void 1271 ShowImageWindow::_ToggleFullScreen() 1272 { 1273 BRect frame; 1274 fFullScreen = !fFullScreen; 1275 if (fFullScreen) { 1276 BScreen screen; 1277 fWindowFrame = Frame(); 1278 frame = screen.Frame(); 1279 frame.top -= fBar->Bounds().Height() + 1; 1280 frame.right += B_V_SCROLL_BAR_WIDTH; 1281 frame.bottom += B_H_SCROLL_BAR_HEIGHT; 1282 frame.InsetBy(-1, -1); // PEN_SIZE in ShowImageView 1283 1284 SetFlags(Flags() | B_NOT_RESIZABLE | B_NOT_MOVABLE); 1285 1286 Activate(); 1287 // make the window frontmost 1288 } else { 1289 frame = fWindowFrame; 1290 1291 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE)); 1292 } 1293 1294 fToolBarView->SetActionVisible(MSG_FULL_SCREEN, fFullScreen); 1295 _SetToolBarVisible(!fFullScreen && fShowToolBar); 1296 1297 MoveTo(frame.left, frame.top); 1298 ResizeTo(frame.Width(), frame.Height()); 1299 1300 fImageView->SetHideIdlingCursor(fFullScreen); 1301 fImageView->SetShowCaption(fFullScreen && fShowCaption); 1302 1303 Layout(false); 1304 // We need to manually relayout here, as the views are layouted 1305 // asynchronously, and FitToBounds() would still have the wrong size 1306 fImageView->FitToBounds(); 1307 } 1308 1309 1310 void 1311 ShowImageWindow::_ApplySettings() 1312 { 1313 ShowImageSettings* settings = my_app->Settings(); 1314 1315 if (settings->Lock()) { 1316 fShowCaption = settings->GetBool("ShowCaption", fShowCaption); 1317 fPrintOptions.SetBounds(BRect(0, 0, 1023, 767)); 1318 1319 fSlideShowDelay = settings->GetTime("SlideShowDelay", fSlideShowDelay); 1320 1321 fPrintOptions.SetOption((enum PrintOptions::Option)settings->GetInt32( 1322 "PO:Option", fPrintOptions.Option())); 1323 fPrintOptions.SetZoomFactor( 1324 settings->GetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor())); 1325 fPrintOptions.SetDPI(settings->GetFloat("PO:DPI", fPrintOptions.DPI())); 1326 fPrintOptions.SetWidth( 1327 settings->GetFloat("PO:Width", fPrintOptions.Width())); 1328 fPrintOptions.SetHeight( 1329 settings->GetFloat("PO:Height", fPrintOptions.Height())); 1330 1331 fShowToolBar = settings->GetBool("ShowToolBar", fShowToolBar); 1332 1333 settings->Unlock(); 1334 } 1335 } 1336 1337 1338 void 1339 ShowImageWindow::_SavePrintOptions() 1340 { 1341 ShowImageSettings* settings = my_app->Settings(); 1342 1343 if (settings->Lock()) { 1344 settings->SetInt32("PO:Option", fPrintOptions.Option()); 1345 settings->SetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor()); 1346 settings->SetFloat("PO:DPI", fPrintOptions.DPI()); 1347 settings->SetFloat("PO:Width", fPrintOptions.Width()); 1348 settings->SetFloat("PO:Height", fPrintOptions.Height()); 1349 settings->Unlock(); 1350 } 1351 } 1352 1353 1354 bool 1355 ShowImageWindow::_PageSetup() 1356 { 1357 BPrintJob printJob(fImageView->Image()->name); 1358 if (fPrintSettings != NULL) 1359 printJob.SetSettings(new BMessage(*fPrintSettings)); 1360 1361 status_t status = printJob.ConfigPage(); 1362 if (status == B_OK) { 1363 delete fPrintSettings; 1364 fPrintSettings = printJob.Settings(); 1365 } 1366 1367 return status == B_OK; 1368 } 1369 1370 1371 void 1372 ShowImageWindow::_PrepareForPrint() 1373 { 1374 if (fPrintSettings == NULL) { 1375 BPrintJob printJob(fImageView->Image()->name); 1376 if (printJob.ConfigJob() == B_OK) 1377 fPrintSettings = printJob.Settings(); 1378 } 1379 1380 fPrintOptions.SetBounds(fImageView->Bitmap()->Bounds()); 1381 fPrintOptions.SetWidth(fImageView->Bitmap()->Bounds().Width() + 1); 1382 1383 new PrintOptionsWindow(BPoint(Frame().left + 30, Frame().top + 50), 1384 &fPrintOptions, this); 1385 } 1386 1387 1388 void 1389 ShowImageWindow::_Print(BMessage* msg) 1390 { 1391 status_t st; 1392 if (msg->FindInt32("status", &st) != B_OK || st != B_OK) 1393 return; 1394 1395 _SavePrintOptions(); 1396 1397 BPrintJob printJob(fImageView->Image()->name); 1398 if (fPrintSettings) 1399 printJob.SetSettings(new BMessage(*fPrintSettings)); 1400 1401 if (printJob.ConfigJob() == B_OK) { 1402 delete fPrintSettings; 1403 fPrintSettings = printJob.Settings(); 1404 1405 // first/lastPage is unused for now 1406 int32 firstPage = printJob.FirstPage(); 1407 int32 lastPage = printJob.LastPage(); 1408 BRect printableRect = printJob.PrintableRect(); 1409 1410 if (firstPage < 1) 1411 firstPage = 1; 1412 if (lastPage < firstPage) 1413 lastPage = firstPage; 1414 1415 BBitmap* bitmap = fImageView->Bitmap(); 1416 float imageWidth = bitmap->Bounds().Width() + 1.0; 1417 float imageHeight = bitmap->Bounds().Height() + 1.0; 1418 1419 float width; 1420 switch (fPrintOptions.Option()) { 1421 case PrintOptions::kFitToPage: { 1422 float w1 = printableRect.Width() + 1; 1423 float w2 = imageWidth * (printableRect.Height() + 1) 1424 / imageHeight; 1425 if (w2 < w1) 1426 width = w2; 1427 else 1428 width = w1; 1429 } break; 1430 case PrintOptions::kZoomFactor: 1431 width = imageWidth * fPrintOptions.ZoomFactor(); 1432 break; 1433 case PrintOptions::kDPI: 1434 width = imageWidth * 72.0 / fPrintOptions.DPI(); 1435 break; 1436 case PrintOptions::kWidth: 1437 case PrintOptions::kHeight: 1438 width = fPrintOptions.Width(); 1439 break; 1440 1441 default: 1442 // keep compiler silent; should not reach here 1443 width = imageWidth; 1444 } 1445 1446 // TODO: eventually print large images on several pages 1447 printJob.BeginJob(); 1448 fImageView->SetScale(width / imageWidth); 1449 // coordinates are relative to printable rectangle 1450 BRect bounds(bitmap->Bounds()); 1451 printJob.DrawView(fImageView, bounds, BPoint(0, 0)); 1452 fImageView->SetScale(1.0); 1453 printJob.SpoolPage(); 1454 printJob.CommitJob(); 1455 } 1456 } 1457 1458 1459 void 1460 ShowImageWindow::_SetSlideShowDelay(bigtime_t delay) 1461 { 1462 if (fSlideShowDelay == delay) 1463 return; 1464 1465 fSlideShowDelay = delay; 1466 1467 ShowImageSettings* settings = my_app->Settings(); 1468 if (settings->Lock()) { 1469 settings->SetTime("SlideShowDelay", fSlideShowDelay); 1470 settings->Unlock(); 1471 } 1472 1473 if (fSlideShowRunner != NULL) 1474 _StartSlideShow(); 1475 } 1476 1477 1478 void 1479 ShowImageWindow::_StartSlideShow() 1480 { 1481 _StopSlideShow(); 1482 1483 BMessage nextSlide(kMsgNextSlide); 1484 fSlideShowRunner = new BMessageRunner(this, &nextSlide, fSlideShowDelay); 1485 } 1486 1487 1488 void 1489 ShowImageWindow::_StopSlideShow() 1490 { 1491 if (fSlideShowRunner != NULL) { 1492 delete fSlideShowRunner; 1493 fSlideShowRunner = NULL; 1494 } 1495 } 1496 1497 1498 void 1499 ShowImageWindow::_UpdateRatingMenu() 1500 { 1501 BFile file(&fNavigator.CurrentRef(), B_READ_ONLY); 1502 if (file.InitCheck() != B_OK) 1503 return; 1504 int32 rating; 1505 ssize_t size = sizeof(rating); 1506 if (file.ReadAttr("Media:Rating", B_INT32_TYPE, 0, &rating, size) != size) 1507 rating = 0; 1508 // TODO: Finding the correct item could be more robust, like by looking 1509 // at the message of each item. 1510 for (int32 i = 1; i <= 10; i++) { 1511 BMenuItem* item = fRatingMenu->ItemAt(i - 1); 1512 if (item == NULL) 1513 break; 1514 item->SetMarked(i == rating); 1515 } 1516 } 1517 1518 1519 void 1520 ShowImageWindow::_SetToolBarVisible(bool visible, bool animate) 1521 { 1522 if (visible == !fToolBarView->IsHidden()) 1523 return; 1524 1525 float diff = fToolBarView->Bounds().Height() + 2; 1526 if (!visible) 1527 diff = -diff; 1528 else 1529 fToolBarView->Show(); 1530 1531 if (animate) { 1532 // Slide the controls into view. We do this with messages in order 1533 // not to block the window thread. 1534 const float kAnimationOffsets[] = { 0.05, 0.2, 0.5, 0.2, 0.05 }; 1535 const int32 steps = sizeof(kAnimationOffsets) / sizeof(float); 1536 float originalY = fToolBarView->Frame().top; 1537 for (int32 i = 0; i < steps; i++) { 1538 BMessage message(kMsgSlideToolBar); 1539 message.AddFloat("offset", floorf(diff * kAnimationOffsets[i])); 1540 PostMessage(&message, this); 1541 } 1542 BMessage finalMessage(kMsgFinishSlidingToolBar); 1543 finalMessage.AddFloat("offset", originalY + diff); 1544 finalMessage.AddBool("show", visible); 1545 PostMessage(&finalMessage, this); 1546 } else { 1547 fScrollView->ResizeBy(0, -diff); 1548 fScrollView->MoveBy(0, diff); 1549 fVerticalScrollBar->ResizeBy(0, -diff); 1550 fVerticalScrollBar->MoveBy(0, diff); 1551 fToolBarView->MoveBy(0, diff); 1552 if (!visible) 1553 fToolBarView->Hide(); 1554 } 1555 } 1556 1557 1558 bool 1559 ShowImageWindow::QuitRequested() 1560 { 1561 if (fSavePanel) { 1562 // Don't allow this window to be closed if a save panel is open 1563 return false; 1564 } 1565 1566 if (!_ClosePrompt()) 1567 return false; 1568 1569 ShowImageSettings* settings = my_app->Settings(); 1570 if (settings->Lock()) { 1571 if (fFullScreen) 1572 settings->SetRect("WindowFrame", fWindowFrame); 1573 else 1574 settings->SetRect("WindowFrame", Frame()); 1575 settings->Unlock(); 1576 } 1577 1578 be_app->PostMessage(MSG_WINDOW_HAS_QUIT); 1579 1580 return true; 1581 } 1582