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 <algobase.h> 30 #include <stdio.h> 31 #include <Application.h> 32 #include <Bitmap.h> 33 #include <BitmapStream.h> 34 #include <Entry.h> 35 #include <File.h> 36 #include <Menu.h> 37 #include <MenuBar.h> 38 #include <MenuItem.h> 39 #include <Path.h> 40 #include <ScrollView.h> 41 #include <TranslationUtils.h> 42 #include <TranslatorRoster.h> 43 #include <Alert.h> 44 #include <SupportDefs.h> 45 #include <Screen.h> 46 47 #include "ShowImageConstants.h" 48 #include "ShowImageWindow.h" 49 #include "ShowImageView.h" 50 #include "ShowImageStatusView.h" 51 52 ShowImageWindow::ShowImageWindow(const entry_ref *pref) 53 : BWindow(BRect(50, 50, 350, 250), "", B_DOCUMENT_WINDOW, 0) 54 { 55 fpSavePanel = NULL; 56 fpRef = NULL; 57 fFullScreen = false; 58 59 // create menu bar 60 fpBar = new BMenuBar(BRect(0, 0, Bounds().right, 20), "menu_bar"); 61 LoadMenus(fpBar); 62 AddChild(fpBar); 63 64 BRect viewFrame = Bounds(); 65 viewFrame.top = fpBar->Bounds().bottom + 1; 66 viewFrame.right -= B_V_SCROLL_BAR_WIDTH; 67 viewFrame.bottom -= B_H_SCROLL_BAR_HEIGHT; 68 69 // create the image view 70 fpImageView = new ShowImageView(viewFrame, "image_view", B_FOLLOW_ALL, 71 B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE | B_PULSE_NEEDED); 72 // wrap a scroll view around the view 73 BScrollView *pscrollView = new BScrollView("image_scroller", fpImageView, 74 B_FOLLOW_ALL, 0, false, false, B_PLAIN_BORDER); 75 AddChild(pscrollView); 76 77 const int32 kstatusWidth = 190; 78 BRect rect; 79 rect = Bounds(); 80 rect.top = viewFrame.bottom + 1; 81 rect.left = viewFrame.left + kstatusWidth; 82 rect.right = viewFrame.right; 83 BScrollBar *phscroll; 84 phscroll = new BScrollBar(rect, "hscroll", fpImageView, 0, 150, 85 B_HORIZONTAL); 86 AddChild(phscroll); 87 88 rect.left = 0; 89 rect.right = kstatusWidth - 1; 90 fpStatusView = new ShowImageStatusView(rect, "status_view", B_FOLLOW_BOTTOM, 91 B_WILL_DRAW); 92 fpStatusView->SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR)); 93 AddChild(fpStatusView); 94 95 rect = Bounds(); 96 rect.top = viewFrame.top; 97 rect.left = viewFrame.right + 1; 98 rect.bottom = viewFrame.bottom; 99 BScrollBar *pvscroll; 100 pvscroll = new BScrollBar(rect, "vscroll", fpImageView, 0, 150, B_VERTICAL); 101 AddChild(pvscroll); 102 103 SetSizeLimits(250, 100000, 100, 100000); 104 105 // finish creating window 106 SetRef(pref); 107 UpdateTitle(); 108 109 fpImageView->SetImage(pref); 110 111 SetPulseRate(100000); // every 1/10 second; ShowImageView needs it for marching ants 112 113 Show(); 114 } 115 116 ShowImageWindow::~ShowImageWindow() 117 { 118 delete fpRef; 119 } 120 121 status_t 122 ShowImageWindow::InitCheck() 123 { 124 if (!fpRef || !fpImageView) 125 return B_ERROR; 126 else 127 return B_OK; 128 } 129 130 void 131 ShowImageWindow::SetRef(const entry_ref *pref) 132 { 133 if (!fpRef) 134 fpRef = new entry_ref(*pref); 135 else 136 *fpRef = *pref; 137 } 138 139 void 140 ShowImageWindow::UpdateTitle() 141 { 142 BEntry entry(fpRef); 143 if (entry.InitCheck() == B_OK) { 144 BPath path; 145 entry.GetPath(&path); 146 if (path.InitCheck() == B_OK) 147 SetTitle(path.Path()); 148 } 149 } 150 151 void 152 ShowImageWindow::LoadMenus(BMenuBar *pbar) 153 { 154 BMenu *pmenu = new BMenu("File"); 155 AddItemMenu(pmenu, "Open", MSG_FILE_OPEN, 'O', 0, 'A', true); 156 pmenu->AddSeparatorItem(); 157 AddItemMenu(pmenu, "Dia Show", MSG_DIA_SHOW, 0, 0, 'W', true); 158 BMenu* pDelay = new BMenu("Delay"); 159 pDelay->SetRadioMode(true); 160 // Note: ShowImage loades images in window thread so it becomes unresponsive if 161 // dia show delay is too short! (Especially if loading the image takes as long as 162 // or longer than the dia show delay). Should load in background thread! 163 // AddDelayItem(pDelay, "Half a Second", 0.5, false); 164 // AddDelayItem(pDelay, "One Second", 1, false); 165 // AddDelayItem(pDelay, "Two Second", 2, false); 166 AddDelayItem(pDelay, "Three Seconds", 3, true); 167 AddDelayItem(pDelay, "Four Second", 4, false); 168 AddDelayItem(pDelay, "Five Seconds", 5, false); 169 AddDelayItem(pDelay, "Six Seconds", 6, false); 170 AddDelayItem(pDelay, "Seven Seconds", 7, false); 171 AddDelayItem(pDelay, "Eight Seconds", 8, false); 172 AddDelayItem(pDelay, "Nine Seconds", 9, false); 173 AddDelayItem(pDelay, "Ten Seconds", 10, false); 174 AddDelayItem(pDelay, "Tweenty Seconds", 20, false); 175 pmenu->AddItem(pDelay); 176 pmenu->AddSeparatorItem(); 177 AddItemMenu(pmenu, "Next", MSG_FILE_NEXT, B_DOWN_ARROW, 0, 'W', true); 178 AddItemMenu(pmenu, "Previous", MSG_FILE_PREV, B_UP_ARROW, 0, 'W', true); 179 pmenu->AddSeparatorItem(); 180 BMenu *pmenuSaveAs = new BMenu("Save As...", B_ITEMS_IN_COLUMN); 181 BTranslationUtils::AddTranslationItems(pmenuSaveAs, B_TRANSLATOR_BITMAP); 182 // Fill Save As submenu with all types that can be converted 183 // to from the Be bitmap image format 184 pmenu->AddItem(pmenuSaveAs); 185 AddItemMenu(pmenu, "Close", MSG_CLOSE, 'W', 0, 'W', true); 186 pmenu->AddSeparatorItem(); 187 AddItemMenu(pmenu, "About ShowImage...", B_ABOUT_REQUESTED, 0, 0, 'A', true); 188 pmenu->AddSeparatorItem(); 189 AddItemMenu(pmenu, "Quit", B_QUIT_REQUESTED, 'Q', 0, 'A', true); 190 pbar->AddItem(pmenu); 191 192 pmenu = new BMenu("Edit"); 193 AddItemMenu(pmenu, "Undo", B_UNDO, 'Z', 0, 'W', false); 194 pmenu->AddSeparatorItem(); 195 AddItemMenu(pmenu, "Cut", B_CUT, 'X', 0, 'W', false); 196 AddItemMenu(pmenu, "Copy", B_COPY, 'C', 0, 'W', false); 197 AddItemMenu(pmenu, "Paste", B_PASTE, 'V', 0, 'W', false); 198 AddItemMenu(pmenu, "Clear", MSG_CLEAR_SELECT, 0, 0, 'W', false); 199 pmenu->AddSeparatorItem(); 200 AddItemMenu(pmenu, "Select All", MSG_SELECT_ALL, 'A', 0, 'W', false); 201 pbar->AddItem(pmenu); 202 203 pmenu = fpPageMenu = new BMenu("Page"); 204 AddItemMenu(pmenu, "First", MSG_PAGE_FIRST, 'F', 0, 'W', true); 205 AddItemMenu(pmenu, "Last", MSG_PAGE_LAST, 'L', 0, 'W', true); 206 AddItemMenu(pmenu, "Next", MSG_PAGE_NEXT, 'N', 0, 'W', true); 207 AddItemMenu(pmenu, "Previous", MSG_PAGE_PREV, 'P', 0, 'W', true); 208 pbar->AddItem(pmenu); 209 210 pmenu = new BMenu("Image"); 211 AddItemMenu(pmenu, "Dither Image", MSG_DITHER_IMAGE, 0, 0, 'W', true); 212 pmenu->AddSeparatorItem(); 213 AddItemMenu(pmenu, "Rotate Clockwise", MSG_ROTATE_CLOCKWISE, B_RIGHT_ARROW, 0, 'W', true); 214 AddItemMenu(pmenu, "Rotate Anticlockwise", MSG_ROTATE_ACLKWISE, B_LEFT_ARROW, 0, 'W', true); 215 pmenu->AddSeparatorItem(); 216 AddItemMenu(pmenu, "Mirror Vertical", MSG_MIRROR_VERTICAL, 0, 0, 'W', true); 217 AddItemMenu(pmenu, "Mirror Horizontal", MSG_MIRROR_HORIZONTAL, 0, 0, 'W', true); 218 pmenu->AddSeparatorItem(); 219 AddItemMenu(pmenu, "Invert", MSG_INVERT, 0, 0, 'W', true); 220 pbar->AddItem(pmenu); 221 222 pmenu = new BMenu("View"); 223 AddItemMenu(pmenu, "Fit To Window Size", MSG_FIT_TO_WINDOW_SIZE, 0, 0, 'W', true); 224 AddItemMenu(pmenu, "Full Screen", MSG_FULL_SCREEN, B_ENTER, 0, 'W', true); 225 pbar->AddItem(pmenu); 226 } 227 228 BMenuItem * 229 ShowImageWindow::AddItemMenu(BMenu *pmenu, char *caption, long unsigned int msg, 230 char shortcut, uint32 modifier, char target, bool benabled) 231 { 232 BMenuItem* pitem; 233 pitem = new BMenuItem(caption, new BMessage(msg), shortcut); 234 235 if (target == 'A') 236 pitem->SetTarget(be_app); 237 238 pitem->SetEnabled(benabled); 239 pmenu->AddItem(pitem); 240 241 return pitem; 242 } 243 244 BMenuItem* 245 ShowImageWindow::AddDelayItem(BMenu *pmenu, char *caption, float value, bool marked) 246 { 247 BMenuItem* pitem; 248 BMessage* pmsg; 249 pmsg = new BMessage(MSG_DIA_SHOW_DELAY); 250 pmsg->AddFloat("value", value); 251 252 pitem = new BMenuItem(caption, pmsg, 0); 253 254 if (marked) pitem->SetMarked(true); 255 pmenu->AddItem(pitem); 256 257 return pitem; 258 } 259 260 void 261 ShowImageWindow::WindowRedimension(BBitmap *pbitmap) 262 { 263 // set the window's min & max size limits 264 // based on document's data bounds 265 float maxWidth = pbitmap->Bounds().Width() + B_V_SCROLL_BAR_WIDTH; 266 float maxHeight = pbitmap->Bounds().Height() + fpBar->Frame().Height() + 267 B_H_SCROLL_BAR_HEIGHT + 1; 268 float minWidth = min(maxWidth, 100.0f); 269 float minHeight = min(maxHeight, 100.0f); 270 271 // adjust the window's current size based on new min/max values 272 float curWidth = Bounds().Width(); 273 float curHeight = Bounds().Height(); 274 if (curWidth < minWidth) 275 curWidth = minWidth; 276 else if (curWidth > maxWidth) 277 curWidth = maxWidth; 278 279 if (curHeight < minHeight) 280 curHeight = minHeight; 281 else if (curHeight > maxHeight) 282 curHeight = maxHeight; 283 284 if (minWidth < 250) 285 minWidth = 250; 286 287 SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 288 ResizeTo(curWidth, curHeight); 289 } 290 291 void 292 ShowImageWindow::FrameResized(float width, float height) 293 { 294 } 295 296 bool 297 ShowImageWindow::ToggleMenuItem(uint32 what) 298 { 299 BMenuItem *item; 300 bool marked = false; 301 item = fpBar->FindItem(what); 302 if (item != NULL) { 303 marked = !item->IsMarked(); 304 item->SetMarked(marked); 305 } 306 return marked; 307 } 308 309 void 310 ShowImageWindow::MessageReceived(BMessage *pmsg) 311 { 312 switch (pmsg->what) { 313 case MSG_OUTPUT_TYPE: 314 // User clicked Save As then choose an output format 315 if (!fpSavePanel) 316 // If user doesn't already have a save panel open 317 SaveAs(pmsg); 318 break; 319 320 case MSG_SAVE_PANEL: 321 // User specified where to save the output image 322 SaveToFile(pmsg); 323 break; 324 325 case MSG_CLOSE: 326 if (CanQuit()) 327 Quit(); 328 break; 329 330 case B_CANCEL: 331 delete fpSavePanel; 332 fpSavePanel = NULL; 333 break; 334 335 case MSG_UPDATE_STATUS: 336 { 337 bool benable = (fpImageView->PageCount() > 1) ? true : false; 338 if (fpPageMenu->IsEnabled() != benable) 339 // Only call this function if the state is changing 340 // to avoid flickering 341 fpPageMenu->SetEnabled(benable); 342 343 BString str; 344 if (pmsg->FindString("status", &str) == B_OK) 345 fpStatusView->SetText(str); 346 347 entry_ref ref; 348 if (pmsg->FindRef("ref", &ref) == B_OK) { 349 SetRef(&ref); 350 UpdateTitle(); 351 } 352 break; 353 } 354 355 case B_UNDO: 356 break; 357 case B_CUT: 358 break; 359 case B_COPY: 360 break; 361 case B_PASTE: 362 break; 363 case MSG_CLEAR_SELECT: 364 break; 365 case MSG_SELECT_ALL: 366 break; 367 368 case MSG_PAGE_FIRST: 369 fpImageView->FirstPage(); 370 break; 371 372 case MSG_PAGE_LAST: 373 fpImageView->LastPage(); 374 break; 375 376 case MSG_PAGE_NEXT: 377 fpImageView->NextPage(); 378 break; 379 380 case MSG_PAGE_PREV: 381 fpImageView->PrevPage(); 382 break; 383 384 case MSG_DITHER_IMAGE: 385 ToggleMenuItem(pmsg->what); 386 break; 387 388 case MSG_FIT_TO_WINDOW_SIZE: 389 { 390 bool resize; 391 resize = ToggleMenuItem(pmsg->what); 392 fpImageView->ResizeToViewBounds(resize); 393 } 394 break; 395 396 case MSG_FILE_PREV: 397 fpImageView->PrevFile(); 398 break; 399 400 case MSG_FILE_NEXT: 401 fpImageView->NextFile(); 402 break; 403 404 case MSG_ROTATE_CLOCKWISE: 405 fpImageView->Rotate(90); 406 break; 407 case MSG_ROTATE_ACLKWISE: 408 fpImageView->Rotate(270); 409 break; 410 case MSG_MIRROR_VERTICAL: 411 fpImageView->Mirror(true); 412 break; 413 case MSG_MIRROR_HORIZONTAL: 414 fpImageView->Mirror(false); 415 break; 416 case MSG_INVERT: 417 fpImageView->Invert(); 418 break; 419 case MSG_DIA_SHOW: 420 if (ToggleMenuItem(pmsg->what)) { 421 fpImageView->StartDiaShow(); 422 } else { 423 fpImageView->StopDiaShow(); 424 } 425 case MSG_DIA_SHOW_DELAY: 426 { 427 float value; 428 if (pmsg->FindFloat("value", &value) == B_OK) { 429 fpImageView->SetDiaShowDelay(value); 430 } 431 } 432 break; 433 434 case MSG_FULL_SCREEN: 435 ToggleFullScreen(); 436 break; 437 438 default: 439 BWindow::MessageReceived(pmsg); 440 break; 441 } 442 } 443 444 void 445 ShowImageWindow::SaveAs(BMessage *pmsg) 446 { 447 // Read the translator and output type the user chose 448 translator_id outTranslator; 449 uint32 outType; 450 if (pmsg->FindInt32(TRANSLATOR_FLD, 451 reinterpret_cast<int32 *>(&outTranslator)) != B_OK) 452 return; 453 if (pmsg->FindInt32(TYPE_FLD, 454 reinterpret_cast<int32 *>(&outType)) != B_OK) 455 return; 456 457 // Add the chosen translator and output type to the 458 // message that the save panel will send back 459 BMessage *ppanelMsg = new BMessage(MSG_SAVE_PANEL); 460 ppanelMsg->AddInt32(TRANSLATOR_FLD, outTranslator); 461 ppanelMsg->AddInt32(TYPE_FLD, outType); 462 463 // Create save panel and show it 464 fpSavePanel = new BFilePanel(B_SAVE_PANEL, new BMessenger(this), NULL, 0, 465 false, ppanelMsg); 466 if (!fpSavePanel) 467 return; 468 fpSavePanel->Window()->SetWorkspaces(B_CURRENT_WORKSPACE); 469 fpSavePanel->Show(); 470 } 471 472 void 473 ShowImageWindow::SaveToFile(BMessage *pmsg) 474 { 475 // Read in where the file should be saved 476 entry_ref dirref; 477 if (pmsg->FindRef("directory", &dirref) != B_OK) 478 return; 479 const char *filename; 480 if (pmsg->FindString("name", &filename) != B_OK) 481 return; 482 483 // Read in the translator and type to be used 484 // to save the output image 485 translator_id outTranslator; 486 uint32 outType; 487 if (pmsg->FindInt32(TRANSLATOR_FLD, 488 reinterpret_cast<int32 *>(&outTranslator)) != B_OK) 489 return; 490 if (pmsg->FindInt32(TYPE_FLD, 491 reinterpret_cast<int32 *>(&outType)) != B_OK) 492 return; 493 494 // Create the output file 495 BDirectory dir(&dirref); 496 BFile file(&dir, filename, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 497 if (file.InitCheck() != B_OK) 498 return; 499 500 // Translate the image and write it out to the output file 501 BBitmapStream stream(fpImageView->GetBitmap()); 502 BTranslatorRoster *proster = BTranslatorRoster::Default(); 503 if (proster->Translate(outTranslator, &stream, NULL, 504 &file, outType) != B_OK) { 505 BAlert *palert = new BAlert(NULL, "Error writing image file.", "Ok"); 506 palert->Go(); 507 } 508 509 BBitmap *pout = NULL; 510 stream.DetachBitmap(&pout); 511 // bitmap used by stream still belongs to the view, 512 // detach so it doesn't get deleted 513 } 514 515 bool 516 ShowImageWindow::CanQuit() 517 { 518 if (fpSavePanel) 519 // Don't allow this window to be closed if a save panel is open 520 return false; 521 else 522 return true; 523 } 524 525 void 526 ShowImageWindow::ToggleFullScreen() 527 { 528 BRect frame; 529 fFullScreen = !fFullScreen; 530 if (fFullScreen) { 531 BScreen screen; 532 fWindowFrame = Frame(); 533 frame = screen.Frame(); 534 frame.top -= fpBar->Bounds().Height()+1; 535 frame.right += B_V_SCROLL_BAR_WIDTH; 536 frame.bottom += B_H_SCROLL_BAR_HEIGHT; 537 frame.InsetBy(-1, -1); // PEN_SIZE in ShowImageView 538 539 SetFlags(Flags() | B_NOT_RESIZABLE | B_NOT_MOVABLE); 540 } else { 541 frame = fWindowFrame; 542 543 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE)); 544 } 545 MoveTo(frame.left, frame.top); 546 ResizeTo(frame.Width(), frame.Height()); 547 } 548 549 bool 550 ShowImageWindow::QuitRequested() 551 { 552 return CanQuit(); 553 } 554 555 void 556 ShowImageWindow::Quit() 557 { 558 // tell the app to forget about this window 559 be_app->PostMessage(MSG_WINDOW_QUIT); 560 BWindow::Quit(); 561 } 562 563