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