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