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