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