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", &backgroundsMessage); 994 break; 995 } 996 997 case MSG_SET_RATING: 998 { 999 int32 rating; 1000 if (message->FindInt32("rating", &rating) != B_OK) 1001 break; 1002 BFile file(&fNavigator.CurrentRef(), B_WRITE_ONLY); 1003 if (file.InitCheck() != B_OK) 1004 break; 1005 file.WriteAttr("Media:Rating", B_INT32_TYPE, 0, &rating, 1006 sizeof(rating)); 1007 _UpdateRatingMenu(); 1008 break; 1009 } 1010 1011 case kMsgToggleToolBar: 1012 { 1013 fShowToolBar = _ToggleMenuItem(message->what); 1014 _SetToolBarVisible(fShowToolBar, true); 1015 1016 ShowImageSettings* settings = my_app->Settings(); 1017 1018 if (settings->Lock()) { 1019 settings->SetBool("ShowToolBar", fShowToolBar); 1020 settings->Unlock(); 1021 } 1022 break; 1023 } 1024 case kShowToolBarIfEnabled: 1025 { 1026 bool show; 1027 if (message->FindBool("show", &show) != B_OK) 1028 break; 1029 _SetToolBarVisible(fShowToolBar && show, true); 1030 break; 1031 } 1032 case kMsgSlideToolBar: 1033 { 1034 float offset; 1035 if (message->FindFloat("offset", &offset) == B_OK) { 1036 fToolBarView->MoveBy(0, offset); 1037 fScrollView->ResizeBy(0, -offset); 1038 fScrollView->MoveBy(0, offset); 1039 fVerticalScrollBar->ResizeBy(0, -offset); 1040 fVerticalScrollBar->MoveBy(0, offset); 1041 UpdateIfNeeded(); 1042 snooze(15000); 1043 } 1044 break; 1045 } 1046 case kMsgFinishSlidingToolBar: 1047 { 1048 float offset; 1049 bool show; 1050 if (message->FindFloat("offset", &offset) == B_OK 1051 && message->FindBool("show", &show) == B_OK) { 1052 // Compensate rounding errors with the final placement 1053 if (show) { 1054 fToolBarView->MoveTo(fToolBarView->Frame().left, 0); 1055 } else { 1056 fToolBarView->MoveTo(fToolBarView->Frame().left, offset); 1057 fToolBarView->Hide(); 1058 } 1059 BRect frame = fToolBarView->Parent()->Bounds(); 1060 frame.top = fToolBarView->Frame().bottom + 1; 1061 fScrollView->MoveTo(0, frame.top); 1062 fScrollView->ResizeTo(fScrollView->Bounds().Width(), 1063 frame.Height() - B_H_SCROLL_BAR_HEIGHT + 1); 1064 fVerticalScrollBar->MoveTo( 1065 frame.right - B_V_SCROLL_BAR_WIDTH + 1, frame.top); 1066 fVerticalScrollBar->ResizeTo( 1067 fVerticalScrollBar->Bounds().Width(), 1068 frame.Height() - B_H_SCROLL_BAR_HEIGHT + 1); 1069 } 1070 break; 1071 } 1072 1073 default: 1074 BWindow::MessageReceived(message); 1075 break; 1076 } 1077 } 1078 1079 1080 void 1081 ShowImageWindow::_UpdateStatusText(const BMessage* message) 1082 { 1083 BString status; 1084 if (fImageView->Bitmap() != NULL) { 1085 BRect bounds = fImageView->Bitmap()->Bounds(); 1086 status << bounds.IntegerWidth() + 1 1087 << "x" << bounds.IntegerHeight() + 1 << ", " << fImageType; 1088 } 1089 1090 BString text; 1091 if (message != NULL && message->FindString("status", &text) == B_OK 1092 && text.Length() > 0) { 1093 status << ", " << text; 1094 } 1095 1096 fStatusView->Update(fNavigator.CurrentRef(), status); 1097 } 1098 1099 1100 void 1101 ShowImageWindow::_LoadError(const entry_ref& ref) 1102 { 1103 // TODO: give a better error message! 1104 BAlert* alert = new BAlert(B_TRANSLATE_SYSTEM_NAME("ShowImage"), 1105 B_TRANSLATE_CONTEXT("Could not load image! Either the " 1106 "file or an image translator for it does not exist.", 1107 "LoadAlerts"), 1108 B_TRANSLATE_CONTEXT("OK", "Alerts"), NULL, NULL, 1109 B_WIDTH_AS_USUAL, B_INFO_ALERT); 1110 alert->Go(); 1111 } 1112 1113 1114 void 1115 ShowImageWindow::_SaveAs(BMessage* message) 1116 { 1117 // Read the translator and output type the user chose 1118 translator_id outTranslator; 1119 uint32 outType; 1120 if (message->FindInt32(kTranslatorField, 1121 reinterpret_cast<int32 *>(&outTranslator)) != B_OK 1122 || message->FindInt32(kTypeField, 1123 reinterpret_cast<int32 *>(&outType)) != B_OK) 1124 return; 1125 1126 // Add the chosen translator and output type to the 1127 // message that the save panel will send back 1128 BMessage panelMsg(MSG_SAVE_PANEL); 1129 panelMsg.AddInt32(kTranslatorField, outTranslator); 1130 panelMsg.AddInt32(kTypeField, outType); 1131 1132 // Create save panel and show it 1133 BMessenger target(this); 1134 fSavePanel = new (std::nothrow) BFilePanel(B_SAVE_PANEL, 1135 &target, NULL, 0, false, &panelMsg); 1136 if (!fSavePanel) 1137 return; 1138 1139 fSavePanel->Window()->SetWorkspaces(B_CURRENT_WORKSPACE); 1140 fSavePanel->Show(); 1141 } 1142 1143 1144 void 1145 ShowImageWindow::_SaveToFile(BMessage* message) 1146 { 1147 // Read in where the file should be saved 1148 entry_ref dirRef; 1149 if (message->FindRef("directory", &dirRef) != B_OK) 1150 return; 1151 1152 const char* filename; 1153 if (message->FindString("name", &filename) != B_OK) 1154 return; 1155 1156 // Read in the translator and type to be used 1157 // to save the output image 1158 translator_id outTranslator; 1159 uint32 outType; 1160 if (message->FindInt32(kTranslatorField, 1161 reinterpret_cast<int32 *>(&outTranslator)) != B_OK 1162 || message->FindInt32(kTypeField, 1163 reinterpret_cast<int32 *>(&outType)) != B_OK) 1164 return; 1165 1166 // Find the translator_format information needed to 1167 // write a MIME attribute for the image file 1168 BTranslatorRoster* roster = BTranslatorRoster::Default(); 1169 const translation_format* outFormat = NULL; 1170 int32 outCount = 0; 1171 if (roster->GetOutputFormats(outTranslator, &outFormat, &outCount) != B_OK 1172 || outCount < 1) 1173 return; 1174 1175 int32 i; 1176 for (i = 0; i < outCount; i++) { 1177 if (outFormat[i].group == B_TRANSLATOR_BITMAP && outFormat[i].type == outType) 1178 break; 1179 } 1180 if (i == outCount) 1181 return; 1182 1183 // Write out the image file 1184 BDirectory dir(&dirRef); 1185 fImageView->SaveToFile(&dir, filename, NULL, &outFormat[i]); 1186 } 1187 1188 1189 #undef B_TRANSLATION_CONTEXT 1190 #define B_TRANSLATION_CONTEXT "ClosePrompt" 1191 1192 1193 bool 1194 ShowImageWindow::_ClosePrompt() 1195 { 1196 if (!fModified) 1197 return true; 1198 1199 int32 count = fNavigator.PageCount(); 1200 int32 page = fNavigator.CurrentPage(); 1201 BString prompt; 1202 1203 if (count > 1) { 1204 bs_printf(&prompt, 1205 B_TRANSLATE("The document '%s' (page %d) has been changed. Do you " 1206 "want to close the document?"), 1207 fImageView->Image()->name, page); 1208 } else { 1209 bs_printf(&prompt, 1210 B_TRANSLATE("The document '%s' has been changed. Do you want to " 1211 "close the document?"), 1212 fImageView->Image()->name); 1213 } 1214 1215 BAlert* alert = new BAlert(B_TRANSLATE("Close document"), prompt.String(), 1216 B_TRANSLATE("Cancel"), B_TRANSLATE("Close")); 1217 if (alert->Go() == 0) { 1218 // Cancel 1219 return false; 1220 } 1221 1222 // Close 1223 fModified = false; 1224 return true; 1225 } 1226 1227 1228 status_t 1229 ShowImageWindow::_LoadImage(bool forward) 1230 { 1231 BMessenger us(this); 1232 status_t status = ImageCache::Default().RetrieveImage( 1233 fNavigator.CurrentRef(), fNavigator.CurrentPage(), &us); 1234 if (status != B_OK) 1235 return status; 1236 1237 fProgressWindow->Start(this); 1238 1239 // Preload previous/next images - two in the navigation direction, one 1240 // in the opposite direction. 1241 1242 entry_ref nextRef = fNavigator.CurrentRef(); 1243 if (_PreloadImage(forward, nextRef)) 1244 _PreloadImage(forward, nextRef); 1245 1246 entry_ref previousRef = fNavigator.CurrentRef(); 1247 _PreloadImage(!forward, previousRef); 1248 1249 return B_OK; 1250 } 1251 1252 1253 bool 1254 ShowImageWindow::_PreloadImage(bool forward, entry_ref& ref) 1255 { 1256 entry_ref currentRef = ref; 1257 if ((forward && !fNavigator.GetNextFile(currentRef, ref)) 1258 || (!forward && !fNavigator.GetPreviousFile(currentRef, ref))) 1259 return false; 1260 1261 return ImageCache::Default().RetrieveImage(ref) == B_OK; 1262 } 1263 1264 1265 void 1266 ShowImageWindow::_ToggleFullScreen() 1267 { 1268 BRect frame; 1269 fFullScreen = !fFullScreen; 1270 if (fFullScreen) { 1271 BScreen screen; 1272 fWindowFrame = Frame(); 1273 frame = screen.Frame(); 1274 frame.top -= fBar->Bounds().Height()+1; 1275 frame.right += B_V_SCROLL_BAR_WIDTH; 1276 frame.bottom += B_H_SCROLL_BAR_HEIGHT; 1277 frame.InsetBy(-1, -1); // PEN_SIZE in ShowImageView 1278 1279 SetFlags(Flags() | B_NOT_RESIZABLE | B_NOT_MOVABLE); 1280 1281 Activate(); 1282 // make the window frontmost 1283 } else { 1284 frame = fWindowFrame; 1285 1286 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE)); 1287 } 1288 1289 fToolBarView->SetActionVisible(MSG_FULL_SCREEN, fFullScreen); 1290 _SetToolBarVisible(!fFullScreen && fShowToolBar); 1291 1292 MoveTo(frame.left, frame.top); 1293 ResizeTo(frame.Width(), frame.Height()); 1294 1295 fImageView->SetHideIdlingCursor(fFullScreen); 1296 fImageView->SetShowCaption(fFullScreen && fShowCaption); 1297 1298 Layout(false); 1299 // We need to manually relayout here, as the views are layouted 1300 // asynchronously, and FitToBounds() would still have the wrong size 1301 fImageView->FitToBounds(); 1302 } 1303 1304 1305 void 1306 ShowImageWindow::_ApplySettings() 1307 { 1308 ShowImageSettings* settings = my_app->Settings(); 1309 1310 if (settings->Lock()) { 1311 fShowCaption = settings->GetBool("ShowCaption", fShowCaption); 1312 fPrintOptions.SetBounds(BRect(0, 0, 1023, 767)); 1313 1314 fSlideShowDelay = settings->GetTime("SlideShowDelay", fSlideShowDelay); 1315 1316 fPrintOptions.SetOption((enum PrintOptions::Option)settings->GetInt32( 1317 "PO:Option", fPrintOptions.Option())); 1318 fPrintOptions.SetZoomFactor( 1319 settings->GetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor())); 1320 fPrintOptions.SetDPI(settings->GetFloat("PO:DPI", fPrintOptions.DPI())); 1321 fPrintOptions.SetWidth( 1322 settings->GetFloat("PO:Width", fPrintOptions.Width())); 1323 fPrintOptions.SetHeight( 1324 settings->GetFloat("PO:Height", fPrintOptions.Height())); 1325 1326 fShowToolBar = settings->GetBool("ShowToolBar", fShowToolBar); 1327 1328 settings->Unlock(); 1329 } 1330 } 1331 1332 1333 void 1334 ShowImageWindow::_SavePrintOptions() 1335 { 1336 ShowImageSettings* settings = my_app->Settings(); 1337 1338 if (settings->Lock()) { 1339 settings->SetInt32("PO:Option", fPrintOptions.Option()); 1340 settings->SetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor()); 1341 settings->SetFloat("PO:DPI", fPrintOptions.DPI()); 1342 settings->SetFloat("PO:Width", fPrintOptions.Width()); 1343 settings->SetFloat("PO:Height", fPrintOptions.Height()); 1344 settings->Unlock(); 1345 } 1346 } 1347 1348 1349 bool 1350 ShowImageWindow::_PageSetup() 1351 { 1352 BPrintJob printJob(fImageView->Image()->name); 1353 if (fPrintSettings != NULL) 1354 printJob.SetSettings(new BMessage(*fPrintSettings)); 1355 1356 status_t status = printJob.ConfigPage(); 1357 if (status == B_OK) { 1358 delete fPrintSettings; 1359 fPrintSettings = printJob.Settings(); 1360 } 1361 1362 return status == B_OK; 1363 } 1364 1365 1366 void 1367 ShowImageWindow::_PrepareForPrint() 1368 { 1369 if (fPrintSettings == NULL) { 1370 BPrintJob printJob(fImageView->Image()->name); 1371 if (printJob.ConfigJob() == B_OK) 1372 fPrintSettings = printJob.Settings(); 1373 } 1374 1375 fPrintOptions.SetBounds(fImageView->Bitmap()->Bounds()); 1376 fPrintOptions.SetWidth(fImageView->Bitmap()->Bounds().Width() + 1); 1377 1378 new PrintOptionsWindow(BPoint(Frame().left + 30, Frame().top + 50), 1379 &fPrintOptions, this); 1380 } 1381 1382 1383 void 1384 ShowImageWindow::_Print(BMessage* msg) 1385 { 1386 status_t st; 1387 if (msg->FindInt32("status", &st) != B_OK || st != B_OK) 1388 return; 1389 1390 _SavePrintOptions(); 1391 1392 BPrintJob printJob(fImageView->Image()->name); 1393 if (fPrintSettings) 1394 printJob.SetSettings(new BMessage(*fPrintSettings)); 1395 1396 if (printJob.ConfigJob() == B_OK) { 1397 delete fPrintSettings; 1398 fPrintSettings = printJob.Settings(); 1399 1400 // first/lastPage is unused for now 1401 int32 firstPage = printJob.FirstPage(); 1402 int32 lastPage = printJob.LastPage(); 1403 BRect printableRect = printJob.PrintableRect(); 1404 1405 if (firstPage < 1) 1406 firstPage = 1; 1407 if (lastPage < firstPage) 1408 lastPage = firstPage; 1409 1410 BBitmap* bitmap = fImageView->Bitmap(); 1411 float imageWidth = bitmap->Bounds().Width() + 1.0; 1412 float imageHeight = bitmap->Bounds().Height() + 1.0; 1413 1414 float width; 1415 switch (fPrintOptions.Option()) { 1416 case PrintOptions::kFitToPage: { 1417 float w1 = printableRect.Width()+1; 1418 float w2 = imageWidth * (printableRect.Height() + 1) 1419 / imageHeight; 1420 if (w2 < w1) 1421 width = w2; 1422 else 1423 width = w1; 1424 } break; 1425 case PrintOptions::kZoomFactor: 1426 width = imageWidth * fPrintOptions.ZoomFactor(); 1427 break; 1428 case PrintOptions::kDPI: 1429 width = imageWidth * 72.0 / fPrintOptions.DPI(); 1430 break; 1431 case PrintOptions::kWidth: 1432 case PrintOptions::kHeight: 1433 width = fPrintOptions.Width(); 1434 break; 1435 1436 default: 1437 // keep compiler silent; should not reach here 1438 width = imageWidth; 1439 } 1440 1441 // TODO: eventually print large images on several pages 1442 printJob.BeginJob(); 1443 fImageView->SetScale(width / imageWidth); 1444 // coordinates are relative to printable rectangle 1445 BRect bounds(bitmap->Bounds()); 1446 printJob.DrawView(fImageView, bounds, BPoint(0, 0)); 1447 fImageView->SetScale(1.0); 1448 printJob.SpoolPage(); 1449 printJob.CommitJob(); 1450 } 1451 } 1452 1453 1454 void 1455 ShowImageWindow::_SetSlideShowDelay(bigtime_t delay) 1456 { 1457 if (fSlideShowDelay == delay) 1458 return; 1459 1460 fSlideShowDelay = delay; 1461 1462 ShowImageSettings* settings = my_app->Settings(); 1463 if (settings->Lock()) { 1464 settings->SetTime("SlideShowDelay", fSlideShowDelay); 1465 settings->Unlock(); 1466 } 1467 1468 if (fSlideShowRunner != NULL) 1469 _StartSlideShow(); 1470 } 1471 1472 1473 void 1474 ShowImageWindow::_StartSlideShow() 1475 { 1476 _StopSlideShow(); 1477 1478 BMessage nextSlide(kMsgNextSlide); 1479 fSlideShowRunner = new BMessageRunner(this, &nextSlide, fSlideShowDelay); 1480 } 1481 1482 1483 void 1484 ShowImageWindow::_StopSlideShow() 1485 { 1486 if (fSlideShowRunner != NULL) { 1487 delete fSlideShowRunner; 1488 fSlideShowRunner = NULL; 1489 } 1490 } 1491 1492 1493 void 1494 ShowImageWindow::_UpdateRatingMenu() 1495 { 1496 BFile file(&fNavigator.CurrentRef(), B_READ_ONLY); 1497 if (file.InitCheck() != B_OK) 1498 return; 1499 int32 rating; 1500 ssize_t size = sizeof(rating); 1501 if (file.ReadAttr("Media:Rating", B_INT32_TYPE, 0, &rating, size) != size) 1502 rating = 0; 1503 // TODO: Finding the correct item could be more robust, like by looking 1504 // at the message of each item. 1505 for (int32 i = 1; i <= 10; i++) { 1506 BMenuItem* item = fRatingMenu->ItemAt(i - 1); 1507 if (item == NULL) 1508 break; 1509 item->SetMarked(i == rating); 1510 } 1511 } 1512 1513 1514 void 1515 ShowImageWindow::_SetToolBarVisible(bool visible, bool animate) 1516 { 1517 if (visible == !fToolBarView->IsHidden()) 1518 return; 1519 1520 float diff = fToolBarView->Bounds().Height() + 2; 1521 if (!visible) 1522 diff = -diff; 1523 else 1524 fToolBarView->Show(); 1525 1526 if (animate) { 1527 // Slide the controls into view. We do this with messages in order 1528 // not to block the window thread. 1529 const float kAnimationOffsets[] = { 0.05, 0.2, 0.5, 0.2, 0.05 }; 1530 const int32 steps = sizeof(kAnimationOffsets) / sizeof(float); 1531 float originalY = fToolBarView->Frame().top; 1532 for (int32 i = 0; i < steps; i++) { 1533 BMessage message(kMsgSlideToolBar); 1534 message.AddFloat("offset", floorf(diff * kAnimationOffsets[i])); 1535 PostMessage(&message, this); 1536 } 1537 BMessage finalMessage(kMsgFinishSlidingToolBar); 1538 finalMessage.AddFloat("offset", originalY + diff); 1539 finalMessage.AddBool("show", visible); 1540 PostMessage(&finalMessage, this); 1541 } else { 1542 fScrollView->ResizeBy(0, -diff); 1543 fScrollView->MoveBy(0, diff); 1544 fVerticalScrollBar->ResizeBy(0, -diff); 1545 fVerticalScrollBar->MoveBy(0, diff); 1546 fToolBarView->MoveBy(0, diff); 1547 if (!visible) 1548 fToolBarView->Hide(); 1549 } 1550 } 1551 1552 1553 bool 1554 ShowImageWindow::QuitRequested() 1555 { 1556 if (fSavePanel) { 1557 // Don't allow this window to be closed if a save panel is open 1558 return false; 1559 } 1560 1561 if (!_ClosePrompt()) 1562 return false; 1563 1564 ShowImageSettings* settings = my_app->Settings(); 1565 if (settings->Lock()) { 1566 if (fFullScreen) 1567 settings->SetRect("WindowFrame", fWindowFrame); 1568 else 1569 settings->SetRect("WindowFrame", Frame()); 1570 settings->Unlock(); 1571 } 1572 1573 be_app->PostMessage(MSG_WINDOW_HAS_QUIT); 1574 1575 return true; 1576 } 1577