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