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_TRANSLATE_CONTEXT 126 #define B_TRANSLATE_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_WITH_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_WITH_CONTEXT("Could not load image! Either the " 1106 "file or an image translator for it does not exist.", 1107 "LoadAlerts"), 1108 B_TRANSLATE_WITH_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_TRANSLATE_CONTEXT 1190 #define B_TRANSLATE_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 fImageView->FitToBounds(); 1298 } 1299 1300 1301 void 1302 ShowImageWindow::_ApplySettings() 1303 { 1304 ShowImageSettings* settings = my_app->Settings(); 1305 1306 if (settings->Lock()) { 1307 fShowCaption = settings->GetBool("ShowCaption", fShowCaption); 1308 fPrintOptions.SetBounds(BRect(0, 0, 1023, 767)); 1309 1310 fSlideShowDelay = settings->GetTime("SlideShowDelay", fSlideShowDelay); 1311 1312 fPrintOptions.SetOption((enum PrintOptions::Option)settings->GetInt32( 1313 "PO:Option", fPrintOptions.Option())); 1314 fPrintOptions.SetZoomFactor( 1315 settings->GetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor())); 1316 fPrintOptions.SetDPI(settings->GetFloat("PO:DPI", fPrintOptions.DPI())); 1317 fPrintOptions.SetWidth( 1318 settings->GetFloat("PO:Width", fPrintOptions.Width())); 1319 fPrintOptions.SetHeight( 1320 settings->GetFloat("PO:Height", fPrintOptions.Height())); 1321 1322 fShowToolBar = settings->GetBool("ShowToolBar", fShowToolBar); 1323 1324 settings->Unlock(); 1325 } 1326 } 1327 1328 1329 void 1330 ShowImageWindow::_SavePrintOptions() 1331 { 1332 ShowImageSettings* settings = my_app->Settings(); 1333 1334 if (settings->Lock()) { 1335 settings->SetInt32("PO:Option", fPrintOptions.Option()); 1336 settings->SetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor()); 1337 settings->SetFloat("PO:DPI", fPrintOptions.DPI()); 1338 settings->SetFloat("PO:Width", fPrintOptions.Width()); 1339 settings->SetFloat("PO:Height", fPrintOptions.Height()); 1340 settings->Unlock(); 1341 } 1342 } 1343 1344 1345 bool 1346 ShowImageWindow::_PageSetup() 1347 { 1348 BPrintJob printJob(fImageView->Image()->name); 1349 if (fPrintSettings != NULL) 1350 printJob.SetSettings(new BMessage(*fPrintSettings)); 1351 1352 status_t status = printJob.ConfigPage(); 1353 if (status == B_OK) { 1354 delete fPrintSettings; 1355 fPrintSettings = printJob.Settings(); 1356 } 1357 1358 return status == B_OK; 1359 } 1360 1361 1362 void 1363 ShowImageWindow::_PrepareForPrint() 1364 { 1365 if (fPrintSettings == NULL) { 1366 BPrintJob printJob(fImageView->Image()->name); 1367 if (printJob.ConfigJob() == B_OK) 1368 fPrintSettings = printJob.Settings(); 1369 } 1370 1371 fPrintOptions.SetBounds(fImageView->Bitmap()->Bounds()); 1372 fPrintOptions.SetWidth(fImageView->Bitmap()->Bounds().Width() + 1); 1373 1374 new PrintOptionsWindow(BPoint(Frame().left + 30, Frame().top + 50), 1375 &fPrintOptions, this); 1376 } 1377 1378 1379 void 1380 ShowImageWindow::_Print(BMessage* msg) 1381 { 1382 status_t st; 1383 if (msg->FindInt32("status", &st) != B_OK || st != B_OK) 1384 return; 1385 1386 _SavePrintOptions(); 1387 1388 BPrintJob printJob(fImageView->Image()->name); 1389 if (fPrintSettings) 1390 printJob.SetSettings(new BMessage(*fPrintSettings)); 1391 1392 if (printJob.ConfigJob() == B_OK) { 1393 delete fPrintSettings; 1394 fPrintSettings = printJob.Settings(); 1395 1396 // first/lastPage is unused for now 1397 int32 firstPage = printJob.FirstPage(); 1398 int32 lastPage = printJob.LastPage(); 1399 BRect printableRect = printJob.PrintableRect(); 1400 1401 if (firstPage < 1) 1402 firstPage = 1; 1403 if (lastPage < firstPage) 1404 lastPage = firstPage; 1405 1406 BBitmap* bitmap = fImageView->Bitmap(); 1407 float imageWidth = bitmap->Bounds().Width() + 1.0; 1408 float imageHeight = bitmap->Bounds().Height() + 1.0; 1409 1410 float width; 1411 switch (fPrintOptions.Option()) { 1412 case PrintOptions::kFitToPage: { 1413 float w1 = printableRect.Width()+1; 1414 float w2 = imageWidth * (printableRect.Height() + 1) 1415 / imageHeight; 1416 if (w2 < w1) 1417 width = w2; 1418 else 1419 width = w1; 1420 } break; 1421 case PrintOptions::kZoomFactor: 1422 width = imageWidth * fPrintOptions.ZoomFactor(); 1423 break; 1424 case PrintOptions::kDPI: 1425 width = imageWidth * 72.0 / fPrintOptions.DPI(); 1426 break; 1427 case PrintOptions::kWidth: 1428 case PrintOptions::kHeight: 1429 width = fPrintOptions.Width(); 1430 break; 1431 1432 default: 1433 // keep compiler silent; should not reach here 1434 width = imageWidth; 1435 } 1436 1437 // TODO: eventually print large images on several pages 1438 printJob.BeginJob(); 1439 fImageView->SetScale(width / imageWidth); 1440 // coordinates are relative to printable rectangle 1441 BRect bounds(bitmap->Bounds()); 1442 printJob.DrawView(fImageView, bounds, BPoint(0, 0)); 1443 fImageView->SetScale(1.0); 1444 printJob.SpoolPage(); 1445 printJob.CommitJob(); 1446 } 1447 } 1448 1449 1450 void 1451 ShowImageWindow::_SetSlideShowDelay(bigtime_t delay) 1452 { 1453 if (fSlideShowDelay == delay) 1454 return; 1455 1456 fSlideShowDelay = delay; 1457 1458 ShowImageSettings* settings = my_app->Settings(); 1459 if (settings->Lock()) { 1460 settings->SetTime("SlideShowDelay", fSlideShowDelay); 1461 settings->Unlock(); 1462 } 1463 1464 if (fSlideShowRunner != NULL) 1465 _StartSlideShow(); 1466 } 1467 1468 1469 void 1470 ShowImageWindow::_StartSlideShow() 1471 { 1472 _StopSlideShow(); 1473 1474 BMessage nextSlide(kMsgNextSlide); 1475 fSlideShowRunner = new BMessageRunner(this, &nextSlide, fSlideShowDelay); 1476 } 1477 1478 1479 void 1480 ShowImageWindow::_StopSlideShow() 1481 { 1482 if (fSlideShowRunner != NULL) { 1483 delete fSlideShowRunner; 1484 fSlideShowRunner = NULL; 1485 } 1486 } 1487 1488 1489 void 1490 ShowImageWindow::_UpdateRatingMenu() 1491 { 1492 BFile file(&fNavigator.CurrentRef(), B_READ_ONLY); 1493 if (file.InitCheck() != B_OK) 1494 return; 1495 int32 rating; 1496 ssize_t size = sizeof(rating); 1497 if (file.ReadAttr("Media:Rating", B_INT32_TYPE, 0, &rating, size) != size) 1498 rating = 0; 1499 // TODO: Finding the correct item could be more robust, like by looking 1500 // at the message of each item. 1501 for (int32 i = 1; i <= 10; i++) { 1502 BMenuItem* item = fRatingMenu->ItemAt(i - 1); 1503 if (item == NULL) 1504 break; 1505 item->SetMarked(i == rating); 1506 } 1507 } 1508 1509 1510 void 1511 ShowImageWindow::_SetToolBarVisible(bool visible, bool animate) 1512 { 1513 if (visible == !fToolBarView->IsHidden()) 1514 return; 1515 1516 float diff = fToolBarView->Bounds().Height() + 2; 1517 if (!visible) 1518 diff = -diff; 1519 else 1520 fToolBarView->Show(); 1521 1522 if (animate) { 1523 // Slide the controls into view. We do this with messages in order 1524 // not to block the window thread. 1525 const float kAnimationOffsets[] = { 0.05, 0.2, 0.5, 0.2, 0.05 }; 1526 const int32 steps = sizeof(kAnimationOffsets) / sizeof(float); 1527 float originalY = fToolBarView->Frame().top; 1528 for (int32 i = 0; i < steps; i++) { 1529 BMessage message(kMsgSlideToolBar); 1530 message.AddFloat("offset", floorf(diff * kAnimationOffsets[i])); 1531 PostMessage(&message, this); 1532 } 1533 BMessage finalMessage(kMsgFinishSlidingToolBar); 1534 finalMessage.AddFloat("offset", originalY + diff); 1535 finalMessage.AddBool("show", visible); 1536 PostMessage(&finalMessage, this); 1537 } else { 1538 fScrollView->ResizeBy(0, -diff); 1539 fScrollView->MoveBy(0, diff); 1540 fVerticalScrollBar->ResizeBy(0, -diff); 1541 fVerticalScrollBar->MoveBy(0, diff); 1542 if (!visible) 1543 fToolBarView->Hide(); 1544 } 1545 } 1546 1547 1548 bool 1549 ShowImageWindow::QuitRequested() 1550 { 1551 if (fSavePanel) { 1552 // Don't allow this window to be closed if a save panel is open 1553 return false; 1554 } 1555 1556 if (!_ClosePrompt()) 1557 return false; 1558 1559 ShowImageSettings* settings = my_app->Settings(); 1560 if (settings->Lock()) { 1561 if (fFullScreen) 1562 settings->SetRect("WindowFrame", fWindowFrame); 1563 else 1564 settings->SetRect("WindowFrame", Frame()); 1565 settings->Unlock(); 1566 } 1567 1568 be_app->PostMessage(MSG_WINDOW_HAS_QUIT); 1569 1570 return true; 1571 } 1572