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
icon_for_type(const BMimeType & type,uint8 ** _data,size_t * _size,icon_source * _source)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
icon_for_type(const BMimeType & type,BBitmap & bitmap,icon_size size,icon_source * _source)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
Icon()152 Icon::Icon()
153 :
154 fLarge(NULL),
155 fMini(NULL),
156 fData(NULL),
157 fSize(0)
158 {
159 }
160
161
Icon(const Icon & source)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
~Icon()173 Icon::~Icon()
174 {
175 delete fLarge;
176 delete fMini;
177 free(fData);
178 }
179
180
181 void
SetTo(const BAppFileInfo & info,const char * type)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
SetTo(const entry_ref & ref,const char * type)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
SetTo(const BMimeType & type,icon_source * _source)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
CopyTo(BAppFileInfo & info,const char * type,bool force) const249 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
CopyTo(const entry_ref & ref,const char * type,bool force) const265 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
CopyTo(BMimeType & type,bool force) const282 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
CopyTo(BMessage & message) const298 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
SetLarge(const BBitmap * large)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
SetMini(const BBitmap * mini)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
SetData(const uint8 * data,size_t size)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
Unset()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
HasData() const383 Icon::HasData() const
384 {
385 return fData != NULL || fLarge != NULL || fMini != NULL;
386 }
387
388
389 status_t
GetData(icon_size which,BBitmap ** _bitmap) const390 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
GetData(uint8 ** _data,size_t * _size) const419 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
GetIcon(BBitmap * bitmap) const436 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&
operator =(const Icon & source)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
AdoptLarge(BBitmap * large)500 Icon::AdoptLarge(BBitmap *large)
501 {
502 delete fLarge;
503 fLarge = large;
504 }
505
506
507 void
AdoptMini(BBitmap * mini)508 Icon::AdoptMini(BBitmap *mini)
509 {
510 delete fMini;
511 fMini = mini;
512 }
513
514
515 void
AdoptData(uint8 * data,size_t size)516 Icon::AdoptData(uint8* data, size_t size)
517 {
518 free(fData);
519 fData = data;
520 fSize = size;
521 }
522
523
524 /*static*/ BBitmap*
AllocateBitmap(icon_size size,int32 space)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
IconView(const char * name,uint32 flags)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
~IconView()570 IconView::~IconView()
571 {
572 delete fIconBitmap;
573 delete fModificationMessage;
574 }
575
576
577 void
AttachedToWindow()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
DetachedFromWindow()591 IconView::DetachedFromWindow()
592 {
593 _StopWatching();
594 }
595
596
597 void
MessageReceived(BMessage * message)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
AcceptsDrag(const BMessage * message)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
BitmapRect() const750 IconView::BitmapRect() const
751 {
752 return fIconRect;
753 }
754
755
756 void
Draw(BRect updateRect)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
GetPreferredSize(float * _width,float * _height)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
MinSize()805 IconView::MinSize()
806 {
807 float width, height;
808 GetPreferredSize(&width, &height);
809 return BSize(width, height);
810 }
811
812
813 BSize
PreferredSize()814 IconView::PreferredSize()
815 {
816 return MinSize();
817 }
818
819
820 BSize
MaxSize()821 IconView::MaxSize()
822 {
823 return MinSize();
824 }
825
826
827 void
MouseDown(BPoint where)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
MouseUp(BPoint where)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
MouseMoved(BPoint where,uint32 transit,const BMessage * dragMessage)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
KeyDown(const char * bytes,int32 numBytes)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
MakeFocus(bool focus)978 IconView::MakeFocus(bool focus)
979 {
980 if (focus != IsFocus())
981 Invalidate();
982
983 BControl::MakeFocus(focus);
984 }
985
986
987 void
SetTo(const entry_ref & ref,const char * fileType)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
SetTo(const BMimeType & type)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
SetTo(::Icon * icon)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
Unset()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
Update()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
SetIconSize(icon_size size)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
ShowIconHeap(bool show)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
ShowEmptyFrame(bool show)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
SetTarget(const BMessenger & target)1166 IconView::SetTarget(const BMessenger& target)
1167 {
1168 fTarget = target;
1169 return B_OK;
1170 }
1171
1172
1173 void
SetModificationMessage(BMessage * message)1174 IconView::SetModificationMessage(BMessage* message)
1175 {
1176 delete fModificationMessage;
1177 fModificationMessage = message;
1178 }
1179
1180
1181 status_t
Invoke(BMessage * message)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*
Icon()1193 IconView::Icon()
1194 {
1195 return fIcon;
1196 }
1197
1198
1199 status_t
GetRef(entry_ref & ref) const1200 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
GetMimeType(BMimeType & type) const1211 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
_AddOrEditIcon()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
_SetIcon(BBitmap * large,BBitmap * mini,const uint8 * data,size_t size,bool force)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
_SetIcon(entry_ref * ref)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
_RemoveIcon()1408 IconView::_RemoveIcon()
1409 {
1410 _SetIcon(NULL, NULL, NULL, 0, true);
1411 }
1412
1413
1414 void
_StartWatching()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
_StopWatching()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
SetTarget(BMessenger target)1446 IconView::SetTarget(BMessenger target)
1447 {
1448 return BControl::SetTarget(target);
1449 }
1450
1451
1452 status_t
SetTarget(const BHandler * handler,const BLooper * looper=NULL)1453 IconView::SetTarget(const BHandler* handler, const BLooper* looper = NULL)
1454 {
1455 return BControl::SetTarget(handler,
1456 looper);
1457 }
1458
1459 #endif
1460
1461