xref: /haiku/src/kits/interface/Shelf.cpp (revision a906d0a031e721e2f2ec9d95274103e74a3a774f)
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) {
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(0L);
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 		fStream = file;
709 	}
710 
711 	if (fStream != NULL) {
712 		BMessage message;
713 		status = _Archive(&message);
714 		if (status == B_OK)
715 			status = message.Flatten(fStream);
716 	}
717 
718 	return status;
719 }
720 
721 
722 void
723 BShelf::SetDirty(bool state)
724 {
725 	fDirty = state;
726 }
727 
728 
729 bool
730 BShelf::IsDirty() const
731 {
732 	return fDirty;
733 }
734 
735 
736 BHandler *
737 BShelf::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
738 						int32 form, const char *property)
739 {
740 	BPropertyInfo shelfPropInfo(sShelfPropertyList);
741 	BHandler *target = NULL;
742 	BView *replicant = NULL;
743 
744 	switch (shelfPropInfo.FindMatch(msg, 0, specifier, form, property)) {
745 		case 0:
746 			target = this;
747 			break;
748 		case 1:
749 			if (msg->PopSpecifier() != B_OK) {
750 				target = this;
751 				break;
752 			}
753 			msg->SetCurrentSpecifier(index);
754 			// fall through
755 		case 2: {
756 			BMessage reply;
757 			status_t err = _GetProperty(specifier, &reply);
758 			int32 i;
759 			uint32 ID;
760 			if (err == B_OK)
761 				err = reply.FindInt32("index", &i);
762 			if (err == B_OK)
763 				ReplicantAt(i, &replicant, &ID, &err);
764 
765 			if (err == B_OK && replicant != NULL) {
766 				if (index == 0)
767 					return this;
768 			} else {
769 				BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
770 				replyMsg.AddInt32("error", B_BAD_INDEX);
771 				replyMsg.AddString("message", "Cannot find replicant at/with specified index/name.");
772 				msg->SendReply(&replyMsg);
773 			}
774 			}
775 			msg->PopSpecifier();
776 			break;
777 	}
778 
779 	if (!replicant) {
780 		if (target)
781 			return target;
782 		return BHandler::ResolveSpecifier(msg, index, specifier, form,
783 			property);
784 	}
785 
786 	int32 repIndex;
787 	status_t err = msg->GetCurrentSpecifier(&repIndex, specifier, &form, &property);
788 	if (err) {
789 		BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
790 		reply.AddInt32("error", err);
791 		msg->SendReply(&reply);
792 		return NULL;
793 	}
794 
795 	BPropertyInfo replicantPropInfo(sReplicantPropertyList);
796 	switch (replicantPropInfo.FindMatch(msg, 0, specifier, form, property)) {
797 		case 0:
798 		case 1:
799 		case 2:
800 		case 3:
801 			msg->SetCurrentSpecifier(index);
802 			target = this;
803 			break;
804 		case 4:
805 			target = replicant;
806 			msg->PopSpecifier();
807 			break;
808 		default:
809 			break;
810 	}
811 	if (!target) {
812 		BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
813 		replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX);
814 		replyMsg.AddString("message", "Didn't understand the specifier(s)");
815 		msg->SendReply(&replyMsg);
816 	}
817 	return target;
818 }
819 
820 
821 status_t
822 BShelf::GetSupportedSuites(BMessage *message)
823 {
824 	status_t err;
825 	err = message->AddString("suites", "suite/vnd.Be-shelf");
826 	if (err == B_OK) {
827 		BPropertyInfo propInfo(sShelfPropertyList);
828 		err = message->AddFlat("messages", &propInfo);
829 	}
830 	if (err == B_OK)
831 		return BHandler::GetSupportedSuites(message);
832 	return err;
833 }
834 
835 
836 status_t
837 BShelf::Perform(perform_code d, void *arg)
838 {
839 	return BHandler::Perform(d, arg);
840 }
841 
842 
843 bool
844 BShelf::AllowsDragging() const
845 {
846 	return fAllowDragging;
847 }
848 
849 
850 void
851 BShelf::SetAllowsDragging(bool state)
852 {
853 	fAllowDragging = state;
854 }
855 
856 
857 bool
858 BShelf::AllowsZombies() const
859 {
860 	return fAllowZombies;
861 }
862 
863 
864 void
865 BShelf::SetAllowsZombies(bool state)
866 {
867 	fAllowZombies = state;
868 }
869 
870 
871 bool
872 BShelf::DisplaysZombies() const
873 {
874 	return fDisplayZombies;
875 }
876 
877 
878 void
879 BShelf::SetDisplaysZombies(bool state)
880 {
881 	fDisplayZombies = state;
882 }
883 
884 
885 bool
886 BShelf::IsTypeEnforced() const
887 {
888 	return fTypeEnforced;
889 }
890 
891 
892 void
893 BShelf::SetTypeEnforced(bool state)
894 {
895 	fTypeEnforced = state;
896 }
897 
898 
899 status_t
900 BShelf::SetSaveLocation(BDataIO *data_io)
901 {
902 	fDirty = true;
903 
904 	if (fEntry) {
905 		delete fEntry;
906 		fEntry = NULL;
907 	}
908 
909 	fStream = data_io;
910 
911 	return B_OK;
912 }
913 
914 
915 status_t
916 BShelf::SetSaveLocation(const entry_ref *ref)
917 {
918 	fDirty = true;
919 
920 	if (fEntry)
921 		delete fEntry;
922 	else
923 		fStream = NULL;
924 
925 	fEntry = new BEntry(ref);
926 
927 	return B_OK;
928 }
929 
930 
931 BDataIO *
932 BShelf::SaveLocation(entry_ref *ref) const
933 {
934 	if (fStream) {
935 		if (ref)
936 			*ref = entry_ref();
937 		return fStream;
938 	} else if (fEntry && ref)
939 		fEntry->GetRef(ref);
940 
941 	return NULL;
942 }
943 
944 
945 status_t
946 BShelf::AddReplicant(BMessage *data, BPoint location)
947 {
948 	return _AddReplicant(data, &location, fGenCount++);
949 }
950 
951 
952 status_t
953 BShelf::DeleteReplicant(BView *replicant)
954 {
955 	int32 index = replicant_data::IndexOf(&fReplicants, replicant, true);
956 
957 	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
958 	if (item == NULL)
959 		return B_BAD_VALUE;
960 
961 	return _DeleteReplicant(item);
962 }
963 
964 
965 status_t
966 BShelf::DeleteReplicant(BMessage *data)
967 {
968 	int32 index = replicant_data::IndexOf(&fReplicants, data);
969 
970 	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
971 	if (!item)
972 		return B_BAD_VALUE;
973 
974 	return _DeleteReplicant(item);
975 }
976 
977 
978 status_t
979 BShelf::DeleteReplicant(int32 index)
980 {
981 	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
982 	if (!item)
983 		return B_BAD_INDEX;
984 
985 	return _DeleteReplicant(item);
986 }
987 
988 
989 int32
990 BShelf::CountReplicants() const
991 {
992 	return fReplicants.CountItems();
993 }
994 
995 
996 BMessage *
997 BShelf::ReplicantAt(int32 index, BView **_view, uint32 *_uniqueID,
998 	status_t *_error) const
999 {
1000 	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
1001 	if (item == NULL) {
1002 		// no replicant found
1003 		if (_view)
1004 			*_view = NULL;
1005 		if (_uniqueID)
1006 			*_uniqueID = ~0UL;
1007 		if (_error)
1008 			*_error = B_BAD_INDEX;
1009 
1010 		return NULL;
1011 	}
1012 
1013 	if (_view)
1014 		*_view = item->view;
1015 	if (_uniqueID)
1016 		*_uniqueID = item->id;
1017 	if (_error)
1018 		*_error = item->error;
1019 
1020 	return item->message;
1021 }
1022 
1023 
1024 int32
1025 BShelf::IndexOf(const BView* replicantView) const
1026 {
1027 	return replicant_data::IndexOf(&fReplicants, replicantView, false);
1028 }
1029 
1030 
1031 int32
1032 BShelf::IndexOf(const BMessage *archive) const
1033 {
1034 	return replicant_data::IndexOf(&fReplicants, archive);
1035 }
1036 
1037 
1038 int32
1039 BShelf::IndexOf(uint32 id) const
1040 {
1041 	return replicant_data::IndexOf(&fReplicants, id);
1042 }
1043 
1044 
1045 bool
1046 BShelf::CanAcceptReplicantMessage(BMessage*) const
1047 {
1048 	return true;
1049 }
1050 
1051 
1052 bool
1053 BShelf::CanAcceptReplicantView(BRect, BView*, BMessage*) const
1054 {
1055 	return true;
1056 }
1057 
1058 
1059 BPoint
1060 BShelf::AdjustReplicantBy(BRect, BMessage*) const
1061 {
1062 	return B_ORIGIN;
1063 }
1064 
1065 
1066 void
1067 BShelf::ReplicantDeleted(int32 index, const BMessage *archive,
1068 	const BView *replicant)
1069 {
1070 }
1071 
1072 
1073 extern "C" void
1074 _ReservedShelf1__6BShelfFv(BShelf *const, int32, const BMessage*, const BView*)
1075 {
1076 	// is not contained in BeOS R5's libbe, so we leave it empty
1077 }
1078 
1079 
1080 void BShelf::_ReservedShelf2() {}
1081 void BShelf::_ReservedShelf3() {}
1082 void BShelf::_ReservedShelf4() {}
1083 void BShelf::_ReservedShelf5() {}
1084 void BShelf::_ReservedShelf6() {}
1085 void BShelf::_ReservedShelf7() {}
1086 void BShelf::_ReservedShelf8() {}
1087 
1088 
1089 BShelf::BShelf(const BShelf&)
1090 {
1091 }
1092 
1093 
1094 BShelf &
1095 BShelf::operator=(const BShelf &)
1096 {
1097 	return *this;
1098 }
1099 
1100 
1101 status_t
1102 BShelf::_Archive(BMessage *data) const
1103 {
1104 	status_t status = BHandler::Archive(data);
1105 	if (status != B_OK)
1106 		return status;
1107 
1108 	status = data->AddBool("_zom_dsp", DisplaysZombies());
1109 	if (status != B_OK)
1110 		return status;
1111 
1112 	status = data->AddBool("_zom_alw", AllowsZombies());
1113 	if (status != B_OK)
1114 		return status;
1115 
1116 	status = data->AddInt32("_sg_cnt", fGenCount);
1117 	if (status != B_OK)
1118 		return status;
1119 
1120 	BMessage archive('ARCV');
1121 	for (int32 i = 0; i < fReplicants.CountItems(); i++) {
1122 		if (((replicant_data *)fReplicants.ItemAt(i))->Archive(&archive) == B_OK)
1123 			status = data->AddMessage("replicant", &archive);
1124 		if (status != B_OK)
1125 			break;
1126 		archive.MakeEmpty();
1127 	}
1128 
1129 	return status;
1130 }
1131 
1132 
1133 void
1134 BShelf::_InitData(BEntry *entry, BDataIO *stream, BView *view,
1135 	bool allowDrags)
1136 {
1137 	fContainerView = view;
1138 	fStream = NULL;
1139 	fEntry = entry;
1140 	fFilter = NULL;
1141 	fGenCount = 1;
1142 	fAllowDragging = allowDrags;
1143 	fDirty = true;
1144 	fDisplayZombies = false;
1145 	fAllowZombies = true;
1146 	fTypeEnforced = false;
1147 
1148 	if (entry)
1149 		fStream = new BFile(entry, B_READ_ONLY);
1150 	else
1151 		fStream = stream;
1152 
1153 	fFilter = new ShelfContainerViewFilter(this, fContainerView);
1154 
1155 	fContainerView->AddFilter(fFilter);
1156 	fContainerView->_SetShelf(this);
1157 
1158 	if (fStream) {
1159 		BMessage archive;
1160 
1161 		if (archive.Unflatten(fStream) == B_OK) {
1162 			bool allowZombies;
1163 			if (archive.FindBool("_zom_dsp", &allowZombies) != B_OK)
1164 				allowZombies = false;
1165 
1166 			SetDisplaysZombies(allowZombies);
1167 
1168 			if (archive.FindBool("_zom_alw", &allowZombies) != B_OK)
1169 				allowZombies = true;
1170 
1171 			SetAllowsZombies(allowZombies);
1172 
1173 			int32 genCount;
1174 			if (!archive.FindInt32("_sg_cnt", &genCount))
1175 				genCount = 1;
1176 
1177 			BMessage replicant;
1178 			BMessage *replmsg = NULL;
1179 			for (int32 i = 0; archive.FindMessage("replicant", i, &replicant) == B_OK; i++) {
1180 				BPoint point;
1181 				replmsg = new BMessage();
1182 				replicant.FindPoint("position", &point);
1183 				replicant.FindMessage("message", replmsg);
1184 				AddReplicant(replmsg, point);
1185 			}
1186 		}
1187 	}
1188 }
1189 
1190 
1191 status_t
1192 BShelf::_DeleteReplicant(replicant_data* item)
1193 {
1194 	BView *view = item->view;
1195 	if (view == NULL)
1196 		view = item->zombie_view;
1197 
1198 	if (view)
1199 		view->RemoveSelf();
1200 
1201 	if (item->dragger)
1202 		item->dragger->RemoveSelf();
1203 
1204 	int32 index = replicant_data::IndexOf(&fReplicants, item->message);
1205 
1206 	ReplicantDeleted(index, item->message, view);
1207 
1208 	fReplicants.RemoveItem(item);
1209 
1210 	if (item->relation == BDragger::TARGET_IS_PARENT
1211 		|| item->relation == BDragger::TARGET_IS_SIBLING) {
1212 		delete view;
1213 	}
1214 	if (item->relation == BDragger::TARGET_IS_CHILD
1215 		|| item->relation == BDragger::TARGET_IS_SIBLING) {
1216 		delete item->dragger;
1217 	}
1218 
1219 	// Update use count for image and unload if necessary
1220 	const char* signature = NULL;
1221 	if (item->message->FindString("add_on", &signature) == B_OK
1222 		&& signature != NULL) {
1223 		LoadedImages* loadedImages = LoadedImages::Default();
1224 		AutoLock<LoadedImages> lock(loadedImages);
1225 		if (lock.IsLocked()) {
1226 			LoadedImageMap::iterator it = loadedImages->images.find(
1227 				BString(signature));
1228 
1229 			if (it != loadedImages->images.end()) {
1230 				(*it).second.second--;
1231 				if ((*it).second.second <= 0) {
1232 					unload_add_on((*it).second.first);
1233 					loadedImages->images.erase(it);
1234 				}
1235 			}
1236 		}
1237 	}
1238 
1239 	delete item;
1240 
1241 	return B_OK;
1242 }
1243 
1244 
1245 //! Takes over ownership of \a data on success only
1246 status_t
1247 BShelf::_AddReplicant(BMessage *data, BPoint *location, uint32 uniqueID)
1248 {
1249 	// Check shelf types if needed
1250 	if (fTypeEnforced) {
1251 		const char *shelfType = NULL;
1252 		if (data->FindString("shelf_type", &shelfType) == B_OK
1253 			&& shelfType != NULL) {
1254 			if (Name() && strcmp(shelfType, Name()) != 0) {
1255 				printf("Replicant was rejected by BShelf: The BShelf's type and the Replicant's type don't match.");
1256 				return send_reply(data, B_ERROR, uniqueID);
1257 			} else {
1258 				printf("Replicant was rejected by BShelf: Replicant indicated a <type> (%s), but the shelf does not.", shelfType);
1259 				return send_reply(data, B_ERROR, uniqueID);
1260 			}
1261 		} else {
1262 			printf("Replicant was rejected by BShelf: Replicant did not have a <type>");
1263 			return send_reply(data, B_ERROR, uniqueID);
1264 		}
1265 	}
1266 
1267 	// Check if we can accept this message
1268 	if (!CanAcceptReplicantMessage(data)) {
1269 		printf("Replicant was rejected by BShelf::CanAcceptReplicantMessage()");
1270 		return send_reply(data, B_ERROR, uniqueID);
1271 	}
1272 
1273 	// Check if we can create multiple instances
1274 	if (data->FindBool("be:load_each_time")) {
1275 		const char *className = NULL;
1276 		const char *addOn = NULL;
1277 
1278 		if (data->FindString("class", &className) == B_OK
1279 			&& data->FindString("add_on", &addOn) == B_OK) {
1280 			if (find_replicant(fReplicants, className, addOn)) {
1281 				printf("Replicant was rejected. Unique replicant already exists. class=%s, signature=%s",
1282 					className, addOn);
1283 				return send_reply(data, B_ERROR, uniqueID);
1284 			}
1285 		}
1286 	}
1287 
1288 	// Instantiate the object, if this fails we have a zombie
1289 	image_id image = -1;
1290 	BArchivable *archivable = _InstantiateObject(data, &image);
1291 
1292 	BView *view = NULL;
1293 
1294 	if (archivable) {
1295 		view = dynamic_cast<BView*>(archivable);
1296 
1297 		if (!view) {
1298 			return send_reply(data, B_ERROR, uniqueID);
1299 		}
1300 	}
1301 
1302 	BDragger* dragger = NULL;
1303 	BView* replicant = NULL;
1304 	BDragger::relation relation = BDragger::TARGET_UNKNOWN;
1305 	_BZombieReplicantView_* zombie = NULL;
1306 	if (view) {
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 
1314 	else if (!fAllowZombies) {
1315 		// There was no view, and we're not allowed to have any zombies
1316 		// in the house
1317 		return send_reply(data, B_ERROR, uniqueID);
1318 	}
1319 
1320 	// Update use count for image
1321 	const char* signature = NULL;
1322 	if (data->FindString("add_on", &signature) == B_OK && signature != NULL) {
1323 		LoadedImages* loadedImages = LoadedImages::Default();
1324 		AutoLock<LoadedImages> lock(loadedImages);
1325 		if (lock.IsLocked()) {
1326 			LoadedImageMap::iterator it = loadedImages->images.find(
1327 				BString(signature));
1328 
1329 			if (it == loadedImages->images.end())
1330 				loadedImages->images.insert(LoadedImageMap::value_type(
1331 					BString(signature), std::pair<image_id, int>(image, 1)));
1332 			else
1333 				(*it).second.second++;
1334 		}
1335 	}
1336 
1337 	if (!zombie) {
1338 		data->RemoveName("_drop_point_");
1339 		data->RemoveName("_drop_offset_");
1340 	}
1341 
1342 	replicant_data *item = new replicant_data(data, replicant, dragger,
1343 		relation, uniqueID);
1344 
1345 	item->error = B_OK;
1346 	item->zombie_view = zombie;
1347 
1348 	fReplicants.AddItem(item);
1349 
1350 	return send_reply(data, B_OK, uniqueID);
1351 }
1352 
1353 
1354 BView *
1355 BShelf::_GetReplicant(BMessage *data, BView *view, const BPoint &point,
1356 		BDragger *&dragger, BDragger::relation &relation)
1357 {
1358 	// TODO: test me -- there seems to be lots of bugs parked here!
1359 	BView *replicant = NULL;
1360 	_GetReplicantData(data, view, replicant, dragger, relation);
1361 
1362 	if (dragger != NULL)
1363 		dragger->_SetViewToDrag(replicant);
1364 
1365 	BRect frame = view->Frame().OffsetToCopy(point);
1366 	if (!CanAcceptReplicantView(frame, replicant, data)) {
1367 		// the view has not been accepted
1368 		if (relation == BDragger::TARGET_IS_PARENT
1369 			|| relation == BDragger::TARGET_IS_SIBLING) {
1370 			delete replicant;
1371 		}
1372 		if (relation == BDragger::TARGET_IS_CHILD
1373 			|| relation == BDragger::TARGET_IS_SIBLING) {
1374 			delete dragger;
1375 		}
1376 		return NULL;
1377 	}
1378 
1379 	BPoint adjust = AdjustReplicantBy(frame, data);
1380 
1381 	if (dragger != NULL)
1382 		dragger->_SetShelf(this);
1383 
1384 	// TODO: could be not correct for some relations
1385 	view->MoveTo(point + adjust);
1386 
1387 	// if it's a sibling or a child, we need to add the dragger
1388 	if (relation == BDragger::TARGET_IS_SIBLING
1389 		|| relation == BDragger::TARGET_IS_CHILD)
1390 		fContainerView->AddChild(dragger);
1391 
1392 	if (relation != BDragger::TARGET_IS_CHILD)
1393 		fContainerView->AddChild(replicant);
1394 
1395 	replicant->AddFilter(new ReplicantViewFilter(this, replicant));
1396 
1397 	return replicant;
1398 }
1399 
1400 
1401 /* static */
1402 void
1403 BShelf::_GetReplicantData(BMessage *data, BView *view, BView *&replicant,
1404 			BDragger *&dragger, BDragger::relation &relation)
1405 {
1406 	// Check if we have a dragger archived as "__widget" inside the message
1407 	BMessage widget;
1408 	if (data->FindMessage("__widget", &widget) == B_OK) {
1409 		image_id draggerImage = B_ERROR;
1410 		replicant = view;
1411 		dragger = dynamic_cast<BDragger*>(_InstantiateObject(&widget, &draggerImage));
1412 		// Replicant is a sibling, or unknown, if there isn't a dragger
1413 		if (dragger != NULL)
1414 			relation = BDragger::TARGET_IS_SIBLING;
1415 
1416 	} else if ((dragger = dynamic_cast<BDragger*>(view)) != NULL) {
1417 		// Replicant is child of the dragger
1418 		relation = BDragger::TARGET_IS_CHILD;
1419 		replicant = dragger->ChildAt(0);
1420 
1421 	} else {
1422 		// Replicant is parent of the dragger
1423 		relation = BDragger::TARGET_IS_PARENT;
1424 		replicant = view;
1425 		dragger = dynamic_cast<BDragger *>(replicant->FindView("_dragger_"));
1426 			// can be NULL, the replicant could not have a dragger at all
1427 	}
1428 }
1429 
1430 
1431 _BZombieReplicantView_ *
1432 BShelf::_CreateZombie(BMessage *data, BDragger *&dragger)
1433 {
1434 	// TODO: the zombies must be adjusted and moved as well!
1435 	BRect frame;
1436 	if (data->FindRect("_frame", &frame) != B_OK)
1437 		frame = BRect();
1438 
1439 	_BZombieReplicantView_ *zombie = NULL;
1440 	if (data->WasDropped()) {
1441 		BPoint offset;
1442 		BPoint dropPoint = data->DropPoint(&offset);
1443 
1444 		frame.OffsetTo(fContainerView->ConvertFromScreen(dropPoint) - offset);
1445 
1446 		zombie = new _BZombieReplicantView_(frame, B_ERROR);
1447 
1448 		frame.OffsetTo(B_ORIGIN);
1449 
1450 		dragger = new BDragger(frame, zombie);
1451 		dragger->_SetShelf(this);
1452 		dragger->_SetZombied(true);
1453 
1454 		zombie->AddChild(dragger);
1455 		zombie->SetArchive(data);
1456 		zombie->AddFilter(new ReplicantViewFilter(this, zombie));
1457 
1458 		fContainerView->AddChild(zombie);
1459 	}
1460 
1461 	return zombie;
1462 }
1463 
1464 
1465 status_t
1466 BShelf::_GetProperty(BMessage *msg, BMessage *reply)
1467 {
1468 	uint32 ID;
1469 	status_t err = B_ERROR;
1470 	BView *replicant = NULL;
1471 	switch (msg->what) {
1472 		case B_INDEX_SPECIFIER:	{
1473 			int32 index = -1;
1474 			if (msg->FindInt32("index", &index)!=B_OK)
1475 				break;
1476 			ReplicantAt(index, &replicant, &ID, &err);
1477 			break;
1478 		}
1479 		case B_REVERSE_INDEX_SPECIFIER:	{
1480 			int32 rindex;
1481 			if (msg->FindInt32("index", &rindex) != B_OK)
1482 				break;
1483 			ReplicantAt(CountReplicants() - rindex, &replicant, &ID, &err);
1484 			break;
1485 		}
1486 		case B_NAME_SPECIFIER: {
1487 			const char *name;
1488 			if (msg->FindString("name", &name) != B_OK)
1489 				break;
1490 			for (int32 i = 0; i < CountReplicants(); i++) {
1491 				BView *view = NULL;
1492 				ReplicantAt(i, &view, &ID, &err);
1493 				if (err == B_OK) {
1494 					if (view->Name() != NULL &&
1495 						strlen(view->Name()) == strlen(name) && !strcmp(view->Name(), name)) {
1496 						replicant = view;
1497 						break;
1498 					}
1499 					err = B_NAME_NOT_FOUND;
1500 				}
1501 			}
1502 			break;
1503 		}
1504 		case B_ID_SPECIFIER: {
1505 			uint32 id;
1506 			if (msg->FindInt32("id", (int32 *)&id) != B_OK)
1507 				break;
1508 			for (int32 i = 0; i < CountReplicants(); i++) {
1509 				BView *view = NULL;
1510 				ReplicantAt(i, &view, &ID, &err);
1511 				if (err == B_OK) {
1512 					if (ID == id) {
1513 						replicant = view;
1514 						break;
1515 					}
1516 					err = B_NAME_NOT_FOUND;
1517 				}
1518 			}
1519 			break;
1520 		}
1521 		default:
1522 			break;
1523 	}
1524 
1525 	if (replicant) {
1526 		reply->AddInt32("index", IndexOf(replicant));
1527 		reply->AddInt32("ID", ID);
1528 	}
1529 
1530 	return err;
1531 }
1532 
1533 
1534 /* static */
1535 BArchivable *
1536 BShelf::_InstantiateObject(BMessage *archive, image_id *image)
1537 {
1538 	// Stay on the safe side. The constructor called by instantiate_object
1539 	// could throw an exception, which we catch here. Otherwise our calling app
1540 	// could die without notice.
1541 	try {
1542 		return instantiate_object(archive, image);
1543 	} catch (...) {
1544 		return NULL;
1545 	}
1546 }
1547 
1548