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