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