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