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