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