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