xref: /haiku/src/preferences/filetypes/IconView.cpp (revision 37c7d5d83a2372a6971e383411d5bacbeef0ebdc)
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 TR_CONTEXT
31 #define TR_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 && BIconUtils::GetVectorIcon(fData, fSize, bitmap) == B_OK)
435 		return B_OK;
436 
437 	int32 width = bitmap->Bounds().IntegerWidth() + 1;
438 
439 	if (width == B_LARGE_ICON && fLarge != NULL) {
440 		bitmap->SetBits(fLarge->Bits(), fLarge->BitsLength(), 0, fLarge->ColorSpace());
441 		return B_OK;
442 	}
443 	if (width == B_MINI_ICON && fMini != NULL) {
444 		bitmap->SetBits(fMini->Bits(), fMini->BitsLength(), 0, fMini->ColorSpace());
445 		return B_OK;
446 	}
447 
448 	return B_ENTRY_NOT_FOUND;
449 }
450 
451 
452 Icon&
453 Icon::operator=(const Icon& source)
454 {
455 	Unset();
456 
457 	SetData(source.fData, source.fSize);
458 	SetLarge(source.fLarge);
459 	SetMini(source.fMini);
460 
461 	return *this;
462 }
463 
464 
465 void
466 Icon::AdoptLarge(BBitmap *large)
467 {
468 	delete fLarge;
469 	fLarge = large;
470 }
471 
472 
473 void
474 Icon::AdoptMini(BBitmap *mini)
475 {
476 	delete fMini;
477 	fMini = mini;
478 }
479 
480 
481 void
482 Icon::AdoptData(uint8* data, size_t size)
483 {
484 	free(fData);
485 	fData = data;
486 	fSize = size;
487 }
488 
489 
490 /*static*/ BBitmap*
491 Icon::AllocateBitmap(int32 size, int32 space)
492 {
493 	int32 kSpace = B_RGBA32;
494 	if (space == -1)
495 		space = kSpace;
496 
497 	BBitmap* bitmap = new (nothrow) BBitmap(BRect(0, 0, size - 1, size - 1),
498 		(color_space)space);
499 	if (bitmap == NULL || bitmap->InitCheck() != B_OK) {
500 		delete bitmap;
501 		return NULL;
502 	}
503 
504 	return bitmap;
505 }
506 
507 
508 //	#pragma mark -
509 
510 
511 IconView::IconView(const char* name, uint32 flags)
512 	: BControl(name, NULL, NULL, B_WILL_DRAW | flags),
513 	fModificationMessage(NULL),
514 	fIconSize(B_LARGE_ICON),
515 	fIcon(NULL),
516 	fHeapIcon(NULL),
517 	fHasRef(false),
518 	fHasType(false),
519 	fIconData(NULL),
520 	fTracking(false),
521 	fDragging(false),
522 	fDropTarget(false),
523 	fShowEmptyFrame(true)
524 {
525 }
526 
527 
528 IconView::~IconView()
529 {
530 	delete fIcon;
531 	delete fModificationMessage;
532 }
533 
534 
535 void
536 IconView::AttachedToWindow()
537 {
538 	if (Parent())
539 		SetViewColor(Parent()->ViewColor());
540 	else
541 		SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
542 
543 	fTarget = this;
544 
545 	// SetTo() was already called before we were a valid messenger
546 	if (fHasRef || fHasType)
547 		_StartWatching();
548 }
549 
550 
551 void
552 IconView::DetachedFromWindow()
553 {
554 	_StopWatching();
555 }
556 
557 
558 void
559 IconView::MessageReceived(BMessage* message)
560 {
561 	if (message->WasDropped() && message->ReturnAddress() != BMessenger(this)
562 		&& AcceptsDrag(message)) {
563 		// set icon from message
564 		BBitmap* mini = NULL;
565 		BBitmap* large = NULL;
566 		const uint8* data = NULL;
567 		ssize_t size = 0;
568 
569 		message->FindData("icon", B_VECTOR_ICON_TYPE, (const void**)&data,
570 			&size);
571 
572 		BMessage archive;
573 		if (message->FindMessage("icon/large", &archive) == B_OK)
574 			large = (BBitmap*)BBitmap::Instantiate(&archive);
575 		if (message->FindMessage("icon/mini", &archive) == B_OK)
576 			mini = (BBitmap*)BBitmap::Instantiate(&archive);
577 
578 		if (large != NULL || mini != NULL || (data != NULL && size > 0))
579 			_SetIcon(large, mini, data, size);
580 		else {
581 			entry_ref ref;
582 			if (message->FindRef("refs", &ref) == B_OK)
583 				_SetIcon(&ref);
584 		}
585 
586 		delete large;
587 		delete mini;
588 
589 		return;
590 	}
591 
592 	switch (message->what) {
593 		case kMsgIconInvoked:
594 		case kMsgEditIcon:
595 		case kMsgAddIcon:
596 			_AddOrEditIcon();
597 			break;
598 		case kMsgRemoveIcon:
599 			_RemoveIcon();
600 			break;
601 
602 		case B_NODE_MONITOR:
603 		{
604 			if (!fHasRef)
605 				break;
606 
607 			int32 opcode;
608 			if (message->FindInt32("opcode", &opcode) != B_OK
609 				|| opcode != B_ATTR_CHANGED)
610 				break;
611 
612 			const char* name;
613 			if (message->FindString("attr", &name) != B_OK)
614 				break;
615 
616 			if (!strcmp(name, "BEOS:L:STD_ICON"))
617 				Update();
618 			break;
619 		}
620 
621 		case B_META_MIME_CHANGED:
622 		{
623 			if (!fHasType)
624 				break;
625 
626 			const char* type;
627 			int32 which;
628 			if (message->FindString("be:type", &type) != B_OK
629 				|| message->FindInt32("be:which", &which) != B_OK)
630 				break;
631 
632 			if (!strcasecmp(type, fType.Type())) {
633 				switch (which) {
634 					case B_MIME_TYPE_DELETED:
635 						Unset();
636 						break;
637 
638 					case B_ICON_CHANGED:
639 						Update();
640 						break;
641 
642 					default:
643 						break;
644 				}
645 			} else if (fSource != kOwnIcon
646 				&& message->FindString("be:extra_type", &type) == B_OK
647 				&& !strcasecmp(type, fType.Type())) {
648 				// this change could still affect our current icon
649 
650 				if (which == B_MIME_TYPE_DELETED
651 					|| which == B_PREFERRED_APP_CHANGED
652 					|| which == B_SUPPORTED_TYPES_CHANGED
653 					|| which == B_ICON_FOR_TYPE_CHANGED)
654 					Update();
655 			}
656 			break;
657 		}
658 
659 		case B_ICON_DATA_EDITED:
660 		{
661 			const uint8* data;
662 			ssize_t size;
663 			if (message->FindData("icon data", B_VECTOR_ICON_TYPE,
664 					(const void**)&data, &size) < B_OK)
665 				break;
666 
667 			_SetIcon(NULL, NULL, data, size);
668 			break;
669 		}
670 
671 		default:
672 			BControl::MessageReceived(message);
673 			break;
674 	}
675 }
676 
677 
678 bool
679 IconView::AcceptsDrag(const BMessage* message)
680 {
681 	if (!IsEnabled())
682 		return false;
683 
684 	type_code type;
685 	int32 count;
686 	if (message->GetInfo("refs", &type, &count) == B_OK && count == 1
687 		&& type == B_REF_TYPE) {
688 		// if we're bound to an entry, check that no one drops this to us
689 		entry_ref ref;
690 		if (fHasRef && message->FindRef("refs", &ref) == B_OK && fRef == ref)
691 			return false;
692 
693 		return true;
694 	}
695 
696 	if (message->GetInfo("icon/large", &type) == B_OK
697 			&& type == B_MESSAGE_TYPE
698 		|| message->GetInfo("icon", &type) == B_OK
699 			&& type == B_VECTOR_ICON_TYPE
700 		|| message->GetInfo("icon/mini", &type) == B_OK
701 			&& type == B_MESSAGE_TYPE)
702 		return true;
703 
704 	return false;
705 }
706 
707 
708 BRect
709 IconView::BitmapRect() const
710 {
711 	return BRect(0, 0, fIconSize - 1, fIconSize - 1);
712 }
713 
714 
715 void
716 IconView::Draw(BRect updateRect)
717 {
718 	SetDrawingMode(B_OP_ALPHA);
719 
720 	if (fHeapIcon != NULL)
721 		DrawBitmap(fHeapIcon, BitmapRect().LeftTop());
722 	else if (fIcon != NULL)
723 		DrawBitmap(fIcon, BitmapRect().LeftTop());
724 	else if (!fDropTarget && fShowEmptyFrame) {
725 		// draw frame so that the user knows here is something he
726 		// might be able to click on
727 		SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
728 		StrokeRect(BitmapRect());
729 	}
730 
731 	if (IsFocus()) {
732 		// mark this view as a having focus
733 		SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
734 		StrokeRect(BitmapRect());
735 	}
736 	if (fDropTarget) {
737 		// mark this view as a drop target
738 		SetHighColor(0, 0, 0);
739 		SetPenSize(2);
740 		BRect rect = BitmapRect();
741 // TODO: this is an incompatibility between R5 and Haiku and should be fixed!
742 // (Necessary adjustment differs.)
743 		rect.left++;
744 		rect.top++;
745 
746 		StrokeRect(rect);
747 		SetPenSize(1);
748 	}
749 }
750 
751 
752 void
753 IconView::GetPreferredSize(float* _width, float* _height)
754 {
755 	if (_width)
756 		*_width = fIconSize;
757 
758 	if (_height)
759 		*_height = fIconSize;
760 }
761 
762 
763 BSize
764 IconView::MinSize()
765 {
766 	float width, height;
767 	GetPreferredSize(&width, &height);
768 	return BSize(width, height);
769 }
770 
771 
772 BSize
773 IconView::PreferredSize()
774 {
775 	return MinSize();
776 }
777 
778 
779 BSize
780 IconView::MaxSize()
781 {
782 	return MinSize();
783 }
784 
785 
786 void
787 IconView::MouseDown(BPoint where)
788 {
789 	if (!IsEnabled())
790 		return;
791 
792 	int32 buttons = B_PRIMARY_MOUSE_BUTTON;
793 	int32 clicks = 1;
794 	if (Looper() != NULL && Looper()->CurrentMessage() != NULL) {
795 		if (Looper()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK)
796 			buttons = B_PRIMARY_MOUSE_BUTTON;
797 		if (Looper()->CurrentMessage()->FindInt32("clicks", &clicks) != B_OK)
798 			clicks = 1;
799 	}
800 
801 	if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0 && BitmapRect().Contains(where)) {
802 		if (clicks == 2) {
803 			// double click - open Icon-O-Matic
804 			Invoke();
805 		} else if (fIcon != NULL) {
806 			// start tracking - this icon might be dragged around
807 			fDragPoint = where;
808 			fTracking = true;
809 		}
810 	}
811 
812 	if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
813 		// show context menu
814 
815 		ConvertToScreen(&where);
816 
817 		BPopUpMenu* menu = new BPopUpMenu("context");
818 		menu->SetFont(be_plain_font);
819 
820 		bool hasIcon = fHasType ? fSource == kOwnIcon : fIcon != NULL;
821 		if (hasIcon) {
822 			menu->AddItem(new BMenuItem(TR("Edit icon" B_UTF8_ELLIPSIS),
823 				new BMessage(kMsgEditIcon)));
824 		} else {
825 			menu->AddItem(new BMenuItem(TR("Add icon" B_UTF8_ELLIPSIS),
826 				new BMessage(kMsgAddIcon)));
827 		}
828 
829 		BMenuItem* item = new BMenuItem(TR("Remove icon"),
830 			new BMessage(kMsgRemoveIcon));
831 		if (!hasIcon)
832 			item->SetEnabled(false);
833 
834 		menu->AddItem(item);
835 		menu->SetTargetForItems(fTarget);
836 
837 		menu->Go(where, true, false, true);
838 	}
839 }
840 
841 
842 void
843 IconView::MouseUp(BPoint where)
844 {
845 	fTracking = false;
846 	fDragging = false;
847 
848 	if (fDropTarget) {
849 		fDropTarget = false;
850 		Invalidate();
851 	}
852 }
853 
854 
855 void
856 IconView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
857 {
858 	if (fTracking && !fDragging && fIcon != NULL
859 		&& (abs((int32)(where.x - fDragPoint.x)) > 3
860 			|| abs((int32)(where.y - fDragPoint.y)) > 3)) {
861 		// Start drag
862 		BMessage message(B_SIMPLE_DATA);
863 
864 		::Icon* icon = fIconData;
865 		if (fHasRef || fHasType) {
866 			icon = new ::Icon;
867 			if (fHasRef)
868 				icon->SetTo(fRef, fType.Type());
869 			else if (fHasType)
870 				icon->SetTo(fType);
871 		}
872 
873 		icon->CopyTo(message);
874 
875 		if (icon != fIconData)
876 			delete icon;
877 
878 		BBitmap *dragBitmap = new BBitmap(fIcon->Bounds(), B_RGBA32, true);
879 		dragBitmap->Lock();
880 		BView *view = new BView(dragBitmap->Bounds(), B_EMPTY_STRING, B_FOLLOW_NONE, 0);
881 		dragBitmap->AddChild(view);
882 
883 		view->SetHighColor(B_TRANSPARENT_COLOR);
884 		view->FillRect(dragBitmap->Bounds());
885 		view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
886 		view->SetDrawingMode(B_OP_ALPHA);
887 		view->SetHighColor(0, 0, 0, 160);
888 		view->DrawBitmap(fIcon);
889 
890 		view->Sync();
891 		dragBitmap->Unlock();
892 
893 		DragMessage(&message, dragBitmap, B_OP_ALPHA,
894 			fDragPoint - BitmapRect().LeftTop(), this);
895 		fDragging = true;
896 		SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
897 	}
898 
899 	if (dragMessage != NULL && !fDragging && AcceptsDrag(dragMessage)) {
900 		bool dropTarget = transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW;
901 		if (dropTarget != fDropTarget) {
902 			fDropTarget = dropTarget;
903 			Invalidate();
904 		}
905 	} else if (fDropTarget) {
906 		fDropTarget = false;
907 		Invalidate();
908 	}
909 }
910 
911 
912 void
913 IconView::KeyDown(const char* bytes, int32 numBytes)
914 {
915 	if (numBytes == 1) {
916 		switch (bytes[0]) {
917 			case B_DELETE:
918 			case B_BACKSPACE:
919 				_RemoveIcon();
920 				return;
921 			case B_ENTER:
922 			case B_SPACE:
923 				Invoke();
924 				return;
925 		}
926 	}
927 
928 	BControl::KeyDown(bytes, numBytes);
929 }
930 
931 
932 void
933 IconView::MakeFocus(bool focus)
934 {
935 	if (focus != IsFocus())
936 		Invalidate();
937 
938 	BControl::MakeFocus(focus);
939 }
940 
941 
942 void
943 IconView::SetTo(const entry_ref& ref, const char* fileType)
944 {
945 	Unset();
946 
947 	fHasRef = true;
948 	fRef = ref;
949 	if (fileType != NULL)
950 		fType.SetTo(fileType);
951 	else
952 		fType.Unset();
953 
954 	_StartWatching();
955 	Update();
956 }
957 
958 
959 void
960 IconView::SetTo(const BMimeType& type)
961 {
962 	Unset();
963 
964 	if (type.Type() == NULL)
965 		return;
966 
967 	fHasType = true;
968 	fType.SetTo(type.Type());
969 
970 	_StartWatching();
971 	Update();
972 }
973 
974 
975 void
976 IconView::SetTo(::Icon* icon)
977 {
978 	if (fIconData == icon)
979 		return;
980 
981 	Unset();
982 
983 	fIconData = icon;
984 
985 	Update();
986 }
987 
988 
989 void
990 IconView::Unset()
991 {
992 	if (fHasRef || fHasType)
993 		_StopWatching();
994 
995 	fHasRef = false;
996 	fHasType = false;
997 
998 	fType.Unset();
999 	fIconData = NULL;
1000 }
1001 
1002 
1003 void
1004 IconView::Update()
1005 {
1006 	delete fIcon;
1007 	fIcon = NULL;
1008 
1009 	Invalidate();
1010 		// this will actually trigger a redraw *after* we updated the icon below
1011 
1012 	BBitmap* icon = NULL;
1013 
1014 	if (fHasRef) {
1015 		BFile file(&fRef, B_READ_ONLY);
1016 		if (file.InitCheck() != B_OK)
1017 			return;
1018 
1019 		BAppFileInfo info;
1020 		if (info.SetTo(&file) != B_OK)
1021 			return;
1022 
1023 		icon = Icon::AllocateBitmap(fIconSize);
1024 		if (icon != NULL && info.GetIconForType(fType.Type(), icon,
1025 				(icon_size)fIconSize) != B_OK) {
1026 			delete icon;
1027 			return;
1028 		}
1029 	} else if (fHasType) {
1030 		icon = Icon::AllocateBitmap(fIconSize);
1031 		if (icon != NULL && icon_for_type(fType, *icon, (icon_size)fIconSize,
1032 				&fSource) != B_OK) {
1033 			delete icon;
1034 			return;
1035 		}
1036 	} else if (fIconData) {
1037 		icon = Icon::AllocateBitmap(fIconSize);
1038 		if (fIconData->GetIcon(icon) != B_OK) {
1039 			delete icon;
1040 			icon = NULL;
1041 		}
1042 	}
1043 
1044 	fIcon = icon;
1045 }
1046 
1047 
1048 void
1049 IconView::SetIconSize(int32 size)
1050 {
1051 	if (size < B_MINI_ICON)
1052 		size = B_MINI_ICON;
1053 	if (size > 256)
1054 		size = 256;
1055 	if (size == fIconSize)
1056 		return;
1057 
1058 	fIconSize = size;
1059 	Update();
1060 }
1061 
1062 
1063 void
1064 IconView::ShowIconHeap(bool show)
1065 {
1066 	if (show == (fHeapIcon != NULL))
1067 		return;
1068 
1069 	if (show) {
1070 		BResources* resources = be_app->AppResources();
1071 		if (resources != NULL) {
1072 			const void* data = NULL;
1073 			size_t size;
1074 			data = resources->LoadResource('VICN', "icon heap", &size);
1075 			if (data != NULL) {
1076 				// got vector icon data
1077 				fHeapIcon = Icon::AllocateBitmap(B_LARGE_ICON, B_RGBA32);
1078 				if (BIconUtils::GetVectorIcon((const uint8*)data,
1079 						size, fHeapIcon) != B_OK) {
1080 					// bad data
1081 					delete fHeapIcon;
1082 					fHeapIcon = NULL;
1083 					data = NULL;
1084 				}
1085 			}
1086 			if (data == NULL) {
1087 				// no vector icon or failed to get bitmap
1088 				// try bitmap icon
1089 				data = resources->LoadResource(B_LARGE_ICON_TYPE, "icon heap", NULL);
1090 				if (data != NULL) {
1091 					fHeapIcon = Icon::AllocateBitmap(B_LARGE_ICON, B_CMAP8);
1092 					memcpy(fHeapIcon->Bits(), data, fHeapIcon->BitsLength());
1093 				}
1094 			}
1095 		}
1096 	} else {
1097 		delete fHeapIcon;
1098 		fHeapIcon = NULL;
1099 	}
1100 }
1101 
1102 
1103 void
1104 IconView::ShowEmptyFrame(bool show)
1105 {
1106 	if (show == fShowEmptyFrame)
1107 		return;
1108 
1109 	fShowEmptyFrame = show;
1110 	if (fIcon == NULL)
1111 		Invalidate();
1112 }
1113 
1114 
1115 status_t
1116 IconView::SetTarget(const BMessenger& target)
1117 {
1118 	fTarget = target;
1119 	return B_OK;
1120 }
1121 
1122 
1123 void
1124 IconView::SetModificationMessage(BMessage* message)
1125 {
1126 	delete fModificationMessage;
1127 	fModificationMessage = message;
1128 }
1129 
1130 
1131 status_t
1132 IconView::Invoke(const BMessage* _message)
1133 {
1134 	if (_message == NULL)
1135 		fTarget.SendMessage(kMsgIconInvoked);
1136 	else {
1137 		BMessage message(*_message);
1138 		fTarget.SendMessage(&message);
1139 	}
1140 	return B_OK;
1141 }
1142 
1143 
1144 Icon*
1145 IconView::Icon()
1146 {
1147 	return fIconData;
1148 }
1149 
1150 
1151 status_t
1152 IconView::GetRef(entry_ref& ref) const
1153 {
1154 	if (!fHasRef)
1155 		return B_BAD_TYPE;
1156 
1157 	ref = fRef;
1158 	return B_OK;
1159 }
1160 
1161 
1162 status_t
1163 IconView::GetMimeType(BMimeType& type) const
1164 {
1165 	if (!fHasType)
1166 		return B_BAD_TYPE;
1167 
1168 	type.SetTo(fType.Type());
1169 	return B_OK;
1170 }
1171 
1172 
1173 void
1174 IconView::_AddOrEditIcon()
1175 {
1176 	BMessage message;
1177 	if (fHasRef && fType.Type() == NULL) {
1178 		// in ref mode, Icon-O-Matic can change the icon directly, and
1179 		// we'll pick it up via node monitoring
1180 		message.what = B_REFS_RECEIVED;
1181 		message.AddRef("refs", &fRef);
1182 	} else {
1183 		// in static or MIME type mode, Icon-O-Matic needs to return the
1184 		// buffer it changed once its done
1185 		message.what = B_EDIT_ICON_DATA;
1186 		message.AddMessenger("reply to", BMessenger(this));
1187 
1188 		::Icon* icon = fIconData;
1189 		if (icon == NULL) {
1190 			icon = new ::Icon();
1191 			if (fHasRef)
1192 				icon->SetTo(fRef, fType.Type());
1193 			else
1194 				icon->SetTo(fType);
1195 		}
1196 
1197 		if (icon->HasData()) {
1198 			uint8* data;
1199 			size_t size;
1200 			if (icon->GetData(&data, &size) == B_OK) {
1201 				message.AddData("icon data", B_VECTOR_ICON_TYPE, data, size);
1202 				free(data);
1203 			}
1204 
1205 			// TODO: somehow figure out how names of objects in the icon
1206 			// can be preserved. Maybe in a second (optional) attribute
1207 			// where ever a vector icon attribute is present?
1208 		}
1209 	}
1210 
1211 	be_roster->Launch("application/x-vnd.haiku-icon_o_matic", &message);
1212 }
1213 
1214 
1215 void
1216 IconView::_SetIcon(BBitmap* large, BBitmap* mini, const uint8* data, size_t size, bool force)
1217 {
1218 	if (fHasRef) {
1219 		BFile file(&fRef, B_READ_WRITE);
1220 
1221 		BAppFileInfo info(&file);
1222 		if (info.InitCheck() == B_OK) {
1223 			if (large != NULL || force)
1224 				info.SetIconForType(fType.Type(), large, B_LARGE_ICON);
1225 			if (mini != NULL || force)
1226 				info.SetIconForType(fType.Type(), mini, B_MINI_ICON);
1227 			if (data != NULL || force)
1228 				info.SetIconForType(fType.Type(), data, size);
1229 		}
1230 		// the icon shown will be updated using node monitoring
1231 	} else if (fHasType) {
1232 		if (large != NULL || force)
1233 			fType.SetIcon(large, B_LARGE_ICON);
1234 		if (mini != NULL || force)
1235 			fType.SetIcon(mini, B_MINI_ICON);
1236 		if (data != NULL || force)
1237 			fType.SetIcon(data, size);
1238 		// the icon shown will be updated automatically - we're watching
1239 		// any changes to the MIME database
1240 	} else if (fIconData != NULL) {
1241 		if (large != NULL || force)
1242 			fIconData->SetLarge(large);
1243 		if (mini != NULL || force)
1244 			fIconData->SetMini(mini);
1245 		if (data != NULL || force)
1246 			fIconData->SetData(data, size);
1247 
1248 		// replace visible icon
1249 		if (fIcon == NULL && fIconData->HasData())
1250 			fIcon = Icon::AllocateBitmap(fIconSize);
1251 
1252 		if (fIconData->GetIcon(fIcon) != B_OK) {
1253 			delete fIcon;
1254 			fIcon = NULL;
1255 		}
1256 		Invalidate();
1257 	}
1258 
1259 	if (fModificationMessage)
1260 		Invoke(fModificationMessage);
1261 }
1262 
1263 
1264 void
1265 IconView::_SetIcon(entry_ref* ref)
1266 {
1267 	// retrieve icons from file
1268 	BFile file(ref, B_READ_ONLY);
1269 	BAppFileInfo info(&file);
1270 	if (file.InitCheck() != B_OK || info.InitCheck() != B_OK)
1271 		return;
1272 
1273 	// try vector/PNG icon first
1274 	uint8* data = NULL;
1275 	size_t size = 0;
1276 	if (info.GetIcon(&data, &size) == B_OK) {
1277 		_SetIcon(NULL, NULL, data, size);
1278 		free(data);
1279 		return;
1280 	}
1281 
1282 	// try large/mini icons
1283 	bool hasMini = false;
1284 	bool hasLarge = false;
1285 
1286 	BBitmap* large = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8);
1287 	if (large->InitCheck() != B_OK) {
1288 		delete large;
1289 		large = NULL;
1290 	}
1291 	BBitmap* mini = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8);
1292 	if (mini->InitCheck() != B_OK) {
1293 		delete mini;
1294 		mini = NULL;
1295 	}
1296 
1297 	if (large != NULL && info.GetIcon(large, B_LARGE_ICON) == B_OK)
1298 		hasLarge = true;
1299 	if (mini != NULL && info.GetIcon(mini, B_MINI_ICON) == B_OK)
1300 		hasMini = true;
1301 
1302 	if (!hasMini && !hasLarge) {
1303 		// TODO: don't forget device icons!
1304 
1305 		// try MIME type icon
1306 		char type[B_MIME_TYPE_LENGTH];
1307 		if (info.GetType(type) != B_OK)
1308 			return;
1309 
1310 		BMimeType mimeType(type);
1311 		if (icon_for_type(mimeType, &data, &size) != B_OK) {
1312 			// only try large/mini icons when there is no vector icon
1313 			if (large != NULL && icon_for_type(mimeType, *large, B_LARGE_ICON) == B_OK)
1314 				hasLarge = true;
1315 			if (mini != NULL && icon_for_type(mimeType, *mini, B_MINI_ICON) == B_OK)
1316 				hasMini = true;
1317 		}
1318 	}
1319 
1320 	if (data != NULL) {
1321 		_SetIcon(NULL, NULL, data, size);
1322 		free(data);
1323 	} else if (hasLarge || hasMini)
1324 		_SetIcon(large, mini, NULL, 0);
1325 
1326 	delete large;
1327 	delete mini;
1328 }
1329 
1330 
1331 void
1332 IconView::_RemoveIcon()
1333 {
1334 	_SetIcon(NULL, NULL, NULL, 0, true);
1335 }
1336 
1337 
1338 void
1339 IconView::_StartWatching()
1340 {
1341 	if (Looper() == NULL) {
1342 		// we are not a valid messenger yet
1343 		return;
1344 	}
1345 
1346 	if (fHasRef) {
1347 		BNode node(&fRef);
1348 		node_ref nodeRef;
1349 		if (node.InitCheck() == B_OK
1350 			&& node.GetNodeRef(&nodeRef) == B_OK)
1351 			watch_node(&nodeRef, B_WATCH_ATTR, this);
1352 	} else if (fHasType)
1353 		BMimeType::StartWatching(this);
1354 }
1355 
1356 
1357 void
1358 IconView::_StopWatching()
1359 {
1360 	if (fHasRef)
1361 		stop_watching(this);
1362 	else if (fHasType)
1363 		BMimeType::StopWatching(this);
1364 }
1365 
1366