1 /*****************************************************************************/ 2 // ShowImageView 3 // Written by Fernando Francisco de Oliveira, Michael Wilber, Michael Pfeiffer 4 // 5 // ShowImageView.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 <Debug.h> 31 #include <Message.h> 32 #include <ScrollBar.h> 33 #include <StopWatch.h> 34 #include <Alert.h> 35 #include <MenuBar.h> 36 #include <MenuItem.h> 37 #include <File.h> 38 #include <Bitmap.h> 39 #include <TranslatorRoster.h> 40 #include <BitmapStream.h> 41 #include <Rect.h> 42 #include <SupportDefs.h> 43 #include <Directory.h> 44 #include <Application.h> 45 #include <Roster.h> 46 #include <NodeInfo.h> 47 #include <Clipboard.h> 48 #include <Path.h> 49 #include <PopUpMenu.h> 50 #include <Region.h> 51 52 53 #include "ShowImageApp.h" 54 #include "ShowImageConstants.h" 55 #include "ShowImageView.h" 56 #include "ShowImageWindow.h" 57 58 #ifndef min 59 #define min(a,b) ((a)>(b)?(b):(a)) 60 #endif 61 #ifndef max 62 #define max(a,b) ((a)>(b)?(a):(b)) 63 #endif 64 65 #define BORDER_WIDTH 16 66 #define BORDER_HEIGHT 16 67 #define PEN_SIZE 1.0f 68 const rgb_color kborderColor = { 0, 0, 0, 255 }; 69 70 // use patterns to simulate marching ants for selection 71 void 72 ShowImageView::InitPatterns() 73 { 74 uchar p; 75 uchar p1 = 0x33; 76 uchar p2 = 0xCC; 77 for (int i = 0; i <= 7; i ++) { 78 fPatternLeft.data[i] = p1; 79 fPatternRight.data[i] = p2; 80 if ((i / 2) % 2 == 0) { 81 p = 255; 82 } else { 83 p = 0; 84 } 85 fPatternUp.data[i] = p; 86 fPatternDown.data[i] = ~p; 87 } 88 } 89 90 void 91 ShowImageView::RotatePatterns() 92 { 93 int i; 94 uchar p; 95 bool set; 96 97 // rotate up 98 p = fPatternUp.data[0]; 99 for (i = 0; i <= 6; i ++) { 100 fPatternUp.data[i] = fPatternUp.data[i+1]; 101 } 102 fPatternUp.data[7] = p; 103 104 // rotate down 105 p = fPatternDown.data[7]; 106 for (i = 7; i >= 1; i --) { 107 fPatternDown.data[i] = fPatternDown.data[i-1]; 108 } 109 fPatternDown.data[0] = p; 110 111 // rotate to left 112 p = fPatternLeft.data[0]; 113 set = (p & 0x80) != 0; 114 p <<= 1; 115 p &= 0xfe; 116 if (set) p |= 1; 117 memset(fPatternLeft.data, p, 8); 118 119 // rotate to right 120 p = fPatternRight.data[0]; 121 set = (p & 1) != 0; 122 p >>= 1; 123 if (set) p |= 0x80; 124 memset(fPatternRight.data, p, 8); 125 } 126 127 void 128 ShowImageView::AnimateSelection(bool a) 129 { 130 fAnimateSelection = a; 131 } 132 133 void 134 ShowImageView::Pulse() 135 { 136 // animate marching ants 137 if (HasSelection() && fAnimateSelection && Window()->IsActive()) { 138 RotatePatterns(); 139 DrawSelectionBox(fSelectionRect); 140 } 141 if (fSlideShow) { 142 fSlideShowCountDown --; 143 if (fSlideShowCountDown <= 0) { 144 fSlideShowCountDown = fSlideShowDelay; 145 if (!NextFile()) { 146 FirstFile(); 147 } 148 } 149 } 150 } 151 152 ShowImageView::ShowImageView(BRect rect, const char *name, uint32 resizingMode, 153 uint32 flags) 154 : BView(rect, name, resizingMode, flags) 155 { 156 ShowImageSettings* settings; 157 settings = my_app->Settings(); 158 159 InitPatterns(); 160 fBitmap = NULL; 161 fSelBitmap = NULL; 162 fDocumentIndex = 1; 163 fDocumentCount = 1; 164 fAnimateSelection = true; 165 fbHasSelection = false; 166 fShrinkToBounds = false; 167 fZoomToBounds = false; 168 fHasBorder = true; 169 fHAlignment = B_ALIGN_LEFT; 170 fVAlignment = B_ALIGN_TOP; 171 fSlideShow = false; 172 fSlideShowDelay = 3 * 10; // 3 seconds 173 fShowCaption = false; 174 fZoom = 1.0; 175 fMovesImage = false; 176 fScaleBilinear = false; 177 fScaler = NULL; 178 179 if (settings->Lock()) { 180 fShrinkToBounds = settings->GetBool("ShrinkToBounds", fShrinkToBounds); 181 fZoomToBounds = settings->GetBool("ZoomToBounds", fZoomToBounds); 182 fSlideShowDelay = settings->GetInt32("SlideShowDelay", fSlideShowDelay); 183 fScaleBilinear = settings->GetBool("ScaleBilinear", fScaleBilinear); 184 settings->Unlock(); 185 } 186 187 SetViewColor(B_TRANSPARENT_COLOR); 188 SetHighColor(kborderColor); 189 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 190 SetPenSize(PEN_SIZE); 191 } 192 193 ShowImageView::~ShowImageView() 194 { 195 DeleteBitmap(); 196 } 197 198 bool 199 ShowImageView::IsImage(const entry_ref *pref) 200 { 201 BFile file(pref, B_READ_ONLY); 202 translator_info info; 203 memset(&info, 0, sizeof(translator_info)); 204 BMessage ioExtension; 205 206 BTranslatorRoster *proster = BTranslatorRoster::Default(); 207 if (!proster) 208 return false; 209 210 if (ioExtension.AddInt32("/documentIndex", fDocumentIndex) != B_OK) 211 return false; 212 213 if (proster->Identify(&file, &ioExtension, &info, 0, NULL, 214 B_TRANSLATOR_BITMAP) != B_OK) 215 return false; 216 217 return true; 218 } 219 220 // send message to parent about new image 221 void 222 ShowImageView::Notify(const char* status) 223 { 224 BMessage msg(MSG_UPDATE_STATUS); 225 if (status != NULL) { 226 msg.AddString("status", status); 227 } 228 BMessenger msgr(Window()); 229 msgr.SendMessage(&msg); 230 231 FixupScrollBars(); 232 Invalidate(); 233 } 234 235 void 236 ShowImageView::AddToRecentDocuments() 237 { 238 be_roster->AddToRecentDocuments(&fCurrentRef, APP_SIG); 239 be_app_messenger.SendMessage(MSG_UPDATE_RECENT_DOCUMENTS); 240 } 241 242 void 243 ShowImageView::DeleteScaler() 244 { 245 if (fScaler) { 246 fScaler->Stop(); 247 delete fScaler; 248 fScaler = NULL; 249 } 250 } 251 252 void 253 ShowImageView::DeleteBitmap() 254 { 255 DeleteScaler(); 256 DeleteSelBitmap(); 257 delete fBitmap; 258 fBitmap = NULL; 259 } 260 261 void ShowImageView::DeleteSelBitmap() 262 { 263 delete fSelBitmap; 264 fSelBitmap = NULL; 265 } 266 267 void 268 ShowImageView::SetImage(const entry_ref *pref) 269 { 270 DeleteBitmap(); 271 SetHasSelection(false); 272 fMakesSelection = false; 273 274 entry_ref ref; 275 if (!pref) 276 ref = fCurrentRef; 277 else 278 ref = *pref; 279 280 BTranslatorRoster *proster = BTranslatorRoster::Default(); 281 if (!proster) 282 return; 283 BFile file(&ref, B_READ_ONLY); 284 translator_info info; 285 memset(&info, 0, sizeof(translator_info)); 286 BMessage ioExtension; 287 if (ref != fCurrentRef) 288 // if new image, reset to first document 289 fDocumentIndex = 1; 290 if (ioExtension.AddInt32("/documentIndex", fDocumentIndex) != B_OK) 291 return; 292 if (proster->Identify(&file, &ioExtension, &info, 0, NULL, 293 B_TRANSLATOR_BITMAP) != B_OK) 294 return; 295 296 // Translate image data and create a new ShowImage window 297 BBitmapStream outstream; 298 if (proster->Translate(&file, &info, &ioExtension, &outstream, 299 B_TRANSLATOR_BITMAP) != B_OK) 300 return; 301 if (outstream.DetachBitmap(&fBitmap) != B_OK) 302 return; 303 fCurrentRef = ref; 304 305 // get the number of documents (pages) if it has been supplied 306 int32 documentCount = 0; 307 if (ioExtension.FindInt32("/documentCount", &documentCount) == B_OK && 308 documentCount > 0) 309 fDocumentCount = documentCount; 310 else 311 fDocumentCount = 1; 312 313 GetPath(&fCaption); 314 if (fDocumentCount > 1) { 315 fCaption << ", " << fDocumentIndex << "/" << fDocumentCount; 316 } 317 fCaption << ", " << info.name; 318 319 AddToRecentDocuments(); 320 321 Notify(info.name); 322 } 323 324 void 325 ShowImageView::SetShowCaption(bool show) 326 { 327 if (fShowCaption != show) { 328 fShowCaption = show; 329 UpdateCaption(); 330 } 331 } 332 333 void 334 ShowImageView::SetShrinkToBounds(bool enable) 335 { 336 if (fShrinkToBounds != enable) { 337 SettingsSetBool("ShrinkToBounds", enable); 338 fShrinkToBounds = enable; 339 FixupScrollBars(); 340 Invalidate(); 341 } 342 } 343 344 void 345 ShowImageView::SetZoomToBounds(bool enable) 346 { 347 if (fZoomToBounds != enable) { 348 SettingsSetBool("ZoomToBounds", enable); 349 fZoomToBounds = enable; 350 FixupScrollBars(); 351 Invalidate(); 352 } 353 } 354 355 void 356 ShowImageView::SetBorder(bool hasBorder) 357 { 358 if (fHasBorder != hasBorder) { 359 fHasBorder = hasBorder; 360 FixupScrollBars(); 361 Invalidate(); 362 } 363 } 364 365 void 366 ShowImageView::SetAlignment(alignment horizontal, vertical_alignment vertical) 367 { 368 bool hasChanged; 369 hasChanged = fHAlignment != horizontal || fVAlignment != vertical; 370 if (hasChanged) { 371 fHAlignment = horizontal; 372 fVAlignment = vertical; 373 FixupScrollBars(); 374 Invalidate(); 375 } 376 } 377 378 BBitmap * 379 ShowImageView::GetBitmap() 380 { 381 return fBitmap; 382 } 383 384 void 385 ShowImageView::GetName(BString *name) 386 { 387 *name = ""; 388 BEntry entry(&fCurrentRef); 389 if (entry.InitCheck() == B_OK) { 390 char n[B_FILE_NAME_LENGTH]; 391 if (entry.GetName(n) == B_OK) { 392 name->SetTo(n); 393 } 394 } 395 } 396 397 void 398 ShowImageView::GetPath(BString *name) 399 { 400 *name = ""; 401 BEntry entry(&fCurrentRef); 402 if (entry.InitCheck() == B_OK) { 403 BPath path; 404 entry.GetPath(&path); 405 if (path.InitCheck() == B_OK) { 406 name->SetTo(path.Path()); 407 } 408 } 409 } 410 411 void 412 ShowImageView::FlushToLeftTop() 413 { 414 BRect rect = AlignBitmap(); 415 BPoint p(rect.left, rect.top); 416 ScrollTo(p); 417 } 418 419 void 420 ShowImageView::SetScaleBilinear(bool s) 421 { 422 if (fScaleBilinear != s) { 423 SettingsSetBool("ScaleBilinear", s); 424 fScaleBilinear = s; Invalidate(); 425 } 426 } 427 428 void 429 ShowImageView::AttachedToWindow() 430 { 431 FixupScrollBars(); 432 } 433 434 BRect 435 ShowImageView::AlignBitmap() 436 { 437 BRect rect(fBitmap->Bounds()); 438 float width, height; 439 width = Bounds().Width()-2*PEN_SIZE+1; 440 height = Bounds().Height()-2*PEN_SIZE+1; 441 if (width == 0 || height == 0) return rect; 442 fShrinkOrZoomToBounds = fShrinkToBounds && (rect.Width() >= Bounds().Width() || rect.Height() >= Bounds().Height()) || 443 fZoomToBounds && rect.Width() < Bounds().Width() && rect.Height() < Bounds().Height(); 444 if (fShrinkOrZoomToBounds) { 445 float s; 446 s = width / (rect.Width()+1.0); 447 448 if (s * (rect.Height()+1.0) <= height) { 449 rect.right = width-1; 450 rect.bottom = static_cast<int>(s * (rect.Height()+1.0))-1; 451 // center vertically 452 rect.OffsetBy(0, (height - rect.Height()) / 2); 453 } else { 454 s = height / (rect.Height()+1.0); 455 rect.right = static_cast<int>(s * (rect.Width()+1.0))-1; 456 rect.bottom = height-1; 457 // center horizontally 458 rect.OffsetBy((width - rect.Width()) / 2, 0); 459 } 460 } else { 461 // zoom image 462 rect.right = static_cast<int>((rect.right+1.0)*fZoom)-1; 463 rect.bottom = static_cast<int>((rect.bottom+1.0)*fZoom)-1; 464 // align 465 switch (fHAlignment) { 466 case B_ALIGN_CENTER: 467 if (width > rect.Width()) { 468 rect.OffsetBy((width - rect.Width()) / 2.0, 0); 469 break; 470 } 471 // fall through 472 default: 473 case B_ALIGN_LEFT: 474 if (fHasBorder) { 475 rect.OffsetBy(BORDER_WIDTH, 0); 476 } 477 break; 478 } 479 switch (fVAlignment) { 480 case B_ALIGN_MIDDLE: 481 if (height > rect.Height()) { 482 rect.OffsetBy(0, (height - rect.Height()) / 2.0); 483 break; 484 } 485 // fall through 486 default: 487 case B_ALIGN_TOP: 488 if (fHasBorder) { 489 rect.OffsetBy(0, BORDER_WIDTH); 490 } 491 break; 492 } 493 } 494 rect.OffsetBy(PEN_SIZE, PEN_SIZE); 495 return rect; 496 } 497 498 void 499 ShowImageView::Setup(BRect rect) 500 { 501 fLeft = rect.left; 502 fTop = rect.top; 503 fScaleX = rect.Width() / fBitmap->Bounds().Width(); 504 fScaleY = rect.Height() / fBitmap->Bounds().Height(); 505 } 506 507 BPoint 508 ShowImageView::ImageToView(BPoint p) const 509 { 510 p.x = fScaleX * p.x + fLeft; 511 p.y = fScaleY * p.y + fTop; 512 return p; 513 } 514 515 BPoint 516 ShowImageView::ViewToImage(BPoint p) const 517 { 518 p.x = (p.x - fLeft) / fScaleX; 519 p.y = (p.y - fTop) / fScaleY; 520 return p; 521 } 522 523 BRect 524 ShowImageView::ImageToView(BRect r) const 525 { 526 BPoint leftTop(ImageToView(BPoint(r.left, r.top))); 527 BPoint rightBottom(ImageToView(BPoint(r.right, r.bottom))); 528 return BRect(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y); 529 } 530 531 void 532 ShowImageView::DrawBorder(BRect border) 533 { 534 BRect bounds(Bounds()); 535 // top 536 FillRect(BRect(0, 0, bounds.right, border.top-1), B_SOLID_LOW); 537 // left 538 FillRect(BRect(0, border.top, border.left-1, border.bottom), B_SOLID_LOW); 539 // right 540 FillRect(BRect(border.right+1, border.top, bounds.right, border.bottom), B_SOLID_LOW); 541 // bottom 542 FillRect(BRect(0, border.bottom+1, bounds.right, bounds.bottom), B_SOLID_LOW); 543 } 544 545 void 546 ShowImageView::LayoutCaption(BFont &font, BPoint &pos, BRect &rect) 547 { 548 font_height fontHeight; 549 float width, height; 550 BRect bounds(Bounds()); 551 font = be_plain_font; 552 width = font.StringWidth(fCaption.String()) + 1; // 1 for text shadow 553 font.GetHeight(&fontHeight); 554 height = fontHeight.ascent + fontHeight.descent; 555 // center text horizontally 556 pos.x = (bounds.left + bounds.right - width)/2; 557 // flush bottom 558 pos.y = bounds.bottom - fontHeight.descent - 5; 559 560 // background rectangle 561 rect.Set(0, 0, (width-1)+2, (height-1)+2+1); // 2 for border and 1 for text shadow 562 rect.OffsetTo(pos); 563 rect.OffsetBy(-1, -1-fontHeight.ascent); // -1 for border 564 } 565 566 void 567 ShowImageView::DrawCaption() 568 { 569 BFont font; 570 BPoint pos; 571 BRect rect; 572 LayoutCaption(font, pos, rect); 573 574 PushState(); 575 // draw background 576 SetDrawingMode(B_OP_ALPHA); 577 SetHighColor(0, 0, 255, 128); 578 FillRect(rect); 579 // draw text 580 SetDrawingMode(B_OP_OVER); 581 SetFont(&font); 582 SetLowColor(B_TRANSPARENT_COLOR); 583 // text shadow 584 pos += BPoint(1, 1); 585 SetHighColor(0, 0, 0); 586 SetPenSize(1); 587 DrawString(fCaption.String(), pos); 588 // text 589 pos -= BPoint(1, 1); 590 SetHighColor(255, 255, 0); 591 DrawString(fCaption.String(), pos); 592 PopState(); 593 } 594 595 void 596 ShowImageView::UpdateCaption() 597 { 598 BFont font; 599 BPoint pos; 600 BRect rect; 601 LayoutCaption(font, pos, rect); 602 603 // draw over portion of image where caption is located 604 BRegion clip(rect); 605 PushState(); 606 ConstrainClippingRegion(&clip); 607 Draw(rect); 608 PopState(); 609 } 610 611 Scaler* 612 ShowImageView::GetScaler(BRect rect) 613 { 614 if (fScaler == NULL || !fScaler->Matches(rect)) { 615 DeleteScaler(); 616 BMessenger msgr(this, Window()); 617 fScaler = new Scaler(fBitmap, rect, msgr, MSG_INVALIDATE); 618 fScaler->Start(); 619 } 620 return fScaler; 621 } 622 623 void 624 ShowImageView::DrawImage(BRect rect) 625 { 626 if (fScaleBilinear) { 627 Scaler* scaler = GetScaler(rect); 628 if (scaler != NULL && scaler->GetBitmap() != NULL && !scaler->IsRunning()) { 629 BBitmap* bitmap = scaler->GetBitmap(); 630 DrawBitmap(bitmap, BPoint(rect.left, rect.top)); 631 return; 632 } 633 } 634 DrawBitmap(fBitmap, fBitmap->Bounds(), rect); 635 } 636 637 void 638 ShowImageView::Draw(BRect updateRect) 639 { 640 if (fBitmap) { 641 if (!IsPrinting()) { 642 BRect rect = AlignBitmap(); 643 Setup(rect); 644 645 BRect border(rect); 646 border.InsetBy(-PEN_SIZE, -PEN_SIZE); 647 648 DrawBorder(border); 649 650 // Draw black rectangle around image 651 StrokeRect(border); 652 653 // Draw image 654 DrawImage(rect); 655 656 if (fShowCaption) { 657 // fShowCaption is set to false by ScrollRestricted() 658 // to prevent the caption from dirtying up the image 659 // during scrolling. 660 DrawCaption(); 661 } 662 663 if (HasSelection()) { 664 if (fSelBitmap) { 665 BRect srcBits, destRect; 666 GetSelMergeRects(srcBits, destRect); 667 destRect = ImageToView(destRect); 668 DrawBitmap(fSelBitmap, srcBits, destRect); 669 } 670 DrawSelectionBox(fSelectionRect); 671 } 672 } else { 673 DrawBitmap(fBitmap); 674 } 675 } 676 } 677 678 void 679 ShowImageView::DrawSelectionBox(BRect &rect) 680 { 681 BRect r = rect; 682 ConstrainToImage(r); 683 684 PushState(); 685 rgb_color white = {255, 255, 255}; 686 SetLowColor(white); 687 r = ImageToView(r); 688 StrokeLine(BPoint(r.left, r.top), BPoint(r.right, r.top), fPatternLeft); 689 StrokeLine(BPoint(r.right, r.top+1), BPoint(r.right, r.bottom-1), fPatternUp); 690 StrokeLine(BPoint(r.left, r.bottom), BPoint(r.right, r.bottom), fPatternRight); 691 StrokeLine(BPoint(r.left, r.top+1), BPoint(r.left, r.bottom-1), fPatternDown); 692 PopState(); 693 } 694 695 void 696 ShowImageView::FrameResized(float /* width */, float /* height */) 697 { 698 FixupScrollBars(); 699 } 700 701 void 702 ShowImageView::ConstrainToImage(BPoint &point) 703 { 704 point.ConstrainTo(fBitmap->Bounds()); 705 } 706 707 void 708 ShowImageView::ConstrainToImage(BRect &rect) 709 { 710 BRect bounds = fBitmap->Bounds(); 711 BPoint leftTop, rightBottom; 712 713 leftTop = rect.LeftTop(); 714 leftTop.ConstrainTo(bounds); 715 716 rightBottom = rect.RightBottom(); 717 rightBottom.ConstrainTo(bounds); 718 719 rect.SetLeftTop(leftTop); 720 rect.SetRightBottom(rightBottom); 721 } 722 723 BBitmap* 724 ShowImageView::CopySelection(uchar alpha) 725 { 726 bool hasAlpha = alpha != 255; 727 728 if (!HasSelection()) return NULL; 729 730 BRect rect(0, 0, fSelectionRect.Width(), fSelectionRect.Height()); 731 BView view(rect, NULL, B_FOLLOW_NONE, B_WILL_DRAW); 732 BBitmap *bitmap = new BBitmap(rect, hasAlpha ? B_RGBA32 : fBitmap->ColorSpace(), true); 733 if (bitmap == NULL) return NULL; 734 735 if (bitmap->Lock()) { 736 bitmap->AddChild(&view); 737 if (fSelBitmap) 738 view.DrawBitmap(fSelBitmap, fSelBitmap->Bounds(), rect); 739 else 740 view.DrawBitmap(fBitmap, fCopyFromRect, rect); 741 if (hasAlpha) { 742 view.SetDrawingMode(B_OP_SUBTRACT); 743 view.SetHighColor(0, 0, 0, 255-alpha); 744 view.FillRect(rect, B_SOLID_HIGH); 745 } 746 view.Sync(); 747 bitmap->RemoveChild(&view); 748 bitmap->Unlock(); 749 } 750 751 return bitmap; 752 } 753 754 bool 755 ShowImageView::AddSupportedTypes(BMessage* msg, BBitmap* bitmap) 756 { 757 bool found = false; 758 BTranslatorRoster *roster = BTranslatorRoster::Default(); 759 if (roster == NULL) return false; 760 761 BBitmapStream stream(bitmap); 762 763 translator_info *outInfo; 764 int32 outNumInfo; 765 if (roster->GetTranslators(&stream, NULL, &outInfo, &outNumInfo) == B_OK) { 766 for (int32 i = 0; i < outNumInfo; i++) { 767 const translation_format *fmts; 768 int32 num_fmts; 769 roster->GetOutputFormats(outInfo[i].translator, &fmts, &num_fmts); 770 for (int32 j = 0; j < num_fmts; j++) { 771 if (strcmp(fmts[j].MIME, "image/x-be-bitmap") != 0) { 772 // needed to send data in message 773 msg->AddString("be:types", fmts[j].MIME); 774 // needed to pass data via file 775 msg->AddString("be:filetypes", fmts[j].MIME); 776 msg->AddString("be:type_descriptions", fmts[j].name); 777 } 778 found = true; 779 } 780 } 781 } 782 stream.DetachBitmap(&bitmap); 783 return found; 784 } 785 786 void 787 ShowImageView::BeginDrag(BPoint sourcePoint) 788 { 789 BBitmap* bitmap = CopySelection(128); 790 if (bitmap == NULL) return; 791 792 SetMouseEventMask(B_POINTER_EVENTS); 793 BPoint leftTop(fSelectionRect.left, fSelectionRect.top); 794 795 // fill the drag message 796 BMessage drag(B_SIMPLE_DATA); 797 drag.AddInt32("be:actions", B_COPY_TARGET); 798 drag.AddString("be:clip_name", "Bitmap Clip"); 799 // XXX undocumented fields 800 drag.AddPoint("be:_source_point", sourcePoint); 801 drag.AddRect("be:_frame", fSelectionRect); 802 // XXX meaning unknown??? 803 // drag.AddInt32("be:_format", e.g.: B_TGA_FORMAT); 804 // drag.AddInt32("be_translator", translator_id); 805 // drag.AddPointer("be:_bitmap_ptr, ?); 806 if (AddSupportedTypes(&drag, bitmap)) { 807 // we also support "Passing Data via File" protocol 808 drag.AddString("be:types", B_FILE_MIME_TYPE); 809 // avoid flickering of dragged bitmap caused by drawing into the window 810 AnimateSelection(false); 811 // DragMessage takes ownership of bitmap 812 DragMessage(&drag, bitmap, B_OP_ALPHA, sourcePoint - leftTop); 813 } 814 } 815 816 bool 817 ShowImageView::OutputFormatForType(BBitmap* bitmap, const char* type, translation_format* format) 818 { 819 bool found = false; 820 821 BTranslatorRoster *roster = BTranslatorRoster::Default(); 822 if (roster == NULL) return false; 823 824 BBitmapStream stream(bitmap); 825 826 translator_info *outInfo; 827 int32 outNumInfo; 828 if (roster->GetTranslators(&stream, NULL, &outInfo, &outNumInfo) == B_OK) { 829 for (int32 i = 0; i < outNumInfo; i++) { 830 const translation_format *fmts; 831 int32 num_fmts; 832 roster->GetOutputFormats(outInfo[i].translator, &fmts, &num_fmts); 833 for (int32 j = 0; j < num_fmts; j++) { 834 if (strcmp(fmts[j].MIME, type) == 0) { 835 *format = fmts[j]; 836 found = true; 837 break; 838 } 839 } 840 } 841 } 842 stream.DetachBitmap(&bitmap); 843 return found; 844 } 845 846 void 847 ShowImageView::SaveToFile(BDirectory* dir, const char* name, BBitmap* bitmap, translation_format* format) 848 { 849 BTranslatorRoster *roster = BTranslatorRoster::Default(); 850 BBitmapStream stream(bitmap); // destructor deletes bitmap 851 // write data 852 BFile file(dir, name, B_WRITE_ONLY); 853 roster->Translate(&stream, NULL, NULL, &file, format->type); 854 // set mime type 855 BNodeInfo info(&file); 856 if (info.InitCheck() == B_OK) { 857 info.SetType(format->MIME); 858 } 859 } 860 861 void 862 ShowImageView::SendInMessage(BMessage* msg, BBitmap* bitmap, translation_format* format) 863 { 864 BMessage reply(B_MIME_DATA); 865 BBitmapStream stream(bitmap); // destructor deletes bitmap 866 BTranslatorRoster *roster = BTranslatorRoster::Default(); 867 BMallocIO memStream; 868 if (roster->Translate(&stream, NULL, NULL, &memStream, format->type) == B_OK) { 869 reply.AddData(format->MIME, B_MIME_TYPE, memStream.Buffer(), memStream.BufferLength()); 870 msg->SendReply(&reply); 871 } 872 } 873 874 void 875 ShowImageView::HandleDrop(BMessage* msg) 876 { 877 BMessage data(B_MIME_DATA); 878 entry_ref dirRef; 879 BString name, type; 880 bool saveToFile; 881 bool sendInMessage; 882 BBitmap *bitmap; 883 884 saveToFile = msg->FindString("be:filetypes", &type) == B_OK && 885 msg->FindRef("directory", &dirRef) == B_OK && 886 msg->FindString("name", &name) == B_OK; 887 888 sendInMessage = (!saveToFile) && msg->FindString("be:types", &type) == B_OK; 889 890 bitmap = CopySelection(); 891 if (bitmap == NULL) return; 892 893 translation_format format; 894 if (!OutputFormatForType(bitmap, type.String(), &format)) { 895 delete bitmap; 896 return; 897 } 898 899 if (saveToFile) { 900 BDirectory dir(&dirRef); 901 SaveToFile(&dir, name.String(), bitmap, &format); 902 } else if (sendInMessage) { 903 SendInMessage(msg, bitmap, &format); 904 } else { 905 delete bitmap; 906 } 907 } 908 909 void 910 ShowImageView::MoveImage() 911 { 912 BPoint point, delta; 913 uint32 buttons; 914 // get CURRENT position 915 GetMouse(&point, &buttons); 916 point = ConvertToScreen(point); 917 delta = fFirstPoint - point; 918 fFirstPoint = point; 919 ScrollRestrictedBy(delta.x, delta.y); 920 // in case we miss MouseUp 921 if ((GetMouseButtons() & B_TERTIARY_MOUSE_BUTTON) == 0) { 922 fMovesImage = false; 923 Invalidate(); 924 } 925 } 926 927 uint32 928 ShowImageView::GetMouseButtons() 929 { 930 uint32 buttons; 931 BPoint point; 932 GetMouse(&point, &buttons); 933 if (buttons == B_PRIMARY_MOUSE_BUTTON) { 934 if ((modifiers() & B_CONTROL_KEY) != 0) { 935 buttons = B_SECONDARY_MOUSE_BUTTON; // simulate second button 936 } else if ((modifiers() & B_SHIFT_KEY) != 0) { 937 buttons = B_TERTIARY_MOUSE_BUTTON; // simulate third button 938 } 939 } 940 return buttons; 941 } 942 943 void 944 ShowImageView::GetSelMergeRects(BRect &srcBits, BRect &destRect) 945 { 946 destRect = fSelectionRect; 947 ConstrainToImage(destRect); 948 949 srcBits = fSelectionRect; 950 if (srcBits.left < 0) 951 srcBits.left = -(srcBits.left); 952 else 953 srcBits.left = 0; 954 if (srcBits.top < 0) 955 srcBits.top = -(srcBits.top); 956 else 957 srcBits.top = 0; 958 if (srcBits.right > fBitmap->Bounds().right) 959 srcBits.right = srcBits.left + destRect.Width(); 960 else 961 srcBits.right = fSelBitmap->Bounds().right; 962 if (srcBits.bottom > fBitmap->Bounds().bottom) 963 srcBits.bottom = srcBits.top + destRect.Height(); 964 else 965 srcBits.bottom = fSelBitmap->Bounds().bottom; 966 } 967 968 void 969 ShowImageView::MergeSelection() 970 { 971 if (!HasSelection() || !fSelBitmap) 972 return; 973 974 // Merge selection with background 975 BView view(fBitmap->Bounds(), NULL, B_FOLLOW_NONE, B_WILL_DRAW); 976 BBitmap *bitmap = new BBitmap(fBitmap->Bounds(), fBitmap->ColorSpace(), true); 977 if (bitmap == NULL) 978 return; 979 980 if (bitmap->Lock()) { 981 bitmap->AddChild(&view); 982 view.DrawBitmap(fBitmap, fBitmap->Bounds()); 983 984 BRect srcBits, destRect; 985 GetSelMergeRects(srcBits, destRect); 986 view.DrawBitmap(fSelBitmap, srcBits, destRect); 987 988 view.Sync(); 989 bitmap->RemoveChild(&view); 990 bitmap->Unlock(); 991 992 DeleteBitmap(); 993 fBitmap = bitmap; 994 } else 995 delete bitmap; 996 } 997 998 void 999 ShowImageView::MouseDown(BPoint position) 1000 { 1001 BPoint point; 1002 uint32 buttons; 1003 MakeFocus(true); 1004 1005 point = ViewToImage(position); 1006 buttons = GetMouseButtons(); 1007 1008 if (HasSelection() && fSelectionRect.Contains(point) && 1009 (buttons & (B_PRIMARY_MOUSE_BUTTON | B_SECONDARY_MOUSE_BUTTON))) { 1010 if (!fSelBitmap) { 1011 fSelBitmap = CopySelection(); 1012 } 1013 BPoint sourcePoint = point; 1014 BeginDrag(sourcePoint); 1015 1016 while (buttons) { 1017 // Keep reading mouse movement until 1018 // the user lets up on all mouse buttons 1019 GetMouse(&point, &buttons); 1020 snooze(25 * 1000); 1021 // sleep for 25 milliseconds to minimize CPU usage during loop 1022 } 1023 1024 if (Bounds().Contains(point)) { 1025 // If selection stayed inside this view 1026 // (Some of the selection may be in the border area, which can be OK) 1027 BPoint last, diff; 1028 last = ViewToImage(point); 1029 diff = last - sourcePoint; 1030 1031 BRect newSelection = fSelectionRect; 1032 newSelection.OffsetBy(diff); 1033 1034 if (fBitmap->Bounds().Intersects(newSelection)) { 1035 // Do not accept the new selection box location 1036 // if it does not intersect with the bitmap rectangle 1037 fSelectionRect = newSelection; 1038 Invalidate(); 1039 } 1040 } 1041 1042 AnimateSelection(true); 1043 1044 } else if (buttons == B_PRIMARY_MOUSE_BUTTON) { 1045 MergeSelection(); 1046 // If there is an existing selection, 1047 // Make it part of the background image 1048 1049 // begin new selection 1050 SetHasSelection(true); 1051 fMakesSelection = true; 1052 SetMouseEventMask(B_POINTER_EVENTS); 1053 ConstrainToImage(point); 1054 fFirstPoint = point; 1055 fCopyFromRect.Set(point.x, point.y, point.x, point.y); 1056 fSelectionRect = fCopyFromRect; 1057 Invalidate(); 1058 } else if (buttons == B_SECONDARY_MOUSE_BUTTON) { 1059 ShowPopUpMenu(ConvertToScreen(position)); 1060 } else if (buttons == B_TERTIARY_MOUSE_BUTTON) { 1061 // move image in window 1062 SetMouseEventMask(B_POINTER_EVENTS); 1063 fMovesImage = true; 1064 fFirstPoint = ConvertToScreen(position); 1065 Invalidate(); 1066 } 1067 } 1068 1069 void 1070 ShowImageView::UpdateSelectionRect(BPoint point, bool final) { 1071 BRect oldSelection = fCopyFromRect; 1072 point = ViewToImage(point); 1073 ConstrainToImage(point); 1074 fCopyFromRect.left = min(fFirstPoint.x, point.x); 1075 fCopyFromRect.right = max(fFirstPoint.x, point.x); 1076 fCopyFromRect.top = min(fFirstPoint.y, point.y); 1077 fCopyFromRect.bottom = max(fFirstPoint.y, point.y); 1078 fSelectionRect = fCopyFromRect; 1079 if (final) { 1080 // selection must contain a few pixels 1081 if (fCopyFromRect.Width() * fCopyFromRect.Height() <= 1) { 1082 SetHasSelection(false); 1083 } 1084 } 1085 if (oldSelection != fCopyFromRect || !HasSelection()) { 1086 BRect updateRect; 1087 updateRect = oldSelection | fCopyFromRect; 1088 updateRect = ImageToView(updateRect); 1089 updateRect.InsetBy(-PEN_SIZE, -PEN_SIZE); 1090 Invalidate(updateRect); 1091 } 1092 } 1093 1094 void 1095 ShowImageView::MouseMoved(BPoint point, uint32 state, const BMessage *pmsg) 1096 { 1097 if (fMakesSelection) { 1098 UpdateSelectionRect(point, false); 1099 } else if (fMovesImage) { 1100 MoveImage(); 1101 } 1102 } 1103 1104 void 1105 ShowImageView::MouseUp(BPoint point) 1106 { 1107 if (fMakesSelection) { 1108 UpdateSelectionRect(point, true); 1109 fMakesSelection = false; 1110 } else if (fMovesImage) { 1111 MoveImage(); 1112 if (fMovesImage) { 1113 fMovesImage = false; 1114 Invalidate(); 1115 } 1116 } 1117 AnimateSelection(true); 1118 } 1119 1120 float 1121 ShowImageView::LimitToRange(float v, orientation o, bool absolute) 1122 { 1123 BScrollBar* psb = ScrollBar(o); 1124 if (psb) { 1125 float min, max, pos; 1126 pos = v; 1127 if (!absolute) { 1128 pos += psb->Value(); 1129 } 1130 psb->GetRange(&min, &max); 1131 if (pos < min) { 1132 pos = min; 1133 } else if (pos > max) { 1134 pos = max; 1135 } 1136 v = pos; 1137 if (!absolute) { 1138 v -= psb->Value(); 1139 } 1140 } 1141 return v; 1142 } 1143 1144 void 1145 ShowImageView::ScrollRestricted(float x, float y, bool absolute) 1146 { 1147 if (x != 0) { 1148 x = LimitToRange(x, B_HORIZONTAL, absolute); 1149 } 1150 1151 if (y != 0) { 1152 y = LimitToRange(y, B_VERTICAL, absolute); 1153 } 1154 1155 // hide the caption when using mouse wheel 1156 // in full screen mode 1157 bool caption = fShowCaption; 1158 if (caption) { 1159 fShowCaption = false; 1160 UpdateCaption(); 1161 } 1162 1163 ScrollBy(x, y); 1164 1165 if (caption) { 1166 // show the caption again 1167 fShowCaption = true; 1168 UpdateCaption(); 1169 } 1170 } 1171 1172 // XXX method is not unused 1173 void 1174 ShowImageView::ScrollRestrictedTo(float x, float y) 1175 { 1176 ScrollRestricted(x, y, true); 1177 } 1178 1179 void 1180 ShowImageView::ScrollRestrictedBy(float x, float y) 1181 { 1182 ScrollRestricted(x, y, false); 1183 } 1184 1185 void 1186 ShowImageView::KeyDown (const char * bytes, int32 numBytes) 1187 { 1188 if (numBytes == 1) { 1189 switch (*bytes) { 1190 case B_DOWN_ARROW: 1191 ScrollRestrictedBy(0, 10); 1192 break; 1193 case B_UP_ARROW: 1194 ScrollRestrictedBy(0, -10); 1195 break; 1196 case B_LEFT_ARROW: 1197 ScrollRestrictedBy(-10, 0); 1198 break; 1199 case B_RIGHT_ARROW: 1200 ScrollRestrictedBy(10, 0); 1201 break; 1202 case B_SPACE: 1203 case B_ENTER: 1204 NextFile(); 1205 break; 1206 case B_BACKSPACE: 1207 PrevFile(); 1208 break; 1209 case B_HOME: 1210 break; 1211 case B_END: 1212 break; 1213 case B_ESCAPE: 1214 if (fSlideShow) { 1215 BMessenger msgr(Window()); 1216 msgr.SendMessage(MSG_SLIDE_SHOW); 1217 } 1218 break; 1219 } 1220 } 1221 } 1222 1223 void 1224 ShowImageView::MouseWheelChanged(BMessage *msg) 1225 { 1226 // The BeOS driver does not currently support 1227 // X wheel scrolling, therefore, dx is zero. 1228 // |dy| is the number of notches scrolled up or down. 1229 // When the wheel is scrolled down (towards the user) dy > 0 1230 // When the wheel is scrolled up (away from the user) dy < 0 1231 const float kscrollBy = 40; 1232 float dy, dx; 1233 float x, y; 1234 x = 0; y = 0; 1235 if (msg->FindFloat("be:wheel_delta_x", &dx) == B_OK) { 1236 x = dx * kscrollBy; 1237 } 1238 if (msg->FindFloat("be:wheel_delta_y", &dy) == B_OK) { 1239 y = dy * kscrollBy; 1240 } 1241 1242 ScrollRestrictedBy(x, y); 1243 } 1244 1245 void 1246 ShowImageView::ShowPopUpMenu(BPoint screen) 1247 { 1248 BPopUpMenu* menu = new BPopUpMenu("PopUpMenu"); 1249 menu->SetAsyncAutoDestruct(true); 1250 menu->SetRadioMode(false); 1251 1252 ShowImageWindow* showImage = dynamic_cast<ShowImageWindow*>(Window()); 1253 if (showImage) { 1254 showImage->BuildViewMenu(menu); 1255 } 1256 menu->AddSeparatorItem(); 1257 menu->AddItem(new BMenuItem("Cancel", 0, 0)); 1258 1259 screen -= BPoint(10, 10); 1260 menu->Go(screen, true, false, true); 1261 } 1262 1263 void 1264 ShowImageView::SettingsSetBool(const char* name, bool value) 1265 { 1266 ShowImageSettings* settings; 1267 settings = my_app->Settings(); 1268 if (settings->Lock()) { 1269 settings->SetBool(name, value); 1270 settings->Unlock(); 1271 } 1272 } 1273 1274 void 1275 ShowImageView::MessageReceived(BMessage *pmsg) 1276 { 1277 switch (pmsg->what) { 1278 case B_COPY_TARGET: 1279 HandleDrop(pmsg); 1280 break; 1281 case B_MOUSE_WHEEL_CHANGED: 1282 MouseWheelChanged(pmsg); 1283 break; 1284 case MSG_INVALIDATE: 1285 Invalidate(); 1286 break; 1287 default: 1288 BView::MessageReceived(pmsg); 1289 break; 1290 } 1291 } 1292 1293 void 1294 ShowImageView::FixupScrollBar(orientation o, float bitmapLength, float viewLength) 1295 { 1296 float prop, range; 1297 BScrollBar *psb; 1298 1299 psb = ScrollBar(o); 1300 if (psb) { 1301 if (fHasBorder && !fShrinkOrZoomToBounds) { 1302 bitmapLength += BORDER_WIDTH*2; 1303 } 1304 range = bitmapLength - viewLength; 1305 if (range < 0.0) { 1306 range = 0.0; 1307 } 1308 prop = viewLength / bitmapLength; 1309 if (prop > 1.0) { 1310 prop = 1.0; 1311 } 1312 psb->SetRange(0, range); 1313 psb->SetProportion(prop); 1314 psb->SetSteps(10, 100); 1315 } 1316 } 1317 1318 void 1319 ShowImageView::FixupScrollBars() 1320 { 1321 BRect rctview = Bounds(), rctbitmap(0, 0, 0, 0); 1322 if (fBitmap) { 1323 BRect rect(AlignBitmap()); 1324 rctbitmap.Set(0, 0, rect.Width(), rect.Height()); 1325 } 1326 1327 FixupScrollBar(B_HORIZONTAL, rctbitmap.Width(), rctview.Width()); 1328 FixupScrollBar(B_VERTICAL, rctbitmap.Height(), rctview.Height()); 1329 } 1330 1331 int32 1332 ShowImageView::CurrentPage() 1333 { 1334 return fDocumentIndex; 1335 } 1336 1337 int32 1338 ShowImageView::PageCount() 1339 { 1340 return fDocumentCount; 1341 } 1342 1343 void 1344 ShowImageView::Cut() 1345 { 1346 CopySelectionToClipboard(); 1347 ClearSelection(); 1348 } 1349 1350 void 1351 ShowImageView::Paste() 1352 { 1353 if (be_clipboard->Lock()) { 1354 BMessage *pclip; 1355 if ((pclip = be_clipboard->Data()) != NULL) { 1356 BBitmap *pbits = dynamic_cast<BBitmap *>(BBitmap::Instantiate(pclip)); 1357 if (pbits) { 1358 MergeSelection(); 1359 1360 SetHasSelection(true); 1361 fSelBitmap = pbits; 1362 fCopyFromRect = BRect(); 1363 fSelectionRect = pbits->Bounds(); 1364 1365 BPoint point; 1366 if (pclip->FindPoint("be:location", &point) == B_OK && 1367 fBitmap->Bounds().Contains(point)) 1368 // Set the selection rectangle to the same location it was 1369 // copied from, but only if the background bitmap is large enough 1370 // to contain that point 1371 fSelectionRect.OffsetBy(point); 1372 1373 Invalidate(); 1374 } 1375 } 1376 1377 be_clipboard->Unlock(); 1378 } 1379 } 1380 1381 void 1382 ShowImageView::SelectAll() 1383 { 1384 SetHasSelection(true); 1385 fCopyFromRect.Set(0, 0, fBitmap->Bounds().Width(), fBitmap->Bounds().Height()); 1386 fSelectionRect = fCopyFromRect; 1387 Invalidate(); 1388 } 1389 1390 void 1391 ShowImageView::ClearSelection() 1392 { 1393 if (HasSelection()) { 1394 SetHasSelection(false); 1395 Invalidate(); 1396 } 1397 } 1398 1399 void 1400 ShowImageView::SetHasSelection(bool bHasSelection) 1401 { 1402 DeleteSelBitmap(); 1403 fbHasSelection = bHasSelection; 1404 1405 BMessage msg(MSG_SELECTION); 1406 msg.AddBool("has_selection", fbHasSelection); 1407 BMessenger msgr(Window()); 1408 msgr.SendMessage(&msg); 1409 } 1410 1411 void 1412 ShowImageView::CopySelectionToClipboard() 1413 { 1414 if (HasSelection() && be_clipboard->Lock()) { 1415 be_clipboard->Clear(); 1416 BMessage *clip = NULL; 1417 if ((clip = be_clipboard->Data()) != NULL) { 1418 BMessage data; 1419 BBitmap* bitmap = CopySelection(); 1420 if (bitmap != NULL) { 1421 #if 0 1422 // According to BeBook and Becasso, Gobe Productive do the following. 1423 // Paste works in Productive, but not in Becasso and original ShowImage. 1424 BMessage msg(B_OK); // Becasso uses B_TRANSLATOR_BITMAP, BeBook says its unused 1425 bitmap->Archive(&msg); 1426 clip->AddMessage("image/x-be-bitmap", &msg); 1427 #else 1428 // original ShowImage performs this. Paste works with original ShowImage. 1429 bitmap->Archive(clip); 1430 // original ShowImage uses be:location for insertion point 1431 clip->AddPoint("be:location", BPoint(fSelectionRect.left, fSelectionRect.top)); 1432 #endif 1433 delete bitmap; 1434 be_clipboard->Commit(); 1435 } 1436 } 1437 be_clipboard->Unlock(); 1438 } 1439 } 1440 1441 void 1442 ShowImageView::FirstPage() 1443 { 1444 if (fDocumentIndex != 1) { 1445 fDocumentIndex = 1; 1446 SetImage(NULL); 1447 } 1448 } 1449 1450 void 1451 ShowImageView::LastPage() 1452 { 1453 if (fDocumentIndex != fDocumentCount) { 1454 fDocumentIndex = fDocumentCount; 1455 SetImage(NULL); 1456 } 1457 } 1458 1459 void 1460 ShowImageView::NextPage() 1461 { 1462 if (fDocumentIndex < fDocumentCount) { 1463 fDocumentIndex++; 1464 SetImage(NULL); 1465 } 1466 } 1467 1468 void 1469 ShowImageView::PrevPage() 1470 { 1471 if (fDocumentIndex > 1) { 1472 fDocumentIndex--; 1473 SetImage(NULL); 1474 } 1475 } 1476 1477 int 1478 ShowImageView::CompareEntries(const void* a, const void* b) 1479 { 1480 entry_ref *r1, *r2; 1481 r1 = *(entry_ref**)a; 1482 r2 = *(entry_ref**)b; 1483 return strcasecmp(r1->name, r2->name); 1484 } 1485 1486 void 1487 ShowImageView::GoToPage(int32 page) 1488 { 1489 if (page > 0 && page <= fDocumentCount && page != fDocumentIndex) { 1490 fDocumentIndex = page; 1491 SetImage(NULL); 1492 } 1493 } 1494 1495 void 1496 ShowImageView::FreeEntries(BList* entries) 1497 { 1498 const int32 n = entries->CountItems(); 1499 for (int32 i = 0; i < n; i ++) { 1500 entry_ref* ref = (entry_ref*)entries->ItemAt(i); 1501 delete ref; 1502 } 1503 entries->MakeEmpty(); 1504 } 1505 1506 bool 1507 ShowImageView::FindNextImage(entry_ref* image, bool next, bool rewind) 1508 { 1509 ASSERT(next || !rewind); 1510 BEntry curImage(&fCurrentRef); 1511 entry_ref entry, *ref; 1512 BDirectory parent; 1513 BList entries; 1514 bool found = false; 1515 int32 cur; 1516 1517 if (curImage.GetParent(&parent) != B_OK) 1518 return false; 1519 1520 while (parent.GetNextRef(&entry) == B_OK) { 1521 if (entry != fCurrentRef) { 1522 entries.AddItem(new entry_ref(entry)); 1523 } else { 1524 // insert current ref, so we can find it easily after sorting 1525 entries.AddItem(&fCurrentRef); 1526 } 1527 } 1528 1529 entries.SortItems(CompareEntries); 1530 1531 cur = entries.IndexOf(&fCurrentRef); 1532 ASSERT(cur >= 0); 1533 1534 // remove it so FreeEntries() does not delete it 1535 entries.RemoveItem(&fCurrentRef); 1536 1537 if (next) { 1538 // find the next image in the list 1539 if (rewind) cur = 0; // start with first 1540 for (; (ref = (entry_ref*)entries.ItemAt(cur)) != NULL; cur ++) { 1541 if (IsImage(ref)) { 1542 found = true; 1543 *image = (const entry_ref)*ref; 1544 break; 1545 } 1546 } 1547 } else { 1548 // find the previous image in the list 1549 cur --; 1550 for (; cur >= 0; cur --) { 1551 ref = (entry_ref*)entries.ItemAt(cur); 1552 if (IsImage(ref)) { 1553 found = true; 1554 *image = (const entry_ref)*ref; 1555 break; 1556 } 1557 } 1558 } 1559 1560 FreeEntries(&entries); 1561 return found; 1562 } 1563 1564 bool 1565 ShowImageView::ShowNextImage(bool next, bool rewind) 1566 { 1567 entry_ref ref; 1568 1569 if (FindNextImage(&ref, next, rewind)) { 1570 SetImage(&ref); 1571 return true; 1572 } 1573 return false; 1574 } 1575 1576 bool 1577 ShowImageView::NextFile() 1578 { 1579 return ShowNextImage(true, false); 1580 } 1581 1582 bool 1583 ShowImageView::PrevFile() 1584 { 1585 return ShowNextImage(false, false); 1586 } 1587 1588 bool 1589 ShowImageView::FirstFile() 1590 { 1591 return ShowNextImage(true, true); 1592 } 1593 1594 void 1595 ShowImageView::SetZoom(float zoom) 1596 { 1597 if (fScaleBilinear && fZoom != zoom) { 1598 DeleteScaler(); 1599 } 1600 fZoom = zoom; 1601 FixupScrollBars(); 1602 Invalidate(); 1603 } 1604 1605 void 1606 ShowImageView::ZoomIn() 1607 { 1608 SetZoom(fZoom + 0.25); 1609 } 1610 1611 void 1612 ShowImageView::ZoomOut() 1613 { 1614 if (fZoom > 0.25) { 1615 SetZoom(fZoom - 0.25); 1616 } 1617 } 1618 1619 void 1620 ShowImageView::SetSlideShowDelay(float seconds) 1621 { 1622 ShowImageSettings* settings; 1623 fSlideShowDelay = (int)(seconds * 10.0); 1624 settings = my_app->Settings(); 1625 if (settings->Lock()) { 1626 settings->SetInt32("SlideShowDelay", fSlideShowDelay); 1627 settings->Unlock(); 1628 } 1629 } 1630 1631 void 1632 ShowImageView::StartSlideShow() 1633 { 1634 fSlideShow = true; fSlideShowCountDown = fSlideShowDelay; 1635 } 1636 1637 void 1638 ShowImageView::StopSlideShow() 1639 { 1640 fSlideShow = false; 1641 } 1642 1643 int32 1644 ShowImageView::BytesPerPixel(color_space cs) const 1645 { 1646 switch (cs) { 1647 case B_RGB32: // fall through 1648 case B_RGB32_BIG: // fall through 1649 case B_RGBA32: // fall through 1650 case B_RGBA32_BIG: return 4; 1651 1652 case B_RGB24_BIG: // fall through 1653 case B_RGB24: return 3; 1654 1655 case B_RGB16: // fall through 1656 case B_RGB16_BIG: // fall through 1657 case B_RGB15: // fall through 1658 case B_RGB15_BIG: // fall through 1659 case B_RGBA15: // fall through 1660 case B_RGBA15_BIG: return 2; 1661 1662 case B_GRAY8: // fall through 1663 case B_CMAP8: return 1; 1664 case B_GRAY1: return 0; 1665 default: return -1; 1666 } 1667 } 1668 1669 void 1670 ShowImageView::CopyPixel(uchar* dest, int32 destX, int32 destY, int32 destBPR, uchar* src, int32 x, int32 y, int32 bpr, int32 bpp) 1671 { 1672 dest += destBPR * destY + destX * bpp; 1673 src += bpr * y + x * bpp; 1674 memcpy(dest, src, bpp); 1675 } 1676 1677 // FIXME: In color space with alpha channel the alpha value should not be inverted! 1678 // This could be a problem when image is saved and opened in another application. 1679 // Note: For B_CMAP8 InvertPixel inverts the color index not the color value! 1680 void 1681 ShowImageView::InvertPixel(int32 x, int32 y, uchar* dest, int32 destBPR, uchar* src, int32 bpr, int32 bpp) 1682 { 1683 dest += destBPR * y + x * bpp; 1684 src += bpr * y + x * bpp; 1685 for (; bpp > 0; bpp --, dest ++, src ++) { 1686 *dest = ~*src; 1687 } 1688 } 1689 1690 // DoImageOperation supports only color spaces with bytes per pixel >= 1 1691 // See above for limitations about kInvert 1692 void 1693 ShowImageView::DoImageOperation(image_operation op) 1694 { 1695 color_space cs; 1696 int32 bpp; 1697 BBitmap* bm; 1698 int32 width, height; 1699 uchar* src; 1700 uchar* dest; 1701 int32 bpr, destBPR; 1702 int32 x, y, destX, destY; 1703 BRect rect; 1704 1705 if (fBitmap == NULL) return; 1706 1707 cs = fBitmap->ColorSpace(); 1708 bpp = BytesPerPixel(cs); 1709 if (bpp < 1) return; 1710 1711 width = fBitmap->Bounds().IntegerWidth(); 1712 height = fBitmap->Bounds().IntegerHeight(); 1713 1714 if (op == kRotateClockwise || op == kRotateAntiClockwise) { 1715 rect.Set(0, 0, height, width); 1716 } else { 1717 rect.Set(0, 0, width, height); 1718 } 1719 1720 bm = new BBitmap(rect, cs); 1721 if (bm == NULL) return; 1722 1723 src = (uchar*)fBitmap->Bits(); 1724 dest = (uchar*)bm->Bits(); 1725 bpr = fBitmap->BytesPerRow(); 1726 destBPR = bm->BytesPerRow(); 1727 1728 switch (op) { 1729 case kRotateClockwise: 1730 for (y = 0; y <= height; y ++) { 1731 for (x = 0; x <= width; x ++) { 1732 destX = height - y; 1733 destY = x; 1734 CopyPixel(dest, destX, destY, destBPR, src, x, y, bpr, bpp); 1735 } 1736 } 1737 break; 1738 case kRotateAntiClockwise: 1739 for (y = 0; y <= height; y ++) { 1740 for (x = 0; x <= width; x ++) { 1741 destX = y; 1742 destY = width - x; 1743 CopyPixel(dest, destX, destY, destBPR, src, x, y, bpr, bpp); 1744 } 1745 } 1746 break; 1747 case kMirrorHorizontal: 1748 for (y = 0; y <= height; y ++) { 1749 for (x = 0; x <= width; x ++) { 1750 destX = x; 1751 destY = height - y; 1752 CopyPixel(dest, destX, destY, destBPR, src, x, y, bpr, bpp); 1753 } 1754 } 1755 break; 1756 case kMirrorVertical: 1757 for (y = 0; y <= height; y ++) { 1758 for (x = 0; x <= width; x ++) { 1759 destX = width - x; 1760 destY = y; 1761 CopyPixel(dest, destX, destY, destBPR, src, x, y, bpr, bpp); 1762 } 1763 } 1764 break; 1765 case kInvert: 1766 for (y = 0; y <= height; y ++) { 1767 for (x = 0; x <= width; x ++) { 1768 InvertPixel(x, y, dest, destBPR, src, bpr, bpp); 1769 } 1770 } 1771 break; 1772 } 1773 1774 // set new bitmap 1775 DeleteBitmap(); 1776 fBitmap = bm; 1777 // remove selection 1778 SetHasSelection(false); 1779 Notify(NULL); 1780 } 1781 1782 void 1783 ShowImageView::Rotate(int degree) 1784 { 1785 if (degree == 90) { 1786 DoImageOperation(kRotateClockwise); 1787 } else if (degree == 270) { 1788 DoImageOperation(kRotateAntiClockwise); 1789 } 1790 } 1791 1792 void 1793 ShowImageView::Mirror(bool vertical) 1794 { 1795 if (vertical) { 1796 DoImageOperation(kMirrorVertical); 1797 } else { 1798 DoImageOperation(kMirrorHorizontal); 1799 } 1800 } 1801 1802 void 1803 ShowImageView::Invert() 1804 { 1805 DoImageOperation(kInvert); 1806 } 1807