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