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