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