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