1 /* 2 * Copyright 2006-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "IconView.h" 8 9 #include <new> 10 #include <stdlib.h> 11 #include <strings.h> 12 13 #include <Application.h> 14 #include <AppFileInfo.h> 15 #include <Attributes.h> 16 #include <Bitmap.h> 17 #include <Catalog.h> 18 #include <ControlLook.h> 19 #include <IconEditorProtocol.h> 20 #include <IconUtils.h> 21 #include <Locale.h> 22 #include <MenuItem.h> 23 #include <Mime.h> 24 #include <NodeMonitor.h> 25 #include <PopUpMenu.h> 26 #include <Resources.h> 27 #include <Roster.h> 28 #include <Size.h> 29 30 #include "FileTypes.h" 31 #include "MimeTypeListView.h" 32 33 34 #undef B_TRANSLATION_CONTEXT 35 #define B_TRANSLATION_CONTEXT "Icon View" 36 37 38 using namespace std; 39 40 41 status_t 42 icon_for_type(const BMimeType& type, uint8** _data, size_t* _size, 43 icon_source* _source) 44 { 45 if (_data == NULL || _size == NULL) 46 return B_BAD_VALUE; 47 48 icon_source source = kNoIcon; 49 uint8* data; 50 size_t size; 51 52 if (type.GetIcon(&data, &size) == B_OK) 53 source = kOwnIcon; 54 55 if (source == kNoIcon) { 56 // check for icon from preferred app 57 58 char preferred[B_MIME_TYPE_LENGTH]; 59 if (type.GetPreferredApp(preferred) == B_OK) { 60 BMimeType preferredApp(preferred); 61 62 if (preferredApp.GetIconForType(type.Type(), &data, &size) == B_OK) 63 source = kApplicationIcon; 64 } 65 } 66 67 if (source == kNoIcon) { 68 // check super type for an icon 69 70 BMimeType superType; 71 if (type.GetSupertype(&superType) == B_OK) { 72 if (superType.GetIcon(&data, &size) == B_OK) 73 source = kSupertypeIcon; 74 else { 75 // check the super type's preferred app 76 char preferred[B_MIME_TYPE_LENGTH]; 77 if (superType.GetPreferredApp(preferred) == B_OK) { 78 BMimeType preferredApp(preferred); 79 80 if (preferredApp.GetIconForType(superType.Type(), 81 &data, &size) == B_OK) 82 source = kSupertypeIcon; 83 } 84 } 85 } 86 } 87 88 if (source != kNoIcon) { 89 *_data = data; 90 *_size = size; 91 } // NOTE: else there is no data, so nothing is leaked. 92 if (_source) 93 *_source = source; 94 95 return source != kNoIcon ? B_OK : B_ERROR; 96 } 97 98 99 status_t 100 icon_for_type(const BMimeType& type, BBitmap& bitmap, icon_size size, 101 icon_source* _source) 102 { 103 icon_source source = kNoIcon; 104 105 if (type.GetIcon(&bitmap, size) == B_OK) 106 source = kOwnIcon; 107 108 if (source == kNoIcon) { 109 // check for icon from preferred app 110 111 char preferred[B_MIME_TYPE_LENGTH]; 112 if (type.GetPreferredApp(preferred) == B_OK) { 113 BMimeType preferredApp(preferred); 114 115 if (preferredApp.GetIconForType(type.Type(), &bitmap, size) == B_OK) 116 source = kApplicationIcon; 117 } 118 } 119 120 if (source == kNoIcon) { 121 // check super type for an icon 122 123 BMimeType superType; 124 if (type.GetSupertype(&superType) == B_OK) { 125 if (superType.GetIcon(&bitmap, size) == B_OK) 126 source = kSupertypeIcon; 127 else { 128 // check the super type's preferred app 129 char preferred[B_MIME_TYPE_LENGTH]; 130 if (superType.GetPreferredApp(preferred) == B_OK) { 131 BMimeType preferredApp(preferred); 132 133 if (preferredApp.GetIconForType(superType.Type(), 134 &bitmap, size) == B_OK) 135 source = kSupertypeIcon; 136 } 137 } 138 } 139 } 140 141 if (_source) 142 *_source = source; 143 144 return source != kNoIcon ? B_OK : B_ERROR; 145 } 146 147 148 // #pragma mark - 149 150 151 Icon::Icon() 152 : 153 fLarge(NULL), 154 fMini(NULL), 155 fData(NULL), 156 fSize(0) 157 { 158 } 159 160 161 Icon::Icon(const Icon& source) 162 : 163 fLarge(NULL), 164 fMini(NULL), 165 fData(NULL), 166 fSize(0) 167 { 168 *this = source; 169 } 170 171 172 Icon::~Icon() 173 { 174 delete fLarge; 175 delete fMini; 176 free(fData); 177 } 178 179 180 void 181 Icon::SetTo(const BAppFileInfo& info, const char* type) 182 { 183 Unset(); 184 185 uint8* data; 186 size_t size; 187 188 if (info.GetIconForType(type, &data, &size) == B_OK) { 189 // we have the vector icon, no need to get the rest 190 AdoptData(data, size); 191 return; 192 } 193 194 BBitmap* icon = AllocateBitmap(B_LARGE_ICON, B_CMAP8); 195 if (icon && info.GetIconForType(type, icon, B_LARGE_ICON) == B_OK) 196 AdoptLarge(icon); 197 else 198 delete icon; 199 200 icon = AllocateBitmap(B_MINI_ICON, B_CMAP8); 201 if (icon && info.GetIconForType(type, icon, B_MINI_ICON) == B_OK) 202 AdoptMini(icon); 203 else 204 delete icon; 205 } 206 207 208 void 209 Icon::SetTo(const entry_ref& ref, const char* type) 210 { 211 Unset(); 212 213 BFile file(&ref, B_READ_ONLY); 214 BAppFileInfo info(&file); 215 if (file.InitCheck() == B_OK 216 && info.InitCheck() == B_OK) 217 SetTo(info, type); 218 } 219 220 221 void 222 Icon::SetTo(const BMimeType& type, icon_source* _source) 223 { 224 Unset(); 225 226 uint8* data; 227 size_t size; 228 if (icon_for_type(type, &data, &size, _source) == B_OK) { 229 // we have the vector icon, no need to get the rest 230 AdoptData(data, size); 231 return; 232 } 233 234 BBitmap* icon = AllocateBitmap(B_LARGE_ICON, B_CMAP8); 235 if (icon && icon_for_type(type, *icon, B_LARGE_ICON, _source) == B_OK) 236 AdoptLarge(icon); 237 else 238 delete icon; 239 240 icon = AllocateBitmap(B_MINI_ICON, B_CMAP8); 241 if (icon && icon_for_type(type, *icon, B_MINI_ICON) == B_OK) 242 AdoptMini(icon); 243 else 244 delete icon; 245 } 246 247 248 status_t 249 Icon::CopyTo(BAppFileInfo& info, const char* type, bool force) const 250 { 251 status_t status = B_OK; 252 253 if (fLarge != NULL || force) 254 status = info.SetIconForType(type, fLarge, B_LARGE_ICON); 255 if (fMini != NULL || force) 256 status = info.SetIconForType(type, fMini, B_MINI_ICON); 257 if (fData != NULL || force) 258 status = info.SetIconForType(type, fData, fSize); 259 260 return status; 261 } 262 263 264 status_t 265 Icon::CopyTo(const entry_ref& ref, const char* type, bool force) const 266 { 267 BFile file; 268 status_t status = file.SetTo(&ref, B_READ_ONLY); 269 if (status < B_OK) 270 return status; 271 272 BAppFileInfo info(&file); 273 status = info.InitCheck(); 274 if (status < B_OK) 275 return status; 276 277 return CopyTo(info, type, force); 278 } 279 280 281 status_t 282 Icon::CopyTo(BMimeType& type, bool force) const 283 { 284 status_t status = B_OK; 285 286 if (fLarge != NULL || force) 287 status = type.SetIcon(fLarge, B_LARGE_ICON); 288 if (fMini != NULL || force) 289 status = type.SetIcon(fMini, B_MINI_ICON); 290 if (fData != NULL || force) 291 status = type.SetIcon(fData, fSize); 292 293 return status; 294 } 295 296 297 status_t 298 Icon::CopyTo(BMessage& message) const 299 { 300 status_t status = B_OK; 301 302 if (status == B_OK && fLarge != NULL) { 303 BMessage archive; 304 status = fLarge->Archive(&archive); 305 if (status == B_OK) 306 status = message.AddMessage("icon/large", &archive); 307 } 308 if (status == B_OK && fMini != NULL) { 309 BMessage archive; 310 status = fMini->Archive(&archive); 311 if (status == B_OK) 312 status = message.AddMessage("icon/mini", &archive); 313 } 314 if (status == B_OK && fData != NULL) 315 status = message.AddData("icon", B_VECTOR_ICON_TYPE, fData, fSize); 316 317 return B_OK; 318 } 319 320 321 void 322 Icon::SetLarge(const BBitmap* large) 323 { 324 if (large != NULL) { 325 if (fLarge == NULL) 326 fLarge = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8); 327 328 memcpy(fLarge->Bits(), large->Bits(), min_c(large->BitsLength(), 329 fLarge->BitsLength())); 330 } else { 331 delete fLarge; 332 fLarge = NULL; 333 } 334 } 335 336 337 void 338 Icon::SetMini(const BBitmap* mini) 339 { 340 if (mini != NULL) { 341 if (fMini == NULL) 342 fMini = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8); 343 344 memcpy(fMini->Bits(), mini->Bits(), min_c(mini->BitsLength(), 345 fMini->BitsLength())); 346 } else { 347 delete fMini; 348 fMini = NULL; 349 } 350 } 351 352 353 void 354 Icon::SetData(const uint8* data, size_t size) 355 { 356 free(fData); 357 fData = NULL; 358 359 if (data != NULL) { 360 fData = (uint8*)malloc(size); 361 if (fData != NULL) { 362 fSize = size; 363 //fType = B_VECTOR_ICON_TYPE; 364 memcpy(fData, data, size); 365 } 366 } 367 } 368 369 370 void 371 Icon::Unset() 372 { 373 delete fLarge; 374 delete fMini; 375 free(fData); 376 377 fLarge = fMini = NULL; 378 fData = NULL; 379 } 380 381 382 bool 383 Icon::HasData() const 384 { 385 return fData != NULL || fLarge != NULL || fMini != NULL; 386 } 387 388 389 status_t 390 Icon::GetData(icon_size which, BBitmap** _bitmap) const 391 { 392 BBitmap* source; 393 switch (which) { 394 case B_LARGE_ICON: 395 source = fLarge; 396 break; 397 case B_MINI_ICON: 398 source = fMini; 399 break; 400 default: 401 return B_BAD_VALUE; 402 } 403 404 if (source == NULL) 405 return B_ENTRY_NOT_FOUND; 406 407 BBitmap* bitmap = new (nothrow) BBitmap(source); 408 if (bitmap == NULL || bitmap->InitCheck() != B_OK) { 409 delete bitmap; 410 return B_NO_MEMORY; 411 } 412 413 *_bitmap = bitmap; 414 return B_OK; 415 } 416 417 418 status_t 419 Icon::GetData(uint8** _data, size_t* _size) const 420 { 421 if (fData == NULL) 422 return B_ENTRY_NOT_FOUND; 423 424 uint8* data = (uint8*)malloc(fSize); 425 if (data == NULL) 426 return B_NO_MEMORY; 427 428 memcpy(data, fData, fSize); 429 *_data = data; 430 *_size = fSize; 431 return B_OK; 432 } 433 434 435 status_t 436 Icon::GetIcon(BBitmap* bitmap) const 437 { 438 if (bitmap == NULL) 439 return B_BAD_VALUE; 440 441 if (fData != NULL 442 && BIconUtils::GetVectorIcon(fData, fSize, bitmap) == B_OK) 443 return B_OK; 444 445 int32 width = bitmap->Bounds().IntegerWidth() + 1; 446 447 if (width == B_LARGE_ICON && fLarge != NULL) { 448 bitmap->SetBits(fLarge->Bits(), fLarge->BitsLength(), 0, 449 fLarge->ColorSpace()); 450 return B_OK; 451 } 452 if (width == B_MINI_ICON && fMini != NULL) { 453 bitmap->SetBits(fMini->Bits(), fMini->BitsLength(), 0, 454 fMini->ColorSpace()); 455 return B_OK; 456 } 457 458 return B_ENTRY_NOT_FOUND; 459 } 460 461 462 Icon& 463 Icon::operator=(const Icon& source) 464 { 465 Unset(); 466 467 SetData(source.fData, source.fSize); 468 SetLarge(source.fLarge); 469 SetMini(source.fMini); 470 471 return *this; 472 } 473 474 475 void 476 Icon::AdoptLarge(BBitmap *large) 477 { 478 delete fLarge; 479 fLarge = large; 480 } 481 482 483 void 484 Icon::AdoptMini(BBitmap *mini) 485 { 486 delete fMini; 487 fMini = mini; 488 } 489 490 491 void 492 Icon::AdoptData(uint8* data, size_t size) 493 { 494 free(fData); 495 fData = data; 496 fSize = size; 497 } 498 499 500 /*static*/ BBitmap* 501 Icon::AllocateBitmap(icon_size size, int32 space) 502 { 503 int32 kSpace = B_RGBA32; 504 if (space == -1) 505 space = kSpace; 506 507 BBitmap* bitmap = new (nothrow) BBitmap(BRect(BPoint(0, 0), 508 be_control_look->ComposeIconSize(size)), 509 (color_space)space); 510 if (bitmap == NULL || bitmap->InitCheck() != B_OK) { 511 delete bitmap; 512 return NULL; 513 } 514 515 return bitmap; 516 } 517 518 519 // #pragma mark - 520 521 522 IconView::IconView(const char* name, uint32 flags) 523 : BControl(name, NULL, NULL, B_WILL_DRAW | flags), 524 fModificationMessage(NULL), 525 fIconSize((icon_size)0), 526 fIcon(NULL), 527 fHeapIcon(NULL), 528 fHasRef(false), 529 fHasType(false), 530 fIconData(NULL), 531 fTracking(false), 532 fDragging(false), 533 fDropTarget(false), 534 fShowEmptyFrame(true) 535 { 536 SetIconSize(B_LARGE_ICON); 537 } 538 539 540 IconView::~IconView() 541 { 542 delete fIcon; 543 delete fModificationMessage; 544 } 545 546 547 void 548 IconView::AttachedToWindow() 549 { 550 AdoptParentColors(); 551 552 fTarget = this; 553 554 // SetTo() was already called before we were a valid messenger 555 if (fHasRef || fHasType) 556 _StartWatching(); 557 } 558 559 560 void 561 IconView::DetachedFromWindow() 562 { 563 _StopWatching(); 564 } 565 566 567 void 568 IconView::MessageReceived(BMessage* message) 569 { 570 if (message->WasDropped() && message->ReturnAddress() != BMessenger(this) 571 && AcceptsDrag(message)) { 572 // set icon from message 573 BBitmap* mini = NULL; 574 BBitmap* large = NULL; 575 const uint8* data = NULL; 576 ssize_t size = 0; 577 578 message->FindData("icon", B_VECTOR_ICON_TYPE, (const void**)&data, 579 &size); 580 581 BMessage archive; 582 if (message->FindMessage("icon/large", &archive) == B_OK) 583 large = (BBitmap*)BBitmap::Instantiate(&archive); 584 if (message->FindMessage("icon/mini", &archive) == B_OK) 585 mini = (BBitmap*)BBitmap::Instantiate(&archive); 586 587 if (large != NULL || mini != NULL || (data != NULL && size > 0)) 588 _SetIcon(large, mini, data, size); 589 else { 590 entry_ref ref; 591 if (message->FindRef("refs", &ref) == B_OK) 592 _SetIcon(&ref); 593 } 594 595 delete large; 596 delete mini; 597 598 return; 599 } 600 601 switch (message->what) { 602 case kMsgIconInvoked: 603 case kMsgEditIcon: 604 case kMsgAddIcon: 605 _AddOrEditIcon(); 606 break; 607 case kMsgRemoveIcon: 608 _RemoveIcon(); 609 break; 610 611 case B_NODE_MONITOR: 612 { 613 if (!fHasRef) 614 break; 615 616 int32 opcode; 617 if (message->FindInt32("opcode", &opcode) != B_OK 618 || opcode != B_ATTR_CHANGED) 619 break; 620 621 const char* name; 622 if (message->FindString("attr", &name) != B_OK) 623 break; 624 625 if (!strcmp(name, kAttrMiniIcon) 626 || !strcmp(name, kAttrLargeIcon) 627 || !strcmp(name, kAttrIcon)) 628 Update(); 629 break; 630 } 631 632 case B_META_MIME_CHANGED: 633 { 634 if (!fHasType) 635 break; 636 637 const char* type; 638 int32 which; 639 if (message->FindString("be:type", &type) != B_OK 640 || message->FindInt32("be:which", &which) != B_OK) 641 break; 642 643 if (!strcasecmp(type, fType.Type())) { 644 switch (which) { 645 case B_MIME_TYPE_DELETED: 646 Unset(); 647 break; 648 649 case B_ICON_CHANGED: 650 Update(); 651 break; 652 653 default: 654 break; 655 } 656 } else if (fSource != kOwnIcon 657 && message->FindString("be:extra_type", &type) == B_OK 658 && !strcasecmp(type, fType.Type())) { 659 // this change could still affect our current icon 660 661 if (which == B_MIME_TYPE_DELETED 662 || which == B_PREFERRED_APP_CHANGED 663 || which == B_SUPPORTED_TYPES_CHANGED 664 || which == B_ICON_FOR_TYPE_CHANGED) 665 Update(); 666 } 667 break; 668 } 669 670 case B_ICON_DATA_EDITED: 671 { 672 const uint8* data; 673 ssize_t size; 674 if (message->FindData("icon data", B_VECTOR_ICON_TYPE, 675 (const void**)&data, &size) < B_OK) 676 break; 677 678 _SetIcon(NULL, NULL, data, size); 679 break; 680 } 681 682 default: 683 BControl::MessageReceived(message); 684 break; 685 } 686 } 687 688 689 bool 690 IconView::AcceptsDrag(const BMessage* message) 691 { 692 if (!IsEnabled()) 693 return false; 694 695 type_code type; 696 int32 count; 697 if (message->GetInfo("refs", &type, &count) == B_OK && count == 1 698 && type == B_REF_TYPE) { 699 // if we're bound to an entry, check that no one drops this to us 700 entry_ref ref; 701 if (fHasRef && message->FindRef("refs", &ref) == B_OK && fRef == ref) 702 return false; 703 704 return true; 705 } 706 707 if ((message->GetInfo("icon/large", &type) == B_OK 708 && type == B_MESSAGE_TYPE) 709 || (message->GetInfo("icon", &type) == B_OK 710 && type == B_VECTOR_ICON_TYPE) 711 || (message->GetInfo("icon/mini", &type) == B_OK 712 && type == B_MESSAGE_TYPE)) 713 return true; 714 715 return false; 716 } 717 718 719 BRect 720 IconView::BitmapRect() const 721 { 722 return fIconRect; 723 } 724 725 726 void 727 IconView::Draw(BRect updateRect) 728 { 729 SetDrawingMode(B_OP_ALPHA); 730 731 if (fHeapIcon != NULL) 732 DrawBitmap(fHeapIcon, BitmapRect().LeftTop()); 733 else if (fIcon != NULL) 734 DrawBitmap(fIcon, BitmapRect().LeftTop()); 735 else if (!fDropTarget && fShowEmptyFrame) { 736 // draw frame so that the user knows here is something he 737 // might be able to click on 738 SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); 739 StrokeRect(BitmapRect()); 740 } 741 742 if (IsFocus()) { 743 // mark this view as a having focus 744 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 745 StrokeRect(BitmapRect()); 746 } 747 if (fDropTarget) { 748 // mark this view as a drop target 749 SetHighColor(0, 0, 0); 750 SetPenSize(2); 751 BRect rect = BitmapRect(); 752 // TODO: this is an incompatibility between R5 and Haiku and should be fixed! 753 // (Necessary adjustment differs.) 754 rect.left++; 755 rect.top++; 756 757 StrokeRect(rect); 758 SetPenSize(1); 759 } 760 } 761 762 763 void 764 IconView::GetPreferredSize(float* _width, float* _height) 765 { 766 if (_width) 767 *_width = fIconRect.Width(); 768 769 if (_height) 770 *_height = fIconRect.Height(); 771 } 772 773 774 BSize 775 IconView::MinSize() 776 { 777 float width, height; 778 GetPreferredSize(&width, &height); 779 return BSize(width, height); 780 } 781 782 783 BSize 784 IconView::PreferredSize() 785 { 786 return MinSize(); 787 } 788 789 790 BSize 791 IconView::MaxSize() 792 { 793 return MinSize(); 794 } 795 796 797 void 798 IconView::MouseDown(BPoint where) 799 { 800 if (!IsEnabled()) 801 return; 802 803 int32 buttons = B_PRIMARY_MOUSE_BUTTON; 804 int32 clicks = 1; 805 if (Looper() != NULL && Looper()->CurrentMessage() != NULL) { 806 if (Looper()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK) 807 buttons = B_PRIMARY_MOUSE_BUTTON; 808 if (Looper()->CurrentMessage()->FindInt32("clicks", &clicks) != B_OK) 809 clicks = 1; 810 } 811 812 if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0 813 && BitmapRect().Contains(where)) { 814 if (clicks == 2) { 815 // double click - open Icon-O-Matic 816 Invoke(); 817 } else if (fIcon != NULL) { 818 // start tracking - this icon might be dragged around 819 fDragPoint = where; 820 fTracking = true; 821 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 822 } 823 } 824 825 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) { 826 // show context menu 827 828 ConvertToScreen(&where); 829 830 BPopUpMenu* menu = new BPopUpMenu("context"); 831 menu->SetFont(be_plain_font); 832 833 bool hasIcon = fHasType ? fSource == kOwnIcon : fIcon != NULL; 834 if (hasIcon) { 835 menu->AddItem(new BMenuItem( 836 B_TRANSLATE("Edit icon" B_UTF8_ELLIPSIS), 837 new BMessage(kMsgEditIcon))); 838 } else { 839 menu->AddItem(new BMenuItem( 840 B_TRANSLATE("Add icon" B_UTF8_ELLIPSIS), 841 new BMessage(kMsgAddIcon))); 842 } 843 844 BMenuItem* item = new BMenuItem( 845 B_TRANSLATE("Remove icon"), new BMessage(kMsgRemoveIcon)); 846 if (!hasIcon) 847 item->SetEnabled(false); 848 849 menu->AddItem(item); 850 menu->SetTargetForItems(fTarget); 851 852 menu->Go(where, true, false, true); 853 } 854 } 855 856 857 void 858 IconView::MouseUp(BPoint where) 859 { 860 fTracking = false; 861 fDragging = false; 862 863 if (fDropTarget) { 864 fDropTarget = false; 865 Invalidate(); 866 } 867 } 868 869 870 void 871 IconView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) 872 { 873 if (fTracking && !fDragging && fIcon != NULL 874 && (abs((int32)(where.x - fDragPoint.x)) > 3 875 || abs((int32)(where.y - fDragPoint.y)) > 3)) { 876 // Start drag 877 BMessage message(B_SIMPLE_DATA); 878 879 ::Icon* icon = fIconData; 880 if (fHasRef || fHasType) { 881 icon = new ::Icon; 882 if (fHasRef) 883 icon->SetTo(fRef, fType.Type()); 884 else if (fHasType) 885 icon->SetTo(fType); 886 } 887 888 icon->CopyTo(message); 889 890 if (icon != fIconData) 891 delete icon; 892 893 BBitmap *dragBitmap = new BBitmap(fIcon->Bounds(), B_RGBA32, true); 894 dragBitmap->Lock(); 895 BView *view 896 = new BView(dragBitmap->Bounds(), B_EMPTY_STRING, B_FOLLOW_NONE, 0); 897 dragBitmap->AddChild(view); 898 899 view->SetHighColor(B_TRANSPARENT_COLOR); 900 view->FillRect(dragBitmap->Bounds()); 901 view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); 902 view->SetDrawingMode(B_OP_ALPHA); 903 view->SetHighColor(0, 0, 0, 160); 904 view->DrawBitmap(fIcon); 905 906 view->Sync(); 907 dragBitmap->Unlock(); 908 909 DragMessage(&message, dragBitmap, B_OP_ALPHA, 910 fDragPoint - BitmapRect().LeftTop(), this); 911 fDragging = true; 912 } 913 914 if (dragMessage != NULL && !fDragging && AcceptsDrag(dragMessage)) { 915 bool dropTarget = transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW; 916 if (dropTarget != fDropTarget) { 917 fDropTarget = dropTarget; 918 Invalidate(); 919 } 920 } else if (fDropTarget) { 921 fDropTarget = false; 922 Invalidate(); 923 } 924 } 925 926 927 void 928 IconView::KeyDown(const char* bytes, int32 numBytes) 929 { 930 if (numBytes == 1) { 931 switch (bytes[0]) { 932 case B_DELETE: 933 case B_BACKSPACE: 934 _RemoveIcon(); 935 return; 936 case B_ENTER: 937 case B_SPACE: 938 Invoke(); 939 return; 940 } 941 } 942 943 BControl::KeyDown(bytes, numBytes); 944 } 945 946 947 void 948 IconView::MakeFocus(bool focus) 949 { 950 if (focus != IsFocus()) 951 Invalidate(); 952 953 BControl::MakeFocus(focus); 954 } 955 956 957 void 958 IconView::SetTo(const entry_ref& ref, const char* fileType) 959 { 960 Unset(); 961 962 fHasRef = true; 963 fRef = ref; 964 if (fileType != NULL) 965 fType.SetTo(fileType); 966 else 967 fType.Unset(); 968 969 _StartWatching(); 970 Update(); 971 } 972 973 974 void 975 IconView::SetTo(const BMimeType& type) 976 { 977 Unset(); 978 979 if (type.Type() == NULL) 980 return; 981 982 fHasType = true; 983 fType.SetTo(type.Type()); 984 985 _StartWatching(); 986 Update(); 987 } 988 989 990 void 991 IconView::SetTo(::Icon* icon) 992 { 993 if (fIconData == icon) 994 return; 995 996 Unset(); 997 998 fIconData = icon; 999 1000 Update(); 1001 } 1002 1003 1004 void 1005 IconView::Unset() 1006 { 1007 if (fHasRef || fHasType) 1008 _StopWatching(); 1009 1010 fHasRef = false; 1011 fHasType = false; 1012 1013 fType.Unset(); 1014 fIconData = NULL; 1015 } 1016 1017 1018 void 1019 IconView::Update() 1020 { 1021 delete fIcon; 1022 fIcon = NULL; 1023 1024 Invalidate(); 1025 // this will actually trigger a redraw *after* we updated the icon below 1026 1027 BBitmap* icon = NULL; 1028 1029 if (fHasRef) { 1030 BFile file(&fRef, B_READ_ONLY); 1031 if (file.InitCheck() != B_OK) 1032 return; 1033 1034 BNodeInfo info; 1035 if (info.SetTo(&file) != B_OK) 1036 return; 1037 1038 icon = Icon::AllocateBitmap(fIconSize); 1039 if (icon != NULL && info.GetTrackerIcon(icon, 1040 (icon_size)(icon->Bounds().IntegerWidth() + 1)) != B_OK) { 1041 delete icon; 1042 return; 1043 } 1044 } else if (fHasType) { 1045 icon = Icon::AllocateBitmap(fIconSize); 1046 if (icon != NULL && icon_for_type(fType, *icon, (icon_size)fIconSize, 1047 &fSource) != B_OK) { 1048 delete icon; 1049 return; 1050 } 1051 } else if (fIconData) { 1052 icon = Icon::AllocateBitmap(fIconSize); 1053 if (fIconData->GetIcon(icon) != B_OK) { 1054 delete icon; 1055 icon = NULL; 1056 } 1057 } 1058 1059 fIcon = icon; 1060 } 1061 1062 1063 void 1064 IconView::SetIconSize(icon_size size) 1065 { 1066 if (size < B_MINI_ICON) 1067 size = B_MINI_ICON; 1068 if (size > 256) 1069 size = (icon_size)256; 1070 if (size == fIconSize) 1071 return; 1072 1073 fIconSize = size; 1074 fIconRect = BRect(BPoint(0, 0), be_control_look->ComposeIconSize(fIconSize)); 1075 Update(); 1076 } 1077 1078 1079 void 1080 IconView::ShowIconHeap(bool show) 1081 { 1082 if (show == (fHeapIcon != NULL)) 1083 return; 1084 1085 if (show) { 1086 BResources* resources = be_app->AppResources(); 1087 if (resources != NULL) { 1088 const void* data = NULL; 1089 size_t size; 1090 data = resources->LoadResource('VICN', "icon heap", &size); 1091 if (data != NULL) { 1092 // got vector icon data 1093 fHeapIcon = Icon::AllocateBitmap(B_LARGE_ICON, B_RGBA32); 1094 if (BIconUtils::GetVectorIcon((const uint8*)data, 1095 size, fHeapIcon) != B_OK) { 1096 // bad data 1097 delete fHeapIcon; 1098 fHeapIcon = NULL; 1099 data = NULL; 1100 } 1101 } 1102 if (data == NULL) { 1103 // no vector icon or failed to get bitmap 1104 // try bitmap icon 1105 data = resources->LoadResource(B_LARGE_ICON_TYPE, "icon heap", 1106 NULL); 1107 if (data != NULL) { 1108 fHeapIcon = Icon::AllocateBitmap(B_LARGE_ICON, B_CMAP8); 1109 if (fHeapIcon != NULL) { 1110 memcpy(fHeapIcon->Bits(), data, 1111 fHeapIcon->BitsLength()); 1112 } 1113 } 1114 } 1115 } 1116 } else { 1117 delete fHeapIcon; 1118 fHeapIcon = NULL; 1119 } 1120 } 1121 1122 1123 void 1124 IconView::ShowEmptyFrame(bool show) 1125 { 1126 if (show == fShowEmptyFrame) 1127 return; 1128 1129 fShowEmptyFrame = show; 1130 if (fIcon == NULL) 1131 Invalidate(); 1132 } 1133 1134 1135 status_t 1136 IconView::SetTarget(const BMessenger& target) 1137 { 1138 fTarget = target; 1139 return B_OK; 1140 } 1141 1142 1143 void 1144 IconView::SetModificationMessage(BMessage* message) 1145 { 1146 delete fModificationMessage; 1147 fModificationMessage = message; 1148 } 1149 1150 1151 status_t 1152 IconView::Invoke(BMessage* message) 1153 { 1154 if (message == NULL) 1155 fTarget.SendMessage(kMsgIconInvoked); 1156 else 1157 fTarget.SendMessage(message); 1158 return B_OK; 1159 } 1160 1161 1162 Icon* 1163 IconView::Icon() 1164 { 1165 return fIconData; 1166 } 1167 1168 1169 status_t 1170 IconView::GetRef(entry_ref& ref) const 1171 { 1172 if (!fHasRef) 1173 return B_BAD_TYPE; 1174 1175 ref = fRef; 1176 return B_OK; 1177 } 1178 1179 1180 status_t 1181 IconView::GetMimeType(BMimeType& type) const 1182 { 1183 if (!fHasType) 1184 return B_BAD_TYPE; 1185 1186 type.SetTo(fType.Type()); 1187 return B_OK; 1188 } 1189 1190 1191 void 1192 IconView::_AddOrEditIcon() 1193 { 1194 BMessage message; 1195 if (fHasRef && fType.Type() == NULL) { 1196 // in ref mode, Icon-O-Matic can change the icon directly, and 1197 // we'll pick it up via node monitoring 1198 message.what = B_REFS_RECEIVED; 1199 message.AddRef("refs", &fRef); 1200 } else { 1201 // in static or MIME type mode, Icon-O-Matic needs to return the 1202 // buffer it changed once its done 1203 message.what = B_EDIT_ICON_DATA; 1204 message.AddMessenger("reply to", BMessenger(this)); 1205 1206 ::Icon* icon = fIconData; 1207 if (icon == NULL) { 1208 icon = new ::Icon(); 1209 if (fHasRef) 1210 icon->SetTo(fRef, fType.Type()); 1211 else 1212 icon->SetTo(fType); 1213 } 1214 1215 if (icon->HasData()) { 1216 uint8* data; 1217 size_t size; 1218 if (icon->GetData(&data, &size) == B_OK) { 1219 message.AddData("icon data", B_VECTOR_ICON_TYPE, data, size); 1220 free(data); 1221 } 1222 1223 // TODO: somehow figure out how names of objects in the icon 1224 // can be preserved. Maybe in a second (optional) attribute 1225 // where ever a vector icon attribute is present? 1226 } 1227 1228 if (icon != fIconData) 1229 delete icon; 1230 } 1231 1232 be_roster->Launch("application/x-vnd.haiku-icon_o_matic", &message); 1233 } 1234 1235 1236 void 1237 IconView::_SetIcon(BBitmap* large, BBitmap* mini, const uint8* data, 1238 size_t size, bool force) 1239 { 1240 if (fHasRef) { 1241 BFile file(&fRef, B_READ_WRITE); 1242 1243 if (is_application(file)) { 1244 BAppFileInfo info(&file); 1245 if (info.InitCheck() == B_OK) { 1246 if (large != NULL || force) 1247 info.SetIconForType(fType.Type(), large, B_LARGE_ICON); 1248 if (mini != NULL || force) 1249 info.SetIconForType(fType.Type(), mini, B_MINI_ICON); 1250 if (data != NULL || force) 1251 info.SetIconForType(fType.Type(), data, size); 1252 } 1253 } else { 1254 BNodeInfo info(&file); 1255 if (info.InitCheck() == B_OK) { 1256 if (large != NULL || force) 1257 info.SetIcon(large, B_LARGE_ICON); 1258 if (mini != NULL || force) 1259 info.SetIcon(mini, B_MINI_ICON); 1260 if (data != NULL || force) 1261 info.SetIcon(data, size); 1262 } 1263 } 1264 // the icon shown will be updated using node monitoring 1265 } else if (fHasType) { 1266 if (large != NULL || force) 1267 fType.SetIcon(large, B_LARGE_ICON); 1268 if (mini != NULL || force) 1269 fType.SetIcon(mini, B_MINI_ICON); 1270 if (data != NULL || force) 1271 fType.SetIcon(data, size); 1272 1273 // the icon shown will be updated automatically - we're watching 1274 // any changes to the MIME database 1275 } else if (fIconData != NULL) { 1276 if (large != NULL || force) 1277 fIconData->SetLarge(large); 1278 if (mini != NULL || force) 1279 fIconData->SetMini(mini); 1280 if (data != NULL || force) 1281 fIconData->SetData(data, size); 1282 1283 // replace visible icon 1284 if (fIcon == NULL && fIconData->HasData()) 1285 fIcon = Icon::AllocateBitmap(fIconSize); 1286 1287 if (fIconData->GetIcon(fIcon) != B_OK) { 1288 delete fIcon; 1289 fIcon = NULL; 1290 } 1291 Invalidate(); 1292 } 1293 1294 if (fModificationMessage) 1295 Invoke(fModificationMessage); 1296 } 1297 1298 1299 void 1300 IconView::_SetIcon(entry_ref* ref) 1301 { 1302 // retrieve icons from file 1303 BFile file(ref, B_READ_ONLY); 1304 BAppFileInfo info(&file); 1305 if (file.InitCheck() != B_OK || info.InitCheck() != B_OK) 1306 return; 1307 1308 // try vector/PNG icon first 1309 uint8* data = NULL; 1310 size_t size = 0; 1311 if (info.GetIcon(&data, &size) == B_OK) { 1312 _SetIcon(NULL, NULL, data, size); 1313 free(data); 1314 return; 1315 } 1316 1317 // try large/mini icons 1318 bool hasMini = false; 1319 bool hasLarge = false; 1320 1321 BBitmap* large = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8); 1322 if (large->InitCheck() != B_OK) { 1323 delete large; 1324 large = NULL; 1325 } 1326 BBitmap* mini = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8); 1327 if (mini->InitCheck() != B_OK) { 1328 delete mini; 1329 mini = NULL; 1330 } 1331 1332 if (large != NULL && info.GetIcon(large, B_LARGE_ICON) == B_OK) 1333 hasLarge = true; 1334 if (mini != NULL && info.GetIcon(mini, B_MINI_ICON) == B_OK) 1335 hasMini = true; 1336 1337 if (!hasMini && !hasLarge) { 1338 // TODO: don't forget device icons! 1339 1340 // try MIME type icon 1341 char type[B_MIME_TYPE_LENGTH]; 1342 if (info.GetType(type) != B_OK) 1343 return; 1344 1345 BMimeType mimeType(type); 1346 if (icon_for_type(mimeType, &data, &size) != B_OK) { 1347 // only try large/mini icons when there is no vector icon 1348 if (large != NULL 1349 && icon_for_type(mimeType, *large, B_LARGE_ICON) == B_OK) 1350 hasLarge = true; 1351 if (mini != NULL 1352 && icon_for_type(mimeType, *mini, B_MINI_ICON) == B_OK) 1353 hasMini = true; 1354 } 1355 } 1356 1357 if (data != NULL) { 1358 _SetIcon(NULL, NULL, data, size); 1359 free(data); 1360 } else if (hasLarge || hasMini) 1361 _SetIcon(large, mini, NULL, 0); 1362 1363 delete large; 1364 delete mini; 1365 } 1366 1367 1368 void 1369 IconView::_RemoveIcon() 1370 { 1371 _SetIcon(NULL, NULL, NULL, 0, true); 1372 } 1373 1374 1375 void 1376 IconView::_StartWatching() 1377 { 1378 if (Looper() == NULL) { 1379 // we are not a valid messenger yet 1380 return; 1381 } 1382 1383 if (fHasRef) { 1384 BNode node(&fRef); 1385 node_ref nodeRef; 1386 if (node.InitCheck() == B_OK 1387 && node.GetNodeRef(&nodeRef) == B_OK) 1388 watch_node(&nodeRef, B_WATCH_ATTR, this); 1389 } else if (fHasType) 1390 BMimeType::StartWatching(this); 1391 } 1392 1393 1394 void 1395 IconView::_StopWatching() 1396 { 1397 if (fHasRef) 1398 stop_watching(this); 1399 else if (fHasType) 1400 BMimeType::StopWatching(this); 1401 } 1402 1403 1404 #if __GNUC__ == 2 1405 1406 status_t 1407 IconView::SetTarget(BMessenger target) 1408 { 1409 return BControl::SetTarget(target); 1410 } 1411 1412 1413 status_t 1414 IconView::SetTarget(const BHandler* handler, const BLooper* looper = NULL) 1415 { 1416 return BControl::SetTarget(handler, 1417 looper); 1418 } 1419 1420 #endif 1421 1422