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