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