1 /* 2 * Copyright 2003-2006, 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 */ 14 15 16 #include "BackgroundImage.h" 17 #include "EntryMenuItem.h" 18 #include "ResizerWindow.h" 19 #include "ShowImageApp.h" 20 #include "ShowImageConstants.h" 21 #include "ShowImageStatusView.h" 22 #include "ShowImageView.h" 23 #include "ShowImageWindow.h" 24 25 #include <Alert.h> 26 #include <Application.h> 27 #include <Bitmap.h> 28 #include <BitmapStream.h> 29 #include <Clipboard.h> 30 #include <Entry.h> 31 #include <File.h> 32 #include <Menu.h> 33 #include <MenuBar.h> 34 #include <MenuItem.h> 35 #include <Path.h> 36 #include <PrintJob.h> 37 #include <Roster.h> 38 #include <Screen.h> 39 #include <ScrollView.h> 40 #include <SupportDefs.h> 41 #include <TranslationUtils.h> 42 #include <TranslatorRoster.h> 43 44 #include <new> 45 #include <stdio.h> 46 47 48 RecentDocumentsMenu::RecentDocumentsMenu(const char *title, menu_layout layout) 49 : BMenu(title, layout) 50 { 51 } 52 53 54 bool 55 RecentDocumentsMenu::AddDynamicItem(add_state addState) 56 { 57 if (addState != B_INITIAL_ADD) 58 return false; 59 60 BMenuItem *item; 61 BMessage list, *msg; 62 entry_ref ref; 63 char name[B_FILE_NAME_LENGTH]; 64 65 while ((item = RemoveItem((int32)0)) != NULL) { 66 delete item; 67 } 68 69 be_roster->GetRecentDocuments(&list, 20, NULL, kApplicationSignature); 70 for (int i = 0; list.FindRef("refs", i, &ref) == B_OK; i++) { 71 BEntry entry(&ref); 72 if (entry.Exists() && entry.GetName(name) == B_OK) { 73 msg = new BMessage(B_REFS_RECEIVED); 74 msg->AddRef("refs", &ref); 75 item = new EntryMenuItem(&ref, name, msg, 0, 0); 76 AddItem(item); 77 item->SetTarget(be_app, NULL); 78 } 79 } 80 81 return false; 82 } 83 84 85 // #pragma mark - 86 87 88 ShowImageWindow::ShowImageWindow(const entry_ref *ref, 89 const BMessenger& trackerMessenger) 90 : BWindow(BRect(5, 24, 250, 100), "", B_DOCUMENT_WINDOW, 0) 91 { 92 fSavePanel = NULL; 93 fModified = false; 94 fFullScreen = false; 95 fShowCaption = true; 96 fPrintSettings = NULL; 97 fImageView = NULL; 98 fSlideShowDelay = NULL; 99 fResizerWindowMessenger = NULL; 100 fHeight = fWidth = 0; 101 102 LoadSettings(); 103 104 // create menu bar 105 fBar = new BMenuBar(BRect(0, 0, Bounds().right, 1), "menu_bar"); 106 AddMenus(fBar); 107 AddChild(fBar); 108 109 BRect viewFrame = Bounds(); 110 viewFrame.top = fBar->Bounds().Height() + 1; 111 viewFrame.right -= B_V_SCROLL_BAR_WIDTH; 112 viewFrame.bottom -= B_H_SCROLL_BAR_HEIGHT; 113 114 // create the image view 115 fImageView = new ShowImageView(viewFrame, "image_view", B_FOLLOW_ALL, 116 B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE | B_PULSE_NEEDED); 117 // wrap a scroll view around the view 118 BScrollView *scrollView = new BScrollView("image_scroller", fImageView, 119 B_FOLLOW_ALL, 0, false, false, B_PLAIN_BORDER); 120 AddChild(scrollView); 121 122 const int32 kstatusWidth = 190; 123 BRect rect; 124 rect = Bounds(); 125 rect.top = viewFrame.bottom + 1; 126 rect.left = viewFrame.left + kstatusWidth; 127 rect.right = viewFrame.right + 1; 128 rect.bottom += 1; 129 BScrollBar *horizontalScrollBar = new BScrollBar(rect, "hscroll", 130 fImageView, 0, 150, B_HORIZONTAL); 131 AddChild(horizontalScrollBar); 132 133 rect.left = 0; 134 rect.right = kstatusWidth - 1; 135 rect.bottom -= 1; 136 fStatusView = new ShowImageStatusView(rect, "status_view", B_FOLLOW_BOTTOM, 137 B_WILL_DRAW); 138 AddChild(fStatusView); 139 140 rect = Bounds(); 141 rect.top = viewFrame.top - 1; 142 rect.left = viewFrame.right + 1; 143 rect.bottom = viewFrame.bottom + 1; 144 rect.right += 1; 145 BScrollBar *verticalScrollBar = new BScrollBar(rect, "vscroll", fImageView, 146 0, 150, B_VERTICAL); 147 AddChild(verticalScrollBar); 148 149 SetSizeLimits(250, 100000, 100, 100000); 150 151 // finish creating the window 152 fImageView->SetImage(ref); 153 fImageView->SetTrackerMessenger(trackerMessenger); 154 155 if (InitCheck() != B_OK) { 156 BAlert* alert; 157 alert = new BAlert("ShowImage", 158 "Could not load image! Either the file or an image translator for it does not exist.", 159 "OK", NULL, NULL, 160 B_WIDTH_AS_USUAL, B_INFO_ALERT); 161 alert->Go(); 162 163 // quit if file could not be opened 164 Quit(); 165 return; 166 } 167 168 // add View menu here so it can access ShowImageView methods 169 BMenu* menu = new BMenu("View"); 170 BuildViewMenu(menu, false); 171 fBar->AddItem(menu); 172 MarkMenuItem(fBar, MSG_DITHER_IMAGE, fImageView->GetDither()); 173 UpdateTitle(); 174 175 SetPulseRate(100000); 176 // every 1/10 second; ShowImageView needs it for marching ants 177 178 WindowRedimension(fImageView->GetBitmap()); 179 fImageView->MakeFocus(true); // to receive KeyDown messages 180 Show(); 181 182 // Tell application object to query the clipboard 183 // and tell this window if it contains interesting data or not 184 be_app_messenger.SendMessage(B_CLIPBOARD_CHANGED); 185 } 186 187 188 ShowImageWindow::~ShowImageWindow() 189 { 190 } 191 192 193 status_t 194 ShowImageWindow::InitCheck() 195 { 196 if (!fImageView || fImageView->GetBitmap() == NULL) 197 return B_ERROR; 198 199 return B_OK; 200 } 201 202 203 void 204 ShowImageWindow::UpdateTitle() 205 { 206 BString path; 207 fImageView->GetPath(&path); 208 SetTitle(path.String()); 209 } 210 211 void 212 ShowImageWindow::BuildContextMenu(BMenu *menu) 213 { 214 BuildViewMenu(menu, true); 215 } 216 217 void 218 ShowImageWindow::BuildViewMenu(BMenu *menu, bool popupMenu) 219 { 220 AddItemMenu(menu, "Slide Show", MSG_SLIDE_SHOW, 0, 0, 'W', true); 221 MarkMenuItem(menu, MSG_SLIDE_SHOW, fImageView->SlideShowStarted()); 222 BMenu* delayMenu = new BMenu("Slide Delay"); 223 if (fSlideShowDelay == NULL) 224 fSlideShowDelay = delayMenu; 225 226 delayMenu->SetRadioMode(true); 227 // Note: ShowImage loads images in window thread so it becomes unresponsive if 228 // slide show delay is too short! (Especially if loading the image takes as long as 229 // or longer than the slide show delay). Should load in background thread! 230 AddDelayItem(delayMenu, "Three Seconds", 3); 231 AddDelayItem(delayMenu, "Four Second", 4); 232 AddDelayItem(delayMenu, "Five Seconds", 5); 233 AddDelayItem(delayMenu, "Six Seconds", 6); 234 AddDelayItem(delayMenu, "Seven Seconds", 7); 235 AddDelayItem(delayMenu, "Eight Seconds", 8); 236 AddDelayItem(delayMenu, "Nine Seconds", 9); 237 AddDelayItem(delayMenu, "Ten Seconds", 10); 238 AddDelayItem(delayMenu, "Twenty Seconds", 20); 239 menu->AddItem(delayMenu); 240 241 menu->AddSeparatorItem(); 242 243 AddItemMenu(menu, "Original Size", MSG_ORIGINAL_SIZE, 0, 0, 'W', true); 244 AddItemMenu(menu, "Zoom In", MSG_ZOOM_IN, '+', 0, 'W', true); 245 AddItemMenu(menu, "Zoom Out", MSG_ZOOM_OUT, '-', 0, 'W', true); 246 247 menu->AddSeparatorItem(); 248 249 AddItemMenu(menu, "High-Quality Zooming", MSG_SCALE_BILINEAR, 0, 0, 'W', true); 250 251 menu->AddSeparatorItem(); 252 253 AddItemMenu(menu, "Shrink to Window", MSG_SHRINK_TO_WINDOW, 0, 0, 'W', true); 254 AddItemMenu(menu, "Zoom to Window", MSG_ZOOM_TO_WINDOW, 0, 0, 'W', true); 255 256 menu->AddSeparatorItem(); 257 258 AddItemMenu(menu, "Full Screen", MSG_FULL_SCREEN, 'F', 0, 'W', true); 259 MarkMenuItem(menu, MSG_FULL_SCREEN, fFullScreen); 260 261 AddShortcut(B_ENTER, 0, new BMessage(MSG_FULL_SCREEN)); 262 263 AddItemMenu(menu, "Show Caption in Full Screen Mode", MSG_SHOW_CAPTION, 0, 0, 'W', true); 264 MarkMenuItem(menu, MSG_SHOW_CAPTION, fShowCaption); 265 266 MarkMenuItem(menu, MSG_SCALE_BILINEAR, fImageView->GetScaleBilinear()); 267 268 bool shrink, zoom, enabled; 269 270 shrink = fImageView->GetShrinkToBounds(); 271 zoom = fImageView->GetZoomToBounds(); 272 MarkMenuItem(menu, MSG_SHRINK_TO_WINDOW, shrink); 273 MarkMenuItem(menu, MSG_ZOOM_TO_WINDOW, zoom); 274 275 enabled = !(shrink || zoom); 276 EnableMenuItem(menu, MSG_ORIGINAL_SIZE, enabled); 277 EnableMenuItem(menu, MSG_ZOOM_IN, enabled); 278 EnableMenuItem(menu, MSG_ZOOM_OUT, enabled); 279 280 if (popupMenu) { 281 menu->AddSeparatorItem(); 282 283 AddItemMenu(menu, "As Desktop Background", MSG_DESKTOP_BACKGROUND, 0, 0, 'W', true); 284 } 285 } 286 287 288 void 289 ShowImageWindow::AddMenus(BMenuBar *bar) 290 { 291 BMenu *menu = new BMenu("File"); 292 fOpenMenu = new RecentDocumentsMenu("Open"); 293 menu->AddItem(fOpenMenu); 294 fOpenMenu->Superitem()->SetTrigger('O'); 295 fOpenMenu->Superitem()->SetMessage(new BMessage(MSG_FILE_OPEN)); 296 fOpenMenu->Superitem()->SetTarget(be_app); 297 fOpenMenu->Superitem()->SetShortcut('O', 0); 298 menu->AddSeparatorItem(); 299 BMenu *pmenuSaveAs = new BMenu("Save As" B_UTF8_ELLIPSIS, B_ITEMS_IN_COLUMN); 300 BTranslationUtils::AddTranslationItems(pmenuSaveAs, B_TRANSLATOR_BITMAP); 301 // Fill Save As submenu with all types that can be converted 302 // to from the Be bitmap image format 303 menu->AddItem(pmenuSaveAs); 304 AddItemMenu(menu, "Close", B_QUIT_REQUESTED, 'W', 0, 'W', true); 305 menu->AddSeparatorItem(); 306 AddItemMenu(menu, "Page Setup" B_UTF8_ELLIPSIS, MSG_PAGE_SETUP, 0, 0, 'W', true); 307 AddItemMenu(menu, "Print" B_UTF8_ELLIPSIS, MSG_PREPARE_PRINT, 0, 0, 'W', true); 308 menu->AddSeparatorItem(); 309 AddItemMenu(menu, "About ShowImage" B_UTF8_ELLIPSIS, B_ABOUT_REQUESTED, 0, 0, 'A', true); 310 menu->AddSeparatorItem(); 311 AddItemMenu(menu, "Quit", B_QUIT_REQUESTED, 'Q', 0, 'A', true); 312 bar->AddItem(menu); 313 314 menu = new BMenu("Edit"); 315 AddItemMenu(menu, "Undo", B_UNDO, 'Z', 0, 'W', false); 316 menu->AddSeparatorItem(); 317 AddItemMenu(menu, "Cut", B_CUT, 'X', 0, 'W', false); 318 AddItemMenu(menu, "Copy", B_COPY, 'C', 0, 'W', false); 319 AddItemMenu(menu, "Paste", B_PASTE, 'V', 0, 'W', false); 320 AddItemMenu(menu, "Clear", MSG_CLEAR_SELECT, 0, 0, 'W', false); 321 menu->AddSeparatorItem(); 322 AddItemMenu(menu, "Select All", MSG_SELECT_ALL, 'A', 0, 'W', true); 323 bar->AddItem(menu); 324 325 menu = fBrowseMenu = new BMenu("Browse"); 326 AddItemMenu(menu, "First Page", MSG_PAGE_FIRST, B_LEFT_ARROW, B_SHIFT_KEY, 'W', true); 327 AddItemMenu(menu, "Last Page", MSG_PAGE_LAST, B_RIGHT_ARROW, B_SHIFT_KEY, 'W', true); 328 AddItemMenu(menu, "Previous Page", MSG_PAGE_PREV, B_LEFT_ARROW, 0, 'W', true); 329 AddItemMenu(menu, "Next Page", MSG_PAGE_NEXT, B_RIGHT_ARROW, 0, 'W', true); 330 fGoToPageMenu = new BMenu("Go to Page"); 331 fGoToPageMenu->SetRadioMode(true); 332 menu->AddItem(fGoToPageMenu); 333 menu->AddSeparatorItem(); 334 AddItemMenu(menu, "Previous File", MSG_FILE_PREV, B_UP_ARROW, 0, 'W', true); 335 AddItemMenu(menu, "Next File", MSG_FILE_NEXT, B_DOWN_ARROW, 0, 'W', true); 336 bar->AddItem(menu); 337 338 menu = new BMenu("Image"); 339 AddItemMenu(menu, "Rotate Counterclockwise", MSG_ROTATE_270, '[', 0, 'W', true); 340 AddItemMenu(menu, "Rotate Clockwise", MSG_ROTATE_90, ']', 0, 'W', true); 341 menu->AddSeparatorItem(); 342 AddItemMenu(menu, "Flip Left to Right", MSG_FLIP_LEFT_TO_RIGHT, 0, 0, 'W', true); 343 AddItemMenu(menu, "Flip Top to Bottom", MSG_FLIP_TOP_TO_BOTTOM, 0, 0, 'W', true); 344 menu->AddSeparatorItem(); 345 AddItemMenu(menu, "Invert Colors", MSG_INVERT, 0, 0, 'W', true); 346 menu->AddSeparatorItem(); 347 fResizeItem = AddItemMenu(menu, "Resize" B_UTF8_ELLIPSIS, MSG_OPEN_RESIZER_WINDOW, 0, 0, 'W', true); 348 bar->AddItem(menu); 349 menu->AddSeparatorItem(); 350 AddItemMenu(menu, "Use as Desktop Background", MSG_DESKTOP_BACKGROUND, 0, 0, 'W', true); 351 } 352 353 354 BMenuItem * 355 ShowImageWindow::AddItemMenu(BMenu *menu, char *caption, uint32 command, 356 char shortcut, uint32 modifier, char target, bool enabled) 357 { 358 BMenuItem* item = new BMenuItem(caption, new BMessage(command), shortcut, modifier); 359 360 if (target == 'A') 361 item->SetTarget(be_app); 362 else 363 item->SetTarget(this); 364 365 item->SetEnabled(enabled); 366 menu->AddItem(item); 367 368 return item; 369 } 370 371 372 BMenuItem* 373 ShowImageWindow::AddDelayItem(BMenu *menu, char *caption, float value) 374 { 375 BMessage* message = new BMessage(MSG_SLIDE_SHOW_DELAY); 376 message->AddFloat("value", value); 377 378 BMenuItem* item = new BMenuItem(caption, message, 0); 379 item->SetTarget(this); 380 381 bool marked = fImageView->GetSlideShowDelay() == value; 382 if (marked) 383 item->SetMarked(true); 384 385 menu->AddItem(item); 386 return item; 387 } 388 389 390 void 391 ShowImageWindow::WindowRedimension(BBitmap *pbitmap) 392 { 393 BScreen screen; 394 if (!screen.IsValid()) 395 return; 396 397 BRect r(pbitmap->Bounds()); 398 const float windowBorderWidth = 5; 399 const float windowBorderHeight = 5; 400 401 float width = r.Width() + 2 * PEN_SIZE + B_V_SCROLL_BAR_WIDTH; 402 float height = r.Height() + 2 * PEN_SIZE + 1 + fBar->Frame().Height() + B_H_SCROLL_BAR_HEIGHT; 403 404 // dimensions so that window does not reach outside of screen 405 float maxWidth = screen.Frame().Width() + 1 - windowBorderWidth - Frame().left; 406 float maxHeight = screen.Frame().Height() + 1 - windowBorderHeight - Frame().top; 407 408 // We have to check size limits manually, otherwise 409 // menu bar will be too short for small images. 410 411 float minW, maxW, minH, maxH; 412 GetSizeLimits(&minW, &maxW, &minH, &maxH); 413 if (maxWidth > maxW) 414 maxWidth = maxW; 415 if (maxHeight > maxH) 416 maxHeight = maxH; 417 if (width < minW) 418 width = minW; 419 if (height < minH) 420 height = minH; 421 422 if (width > maxWidth) 423 width = maxWidth; 424 if (height > maxHeight) 425 height = maxHeight; 426 427 ResizeTo(width, height); 428 } 429 430 431 void 432 ShowImageWindow::FrameResized(float width, float height) 433 { 434 } 435 436 437 bool 438 ShowImageWindow::ToggleMenuItem(uint32 what) 439 { 440 BMenuItem *item; 441 bool marked = false; 442 item = fBar->FindItem(what); 443 if (item != NULL) { 444 marked = !item->IsMarked(); 445 item->SetMarked(marked); 446 } 447 return marked; 448 } 449 450 451 void 452 ShowImageWindow::EnableMenuItem(BMenu *menu, uint32 what, bool enable) 453 { 454 BMenuItem* item; 455 item = menu->FindItem(what); 456 if (item && item->IsEnabled() != enable) { 457 item->SetEnabled(enable); 458 } 459 } 460 461 462 void 463 ShowImageWindow::MarkMenuItem(BMenu *menu, uint32 what, bool marked) 464 { 465 BMenuItem* item; 466 item = menu->FindItem(what); 467 if (item && item->IsMarked() != marked) { 468 item->SetMarked(marked); 469 } 470 } 471 472 473 void 474 ShowImageWindow::MarkSlideShowDelay(float value) 475 { 476 const int32 n = fSlideShowDelay->CountItems(); 477 float v; 478 for (int32 i = 0; i < n; i ++) { 479 BMenuItem* item = fSlideShowDelay->ItemAt(i); 480 if (item) { 481 if (item->Message()->FindFloat("value", &v) == B_OK && v == value) { 482 if (!item->IsMarked()) { 483 item->SetMarked(true); 484 } 485 return; 486 } 487 } 488 } 489 } 490 491 492 void 493 ShowImageWindow::ResizeToWindow(bool shrink, uint32 what) 494 { 495 bool enabled; 496 enabled = ToggleMenuItem(what); 497 if (shrink) { 498 fImageView->SetShrinkToBounds(enabled); 499 } else { 500 fImageView->SetZoomToBounds(enabled); 501 } 502 enabled = !(fImageView->GetShrinkToBounds() || fImageView->GetZoomToBounds()); 503 EnableMenuItem(fBar, MSG_ORIGINAL_SIZE, enabled); 504 EnableMenuItem(fBar, MSG_ZOOM_IN, enabled); 505 EnableMenuItem(fBar, MSG_ZOOM_OUT, enabled); 506 } 507 508 509 void 510 ShowImageWindow::MessageReceived(BMessage *message) 511 { 512 switch (message->what) { 513 case MSG_MODIFIED: 514 // If image has been modified due to a Cut or Paste 515 fModified = true; 516 break; 517 518 case MSG_OUTPUT_TYPE: 519 // User clicked Save As then choose an output format 520 if (!fSavePanel) 521 // If user doesn't already have a save panel open 522 SaveAs(message); 523 break; 524 525 case MSG_SAVE_PANEL: 526 // User specified where to save the output image 527 SaveToFile(message); 528 break; 529 530 case B_CANCEL: 531 delete fSavePanel; 532 fSavePanel = NULL; 533 break; 534 535 case MSG_UPDATE_STATUS: 536 { 537 int32 pages, curPage; 538 pages = fImageView->PageCount(); 539 curPage = fImageView->CurrentPage(); 540 541 bool benable = (pages > 1) ? true : false; 542 EnableMenuItem(fBar, MSG_PAGE_FIRST, benable); 543 EnableMenuItem(fBar, MSG_PAGE_LAST, benable); 544 EnableMenuItem(fBar, MSG_PAGE_NEXT, benable); 545 EnableMenuItem(fBar, MSG_PAGE_PREV, benable); 546 547 EnableMenuItem(fBar, MSG_FILE_NEXT, fImageView->HasNextFile()); 548 EnableMenuItem(fBar, MSG_FILE_PREV, fImageView->HasPrevFile()); 549 550 if (fGoToPageMenu->CountItems() != pages) { 551 // Only rebuild the submenu if the number of 552 // pages is different 553 554 while (fGoToPageMenu->CountItems() > 0) 555 // Remove all page numbers 556 delete fGoToPageMenu->RemoveItem(0L); 557 558 for (int32 i = 1; i <= pages; i++) { 559 // Fill Go To page submenu with an entry for each page 560 BMessage *pgomsg; 561 char shortcut = 0; 562 pgomsg = new BMessage(MSG_GOTO_PAGE); 563 pgomsg->AddInt32("page", i); 564 BString strCaption; 565 strCaption << i; 566 BMenuItem *item; 567 if (i < 10) { 568 shortcut = '0' + i; 569 } else if (i == 10) { 570 shortcut = '0'; 571 } 572 item = new BMenuItem(strCaption.String(), pgomsg, shortcut); 573 if (curPage == i) 574 item->SetMarked(true); 575 fGoToPageMenu->AddItem(item); 576 } 577 } else { 578 // Make sure the correct page is marked 579 BMenuItem *pcurItem; 580 pcurItem = fGoToPageMenu->ItemAt(curPage - 1); 581 if (!pcurItem->IsMarked()) { 582 pcurItem->SetMarked(true); 583 } 584 } 585 586 // Disable the Invert menu item if the bitmap color space 587 // is B_CMAP8. (B_CMAP8 is currently unsupported by the 588 // invert algorithm) 589 color_space colors = B_NO_COLOR_SPACE; 590 message->FindInt32("colors", reinterpret_cast<int32 *>(&colors)); 591 EnableMenuItem(fBar, MSG_INVERT, (colors != B_CMAP8)); 592 593 BString status; 594 bool messageProvidesSize = false; 595 if (message->FindInt32("width", &fWidth) >= B_OK 596 && message->FindInt32("height", &fHeight) >= B_OK) { 597 status << fWidth << "x" << fHeight; 598 messageProvidesSize = true; 599 } 600 601 BString str; 602 if (message->FindString("status", &str) == B_OK && str.Length() > 0) { 603 if (status.Length() > 0) 604 status << ", "; 605 status << str; 606 } 607 608 if (messageProvidesSize) { 609 UpdateResizerWindow(fWidth, fHeight); 610 } 611 612 fStatusView->SetText(status); 613 614 UpdateTitle(); 615 break; 616 } 617 618 case MSG_UPDATE_STATUS_TEXT: 619 { 620 BString status; 621 status << fWidth << "x" << fHeight; 622 BString str; 623 if (message->FindString("status", &str) == B_OK && str.Length() > 0) { 624 status << ", " << str; 625 fStatusView->SetText(status); 626 } 627 break; 628 } 629 630 case MSG_SELECTION: 631 { 632 // The view sends this message when a selection is 633 // made or the selection is cleared so that the window 634 // can update the state of the appropriate menu items 635 bool benable; 636 if (message->FindBool("has_selection", &benable) == B_OK) { 637 EnableMenuItem(fBar, B_CUT, benable); 638 EnableMenuItem(fBar, B_COPY, benable); 639 EnableMenuItem(fBar, MSG_CLEAR_SELECT, benable); 640 } 641 break; 642 } 643 644 case MSG_UNDO_STATE: 645 { 646 bool benable; 647 if (message->FindBool("can_undo", &benable) == B_OK) 648 EnableMenuItem(fBar, B_UNDO, benable); 649 break; 650 } 651 652 case MSG_CLIPBOARD_CHANGED: 653 { 654 // The app sends this message after it examines 655 // the clipboard in response to a B_CLIPBOARD_CHANGED 656 // message 657 bool bdata; 658 if (message->FindBool("data_available", &bdata) == B_OK) 659 EnableMenuItem(fBar, B_PASTE, bdata); 660 break; 661 } 662 663 case B_UNDO: 664 fImageView->Undo(); 665 break; 666 case B_CUT: 667 fImageView->Cut(); 668 break; 669 case B_COPY: 670 fImageView->CopySelectionToClipboard(); 671 break; 672 case B_PASTE: 673 fImageView->Paste(); 674 break; 675 case MSG_CLEAR_SELECT: 676 fImageView->ClearSelection(); 677 break; 678 case MSG_SELECT_ALL: 679 fImageView->SelectAll(); 680 break; 681 682 case MSG_PAGE_FIRST: 683 if (ClosePrompt()) 684 fImageView->FirstPage(); 685 break; 686 687 case MSG_PAGE_LAST: 688 if (ClosePrompt()) 689 fImageView->LastPage(); 690 break; 691 692 case MSG_PAGE_NEXT: 693 if (ClosePrompt()) 694 fImageView->NextPage(); 695 break; 696 697 case MSG_PAGE_PREV: 698 if (ClosePrompt()) 699 fImageView->PrevPage(); 700 break; 701 702 case MSG_GOTO_PAGE: 703 { 704 if (!ClosePrompt()) 705 break; 706 707 int32 curPage, newPage, pages; 708 if (message->FindInt32("page", &newPage) == B_OK) { 709 curPage = fImageView->CurrentPage(); 710 pages = fImageView->PageCount(); 711 712 if (newPage > 0 && newPage <= pages) { 713 BMenuItem *pcurItem, *pnewItem; 714 pcurItem = fGoToPageMenu->ItemAt(curPage - 1); 715 pnewItem = fGoToPageMenu->ItemAt(newPage - 1); 716 if (!pcurItem || !pnewItem) 717 break; 718 pcurItem->SetMarked(false); 719 pnewItem->SetMarked(true); 720 fImageView->GoToPage(newPage); 721 } 722 } 723 break; 724 } 725 726 case MSG_DITHER_IMAGE: 727 fImageView->SetDither(ToggleMenuItem(message->what)); 728 break; 729 730 case MSG_SHRINK_TO_WINDOW: 731 ResizeToWindow(true, message->what); 732 break; 733 case MSG_ZOOM_TO_WINDOW: 734 ResizeToWindow(false, message->what); 735 break; 736 737 case MSG_FILE_PREV: 738 if (ClosePrompt()) 739 fImageView->PrevFile(); 740 break; 741 742 case MSG_FILE_NEXT: 743 if (ClosePrompt()) 744 fImageView->NextFile(); 745 break; 746 747 case MSG_ROTATE_90: 748 fImageView->Rotate(90); 749 break; 750 case MSG_ROTATE_270: 751 fImageView->Rotate(270); 752 break; 753 case MSG_FLIP_LEFT_TO_RIGHT: 754 fImageView->Flip(true); 755 break; 756 case MSG_FLIP_TOP_TO_BOTTOM: 757 fImageView->Flip(false); 758 break; 759 case MSG_INVERT: 760 fImageView->Invert(); 761 break; 762 case MSG_SLIDE_SHOW: 763 { 764 BMenuItem *item; 765 item = fBar->FindItem(message->what); 766 if (!item) 767 break; 768 if (item->IsMarked()) { 769 item->SetMarked(false); 770 fResizeItem->SetEnabled(true); 771 fImageView->StopSlideShow(); 772 } else if (ClosePrompt()) { 773 item->SetMarked(true); 774 fResizeItem->SetEnabled(false); 775 fImageView->StartSlideShow(); 776 } 777 break; 778 } 779 780 case MSG_SLIDE_SHOW_DELAY: 781 { 782 float value; 783 if (message->FindFloat("value", &value) == B_OK) { 784 fImageView->SetSlideShowDelay(value); 785 // in case message is sent from popup menu 786 MarkSlideShowDelay(value); 787 } 788 break; 789 } 790 791 case MSG_FULL_SCREEN: 792 ToggleFullScreen(); 793 break; 794 case MSG_EXIT_FULL_SCREEN: 795 if (fFullScreen) 796 ToggleFullScreen(); 797 break; 798 case MSG_SHOW_CAPTION: 799 { 800 fShowCaption = ToggleMenuItem(message->what); 801 ShowImageSettings* settings = my_app->Settings(); 802 803 if (settings->Lock()) { 804 settings->SetBool("ShowCaption", fShowCaption); 805 settings->Unlock(); 806 } 807 if (fFullScreen) 808 fImageView->SetShowCaption(fShowCaption); 809 break; 810 } 811 812 case MSG_PAGE_SETUP: 813 PageSetup(); 814 break; 815 case MSG_PREPARE_PRINT: 816 PrepareForPrint(); 817 break; 818 case MSG_PRINT: 819 Print(message); 820 break; 821 822 case MSG_ZOOM_IN: 823 fImageView->ZoomIn(); 824 break; 825 case MSG_ZOOM_OUT: 826 fImageView->ZoomOut(); 827 break; 828 case MSG_ORIGINAL_SIZE: 829 fImageView->SetZoom(1.0); 830 break; 831 case MSG_SCALE_BILINEAR: 832 fImageView->SetScaleBilinear(ToggleMenuItem(message->what)); 833 break; 834 835 case MSG_OPEN_RESIZER_WINDOW: 836 if (fImageView->GetBitmap() != NULL) 837 { 838 BRect rect = fImageView->GetBitmap()->Bounds(); 839 OpenResizerWindow(rect.IntegerWidth()+1, rect.IntegerHeight()+1); 840 } 841 break; 842 case MSG_RESIZE: 843 { 844 int w = message->FindInt32("w"); 845 int h = message->FindInt32("h"); 846 fImageView->ResizeImage(w, h); 847 break; 848 } 849 case MSG_RESIZER_WINDOW_QUIT: 850 fResizerWindowMessenger = NULL; 851 break; 852 853 case MSG_DESKTOP_BACKGROUND: 854 { 855 BPath path; 856 if (path.SetTo(fImageView->Image()) == B_OK) { 857 BackgroundImage::SetDesktopImage(B_CURRENT_WORKSPACE, 858 path.Path(), BackgroundImage::kScaledToFit, 859 BPoint(0, 0), false); 860 } 861 break; 862 } 863 864 default: 865 BWindow::MessageReceived(message); 866 break; 867 } 868 } 869 870 871 void 872 ShowImageWindow::SaveAs(BMessage *message) 873 { 874 // Read the translator and output type the user chose 875 translator_id outTranslator; 876 uint32 outType; 877 if (message->FindInt32(TRANSLATOR_FLD, 878 reinterpret_cast<int32 *>(&outTranslator)) != B_OK 879 || message->FindInt32(TYPE_FLD, 880 reinterpret_cast<int32 *>(&outType)) != B_OK) 881 return; 882 883 // Add the chosen translator and output type to the 884 // message that the save panel will send back 885 BMessage *panelMsg = new BMessage(MSG_SAVE_PANEL); 886 panelMsg->AddInt32(TRANSLATOR_FLD, outTranslator); 887 panelMsg->AddInt32(TYPE_FLD, outType); 888 889 // Create save panel and show it 890 fSavePanel = new (std::nothrow) BFilePanel(B_SAVE_PANEL, 891 new BMessenger(this), NULL, 0, false, panelMsg); 892 if (!fSavePanel) 893 return; 894 895 fSavePanel->Window()->SetWorkspaces(B_CURRENT_WORKSPACE); 896 fSavePanel->Show(); 897 } 898 899 900 void 901 ShowImageWindow::SaveToFile(BMessage *message) 902 { 903 // Read in where the file should be saved 904 entry_ref dirRef; 905 if (message->FindRef("directory", &dirRef) != B_OK) 906 return; 907 908 const char *filename; 909 if (message->FindString("name", &filename) != B_OK) 910 return; 911 912 // Read in the translator and type to be used 913 // to save the output image 914 translator_id outTranslator; 915 uint32 outType; 916 if (message->FindInt32(TRANSLATOR_FLD, 917 reinterpret_cast<int32 *>(&outTranslator)) != B_OK 918 || message->FindInt32(TYPE_FLD, 919 reinterpret_cast<int32 *>(&outType)) != B_OK) 920 return; 921 922 // Find the translator_format information needed to 923 // write a MIME attribute for the image file 924 BTranslatorRoster *roster = BTranslatorRoster::Default(); 925 const translation_format *outFormat = NULL; 926 int32 outCount = 0; 927 if (roster->GetOutputFormats(outTranslator, &outFormat, &outCount) != B_OK 928 || outCount < 1) 929 return; 930 931 int32 i; 932 for (i = 0; i < outCount; i++) { 933 if (outFormat[i].group == B_TRANSLATOR_BITMAP && outFormat[i].type == outType) 934 break; 935 } 936 if (i == outCount) 937 return; 938 939 // Write out the image file 940 BDirectory dir(&dirRef); 941 fImageView->SaveToFile(&dir, filename, NULL, &outFormat[i]); 942 } 943 944 945 bool 946 ShowImageWindow::ClosePrompt() 947 { 948 if (!fModified) 949 return true; 950 951 int32 page, count; 952 count = fImageView->PageCount(); 953 page = fImageView->CurrentPage(); 954 BString prompt, name; 955 fImageView->GetName(&name); 956 prompt << "The document '" << name << "'"; 957 if (count > 1) 958 prompt << " (page " << page << ")"; 959 960 prompt << " has been changed. " 961 << "Do you want to close the document?"; 962 BAlert *pAlert = new BAlert("Close document", prompt.String(), 963 "Cancel", "Close"); 964 if (pAlert->Go() == 0) { 965 // Cancel 966 return false; 967 } else { 968 // Close 969 fModified = false; 970 return true; 971 } 972 } 973 974 975 void 976 ShowImageWindow::ToggleFullScreen() 977 { 978 BRect frame; 979 fFullScreen = !fFullScreen; 980 if (fFullScreen) { 981 BScreen screen; 982 fWindowFrame = Frame(); 983 frame = screen.Frame(); 984 frame.top -= fBar->Bounds().Height()+1; 985 frame.right += B_V_SCROLL_BAR_WIDTH; 986 frame.bottom += B_H_SCROLL_BAR_HEIGHT; 987 frame.InsetBy(-1, -1); // PEN_SIZE in ShowImageView 988 989 SetFlags(Flags() | B_NOT_RESIZABLE | B_NOT_MOVABLE); 990 991 Activate(); 992 // make the window frontmost 993 } else { 994 frame = fWindowFrame; 995 996 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE)); 997 } 998 999 fImageView->SetFullScreen(fFullScreen); 1000 fImageView->SetShowCaption(fFullScreen && fShowCaption); 1001 MoveTo(frame.left, frame.top); 1002 ResizeTo(frame.Width(), frame.Height()); 1003 } 1004 1005 1006 void 1007 ShowImageWindow::LoadSettings() 1008 { 1009 ShowImageSettings* settings = my_app->Settings(); 1010 1011 if (settings->Lock()) { 1012 fShowCaption = settings->GetBool("ShowCaption", fShowCaption); 1013 fPrintOptions.SetBounds(BRect(0, 0, 1023, 767)); 1014 1015 int32 op = settings->GetInt32("PO:Option", fPrintOptions.Option()); 1016 fPrintOptions.SetOption((enum PrintOptions::Option)op); 1017 1018 float f = settings->GetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor()); 1019 fPrintOptions.SetZoomFactor(f); 1020 1021 f = settings->GetFloat("PO:DPI", fPrintOptions.DPI()); 1022 fPrintOptions.SetDPI(f); 1023 1024 f = settings->GetFloat("PO:Width", fPrintOptions.Width()); 1025 fPrintOptions.SetWidth(f); 1026 1027 f = settings->GetFloat("PO:Height", fPrintOptions.Height()); 1028 fPrintOptions.SetHeight(f); 1029 1030 settings->Unlock(); 1031 } 1032 } 1033 1034 1035 void 1036 ShowImageWindow::SavePrintOptions() 1037 { 1038 ShowImageSettings* settings = my_app->Settings(); 1039 1040 if (settings->Lock()) { 1041 settings->SetInt32("PO:Option", fPrintOptions.Option()); 1042 settings->SetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor()); 1043 settings->SetFloat("PO:DPI", fPrintOptions.DPI()); 1044 settings->SetFloat("PO:Width", fPrintOptions.Width()); 1045 settings->SetFloat("PO:Height", fPrintOptions.Height()); 1046 settings->Unlock(); 1047 } 1048 } 1049 1050 1051 bool 1052 ShowImageWindow::PageSetup() 1053 { 1054 status_t st; 1055 BString name; 1056 fImageView->GetName(&name); 1057 BPrintJob printJob(name.String()); 1058 if (fPrintSettings != NULL) 1059 printJob.SetSettings(new BMessage(*fPrintSettings)); 1060 1061 st = printJob.ConfigPage(); 1062 if (st == B_OK) { 1063 delete fPrintSettings; 1064 fPrintSettings = printJob.Settings(); 1065 } 1066 1067 return st == B_OK; 1068 } 1069 1070 1071 void 1072 ShowImageWindow::PrepareForPrint() 1073 { 1074 if (fPrintSettings == NULL && !PageSetup()) 1075 return; 1076 1077 fPrintOptions.SetBounds(fImageView->GetBitmap()->Bounds()); 1078 fPrintOptions.SetWidth(fImageView->GetBitmap()->Bounds().Width()+1); 1079 1080 new PrintOptionsWindow(BPoint(Frame().left+30, Frame().top+50), 1081 &fPrintOptions, this); 1082 } 1083 1084 1085 void 1086 ShowImageWindow::Print(BMessage *msg) 1087 { 1088 status_t st; 1089 if (msg->FindInt32("status", &st) != B_OK || st != B_OK) 1090 return; 1091 1092 SavePrintOptions(); 1093 1094 BString name; 1095 fPrintOptions.SetBounds(fImageView->GetBitmap()->Bounds()); 1096 fImageView->GetName(&name); 1097 BPrintJob printJob(name.String()); 1098 printJob.SetSettings(new BMessage(*fPrintSettings)); 1099 if (printJob.ConfigJob() == B_OK) { 1100 int32 firstPage; 1101 int32 lastPage; 1102 BRect printableRect = printJob.PrintableRect(); 1103 float width, imageWidth, imageHeight, w1, w2; 1104 BBitmap* bitmap; 1105 1106 // first/lastPage is unused for now 1107 firstPage = printJob.FirstPage(); 1108 lastPage = printJob.LastPage(); 1109 if (firstPage < 1) 1110 firstPage = 1; 1111 if (lastPage < firstPage) 1112 lastPage = firstPage; 1113 1114 bitmap = fImageView->GetBitmap(); 1115 imageWidth = bitmap->Bounds().Width() + 1.0; 1116 imageHeight = bitmap->Bounds().Height() + 1.0; 1117 1118 switch (fPrintOptions.Option()) { 1119 case PrintOptions::kFitToPage: 1120 w1 = printableRect.Width()+1; 1121 w2 = imageWidth * (printableRect.Height() + 1) / imageHeight; 1122 if (w2 < w1) 1123 width = w2; 1124 else 1125 width = w1; 1126 break; 1127 case PrintOptions::kZoomFactor: 1128 width = imageWidth * fPrintOptions.ZoomFactor(); 1129 break; 1130 case PrintOptions::kDPI: 1131 width = imageWidth * 72.0 / fPrintOptions.DPI(); 1132 break; 1133 case PrintOptions::kWidth: 1134 case PrintOptions::kHeight: 1135 width = fPrintOptions.Width(); 1136 break; 1137 1138 default: 1139 // keep compiler silent; should not reach here 1140 width = imageWidth; 1141 } 1142 1143 // TODO: eventually print large images on several pages 1144 printJob.BeginJob(); 1145 fImageView->SetScale(width / imageWidth); 1146 // coordinates are relative to printable rectangle 1147 BRect bounds(bitmap->Bounds()); 1148 printJob.DrawView(fImageView, bounds, BPoint(0, 0)); 1149 fImageView->SetScale(1.0); 1150 printJob.SpoolPage(); 1151 printJob.CommitJob(); 1152 } 1153 } 1154 1155 void 1156 ShowImageWindow::OpenResizerWindow(int32 width, int32 height) 1157 { 1158 if (fResizerWindowMessenger == NULL) { 1159 // open window if it is not already opened 1160 BWindow* window = new ResizerWindow(this, width, height); 1161 fResizerWindowMessenger = new BMessenger(window); 1162 window->Show(); 1163 } else 1164 fResizerWindowMessenger->SendMessage(ResizerWindow::kActivateMsg); 1165 } 1166 1167 void 1168 ShowImageWindow::UpdateResizerWindow(int32 width, int32 height) 1169 { 1170 if (fResizerWindowMessenger == NULL) { 1171 // window not opened 1172 return; 1173 } 1174 1175 BMessage updateMsg(ResizerWindow::kUpdateMsg); 1176 updateMsg.AddInt32("width", width); 1177 updateMsg.AddInt32("height", height); 1178 fResizerWindowMessenger->SendMessage(&updateMsg); 1179 } 1180 1181 void 1182 ShowImageWindow::CloseResizerWindow() 1183 { 1184 if (fResizerWindowMessenger == NULL) { 1185 // window not opened 1186 return; 1187 } 1188 fResizerWindowMessenger->SendMessage(B_QUIT_REQUESTED); 1189 fResizerWindowMessenger = NULL; 1190 } 1191 1192 bool 1193 ShowImageWindow::QuitRequested() 1194 { 1195 if (fSavePanel) { 1196 // Don't allow this window to be closed if a save panel is open 1197 return false; 1198 } 1199 1200 bool quit = ClosePrompt(); 1201 1202 if (quit) { 1203 CloseResizerWindow(); 1204 1205 // tell the app to forget about this window 1206 be_app->PostMessage(MSG_WINDOW_QUIT); 1207 } 1208 1209 return quit; 1210 } 1211 1212 1213 void 1214 ShowImageWindow::ScreenChanged(BRect frame, color_space mode) 1215 { 1216 fImageView->SetDither(mode == B_CMAP8); 1217 } 1218 1219