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 <math.h> 31 #include <Debug.h> 32 #include <Message.h> 33 #include <ScrollBar.h> 34 #include <StopWatch.h> 35 #include <Alert.h> 36 #include <MenuBar.h> 37 #include <MenuItem.h> 38 #include <File.h> 39 #include <Bitmap.h> 40 #include <TranslatorRoster.h> 41 #include <BitmapStream.h> 42 #include <Rect.h> 43 #include <SupportDefs.h> 44 #include <Directory.h> 45 #include <Application.h> 46 #include <Roster.h> 47 #include <NodeInfo.h> 48 #include <Clipboard.h> 49 #include <Path.h> 50 #include <PopUpMenu.h> 51 #include <Region.h> 52 #include <Screen.h> 53 54 55 #include "ShowImageApp.h" 56 #include "ShowImageConstants.h" 57 #include "ShowImageView.h" 58 #include "ShowImageWindow.h" 59 60 #ifndef min 61 #define min(a,b) ((a)>(b)?(b):(a)) 62 #endif 63 #ifndef max 64 #define max(a,b) ((a)>(b)?(a):(b)) 65 #endif 66 67 #define SHOW_IMAGE_ORIENTATION_ATTRIBUTE "ShowImage:orientation" 68 #define BORDER_WIDTH 16 69 #define BORDER_HEIGHT 16 70 #define PEN_SIZE 1.0f 71 const rgb_color kBorderColor = { 0, 0, 0, 255 }; 72 73 enum ShowImageView::image_orientation 74 ShowImageView::fTransformation[ImageProcessor::kNumberOfAffineTransformations][kNumberOfOrientations] = 75 { 76 // rotate 90° 77 {k90, k180, k270, k0, k270V, k0V, k90V, k0H}, 78 // rotate -90° 79 {k270, k0, k90, k180, k90V, k0H, k270V, k0V}, 80 // mirror vertical 81 {k0H, k270V, k0V, k90V, k180, k270, k0, k90}, 82 // mirror horizontal 83 {k0V, k90V, k0H, k270V, k0, k90, k180, k270} 84 }; 85 86 87 // use patterns to simulate marching ants for selection 88 void 89 ShowImageView::InitPatterns() 90 { 91 uchar p; 92 uchar p1 = 0x33; 93 uchar p2 = 0xCC; 94 for (int i = 0; i <= 7; i ++) { 95 fPatternLeft.data[i] = p1; 96 fPatternRight.data[i] = p2; 97 if ((i / 2) % 2 == 0) { 98 p = 255; 99 } else { 100 p = 0; 101 } 102 fPatternUp.data[i] = p; 103 fPatternDown.data[i] = ~p; 104 } 105 } 106 107 void 108 ShowImageView::RotatePatterns() 109 { 110 int i; 111 uchar p; 112 bool set; 113 114 // rotate up 115 p = fPatternUp.data[0]; 116 for (i = 0; i <= 6; i ++) { 117 fPatternUp.data[i] = fPatternUp.data[i+1]; 118 } 119 fPatternUp.data[7] = p; 120 121 // rotate down 122 p = fPatternDown.data[7]; 123 for (i = 7; i >= 1; i --) { 124 fPatternDown.data[i] = fPatternDown.data[i-1]; 125 } 126 fPatternDown.data[0] = p; 127 128 // rotate to left 129 p = fPatternLeft.data[0]; 130 set = (p & 0x80) != 0; 131 p <<= 1; 132 p &= 0xfe; 133 if (set) p |= 1; 134 memset(fPatternLeft.data, p, 8); 135 136 // rotate to right 137 p = fPatternRight.data[0]; 138 set = (p & 1) != 0; 139 p >>= 1; 140 if (set) p |= 0x80; 141 memset(fPatternRight.data, p, 8); 142 } 143 144 void 145 ShowImageView::AnimateSelection(bool a) 146 { 147 fAnimateSelection = a; 148 } 149 150 void 151 ShowImageView::Pulse() 152 { 153 // animate marching ants 154 if (HasSelection() && fAnimateSelection && Window()->IsActive()) { 155 RotatePatterns(); 156 DrawSelectionBox(); 157 } 158 if (fSlideShow) { 159 fSlideShowCountDown --; 160 if (fSlideShowCountDown <= 0) { 161 fSlideShowCountDown = fSlideShowDelay; 162 if (!NextFile()) { 163 FirstFile(); 164 } 165 } 166 } 167 #if DELAYED_SCALING 168 if (fBitmap && (fScaleBilinear || fDither) && fScalingCountDown > 0) { 169 fScalingCountDown --; 170 if (fScalingCountDown == 0) { 171 GetScaler(AlignBitmap()); 172 } 173 } 174 #endif 175 } 176 177 ShowImageView::ShowImageView(BRect rect, const char *name, uint32 resizingMode, 178 uint32 flags) 179 : BView(rect, name, resizingMode, flags) 180 { 181 ShowImageSettings* settings; 182 settings = my_app->Settings(); 183 184 InitPatterns(); 185 fDither = false; 186 fBitmap = NULL; 187 fSelBitmap = NULL; 188 fDocumentIndex = 1; 189 fDocumentCount = 1; 190 fAnimateSelection = true; 191 fHasSelection = false; 192 fShrinkToBounds = false; 193 fZoomToBounds = false; 194 fHasBorder = true; 195 fHAlignment = B_ALIGN_LEFT; 196 fVAlignment = B_ALIGN_TOP; 197 fSlideShow = false; 198 fSlideShowDelay = 3 * 10; // 3 seconds 199 fShowCaption = false; 200 fZoom = 1.0; 201 fMovesImage = false; 202 fScaleBilinear = false; 203 fScaler = NULL; 204 #if DELAYED_SCALING 205 fScalingCountDown = 10; 206 #endif 207 208 if (settings->Lock()) { 209 fDither = settings->GetBool("Dither", fDither); 210 fShrinkToBounds = settings->GetBool("ShrinkToBounds", fShrinkToBounds); 211 fZoomToBounds = settings->GetBool("ZoomToBounds", fZoomToBounds); 212 fSlideShowDelay = settings->GetInt32("SlideShowDelay", fSlideShowDelay); 213 fScaleBilinear = settings->GetBool("ScaleBilinear", fScaleBilinear); 214 settings->Unlock(); 215 } 216 217 SetViewColor(B_TRANSPARENT_COLOR); 218 SetHighColor(kBorderColor); 219 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 220 SetPenSize(PEN_SIZE); 221 } 222 223 ShowImageView::~ShowImageView() 224 { 225 DeleteBitmap(); 226 } 227 228 bool 229 ShowImageView::IsImage(const entry_ref *pref) 230 { 231 if (!pref) 232 return false; 233 234 BFile file(pref, B_READ_ONLY); 235 if (file.InitCheck() != B_OK) 236 return false; 237 238 BTranslatorRoster *proster = BTranslatorRoster::Default(); 239 if (!proster) 240 return false; 241 242 BMessage ioExtension; 243 if (ioExtension.AddInt32("/documentIndex", fDocumentIndex) != B_OK) 244 return false; 245 246 translator_info info; 247 memset(&info, 0, sizeof(translator_info)); 248 if (proster->Identify(&file, &ioExtension, &info, 0, NULL, 249 B_TRANSLATOR_BITMAP) != B_OK) 250 return false; 251 252 return true; 253 } 254 255 void 256 ShowImageView::SetTrackerMessenger(const BMessenger& trackerMessenger) 257 { 258 fTrackerMessenger = trackerMessenger; 259 } 260 261 // send message to parent about new image 262 void 263 ShowImageView::Notify(const char* status) 264 { 265 BMessage msg(MSG_UPDATE_STATUS); 266 if (status != NULL) { 267 msg.AddString("status", status); 268 } 269 msg.AddInt32("colors", fBitmap->ColorSpace()); 270 BMessenger msgr(Window()); 271 msgr.SendMessage(&msg); 272 273 FixupScrollBars(); 274 Invalidate(); 275 } 276 277 void 278 ShowImageView::AddToRecentDocuments() 279 { 280 be_roster->AddToRecentDocuments(&fCurrentRef, APP_SIG); 281 } 282 283 void 284 ShowImageView::DeleteScaler() 285 { 286 if (fScaler) { 287 fScaler->Stop(); 288 delete fScaler; 289 fScaler = NULL; 290 } 291 #if DELAYED_SCALING 292 fScalingCountDown = 3; // delay for 3/10 seconds 293 #endif 294 } 295 296 void 297 ShowImageView::DeleteBitmap() 298 { 299 DeleteScaler(); 300 DeleteSelBitmap(); 301 delete fBitmap; 302 fBitmap = NULL; 303 } 304 305 void ShowImageView::DeleteSelBitmap() 306 { 307 delete fSelBitmap; 308 fSelBitmap = NULL; 309 } 310 311 status_t 312 ShowImageView::SetImage(const entry_ref *pref) 313 { 314 entry_ref ref; 315 if (!pref) 316 ref = fCurrentRef; 317 else 318 ref = *pref; 319 320 BTranslatorRoster *proster = BTranslatorRoster::Default(); 321 if (!proster) 322 return B_ERROR; 323 BFile file(&ref, B_READ_ONLY); 324 translator_info info; 325 memset(&info, 0, sizeof(translator_info)); 326 BMessage ioExtension; 327 if (ref != fCurrentRef) 328 // if new image, reset to first document 329 fDocumentIndex = 1; 330 if (ioExtension.AddInt32("/documentIndex", fDocumentIndex) != B_OK) 331 return B_ERROR; 332 if (proster->Identify(&file, &ioExtension, &info, 0, NULL, 333 B_TRANSLATOR_BITMAP) != B_OK) 334 return B_ERROR; 335 336 // Translate image data and create a new ShowImage window 337 BBitmapStream outstream; 338 if (proster->Translate(&file, &info, &ioExtension, &outstream, 339 B_TRANSLATOR_BITMAP) != B_OK) 340 return B_ERROR; 341 BBitmap *newBitmap = NULL; 342 if (outstream.DetachBitmap(&newBitmap) != B_OK) 343 return B_ERROR; 344 345 // Now that I've successfully loaded the new bitmap, 346 // I can be sure it is safe to delete the old one, 347 // and clear everything 348 fUndo.Clear(); 349 SetHasSelection(false); 350 fMakesSelection = false; 351 DeleteBitmap(); 352 fBitmap = newBitmap; 353 newBitmap = NULL; 354 fCurrentRef = ref; 355 356 // restore orientation 357 int32 orientation; 358 fImageOrientation = k0; 359 fInverted = false; 360 if (file.ReadAttr(SHOW_IMAGE_ORIENTATION_ATTRIBUTE, B_INT32_TYPE, 0, &orientation, sizeof(orientation)) == sizeof(orientation)) { 361 if (orientation & 256) { 362 DoImageOperation(ImageProcessor::ImageProcessor::kInvert, true); 363 } 364 orientation &= 255; 365 switch (orientation) { 366 case k0: 367 break; 368 case k90: 369 DoImageOperation(ImageProcessor::kRotateClockwise, true); 370 break; 371 case k180: 372 DoImageOperation(ImageProcessor::kRotateClockwise, true); 373 DoImageOperation(ImageProcessor::kRotateClockwise, true); 374 break; 375 case k270: 376 DoImageOperation(ImageProcessor::kRotateAntiClockwise, true); 377 break; 378 case k0V: 379 DoImageOperation(ImageProcessor::ImageProcessor::kMirrorHorizontal, true); 380 break; 381 case k90V: 382 DoImageOperation(ImageProcessor::kRotateClockwise, true); 383 DoImageOperation(ImageProcessor::ImageProcessor::kMirrorHorizontal, true); 384 break; 385 case k0H: 386 DoImageOperation(ImageProcessor::ImageProcessor::kMirrorVertical, true); 387 break; 388 case k270V: 389 DoImageOperation(ImageProcessor::kRotateAntiClockwise, true); 390 DoImageOperation(ImageProcessor::ImageProcessor::kMirrorHorizontal, true); 391 break; 392 } 393 } 394 395 // get the number of documents (pages) if it has been supplied 396 int32 documentCount = 0; 397 if (ioExtension.FindInt32("/documentCount", &documentCount) == B_OK && 398 documentCount > 0) 399 fDocumentCount = documentCount; 400 else 401 fDocumentCount = 1; 402 403 GetPath(&fCaption); 404 if (fDocumentCount > 1) { 405 fCaption << ", " << fDocumentIndex << "/" << fDocumentCount; 406 } 407 fCaption << ", " << info.name; 408 409 AddToRecentDocuments(); 410 411 Notify(info.name); 412 return B_OK; 413 } 414 415 status_t 416 ShowImageView::SetSelection(const entry_ref *pref, BPoint point) 417 { 418 BTranslatorRoster *proster = BTranslatorRoster::Default(); 419 if (!proster) 420 return B_ERROR; 421 BFile file(pref, B_READ_ONLY); 422 translator_info info; 423 memset(&info, 0, sizeof(translator_info)); 424 if (proster->Identify(&file, NULL, &info, 0, NULL, 425 B_TRANSLATOR_BITMAP) != B_OK) 426 return B_ERROR; 427 428 // Translate image data and create a new ShowImage window 429 BBitmapStream outstream; 430 if (proster->Translate(&file, &info, NULL, &outstream, 431 B_TRANSLATOR_BITMAP) != B_OK) 432 return B_ERROR; 433 BBitmap *newBitmap = NULL; 434 if (outstream.DetachBitmap(&newBitmap) != B_OK) 435 return B_ERROR; 436 437 return PasteBitmap(newBitmap, point); 438 } 439 440 void 441 ShowImageView::SetDither(bool dither) 442 { 443 if (fDither != dither) { 444 SettingsSetBool("Dither", dither); 445 fDither = dither; 446 Invalidate(); 447 } 448 } 449 450 void 451 ShowImageView::SetShowCaption(bool show) 452 { 453 if (fShowCaption != show) { 454 fShowCaption = show; 455 UpdateCaption(); 456 } 457 } 458 459 void 460 ShowImageView::SetShrinkToBounds(bool enable) 461 { 462 if (fShrinkToBounds != enable) { 463 SettingsSetBool("ShrinkToBounds", enable); 464 fShrinkToBounds = enable; 465 FixupScrollBars(); 466 Invalidate(); 467 } 468 } 469 470 void 471 ShowImageView::SetZoomToBounds(bool enable) 472 { 473 if (fZoomToBounds != enable) { 474 SettingsSetBool("ZoomToBounds", enable); 475 fZoomToBounds = enable; 476 FixupScrollBars(); 477 Invalidate(); 478 } 479 } 480 481 void 482 ShowImageView::SetBorder(bool hasBorder) 483 { 484 if (fHasBorder != hasBorder) { 485 fHasBorder = hasBorder; 486 FixupScrollBars(); 487 Invalidate(); 488 } 489 } 490 491 void 492 ShowImageView::SetAlignment(alignment horizontal, vertical_alignment vertical) 493 { 494 bool hasChanged; 495 hasChanged = fHAlignment != horizontal || fVAlignment != vertical; 496 if (hasChanged) { 497 fHAlignment = horizontal; 498 fVAlignment = vertical; 499 FixupScrollBars(); 500 Invalidate(); 501 } 502 } 503 504 BBitmap * 505 ShowImageView::GetBitmap() 506 { 507 return fBitmap; 508 } 509 510 void 511 ShowImageView::GetName(BString *name) 512 { 513 *name = ""; 514 BEntry entry(&fCurrentRef); 515 if (entry.InitCheck() == B_OK) { 516 char n[B_FILE_NAME_LENGTH]; 517 if (entry.GetName(n) == B_OK) { 518 name->SetTo(n); 519 } 520 } 521 } 522 523 void 524 ShowImageView::GetPath(BString *name) 525 { 526 *name = ""; 527 BEntry entry(&fCurrentRef); 528 if (entry.InitCheck() == B_OK) { 529 BPath path; 530 entry.GetPath(&path); 531 if (path.InitCheck() == B_OK) { 532 name->SetTo(path.Path()); 533 } 534 } 535 } 536 537 void 538 ShowImageView::FlushToLeftTop() 539 { 540 BRect rect = AlignBitmap(); 541 BPoint p(rect.left, rect.top); 542 ScrollTo(p); 543 } 544 545 void 546 ShowImageView::SetScaleBilinear(bool s) 547 { 548 if (fScaleBilinear != s) { 549 SettingsSetBool("ScaleBilinear", s); 550 fScaleBilinear = s; Invalidate(); 551 } 552 } 553 554 void 555 ShowImageView::AttachedToWindow() 556 { 557 fUndo.SetWindow(Window()); 558 FixupScrollBars(); 559 } 560 561 BRect 562 ShowImageView::AlignBitmap() 563 { 564 BRect rect(fBitmap->Bounds()); 565 float width, height; 566 width = Bounds().Width()-2*PEN_SIZE+1; 567 height = Bounds().Height()-2*PEN_SIZE+1; 568 if (width == 0 || height == 0) return rect; 569 fShrinkOrZoomToBounds = fShrinkToBounds && 570 (rect.Width() >= Bounds().Width() || rect.Height() >= Bounds().Height()) || 571 fZoomToBounds && rect.Width() < Bounds().Width() && rect.Height() < Bounds().Height(); 572 if (fShrinkOrZoomToBounds) { 573 float s; 574 s = width / (rect.Width()+1.0); 575 576 if (s * (rect.Height()+1.0) <= height) { 577 rect.right = width-1; 578 rect.bottom = static_cast<int>(s * (rect.Height()+1.0))-1; 579 // center vertically 580 rect.OffsetBy(0, static_cast<int>((height - rect.Height()) / 2)); 581 } else { 582 s = height / (rect.Height()+1.0); 583 rect.right = static_cast<int>(s * (rect.Width()+1.0))-1; 584 rect.bottom = height-1; 585 // center horizontally 586 rect.OffsetBy(static_cast<int>((width - rect.Width()) / 2), 0); 587 } 588 } else { 589 float zoom; 590 if (fShrinkToBounds || fZoomToBounds) { 591 // ignore user zoom setting in automatic zoom modes 592 zoom = 1.0; 593 } else { 594 zoom = fZoom; 595 } 596 // zoom image 597 rect.right = static_cast<int>((rect.right+1.0)*zoom)-1; 598 rect.bottom = static_cast<int>((rect.bottom+1.0)*zoom)-1; 599 // align 600 switch (fHAlignment) { 601 case B_ALIGN_CENTER: 602 if (width > rect.Width()) { 603 rect.OffsetBy((width - rect.Width()) / 2.0, 0); 604 break; 605 } 606 // fall through 607 default: 608 case B_ALIGN_LEFT: 609 if (fHasBorder) { 610 rect.OffsetBy(BORDER_WIDTH, 0); 611 } 612 break; 613 } 614 switch (fVAlignment) { 615 case B_ALIGN_MIDDLE: 616 if (height > rect.Height()) { 617 rect.OffsetBy(0, (height - rect.Height()) / 2.0); 618 break; 619 } 620 // fall through 621 default: 622 case B_ALIGN_TOP: 623 if (fHasBorder) { 624 rect.OffsetBy(0, BORDER_WIDTH); 625 } 626 break; 627 } 628 } 629 rect.OffsetBy(PEN_SIZE, PEN_SIZE); 630 return rect; 631 } 632 633 void 634 ShowImageView::Setup(BRect rect) 635 { 636 fLeft = floorf(rect.left); 637 fTop = floorf(rect.top); 638 fScaleX = (rect.Width()+1.0) / (fBitmap->Bounds().Width()+1.0); 639 fScaleY = (rect.Height()+1.0) / (fBitmap->Bounds().Height()+1.0); 640 } 641 642 BPoint 643 ShowImageView::ImageToView(BPoint p) const 644 { 645 p.x = floorf(fScaleX * p.x + fLeft); 646 p.y = floorf(fScaleY * p.y + fTop); 647 return p; 648 } 649 650 BPoint 651 ShowImageView::ViewToImage(BPoint p) const 652 { 653 p.x = floorf((p.x - fLeft) / fScaleX); 654 p.y = floorf((p.y - fTop) / fScaleY); 655 return p; 656 } 657 658 BRect 659 ShowImageView::ImageToView(BRect r) const 660 { 661 BPoint leftTop(ImageToView(BPoint(r.left, r.top))); 662 BPoint rightBottom(r.right, r.bottom); 663 rightBottom += BPoint(1, 1); 664 rightBottom = ImageToView(rightBottom); 665 rightBottom -= BPoint(1, 1); 666 return BRect(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y); 667 } 668 669 void 670 ShowImageView::DrawBorder(BRect border) 671 { 672 BRect bounds(Bounds()); 673 // top 674 FillRect(BRect(0, 0, bounds.right, border.top-1), B_SOLID_LOW); 675 // left 676 FillRect(BRect(0, border.top, border.left-1, border.bottom), B_SOLID_LOW); 677 // right 678 FillRect(BRect(border.right+1, border.top, bounds.right, border.bottom), B_SOLID_LOW); 679 // bottom 680 FillRect(BRect(0, border.bottom+1, bounds.right, bounds.bottom), B_SOLID_LOW); 681 } 682 683 void 684 ShowImageView::LayoutCaption(BFont &font, BPoint &pos, BRect &rect) 685 { 686 font_height fontHeight; 687 float width, height; 688 BRect bounds(Bounds()); 689 font = be_plain_font; 690 width = font.StringWidth(fCaption.String()) + 1; // 1 for text shadow 691 font.GetHeight(&fontHeight); 692 height = fontHeight.ascent + fontHeight.descent; 693 // center text horizontally 694 pos.x = (bounds.left + bounds.right - width)/2; 695 // flush bottom 696 pos.y = bounds.bottom - fontHeight.descent - 5; 697 698 // background rectangle 699 rect.Set(0, 0, (width-1)+2, (height-1)+2+1); // 2 for border and 1 for text shadow 700 rect.OffsetTo(pos); 701 rect.OffsetBy(-1, -1-fontHeight.ascent); // -1 for border 702 } 703 704 void 705 ShowImageView::DrawCaption() 706 { 707 BFont font; 708 BPoint pos; 709 BRect rect; 710 LayoutCaption(font, pos, rect); 711 712 PushState(); 713 // draw background 714 SetDrawingMode(B_OP_ALPHA); 715 SetHighColor(0, 0, 255, 128); 716 FillRect(rect); 717 // draw text 718 SetDrawingMode(B_OP_OVER); 719 SetFont(&font); 720 SetLowColor(B_TRANSPARENT_COLOR); 721 // text shadow 722 pos += BPoint(1, 1); 723 SetHighColor(0, 0, 0); 724 SetPenSize(1); 725 DrawString(fCaption.String(), pos); 726 // text 727 pos -= BPoint(1, 1); 728 SetHighColor(255, 255, 0); 729 DrawString(fCaption.String(), pos); 730 PopState(); 731 } 732 733 void 734 ShowImageView::UpdateCaption() 735 { 736 BFont font; 737 BPoint pos; 738 BRect rect; 739 LayoutCaption(font, pos, rect); 740 741 // draw over portion of image where caption is located 742 BRegion clip(rect); 743 PushState(); 744 ConstrainClippingRegion(&clip); 745 Draw(rect); 746 PopState(); 747 } 748 749 Scaler* 750 ShowImageView::GetScaler(BRect rect) 751 { 752 if (fScaler == NULL || !fScaler->Matches(rect, fDither)) { 753 DeleteScaler(); 754 BMessenger msgr(this, Window()); 755 fScaler = new Scaler(fBitmap, rect, msgr, MSG_INVALIDATE, fDither); 756 fScaler->Start(); 757 } 758 return fScaler; 759 } 760 761 void 762 ShowImageView::DrawImage(BRect rect) 763 { 764 if (fScaleBilinear || fDither) { 765 #if DELAYED_SCALING 766 Scaler* scaler = fScaler; 767 if (scaler != NULL && !scaler->Matches(rect, fDither)) { 768 DeleteScaler(); scaler = NULL; 769 } 770 #else 771 Scaler* scaler = GetScaler(rect); 772 #endif 773 if (scaler != NULL && !scaler->IsRunning()) { 774 BBitmap* bitmap = scaler->GetBitmap(); 775 if (bitmap) { 776 DrawBitmap(bitmap, BPoint(rect.left, rect.top)); 777 return; 778 } 779 } 780 } 781 DrawBitmap(fBitmap, fBitmap->Bounds(), rect); 782 } 783 784 void 785 ShowImageView::Draw(BRect updateRect) 786 { 787 if (fBitmap) { 788 if (!IsPrinting()) { 789 BRect rect = AlignBitmap(); 790 Setup(rect); 791 792 BRect border(rect); 793 border.InsetBy(-PEN_SIZE, -PEN_SIZE); 794 795 DrawBorder(border); 796 797 // Draw black rectangle around image 798 StrokeRect(border); 799 800 // Draw image 801 DrawImage(rect); 802 803 if (fShowCaption) { 804 // fShowCaption is set to false by ScrollRestricted() 805 // to prevent the caption from dirtying up the image 806 // during scrolling. 807 DrawCaption(); 808 } 809 810 if (HasSelection()) { 811 if (fSelBitmap) { 812 BRect srcBits, destRect; 813 GetSelMergeRects(srcBits, destRect); 814 destRect = ImageToView(destRect); 815 DrawBitmap(fSelBitmap, srcBits, destRect); 816 } 817 DrawSelectionBox(); 818 } 819 } else { 820 DrawBitmap(fBitmap); 821 } 822 } 823 } 824 825 void 826 ShowImageView::DrawSelectionBox() 827 { 828 BRect r(fSelectionRect); 829 ConstrainToImage(r); 830 r = ImageToView(r); 831 // draw selection box *around* selection 832 r.InsetBy(-1, -1); 833 PushState(); 834 rgb_color white = {255, 255, 255}; 835 SetLowColor(white); 836 StrokeLine(BPoint(r.left, r.top), BPoint(r.right, r.top), fPatternLeft); 837 StrokeLine(BPoint(r.right, r.top+1), BPoint(r.right, r.bottom-1), fPatternUp); 838 StrokeLine(BPoint(r.left, r.bottom), BPoint(r.right, r.bottom), fPatternRight); 839 StrokeLine(BPoint(r.left, r.top+1), BPoint(r.left, r.bottom-1), fPatternDown); 840 PopState(); 841 } 842 843 void 844 ShowImageView::FrameResized(float /* width */, float /* height */) 845 { 846 FixupScrollBars(); 847 } 848 849 void 850 ShowImageView::ConstrainToImage(BPoint &point) 851 { 852 point.ConstrainTo(fBitmap->Bounds()); 853 } 854 855 void 856 ShowImageView::ConstrainToImage(BRect &rect) 857 { 858 BRect bounds = fBitmap->Bounds(); 859 BPoint leftTop, rightBottom; 860 861 leftTop = rect.LeftTop(); 862 leftTop.ConstrainTo(bounds); 863 864 rightBottom = rect.RightBottom(); 865 rightBottom.ConstrainTo(bounds); 866 867 rect.SetLeftTop(leftTop); 868 rect.SetRightBottom(rightBottom); 869 } 870 871 BBitmap* 872 ShowImageView::CopyFromRect(BRect srcRect) 873 { 874 BRect rect(0, 0, srcRect.Width(), srcRect.Height()); 875 BView view(rect, NULL, B_FOLLOW_NONE, B_WILL_DRAW); 876 BBitmap *bitmap = new BBitmap(rect, fBitmap->ColorSpace(), true); 877 if (bitmap == NULL) return NULL; 878 879 if (bitmap->Lock()) { 880 bitmap->AddChild(&view); 881 view.DrawBitmap(fBitmap, srcRect, rect); 882 view.Sync(); 883 bitmap->RemoveChild(&view); 884 bitmap->Unlock(); 885 } 886 887 return bitmap; 888 } 889 890 BBitmap* 891 ShowImageView::CopySelection(uchar alpha, bool imageSize) 892 { 893 bool hasAlpha = alpha != 255; 894 895 if (!HasSelection()) return NULL; 896 897 BRect rect(0, 0, fSelectionRect.Width(), fSelectionRect.Height()); 898 if (!imageSize) { 899 // scale image to view size 900 rect.right = floorf((rect.right + 1.0) * fScaleX - 1.0); 901 rect.bottom = floorf((rect.bottom + 1.0) * fScaleY - 1.0); 902 } 903 BView view(rect, NULL, B_FOLLOW_NONE, B_WILL_DRAW); 904 BBitmap *bitmap = new BBitmap(rect, hasAlpha ? B_RGBA32 : fBitmap->ColorSpace(), true); 905 if (bitmap == NULL) return NULL; 906 907 if (bitmap->Lock()) { 908 bitmap->AddChild(&view); 909 if (fSelBitmap) 910 view.DrawBitmap(fSelBitmap, fSelBitmap->Bounds(), rect); 911 else 912 view.DrawBitmap(fBitmap, fCopyFromRect, rect); 913 if (hasAlpha) { 914 view.SetDrawingMode(B_OP_SUBTRACT); 915 view.SetHighColor(0, 0, 0, 255-alpha); 916 view.FillRect(rect, B_SOLID_HIGH); 917 } 918 view.Sync(); 919 bitmap->RemoveChild(&view); 920 bitmap->Unlock(); 921 } 922 923 return bitmap; 924 } 925 926 bool 927 ShowImageView::AddSupportedTypes(BMessage* msg, BBitmap* bitmap) 928 { 929 bool found = false; 930 BTranslatorRoster *roster = BTranslatorRoster::Default(); 931 if (roster == NULL) return false; 932 933 BBitmapStream stream(bitmap); 934 935 translator_info *outInfo; 936 int32 outNumInfo; 937 if (roster->GetTranslators(&stream, NULL, &outInfo, &outNumInfo) == B_OK) { 938 for (int32 i = 0; i < outNumInfo; i++) { 939 const translation_format *fmts; 940 int32 num_fmts; 941 roster->GetOutputFormats(outInfo[i].translator, &fmts, &num_fmts); 942 for (int32 j = 0; j < num_fmts; j++) { 943 if (strcmp(fmts[j].MIME, "image/x-be-bitmap") != 0) { 944 // needed to send data in message 945 msg->AddString("be:types", fmts[j].MIME); 946 // needed to pass data via file 947 msg->AddString("be:filetypes", fmts[j].MIME); 948 msg->AddString("be:type_descriptions", fmts[j].name); 949 } 950 found = true; 951 } 952 } 953 } 954 stream.DetachBitmap(&bitmap); 955 return found; 956 } 957 958 void 959 ShowImageView::BeginDrag(BPoint sourcePoint) 960 { 961 BBitmap* bitmap = CopySelection(128, false); 962 if (bitmap == NULL) return; 963 964 SetMouseEventMask(B_POINTER_EVENTS); 965 BPoint leftTop(fSelectionRect.left, fSelectionRect.top); 966 967 // fill the drag message 968 BMessage drag(B_SIMPLE_DATA); 969 drag.AddInt32("be:actions", B_COPY_TARGET); 970 drag.AddString("be:clip_name", "Bitmap Clip"); 971 // ShowImage specific fields 972 drag.AddPoint("be:_source_point", sourcePoint); 973 drag.AddRect("be:_frame", fSelectionRect); 974 if (AddSupportedTypes(&drag, bitmap)) { 975 // we also support "Passing Data via File" protocol 976 drag.AddString("be:types", B_FILE_MIME_TYPE); 977 // avoid flickering of dragged bitmap caused by drawing into the window 978 AnimateSelection(false); 979 sourcePoint -= leftTop; 980 sourcePoint.x *= fScaleX; 981 sourcePoint.y *= fScaleY; 982 // DragMessage takes ownership of bitmap 983 DragMessage(&drag, bitmap, B_OP_ALPHA, sourcePoint); 984 bitmap = NULL; 985 } 986 } 987 988 bool 989 ShowImageView::OutputFormatForType(BBitmap* bitmap, const char* type, translation_format* format) 990 { 991 bool found = false; 992 993 BTranslatorRoster *roster = BTranslatorRoster::Default(); 994 if (roster == NULL) return false; 995 996 BBitmapStream stream(bitmap); 997 998 translator_info *outInfo; 999 int32 outNumInfo; 1000 if (roster->GetTranslators(&stream, NULL, &outInfo, &outNumInfo) == B_OK) { 1001 for (int32 i = 0; i < outNumInfo; i++) { 1002 const translation_format *fmts; 1003 int32 num_fmts; 1004 roster->GetOutputFormats(outInfo[i].translator, &fmts, &num_fmts); 1005 for (int32 j = 0; j < num_fmts; j++) { 1006 if (strcmp(fmts[j].MIME, type) == 0) { 1007 *format = fmts[j]; 1008 found = true; 1009 break; 1010 } 1011 } 1012 } 1013 } 1014 stream.DetachBitmap(&bitmap); 1015 return found; 1016 } 1017 1018 void 1019 ShowImageView::SaveToFile(BDirectory* dir, const char* name, BBitmap* bitmap, const translation_format* format) 1020 { 1021 if (!bitmap) 1022 // If no bitmap is supplied, write out the whole image 1023 bitmap = fBitmap; 1024 1025 BBitmapStream stream(bitmap); 1026 1027 bool loop = true; 1028 while (loop) { 1029 BTranslatorRoster *roster = BTranslatorRoster::Default(); 1030 if (!roster) 1031 break; 1032 // write data 1033 BFile file(dir, name, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 1034 if (file.InitCheck() != B_OK) 1035 break; 1036 if (roster->Translate(&stream, NULL, NULL, &file, format->type) < B_OK) 1037 break; 1038 // set mime type 1039 BNodeInfo info(&file); 1040 if (info.InitCheck() == B_OK) 1041 info.SetType(format->MIME); 1042 1043 loop = false; 1044 // break out of loop gracefully (indicates no errors) 1045 } 1046 if (loop) { 1047 // If loop terminated because of a break, there was an error 1048 BString errText; 1049 errText << "Sorry, the file '" << name << "' could not be written."; 1050 BAlert *palert = new BAlert(NULL, errText.String(), "Ok"); 1051 palert->Go(); 1052 } 1053 1054 stream.DetachBitmap(&bitmap); 1055 // Don't allow the bitmap to be deleted, this is 1056 // especially important when using fBitmap as the bitmap 1057 } 1058 1059 void 1060 ShowImageView::SendInMessage(BMessage* msg, BBitmap* bitmap, translation_format* format) 1061 { 1062 BMessage reply(B_MIME_DATA); 1063 BBitmapStream stream(bitmap); // destructor deletes bitmap 1064 BTranslatorRoster *roster = BTranslatorRoster::Default(); 1065 BMallocIO memStream; 1066 if (roster->Translate(&stream, NULL, NULL, &memStream, format->type) == B_OK) { 1067 reply.AddData(format->MIME, B_MIME_TYPE, memStream.Buffer(), memStream.BufferLength()); 1068 msg->SendReply(&reply); 1069 } 1070 } 1071 1072 void 1073 ShowImageView::HandleDrop(BMessage* msg) 1074 { 1075 BMessage data(B_MIME_DATA); 1076 entry_ref dirRef; 1077 BString name, type; 1078 bool saveToFile; 1079 bool sendInMessage; 1080 BBitmap *bitmap; 1081 1082 saveToFile = msg->FindString("be:filetypes", &type) == B_OK && 1083 msg->FindRef("directory", &dirRef) == B_OK && 1084 msg->FindString("name", &name) == B_OK; 1085 1086 sendInMessage = (!saveToFile) && msg->FindString("be:types", &type) == B_OK; 1087 1088 bitmap = CopySelection(); 1089 if (bitmap == NULL) return; 1090 1091 translation_format format; 1092 if (!OutputFormatForType(bitmap, type.String(), &format)) { 1093 delete bitmap; 1094 return; 1095 } 1096 1097 if (saveToFile) { 1098 BDirectory dir(&dirRef); 1099 SaveToFile(&dir, name.String(), bitmap, &format); 1100 delete bitmap; 1101 } else if (sendInMessage) { 1102 SendInMessage(msg, bitmap, &format); 1103 } else { 1104 delete bitmap; 1105 } 1106 } 1107 1108 void 1109 ShowImageView::MoveImage() 1110 { 1111 BPoint point, delta; 1112 uint32 buttons; 1113 // get CURRENT position 1114 GetMouse(&point, &buttons); 1115 point = ConvertToScreen(point); 1116 delta = fFirstPoint - point; 1117 fFirstPoint = point; 1118 ScrollRestrictedBy(delta.x, delta.y); 1119 // in case we miss MouseUp 1120 if ((GetMouseButtons() & B_TERTIARY_MOUSE_BUTTON) == 0) 1121 fMovesImage = false; 1122 } 1123 1124 uint32 1125 ShowImageView::GetMouseButtons() 1126 { 1127 uint32 buttons; 1128 BPoint point; 1129 GetMouse(&point, &buttons); 1130 if (buttons == B_PRIMARY_MOUSE_BUTTON) { 1131 if ((modifiers() & B_CONTROL_KEY) != 0) { 1132 buttons = B_SECONDARY_MOUSE_BUTTON; // simulate second button 1133 } else if ((modifiers() & B_SHIFT_KEY) != 0) { 1134 buttons = B_TERTIARY_MOUSE_BUTTON; // simulate third button 1135 } 1136 } 1137 return buttons; 1138 } 1139 1140 void 1141 ShowImageView::GetMergeRects(BBitmap *merge, BRect selection, BRect &srcBits, BRect &destRect) 1142 { 1143 destRect = selection; 1144 ConstrainToImage(destRect); 1145 1146 srcBits = selection; 1147 if (srcBits.left < 0) 1148 srcBits.left = -(srcBits.left); 1149 else 1150 srcBits.left = 0; 1151 if (srcBits.top < 0) 1152 srcBits.top = -(srcBits.top); 1153 else 1154 srcBits.top = 0; 1155 if (srcBits.right > fBitmap->Bounds().right) 1156 srcBits.right = srcBits.left + destRect.Width(); 1157 else 1158 srcBits.right = merge->Bounds().right; 1159 if (srcBits.bottom > fBitmap->Bounds().bottom) 1160 srcBits.bottom = srcBits.top + destRect.Height(); 1161 else 1162 srcBits.bottom = merge->Bounds().bottom; 1163 } 1164 1165 void 1166 ShowImageView::GetSelMergeRects(BRect &srcBits, BRect &destRect) 1167 { 1168 GetMergeRects(fSelBitmap, fSelectionRect, srcBits, destRect); 1169 } 1170 1171 void 1172 ShowImageView::MergeWithBitmap(BBitmap *merge, BRect selection) 1173 { 1174 BView view(fBitmap->Bounds(), NULL, B_FOLLOW_NONE, B_WILL_DRAW); 1175 BBitmap *bitmap = new BBitmap(fBitmap->Bounds(), fBitmap->ColorSpace(), true); 1176 if (bitmap == NULL) 1177 return; 1178 1179 if (bitmap->Lock()) { 1180 bitmap->AddChild(&view); 1181 view.DrawBitmap(fBitmap, fBitmap->Bounds()); 1182 BRect srcBits, destRect; 1183 GetMergeRects(merge, selection, srcBits, destRect); 1184 view.DrawBitmap(merge, srcBits, destRect); 1185 1186 view.Sync(); 1187 bitmap->RemoveChild(&view); 1188 bitmap->Unlock(); 1189 1190 DeleteBitmap(); 1191 fBitmap = bitmap; 1192 1193 BMessenger msgr(Window()); 1194 msgr.SendMessage(MSG_MODIFIED); 1195 } else 1196 delete bitmap; 1197 } 1198 1199 void 1200 ShowImageView::MergeSelection() 1201 { 1202 if (!HasSelection()) 1203 return; 1204 1205 if (!fSelBitmap) { 1206 // Even though the merge will not change 1207 // the background image, I still need to save 1208 // some undo information here 1209 fUndo.SetTo(fSelectionRect, NULL, CopySelection()); 1210 return; 1211 } 1212 1213 // Merge selection with background 1214 fUndo.SetTo(fSelectionRect, CopyFromRect(fSelectionRect), CopySelection()); 1215 MergeWithBitmap(fSelBitmap, fSelectionRect); 1216 } 1217 1218 void 1219 ShowImageView::MouseDown(BPoint position) 1220 { 1221 BPoint point; 1222 uint32 buttons; 1223 MakeFocus(true); 1224 1225 point = ViewToImage(position); 1226 buttons = GetMouseButtons(); 1227 1228 if (HasSelection() && fSelectionRect.Contains(point) && 1229 (buttons & (B_PRIMARY_MOUSE_BUTTON | B_SECONDARY_MOUSE_BUTTON))) { 1230 if (!fSelBitmap) { 1231 fSelBitmap = CopySelection(); 1232 } 1233 BPoint sourcePoint = point; 1234 BeginDrag(sourcePoint); 1235 1236 while (buttons) { 1237 // Keep reading mouse movement until 1238 // the user lets up on all mouse buttons 1239 GetMouse(&point, &buttons); 1240 snooze(25 * 1000); 1241 // sleep for 25 milliseconds to minimize CPU usage during loop 1242 } 1243 1244 if (Bounds().Contains(point)) { 1245 // If selection stayed inside this view 1246 // (Some of the selection may be in the border area, which can be OK) 1247 BPoint last, diff; 1248 last = ViewToImage(point); 1249 diff = last - sourcePoint; 1250 1251 BRect newSelection = fSelectionRect; 1252 newSelection.OffsetBy(diff); 1253 1254 if (fBitmap->Bounds().Intersects(newSelection)) { 1255 // Do not accept the new selection box location 1256 // if it does not intersect with the bitmap rectangle 1257 fSelectionRect = newSelection; 1258 Invalidate(); 1259 } 1260 } 1261 1262 AnimateSelection(true); 1263 1264 } else if (buttons == B_PRIMARY_MOUSE_BUTTON) { 1265 MergeSelection(); 1266 // If there is an existing selection, 1267 // Make it part of the background image 1268 1269 // begin new selection 1270 SetHasSelection(true); 1271 fMakesSelection = true; 1272 SetMouseEventMask(B_POINTER_EVENTS); 1273 ConstrainToImage(point); 1274 fFirstPoint = point; 1275 fCopyFromRect.Set(point.x, point.y, point.x, point.y); 1276 fSelectionRect = fCopyFromRect; 1277 Invalidate(); 1278 } else if (buttons == B_SECONDARY_MOUSE_BUTTON) { 1279 ShowPopUpMenu(ConvertToScreen(position)); 1280 } else if (buttons == B_TERTIARY_MOUSE_BUTTON) { 1281 // move image in window 1282 SetMouseEventMask(B_POINTER_EVENTS); 1283 fMovesImage = true; 1284 fFirstPoint = ConvertToScreen(position); 1285 } 1286 } 1287 1288 void 1289 ShowImageView::UpdateSelectionRect(BPoint point, bool final) 1290 { 1291 BRect oldSelection = fCopyFromRect; 1292 point = ViewToImage(point); 1293 ConstrainToImage(point); 1294 fCopyFromRect.left = min(fFirstPoint.x, point.x); 1295 fCopyFromRect.right = max(fFirstPoint.x, point.x); 1296 fCopyFromRect.top = min(fFirstPoint.y, point.y); 1297 fCopyFromRect.bottom = max(fFirstPoint.y, point.y); 1298 fSelectionRect = fCopyFromRect; 1299 if (final) { 1300 // selection must be at least 2 pixels wide or 2 pixels tall 1301 if (fCopyFromRect.Width() < 1.0 && fCopyFromRect.Height() < 1.0) 1302 SetHasSelection(false); 1303 } 1304 if (oldSelection != fCopyFromRect || !HasSelection()) { 1305 BRect updateRect; 1306 updateRect = oldSelection | fCopyFromRect; 1307 updateRect = ImageToView(updateRect); 1308 updateRect.InsetBy(-PEN_SIZE, -PEN_SIZE); 1309 Invalidate(updateRect); 1310 } 1311 } 1312 1313 void 1314 ShowImageView::MouseMoved(BPoint point, uint32 state, const BMessage *pmsg) 1315 { 1316 if (fMakesSelection) { 1317 UpdateSelectionRect(point, false); 1318 } else if (fMovesImage) { 1319 MoveImage(); 1320 } 1321 } 1322 1323 void 1324 ShowImageView::MouseUp(BPoint point) 1325 { 1326 if (fMakesSelection) { 1327 UpdateSelectionRect(point, true); 1328 fMakesSelection = false; 1329 } else if (fMovesImage) { 1330 MoveImage(); 1331 if (fMovesImage) 1332 fMovesImage = false; 1333 } 1334 AnimateSelection(true); 1335 } 1336 1337 float 1338 ShowImageView::LimitToRange(float v, orientation o, bool absolute) 1339 { 1340 BScrollBar* psb = ScrollBar(o); 1341 if (psb) { 1342 float min, max, pos; 1343 pos = v; 1344 if (!absolute) { 1345 pos += psb->Value(); 1346 } 1347 psb->GetRange(&min, &max); 1348 if (pos < min) { 1349 pos = min; 1350 } else if (pos > max) { 1351 pos = max; 1352 } 1353 v = pos; 1354 if (!absolute) { 1355 v -= psb->Value(); 1356 } 1357 } 1358 return v; 1359 } 1360 1361 void 1362 ShowImageView::ScrollRestricted(float x, float y, bool absolute) 1363 { 1364 if (x != 0) { 1365 x = LimitToRange(x, B_HORIZONTAL, absolute); 1366 } 1367 1368 if (y != 0) { 1369 y = LimitToRange(y, B_VERTICAL, absolute); 1370 } 1371 1372 // hide the caption when using mouse wheel 1373 // in full screen mode 1374 bool caption = fShowCaption; 1375 if (caption) { 1376 fShowCaption = false; 1377 UpdateCaption(); 1378 } 1379 1380 ScrollBy(x, y); 1381 1382 if (caption) { 1383 // show the caption again 1384 fShowCaption = true; 1385 UpdateCaption(); 1386 } 1387 } 1388 1389 // XXX method is not unused 1390 void 1391 ShowImageView::ScrollRestrictedTo(float x, float y) 1392 { 1393 ScrollRestricted(x, y, true); 1394 } 1395 1396 void 1397 ShowImageView::ScrollRestrictedBy(float x, float y) 1398 { 1399 ScrollRestricted(x, y, false); 1400 } 1401 1402 void 1403 ShowImageView::KeyDown (const char * bytes, int32 numBytes) 1404 { 1405 if (numBytes == 1) { 1406 switch (*bytes) { 1407 case B_DOWN_ARROW: 1408 ScrollRestrictedBy(0, 10); 1409 break; 1410 case B_UP_ARROW: 1411 ScrollRestrictedBy(0, -10); 1412 break; 1413 case B_LEFT_ARROW: 1414 ScrollRestrictedBy(-10, 0); 1415 break; 1416 case B_RIGHT_ARROW: 1417 ScrollRestrictedBy(10, 0); 1418 break; 1419 case B_SPACE: 1420 case B_ENTER: 1421 NextFile(); 1422 break; 1423 case B_BACKSPACE: 1424 PrevFile(); 1425 break; 1426 case B_HOME: 1427 break; 1428 case B_END: 1429 break; 1430 case B_ESCAPE: 1431 if (fSlideShow) { 1432 BMessenger msgr(Window()); 1433 msgr.SendMessage(MSG_SLIDE_SHOW); 1434 } 1435 ClearSelection(); 1436 break; 1437 } 1438 } 1439 } 1440 1441 void 1442 ShowImageView::MouseWheelChanged(BMessage *msg) 1443 { 1444 // The BeOS driver does not currently support 1445 // X wheel scrolling, therefore, dx is zero. 1446 // |dy| is the number of notches scrolled up or down. 1447 // When the wheel is scrolled down (towards the user) dy > 0 1448 // When the wheel is scrolled up (away from the user) dy < 0 1449 const float kscrollBy = 40; 1450 float dy, dx; 1451 float x, y; 1452 x = 0; y = 0; 1453 if (msg->FindFloat("be:wheel_delta_x", &dx) == B_OK) { 1454 x = dx * kscrollBy; 1455 } 1456 if (msg->FindFloat("be:wheel_delta_y", &dy) == B_OK) { 1457 y = dy * kscrollBy; 1458 } 1459 1460 ScrollRestrictedBy(x, y); 1461 } 1462 1463 void 1464 ShowImageView::ShowPopUpMenu(BPoint screen) 1465 { 1466 BPopUpMenu* menu = new BPopUpMenu("PopUpMenu"); 1467 menu->SetAsyncAutoDestruct(true); 1468 menu->SetRadioMode(false); 1469 1470 ShowImageWindow* showImage = dynamic_cast<ShowImageWindow*>(Window()); 1471 if (showImage) { 1472 showImage->BuildViewMenu(menu); 1473 } 1474 menu->AddSeparatorItem(); 1475 menu->AddItem(new BMenuItem("Cancel", 0, 0)); 1476 1477 screen -= BPoint(10, 10); 1478 menu->Go(screen, true, false, true); 1479 } 1480 1481 void 1482 ShowImageView::SettingsSetBool(const char* name, bool value) 1483 { 1484 ShowImageSettings* settings; 1485 settings = my_app->Settings(); 1486 if (settings->Lock()) { 1487 settings->SetBool(name, value); 1488 settings->Unlock(); 1489 } 1490 } 1491 1492 void 1493 ShowImageView::MessageReceived(BMessage *pmsg) 1494 { 1495 switch (pmsg->what) { 1496 case MSG_SELECTION_BITMAP: 1497 { 1498 // In response to a B_SIMPLE_DATA message, a view will 1499 // send this message and expect a reply with a pointer to 1500 // the currently selected bitmap clip. Although this view 1501 // allocates the BBitmap * sent in the reply, it is only 1502 // to be used and deleted by the view that is being replied to. 1503 BMessage msg; 1504 msg.AddPointer("be:_bitmap_ptr", CopySelection()); 1505 pmsg->SendReply(&msg); 1506 break; 1507 } 1508 1509 case B_SIMPLE_DATA: 1510 if (pmsg->WasDropped()) { 1511 uint32 type; 1512 int32 count; 1513 status_t ret = pmsg->GetInfo("refs", &type, &count); 1514 if (ret == B_OK && type == B_REF_TYPE) { 1515 // If file was dropped, open it as the selection 1516 entry_ref ref; 1517 if (pmsg->FindRef("refs", 0, &ref) == B_OK) { 1518 BPoint point = pmsg->DropPoint(); 1519 point = ConvertFromScreen(point); 1520 point = ViewToImage(point); 1521 SetSelection(&ref, point); 1522 } 1523 } else { 1524 // If a user drags a clip from another ShowImage window, 1525 // request a BBitmap pointer to that clip, allocated by the 1526 // other view, for use solely by this view, so that it can 1527 // be dropped/pasted onto this view. 1528 BMessenger retMsgr, localMsgr(this); 1529 retMsgr = pmsg->ReturnAddress(); 1530 if (retMsgr != localMsgr) { 1531 BMessage msgReply; 1532 retMsgr.SendMessage(MSG_SELECTION_BITMAP, &msgReply); 1533 BBitmap *bitmap = NULL; 1534 if (msgReply.FindPointer("be:_bitmap_ptr", 1535 reinterpret_cast<void **>(&bitmap)) == B_OK) { 1536 BRect sourceRect; 1537 BPoint point, sourcePoint; 1538 pmsg->FindPoint("be:_source_point", &sourcePoint); 1539 pmsg->FindRect("be:_frame", &sourceRect); 1540 point = pmsg->DropPoint(); 1541 point.Set(point.x - (sourcePoint.x - sourceRect.left), 1542 point.y - (sourcePoint.y - sourceRect.top)); 1543 // adjust drop point before scaling is factored in 1544 point = ConvertFromScreen(point); 1545 point = ViewToImage(point); 1546 1547 PasteBitmap(bitmap, point); 1548 } 1549 } 1550 } 1551 } 1552 break; 1553 1554 case B_COPY_TARGET: 1555 HandleDrop(pmsg); 1556 break; 1557 case B_MOUSE_WHEEL_CHANGED: 1558 MouseWheelChanged(pmsg); 1559 break; 1560 case MSG_INVALIDATE: 1561 Invalidate(); 1562 break; 1563 default: 1564 BView::MessageReceived(pmsg); 1565 break; 1566 } 1567 } 1568 1569 void 1570 ShowImageView::FixupScrollBar(orientation o, float bitmapLength, float viewLength) 1571 { 1572 float prop, range; 1573 BScrollBar *psb; 1574 1575 psb = ScrollBar(o); 1576 if (psb) { 1577 if (fHasBorder && !fShrinkOrZoomToBounds) { 1578 bitmapLength += BORDER_WIDTH*2; 1579 } 1580 range = bitmapLength - viewLength; 1581 if (range < 0.0) { 1582 range = 0.0; 1583 } 1584 prop = viewLength / bitmapLength; 1585 if (prop > 1.0) { 1586 prop = 1.0; 1587 } 1588 psb->SetRange(0, range); 1589 psb->SetProportion(prop); 1590 psb->SetSteps(10, 100); 1591 } 1592 } 1593 1594 void 1595 ShowImageView::FixupScrollBars() 1596 { 1597 BRect rctview = Bounds(), rctbitmap(0, 0, 0, 0); 1598 if (fBitmap) { 1599 BRect rect(AlignBitmap()); 1600 rctbitmap.Set(0, 0, rect.Width(), rect.Height()); 1601 } 1602 1603 FixupScrollBar(B_HORIZONTAL, rctbitmap.Width(), rctview.Width()); 1604 FixupScrollBar(B_VERTICAL, rctbitmap.Height(), rctview.Height()); 1605 } 1606 1607 int32 1608 ShowImageView::CurrentPage() 1609 { 1610 return fDocumentIndex; 1611 } 1612 1613 int32 1614 ShowImageView::PageCount() 1615 { 1616 return fDocumentCount; 1617 } 1618 1619 void 1620 ShowImageView::Undo() 1621 { 1622 int32 undoType = fUndo.GetType(); 1623 if (undoType != UNDO_UNDO && undoType != UNDO_REDO) 1624 return; 1625 1626 // backup current selection 1627 BRect undoneSelRect; 1628 BBitmap *undoneSelection; 1629 undoneSelRect = fSelectionRect; 1630 undoneSelection = CopySelection(); 1631 1632 if (undoType == UNDO_UNDO) { 1633 BBitmap *undoRestore; 1634 undoRestore = fUndo.GetRestoreBitmap(); 1635 if (undoRestore) 1636 MergeWithBitmap(undoRestore, fUndo.GetRect()); 1637 } 1638 1639 // restore previous image/selection 1640 BBitmap *undoSelection; 1641 undoSelection = fUndo.GetSelectionBitmap(); 1642 // NOTE: ShowImageView is responsible for deleting this bitmap 1643 // (Which it will, as it would with a fSelBitmap that it allocated itself) 1644 if (!undoSelection) 1645 SetHasSelection(false); 1646 else { 1647 SetHasSelection(true); 1648 fCopyFromRect = BRect(); 1649 fSelectionRect = fUndo.GetRect(); 1650 fSelBitmap = undoSelection; 1651 } 1652 1653 fUndo.Undo(undoneSelRect, NULL, undoneSelection); 1654 1655 Invalidate(); 1656 } 1657 1658 void 1659 ShowImageView::AddWhiteRect(BRect &rect) 1660 { 1661 // Paint white rectangle, using rect, into the background image 1662 BView view(fBitmap->Bounds(), NULL, B_FOLLOW_NONE, B_WILL_DRAW); 1663 BBitmap *bitmap = new BBitmap(fBitmap->Bounds(), fBitmap->ColorSpace(), true); 1664 if (bitmap == NULL) 1665 return; 1666 1667 if (bitmap->Lock()) { 1668 bitmap->AddChild(&view); 1669 view.DrawBitmap(fBitmap, fBitmap->Bounds()); 1670 1671 view.FillRect(rect, B_SOLID_LOW); 1672 // draw white rect 1673 1674 view.Sync(); 1675 bitmap->RemoveChild(&view); 1676 bitmap->Unlock(); 1677 1678 DeleteBitmap(); 1679 fBitmap = bitmap; 1680 1681 BMessenger msgr(Window()); 1682 msgr.SendMessage(MSG_MODIFIED); 1683 } else 1684 delete bitmap; 1685 } 1686 1687 void 1688 ShowImageView::RemoveSelection(bool bToClipboard) 1689 { 1690 if (HasSelection()) { 1691 BRect rect = fSelectionRect; 1692 bool bCutBackground = (fSelBitmap) ? false : true; 1693 BBitmap *selection, *restore = NULL; 1694 selection = CopySelection(); 1695 1696 if (bToClipboard) 1697 CopySelectionToClipboard(); 1698 SetHasSelection(false); 1699 1700 if (bCutBackground) { 1701 // If the user hasn't dragged the selection, 1702 // paint a white rectangle where the selection was 1703 restore = CopyFromRect(rect); 1704 AddWhiteRect(rect); 1705 } 1706 1707 fUndo.SetTo(rect, restore, selection); 1708 Invalidate(); 1709 } 1710 } 1711 1712 void 1713 ShowImageView::Cut() 1714 { 1715 // Copy the selection to the clipboard, 1716 // then remove it 1717 RemoveSelection(true); 1718 } 1719 1720 status_t 1721 ShowImageView::PasteBitmap(BBitmap *bitmap, BPoint point) 1722 { 1723 if (bitmap && bitmap->IsValid()) { 1724 MergeSelection(); 1725 1726 SetHasSelection(true); 1727 fSelBitmap = bitmap; 1728 fCopyFromRect = BRect(); 1729 fSelectionRect = bitmap->Bounds(); 1730 1731 BRect offsetRect = fSelectionRect; 1732 offsetRect.OffsetBy(point); 1733 if (fBitmap->Bounds().Intersects(offsetRect)) 1734 // Move the selection rectangle to desired origin, 1735 // but only if the resulting selection rectangle 1736 // intersects with the background bitmap rectangle 1737 fSelectionRect = offsetRect; 1738 1739 Invalidate(); 1740 1741 return B_OK; 1742 } 1743 1744 return B_ERROR; 1745 } 1746 1747 void 1748 ShowImageView::Paste() 1749 { 1750 if (be_clipboard->Lock()) { 1751 BMessage *pclip; 1752 if ((pclip = be_clipboard->Data()) != NULL) { 1753 BPoint point(0, 0); 1754 pclip->FindPoint("be:location", &point); 1755 BBitmap *pbits; 1756 pbits = dynamic_cast<BBitmap *>(BBitmap::Instantiate(pclip)); 1757 PasteBitmap(pbits, point); 1758 } 1759 1760 be_clipboard->Unlock(); 1761 } 1762 } 1763 1764 void 1765 ShowImageView::SelectAll() 1766 { 1767 SetHasSelection(true); 1768 fCopyFromRect.Set(0, 0, fBitmap->Bounds().Width(), fBitmap->Bounds().Height()); 1769 fSelectionRect = fCopyFromRect; 1770 Invalidate(); 1771 } 1772 1773 void 1774 ShowImageView::ClearSelection() 1775 { 1776 // Remove the selection, 1777 // DON'T copy it to the clipboard 1778 RemoveSelection(false); 1779 } 1780 1781 void 1782 ShowImageView::SetHasSelection(bool bHasSelection) 1783 { 1784 DeleteSelBitmap(); 1785 fHasSelection = bHasSelection; 1786 1787 BMessage msg(MSG_SELECTION); 1788 msg.AddBool("has_selection", fHasSelection); 1789 BMessenger msgr(Window()); 1790 msgr.SendMessage(&msg); 1791 } 1792 1793 void 1794 ShowImageView::CopySelectionToClipboard() 1795 { 1796 if (HasSelection() && be_clipboard->Lock()) { 1797 be_clipboard->Clear(); 1798 BMessage *clip = NULL; 1799 if ((clip = be_clipboard->Data()) != NULL) { 1800 BMessage data; 1801 BBitmap* bitmap = CopySelection(); 1802 if (bitmap != NULL) { 1803 #if 0 1804 // According to BeBook and Becasso, Gobe Productive do the following. 1805 // Paste works in Productive, but not in Becasso and original ShowImage. 1806 BMessage msg(B_OK); // Becasso uses B_TRANSLATOR_BITMAP, BeBook says its unused 1807 bitmap->Archive(&msg); 1808 clip->AddMessage("image/x-be-bitmap", &msg); 1809 #else 1810 // original ShowImage performs this. Paste works with original ShowImage. 1811 bitmap->Archive(clip); 1812 // original ShowImage uses be:location for insertion point 1813 clip->AddPoint("be:location", BPoint(fSelectionRect.left, fSelectionRect.top)); 1814 #endif 1815 delete bitmap; 1816 be_clipboard->Commit(); 1817 } 1818 } 1819 be_clipboard->Unlock(); 1820 } 1821 } 1822 1823 void 1824 ShowImageView::FirstPage() 1825 { 1826 if (fDocumentIndex != 1) { 1827 fDocumentIndex = 1; 1828 SetImage(NULL); 1829 } 1830 } 1831 1832 void 1833 ShowImageView::LastPage() 1834 { 1835 if (fDocumentIndex != fDocumentCount) { 1836 fDocumentIndex = fDocumentCount; 1837 SetImage(NULL); 1838 } 1839 } 1840 1841 void 1842 ShowImageView::NextPage() 1843 { 1844 if (fDocumentIndex < fDocumentCount) { 1845 fDocumentIndex++; 1846 SetImage(NULL); 1847 } 1848 } 1849 1850 void 1851 ShowImageView::PrevPage() 1852 { 1853 if (fDocumentIndex > 1) { 1854 fDocumentIndex--; 1855 SetImage(NULL); 1856 } 1857 } 1858 1859 int 1860 ShowImageView::CompareEntries(const void* a, const void* b) 1861 { 1862 entry_ref *r1, *r2; 1863 r1 = *(entry_ref**)a; 1864 r2 = *(entry_ref**)b; 1865 return strcasecmp(r1->name, r2->name); 1866 } 1867 1868 void 1869 ShowImageView::GoToPage(int32 page) 1870 { 1871 if (page > 0 && page <= fDocumentCount && page != fDocumentIndex) { 1872 fDocumentIndex = page; 1873 SetImage(NULL); 1874 } 1875 } 1876 1877 void 1878 ShowImageView::FreeEntries(BList* entries) 1879 { 1880 const int32 n = entries->CountItems(); 1881 for (int32 i = 0; i < n; i ++) { 1882 entry_ref* ref = (entry_ref*)entries->ItemAt(i); 1883 delete ref; 1884 } 1885 entries->MakeEmpty(); 1886 } 1887 1888 1889 void 1890 ShowImageView::SetTrackerSelectionToCurrent() 1891 { 1892 BMessage setsel(B_SET_PROPERTY); 1893 setsel.AddSpecifier("Selection"); 1894 setsel.AddRef("data", &fCurrentRef); 1895 fTrackerMessenger.SendMessage(&setsel); 1896 } 1897 1898 bool 1899 ShowImageView::FindNextImage(entry_ref *in_current, entry_ref *ref, bool next, bool rewind) 1900 { 1901 // shamelessly stolen, I mean copied from BeMail! 1902 // XXX rewind is not implemented yet => slide show will stop with last image 1903 1904 if (!fTrackerMessenger.IsValid()) 1905 return false; 1906 1907 // 1908 // Ask the Tracker what the next/prev file in the window is. 1909 // Continue asking for the next reference until a valid 1910 // image is found. 1911 // 1912 entry_ref nextRef = *in_current; 1913 bool foundRef = false; 1914 while (!foundRef) 1915 { 1916 BMessage request(B_GET_PROPERTY); 1917 BMessage spc; 1918 if (next) 1919 spc.what = 'snxt'; 1920 else 1921 spc.what = 'sprv'; 1922 1923 spc.AddString("property", "Entry"); 1924 spc.AddRef("data", &nextRef); 1925 1926 request.AddSpecifier(&spc); 1927 BMessage reply; 1928 if (fTrackerMessenger.SendMessage(&request, &reply) != B_OK) { 1929 return false; 1930 } 1931 1932 if (reply.FindRef("result", &nextRef) != B_OK) { 1933 return false; 1934 } 1935 1936 if (IsImage(&nextRef)) { 1937 foundRef = true; 1938 } 1939 } 1940 1941 *ref = nextRef; 1942 return foundRef; 1943 } 1944 1945 bool 1946 ShowImageView::ShowNextImage(bool next, bool rewind) 1947 { 1948 bool found; 1949 entry_ref curRef, imgRef; 1950 1951 curRef = fCurrentRef; 1952 found = FindNextImage(&curRef, &imgRef, next, rewind); 1953 if (found) { 1954 // Keep trying to load images until: 1955 // 1. The image loads successfully 1956 // 2. The last file in the directory is found (for find next or find first) 1957 // 3. The first file in the directory is found (for find prev) 1958 // 4. The call to FindNextImage fails for any other reason 1959 while (SetImage(&imgRef) != B_OK) { 1960 curRef = imgRef; 1961 found = FindNextImage(&curRef, &imgRef, next, false); 1962 if (!found) 1963 return false; 1964 } 1965 SetTrackerSelectionToCurrent(); 1966 return true; 1967 } 1968 return false; 1969 } 1970 1971 bool 1972 ShowImageView::NextFile() 1973 { 1974 return ShowNextImage(true, false); 1975 } 1976 1977 bool 1978 ShowImageView::PrevFile() 1979 { 1980 return ShowNextImage(false, false); 1981 } 1982 1983 bool 1984 ShowImageView::HasNextFile() 1985 { 1986 entry_ref ref; 1987 return FindNextImage(&fCurrentRef, &ref, true, false); 1988 } 1989 1990 bool 1991 ShowImageView::HasPrevFile() 1992 { 1993 entry_ref ref; 1994 return FindNextImage(&fCurrentRef, &ref, false, false); 1995 } 1996 1997 bool 1998 ShowImageView::FirstFile() 1999 { 2000 return ShowNextImage(true, true); 2001 } 2002 2003 void 2004 ShowImageView::SetZoom(float zoom) 2005 { 2006 if ((fScaleBilinear || fDither) && fZoom != zoom) { 2007 DeleteScaler(); 2008 } 2009 fZoom = zoom; 2010 FixupScrollBars(); 2011 Invalidate(); 2012 } 2013 2014 void 2015 ShowImageView::ZoomIn() 2016 { 2017 if (fZoom < 16) { 2018 SetZoom(fZoom + 0.25); 2019 } 2020 } 2021 2022 void 2023 ShowImageView::ZoomOut() 2024 { 2025 if (fZoom > 0.25) { 2026 SetZoom(fZoom - 0.25); 2027 } 2028 } 2029 2030 void 2031 ShowImageView::SetSlideShowDelay(float seconds) 2032 { 2033 ShowImageSettings* settings; 2034 int32 delay = (int)(seconds * 10.0); 2035 if (fSlideShowDelay != delay) { 2036 // update counter 2037 fSlideShowCountDown = delay - (fSlideShowDelay - fSlideShowCountDown); 2038 if (fSlideShowCountDown <= 0) { 2039 // show next image on next Pulse() 2040 fSlideShowCountDown = 1; 2041 } 2042 fSlideShowDelay = delay; 2043 settings = my_app->Settings(); 2044 if (settings->Lock()) { 2045 settings->SetInt32("SlideShowDelay", fSlideShowDelay); 2046 settings->Unlock(); 2047 } 2048 } 2049 } 2050 2051 void 2052 ShowImageView::StartSlideShow() 2053 { 2054 fSlideShow = true; fSlideShowCountDown = fSlideShowDelay; 2055 } 2056 2057 void 2058 ShowImageView::StopSlideShow() 2059 { 2060 fSlideShow = false; 2061 } 2062 2063 void 2064 ShowImageView::DoImageOperation(ImageProcessor::operation op, bool quiet) 2065 { 2066 BMessenger msgr; 2067 ImageProcessor imageProcessor(op, fBitmap, msgr, 0); 2068 imageProcessor.Start(false); 2069 BBitmap* bm = imageProcessor.DetachBitmap(); 2070 if (bm == NULL) { 2071 // operation failed 2072 return; 2073 } 2074 2075 // update orientation state 2076 if (op != ImageProcessor::kInvert) { 2077 // Note: If one of these fails, check its definition in class ImageProcessor. 2078 ASSERT(ImageProcessor::kRotateClockwise < ImageProcessor::kNumberOfAffineTransformations); 2079 ASSERT(ImageProcessor::kRotateAntiClockwise < ImageProcessor::kNumberOfAffineTransformations); 2080 ASSERT(ImageProcessor::kMirrorVertical < ImageProcessor::kNumberOfAffineTransformations); 2081 ASSERT(ImageProcessor::kMirrorHorizontal < ImageProcessor::kNumberOfAffineTransformations); 2082 fImageOrientation = fTransformation[op][fImageOrientation]; 2083 } else { 2084 fInverted = !fInverted; 2085 } 2086 2087 if (!quiet) { 2088 // write orientation state 2089 BNode node(&fCurrentRef); 2090 int32 orientation = fImageOrientation; 2091 if (fInverted) orientation += 256; 2092 if (orientation != k0) { 2093 node.WriteAttr(SHOW_IMAGE_ORIENTATION_ATTRIBUTE, B_INT32_TYPE, 0, &orientation, sizeof(orientation)); 2094 } else { 2095 node.RemoveAttr(SHOW_IMAGE_ORIENTATION_ATTRIBUTE); 2096 } 2097 } 2098 2099 // set new bitmap 2100 DeleteBitmap(); 2101 fBitmap = bm; 2102 2103 if (!quiet) { 2104 // remove selection 2105 SetHasSelection(false); 2106 Notify(NULL); 2107 } 2108 } 2109 2110 // image operation initiated by user 2111 void 2112 ShowImageView::UserDoImageOperation(ImageProcessor::operation op, bool quiet) 2113 { 2114 fUndo.Clear(); 2115 DoImageOperation(op, quiet); 2116 } 2117 2118 void 2119 ShowImageView::Rotate(int degree) 2120 { 2121 if (degree == 90) { 2122 UserDoImageOperation(ImageProcessor::kRotateClockwise); 2123 } else if (degree == 270) { 2124 UserDoImageOperation(ImageProcessor::kRotateAntiClockwise); 2125 } 2126 } 2127 2128 void 2129 ShowImageView::Mirror(bool vertical) 2130 { 2131 if (vertical) { 2132 UserDoImageOperation(ImageProcessor::kMirrorVertical); 2133 } else { 2134 UserDoImageOperation(ImageProcessor::kMirrorHorizontal); 2135 } 2136 } 2137 2138 void 2139 ShowImageView::Invert() 2140 { 2141 if (fBitmap->ColorSpace() != B_CMAP8) { 2142 // Only allow an invert operation if the 2143 // bitmap color space is supported by the 2144 // invert algorithm 2145 UserDoImageOperation(ImageProcessor::kInvert); 2146 } 2147 } 2148 2149 void 2150 ShowImageView::SetIcon(bool clear, icon_size which) 2151 { 2152 int32 size; 2153 switch (which) { 2154 case B_MINI_ICON: size = 16; 2155 break; 2156 case B_LARGE_ICON: size = 32; 2157 break; 2158 default: 2159 return; 2160 } 2161 2162 BRect rect(fBitmap->Bounds()); 2163 float s; 2164 s = size / (rect.Width()+1.0); 2165 2166 if (s * (rect.Height()+1.0) <= size) { 2167 rect.right = size-1; 2168 rect.bottom = static_cast<int>(s * (rect.Height()+1.0))-1; 2169 // center vertically 2170 rect.OffsetBy(0, (size - rect.IntegerHeight()) / 2); 2171 } else { 2172 s = size / (rect.Height()+1.0); 2173 rect.right = static_cast<int>(s * (rect.Width()+1.0))-1; 2174 rect.bottom = size-1; 2175 // center horizontally 2176 rect.OffsetBy((size - rect.IntegerWidth()) / 2, 0); 2177 } 2178 2179 // scale bitmap to thumbnail size 2180 BMessenger msgr; 2181 Scaler scaler(fBitmap, rect, msgr, 0, true); 2182 BBitmap* thumbnail = scaler.GetBitmap(); 2183 scaler.Start(false); 2184 ASSERT(thumbnail->ColorSpace() == B_CMAP8); 2185 // create icon from thumbnail 2186 BBitmap icon(BRect(0, 0, size-1, size-1), B_CMAP8); 2187 memset(icon.Bits(), B_TRANSPARENT_MAGIC_CMAP8, icon.BitsLength()); 2188 BScreen screen; 2189 const uchar* src = (uchar*)thumbnail->Bits(); 2190 uchar* dest = (uchar*)icon.Bits(); 2191 const int32 srcBPR = thumbnail->BytesPerRow(); 2192 const int32 destBPR = icon.BytesPerRow(); 2193 const int32 dx = (int32)rect.left; 2194 const int32 dy = (int32)rect.top; 2195 2196 for (int32 y = 0; y <= rect.IntegerHeight(); y ++) { 2197 for (int32 x = 0; x <= rect.IntegerWidth(); x ++) { 2198 const uchar* s = src + y * srcBPR + x; 2199 uchar* d = dest + (y+dy) * destBPR + (x+dx); 2200 *d = *s; 2201 } 2202 } 2203 2204 // set icon 2205 BNode node(&fCurrentRef); 2206 BNodeInfo info(&node); 2207 info.SetIcon(clear ? NULL : &icon, which); 2208 } 2209 2210 void 2211 ShowImageView::SetIcon(bool clear) 2212 { 2213 SetIcon(clear, B_MINI_ICON); 2214 SetIcon(clear, B_LARGE_ICON); 2215 } 2216