1 /* 2 * Copyright 2006-2024, 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 && 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 && 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 BBitmap* source = (width > B_LARGE_ICON && fLarge != NULL) || fMini == NULL 458 ? fLarge : fMini; 459 if (source == NULL) 460 return B_ENTRY_NOT_FOUND; 461 462 // Resize bitmap to fit the target 463 BBitmap* target = new (nothrow) BBitmap(bitmap->Bounds(), 464 B_BITMAP_ACCEPTS_VIEWS, bitmap->ColorSpace()); 465 if (target != NULL && target->InitCheck() == B_OK && target->Lock()) { 466 BView* view = new BView(bitmap->Bounds(), NULL, B_FOLLOW_NONE, 467 B_WILL_DRAW); 468 target->AddChild(view); 469 view->DrawBitmap(source, bitmap->Bounds()); 470 view->Flush(); 471 target->RemoveChild(view); 472 target->Unlock(); 473 474 // Copy target to original bitmap 475 bitmap->SetBits(target->Bits(), target->BitsLength(), 0, 476 target->ColorSpace()); 477 478 delete view; 479 } 480 delete target; 481 482 return B_OK; 483 } 484 485 486 Icon& 487 Icon::operator=(const Icon& source) 488 { 489 Unset(); 490 491 SetData(source.fData, source.fSize); 492 SetLarge(source.fLarge); 493 SetMini(source.fMini); 494 495 return *this; 496 } 497 498 499 void 500 Icon::AdoptLarge(BBitmap *large) 501 { 502 delete fLarge; 503 fLarge = large; 504 } 505 506 507 void 508 Icon::AdoptMini(BBitmap *mini) 509 { 510 delete fMini; 511 fMini = mini; 512 } 513 514 515 void 516 Icon::AdoptData(uint8* data, size_t size) 517 { 518 free(fData); 519 fData = data; 520 fSize = size; 521 } 522 523 524 /*static*/ BBitmap* 525 Icon::AllocateBitmap(icon_size size, int32 space) 526 { 527 int32 kSpace = B_RGBA32; 528 if (space == -1) 529 space = kSpace; 530 531 BBitmap* bitmap; 532 if (space == B_CMAP8) { 533 // Legacy mode; no scaling 534 bitmap = new (nothrow) BBitmap(BRect(0, 0, (int32)size - 1, (int32)size - 1), B_CMAP8); 535 } else { 536 bitmap = new (nothrow) BBitmap(BRect(BPoint(0, 0), 537 be_control_look->ComposeIconSize(size)), (color_space)space); 538 } 539 if (bitmap == NULL || bitmap->InitCheck() != B_OK) { 540 delete bitmap; 541 return NULL; 542 } 543 544 return bitmap; 545 } 546 547 548 // #pragma mark - 549 550 551 IconView::IconView(const char* name, uint32 flags) 552 : 553 BControl(name, NULL, NULL, B_WILL_DRAW | flags), 554 fModificationMessage(NULL), 555 fIconSize((icon_size)0), 556 fIconBitmap(NULL), 557 fHeapIconBitmap(NULL), 558 fHasRef(false), 559 fHasType(false), 560 fIcon(NULL), 561 fTracking(false), 562 fDragging(false), 563 fDropTarget(false), 564 fShowEmptyFrame(true) 565 { 566 SetIconSize(B_LARGE_ICON); 567 } 568 569 570 IconView::~IconView() 571 { 572 delete fIconBitmap; 573 delete fModificationMessage; 574 } 575 576 577 void 578 IconView::AttachedToWindow() 579 { 580 AdoptParentColors(); 581 582 fTarget = this; 583 584 // SetTo() was already called before we were a valid messenger 585 if (fHasRef || fHasType) 586 _StartWatching(); 587 } 588 589 590 void 591 IconView::DetachedFromWindow() 592 { 593 _StopWatching(); 594 } 595 596 597 void 598 IconView::MessageReceived(BMessage* message) 599 { 600 if (message->WasDropped() && message->ReturnAddress() != BMessenger(this) 601 && AcceptsDrag(message)) { 602 // set icon from message 603 BBitmap* mini = NULL; 604 BBitmap* large = NULL; 605 const uint8* data = NULL; 606 ssize_t size = 0; 607 608 message->FindData("icon", B_VECTOR_ICON_TYPE, (const void**)&data, 609 &size); 610 611 BMessage archive; 612 if (message->FindMessage("icon/large", &archive) == B_OK) 613 large = (BBitmap*)BBitmap::Instantiate(&archive); 614 if (message->FindMessage("icon/mini", &archive) == B_OK) 615 mini = (BBitmap*)BBitmap::Instantiate(&archive); 616 617 if (large != NULL || mini != NULL || (data != NULL && size > 0)) 618 _SetIcon(large, mini, data, size); 619 else { 620 entry_ref ref; 621 if (message->FindRef("refs", &ref) == B_OK) 622 _SetIcon(&ref); 623 } 624 625 delete large; 626 delete mini; 627 628 return; 629 } 630 631 switch (message->what) { 632 case kMsgIconInvoked: 633 case kMsgEditIcon: 634 case kMsgAddIcon: 635 _AddOrEditIcon(); 636 break; 637 case kMsgRemoveIcon: 638 _RemoveIcon(); 639 break; 640 641 case B_NODE_MONITOR: 642 { 643 if (!fHasRef) 644 break; 645 646 int32 opcode; 647 if (message->FindInt32("opcode", &opcode) != B_OK 648 || opcode != B_ATTR_CHANGED) 649 break; 650 651 const char* name; 652 if (message->FindString("attr", &name) != B_OK) 653 break; 654 655 if (!strcmp(name, kAttrMiniIcon) 656 || !strcmp(name, kAttrLargeIcon) 657 || !strcmp(name, kAttrIcon)) 658 Update(); 659 break; 660 } 661 662 case B_META_MIME_CHANGED: 663 { 664 if (!fHasType) 665 break; 666 667 const char* type; 668 int32 which; 669 if (message->FindString("be:type", &type) != B_OK 670 || message->FindInt32("be:which", &which) != B_OK) 671 break; 672 673 if (!strcasecmp(type, fType.Type())) { 674 switch (which) { 675 case B_MIME_TYPE_DELETED: 676 Unset(); 677 break; 678 679 case B_ICON_CHANGED: 680 Update(); 681 break; 682 683 default: 684 break; 685 } 686 } else if (fSource != kOwnIcon 687 && message->FindString("be:extra_type", &type) == B_OK 688 && !strcasecmp(type, fType.Type())) { 689 // this change could still affect our current icon 690 691 if (which == B_MIME_TYPE_DELETED 692 || which == B_PREFERRED_APP_CHANGED 693 || which == B_SUPPORTED_TYPES_CHANGED 694 || which == B_ICON_FOR_TYPE_CHANGED) 695 Update(); 696 } 697 break; 698 } 699 700 case B_ICON_DATA_EDITED: 701 { 702 const uint8* data; 703 ssize_t size; 704 if (message->FindData("icon data", B_VECTOR_ICON_TYPE, 705 (const void**)&data, &size) < B_OK) 706 break; 707 708 _SetIcon(NULL, NULL, data, size); 709 break; 710 } 711 712 default: 713 BControl::MessageReceived(message); 714 break; 715 } 716 } 717 718 719 bool 720 IconView::AcceptsDrag(const BMessage* message) 721 { 722 if (!IsEnabled()) 723 return false; 724 725 type_code type; 726 int32 count; 727 if (message->GetInfo("refs", &type, &count) == B_OK && count == 1 728 && type == B_REF_TYPE) { 729 // if we're bound to an entry, check that no one drops this to us 730 entry_ref ref; 731 if (fHasRef && message->FindRef("refs", &ref) == B_OK && fRef == ref) 732 return false; 733 734 return true; 735 } 736 737 if ((message->GetInfo("icon/large", &type) == B_OK 738 && type == B_MESSAGE_TYPE) 739 || (message->GetInfo("icon", &type) == B_OK 740 && type == B_VECTOR_ICON_TYPE) 741 || (message->GetInfo("icon/mini", &type) == B_OK 742 && type == B_MESSAGE_TYPE)) 743 return true; 744 745 return false; 746 } 747 748 749 BRect 750 IconView::BitmapRect() const 751 { 752 return fIconRect; 753 } 754 755 756 void 757 IconView::Draw(BRect updateRect) 758 { 759 SetDrawingMode(B_OP_ALPHA); 760 761 if (fHeapIconBitmap != NULL) 762 DrawBitmap(fHeapIconBitmap, BitmapRect()); 763 else if (fIconBitmap != NULL) 764 DrawBitmap(fIconBitmap, BitmapRect()); 765 else if (!fDropTarget && fShowEmptyFrame) { 766 // draw frame so that the user knows here is something he 767 // might be able to click on 768 SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); 769 StrokeRect(BitmapRect()); 770 } 771 772 if (IsFocus()) { 773 // mark this view as a having focus 774 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 775 StrokeRect(BitmapRect()); 776 } 777 if (fDropTarget) { 778 // mark this view as a drop target 779 SetHighColor(0, 0, 0); 780 SetPenSize(2); 781 BRect rect = BitmapRect(); 782 // TODO: this is an incompatibility between R5 and Haiku and should be fixed! 783 // (Necessary adjustment differs.) 784 rect.left++; 785 rect.top++; 786 787 StrokeRect(rect); 788 SetPenSize(1); 789 } 790 } 791 792 793 void 794 IconView::GetPreferredSize(float* _width, float* _height) 795 { 796 if (_width) 797 *_width = fIconRect.Width(); 798 799 if (_height) 800 *_height = fIconRect.Height(); 801 } 802 803 804 BSize 805 IconView::MinSize() 806 { 807 float width, height; 808 GetPreferredSize(&width, &height); 809 return BSize(width, height); 810 } 811 812 813 BSize 814 IconView::PreferredSize() 815 { 816 return MinSize(); 817 } 818 819 820 BSize 821 IconView::MaxSize() 822 { 823 return MinSize(); 824 } 825 826 827 void 828 IconView::MouseDown(BPoint where) 829 { 830 if (!IsEnabled()) 831 return; 832 833 int32 buttons = B_PRIMARY_MOUSE_BUTTON; 834 int32 clicks = 1; 835 if (Looper() != NULL && Looper()->CurrentMessage() != NULL) { 836 if (Looper()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK) 837 buttons = B_PRIMARY_MOUSE_BUTTON; 838 if (Looper()->CurrentMessage()->FindInt32("clicks", &clicks) != B_OK) 839 clicks = 1; 840 } 841 842 if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0 843 && BitmapRect().Contains(where)) { 844 if (clicks == 2) { 845 // double click - open Icon-O-Matic 846 Invoke(); 847 } else if (fIconBitmap != NULL) { 848 // start tracking - this icon might be dragged around 849 fDragPoint = where; 850 fTracking = true; 851 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 852 } 853 } 854 855 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) { 856 // show context menu 857 858 ConvertToScreen(&where); 859 860 BPopUpMenu* menu = new BPopUpMenu("context"); 861 menu->SetFont(be_plain_font); 862 863 bool hasIcon = fHasType ? fSource == kOwnIcon : fIconBitmap != NULL; 864 if (hasIcon) { 865 menu->AddItem(new BMenuItem( 866 B_TRANSLATE("Edit icon" B_UTF8_ELLIPSIS), 867 new BMessage(kMsgEditIcon))); 868 } else { 869 menu->AddItem(new BMenuItem( 870 B_TRANSLATE("Add icon" B_UTF8_ELLIPSIS), 871 new BMessage(kMsgAddIcon))); 872 } 873 874 BMenuItem* item = new BMenuItem( 875 B_TRANSLATE("Remove icon"), new BMessage(kMsgRemoveIcon)); 876 if (!hasIcon) 877 item->SetEnabled(false); 878 879 menu->AddItem(item); 880 menu->SetTargetForItems(fTarget); 881 882 menu->Go(where, true, false, true); 883 } 884 } 885 886 887 void 888 IconView::MouseUp(BPoint where) 889 { 890 fTracking = false; 891 fDragging = false; 892 893 if (fDropTarget) { 894 fDropTarget = false; 895 Invalidate(); 896 } 897 } 898 899 900 void 901 IconView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) 902 { 903 if (fTracking && !fDragging && fIconBitmap != NULL 904 && (abs((int32)(where.x - fDragPoint.x)) > 3 905 || abs((int32)(where.y - fDragPoint.y)) > 3)) { 906 // Start drag 907 BMessage message(B_SIMPLE_DATA); 908 909 ::Icon* icon = fIcon; 910 if (fHasRef || fHasType) { 911 icon = new ::Icon; 912 if (fHasRef) 913 icon->SetTo(fRef, fType.Type()); 914 else if (fHasType) 915 icon->SetTo(fType); 916 } 917 918 icon->CopyTo(message); 919 920 if (icon != fIcon) 921 delete icon; 922 923 BBitmap *dragBitmap = new BBitmap(fIconBitmap->Bounds(), B_RGBA32, true); 924 dragBitmap->Lock(); 925 BView *view 926 = new BView(dragBitmap->Bounds(), B_EMPTY_STRING, B_FOLLOW_NONE, 0); 927 dragBitmap->AddChild(view); 928 929 view->SetHighColor(B_TRANSPARENT_COLOR); 930 view->FillRect(dragBitmap->Bounds()); 931 view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); 932 view->SetDrawingMode(B_OP_ALPHA); 933 view->SetHighColor(0, 0, 0, 160); 934 view->DrawBitmap(fIconBitmap); 935 936 view->Sync(); 937 dragBitmap->Unlock(); 938 939 DragMessage(&message, dragBitmap, B_OP_ALPHA, 940 fDragPoint - BitmapRect().LeftTop(), this); 941 fDragging = true; 942 } 943 944 if (dragMessage != NULL && !fDragging && AcceptsDrag(dragMessage)) { 945 bool dropTarget = transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW; 946 if (dropTarget != fDropTarget) { 947 fDropTarget = dropTarget; 948 Invalidate(); 949 } 950 } else if (fDropTarget) { 951 fDropTarget = false; 952 Invalidate(); 953 } 954 } 955 956 957 void 958 IconView::KeyDown(const char* bytes, int32 numBytes) 959 { 960 if (numBytes == 1) { 961 switch (bytes[0]) { 962 case B_DELETE: 963 case B_BACKSPACE: 964 _RemoveIcon(); 965 return; 966 case B_ENTER: 967 case B_SPACE: 968 Invoke(); 969 return; 970 } 971 } 972 973 BControl::KeyDown(bytes, numBytes); 974 } 975 976 977 void 978 IconView::MakeFocus(bool focus) 979 { 980 if (focus != IsFocus()) 981 Invalidate(); 982 983 BControl::MakeFocus(focus); 984 } 985 986 987 void 988 IconView::SetTo(const entry_ref& ref, const char* fileType) 989 { 990 Unset(); 991 992 fHasRef = true; 993 fRef = ref; 994 if (fileType != NULL) 995 fType.SetTo(fileType); 996 else 997 fType.Unset(); 998 999 _StartWatching(); 1000 Update(); 1001 } 1002 1003 1004 void 1005 IconView::SetTo(const BMimeType& type) 1006 { 1007 Unset(); 1008 1009 if (type.Type() == NULL) 1010 return; 1011 1012 fHasType = true; 1013 fType.SetTo(type.Type()); 1014 1015 _StartWatching(); 1016 Update(); 1017 } 1018 1019 1020 void 1021 IconView::SetTo(::Icon* icon) 1022 { 1023 if (fIcon == icon) 1024 return; 1025 1026 Unset(); 1027 1028 fIcon = icon; 1029 1030 Update(); 1031 } 1032 1033 1034 void 1035 IconView::Unset() 1036 { 1037 if (fHasRef || fHasType) 1038 _StopWatching(); 1039 1040 fHasRef = false; 1041 fHasType = false; 1042 1043 fType.Unset(); 1044 fIcon = NULL; 1045 } 1046 1047 1048 void 1049 IconView::Update() 1050 { 1051 delete fIconBitmap; 1052 fIconBitmap = NULL; 1053 1054 Invalidate(); 1055 // this will actually trigger a redraw *after* we updated the icon below 1056 1057 BBitmap* bitmap = NULL; 1058 1059 if (fHasRef) { 1060 BFile file(&fRef, B_READ_ONLY); 1061 if (file.InitCheck() != B_OK) 1062 return; 1063 1064 BNodeInfo info; 1065 if (info.SetTo(&file) != B_OK) 1066 return; 1067 1068 bitmap = Icon::AllocateBitmap(fIconSize); 1069 if (bitmap != NULL && info.GetTrackerIcon(bitmap, 1070 (icon_size)(bitmap->Bounds().IntegerWidth() + 1)) != B_OK) { 1071 delete bitmap; 1072 return; 1073 } 1074 } else if (fHasType) { 1075 bitmap = Icon::AllocateBitmap(fIconSize); 1076 if (bitmap != NULL && icon_for_type(fType, *bitmap, (icon_size)fIconSize, 1077 &fSource) != B_OK) { 1078 delete bitmap; 1079 return; 1080 } 1081 } else if (fIcon != NULL) { 1082 bitmap = Icon::AllocateBitmap(fIconSize); 1083 if (fIcon->GetIcon(bitmap) != B_OK) { 1084 delete bitmap; 1085 bitmap = NULL; 1086 } 1087 } 1088 1089 fIconBitmap = bitmap; 1090 } 1091 1092 1093 void 1094 IconView::SetIconSize(icon_size size) 1095 { 1096 if (size < B_MINI_ICON) 1097 size = B_MINI_ICON; 1098 if (size > 256) 1099 size = (icon_size)256; 1100 if (size == fIconSize) 1101 return; 1102 1103 fIconSize = size; 1104 fIconRect = BRect(BPoint(0, 0), be_control_look->ComposeIconSize(fIconSize)); 1105 Update(); 1106 } 1107 1108 1109 void 1110 IconView::ShowIconHeap(bool show) 1111 { 1112 if (show == (fHeapIconBitmap != NULL)) 1113 return; 1114 1115 if (show) { 1116 BResources* resources = be_app->AppResources(); 1117 if (resources != NULL) { 1118 const void* data = NULL; 1119 size_t size; 1120 data = resources->LoadResource('VICN', "icon heap", &size); 1121 if (data != NULL) { 1122 // got vector icon data 1123 fHeapIconBitmap = Icon::AllocateBitmap(B_LARGE_ICON, B_RGBA32); 1124 if (BIconUtils::GetVectorIcon((const uint8*)data, 1125 size, fHeapIconBitmap) != B_OK) { 1126 // bad data 1127 delete fHeapIconBitmap; 1128 fHeapIconBitmap = NULL; 1129 data = NULL; 1130 } 1131 } 1132 if (data == NULL) { 1133 // no vector icon or failed to get bitmap 1134 // try bitmap icon 1135 data = resources->LoadResource(B_LARGE_ICON_TYPE, "icon heap", 1136 NULL); 1137 if (data != NULL) { 1138 fHeapIconBitmap = Icon::AllocateBitmap(B_LARGE_ICON, B_CMAP8); 1139 if (fHeapIconBitmap != NULL) { 1140 memcpy(fHeapIconBitmap->Bits(), data, 1141 fHeapIconBitmap->BitsLength()); 1142 } 1143 } 1144 } 1145 } 1146 } else { 1147 delete fHeapIconBitmap; 1148 fHeapIconBitmap = NULL; 1149 } 1150 } 1151 1152 1153 void 1154 IconView::ShowEmptyFrame(bool show) 1155 { 1156 if (show == fShowEmptyFrame) 1157 return; 1158 1159 fShowEmptyFrame = show; 1160 if (fIconBitmap == NULL) 1161 Invalidate(); 1162 } 1163 1164 1165 status_t 1166 IconView::SetTarget(const BMessenger& target) 1167 { 1168 fTarget = target; 1169 return B_OK; 1170 } 1171 1172 1173 void 1174 IconView::SetModificationMessage(BMessage* message) 1175 { 1176 delete fModificationMessage; 1177 fModificationMessage = message; 1178 } 1179 1180 1181 status_t 1182 IconView::Invoke(BMessage* message) 1183 { 1184 if (message == NULL) 1185 fTarget.SendMessage(kMsgIconInvoked); 1186 else 1187 fTarget.SendMessage(message); 1188 return B_OK; 1189 } 1190 1191 1192 Icon* 1193 IconView::Icon() 1194 { 1195 return fIcon; 1196 } 1197 1198 1199 status_t 1200 IconView::GetRef(entry_ref& ref) const 1201 { 1202 if (!fHasRef) 1203 return B_BAD_TYPE; 1204 1205 ref = fRef; 1206 return B_OK; 1207 } 1208 1209 1210 status_t 1211 IconView::GetMimeType(BMimeType& type) const 1212 { 1213 if (!fHasType) 1214 return B_BAD_TYPE; 1215 1216 type.SetTo(fType.Type()); 1217 return B_OK; 1218 } 1219 1220 1221 void 1222 IconView::_AddOrEditIcon() 1223 { 1224 BMessage message; 1225 if (fHasRef && fType.Type() == NULL) { 1226 // in ref mode, Icon-O-Matic can change the icon directly, and 1227 // we'll pick it up via node monitoring 1228 message.what = B_REFS_RECEIVED; 1229 message.AddRef("refs", &fRef); 1230 } else { 1231 // in static or MIME type mode, Icon-O-Matic needs to return the 1232 // buffer it changed once its done 1233 message.what = B_EDIT_ICON_DATA; 1234 message.AddMessenger("reply to", BMessenger(this)); 1235 1236 ::Icon* icon = fIcon; 1237 if (icon == NULL) { 1238 icon = new ::Icon(); 1239 if (fHasRef) 1240 icon->SetTo(fRef, fType.Type()); 1241 else 1242 icon->SetTo(fType); 1243 } 1244 1245 if (icon->HasData()) { 1246 uint8* data; 1247 size_t size; 1248 if (icon->GetData(&data, &size) == B_OK) { 1249 message.AddData("icon data", B_VECTOR_ICON_TYPE, data, size); 1250 free(data); 1251 } 1252 1253 // TODO: somehow figure out how names of objects in the icon 1254 // can be preserved. Maybe in a second (optional) attribute 1255 // where ever a vector icon attribute is present? 1256 } 1257 1258 if (icon != fIcon) 1259 delete icon; 1260 } 1261 1262 be_roster->Launch("application/x-vnd.haiku-icon_o_matic", &message); 1263 } 1264 1265 1266 void 1267 IconView::_SetIcon(BBitmap* large, BBitmap* mini, const uint8* data, 1268 size_t size, bool force) 1269 { 1270 if (fHasRef) { 1271 BNodeInfo node; 1272 BDirectory refdir; 1273 BFile file; 1274 BEntry entry(&fRef, true); 1275 1276 if (entry.IsFile()) { 1277 file.SetTo(&fRef, B_READ_WRITE); 1278 if (is_application(file)) { 1279 BAppFileInfo info(&file); 1280 if (info.InitCheck() == B_OK) { 1281 if (large != NULL || force) 1282 info.SetIconForType(fType.Type(), large, B_LARGE_ICON); 1283 if (mini != NULL || force) 1284 info.SetIconForType(fType.Type(), mini, B_MINI_ICON); 1285 if (data != NULL || force) 1286 info.SetIconForType(fType.Type(), data, size); 1287 } 1288 } else 1289 node.SetTo(&file); 1290 } 1291 if (entry.IsDirectory()) { 1292 refdir.SetTo(&fRef); 1293 node.SetTo(&refdir); 1294 } 1295 if (node.InitCheck() == B_OK) { 1296 if (large != NULL || force) 1297 node.SetIcon(large, B_LARGE_ICON); 1298 if (mini != NULL || force) 1299 node.SetIcon(mini, B_MINI_ICON); 1300 if (data != NULL || force) 1301 node.SetIcon(data, size); 1302 } 1303 // the icon shown will be updated using node monitoring 1304 } else if (fHasType) { 1305 if (large != NULL || force) 1306 fType.SetIcon(large, B_LARGE_ICON); 1307 if (mini != NULL || force) 1308 fType.SetIcon(mini, B_MINI_ICON); 1309 if (data != NULL || force) 1310 fType.SetIcon(data, size); 1311 1312 // the icon shown will be updated automatically - we're watching 1313 // any changes to the MIME database 1314 } else if (fIcon != NULL) { 1315 if (large != NULL || force) 1316 fIcon->SetLarge(large); 1317 if (mini != NULL || force) 1318 fIcon->SetMini(mini); 1319 if (data != NULL || force) 1320 fIcon->SetData(data, size); 1321 1322 // replace visible icon 1323 if (fIconBitmap == NULL && fIcon->HasData()) 1324 fIconBitmap = Icon::AllocateBitmap(fIconSize); 1325 1326 if (fIcon->GetIcon(fIconBitmap) != B_OK) { 1327 delete fIconBitmap; 1328 fIconBitmap = NULL; 1329 } 1330 Invalidate(); 1331 } 1332 1333 if (fModificationMessage) 1334 Invoke(fModificationMessage); 1335 } 1336 1337 1338 void 1339 IconView::_SetIcon(entry_ref* ref) 1340 { 1341 // retrieve icons from file 1342 BFile file(ref, B_READ_ONLY); 1343 BAppFileInfo info(&file); 1344 if (file.InitCheck() != B_OK || info.InitCheck() != B_OK) 1345 return; 1346 1347 // try vector/PNG icon first 1348 uint8* data = NULL; 1349 size_t size = 0; 1350 if (info.GetIcon(&data, &size) == B_OK) { 1351 _SetIcon(NULL, NULL, data, size); 1352 free(data); 1353 return; 1354 } 1355 1356 // try large/mini icons 1357 bool hasMini = false; 1358 bool hasLarge = false; 1359 1360 BBitmap* large = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8); 1361 if (large->InitCheck() != B_OK) { 1362 delete large; 1363 large = NULL; 1364 } 1365 BBitmap* mini = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8); 1366 if (mini->InitCheck() != B_OK) { 1367 delete mini; 1368 mini = NULL; 1369 } 1370 1371 if (large != NULL && info.GetIcon(large, B_LARGE_ICON) == B_OK) 1372 hasLarge = true; 1373 if (mini != NULL && info.GetIcon(mini, B_MINI_ICON) == B_OK) 1374 hasMini = true; 1375 1376 if (!hasMini && !hasLarge) { 1377 // TODO: don't forget device icons! 1378 1379 // try MIME type icon 1380 char type[B_MIME_TYPE_LENGTH]; 1381 if (info.GetType(type) != B_OK) 1382 return; 1383 1384 BMimeType mimeType(type); 1385 if (icon_for_type(mimeType, &data, &size) != B_OK) { 1386 // only try large/mini icons when there is no vector icon 1387 if (large != NULL 1388 && icon_for_type(mimeType, *large, B_LARGE_ICON) == B_OK) 1389 hasLarge = true; 1390 if (mini != NULL 1391 && icon_for_type(mimeType, *mini, B_MINI_ICON) == B_OK) 1392 hasMini = true; 1393 } 1394 } 1395 1396 if (data != NULL) { 1397 _SetIcon(NULL, NULL, data, size); 1398 free(data); 1399 } else if (hasLarge || hasMini) 1400 _SetIcon(large, mini, NULL, 0); 1401 1402 delete large; 1403 delete mini; 1404 } 1405 1406 1407 void 1408 IconView::_RemoveIcon() 1409 { 1410 _SetIcon(NULL, NULL, NULL, 0, true); 1411 } 1412 1413 1414 void 1415 IconView::_StartWatching() 1416 { 1417 if (Looper() == NULL) { 1418 // we are not a valid messenger yet 1419 return; 1420 } 1421 1422 if (fHasRef) { 1423 BNode node(&fRef); 1424 node_ref nodeRef; 1425 if (node.InitCheck() == B_OK 1426 && node.GetNodeRef(&nodeRef) == B_OK) 1427 watch_node(&nodeRef, B_WATCH_ATTR, this); 1428 } else if (fHasType) 1429 BMimeType::StartWatching(this); 1430 } 1431 1432 1433 void 1434 IconView::_StopWatching() 1435 { 1436 if (fHasRef) 1437 stop_watching(this); 1438 else if (fHasType) 1439 BMimeType::StopWatching(this); 1440 } 1441 1442 1443 #if __GNUC__ == 2 1444 1445 status_t 1446 IconView::SetTarget(BMessenger target) 1447 { 1448 return BControl::SetTarget(target); 1449 } 1450 1451 1452 status_t 1453 IconView::SetTarget(const BHandler* handler, const BLooper* looper = NULL) 1454 { 1455 return BControl::SetTarget(handler, 1456 looper); 1457 } 1458 1459 #endif 1460 1461