xref: /haiku/src/kits/interface/Shelf.cpp (revision 37c7d5d83a2372a6971e383411d5bacbeef0ebdc)
1 /*
2  * Copyright 2001-2009, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Axel Dörfler, axeld@pinc-software.de
8  *		Jérôme Duval
9  *		René Gollent
10  *		Alexandre Deckner, alex@zappotek.com
11  */
12 
13 /*!	BShelf stores replicant views that are dropped onto it */
14 
15 #include <Shelf.h>
16 
17 #include <pthread.h>
18 
19 #include <AutoLock.h>
20 #include <Beep.h>
21 #include <Dragger.h>
22 #include <Entry.h>
23 #include <File.h>
24 #include <Looper.h>
25 #include <Message.h>
26 #include <MessageFilter.h>
27 #include <Messenger.h>
28 #include <Point.h>
29 #include <PropertyInfo.h>
30 #include <Rect.h>
31 #include <String.h>
32 #include <View.h>
33 
34 #include <ViewPrivate.h>
35 
36 #include "ZombieReplicantView.h"
37 
38 #include <stdio.h>
39 #include <string.h>
40 
41 #include <map>
42 #include <utility>
43 
44 
45 namespace {
46 
47 typedef std::map<BString, std::pair<image_id, int32> > LoadedImageMap;
48 
49 struct LoadedImages {
50 	LoadedImageMap			images;
51 
52 	LoadedImages()
53 		:
54 		fLock("BShelf loaded image map")
55 	{
56 	}
57 
58 	bool Lock()
59 	{
60 		return fLock.Lock();
61 	}
62 
63 	void Unlock()
64 	{
65 		fLock.Unlock();
66 	}
67 
68 	static LoadedImages* Default()
69 	{
70 		if (sDefaultInstance == NULL)
71 			pthread_once(&sDefaultInitOnce, &_InitSingleton);
72 
73 		return sDefaultInstance;
74 	}
75 
76 private:
77 	static void _InitSingleton()
78 	{
79 		sDefaultInstance = new LoadedImages;
80 	}
81 
82 private:
83 	BLocker					fLock;
84 
85 	static pthread_once_t	sDefaultInitOnce;
86 	static LoadedImages*	sDefaultInstance;
87 };
88 
89 pthread_once_t LoadedImages::sDefaultInitOnce = PTHREAD_ONCE_INIT;
90 LoadedImages* LoadedImages::sDefaultInstance = NULL;
91 
92 }	// unnamed namespace
93 
94 
95 static property_info sShelfPropertyList[] = {
96 	{
97 		"Replicant",
98 		{ B_COUNT_PROPERTIES, B_CREATE_PROPERTY },
99 		{ B_DIRECT_SPECIFIER },
100 		NULL, 0,
101 	},
102 
103 	{
104 		"Replicant",
105 		{ B_DELETE_PROPERTY, B_GET_PROPERTY },
106 		{ B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, B_NAME_SPECIFIER, B_ID_SPECIFIER },
107 		NULL, 0,
108 	},
109 
110 	{
111 		"Replicant",
112 		{},
113 		{ B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, B_NAME_SPECIFIER, B_ID_SPECIFIER },
114 		"... of Replicant {index | name | id} of ...", 0,
115 	},
116 
117 	{ 0, { 0 }, { 0 }, 0, 0 }
118 };
119 
120 static property_info sReplicantPropertyList[] = {
121 	{
122 		"ID",
123 		{ B_GET_PROPERTY },
124 		{ B_DIRECT_SPECIFIER },
125 		NULL, 0, { B_INT32_TYPE }
126 	},
127 
128 	{
129 		"Name",
130 		{ B_GET_PROPERTY },
131 		{ B_DIRECT_SPECIFIER },
132 		NULL, 0, { B_STRING_TYPE }
133 	},
134 
135 	{
136 		"Signature",
137 		{ B_GET_PROPERTY },
138 		{ B_DIRECT_SPECIFIER },
139 		NULL, 0, { B_STRING_TYPE }
140 	},
141 
142 	{
143 		"Suites",
144 		{ B_GET_PROPERTY },
145 		{ B_DIRECT_SPECIFIER },
146 		NULL, 0, { B_PROPERTY_INFO_TYPE }
147 	},
148 
149 	{
150 		"View",
151 		{ },
152 		{ B_DIRECT_SPECIFIER },
153 		NULL, 0,
154 	},
155 
156 	{ 0, { 0 }, { 0 }, 0, 0 }
157 };
158 
159 
160 namespace BPrivate {
161 
162 struct replicant_data {
163 	replicant_data(BMessage *message, BView *view, BDragger *dragger,
164 		BDragger::relation relation, unsigned long id);
165 	replicant_data();
166 	~replicant_data();
167 
168 	static replicant_data* Find(BList const *list, BMessage const *msg);
169 	static replicant_data* Find(BList const *list, BView const *view, bool allowZombie);
170 	static replicant_data* Find(BList const *list, unsigned long id);
171 
172 	static int32 IndexOf(BList const *list, BMessage const *msg);
173 	static int32 IndexOf(BList const *list, BView const *view, bool allowZombie);
174 	static int32 IndexOf(BList const *list, unsigned long id);
175 
176 	status_t Archive(BMessage *msg);
177 
178 	BMessage*			message;
179 	BView*				view;
180 	BDragger*			dragger;
181 	BDragger::relation	relation;
182 	unsigned long		id;
183 	status_t			error;
184 	BView*				zombie_view;
185 };
186 
187 class ShelfContainerViewFilter : public BMessageFilter {
188 	public:
189 		ShelfContainerViewFilter(BShelf *shelf, BView *view);
190 
191 		filter_result	Filter(BMessage *msg, BHandler **handler);
192 
193 	private:
194 		filter_result	_ObjectDropFilter(BMessage *msg, BHandler **handler);
195 
196 		BShelf	*fShelf;
197 		BView	*fView;
198 };
199 
200 class ReplicantViewFilter : public BMessageFilter {
201 	public:
202 		ReplicantViewFilter(BShelf *shelf, BView *view);
203 
204 		filter_result Filter(BMessage *message, BHandler **handler);
205 
206 	private:
207 		BShelf	*fShelf;
208 		BView	*fView;
209 };
210 
211 }	// namespace BPrivate
212 
213 
214 using BPrivate::replicant_data;
215 using BPrivate::ReplicantViewFilter;
216 using BPrivate::ShelfContainerViewFilter;
217 
218 
219 //	#pragma mark -
220 
221 
222 /*!	\brief Helper function for BShelf::_AddReplicant()
223 */
224 static status_t
225 send_reply(BMessage* message, status_t status, uint32 uniqueID)
226 {
227 	if (message->IsSourceWaiting()) {
228 		BMessage reply(B_REPLY);
229 		reply.AddInt32("id", uniqueID);
230 		reply.AddInt32("error", status);
231 		message->SendReply(&reply);
232 	}
233 
234 	return status;
235 }
236 
237 
238 static bool
239 find_replicant(BList &list, const char *className, const char *addOn)
240 {
241 	int32 i = 0;
242 	replicant_data *item;
243 	while ((item = (replicant_data *)list.ItemAt(i++)) != NULL) {
244 		const char *replicantClassName;
245 		const char *replicantAddOn;
246 		if (item->message->FindString("class", &replicantClassName) == B_OK
247 			&& item->message->FindString("add_on", &replicantAddOn) == B_OK
248 			&& !strcmp(className, replicantClassName)
249 			&& addOn != NULL && replicantAddOn != NULL
250 			&& !strcmp(addOn, replicantAddOn))
251 		return true;
252 	}
253 	return false;
254 }
255 
256 
257 //	#pragma mark -
258 
259 
260 replicant_data::replicant_data(BMessage *_message, BView *_view, BDragger *_dragger,
261 	BDragger::relation _relation, unsigned long _id)
262 	:
263 	message(_message),
264 	view(_view),
265 	dragger(NULL),
266 	relation(_relation),
267 	id(_id),
268 	error(B_OK),
269 	zombie_view(NULL)
270 {
271 }
272 
273 
274 replicant_data::replicant_data()
275 	:
276 	message(NULL),
277 	view(NULL),
278 	dragger(NULL),
279 	relation(BDragger::TARGET_UNKNOWN),
280 	id(0),
281 	error(B_ERROR),
282 	zombie_view(NULL)
283 {
284 }
285 
286 replicant_data::~replicant_data()
287 {
288 	delete message;
289 }
290 
291 status_t
292 replicant_data::Archive(BMessage* msg)
293 {
294 	status_t result = B_ERROR;
295 	BMessage archive;
296 	if (view && (view->Archive(&archive) == B_OK)) {
297 		msg->AddInt32("uniqueid", id);
298 		BPoint pos (0,0);
299 		if (view) {
300 			msg->AddMessage("message", &archive);
301 			pos = view->Frame().LeftTop();
302 		} else if (zombie_view)
303 			pos = zombie_view->Frame().LeftTop();
304 		msg->AddPoint("position", pos);
305 		result = B_OK;
306 	}
307 
308 	return result;
309 }
310 
311 //static
312 replicant_data *
313 replicant_data::Find(BList const *list, BMessage const *msg)
314 {
315 	int32 i = 0;
316 	replicant_data *item;
317 	while ((item = (replicant_data*)list->ItemAt(i++)) != NULL) {
318 		if (item->message == msg)
319 			return item;
320 	}
321 
322 	return NULL;
323 }
324 
325 
326 //static
327 replicant_data *
328 replicant_data::Find(BList const *list, BView const *view, bool allowZombie)
329 {
330 	int32 i = 0;
331 	replicant_data *item;
332 	while ((item = (replicant_data*)list->ItemAt(i++)) != NULL) {
333 		if (item->view == view)
334 			return item;
335 
336 		if (allowZombie && item->zombie_view == view)
337 			return item;
338 	}
339 
340 	return NULL;
341 }
342 
343 
344 //static
345 replicant_data *
346 replicant_data::Find(BList const *list, unsigned long id)
347 {
348 	int32 i = 0;
349 	replicant_data *item;
350 	while ((item = (replicant_data*)list->ItemAt(i++)) != NULL) {
351 		if (item->id == id)
352 			return item;
353 	}
354 
355 	return NULL;
356 }
357 
358 
359 //static
360 int32
361 replicant_data::IndexOf(BList const *list, BMessage const *msg)
362 {
363 	int32 i = 0;
364 	replicant_data *item;
365 	while ((item = (replicant_data*)list->ItemAt(i)) != NULL) {
366 		if (item->message == msg)
367 			return i;
368 		i++;
369 	}
370 
371 	return -1;
372 }
373 
374 
375 //static
376 int32
377 replicant_data::IndexOf(BList const *list, BView const *view, bool allowZombie)
378 {
379 	int32 i = 0;
380 	replicant_data *item;
381 	while ((item = (replicant_data*)list->ItemAt(i)) != NULL) {
382 		if (item->view == view)
383 			return i;
384 
385 		if (allowZombie && item->zombie_view == view)
386 			return i;
387 		i++;
388 	}
389 
390 	return -1;
391 }
392 
393 
394 //static
395 int32
396 replicant_data::IndexOf(BList const *list, unsigned long id)
397 {
398 	int32 i = 0;
399 	replicant_data *item;
400 	while ((item = (replicant_data*)list->ItemAt(i)) != NULL) {
401 		if (item->id == id)
402 			return i;
403 		i++;
404 	}
405 
406 	return -1;
407 }
408 
409 
410 //	#pragma mark -
411 
412 
413 ShelfContainerViewFilter::ShelfContainerViewFilter(BShelf *shelf, BView *view)
414 	: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
415 	fShelf(shelf),
416 	fView(view)
417 {
418 }
419 
420 
421 filter_result
422 ShelfContainerViewFilter::Filter(BMessage *msg, BHandler **handler)
423 {
424 	filter_result filter = B_DISPATCH_MESSAGE;
425 
426 	if (msg->what == B_ARCHIVED_OBJECT
427 		|| msg->what == B_ABOUT_REQUESTED)
428 		return _ObjectDropFilter(msg, handler);
429 
430 	return filter;
431 }
432 
433 
434 filter_result
435 ShelfContainerViewFilter::_ObjectDropFilter(BMessage *msg, BHandler **_handler)
436 {
437 	BView *mouseView = NULL;
438 	if (*_handler)
439 		mouseView = dynamic_cast<BView*>(*_handler);
440 
441 	if (msg->WasDropped()) {
442 		if (!fShelf->fAllowDragging) {
443 			printf("Dragging replicants isn't allowed to this shelf.");
444 			beep();
445 			return B_SKIP_MESSAGE;
446 		}
447 	}
448 
449 	BPoint point;
450 	BPoint offset;
451 
452 	if (msg->WasDropped()) {
453 		point = msg->DropPoint(&offset);
454 		point = mouseView->ConvertFromScreen(point - offset);
455 	}
456 
457 	BLooper *looper = NULL;
458 	BHandler *handler = msg->ReturnAddress().Target(&looper);
459 
460 	if (Looper() == looper) {
461 		BDragger *dragger = NULL;
462 		if (handler)
463 			dragger = dynamic_cast<BDragger*>(handler);
464 
465 		BRect rect;
466 		if (dragger->fRelation == BDragger::TARGET_IS_CHILD)
467 			rect = dragger->Frame();
468 		else
469 			rect = dragger->fTarget->Frame();
470 		rect.OffsetTo(point);
471 		point = rect.LeftTop() + fShelf->AdjustReplicantBy(rect, msg);
472 
473 		if (dragger->fRelation == BDragger::TARGET_IS_PARENT)
474 			dragger->fTarget->MoveTo(point);
475 		else if (dragger->fRelation == BDragger::TARGET_IS_CHILD)
476 			dragger->MoveTo(point);
477 		else {
478 			//TODO: TARGET_UNKNOWN/TARGET_SIBLING
479 		}
480 
481 	} else {
482 		if (fShelf->_AddReplicant(msg, &point, fShelf->fGenCount++) == B_OK)
483 			Looper()->DetachCurrentMessage();
484 	}
485 
486 	return B_SKIP_MESSAGE;
487 }
488 
489 
490 //	#pragma mark -
491 
492 
493 ReplicantViewFilter::ReplicantViewFilter(BShelf *shelf, BView *view)
494 	: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
495 	fShelf(shelf),
496 	fView(view)
497 {
498 }
499 
500 
501 filter_result
502 ReplicantViewFilter::Filter(BMessage *message, BHandler **handler)
503 {
504 	if (message->what == kDeleteReplicant) {
505 		if (handler != NULL)
506 			*handler = fShelf;
507 		message->AddPointer("_target", fView);
508 	}
509 	return B_DISPATCH_MESSAGE;
510 }
511 
512 
513 //	#pragma mark -
514 
515 
516 BShelf::BShelf(BView *view, bool allowDrags, const char *shelfType)
517 	: BHandler(shelfType)
518 {
519 	_InitData(NULL, NULL, view, allowDrags);
520 }
521 
522 
523 BShelf::BShelf(const entry_ref *ref, BView *view, bool allowDrags,
524 	const char *shelfType)
525 	: BHandler(shelfType)
526 {
527 	_InitData(new BEntry(ref), NULL, view, allowDrags);
528 }
529 
530 
531 BShelf::BShelf(BDataIO *stream, BView *view, bool allowDrags,
532 	const char *shelfType)
533 	: BHandler(shelfType)
534 {
535 	_InitData(NULL, stream, view, allowDrags);
536 }
537 
538 
539 BShelf::BShelf(BMessage *data)
540 	: BHandler(data)
541 {
542 	// TODO: Implement ?
543 }
544 
545 
546 BShelf::~BShelf()
547 {
548 	Save();
549 
550 	// we own fStream only when fEntry is set
551 	if (fEntry) {
552 		delete fEntry;
553 		delete fStream;
554 	}
555 
556 	while (fReplicants.CountItems() > 0) {
557 		replicant_data *data = (replicant_data *)fReplicants.ItemAt(0);
558 		fReplicants.RemoveItem(0L);
559 		delete data;
560 	}
561 }
562 
563 
564 status_t
565 BShelf::Archive(BMessage *data, bool deep) const
566 {
567 	return B_ERROR;
568 }
569 
570 
571 BArchivable *
572 BShelf::Instantiate(BMessage *data)
573 {
574 	return NULL;
575 }
576 
577 
578 void
579 BShelf::MessageReceived(BMessage *msg)
580 {
581 	if (msg->what == kDeleteReplicant) {
582 		BHandler *replicant = NULL;
583 		if (msg->FindPointer("_target", (void **)&replicant) == B_OK) {
584 			BView *view = dynamic_cast<BView *>(replicant);
585 			if (view != NULL)
586 				DeleteReplicant(view);
587 		}
588 		return;
589 	}
590 
591 	BMessage replyMsg(B_REPLY);
592 	status_t err = B_BAD_SCRIPT_SYNTAX;
593 
594 	BMessage specifier;
595 	int32 what;
596 	const char *prop;
597 	int32 index;
598 	if (msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK)
599 		return BHandler::MessageReceived(msg);
600 
601 	switch (msg->what) {
602 		case B_DELETE_PROPERTY:
603 		case B_GET_PROPERTY:
604 		case B_GET_SUPPORTED_SUITES:
605 			if (strcmp(prop, "Replicant") == 0) {
606 				BMessage reply;
607 				int32 i;
608 				uint32 ID;
609 				BView *replicant = NULL;
610 				BMessage *repMessage = NULL;
611 				err = _GetProperty(&specifier, &reply);
612 				if (err == B_OK)
613 					err = reply.FindInt32("index", &i);
614 
615 				if (err == B_OK && msg->what == B_DELETE_PROPERTY) { // Delete Replicant
616 					err = DeleteReplicant(i);
617 					break;
618 				}
619 				if (err == B_OK && msg->what == B_GET_SUPPORTED_SUITES) {
620 					err = replyMsg.AddString("suites", "suite/vnd.Be-replicant");
621 					if (err == B_OK) {
622 						BPropertyInfo propInfo(sReplicantPropertyList);
623 						err = replyMsg.AddFlat("messages", &propInfo);
624 					}
625 					break;
626 				}
627 				if (err == B_OK )
628 					repMessage = ReplicantAt(i, &replicant, &ID, &err);
629 				if (err == B_OK && replicant) {
630 					msg->PopSpecifier();
631 					BMessage archive;
632 					err = replicant->Archive(&archive);
633 					if (err == B_OK && msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK) {
634 						err = replyMsg.AddMessage("result", &archive);
635 						break;
636 					}
637 					// now handles the replicant suite
638 					err = B_BAD_SCRIPT_SYNTAX;
639 					if (msg->what != B_GET_PROPERTY)
640 						break;
641 					if (strcmp(prop, "ID") == 0) {
642 						err = replyMsg.AddInt32("result", ID);
643 					} else if (strcmp(prop, "Name") == 0) {
644 						err = replyMsg.AddString("result", replicant->Name());
645 					} else if (strcmp(prop, "Signature") == 0) {
646 						const char *add_on = NULL;
647 						err = repMessage->FindString("add_on", &add_on);
648 						if (err == B_OK)
649 							err = replyMsg.AddString("result", add_on);
650 					} else if (strcmp(prop, "Suites") == 0) {
651 						err = replyMsg.AddString("suites", "suite/vnd.Be-replicant");
652 						if (err == B_OK) {
653 							BPropertyInfo propInfo(sReplicantPropertyList);
654 							err = replyMsg.AddFlat("messages", &propInfo);
655 						}
656 					}
657 				}
658 				break;
659 			}
660 			return BHandler::MessageReceived(msg);
661 
662 		case B_COUNT_PROPERTIES:
663 			if (strcmp(prop, "Replicant") == 0) {
664 				err = replyMsg.AddInt32("result", CountReplicants());
665 				break;
666 			}
667 			return BHandler::MessageReceived(msg);
668 
669 		case B_CREATE_PROPERTY:
670 		{
671 			BMessage replicantMsg;
672 			BPoint pos;
673 			if (msg->FindMessage("data", &replicantMsg) == B_OK
674 				&& msg->FindPoint("location", &pos) == B_OK) {
675 					err = AddReplicant(&replicantMsg, pos);
676 			}
677 		}
678 		break;
679 	}
680 
681 	if (err < B_OK) {
682 		replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;
683 
684 		if (err == B_BAD_SCRIPT_SYNTAX)
685 			replyMsg.AddString("message", "Didn't understand the specifier(s)");
686 		else
687 			replyMsg.AddString("message", strerror(err));
688 	}
689 
690 	replyMsg.AddInt32("error", err);
691 	msg->SendReply(&replyMsg);
692 }
693 
694 
695 status_t
696 BShelf::Save()
697 {
698 	status_t status = B_ERROR;
699 	if (fEntry != NULL) {
700 		BFile *file = new BFile(fEntry, B_READ_WRITE | B_ERASE_FILE);
701 		status = file->InitCheck();
702 		if (status < B_OK) {
703 			delete file;
704 			return status;
705 		}
706 		fStream = file;
707 	}
708 
709 	if (fStream != NULL) {
710 		BMessage message;
711 		status = _Archive(&message);
712 		if (status == B_OK)
713 			status = message.Flatten(fStream);
714 	}
715 
716 	return status;
717 }
718 
719 
720 void
721 BShelf::SetDirty(bool state)
722 {
723 	fDirty = state;
724 }
725 
726 
727 bool
728 BShelf::IsDirty() const
729 {
730 	return fDirty;
731 }
732 
733 
734 BHandler *
735 BShelf::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
736 						int32 form, const char *property)
737 {
738 	BPropertyInfo shelfPropInfo(sShelfPropertyList);
739 	BHandler *target = NULL;
740 	BView *replicant = NULL;
741 
742 	switch (shelfPropInfo.FindMatch(msg, 0, specifier, form, property)) {
743 		case 0:
744 			target = this;
745 			break;
746 		case 1:
747 			if (msg->PopSpecifier() != B_OK) {
748 				target = this;
749 				break;
750 			}
751 			msg->SetCurrentSpecifier(index);
752 			// fall through
753 		case 2: {
754 			BMessage reply;
755 			status_t err = _GetProperty(specifier, &reply);
756 			int32 i;
757 			uint32 ID;
758 			if (err == B_OK)
759 				err = reply.FindInt32("index", &i);
760 			if (err == B_OK)
761 				ReplicantAt(i, &replicant, &ID, &err);
762 
763 			if (err == B_OK && replicant != NULL) {
764 				if (index == 0)
765 					return this;
766 			} else {
767 				BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
768 				replyMsg.AddInt32("error", B_BAD_INDEX);
769 				replyMsg.AddString("message", "Cannot find replicant at/with specified index/name.");
770 				msg->SendReply(&replyMsg);
771 			}
772 			}
773 			msg->PopSpecifier();
774 			break;
775 	}
776 
777 	if (!replicant) {
778 		if (target)
779 			return target;
780 		return BHandler::ResolveSpecifier(msg, index, specifier, form,
781 			property);
782 	}
783 
784 	int32 repIndex;
785 	status_t err = msg->GetCurrentSpecifier(&repIndex, specifier, &form, &property);
786 	if (err) {
787 		BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
788 		reply.AddInt32("error", err);
789 		msg->SendReply(&reply);
790 		return NULL;
791 	}
792 
793 	BPropertyInfo replicantPropInfo(sReplicantPropertyList);
794 	switch (replicantPropInfo.FindMatch(msg, 0, specifier, form, property)) {
795 		case 0:
796 		case 1:
797 		case 2:
798 		case 3:
799 			msg->SetCurrentSpecifier(index);
800 			target = this;
801 			break;
802 		case 4:
803 			target = replicant;
804 			msg->PopSpecifier();
805 			break;
806 		default:
807 			break;
808 	}
809 	if (!target) {
810 		BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
811 		replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX);
812 		replyMsg.AddString("message", "Didn't understand the specifier(s)");
813 		msg->SendReply(&replyMsg);
814 	}
815 	return target;
816 }
817 
818 
819 status_t
820 BShelf::GetSupportedSuites(BMessage *message)
821 {
822 	status_t err;
823 	err = message->AddString("suites", "suite/vnd.Be-shelf");
824 	if (err == B_OK) {
825 		BPropertyInfo propInfo(sShelfPropertyList);
826 		err = message->AddFlat("messages", &propInfo);
827 	}
828 	if (err == B_OK)
829 		return BHandler::GetSupportedSuites(message);
830 	return err;
831 }
832 
833 
834 status_t
835 BShelf::Perform(perform_code d, void *arg)
836 {
837 	return BHandler::Perform(d, arg);
838 }
839 
840 
841 bool
842 BShelf::AllowsDragging() const
843 {
844 	return fAllowDragging;
845 }
846 
847 
848 void
849 BShelf::SetAllowsDragging(bool state)
850 {
851 	fAllowDragging = state;
852 }
853 
854 
855 bool
856 BShelf::AllowsZombies() const
857 {
858 	return fAllowZombies;
859 }
860 
861 
862 void
863 BShelf::SetAllowsZombies(bool state)
864 {
865 	fAllowZombies = state;
866 }
867 
868 
869 bool
870 BShelf::DisplaysZombies() const
871 {
872 	return fDisplayZombies;
873 }
874 
875 
876 void
877 BShelf::SetDisplaysZombies(bool state)
878 {
879 	fDisplayZombies = state;
880 }
881 
882 
883 bool
884 BShelf::IsTypeEnforced() const
885 {
886 	return fTypeEnforced;
887 }
888 
889 
890 void
891 BShelf::SetTypeEnforced(bool state)
892 {
893 	fTypeEnforced = state;
894 }
895 
896 
897 status_t
898 BShelf::SetSaveLocation(BDataIO *data_io)
899 {
900 	fDirty = true;
901 
902 	if (fEntry) {
903 		delete fEntry;
904 		fEntry = NULL;
905 	}
906 
907 	fStream = data_io;
908 
909 	return B_OK;
910 }
911 
912 
913 status_t
914 BShelf::SetSaveLocation(const entry_ref *ref)
915 {
916 	fDirty = true;
917 
918 	if (fEntry)
919 		delete fEntry;
920 	else
921 		fStream = NULL;
922 
923 	fEntry = new BEntry(ref);
924 
925 	return B_OK;
926 }
927 
928 
929 BDataIO *
930 BShelf::SaveLocation(entry_ref *ref) const
931 {
932 	entry_ref entry;
933 
934 	if (fStream && ref) {
935 		*ref = entry;
936 		return fStream;
937 	}
938 	if (fEntry) {
939 		fEntry->GetRef(&entry);
940 
941 		if (ref)
942 			*ref = entry;
943 
944 		return NULL;
945 	}
946 
947 	*ref = entry;
948 	return NULL;
949 }
950 
951 
952 status_t
953 BShelf::AddReplicant(BMessage *data, BPoint location)
954 {
955 	return _AddReplicant(data, &location, fGenCount++);
956 }
957 
958 
959 status_t
960 BShelf::DeleteReplicant(BView *replicant)
961 {
962 	int32 index = replicant_data::IndexOf(&fReplicants, replicant, true);
963 
964 	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
965 	if (item == NULL)
966 		return B_BAD_VALUE;
967 
968 	return _DeleteReplicant(item);
969 }
970 
971 
972 status_t
973 BShelf::DeleteReplicant(BMessage *data)
974 {
975 	int32 index = replicant_data::IndexOf(&fReplicants, data);
976 
977 	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
978 	if (!item)
979 		return B_BAD_VALUE;
980 
981 	return _DeleteReplicant(item);
982 }
983 
984 
985 status_t
986 BShelf::DeleteReplicant(int32 index)
987 {
988 	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
989 	if (!item)
990 		return B_BAD_INDEX;
991 
992 	return _DeleteReplicant(item);
993 }
994 
995 
996 int32
997 BShelf::CountReplicants() const
998 {
999 	return fReplicants.CountItems();
1000 }
1001 
1002 
1003 BMessage *
1004 BShelf::ReplicantAt(int32 index, BView **_view, uint32 *_uniqueID,
1005 	status_t *_error) const
1006 {
1007 	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
1008 	if (item == NULL) {
1009 		// no replicant found
1010 		if (_view)
1011 			*_view = NULL;
1012 		if (_uniqueID)
1013 			*_uniqueID = ~0UL;
1014 		if (_error)
1015 			*_error = B_BAD_INDEX;
1016 
1017 		return NULL;
1018 	}
1019 
1020 	if (_view)
1021 		*_view = item->view;
1022 	if (_uniqueID)
1023 		*_uniqueID = item->id;
1024 	if (_error)
1025 		*_error = item->error;
1026 
1027 	return item->message;
1028 }
1029 
1030 
1031 int32
1032 BShelf::IndexOf(const BView* replicantView) const
1033 {
1034 	return replicant_data::IndexOf(&fReplicants, replicantView, false);
1035 }
1036 
1037 
1038 int32
1039 BShelf::IndexOf(const BMessage *archive) const
1040 {
1041 	return replicant_data::IndexOf(&fReplicants, archive);
1042 }
1043 
1044 
1045 int32
1046 BShelf::IndexOf(uint32 id) const
1047 {
1048 	return replicant_data::IndexOf(&fReplicants, id);
1049 }
1050 
1051 
1052 bool
1053 BShelf::CanAcceptReplicantMessage(BMessage*) const
1054 {
1055 	return true;
1056 }
1057 
1058 
1059 bool
1060 BShelf::CanAcceptReplicantView(BRect, BView*, BMessage*) const
1061 {
1062 	return true;
1063 }
1064 
1065 
1066 BPoint
1067 BShelf::AdjustReplicantBy(BRect, BMessage*) const
1068 {
1069 	return B_ORIGIN;
1070 }
1071 
1072 
1073 void
1074 BShelf::ReplicantDeleted(int32 index, const BMessage *archive,
1075 	const BView *replicant)
1076 {
1077 }
1078 
1079 
1080 extern "C" void
1081 _ReservedShelf1__6BShelfFv(BShelf *const, int32, const BMessage*, const BView*)
1082 {
1083 	// is not contained in BeOS R5's libbe, so we leave it empty
1084 }
1085 
1086 
1087 void BShelf::_ReservedShelf2() {}
1088 void BShelf::_ReservedShelf3() {}
1089 void BShelf::_ReservedShelf4() {}
1090 void BShelf::_ReservedShelf5() {}
1091 void BShelf::_ReservedShelf6() {}
1092 void BShelf::_ReservedShelf7() {}
1093 void BShelf::_ReservedShelf8() {}
1094 
1095 
1096 BShelf::BShelf(const BShelf&)
1097 {
1098 }
1099 
1100 
1101 BShelf &
1102 BShelf::operator=(const BShelf &)
1103 {
1104 	return *this;
1105 }
1106 
1107 
1108 status_t
1109 BShelf::_Archive(BMessage *data) const
1110 {
1111 	status_t status = BHandler::Archive(data);
1112 	if (status != B_OK)
1113 		return status;
1114 
1115 	status = data->AddBool("_zom_dsp", DisplaysZombies());
1116 	if (status != B_OK)
1117 		return status;
1118 
1119 	status = data->AddBool("_zom_alw", AllowsZombies());
1120 	if (status != B_OK)
1121 		return status;
1122 
1123 	status = data->AddInt32("_sg_cnt", fGenCount);
1124 	if (status != B_OK)
1125 		return status;
1126 
1127 	BMessage archive('ARCV');
1128 	for (int32 i = 0; i < fReplicants.CountItems(); i++) {
1129 		if (((replicant_data *)fReplicants.ItemAt(i))->Archive(&archive) == B_OK)
1130 			status = data->AddMessage("replicant", &archive);
1131 		if (status != B_OK)
1132 			break;
1133 		archive.MakeEmpty();
1134 	}
1135 
1136 	return status;
1137 }
1138 
1139 
1140 void
1141 BShelf::_InitData(BEntry *entry, BDataIO *stream, BView *view,
1142 	bool allowDrags)
1143 {
1144 	fContainerView = view;
1145 	fStream = NULL;
1146 	fEntry = entry;
1147 	fFilter = NULL;
1148 	fGenCount = 1;
1149 	fAllowDragging = allowDrags;
1150 	fDirty = true;
1151 	fDisplayZombies = false;
1152 	fAllowZombies = true;
1153 	fTypeEnforced = false;
1154 
1155 	if (entry)
1156 		fStream = new BFile(entry, B_READ_ONLY);
1157 	else
1158 		fStream = stream;
1159 
1160 	fFilter = new ShelfContainerViewFilter(this, fContainerView);
1161 
1162 	fContainerView->AddFilter(fFilter);
1163 	fContainerView->_SetShelf(this);
1164 
1165 	if (fStream) {
1166 		BMessage archive;
1167 
1168 		if (archive.Unflatten(fStream) == B_OK) {
1169 			bool allowZombies;
1170 			if (archive.FindBool("_zom_dsp", &allowZombies) != B_OK)
1171 				allowZombies = false;
1172 
1173 			SetDisplaysZombies(allowZombies);
1174 
1175 			if (archive.FindBool("_zom_alw", &allowZombies) != B_OK)
1176 				allowZombies = true;
1177 
1178 			SetAllowsZombies(allowZombies);
1179 
1180 			int32 genCount;
1181 			if (!archive.FindInt32("_sg_cnt", &genCount))
1182 				genCount = 1;
1183 
1184 			BMessage replicant;
1185 			BMessage *replmsg = NULL;
1186 			for (int32 i = 0; archive.FindMessage("replicant", i, &replicant) == B_OK; i++) {
1187 				BPoint point;
1188 				replmsg = new BMessage();
1189 				replicant.FindPoint("position", &point);
1190 				replicant.FindMessage("message", replmsg);
1191 				AddReplicant(replmsg, point);
1192 			}
1193 		}
1194 	}
1195 }
1196 
1197 
1198 status_t
1199 BShelf::_DeleteReplicant(replicant_data* item)
1200 {
1201 	BView *view = item->view;
1202 	if (view == NULL)
1203 		view = item->zombie_view;
1204 
1205 	if (view)
1206 		view->RemoveSelf();
1207 
1208 	if (item->dragger)
1209 		item->dragger->RemoveSelf();
1210 
1211 	int32 index = replicant_data::IndexOf(&fReplicants, item->message);
1212 
1213 	ReplicantDeleted(index, item->message, view);
1214 
1215 	fReplicants.RemoveItem(item);
1216 
1217 	if (item->relation == BDragger::TARGET_IS_PARENT
1218 		|| item->relation == BDragger::TARGET_IS_SIBLING) {
1219 		delete view;
1220 	}
1221 	if (item->relation == BDragger::TARGET_IS_CHILD
1222 		|| item->relation == BDragger::TARGET_IS_SIBLING) {
1223 		delete item->dragger;
1224 	}
1225 
1226 	// Update use count for image and unload if necessary
1227 	const char* signature = NULL;
1228 	if (item->message->FindString("add_on", &signature) == B_OK
1229 		&& signature != NULL) {
1230 		LoadedImages* loadedImages = LoadedImages::Default();
1231 		AutoLock<LoadedImages> lock(loadedImages);
1232 		if (lock.IsLocked()) {
1233 			LoadedImageMap::iterator it = loadedImages->images.find(
1234 				BString(signature));
1235 
1236 			if (it != loadedImages->images.end()) {
1237 				(*it).second.second--;
1238 				if ((*it).second.second <= 0) {
1239 					unload_add_on((*it).second.first);
1240 					loadedImages->images.erase(it);
1241 				}
1242 			}
1243 		}
1244 	}
1245 
1246 	delete item;
1247 
1248 	return B_OK;
1249 }
1250 
1251 
1252 //! Takes over ownership of \a data on success only
1253 status_t
1254 BShelf::_AddReplicant(BMessage *data, BPoint *location, uint32 uniqueID)
1255 {
1256 	// Check shelf types if needed
1257 	if (fTypeEnforced) {
1258 		const char *shelfType = NULL;
1259 		if (data->FindString("shelf_type", &shelfType) == B_OK
1260 			&& shelfType != NULL) {
1261 			if (Name() && strcmp(shelfType, Name()) != 0) {
1262 				printf("Replicant was rejected by BShelf: The BShelf's type and the Replicant's type don't match.");
1263 				return send_reply(data, B_ERROR, uniqueID);
1264 			} else {
1265 				printf("Replicant was rejected by BShelf: Replicant indicated a <type> (%s), but the shelf does not.", shelfType);
1266 				return send_reply(data, B_ERROR, uniqueID);
1267 			}
1268 		} else {
1269 			printf("Replicant was rejected by BShelf: Replicant did not have a <type>");
1270 			return send_reply(data, B_ERROR, uniqueID);
1271 		}
1272 	}
1273 
1274 	// Check if we can accept this message
1275 	if (!CanAcceptReplicantMessage(data)) {
1276 		printf("Replicant was rejected by BShelf::CanAcceptReplicantMessage()");
1277 		return send_reply(data, B_ERROR, uniqueID);
1278 	}
1279 
1280 	// Check if we can create multiple instances
1281 	if (data->FindBool("be:load_each_time")) {
1282 		const char *className = NULL;
1283 		const char *addOn = NULL;
1284 
1285 		if (data->FindString("class", &className) == B_OK
1286 			&& data->FindString("add_on", &addOn) == B_OK) {
1287 			if (find_replicant(fReplicants, className, addOn)) {
1288 				printf("Replicant was rejected. Unique replicant already exists. class=%s, signature=%s",
1289 					className, addOn);
1290 				return send_reply(data, B_ERROR, uniqueID);
1291 			}
1292 		}
1293 	}
1294 
1295 	// Instantiate the object, if this fails we have a zombie
1296 	image_id image = -1;
1297 	BArchivable *archivable = _InstantiateObject(data, &image);
1298 
1299 	if (archivable == NULL)
1300 		return send_reply(data, B_ERROR, uniqueID);
1301 
1302 	BView *view = dynamic_cast<BView*>(archivable);
1303 	if (view == NULL) {
1304 		printf("Replicant was rejected: it's not a view!");
1305 		return send_reply(data, B_ERROR, uniqueID);
1306 	}
1307 
1308 	BDragger* dragger = NULL;
1309 	BView* replicant = NULL;
1310 	BDragger::relation relation = BDragger::TARGET_UNKNOWN;
1311 	_BZombieReplicantView_* zombie = NULL;
1312 	if (view != NULL) {
1313 		const BPoint point = location ? *location : view->Frame().LeftTop();
1314 		replicant = _GetReplicant(data, view, point, dragger, relation);
1315 		if (replicant == NULL)
1316 			return send_reply(data, B_ERROR, uniqueID);
1317 	} else if (fDisplayZombies && fAllowZombies)
1318 		zombie = _CreateZombie(data, dragger);
1319 
1320 	else if (!fAllowZombies) {
1321 		// There was no view, and we're not allowed to have any zombies
1322 		// in the house
1323 		return send_reply(data, B_ERROR, uniqueID);
1324 	}
1325 
1326 	// Update use count for image
1327 	const char* signature = NULL;
1328 	if (data->FindString("add_on", &signature) == B_OK && signature != NULL) {
1329 		LoadedImages* loadedImages = LoadedImages::Default();
1330 		AutoLock<LoadedImages> lock(loadedImages);
1331 		if (lock.IsLocked()) {
1332 			LoadedImageMap::iterator it = loadedImages->images.find(
1333 				BString(signature));
1334 
1335 			if (it == loadedImages->images.end())
1336 				loadedImages->images.insert(LoadedImageMap::value_type(
1337 					BString(signature), std::pair<image_id, int>(image, 1)));
1338 			else
1339 				(*it).second.second++;
1340 		}
1341 	}
1342 
1343 	data->RemoveName("_drop_point_");
1344 	data->RemoveName("_drop_offset_");
1345 
1346 	replicant_data *item = new replicant_data(data, replicant, dragger,
1347 		relation, uniqueID);
1348 
1349 	item->error = B_OK;
1350 	item->zombie_view = zombie;
1351 
1352 	fReplicants.AddItem(item);
1353 
1354 	return send_reply(data, B_OK, uniqueID);
1355 }
1356 
1357 
1358 BView *
1359 BShelf::_GetReplicant(BMessage *data, BView *view, const BPoint &point,
1360 		BDragger *&dragger, BDragger::relation &relation)
1361 {
1362 	// TODO: test me -- there seems to be lots of bugs parked here!
1363 	BView *replicant = NULL;
1364 	_GetReplicantData(data, view, replicant, dragger, relation);
1365 
1366 	if (dragger != NULL)
1367 		dragger->_SetViewToDrag(replicant);
1368 
1369 	BRect frame = view->Frame().OffsetToCopy(point);
1370 	if (!CanAcceptReplicantView(frame, replicant, data)) {
1371 		// the view has not been accepted
1372 		if (relation == BDragger::TARGET_IS_PARENT
1373 			|| relation == BDragger::TARGET_IS_SIBLING) {
1374 			delete replicant;
1375 		}
1376 		if (relation == BDragger::TARGET_IS_CHILD
1377 			|| relation == BDragger::TARGET_IS_SIBLING) {
1378 			delete dragger;
1379 		}
1380 		return NULL;
1381 	}
1382 
1383 	BPoint adjust = AdjustReplicantBy(frame, data);
1384 
1385 	if (dragger != NULL)
1386 		dragger->_SetShelf(this);
1387 
1388 	// TODO: could be not correct for some relations
1389 	view->MoveTo(point + adjust);
1390 
1391 	// if it's a sibling or a child, we need to add the dragger
1392 	if (relation == BDragger::TARGET_IS_SIBLING
1393 		|| relation == BDragger::TARGET_IS_CHILD)
1394 		fContainerView->AddChild(dragger);
1395 
1396 	if (relation != BDragger::TARGET_IS_CHILD)
1397 		fContainerView->AddChild(replicant);
1398 
1399 	replicant->AddFilter(new ReplicantViewFilter(this, replicant));
1400 
1401 	return replicant;
1402 }
1403 
1404 
1405 /* static */
1406 void
1407 BShelf::_GetReplicantData(BMessage *data, BView *view, BView *&replicant,
1408 			BDragger *&dragger, BDragger::relation &relation)
1409 {
1410 	// Check if we have a dragger archived as "__widget" inside the message
1411 	BMessage widget;
1412 	if (data->FindMessage("__widget", &widget) == B_OK) {
1413 		image_id draggerImage = B_ERROR;
1414 		replicant = view;
1415 		dragger = dynamic_cast<BDragger*>(_InstantiateObject(&widget, &draggerImage));
1416 		// Replicant is a sibling, or unknown, if there isn't a dragger
1417 		if (dragger != NULL)
1418 			relation = BDragger::TARGET_IS_SIBLING;
1419 
1420 	} else if ((dragger = dynamic_cast<BDragger*>(view)) != NULL) {
1421 		// Replicant is child of the dragger
1422 		relation = BDragger::TARGET_IS_CHILD;
1423 		replicant = dragger->ChildAt(0);
1424 
1425 	} else {
1426 		// Replicant is parent of the dragger
1427 		relation = BDragger::TARGET_IS_PARENT;
1428 		replicant = view;
1429 		dragger = dynamic_cast<BDragger *>(replicant->FindView("_dragger_"));
1430 			// can be NULL, the replicant could not have a dragger at all
1431 	}
1432 }
1433 
1434 
1435 _BZombieReplicantView_ *
1436 BShelf::_CreateZombie(BMessage *data, BDragger *&dragger)
1437 {
1438 	// TODO: the zombies must be adjusted and moved as well!
1439 	BRect frame;
1440 	if (data->FindRect("_frame", &frame) != B_OK)
1441 		frame = BRect();
1442 
1443 	_BZombieReplicantView_ *zombie = NULL;
1444 	if (data->WasDropped()) {
1445 		BPoint offset;
1446 		BPoint dropPoint = data->DropPoint(&offset);
1447 
1448 		frame.OffsetTo(fContainerView->ConvertFromScreen(dropPoint) - offset);
1449 
1450 		zombie = new _BZombieReplicantView_(frame, B_ERROR);
1451 
1452 		frame.OffsetTo(B_ORIGIN);
1453 
1454 		dragger = new BDragger(frame, zombie);
1455 		dragger->_SetShelf(this);
1456 		dragger->_SetZombied(true);
1457 
1458 		zombie->AddChild(dragger);
1459 		zombie->SetArchive(data);
1460 		zombie->AddFilter(new ReplicantViewFilter(this, zombie));
1461 
1462 		fContainerView->AddChild(zombie);
1463 	}
1464 
1465 	return zombie;
1466 }
1467 
1468 
1469 status_t
1470 BShelf::_GetProperty(BMessage *msg, BMessage *reply)
1471 {
1472 	uint32 ID;
1473 	status_t err = B_ERROR;
1474 	BView *replicant = NULL;
1475 	switch (msg->what) {
1476 		case B_INDEX_SPECIFIER:	{
1477 			int32 index = -1;
1478 			if (msg->FindInt32("index", &index)!=B_OK)
1479 				break;
1480 			ReplicantAt(index, &replicant, &ID, &err);
1481 			break;
1482 		}
1483 		case B_REVERSE_INDEX_SPECIFIER:	{
1484 			int32 rindex;
1485 			if (msg->FindInt32("index", &rindex) != B_OK)
1486 				break;
1487 			ReplicantAt(CountReplicants() - rindex, &replicant, &ID, &err);
1488 			break;
1489 		}
1490 		case B_NAME_SPECIFIER: {
1491 			const char *name;
1492 			if (msg->FindString("name", &name) != B_OK)
1493 				break;
1494 			for (int32 i = 0; i < CountReplicants(); i++) {
1495 				BView *view = NULL;
1496 				ReplicantAt(i, &view, &ID, &err);
1497 				if (err == B_OK) {
1498 					if (view->Name() != NULL &&
1499 						strlen(view->Name()) == strlen(name) && !strcmp(view->Name(), name)) {
1500 						replicant = view;
1501 						break;
1502 					}
1503 					err = B_NAME_NOT_FOUND;
1504 				}
1505 			}
1506 			break;
1507 		}
1508 		case B_ID_SPECIFIER: {
1509 			uint32 id;
1510 			if (msg->FindInt32("id", (int32 *)&id) != B_OK)
1511 				break;
1512 			for (int32 i = 0; i < CountReplicants(); i++) {
1513 				BView *view = NULL;
1514 				ReplicantAt(i, &view, &ID, &err);
1515 				if (err == B_OK) {
1516 					if (ID == id) {
1517 						replicant = view;
1518 						break;
1519 					}
1520 					err = B_NAME_NOT_FOUND;
1521 				}
1522 			}
1523 			break;
1524 		}
1525 		default:
1526 			break;
1527 	}
1528 
1529 	if (replicant) {
1530 		reply->AddInt32("index", IndexOf(replicant));
1531 		reply->AddInt32("ID", ID);
1532 	}
1533 
1534 	return err;
1535 }
1536 
1537 
1538 /* static */
1539 BArchivable *
1540 BShelf::_InstantiateObject(BMessage *archive, image_id *image)
1541 {
1542 	// Stay on the safe side. The constructor called by instantiate_object
1543 	// could throw an exception, which we catch here. Otherwise our calling app
1544 	// could die without notice.
1545 	try {
1546 		return instantiate_object(archive, image);
1547 	} catch (...) {
1548 		return NULL;
1549 	}
1550 }
1551 
1552