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