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