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