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 <IconEditorProtocol.h> 19 #include <IconUtils.h> 20 #include <Locale.h> 21 #include <MenuItem.h> 22 #include <Mime.h> 23 #include <NodeMonitor.h> 24 #include <PopUpMenu.h> 25 #include <Resources.h> 26 #include <Roster.h> 27 #include <Size.h> 28 29 #include "FileTypes.h" 30 #include "MimeTypeListView.h" 31 32 33 #undef B_TRANSLATION_CONTEXT 34 #define B_TRANSLATION_CONTEXT "Icon View" 35 36 37 using namespace std; 38 39 40 status_t 41 icon_for_type(const BMimeType& type, uint8** _data, size_t* _size, 42 icon_source* _source) 43 { 44 if (_data == NULL || _size == NULL) 45 return B_BAD_VALUE; 46 47 icon_source source = kNoIcon; 48 uint8* data; 49 size_t size; 50 51 if (type.GetIcon(&data, &size) == B_OK) 52 source = kOwnIcon; 53 54 if (source == kNoIcon) { 55 // check for icon from preferred app 56 57 char preferred[B_MIME_TYPE_LENGTH]; 58 if (type.GetPreferredApp(preferred) == B_OK) { 59 BMimeType preferredApp(preferred); 60 61 if (preferredApp.GetIconForType(type.Type(), &data, &size) == B_OK) 62 source = kApplicationIcon; 63 } 64 } 65 66 if (source == kNoIcon) { 67 // check super type for an icon 68 69 BMimeType superType; 70 if (type.GetSupertype(&superType) == B_OK) { 71 if (superType.GetIcon(&data, &size) == B_OK) 72 source = kSupertypeIcon; 73 else { 74 // check the super type's preferred app 75 char preferred[B_MIME_TYPE_LENGTH]; 76 if (superType.GetPreferredApp(preferred) == B_OK) { 77 BMimeType preferredApp(preferred); 78 79 if (preferredApp.GetIconForType(superType.Type(), 80 &data, &size) == B_OK) 81 source = kSupertypeIcon; 82 } 83 } 84 } 85 } 86 87 if (source != kNoIcon) { 88 *_data = data; 89 *_size = size; 90 } // NOTE: else there is no data, so nothing is leaked. 91 if (_source) 92 *_source = source; 93 94 return source != kNoIcon ? B_OK : B_ERROR; 95 } 96 97 98 status_t 99 icon_for_type(const BMimeType& type, BBitmap& bitmap, icon_size size, 100 icon_source* _source) 101 { 102 icon_source source = kNoIcon; 103 104 if (type.GetIcon(&bitmap, size) == B_OK) 105 source = kOwnIcon; 106 107 if (source == kNoIcon) { 108 // check for icon from preferred app 109 110 char preferred[B_MIME_TYPE_LENGTH]; 111 if (type.GetPreferredApp(preferred) == B_OK) { 112 BMimeType preferredApp(preferred); 113 114 if (preferredApp.GetIconForType(type.Type(), &bitmap, size) == B_OK) 115 source = kApplicationIcon; 116 } 117 } 118 119 if (source == kNoIcon) { 120 // check super type for an icon 121 122 BMimeType superType; 123 if (type.GetSupertype(&superType) == B_OK) { 124 if (superType.GetIcon(&bitmap, size) == B_OK) 125 source = kSupertypeIcon; 126 else { 127 // check the super type's preferred app 128 char preferred[B_MIME_TYPE_LENGTH]; 129 if (superType.GetPreferredApp(preferred) == B_OK) { 130 BMimeType preferredApp(preferred); 131 132 if (preferredApp.GetIconForType(superType.Type(), 133 &bitmap, size) == B_OK) 134 source = kSupertypeIcon; 135 } 136 } 137 } 138 } 139 140 if (_source) 141 *_source = source; 142 143 return source != kNoIcon ? B_OK : B_ERROR; 144 } 145 146 147 // #pragma mark - 148 149 150 Icon::Icon() 151 : 152 fLarge(NULL), 153 fMini(NULL), 154 fData(NULL), 155 fSize(0) 156 { 157 } 158 159 160 Icon::Icon(const Icon& source) 161 : 162 fLarge(NULL), 163 fMini(NULL), 164 fData(NULL), 165 fSize(0) 166 { 167 *this = source; 168 } 169 170 171 Icon::~Icon() 172 { 173 delete fLarge; 174 delete fMini; 175 free(fData); 176 } 177 178 179 void 180 Icon::SetTo(const BAppFileInfo& info, const char* type) 181 { 182 Unset(); 183 184 uint8* data; 185 size_t size; 186 187 if (info.GetIconForType(type, &data, &size) == B_OK) { 188 // we have the vector icon, no need to get the rest 189 AdoptData(data, size); 190 return; 191 } 192 193 BBitmap* icon = AllocateBitmap(B_LARGE_ICON, B_CMAP8); 194 if (icon && info.GetIconForType(type, icon, B_LARGE_ICON) == B_OK) 195 AdoptLarge(icon); 196 else 197 delete icon; 198 199 icon = AllocateBitmap(B_MINI_ICON, B_CMAP8); 200 if (icon && info.GetIconForType(type, icon, B_MINI_ICON) == B_OK) 201 AdoptMini(icon); 202 else 203 delete icon; 204 } 205 206 207 void 208 Icon::SetTo(const entry_ref& ref, const char* type) 209 { 210 Unset(); 211 212 BFile file(&ref, B_READ_ONLY); 213 BAppFileInfo info(&file); 214 if (file.InitCheck() == B_OK 215 && info.InitCheck() == B_OK) 216 SetTo(info, type); 217 } 218 219 220 void 221 Icon::SetTo(const BMimeType& type, icon_source* _source) 222 { 223 Unset(); 224 225 uint8* data; 226 size_t size; 227 if (icon_for_type(type, &data, &size, _source) == B_OK) { 228 // we have the vector icon, no need to get the rest 229 AdoptData(data, size); 230 return; 231 } 232 233 BBitmap* icon = AllocateBitmap(B_LARGE_ICON, B_CMAP8); 234 if (icon && icon_for_type(type, *icon, B_LARGE_ICON, _source) == B_OK) 235 AdoptLarge(icon); 236 else 237 delete icon; 238 239 icon = AllocateBitmap(B_MINI_ICON, B_CMAP8); 240 if (icon && icon_for_type(type, *icon, B_MINI_ICON) == B_OK) 241 AdoptMini(icon); 242 else 243 delete icon; 244 } 245 246 247 status_t 248 Icon::CopyTo(BAppFileInfo& info, const char* type, bool force) const 249 { 250 status_t status = B_OK; 251 252 if (fLarge != NULL || force) 253 status = info.SetIconForType(type, fLarge, B_LARGE_ICON); 254 if (fMini != NULL || force) 255 status = info.SetIconForType(type, fMini, B_MINI_ICON); 256 if (fData != NULL || force) 257 status = info.SetIconForType(type, fData, fSize); 258 259 return status; 260 } 261 262 263 status_t 264 Icon::CopyTo(const entry_ref& ref, const char* type, bool force) const 265 { 266 BFile file; 267 status_t status = file.SetTo(&ref, B_READ_ONLY); 268 if (status < B_OK) 269 return status; 270 271 BAppFileInfo info(&file); 272 status = info.InitCheck(); 273 if (status < B_OK) 274 return status; 275 276 return CopyTo(info, type, force); 277 } 278 279 280 status_t 281 Icon::CopyTo(BMimeType& type, bool force) const 282 { 283 status_t status = B_OK; 284 285 if (fLarge != NULL || force) 286 status = type.SetIcon(fLarge, B_LARGE_ICON); 287 if (fMini != NULL || force) 288 status = type.SetIcon(fMini, B_MINI_ICON); 289 if (fData != NULL || force) 290 status = type.SetIcon(fData, fSize); 291 292 return status; 293 } 294 295 296 status_t 297 Icon::CopyTo(BMessage& message) const 298 { 299 status_t status = B_OK; 300 301 if (status == B_OK && fLarge != NULL) { 302 BMessage archive; 303 status = fLarge->Archive(&archive); 304 if (status == B_OK) 305 status = message.AddMessage("icon/large", &archive); 306 } 307 if (status == B_OK && fMini != NULL) { 308 BMessage archive; 309 status = fMini->Archive(&archive); 310 if (status == B_OK) 311 status = message.AddMessage("icon/mini", &archive); 312 } 313 if (status == B_OK && fData != NULL) 314 status = message.AddData("icon", B_VECTOR_ICON_TYPE, fData, fSize); 315 316 return B_OK; 317 } 318 319 320 void 321 Icon::SetLarge(const BBitmap* large) 322 { 323 if (large != NULL) { 324 if (fLarge == NULL) 325 fLarge = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8); 326 327 memcpy(fLarge->Bits(), large->Bits(), min_c(large->BitsLength(), 328 fLarge->BitsLength())); 329 } else { 330 delete fLarge; 331 fLarge = NULL; 332 } 333 } 334 335 336 void 337 Icon::SetMini(const BBitmap* mini) 338 { 339 if (mini != NULL) { 340 if (fMini == NULL) 341 fMini = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8); 342 343 memcpy(fMini->Bits(), mini->Bits(), min_c(mini->BitsLength(), 344 fMini->BitsLength())); 345 } else { 346 delete fMini; 347 fMini = NULL; 348 } 349 } 350 351 352 void 353 Icon::SetData(const uint8* data, size_t size) 354 { 355 free(fData); 356 fData = NULL; 357 358 if (data != NULL) { 359 fData = (uint8*)malloc(size); 360 if (fData != NULL) { 361 fSize = size; 362 //fType = B_VECTOR_ICON_TYPE; 363 memcpy(fData, data, size); 364 } 365 } 366 } 367 368 369 void 370 Icon::Unset() 371 { 372 delete fLarge; 373 delete fMini; 374 free(fData); 375 376 fLarge = fMini = NULL; 377 fData = NULL; 378 } 379 380 381 bool 382 Icon::HasData() const 383 { 384 return fData != NULL || fLarge != NULL || fMini != NULL; 385 } 386 387 388 status_t 389 Icon::GetData(icon_size which, BBitmap** _bitmap) const 390 { 391 BBitmap* source; 392 switch (which) { 393 case B_LARGE_ICON: 394 source = fLarge; 395 break; 396 case B_MINI_ICON: 397 source = fMini; 398 break; 399 default: 400 return B_BAD_VALUE; 401 } 402 403 if (source == NULL) 404 return B_ENTRY_NOT_FOUND; 405 406 BBitmap* bitmap = new (nothrow) BBitmap(source); 407 if (bitmap == NULL || bitmap->InitCheck() != B_OK) { 408 delete bitmap; 409 return B_NO_MEMORY; 410 } 411 412 *_bitmap = bitmap; 413 return B_OK; 414 } 415 416 417 status_t 418 Icon::GetData(uint8** _data, size_t* _size) const 419 { 420 if (fData == NULL) 421 return B_ENTRY_NOT_FOUND; 422 423 uint8* data = (uint8*)malloc(fSize); 424 if (data == NULL) 425 return B_NO_MEMORY; 426 427 memcpy(data, fData, fSize); 428 *_data = data; 429 *_size = fSize; 430 return B_OK; 431 } 432 433 434 status_t 435 Icon::GetIcon(BBitmap* bitmap) const 436 { 437 if (bitmap == NULL) 438 return B_BAD_VALUE; 439 440 if (fData != NULL 441 && BIconUtils::GetVectorIcon(fData, fSize, bitmap) == B_OK) 442 return B_OK; 443 444 int32 width = bitmap->Bounds().IntegerWidth() + 1; 445 446 if (width == B_LARGE_ICON && fLarge != NULL) { 447 bitmap->SetBits(fLarge->Bits(), fLarge->BitsLength(), 0, 448 fLarge->ColorSpace()); 449 return B_OK; 450 } 451 if (width == B_MINI_ICON && fMini != NULL) { 452 bitmap->SetBits(fMini->Bits(), fMini->BitsLength(), 0, 453 fMini->ColorSpace()); 454 return B_OK; 455 } 456 457 return B_ENTRY_NOT_FOUND; 458 } 459 460 461 Icon& 462 Icon::operator=(const Icon& source) 463 { 464 Unset(); 465 466 SetData(source.fData, source.fSize); 467 SetLarge(source.fLarge); 468 SetMini(source.fMini); 469 470 return *this; 471 } 472 473 474 void 475 Icon::AdoptLarge(BBitmap *large) 476 { 477 delete fLarge; 478 fLarge = large; 479 } 480 481 482 void 483 Icon::AdoptMini(BBitmap *mini) 484 { 485 delete fMini; 486 fMini = mini; 487 } 488 489 490 void 491 Icon::AdoptData(uint8* data, size_t size) 492 { 493 free(fData); 494 fData = data; 495 fSize = size; 496 } 497 498 499 /*static*/ BBitmap* 500 Icon::AllocateBitmap(int32 size, int32 space) 501 { 502 int32 kSpace = B_RGBA32; 503 if (space == -1) 504 space = kSpace; 505 506 BBitmap* bitmap = new (nothrow) BBitmap(BRect(0, 0, size - 1, size - 1), 507 (color_space)space); 508 if (bitmap == NULL || bitmap->InitCheck() != B_OK) { 509 delete bitmap; 510 return NULL; 511 } 512 513 return bitmap; 514 } 515 516 517 // #pragma mark - 518 519 520 IconView::IconView(const char* name, uint32 flags) 521 : BControl(name, NULL, NULL, B_WILL_DRAW | flags), 522 fModificationMessage(NULL), 523 fIconSize(B_LARGE_ICON), 524 fIcon(NULL), 525 fHeapIcon(NULL), 526 fHasRef(false), 527 fHasType(false), 528 fIconData(NULL), 529 fTracking(false), 530 fDragging(false), 531 fDropTarget(false), 532 fShowEmptyFrame(true) 533 { 534 } 535 536 537 IconView::~IconView() 538 { 539 delete fIcon; 540 delete fModificationMessage; 541 } 542 543 544 void 545 IconView::AttachedToWindow() 546 { 547 if (Parent()) 548 SetViewColor(Parent()->ViewColor()); 549 else 550 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 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 BRect(0, 0, fIconSize - 1, fIconSize - 1); 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 = fIconSize; 768 769 if (_height) 770 *_height = fIconSize; 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)fIconSize) != 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(int32 size) 1065 { 1066 if (size < B_MINI_ICON) 1067 size = B_MINI_ICON; 1068 if (size > 256) 1069 size = 256; 1070 if (size == fIconSize) 1071 return; 1072 1073 fIconSize = size; 1074 Update(); 1075 } 1076 1077 1078 void 1079 IconView::ShowIconHeap(bool show) 1080 { 1081 if (show == (fHeapIcon != NULL)) 1082 return; 1083 1084 if (show) { 1085 BResources* resources = be_app->AppResources(); 1086 if (resources != NULL) { 1087 const void* data = NULL; 1088 size_t size; 1089 data = resources->LoadResource('VICN', "icon heap", &size); 1090 if (data != NULL) { 1091 // got vector icon data 1092 fHeapIcon = Icon::AllocateBitmap(B_LARGE_ICON, B_RGBA32); 1093 if (BIconUtils::GetVectorIcon((const uint8*)data, 1094 size, fHeapIcon) != B_OK) { 1095 // bad data 1096 delete fHeapIcon; 1097 fHeapIcon = NULL; 1098 data = NULL; 1099 } 1100 } 1101 if (data == NULL) { 1102 // no vector icon or failed to get bitmap 1103 // try bitmap icon 1104 data = resources->LoadResource(B_LARGE_ICON_TYPE, "icon heap", 1105 NULL); 1106 if (data != NULL) { 1107 fHeapIcon = Icon::AllocateBitmap(B_LARGE_ICON, B_CMAP8); 1108 if (fHeapIcon != NULL) { 1109 memcpy(fHeapIcon->Bits(), data, 1110 fHeapIcon->BitsLength()); 1111 } 1112 } 1113 } 1114 } 1115 } else { 1116 delete fHeapIcon; 1117 fHeapIcon = NULL; 1118 } 1119 } 1120 1121 1122 void 1123 IconView::ShowEmptyFrame(bool show) 1124 { 1125 if (show == fShowEmptyFrame) 1126 return; 1127 1128 fShowEmptyFrame = show; 1129 if (fIcon == NULL) 1130 Invalidate(); 1131 } 1132 1133 1134 status_t 1135 IconView::SetTarget(const BMessenger& target) 1136 { 1137 fTarget = target; 1138 return B_OK; 1139 } 1140 1141 1142 void 1143 IconView::SetModificationMessage(BMessage* message) 1144 { 1145 delete fModificationMessage; 1146 fModificationMessage = message; 1147 } 1148 1149 1150 status_t 1151 IconView::Invoke(BMessage* message) 1152 { 1153 if (message == NULL) 1154 fTarget.SendMessage(kMsgIconInvoked); 1155 else 1156 fTarget.SendMessage(message); 1157 return B_OK; 1158 } 1159 1160 1161 Icon* 1162 IconView::Icon() 1163 { 1164 return fIconData; 1165 } 1166 1167 1168 status_t 1169 IconView::GetRef(entry_ref& ref) const 1170 { 1171 if (!fHasRef) 1172 return B_BAD_TYPE; 1173 1174 ref = fRef; 1175 return B_OK; 1176 } 1177 1178 1179 status_t 1180 IconView::GetMimeType(BMimeType& type) const 1181 { 1182 if (!fHasType) 1183 return B_BAD_TYPE; 1184 1185 type.SetTo(fType.Type()); 1186 return B_OK; 1187 } 1188 1189 1190 void 1191 IconView::_AddOrEditIcon() 1192 { 1193 BMessage message; 1194 if (fHasRef && fType.Type() == NULL) { 1195 // in ref mode, Icon-O-Matic can change the icon directly, and 1196 // we'll pick it up via node monitoring 1197 message.what = B_REFS_RECEIVED; 1198 message.AddRef("refs", &fRef); 1199 } else { 1200 // in static or MIME type mode, Icon-O-Matic needs to return the 1201 // buffer it changed once its done 1202 message.what = B_EDIT_ICON_DATA; 1203 message.AddMessenger("reply to", BMessenger(this)); 1204 1205 ::Icon* icon = fIconData; 1206 if (icon == NULL) { 1207 icon = new ::Icon(); 1208 if (fHasRef) 1209 icon->SetTo(fRef, fType.Type()); 1210 else 1211 icon->SetTo(fType); 1212 } 1213 1214 if (icon->HasData()) { 1215 uint8* data; 1216 size_t size; 1217 if (icon->GetData(&data, &size) == B_OK) { 1218 message.AddData("icon data", B_VECTOR_ICON_TYPE, data, size); 1219 free(data); 1220 } 1221 1222 // TODO: somehow figure out how names of objects in the icon 1223 // can be preserved. Maybe in a second (optional) attribute 1224 // where ever a vector icon attribute is present? 1225 } 1226 1227 if (icon != fIconData) 1228 delete icon; 1229 } 1230 1231 be_roster->Launch("application/x-vnd.haiku-icon_o_matic", &message); 1232 } 1233 1234 1235 void 1236 IconView::_SetIcon(BBitmap* large, BBitmap* mini, const uint8* data, 1237 size_t size, bool force) 1238 { 1239 if (fHasRef) { 1240 BFile file(&fRef, B_READ_WRITE); 1241 1242 if (is_application(file)) { 1243 BAppFileInfo info(&file); 1244 if (info.InitCheck() == B_OK) { 1245 if (large != NULL || force) 1246 info.SetIconForType(fType.Type(), large, B_LARGE_ICON); 1247 if (mini != NULL || force) 1248 info.SetIconForType(fType.Type(), mini, B_MINI_ICON); 1249 if (data != NULL || force) 1250 info.SetIconForType(fType.Type(), data, size); 1251 } 1252 } else { 1253 BNodeInfo info(&file); 1254 if (info.InitCheck() == B_OK) { 1255 if (large != NULL || force) 1256 info.SetIcon(large, B_LARGE_ICON); 1257 if (mini != NULL || force) 1258 info.SetIcon(mini, B_MINI_ICON); 1259 if (data != NULL || force) 1260 info.SetIcon(data, size); 1261 } 1262 } 1263 // the icon shown will be updated using node monitoring 1264 } else if (fHasType) { 1265 if (large != NULL || force) 1266 fType.SetIcon(large, B_LARGE_ICON); 1267 if (mini != NULL || force) 1268 fType.SetIcon(mini, B_MINI_ICON); 1269 if (data != NULL || force) 1270 fType.SetIcon(data, size); 1271 1272 // the icon shown will be updated automatically - we're watching 1273 // any changes to the MIME database 1274 } else if (fIconData != NULL) { 1275 if (large != NULL || force) 1276 fIconData->SetLarge(large); 1277 if (mini != NULL || force) 1278 fIconData->SetMini(mini); 1279 if (data != NULL || force) 1280 fIconData->SetData(data, size); 1281 1282 // replace visible icon 1283 if (fIcon == NULL && fIconData->HasData()) 1284 fIcon = Icon::AllocateBitmap(fIconSize); 1285 1286 if (fIconData->GetIcon(fIcon) != B_OK) { 1287 delete fIcon; 1288 fIcon = NULL; 1289 } 1290 Invalidate(); 1291 } 1292 1293 if (fModificationMessage) 1294 Invoke(fModificationMessage); 1295 } 1296 1297 1298 void 1299 IconView::_SetIcon(entry_ref* ref) 1300 { 1301 // retrieve icons from file 1302 BFile file(ref, B_READ_ONLY); 1303 BAppFileInfo info(&file); 1304 if (file.InitCheck() != B_OK || info.InitCheck() != B_OK) 1305 return; 1306 1307 // try vector/PNG icon first 1308 uint8* data = NULL; 1309 size_t size = 0; 1310 if (info.GetIcon(&data, &size) == B_OK) { 1311 _SetIcon(NULL, NULL, data, size); 1312 free(data); 1313 return; 1314 } 1315 1316 // try large/mini icons 1317 bool hasMini = false; 1318 bool hasLarge = false; 1319 1320 BBitmap* large = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8); 1321 if (large->InitCheck() != B_OK) { 1322 delete large; 1323 large = NULL; 1324 } 1325 BBitmap* mini = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8); 1326 if (mini->InitCheck() != B_OK) { 1327 delete mini; 1328 mini = NULL; 1329 } 1330 1331 if (large != NULL && info.GetIcon(large, B_LARGE_ICON) == B_OK) 1332 hasLarge = true; 1333 if (mini != NULL && info.GetIcon(mini, B_MINI_ICON) == B_OK) 1334 hasMini = true; 1335 1336 if (!hasMini && !hasLarge) { 1337 // TODO: don't forget device icons! 1338 1339 // try MIME type icon 1340 char type[B_MIME_TYPE_LENGTH]; 1341 if (info.GetType(type) != B_OK) 1342 return; 1343 1344 BMimeType mimeType(type); 1345 if (icon_for_type(mimeType, &data, &size) != B_OK) { 1346 // only try large/mini icons when there is no vector icon 1347 if (large != NULL 1348 && icon_for_type(mimeType, *large, B_LARGE_ICON) == B_OK) 1349 hasLarge = true; 1350 if (mini != NULL 1351 && icon_for_type(mimeType, *mini, B_MINI_ICON) == B_OK) 1352 hasMini = true; 1353 } 1354 } 1355 1356 if (data != NULL) { 1357 _SetIcon(NULL, NULL, data, size); 1358 free(data); 1359 } else if (hasLarge || hasMini) 1360 _SetIcon(large, mini, NULL, 0); 1361 1362 delete large; 1363 delete mini; 1364 } 1365 1366 1367 void 1368 IconView::_RemoveIcon() 1369 { 1370 _SetIcon(NULL, NULL, NULL, 0, true); 1371 } 1372 1373 1374 void 1375 IconView::_StartWatching() 1376 { 1377 if (Looper() == NULL) { 1378 // we are not a valid messenger yet 1379 return; 1380 } 1381 1382 if (fHasRef) { 1383 BNode node(&fRef); 1384 node_ref nodeRef; 1385 if (node.InitCheck() == B_OK 1386 && node.GetNodeRef(&nodeRef) == B_OK) 1387 watch_node(&nodeRef, B_WATCH_ATTR, this); 1388 } else if (fHasType) 1389 BMimeType::StartWatching(this); 1390 } 1391 1392 1393 void 1394 IconView::_StopWatching() 1395 { 1396 if (fHasRef) 1397 stop_watching(this); 1398 else if (fHasType) 1399 BMimeType::StopWatching(this); 1400 } 1401 1402 1403 #if __GNUC__ == 2 1404 1405 status_t 1406 IconView::SetTarget(BMessenger target) 1407 { 1408 return BControl::SetTarget(target); 1409 } 1410 1411 1412 status_t 1413 IconView::SetTarget(const BHandler* handler, const BLooper* looper = NULL) 1414 { 1415 return BControl::SetTarget(handler, 1416 looper); 1417 } 1418 1419 #endif 1420 1421