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