xref: /haiku/src/preferences/filetypes/IconView.cpp (revision 9760dcae2038d47442f4658c2575844c6cf92c40)
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 	} // NOTE: else there is no data, so nothing is leaked.
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 	fModificationMessage(NULL),
529 	fIconSize(B_LARGE_ICON),
530 	fIcon(NULL),
531 	fHeapIcon(NULL),
532 	fHasRef(false),
533 	fHasType(false),
534 	fIconData(NULL),
535 	fTracking(false),
536 	fDragging(false),
537 	fDropTarget(false),
538 	fShowEmptyFrame(true)
539 {
540 }
541 
542 
543 IconView::~IconView()
544 {
545 	delete fIcon;
546 	delete fModificationMessage;
547 }
548 
549 
550 void
551 IconView::AttachedToWindow()
552 {
553 	if (Parent())
554 		SetViewColor(Parent()->ViewColor());
555 	else
556 		SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
557 
558 	fTarget = this;
559 
560 	// SetTo() was already called before we were a valid messenger
561 	if (fHasRef || fHasType)
562 		_StartWatching();
563 }
564 
565 
566 void
567 IconView::DetachedFromWindow()
568 {
569 	_StopWatching();
570 }
571 
572 
573 void
574 IconView::MessageReceived(BMessage* message)
575 {
576 	if (message->WasDropped() && message->ReturnAddress() != BMessenger(this)
577 		&& AcceptsDrag(message)) {
578 		// set icon from message
579 		BBitmap* mini = NULL;
580 		BBitmap* large = NULL;
581 		const uint8* data = NULL;
582 		ssize_t size = 0;
583 
584 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
585 		message->FindData("icon", B_VECTOR_ICON_TYPE, (const void**)&data, &size);
586 #endif
587 
588 		BMessage archive;
589 		if (message->FindMessage("icon/large", &archive) == B_OK)
590 			large = (BBitmap*)BBitmap::Instantiate(&archive);
591 		if (message->FindMessage("icon/mini", &archive) == B_OK)
592 			mini = (BBitmap*)BBitmap::Instantiate(&archive);
593 
594 		if (large != NULL || mini != NULL || (data != NULL && size > 0))
595 			_SetIcon(large, mini, data, size);
596 		else {
597 			entry_ref ref;
598 			if (message->FindRef("refs", &ref) == B_OK)
599 				_SetIcon(&ref);
600 		}
601 
602 		delete large;
603 		delete mini;
604 
605 		return;
606 	}
607 
608 	switch (message->what) {
609 		case kMsgIconInvoked:
610 		case kMsgEditIcon:
611 		case kMsgAddIcon:
612 			_AddOrEditIcon();
613 			break;
614 		case kMsgRemoveIcon:
615 			_RemoveIcon();
616 			break;
617 
618 		case B_NODE_MONITOR:
619 		{
620 			if (!fHasRef)
621 				break;
622 
623 			int32 opcode;
624 			if (message->FindInt32("opcode", &opcode) != B_OK
625 				|| opcode != B_ATTR_CHANGED)
626 				break;
627 
628 			const char* name;
629 			if (message->FindString("attr", &name) != B_OK)
630 				break;
631 
632 			if (!strcmp(name, "BEOS:L:STD_ICON"))
633 				Update();
634 			break;
635 		}
636 
637 		case B_META_MIME_CHANGED:
638 		{
639 			if (!fHasType)
640 				break;
641 
642 			const char* type;
643 			int32 which;
644 			if (message->FindString("be:type", &type) != B_OK
645 				|| message->FindInt32("be:which", &which) != B_OK)
646 				break;
647 
648 			if (!strcasecmp(type, fType.Type())) {
649 				switch (which) {
650 					case B_MIME_TYPE_DELETED:
651 						Unset();
652 						break;
653 
654 					case B_ICON_CHANGED:
655 						Update();
656 						break;
657 
658 					default:
659 						break;
660 				}
661 			} else if (fSource != kOwnIcon
662 				&& message->FindString("be:extra_type", &type) == B_OK
663 				&& !strcasecmp(type, fType.Type())) {
664 				// this change could still affect our current icon
665 
666 				if (which == B_MIME_TYPE_DELETED
667 					|| which == B_PREFERRED_APP_CHANGED
668 #ifdef __HAIKU__
669 					|| which == B_SUPPORTED_TYPES_CHANGED
670 #endif
671 					|| which == B_ICON_FOR_TYPE_CHANGED)
672 					Update();
673 			}
674 			break;
675 		}
676 
677 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
678 		case B_ICON_DATA_EDITED:
679 		{
680 			const uint8* data;
681 			ssize_t size;
682 			if (message->FindData("icon data", B_VECTOR_ICON_TYPE,
683 					(const void**)&data, &size) < B_OK)
684 				break;
685 
686 			_SetIcon(NULL, NULL, data, size);
687 			break;
688 		}
689 #endif
690 
691 		default:
692 			BControl::MessageReceived(message);
693 			break;
694 	}
695 }
696 
697 
698 bool
699 IconView::AcceptsDrag(const BMessage* message)
700 {
701 	if (!IsEnabled())
702 		return false;
703 
704 	type_code type;
705 	int32 count;
706 	if (message->GetInfo("refs", &type, &count) == B_OK && count == 1 && type == B_REF_TYPE) {
707 		// if we're bound to an entry, check that no one drops this to us
708 		entry_ref ref;
709 		if (fHasRef && message->FindRef("refs", &ref) == B_OK && fRef == ref)
710 			return false;
711 
712 		return true;
713 	}
714 
715 	if (message->GetInfo("icon/large", &type) == B_OK && type == B_MESSAGE_TYPE
716 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
717 		|| message->GetInfo("icon", &type) == B_OK && type == B_VECTOR_ICON_TYPE
718 #endif
719 		|| message->GetInfo("icon/mini", &type) == B_OK && type == B_MESSAGE_TYPE)
720 		return true;
721 
722 	return false;
723 }
724 
725 
726 BRect
727 IconView::BitmapRect() const
728 {
729 	return BRect(0, 0, fIconSize - 1, fIconSize - 1);
730 }
731 
732 
733 void
734 IconView::Draw(BRect updateRect)
735 {
736 	SetDrawingMode(B_OP_ALPHA);
737 
738 	if (fHeapIcon != NULL)
739 		DrawBitmap(fHeapIcon, BitmapRect().LeftTop());
740 	else if (fIcon != NULL)
741 		DrawBitmap(fIcon, BitmapRect().LeftTop());
742 	else if (!fDropTarget && fShowEmptyFrame) {
743 		// draw frame so that the user knows here is something he
744 		// might be able to click on
745 		SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
746 		StrokeRect(BitmapRect());
747 	}
748 
749 	if (IsFocus()) {
750 		// mark this view as a having focus
751 		SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
752 		StrokeRect(BitmapRect());
753 	}
754 	if (fDropTarget) {
755 		// mark this view as a drop target
756 		SetHighColor(0, 0, 0);
757 		SetPenSize(2);
758 		BRect rect = BitmapRect();
759 // TODO: this is an incompatibility between R5 and Haiku and should be fixed!
760 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
761 		rect.left++;
762 		rect.top++;
763 #else
764 		rect.right--;
765 		rect.bottom--;
766 #endif
767 		StrokeRect(rect);
768 		SetPenSize(1);
769 	}
770 }
771 
772 
773 void
774 IconView::GetPreferredSize(float* _width, float* _height)
775 {
776 	if (_width)
777 		*_width = fIconSize;
778 
779 	if (_height)
780 		*_height = fIconSize;
781 }
782 
783 
784 void
785 IconView::MouseDown(BPoint where)
786 {
787 	if (!IsEnabled())
788 		return;
789 
790 	int32 buttons = B_PRIMARY_MOUSE_BUTTON;
791 	int32 clicks = 1;
792 	if (Looper() != NULL && Looper()->CurrentMessage() != NULL) {
793 		if (Looper()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK)
794 			buttons = B_PRIMARY_MOUSE_BUTTON;
795 		if (Looper()->CurrentMessage()->FindInt32("clicks", &clicks) != B_OK)
796 			clicks = 1;
797 	}
798 
799 	if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0 && BitmapRect().Contains(where)) {
800 		if (clicks == 2) {
801 			// double click - open Icon-O-Matic
802 			Invoke();
803 		} else if (fIcon != NULL) {
804 			// start tracking - this icon might be dragged around
805 			fDragPoint = where;
806 			fTracking = true;
807 		}
808 	}
809 
810 	if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
811 		// show context menu
812 
813 		ConvertToScreen(&where);
814 
815 		BPopUpMenu* menu = new BPopUpMenu("context");
816 		menu->SetFont(be_plain_font);
817 
818 		bool hasIcon = fHasType ? fSource == kOwnIcon : fIcon != NULL;
819 		if (hasIcon)
820 			menu->AddItem(new BMenuItem("Edit icon" B_UTF8_ELLIPSIS, new BMessage(kMsgEditIcon)));
821 		else
822 			menu->AddItem(new BMenuItem("Add icon" B_UTF8_ELLIPSIS, new BMessage(kMsgAddIcon)));
823 
824 		BMenuItem* item = new BMenuItem("Remove icon", new BMessage(kMsgRemoveIcon));
825 		if (!hasIcon)
826 			item->SetEnabled(false);
827 
828 		menu->AddItem(item);
829 		menu->SetTargetForItems(fTarget);
830 
831 		menu->Go(where, true, false, true);
832 	}
833 }
834 
835 
836 void
837 IconView::MouseUp(BPoint where)
838 {
839 	fTracking = false;
840 	fDragging = false;
841 
842 	if (fDropTarget) {
843 		fDropTarget = false;
844 		Invalidate();
845 	}
846 }
847 
848 
849 void
850 IconView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
851 {
852 	if (fTracking && !fDragging && fIcon != NULL
853 		&& (abs((int32)(where.x - fDragPoint.x)) > 3
854 			|| abs((int32)(where.y - fDragPoint.y)) > 3)) {
855 		// Start drag
856 		BMessage message(B_SIMPLE_DATA);
857 
858 		::Icon* icon = fIconData;
859 		if (fHasRef || fHasType) {
860 			icon = new ::Icon;
861 			if (fHasRef)
862 				icon->SetTo(fRef, fType.Type());
863 			else if (fHasType)
864 				icon->SetTo(fType);
865 		}
866 
867 		icon->CopyTo(message);
868 
869 		if (icon != fIconData)
870 			delete icon;
871 
872 		BBitmap *dragBitmap = new BBitmap(fIcon->Bounds(), B_RGBA32, true);
873 		dragBitmap->Lock();
874 		BView *view = new BView(dragBitmap->Bounds(), B_EMPTY_STRING, B_FOLLOW_NONE, 0);
875 		dragBitmap->AddChild(view);
876 
877 		view->SetHighColor(B_TRANSPARENT_COLOR);
878 		view->FillRect(dragBitmap->Bounds());
879 		view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
880 		view->SetDrawingMode(B_OP_ALPHA);
881 		view->SetHighColor(0, 0, 0, 160);
882 		view->DrawBitmap(fIcon);
883 
884 		view->Sync();
885 		dragBitmap->Unlock();
886 
887 		DragMessage(&message, dragBitmap, B_OP_ALPHA,
888 			fDragPoint - BitmapRect().LeftTop(), this);
889 		fDragging = true;
890 		SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
891 	}
892 
893 	if (dragMessage != NULL && !fDragging && AcceptsDrag(dragMessage)) {
894 		bool dropTarget = transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW;
895 		if (dropTarget != fDropTarget) {
896 			fDropTarget = dropTarget;
897 			Invalidate();
898 		}
899 	} else if (fDropTarget) {
900 		fDropTarget = false;
901 		Invalidate();
902 	}
903 }
904 
905 
906 void
907 IconView::KeyDown(const char* bytes, int32 numBytes)
908 {
909 	if (numBytes == 1) {
910 		switch (bytes[0]) {
911 			case B_DELETE:
912 			case B_BACKSPACE:
913 				_RemoveIcon();
914 				return;
915 			case B_ENTER:
916 			case B_SPACE:
917 				Invoke();
918 				return;
919 		}
920 	}
921 
922 	BControl::KeyDown(bytes, numBytes);
923 }
924 
925 
926 void
927 IconView::MakeFocus(bool focus)
928 {
929 	if (focus != IsFocus())
930 		Invalidate();
931 
932 	BControl::MakeFocus(focus);
933 }
934 
935 
936 void
937 IconView::SetTo(const entry_ref& ref, const char* fileType)
938 {
939 	Unset();
940 
941 	fHasRef = true;
942 	fRef = ref;
943 	if (fileType != NULL)
944 		fType.SetTo(fileType);
945 	else
946 		fType.Unset();
947 
948 	_StartWatching();
949 	Update();
950 }
951 
952 
953 void
954 IconView::SetTo(const BMimeType& type)
955 {
956 	Unset();
957 
958 	if (type.Type() == NULL)
959 		return;
960 
961 	fHasType = true;
962 	fType.SetTo(type.Type());
963 
964 	_StartWatching();
965 	Update();
966 }
967 
968 
969 void
970 IconView::SetTo(::Icon* icon)
971 {
972 	if (fIconData == icon)
973 		return;
974 
975 	Unset();
976 
977 	fIconData = icon;
978 
979 	Update();
980 }
981 
982 
983 void
984 IconView::Unset()
985 {
986 	if (fHasRef || fHasType)
987 		_StopWatching();
988 
989 	fHasRef = false;
990 	fHasType = false;
991 
992 	fType.Unset();
993 	fIconData = NULL;
994 }
995 
996 
997 void
998 IconView::Update()
999 {
1000 	delete fIcon;
1001 	fIcon = NULL;
1002 
1003 	Invalidate();
1004 		// this will actually trigger a redraw *after* we updated the icon below
1005 
1006 	BBitmap* icon = NULL;
1007 
1008 	if (fHasRef) {
1009 		BFile file(&fRef, B_READ_ONLY);
1010 		if (file.InitCheck() != B_OK)
1011 			return;
1012 
1013 		BAppFileInfo info;
1014 		if (info.SetTo(&file) != B_OK)
1015 			return;
1016 
1017 		icon = Icon::AllocateBitmap(fIconSize);
1018 		if (icon != NULL && info.GetIconForType(fType.Type(), icon,
1019 				(icon_size)fIconSize) != B_OK) {
1020 			delete icon;
1021 			return;
1022 		}
1023 	} else if (fHasType) {
1024 		icon = Icon::AllocateBitmap(fIconSize);
1025 		if (icon != NULL && icon_for_type(fType, *icon, (icon_size)fIconSize,
1026 				&fSource) != B_OK) {
1027 			delete icon;
1028 			return;
1029 		}
1030 	} else if (fIconData) {
1031 		icon = Icon::AllocateBitmap(fIconSize);
1032 		if (fIconData->GetIcon(icon) != B_OK) {
1033 			delete icon;
1034 			icon = NULL;
1035 		}
1036 	}
1037 
1038 	fIcon = icon;
1039 }
1040 
1041 
1042 void
1043 IconView::SetIconSize(int32 size)
1044 {
1045 	if (size < B_MINI_ICON)
1046 		size = B_MINI_ICON;
1047 	if (size > 256)
1048 		size = 256;
1049 	if (size == fIconSize)
1050 		return;
1051 
1052 	fIconSize = size;
1053 	Update();
1054 }
1055 
1056 
1057 void
1058 IconView::ShowIconHeap(bool show)
1059 {
1060 	if (show == (fHeapIcon != NULL))
1061 		return;
1062 
1063 	if (show) {
1064 		BResources* resources = be_app->AppResources();
1065 		if (resources != NULL) {
1066 			const void* data = NULL;
1067 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
1068 			size_t size;
1069 			data = resources->LoadResource('VICN', "icon heap", &size);
1070 			if (data != NULL) {
1071 				// got vector icon data
1072 				fHeapIcon = Icon::AllocateBitmap(B_LARGE_ICON, B_RGBA32);
1073 				if (BIconUtils::GetVectorIcon((const uint8*)data,
1074 						size, fHeapIcon) != B_OK) {
1075 					// bad data
1076 					delete fHeapIcon;
1077 					fHeapIcon = NULL;
1078 					data = NULL;
1079 				}
1080 			}
1081 #endif // HAIKU_TARGET_PLATFORM_HAIKU
1082 			if (data == NULL) {
1083 				// no vector icon or failed to get bitmap
1084 				// try bitmap icon
1085 				data = resources->LoadResource(B_LARGE_ICON_TYPE, "icon heap", NULL);
1086 				if (data != NULL) {
1087 					fHeapIcon = Icon::AllocateBitmap(B_LARGE_ICON, B_CMAP8);
1088 					memcpy(fHeapIcon->Bits(), data, fHeapIcon->BitsLength());
1089 				}
1090 			}
1091 		}
1092 	} else {
1093 		delete fHeapIcon;
1094 		fHeapIcon = NULL;
1095 	}
1096 }
1097 
1098 
1099 void
1100 IconView::ShowEmptyFrame(bool show)
1101 {
1102 	if (show == fShowEmptyFrame)
1103 		return;
1104 
1105 	fShowEmptyFrame = show;
1106 	if (fIcon == NULL)
1107 		Invalidate();
1108 }
1109 
1110 
1111 void
1112 IconView::SetTarget(const BMessenger& target)
1113 {
1114 	fTarget = target;
1115 }
1116 
1117 
1118 void
1119 IconView::SetModificationMessage(BMessage* message)
1120 {
1121 	delete fModificationMessage;
1122 	fModificationMessage = message;
1123 }
1124 
1125 
1126 void
1127 IconView::Invoke(const BMessage* _message)
1128 {
1129 	if (_message == NULL)
1130 		fTarget.SendMessage(kMsgIconInvoked);
1131 	else {
1132 		BMessage message(*_message);
1133 		fTarget.SendMessage(&message);
1134 	}
1135 }
1136 
1137 
1138 Icon*
1139 IconView::Icon()
1140 {
1141 	return fIconData;
1142 }
1143 
1144 
1145 status_t
1146 IconView::GetRef(entry_ref& ref) const
1147 {
1148 	if (!fHasRef)
1149 		return B_BAD_TYPE;
1150 
1151 	ref = fRef;
1152 	return B_OK;
1153 }
1154 
1155 
1156 status_t
1157 IconView::GetMimeType(BMimeType& type) const
1158 {
1159 	if (!fHasType)
1160 		return B_BAD_TYPE;
1161 
1162 	type.SetTo(fType.Type());
1163 	return B_OK;
1164 }
1165 
1166 
1167 void
1168 IconView::_AddOrEditIcon()
1169 {
1170 	// this works only in Haiku! (the icon editor is built-in in R5's FileType)
1171 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
1172 	BMessage message;
1173 	if (fHasRef && fType.Type() == NULL) {
1174 		// in ref mode, Icon-O-Matic can change the icon directly, and
1175 		// we'll pick it up via node monitoring
1176 		message.what = B_REFS_RECEIVED;
1177 		message.AddRef("refs", &fRef);
1178 	} else {
1179 		// in static or MIME type mode, Icon-O-Matic needs to return the
1180 		// buffer it changed once its done
1181 		message.what = B_EDIT_ICON_DATA;
1182 		message.AddMessenger("reply to", BMessenger(this));
1183 
1184 		::Icon* icon = fIconData;
1185 		if (icon == NULL) {
1186 			icon = new ::Icon();
1187 			if (fHasRef)
1188 				icon->SetTo(fRef, fType.Type());
1189 			else
1190 				icon->SetTo(fType);
1191 		}
1192 
1193 		if (icon->HasData()) {
1194 			uint8* data;
1195 			size_t size;
1196 			if (icon->GetData(&data, &size) == B_OK) {
1197 				message.AddData("icon data", B_VECTOR_ICON_TYPE, data, size);
1198 				free(data);
1199 			}
1200 
1201 			// TODO: somehow figure out how names of objects in the icon
1202 			// can be preserved. Maybe in a second (optional) attribute
1203 			// where ever a vector icon attribute is present?
1204 		}
1205 	}
1206 
1207 	be_roster->Launch("application/x-vnd.haiku-icon_o_matic", &message);
1208 #endif
1209 }
1210 
1211 
1212 void
1213 IconView::_SetIcon(BBitmap* large, BBitmap* mini, const uint8* data, size_t size, bool force)
1214 {
1215 	if (fHasRef) {
1216 		BFile file(&fRef, B_READ_WRITE);
1217 
1218 		BAppFileInfo info(&file);
1219 		if (info.InitCheck() == B_OK) {
1220 			if (large != NULL || force)
1221 				info.SetIconForType(fType.Type(), large, B_LARGE_ICON);
1222 			if (mini != NULL || force)
1223 				info.SetIconForType(fType.Type(), mini, B_MINI_ICON);
1224 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
1225 			if (data != NULL || force)
1226 				info.SetIconForType(fType.Type(), data, size);
1227 #endif
1228 		}
1229 		// the icon shown will be updated using node monitoring
1230 	} else if (fHasType) {
1231 		if (large != NULL || force)
1232 			fType.SetIcon(large, B_LARGE_ICON);
1233 		if (mini != NULL || force)
1234 			fType.SetIcon(mini, B_MINI_ICON);
1235 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
1236 		if (data != NULL || force)
1237 			fType.SetIcon(data, size);
1238 #endif
1239 		// the icon shown will be updated automatically - we're watching
1240 		// any changes to the MIME database
1241 	} else if (fIconData != NULL) {
1242 		if (large != NULL || force)
1243 			fIconData->SetLarge(large);
1244 		if (mini != NULL || force)
1245 			fIconData->SetMini(mini);
1246 		if (data != NULL || force)
1247 			fIconData->SetData(data, size);
1248 
1249 		// replace visible icon
1250 		if (fIcon == NULL && fIconData->HasData())
1251 			fIcon = Icon::AllocateBitmap(fIconSize);
1252 
1253 		if (fIconData->GetIcon(fIcon) != B_OK) {
1254 			delete fIcon;
1255 			fIcon = NULL;
1256 		}
1257 		Invalidate();
1258 	}
1259 
1260 	if (fModificationMessage)
1261 		Invoke(fModificationMessage);
1262 }
1263 
1264 
1265 void
1266 IconView::_SetIcon(entry_ref* ref)
1267 {
1268 	// retrieve icons from file
1269 	BFile file(ref, B_READ_ONLY);
1270 	BAppFileInfo info(&file);
1271 	if (file.InitCheck() != B_OK || info.InitCheck() != B_OK)
1272 		return;
1273 
1274 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
1275 	// try vector/PNG icon first
1276 	uint8* data = NULL;
1277 	size_t size = 0;
1278 	if (info.GetIcon(&data, &size) == B_OK) {
1279 		_SetIcon(NULL, NULL, data, size);
1280 		free(data);
1281 		return;
1282 	}
1283 #endif
1284 
1285 	// try large/mini icons
1286 	bool hasMini = false;
1287 	bool hasLarge = false;
1288 
1289 	BBitmap* large = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8);
1290 	if (large->InitCheck() != B_OK) {
1291 		delete large;
1292 		large = NULL;
1293 	}
1294 	BBitmap* mini = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8);
1295 	if (mini->InitCheck() != B_OK) {
1296 		delete mini;
1297 		mini = NULL;
1298 	}
1299 
1300 	if (large != NULL && info.GetIcon(large, B_LARGE_ICON) == B_OK)
1301 		hasLarge = true;
1302 	if (mini != NULL && info.GetIcon(mini, B_MINI_ICON) == B_OK)
1303 		hasMini = true;
1304 
1305 	if (!hasMini && !hasLarge) {
1306 		// TODO: don't forget device icons!
1307 
1308 		// try MIME type icon
1309 		char type[B_MIME_TYPE_LENGTH];
1310 		if (info.GetType(type) != B_OK)
1311 			return;
1312 
1313 		BMimeType mimeType(type);
1314 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
1315 		if (icon_for_type(mimeType, &data, &size) != B_OK) {
1316 			// only try large/mini icons when there is no vector icon
1317 #endif
1318 			if (large != NULL && icon_for_type(mimeType, *large, B_LARGE_ICON) == B_OK)
1319 				hasLarge = true;
1320 			if (mini != NULL && icon_for_type(mimeType, *mini, B_MINI_ICON) == B_OK)
1321 				hasMini = true;
1322 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
1323 		}
1324 #endif
1325 	}
1326 
1327 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
1328 	if (data != NULL) {
1329 		_SetIcon(NULL, NULL, data, size);
1330 		free(data);
1331 	} else
1332 #endif
1333 	if (hasLarge || hasMini)
1334 		_SetIcon(large, mini, NULL, 0);
1335 
1336 	delete large;
1337 	delete mini;
1338 }
1339 
1340 
1341 void
1342 IconView::_RemoveIcon()
1343 {
1344 	_SetIcon(NULL, NULL, NULL, 0, true);
1345 }
1346 
1347 
1348 void
1349 IconView::_StartWatching()
1350 {
1351 	if (Looper() == NULL) {
1352 		// we are not a valid messenger yet
1353 		return;
1354 	}
1355 
1356 	if (fHasRef) {
1357 		BNode node(&fRef);
1358 		node_ref nodeRef;
1359 		if (node.InitCheck() == B_OK
1360 			&& node.GetNodeRef(&nodeRef) == B_OK)
1361 			watch_node(&nodeRef, B_WATCH_ATTR, this);
1362 	} else if (fHasType)
1363 		BMimeType::StartWatching(this);
1364 }
1365 
1366 
1367 void
1368 IconView::_StopWatching()
1369 {
1370 	if (fHasRef)
1371 		stop_watching(this);
1372 	else if (fHasType)
1373 		BMimeType::StopWatching(this);
1374 }
1375 
1376