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