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