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