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