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