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) 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 rect.bottom -= 1; 142 fStatusView = new ShowImageStatusView(rect, "status_view", B_FOLLOW_BOTTOM, 143 B_WILL_DRAW); 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, "Previous Page", MSG_PAGE_PREV, B_LEFT_ARROW, 0, 'W', true); 306 AddItemMenu(pmenu, "Next Page", MSG_PAGE_NEXT, B_RIGHT_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, "Previous File", MSG_FILE_PREV, B_UP_ARROW, 0, 'W', true); 312 AddItemMenu(pmenu, "Next File", MSG_FILE_NEXT, B_DOWN_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_270, '[', 0, 'W', true); 319 AddItemMenu(pmenu, "Rotate +90°", MSG_ROTATE_90, ']', 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 const float windowBorderWidth = 5; 369 const float windowBorderHeight = 5; 370 371 if (screen.Frame().right == 0.0) { 372 return; // invalid screen object 373 } 374 375 float width = r.Width() + 2 * PEN_SIZE + B_V_SCROLL_BAR_WIDTH; 376 float height = r.Height() + 2 * PEN_SIZE + 1 + fBar->Frame().Height() + B_H_SCROLL_BAR_HEIGHT; 377 378 // dimensions so that window does not reach outside of screen 379 float maxWidth = screen.Frame().Width() + 1 - windowBorderWidth - Frame().left; 380 float maxHeight = screen.Frame().Height() + 1 - windowBorderHeight - Frame().top; 381 382 // We have to check size limits manually, otherwise 383 // menu bar will be too short for small images. 384 float minW, maxW, minH, maxH; 385 GetSizeLimits(&minW, &maxW, &minH, &maxH); 386 if (maxWidth > maxW) maxWidth = maxW; 387 if (maxHeight > maxH) maxHeight = maxH; 388 if (width < minW) width = minW; 389 if (height < minH) height = minH; 390 391 if (width > maxWidth) width = maxWidth; 392 if (height > maxHeight) height = maxHeight; 393 394 ResizeTo(width, height); 395 } 396 397 void 398 ShowImageWindow::FrameResized(float width, float height) 399 { 400 } 401 402 bool 403 ShowImageWindow::ToggleMenuItem(uint32 what) 404 { 405 BMenuItem *item; 406 bool marked = false; 407 item = fBar->FindItem(what); 408 if (item != NULL) { 409 marked = !item->IsMarked(); 410 item->SetMarked(marked); 411 } 412 return marked; 413 } 414 415 void 416 ShowImageWindow::EnableMenuItem(BMenu *menu, uint32 what, bool enable) 417 { 418 BMenuItem* item; 419 item = menu->FindItem(what); 420 if (item && item->IsEnabled() != enable) { 421 item->SetEnabled(enable); 422 } 423 } 424 425 void 426 ShowImageWindow::MarkMenuItem(BMenu *menu, uint32 what, bool marked) 427 { 428 BMenuItem* item; 429 item = menu->FindItem(what); 430 if (item && item->IsMarked() != marked) { 431 item->SetMarked(marked); 432 } 433 } 434 435 void 436 ShowImageWindow::MarkSlideShowDelay(float value) 437 { 438 const int32 n = fSlideShowDelay->CountItems(); 439 float v; 440 for (int32 i = 0; i < n; i ++) { 441 BMenuItem* item = fSlideShowDelay->ItemAt(i); 442 if (item) { 443 if (item->Message()->FindFloat("value", &v) == B_OK && v == value) { 444 if (!item->IsMarked()) { 445 item->SetMarked(true); 446 } 447 return; 448 } 449 } 450 } 451 } 452 453 454 void 455 ShowImageWindow::ResizeToWindow(bool shrink, uint32 what) 456 { 457 bool enabled; 458 enabled = ToggleMenuItem(what); 459 if (shrink) { 460 fImageView->SetShrinkToBounds(enabled); 461 } else { 462 fImageView->SetZoomToBounds(enabled); 463 } 464 enabled = !(fImageView->GetShrinkToBounds() || fImageView->GetZoomToBounds()); 465 EnableMenuItem(fBar, MSG_ORIGINAL_SIZE, enabled); 466 EnableMenuItem(fBar, MSG_ZOOM_IN, enabled); 467 EnableMenuItem(fBar, MSG_ZOOM_OUT, enabled); 468 } 469 470 void 471 ShowImageWindow::MessageReceived(BMessage *pmsg) 472 { 473 ShowImageSettings* settings; 474 switch (pmsg->what) { 475 case MSG_MODIFIED: 476 // If image has been modified due to a Cut or Paste 477 fModified = true; 478 break; 479 480 case MSG_OUTPUT_TYPE: 481 // User clicked Save As then choose an output format 482 if (!fSavePanel) 483 // If user doesn't already have a save panel open 484 SaveAs(pmsg); 485 break; 486 487 case MSG_SAVE_PANEL: 488 // User specified where to save the output image 489 SaveToFile(pmsg); 490 break; 491 492 case MSG_CLOSE: 493 if (CanQuit()) 494 Quit(); 495 break; 496 497 case B_CANCEL: 498 delete fSavePanel; 499 fSavePanel = NULL; 500 break; 501 502 case MSG_UPDATE_STATUS: 503 { 504 int32 pages, curPage; 505 pages = fImageView->PageCount(); 506 curPage = fImageView->CurrentPage(); 507 508 bool benable = (pages > 1) ? true : false; 509 EnableMenuItem(fBar, MSG_PAGE_FIRST, benable); 510 EnableMenuItem(fBar, MSG_PAGE_LAST, benable); 511 EnableMenuItem(fBar, MSG_PAGE_NEXT, benable); 512 EnableMenuItem(fBar, MSG_PAGE_PREV, benable); 513 514 EnableMenuItem(fBar, MSG_FILE_NEXT, fImageView->HasNextFile()); 515 EnableMenuItem(fBar, MSG_FILE_PREV, fImageView->HasPrevFile()); 516 517 if (fGoToPageMenu->CountItems() != pages) { 518 // Only rebuild the submenu if the number of 519 // pages is different 520 521 while (fGoToPageMenu->CountItems() > 0) 522 // Remove all page numbers 523 delete fGoToPageMenu->RemoveItem(0L); 524 525 for (int32 i = 1; i <= pages; i++) { 526 // Fill Go To page submenu with an entry for each page 527 BMessage *pgomsg; 528 char shortcut = 0; 529 pgomsg = new BMessage(MSG_GOTO_PAGE); 530 pgomsg->AddInt32("page", i); 531 BString strCaption; 532 strCaption << i; 533 BMenuItem *pitem; 534 if (i < 10) { 535 shortcut = '0' + i; 536 } else if (i == 10) { 537 shortcut = '0'; 538 } 539 pitem = new BMenuItem(strCaption.String(), pgomsg, shortcut); 540 if (curPage == i) 541 pitem->SetMarked(true); 542 fGoToPageMenu->AddItem(pitem); 543 } 544 } else { 545 // Make sure the correct page is marked 546 BMenuItem *pcurItem; 547 pcurItem = fGoToPageMenu->ItemAt(curPage - 1); 548 if (!pcurItem->IsMarked()) { 549 pcurItem->SetMarked(true); 550 } 551 } 552 553 // Disable the Invert menu item if the bitmap color space 554 // is B_CMAP8. (B_CMAP8 is currently unsupported by the 555 // invert algorithm) 556 color_space colors = B_NO_COLOR_SPACE; 557 pmsg->FindInt32("colors", reinterpret_cast<int32 *>(&colors)); 558 EnableMenuItem(fBar, MSG_INVERT, (colors != B_CMAP8)); 559 560 BString status; 561 int32 width, height; 562 if (pmsg->FindInt32("width", &width) >= B_OK 563 && pmsg->FindInt32("height", &height) >= B_OK) { 564 status << width << "x" << height << ", "; 565 } 566 567 BString str; 568 if (pmsg->FindString("status", &str) == B_OK) { 569 status << str; 570 } 571 572 fStatusView->SetText(status); 573 574 UpdateTitle(); 575 break; 576 } 577 578 case MSG_SELECTION: 579 { 580 // The view sends this message when a selection is 581 // made or the selection is cleared so that the window 582 // can update the state of the appropriate menu items 583 bool benable; 584 if (pmsg->FindBool("has_selection", &benable) == B_OK) { 585 EnableMenuItem(fBar, B_CUT, benable); 586 EnableMenuItem(fBar, B_COPY, benable); 587 EnableMenuItem(fBar, MSG_CLEAR_SELECT, benable); 588 } 589 break; 590 } 591 592 case MSG_UNDO_STATE: 593 { 594 bool benable; 595 if (pmsg->FindBool("can_undo", &benable) == B_OK) 596 EnableMenuItem(fBar, B_UNDO, benable); 597 break; 598 } 599 600 case MSG_CLIPBOARD_CHANGED: 601 { 602 // The app sends this message after it examines 603 // the clipboard in response to a B_CLIPBOARD_CHANGED 604 // message 605 bool bdata; 606 if (pmsg->FindBool("data_available", &bdata) == B_OK) 607 EnableMenuItem(fBar, B_PASTE, bdata); 608 break; 609 } 610 611 case B_UNDO: 612 fImageView->Undo(); 613 break; 614 case B_CUT: 615 fImageView->Cut(); 616 break; 617 case B_COPY: 618 fImageView->CopySelectionToClipboard(); 619 break; 620 case B_PASTE: 621 fImageView->Paste(); 622 break; 623 case MSG_CLEAR_SELECT: 624 fImageView->ClearSelection(); 625 break; 626 case MSG_SELECT_ALL: 627 fImageView->SelectAll(); 628 break; 629 630 case MSG_PAGE_FIRST: 631 if (ClosePrompt()) 632 fImageView->FirstPage(); 633 break; 634 635 case MSG_PAGE_LAST: 636 if (ClosePrompt()) 637 fImageView->LastPage(); 638 break; 639 640 case MSG_PAGE_NEXT: 641 if (ClosePrompt()) 642 fImageView->NextPage(); 643 break; 644 645 case MSG_PAGE_PREV: 646 if (ClosePrompt()) 647 fImageView->PrevPage(); 648 break; 649 650 case MSG_GOTO_PAGE: 651 { 652 if (!ClosePrompt()) 653 break; 654 655 int32 curPage, newPage, pages; 656 if (pmsg->FindInt32("page", &newPage) == B_OK) { 657 curPage = fImageView->CurrentPage(); 658 pages = fImageView->PageCount(); 659 660 if (newPage > 0 && newPage <= pages) { 661 BMenuItem *pcurItem, *pnewItem; 662 pcurItem = fGoToPageMenu->ItemAt(curPage - 1); 663 pnewItem = fGoToPageMenu->ItemAt(newPage - 1); 664 if (!pcurItem || !pnewItem) 665 break; 666 pcurItem->SetMarked(false); 667 pnewItem->SetMarked(true); 668 fImageView->GoToPage(newPage); 669 } 670 } 671 } 672 break; 673 674 case MSG_DITHER_IMAGE: 675 fImageView->SetDither(ToggleMenuItem(pmsg->what)); 676 break; 677 678 case MSG_SHRINK_TO_WINDOW: 679 ResizeToWindow(true, pmsg->what); 680 break; 681 case MSG_ZOOM_TO_WINDOW: 682 ResizeToWindow(false, pmsg->what); 683 break; 684 685 case MSG_FILE_PREV: 686 if (ClosePrompt()) 687 fImageView->PrevFile(); 688 break; 689 690 case MSG_FILE_NEXT: 691 if (ClosePrompt()) 692 fImageView->NextFile(); 693 break; 694 695 case MSG_ROTATE_90: 696 fImageView->Rotate(90); 697 break; 698 case MSG_ROTATE_270: 699 fImageView->Rotate(270); 700 break; 701 case MSG_MIRROR_VERTICAL: 702 fImageView->Mirror(true); 703 break; 704 case MSG_MIRROR_HORIZONTAL: 705 fImageView->Mirror(false); 706 break; 707 case MSG_INVERT: 708 fImageView->Invert(); 709 break; 710 case MSG_SLIDE_SHOW: 711 { 712 BMenuItem *item; 713 item = fBar->FindItem(pmsg->what); 714 if (!item) 715 break; 716 if (item->IsMarked()) { 717 item->SetMarked(false); 718 fImageView->StopSlideShow(); 719 } else if (ClosePrompt()) { 720 item->SetMarked(true); 721 fImageView->StartSlideShow(); 722 } 723 } 724 break; 725 726 case MSG_SLIDE_SHOW_DELAY: 727 { 728 float value; 729 if (pmsg->FindFloat("value", &value) == B_OK) { 730 fImageView->SetSlideShowDelay(value); 731 // in case message is sent from popup menu 732 MarkSlideShowDelay(value); 733 } 734 } 735 break; 736 737 case MSG_FULL_SCREEN: 738 ToggleFullScreen(); 739 break; 740 case MSG_EXIT_FULL_SCREEN: 741 if (fFullScreen) 742 ToggleFullScreen(); 743 break; 744 case MSG_SHOW_CAPTION: 745 fShowCaption = ToggleMenuItem(pmsg->what); 746 settings = my_app->Settings(); 747 if (settings->Lock()) { 748 settings->SetBool("ShowCaption", fShowCaption); 749 settings->Unlock(); 750 } 751 if (fFullScreen) { 752 fImageView->SetShowCaption(fShowCaption); 753 } 754 break; 755 756 case MSG_PAGE_SETUP: 757 PageSetup(); 758 break; 759 case MSG_PREPARE_PRINT: 760 PrepareForPrint(); 761 break; 762 case MSG_PRINT: 763 Print(pmsg); 764 break; 765 766 case MSG_ZOOM_IN: 767 fImageView->ZoomIn(); 768 break; 769 case MSG_ZOOM_OUT: 770 fImageView->ZoomOut(); 771 break; 772 case MSG_ORIGINAL_SIZE: 773 fImageView->SetZoom(1.0); 774 break; 775 case MSG_SCALE_BILINEAR: 776 fImageView->SetScaleBilinear(ToggleMenuItem(pmsg->what)); 777 break; 778 779 default: 780 BWindow::MessageReceived(pmsg); 781 break; 782 } 783 } 784 785 void 786 ShowImageWindow::SaveAs(BMessage *pmsg) 787 { 788 // Read the translator and output type the user chose 789 translator_id outTranslator; 790 uint32 outType; 791 if (pmsg->FindInt32(TRANSLATOR_FLD, 792 reinterpret_cast<int32 *>(&outTranslator)) != B_OK) 793 return; 794 if (pmsg->FindInt32(TYPE_FLD, 795 reinterpret_cast<int32 *>(&outType)) != B_OK) 796 return; 797 798 // Add the chosen translator and output type to the 799 // message that the save panel will send back 800 BMessage *ppanelMsg = new BMessage(MSG_SAVE_PANEL); 801 ppanelMsg->AddInt32(TRANSLATOR_FLD, outTranslator); 802 ppanelMsg->AddInt32(TYPE_FLD, outType); 803 804 // Create save panel and show it 805 fSavePanel = new BFilePanel(B_SAVE_PANEL, new BMessenger(this), NULL, 0, 806 false, ppanelMsg); 807 if (!fSavePanel) 808 return; 809 fSavePanel->Window()->SetWorkspaces(B_CURRENT_WORKSPACE); 810 fSavePanel->Show(); 811 } 812 813 void 814 ShowImageWindow::SaveToFile(BMessage *pmsg) 815 { 816 // Read in where the file should be saved 817 entry_ref dirref; 818 if (pmsg->FindRef("directory", &dirref) != B_OK) 819 return; 820 const char *filename; 821 if (pmsg->FindString("name", &filename) != B_OK) 822 return; 823 824 // Read in the translator and type to be used 825 // to save the output image 826 translator_id outTranslator; 827 uint32 outType; 828 if (pmsg->FindInt32(TRANSLATOR_FLD, 829 reinterpret_cast<int32 *>(&outTranslator)) != B_OK) 830 return; 831 if (pmsg->FindInt32(TYPE_FLD, 832 reinterpret_cast<int32 *>(&outType)) != B_OK) 833 return; 834 835 // Find the translator_format information needed to 836 // write a MIME attribute for the image file 837 BTranslatorRoster *roster = BTranslatorRoster::Default(); 838 const translation_format *pouts = NULL; 839 int32 outsCount = 0; 840 if (roster->GetOutputFormats(outTranslator, &pouts, &outsCount) != B_OK) 841 return; 842 if (outsCount < 1) 843 return; 844 int32 i; 845 for (i = 0; i < outsCount; i++) { 846 if (pouts[i].group == B_TRANSLATOR_BITMAP && pouts[i].type == outType) 847 break; 848 } 849 if (i == outsCount) 850 return; 851 852 // Write out the image file 853 BDirectory dir(&dirref); 854 fImageView->SaveToFile(&dir, filename, NULL, &pouts[i]); 855 } 856 857 bool 858 ShowImageWindow::ClosePrompt() 859 { 860 if (!fModified) 861 return true; 862 else { 863 int32 page, count; 864 count = fImageView->PageCount(); 865 page = fImageView->CurrentPage(); 866 BString prompt, name; 867 fImageView->GetName(&name); 868 prompt << "The document '" << name << "'"; 869 if (count > 1) 870 prompt << " (page " << page << ")"; 871 prompt << " has been changed. " 872 << "Do you want to close the document?"; 873 BAlert *pAlert = new BAlert("Close document", prompt.String(), 874 "Cancel", "Close"); 875 if (pAlert->Go() == 0) 876 // Cancel 877 return false; 878 else { 879 // Close 880 fModified = false; 881 return true; 882 } 883 } 884 } 885 886 bool 887 ShowImageWindow::CanQuit() 888 { 889 if (fSavePanel) 890 // Don't allow this window to be closed if a save panel is open 891 return false; 892 else 893 return ClosePrompt(); 894 } 895 896 void 897 ShowImageWindow::ToggleFullScreen() 898 { 899 BRect frame; 900 fFullScreen = !fFullScreen; 901 if (fFullScreen) { 902 BScreen screen; 903 fWindowFrame = Frame(); 904 frame = screen.Frame(); 905 frame.top -= fBar->Bounds().Height()+1; 906 frame.right += B_V_SCROLL_BAR_WIDTH; 907 frame.bottom += B_H_SCROLL_BAR_HEIGHT; 908 frame.InsetBy(-1, -1); // PEN_SIZE in ShowImageView 909 910 SetFlags(Flags() | B_NOT_RESIZABLE | B_NOT_MOVABLE); 911 fImageView->SetAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE); 912 } else { 913 frame = fWindowFrame; 914 915 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE)); 916 // NOTE: I changed this to not use left/top alignment at all, because 917 // I have no idea why it would be useful. The layouting is much more 918 // predictable now. -Stephan 919 // fImageView->SetAlignment(B_ALIGN_LEFT, B_ALIGN_TOP); 920 fImageView->SetAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE); 921 } 922 fImageView->SetBorder(!fFullScreen); 923 fImageView->SetShowCaption(fFullScreen && fShowCaption); 924 MoveTo(frame.left, frame.top); 925 ResizeTo(frame.Width(), frame.Height()); 926 } 927 928 void 929 ShowImageWindow::LoadSettings() 930 { 931 ShowImageSettings* settings; 932 settings = my_app->Settings(); 933 int32 op; 934 float f; 935 if (settings->Lock()) { 936 fShowCaption = settings->GetBool("ShowCaption", fShowCaption); 937 fPrintOptions.SetBounds(BRect(0, 0, 1023, 767)); 938 939 op = settings->GetInt32("PO:Option", fPrintOptions.Option()); 940 fPrintOptions.SetOption((enum PrintOptions::Option)op); 941 942 f = settings->GetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor()); 943 fPrintOptions.SetZoomFactor(f); 944 945 f = settings->GetFloat("PO:DPI", fPrintOptions.DPI()); 946 fPrintOptions.SetDPI(f); 947 948 f = settings->GetFloat("PO:Width", fPrintOptions.Width()); 949 fPrintOptions.SetWidth(f); 950 951 f = settings->GetFloat("PO:Height", fPrintOptions.Height()); 952 fPrintOptions.SetHeight(f); 953 954 settings->Unlock(); 955 } 956 } 957 958 void 959 ShowImageWindow::SavePrintOptions() 960 { 961 ShowImageSettings* settings; 962 settings = my_app->Settings(); 963 if (settings->Lock()) { 964 settings->SetInt32("PO:Option", fPrintOptions.Option()); 965 settings->SetFloat("PO:ZoomFactor", fPrintOptions.ZoomFactor()); 966 settings->SetFloat("PO:DPI", fPrintOptions.DPI()); 967 settings->SetFloat("PO:Width", fPrintOptions.Width()); 968 settings->SetFloat("PO:Height", fPrintOptions.Height()); 969 settings->Unlock(); 970 } 971 } 972 973 bool 974 ShowImageWindow::PageSetup() 975 { 976 status_t st; 977 BString name; 978 fImageView->GetName(&name); 979 BPrintJob printJob(name.String()); 980 if (fPrintSettings != NULL) { 981 printJob.SetSettings(new BMessage(*fPrintSettings)); 982 } 983 st = printJob.ConfigPage(); 984 if (st == B_OK) { 985 delete fPrintSettings; 986 fPrintSettings = printJob.Settings(); 987 } 988 return st == B_OK; 989 } 990 991 void 992 ShowImageWindow::PrepareForPrint() 993 { 994 if (fPrintSettings == NULL && !PageSetup()) { 995 return; 996 } 997 998 fPrintOptions.SetBounds(fImageView->GetBitmap()->Bounds()); 999 fPrintOptions.SetWidth(fImageView->GetBitmap()->Bounds().Width()+1); 1000 1001 new PrintOptionsWindow(BPoint(Frame().left+30, Frame().top+50), &fPrintOptions, this); 1002 } 1003 1004 void 1005 ShowImageWindow::Print(BMessage *msg) 1006 { 1007 status_t st; 1008 if (msg->FindInt32("status", &st) != B_OK || st != B_OK) { 1009 return; 1010 } 1011 1012 SavePrintOptions(); 1013 1014 BString name; 1015 fPrintOptions.SetBounds(fImageView->GetBitmap()->Bounds()); 1016 fImageView->GetName(&name); 1017 BPrintJob printJob(name.String()); 1018 printJob.SetSettings(new BMessage(*fPrintSettings)); 1019 if (printJob.ConfigJob() == B_OK) { 1020 int32 firstPage; 1021 int32 lastPage; 1022 BRect printableRect = printJob.PrintableRect(); 1023 float width, imageWidth, imageHeight, w1, w2; 1024 BBitmap* bitmap; 1025 1026 // first/lastPage is unused for now 1027 firstPage = printJob.FirstPage(); 1028 lastPage = printJob.LastPage(); 1029 if (firstPage < 1) { 1030 firstPage = 1; 1031 } 1032 if (lastPage < firstPage) { 1033 lastPage = firstPage; 1034 } 1035 1036 bitmap = fImageView->GetBitmap(); 1037 imageWidth = bitmap->Bounds().Width() + 1.0; 1038 imageHeight = bitmap->Bounds().Height() + 1.0; 1039 1040 switch (fPrintOptions.Option()) { 1041 case PrintOptions::kFitToPage: 1042 w1 = printableRect.Width()+1; 1043 w2 = imageWidth * (printableRect.Height() + 1) / imageHeight; 1044 if (w2 < w1) { 1045 width = w2; 1046 } else { 1047 width = w1; 1048 } 1049 break; 1050 case PrintOptions::kZoomFactor: 1051 width = imageWidth * fPrintOptions.ZoomFactor(); 1052 break; 1053 case PrintOptions::kDPI: 1054 width = imageWidth * 72.0 / fPrintOptions.DPI(); 1055 break; 1056 case PrintOptions::kWidth: 1057 case PrintOptions::kHeight: 1058 width = fPrintOptions.Width(); 1059 break; 1060 default: 1061 // keep compiler silent; should not reach here 1062 width = imageWidth; 1063 } 1064 1065 // XXX: eventually print large images on several pages 1066 printJob.BeginJob(); 1067 fImageView->SetScale(width / imageWidth); 1068 // coordinates are relative to printable rectangle 1069 BRect bounds(bitmap->Bounds()); 1070 printJob.DrawView(fImageView, bounds, BPoint(0, 0)); 1071 fImageView->SetScale(1.0); 1072 printJob.SpoolPage(); 1073 printJob.CommitJob(); 1074 } 1075 } 1076 1077 bool 1078 ShowImageWindow::QuitRequested() 1079 { 1080 return CanQuit(); 1081 } 1082 1083 void 1084 ShowImageWindow::Quit() 1085 { 1086 // tell the app to forget about this window 1087 be_app->PostMessage(MSG_WINDOW_QUIT); 1088 BWindow::Quit(); 1089 } 1090 1091 void 1092 ShowImageWindow::Zoom(BPoint origin, float width, float height) 1093 { 1094 // just go into fullscreen 1095 ToggleFullScreen(); 1096 } 1097 1098