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