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