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