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