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