xref: /haiku/src/kits/interface/Shelf.cpp (revision 03187b607b2b5eec7ee059f1ead09bdba14991fb)
1 /*
2  * Copyright 2001-2009, 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  *		Alexandre Deckner, alex@zappotek.com
11  */
12 
13 /*!	BShelf stores replicant views that are dropped onto it */
14 
15 #include <AutoLock.h>
16 #include <Beep.h>
17 #include <Dragger.h>
18 #include <Entry.h>
19 #include <File.h>
20 #include <Looper.h>
21 #include <Message.h>
22 #include <MessageFilter.h>
23 #include <Messenger.h>
24 #include <Point.h>
25 #include <PropertyInfo.h>
26 #include <Rect.h>
27 #include <Shelf.h>
28 #include <String.h>
29 #include <View.h>
30 
31 #include <ViewPrivate.h>
32 
33 #include "ZombieReplicantView.h"
34 
35 #include <stdio.h>
36 #include <string.h>
37 
38 #include <map>
39 #include <utility>
40 
41 
42 typedef std::map<BString, std::pair<image_id, int32> > LoadedImageMap;
43 static LoadedImageMap	sLoadedImages;
44 static BLocker			sLoadedImageMapLocker("BShelf loaded image map");
45 
46 static property_info sShelfPropertyList[] = {
47 	{
48 		"Replicant",
49 		{ B_COUNT_PROPERTIES, B_CREATE_PROPERTY },
50 		{ B_DIRECT_SPECIFIER },
51 		NULL, 0,
52 	},
53 
54 	{
55 		"Replicant",
56 		{ B_DELETE_PROPERTY, B_GET_PROPERTY },
57 		{ B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, B_NAME_SPECIFIER, B_ID_SPECIFIER },
58 		NULL, 0,
59 	},
60 
61 	{
62 		"Replicant",
63 		{},
64 		{ B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, B_NAME_SPECIFIER, B_ID_SPECIFIER },
65 		"... of Replicant {index | name | id} of ...", 0,
66 	},
67 
68 	{ 0, { 0 }, { 0 }, 0, 0 }
69 };
70 
71 static property_info sReplicantPropertyList[] = {
72 	{
73 		"ID",
74 		{ B_GET_PROPERTY },
75 		{ B_DIRECT_SPECIFIER },
76 		NULL, 0, { B_INT32_TYPE }
77 	},
78 
79 	{
80 		"Name",
81 		{ B_GET_PROPERTY },
82 		{ B_DIRECT_SPECIFIER },
83 		NULL, 0, { B_STRING_TYPE }
84 	},
85 
86 	{
87 		"Signature",
88 		{ B_GET_PROPERTY },
89 		{ B_DIRECT_SPECIFIER },
90 		NULL, 0, { B_STRING_TYPE }
91 	},
92 
93 	{
94 		"Suites",
95 		{ B_GET_PROPERTY },
96 		{ B_DIRECT_SPECIFIER },
97 		NULL, 0, { B_PROPERTY_INFO_TYPE }
98 	},
99 
100 	{
101 		"View",
102 		{ },
103 		{ B_DIRECT_SPECIFIER },
104 		NULL, 0,
105 	},
106 
107 	{ 0, { 0 }, { 0 }, 0, 0 }
108 };
109 
110 
111 namespace BPrivate {
112 
113 struct replicant_data {
114 	replicant_data(BMessage *message, BView *view, BDragger *dragger,
115 		BDragger::relation relation, unsigned long id);
116 	replicant_data();
117 	~replicant_data();
118 
119 	static replicant_data* Find(BList const *list, BMessage const *msg);
120 	static replicant_data* Find(BList const *list, BView const *view, bool allowZombie);
121 	static replicant_data* Find(BList const *list, unsigned long id);
122 
123 	static int32 IndexOf(BList const *list, BMessage const *msg);
124 	static int32 IndexOf(BList const *list, BView const *view, bool allowZombie);
125 	static int32 IndexOf(BList const *list, unsigned long id);
126 
127 	status_t Archive(BMessage *msg);
128 
129 	BMessage*			message;
130 	BView*				view;
131 	BDragger*			dragger;
132 	BDragger::relation	relation;
133 	unsigned long		id;
134 	status_t			error;
135 	BView*				zombie_view;
136 };
137 
138 class ShelfContainerViewFilter : public BMessageFilter {
139 	public:
140 		ShelfContainerViewFilter(BShelf *shelf, BView *view);
141 
142 		filter_result	Filter(BMessage *msg, BHandler **handler);
143 
144 	private:
145 		filter_result	_ObjectDropFilter(BMessage *msg, BHandler **handler);
146 
147 		BShelf	*fShelf;
148 		BView	*fView;
149 };
150 
151 class ReplicantViewFilter : public BMessageFilter {
152 	public:
153 		ReplicantViewFilter(BShelf *shelf, BView *view);
154 
155 		filter_result Filter(BMessage *message, BHandler **handler);
156 
157 	private:
158 		BShelf	*fShelf;
159 		BView	*fView;
160 };
161 
162 }	// namespace BPrivate
163 
164 
165 using BPrivate::replicant_data;
166 using BPrivate::ReplicantViewFilter;
167 using BPrivate::ShelfContainerViewFilter;
168 
169 
170 //	#pragma mark -
171 
172 
173 /*!	\brief Helper function for BShelf::_AddReplicant()
174 */
175 static status_t
176 send_reply(BMessage* message, status_t status, uint32 uniqueID)
177 {
178 	if (message->IsSourceWaiting()) {
179 		BMessage reply(B_REPLY);
180 		reply.AddInt32("id", uniqueID);
181 		reply.AddInt32("error", status);
182 		message->SendReply(&reply);
183 	}
184 
185 	return status;
186 }
187 
188 
189 static bool
190 find_replicant(BList &list, const char *className, const char *addOn)
191 {
192 	int32 i = 0;
193 	replicant_data *item;
194 	while ((item = (replicant_data *)list.ItemAt(i++)) != NULL) {
195 		const char *replicantClassName;
196 		const char *replicantAddOn;
197 		if (item->message->FindString("class", &replicantClassName) == B_OK
198 			&& item->message->FindString("add_on", &replicantAddOn) == B_OK
199 			&& !strcmp(className, replicantClassName)
200 			&& addOn != NULL && replicantAddOn != NULL
201 			&& !strcmp(addOn, replicantAddOn))
202 		return true;
203 	}
204 	return false;
205 }
206 
207 
208 //	#pragma mark -
209 
210 
211 replicant_data::replicant_data(BMessage *_message, BView *_view, BDragger *_dragger,
212 	BDragger::relation _relation, unsigned long _id)
213 	:
214 	message(_message),
215 	view(_view),
216 	dragger(NULL),
217 	relation(_relation),
218 	id(_id),
219 	error(B_OK),
220 	zombie_view(NULL)
221 {
222 }
223 
224 
225 replicant_data::replicant_data()
226 	:
227 	message(NULL),
228 	view(NULL),
229 	dragger(NULL),
230 	relation(BDragger::TARGET_UNKNOWN),
231 	id(0),
232 	error(B_ERROR),
233 	zombie_view(NULL)
234 {
235 }
236 
237 replicant_data::~replicant_data()
238 {
239 	delete message;
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 	// we own fStream only when fEntry is set
502 	if (fEntry) {
503 		delete fEntry;
504 		delete fStream;
505 	}
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 		BHandler *replicant = NULL;
534 		if (msg->FindPointer("_target", (void **)&replicant) == B_OK) {
535 			BView *view = dynamic_cast<BView *>(replicant);
536 			if (view != NULL)
537 				DeleteReplicant(view);
538 		}
539 		return;
540 	}
541 
542 	BMessage replyMsg(B_REPLY);
543 	status_t err = B_BAD_SCRIPT_SYNTAX;
544 
545 	BMessage specifier;
546 	int32 what;
547 	const char *prop;
548 	int32 index;
549 	if (msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK)
550 		return BHandler::MessageReceived(msg);
551 
552 	switch (msg->what) {
553 		case B_DELETE_PROPERTY:
554 		case B_GET_PROPERTY:
555 		case B_GET_SUPPORTED_SUITES:
556 			if (strcmp(prop, "Replicant") == 0) {
557 				BMessage reply;
558 				int32 i;
559 				uint32 ID;
560 				BView *replicant = NULL;
561 				BMessage *repMessage = NULL;
562 				err = _GetProperty(&specifier, &reply);
563 				if (err == B_OK)
564 					err = reply.FindInt32("index", &i);
565 
566 				if (err == B_OK && msg->what == B_DELETE_PROPERTY) { // Delete Replicant
567 					err = DeleteReplicant(i);
568 					break;
569 				}
570 				if (err == B_OK && 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 					msg->PopSpecifier();
582 					BMessage archive;
583 					err = replicant->Archive(&archive);
584 					if (err == B_OK && msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != 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 (strcmp(prop, "ID") == 0) {
593 						err = replyMsg.AddInt32("result", ID);
594 					} else if (strcmp(prop, "Name") == 0) {
595 						err = replyMsg.AddString("result", replicant->Name());
596 					} else if (strcmp(prop, "Signature") == 0) {
597 						const char *add_on = NULL;
598 						err = repMessage->FindString("add_on", &add_on);
599 						if (err == B_OK)
600 							err = replyMsg.AddString("result", add_on);
601 					} else if (strcmp(prop, "Suites") == 0) {
602 						err = replyMsg.AddString("suites", "suite/vnd.Be-replicant");
603 						if (err == B_OK) {
604 							BPropertyInfo propInfo(sReplicantPropertyList);
605 							err = replyMsg.AddFlat("messages", &propInfo);
606 						}
607 					}
608 				}
609 				break;
610 			}
611 			return BHandler::MessageReceived(msg);
612 
613 		case B_COUNT_PROPERTIES:
614 			if (strcmp(prop, "Replicant") == 0) {
615 				err = replyMsg.AddInt32("result", CountReplicants());
616 				break;
617 			}
618 			return BHandler::MessageReceived(msg);
619 
620 		case B_CREATE_PROPERTY:
621 		{
622 			BMessage replicantMsg;
623 			BPoint pos;
624 			if (msg->FindMessage("data", &replicantMsg) == B_OK
625 				&& msg->FindPoint("location", &pos) == B_OK) {
626 					err = AddReplicant(&replicantMsg, pos);
627 			}
628 		}
629 		break;
630 	}
631 
632 	if (err < B_OK) {
633 		replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;
634 
635 		if (err == B_BAD_SCRIPT_SYNTAX)
636 			replyMsg.AddString("message", "Didn't understand the specifier(s)");
637 		else
638 			replyMsg.AddString("message", strerror(err));
639 	}
640 
641 	replyMsg.AddInt32("error", err);
642 	msg->SendReply(&replyMsg);
643 }
644 
645 
646 status_t
647 BShelf::Save()
648 {
649 	status_t status = B_ERROR;
650 	if (fEntry != NULL) {
651 		BFile *file = new BFile(fEntry, B_READ_WRITE | B_ERASE_FILE);
652 		status = file->InitCheck();
653 		if (status < B_OK) {
654 			delete file;
655 			return status;
656 		}
657 		fStream = file;
658 	}
659 
660 	if (fStream != NULL) {
661 		BMessage message;
662 		status = _Archive(&message);
663 		if (status == B_OK)
664 			status = message.Flatten(fStream);
665 	}
666 
667 	return status;
668 }
669 
670 
671 void
672 BShelf::SetDirty(bool state)
673 {
674 	fDirty = state;
675 }
676 
677 
678 bool
679 BShelf::IsDirty() const
680 {
681 	return fDirty;
682 }
683 
684 
685 BHandler *
686 BShelf::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
687 						int32 form, const char *property)
688 {
689 	BPropertyInfo shelfPropInfo(sShelfPropertyList);
690 	BHandler *target = NULL;
691 	BView *replicant = NULL;
692 
693 	switch (shelfPropInfo.FindMatch(msg, 0, specifier, form, property)) {
694 		case 0:
695 			target = this;
696 			break;
697 		case 1:
698 			if (msg->PopSpecifier() != B_OK) {
699 				target = this;
700 				break;
701 			}
702 			msg->SetCurrentSpecifier(index);
703 			// fall through
704 		case 2: {
705 			BMessage reply;
706 			status_t err = _GetProperty(specifier, &reply);
707 			int32 i;
708 			uint32 ID;
709 			if (err == B_OK)
710 				err = reply.FindInt32("index", &i);
711 			if (err == B_OK)
712 				ReplicantAt(i, &replicant, &ID, &err);
713 
714 			if (err == B_OK && replicant != NULL) {
715 				if (index == 0)
716 					return this;
717 			} else {
718 				BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
719 				replyMsg.AddInt32("error", B_BAD_INDEX);
720 				replyMsg.AddString("message", "Cannot find replicant at/with specified index/name.");
721 				msg->SendReply(&replyMsg);
722 			}
723 			}
724 			msg->PopSpecifier();
725 			break;
726 	}
727 
728 	if (!replicant) {
729 		if (target)
730 			return target;
731 		return BHandler::ResolveSpecifier(msg, index, specifier, form,
732 			property);
733 	}
734 
735 	int32 repIndex;
736 	status_t err = msg->GetCurrentSpecifier(&repIndex, specifier, &form, &property);
737 	if (err) {
738 		BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
739 		reply.AddInt32("error", err);
740 		msg->SendReply(&reply);
741 		return NULL;
742 	}
743 
744 	BPropertyInfo replicantPropInfo(sReplicantPropertyList);
745 	switch (replicantPropInfo.FindMatch(msg, 0, specifier, form, property)) {
746 		case 0:
747 		case 1:
748 		case 2:
749 		case 3:
750 			msg->SetCurrentSpecifier(index);
751 			target = this;
752 			break;
753 		case 4:
754 			target = replicant;
755 			msg->PopSpecifier();
756 			break;
757 		default:
758 			break;
759 	}
760 	if (!target) {
761 		BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
762 		replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX);
763 		replyMsg.AddString("message", "Didn't understand the specifier(s)");
764 		msg->SendReply(&replyMsg);
765 	}
766 	return target;
767 }
768 
769 
770 status_t
771 BShelf::GetSupportedSuites(BMessage *message)
772 {
773 	status_t err;
774 	err = message->AddString("suites", "suite/vnd.Be-shelf");
775 	if (err == B_OK) {
776 		BPropertyInfo propInfo(sShelfPropertyList);
777 		err = message->AddFlat("messages", &propInfo);
778 	}
779 	if (err == B_OK)
780 		return BHandler::GetSupportedSuites(message);
781 	return err;
782 }
783 
784 
785 status_t
786 BShelf::Perform(perform_code d, void *arg)
787 {
788 	return BHandler::Perform(d, arg);
789 }
790 
791 
792 bool
793 BShelf::AllowsDragging() const
794 {
795 	return fAllowDragging;
796 }
797 
798 
799 void
800 BShelf::SetAllowsDragging(bool state)
801 {
802 	fAllowDragging = state;
803 }
804 
805 
806 bool
807 BShelf::AllowsZombies() const
808 {
809 	return fAllowZombies;
810 }
811 
812 
813 void
814 BShelf::SetAllowsZombies(bool state)
815 {
816 	fAllowZombies = state;
817 }
818 
819 
820 bool
821 BShelf::DisplaysZombies() const
822 {
823 	return fDisplayZombies;
824 }
825 
826 
827 void
828 BShelf::SetDisplaysZombies(bool state)
829 {
830 	fDisplayZombies = state;
831 }
832 
833 
834 bool
835 BShelf::IsTypeEnforced() const
836 {
837 	return fTypeEnforced;
838 }
839 
840 
841 void
842 BShelf::SetTypeEnforced(bool state)
843 {
844 	fTypeEnforced = state;
845 }
846 
847 
848 status_t
849 BShelf::SetSaveLocation(BDataIO *data_io)
850 {
851 	fDirty = true;
852 
853 	if (fEntry) {
854 		delete fEntry;
855 		fEntry = NULL;
856 	}
857 
858 	fStream = data_io;
859 
860 	return B_OK;
861 }
862 
863 
864 status_t
865 BShelf::SetSaveLocation(const entry_ref *ref)
866 {
867 	fDirty = true;
868 
869 	if (fEntry)
870 		delete fEntry;
871 	else
872 		fStream = NULL;
873 
874 	fEntry = new BEntry(ref);
875 
876 	return B_OK;
877 }
878 
879 
880 BDataIO *
881 BShelf::SaveLocation(entry_ref *ref) const
882 {
883 	entry_ref entry;
884 
885 	if (fStream && ref) {
886 		*ref = entry;
887 		return fStream;
888 	}
889 	if (fEntry) {
890 		fEntry->GetRef(&entry);
891 
892 		if (ref)
893 			*ref = entry;
894 
895 		return NULL;
896 	}
897 
898 	*ref = entry;
899 	return NULL;
900 }
901 
902 
903 status_t
904 BShelf::AddReplicant(BMessage *data, BPoint location)
905 {
906 	return _AddReplicant(data, &location, fGenCount++);
907 }
908 
909 
910 status_t
911 BShelf::DeleteReplicant(BView *replicant)
912 {
913 	int32 index = replicant_data::IndexOf(&fReplicants, replicant, true);
914 
915 	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
916 	if (item == NULL)
917 		return B_BAD_VALUE;
918 
919 	return _DeleteReplicant(item);
920 }
921 
922 
923 status_t
924 BShelf::DeleteReplicant(BMessage *data)
925 {
926 	int32 index = replicant_data::IndexOf(&fReplicants, data);
927 
928 	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
929 	if (!item)
930 		return B_BAD_VALUE;
931 
932 	return _DeleteReplicant(item);
933 }
934 
935 
936 status_t
937 BShelf::DeleteReplicant(int32 index)
938 {
939 	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
940 	if (!item)
941 		return B_BAD_INDEX;
942 
943 	return _DeleteReplicant(item);
944 }
945 
946 
947 int32
948 BShelf::CountReplicants() const
949 {
950 	return fReplicants.CountItems();
951 }
952 
953 
954 BMessage *
955 BShelf::ReplicantAt(int32 index, BView **_view, uint32 *_uniqueID,
956 	status_t *_error) const
957 {
958 	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
959 	if (item == NULL) {
960 		// no replicant found
961 		if (_view)
962 			*_view = NULL;
963 		if (_uniqueID)
964 			*_uniqueID = ~0UL;
965 		if (_error)
966 			*_error = B_BAD_INDEX;
967 
968 		return NULL;
969 	}
970 
971 	if (_view)
972 		*_view = item->view;
973 	if (_uniqueID)
974 		*_uniqueID = item->id;
975 	if (_error)
976 		*_error = item->error;
977 
978 	return item->message;
979 }
980 
981 
982 int32
983 BShelf::IndexOf(const BView* replicantView) const
984 {
985 	return replicant_data::IndexOf(&fReplicants, replicantView, false);
986 }
987 
988 
989 int32
990 BShelf::IndexOf(const BMessage *archive) const
991 {
992 	return replicant_data::IndexOf(&fReplicants, archive);
993 }
994 
995 
996 int32
997 BShelf::IndexOf(uint32 id) const
998 {
999 	return replicant_data::IndexOf(&fReplicants, id);
1000 }
1001 
1002 
1003 bool
1004 BShelf::CanAcceptReplicantMessage(BMessage*) const
1005 {
1006 	return true;
1007 }
1008 
1009 
1010 bool
1011 BShelf::CanAcceptReplicantView(BRect, BView*, BMessage*) const
1012 {
1013 	return true;
1014 }
1015 
1016 
1017 BPoint
1018 BShelf::AdjustReplicantBy(BRect, BMessage*) const
1019 {
1020 	return B_ORIGIN;
1021 }
1022 
1023 
1024 void
1025 BShelf::ReplicantDeleted(int32 index, const BMessage *archive,
1026 	const BView *replicant)
1027 {
1028 }
1029 
1030 
1031 extern "C" void
1032 _ReservedShelf1__6BShelfFv(BShelf *const, int32, const BMessage*, const BView*)
1033 {
1034 	// is not contained in BeOS R5's libbe, so we leave it empty
1035 }
1036 
1037 
1038 void BShelf::_ReservedShelf2() {}
1039 void BShelf::_ReservedShelf3() {}
1040 void BShelf::_ReservedShelf4() {}
1041 void BShelf::_ReservedShelf5() {}
1042 void BShelf::_ReservedShelf6() {}
1043 void BShelf::_ReservedShelf7() {}
1044 void BShelf::_ReservedShelf8() {}
1045 
1046 
1047 BShelf::BShelf(const BShelf&)
1048 {
1049 }
1050 
1051 
1052 BShelf &
1053 BShelf::operator=(const BShelf &)
1054 {
1055 	return *this;
1056 }
1057 
1058 
1059 status_t
1060 BShelf::_Archive(BMessage *data) const
1061 {
1062 	BHandler::Archive(data);
1063 
1064 	data->AddBool("_zom_dsp", DisplaysZombies());
1065 	data->AddBool("_zom_alw", AllowsZombies());
1066 	data->AddInt32("_sg_cnt", fGenCount);
1067 
1068 	BMessage archive('ARCV');
1069 
1070 	for (int32 i = 0; i < fReplicants.CountItems(); i++) {
1071 		if (((replicant_data *)fReplicants.ItemAt(i))->Archive(&archive) == B_OK)
1072 			data->AddMessage("replicant", &archive);
1073 		archive.MakeEmpty();
1074 	}
1075 
1076 	return B_OK;
1077 }
1078 
1079 
1080 void
1081 BShelf::_InitData(BEntry *entry, BDataIO *stream, BView *view,
1082 	bool allowDrags)
1083 {
1084 	fContainerView = view;
1085 	fStream = NULL;
1086 	fEntry = entry;
1087 	fFilter = NULL;
1088 	fGenCount = 1;
1089 	fAllowDragging = allowDrags;
1090 	fDirty = true;
1091 	fDisplayZombies = false;
1092 	fAllowZombies = true;
1093 	fTypeEnforced = false;
1094 
1095 	if (entry)
1096 		fStream = new BFile(entry, B_READ_ONLY);
1097 	else
1098 		fStream = stream;
1099 
1100 	fFilter = new ShelfContainerViewFilter(this, fContainerView);
1101 
1102 	fContainerView->AddFilter(fFilter);
1103 	fContainerView->_SetShelf(this);
1104 
1105 	if (fStream) {
1106 		BMessage archive;
1107 
1108 		if (archive.Unflatten(fStream) == B_OK) {
1109 			bool allowZombies;
1110 			if (archive.FindBool("_zom_dsp", &allowZombies) != B_OK)
1111 				allowZombies = false;
1112 
1113 			SetDisplaysZombies(allowZombies);
1114 
1115 			if (archive.FindBool("_zom_alw", &allowZombies) != B_OK)
1116 				allowZombies = true;
1117 
1118 			SetAllowsZombies(allowZombies);
1119 
1120 			int32 genCount;
1121 			if (!archive.FindInt32("_sg_cnt", &genCount))
1122 				genCount = 1;
1123 
1124 			BMessage replicant;
1125 			BMessage *replmsg = NULL;
1126 			for (int32 i = 0; archive.FindMessage("replicant", i, &replicant) == B_OK; i++) {
1127 				BPoint point;
1128 				replmsg = new BMessage();
1129 				replicant.FindPoint("position", &point);
1130 				replicant.FindMessage("message", replmsg);
1131 				AddReplicant(replmsg, point);
1132 			}
1133 		}
1134 	}
1135 }
1136 
1137 
1138 status_t
1139 BShelf::_DeleteReplicant(replicant_data* item)
1140 {
1141 	BView *view = item->view;
1142 	if (view == NULL)
1143 		view = item->zombie_view;
1144 
1145 	if (view)
1146 		view->RemoveSelf();
1147 
1148 	if (item->dragger)
1149 		item->dragger->RemoveSelf();
1150 
1151 	int32 index = replicant_data::IndexOf(&fReplicants, item->message);
1152 
1153 	ReplicantDeleted(index, item->message, view);
1154 
1155 	fReplicants.RemoveItem(item);
1156 
1157 	if (item->relation == BDragger::TARGET_IS_PARENT
1158 		|| item->relation == BDragger::TARGET_IS_SIBLING) {
1159 		delete view;
1160 	}
1161 	if (item->relation == BDragger::TARGET_IS_CHILD
1162 		|| item->relation == BDragger::TARGET_IS_SIBLING) {
1163 		delete item->dragger;
1164 	}
1165 
1166 	// Update use count for image and unload if necessary
1167 	const char* signature = NULL;
1168 	if (item->message->FindString("add_on", &signature) == B_OK
1169 		&& signature != NULL) {
1170 		AutoLock<BLocker> lock(sLoadedImageMapLocker);
1171 		if (lock.IsLocked()) {
1172 			LoadedImageMap::iterator it = sLoadedImages.find(BString(signature));
1173 
1174 			if (it != sLoadedImages.end()) {
1175 				(*it).second.second--;
1176 				if ((*it).second.second <= 0) {
1177 					unload_add_on((*it).second.first);
1178 					sLoadedImages.erase(it);
1179 				}
1180 			}
1181 		}
1182 	}
1183 
1184 	delete item;
1185 
1186 	return B_OK;
1187 }
1188 
1189 
1190 //! Takes over ownership of \a data on success only
1191 status_t
1192 BShelf::_AddReplicant(BMessage *data, BPoint *location, uint32 uniqueID)
1193 {
1194 	// Check shelf types if needed
1195 	if (fTypeEnforced) {
1196 		const char *shelfType = NULL;
1197 		if (data->FindString("shelf_type", &shelfType) == B_OK
1198 			&& shelfType != NULL) {
1199 			if (Name() && strcmp(shelfType, Name()) != 0) {
1200 				printf("Replicant was rejected by BShelf: The BShelf's type and the Replicant's type don't match.");
1201 				return send_reply(data, B_ERROR, uniqueID);
1202 			} else {
1203 				printf("Replicant was rejected by BShelf: Replicant indicated a <type> (%s), but the shelf does not.", shelfType);
1204 				return send_reply(data, B_ERROR, uniqueID);
1205 			}
1206 		} else {
1207 			printf("Replicant was rejected by BShelf: Replicant did not have a <type>");
1208 			return send_reply(data, B_ERROR, uniqueID);
1209 		}
1210 	}
1211 
1212 	// Check if we can accept this message
1213 	if (!CanAcceptReplicantMessage(data)) {
1214 		printf("Replicant was rejected by BShelf::CanAcceptReplicantMessage()");
1215 		return send_reply(data, B_ERROR, uniqueID);
1216 	}
1217 
1218 	// Check if we can create multiple instances
1219 	if (data->FindBool("be:load_each_time")) {
1220 		const char *className = NULL;
1221 		const char *addOn = NULL;
1222 
1223 		if (data->FindString("class", &className) == B_OK
1224 			&& data->FindString("add_on", &addOn) == B_OK) {
1225 			if (find_replicant(fReplicants, className, addOn)) {
1226 				printf("Replicant was rejected. Unique replicant already exists. class=%s, signature=%s",
1227 					className, addOn);
1228 				return send_reply(data, B_ERROR, uniqueID);
1229 			}
1230 		}
1231 	}
1232 
1233 	// Instantiate the object, if this fails we have a zombie
1234 	image_id image = -1;
1235 	BArchivable *archivable = _InstantiateObject(data, &image);
1236 
1237 	if (archivable == NULL)
1238 		return send_reply(data, B_ERROR, uniqueID);
1239 
1240 	BView *view = dynamic_cast<BView*>(archivable);
1241 	if (view == NULL) {
1242 		printf("Replicant was rejected: it's not a view!");
1243 		return send_reply(data, B_ERROR, uniqueID);
1244 	}
1245 
1246 	BDragger* dragger = NULL;
1247 	BView* replicant = NULL;
1248 	BDragger::relation relation = BDragger::TARGET_UNKNOWN;
1249 	_BZombieReplicantView_* zombie = NULL;
1250 	if (view != NULL) {
1251 		const BPoint point = location ? *location : view->Frame().LeftTop();
1252 		replicant = _GetReplicant(data, view, point, dragger, relation);
1253 		if (replicant == NULL)
1254 			return send_reply(data, B_ERROR, uniqueID);
1255 	} else if (fDisplayZombies && fAllowZombies)
1256 		zombie = _CreateZombie(data, dragger);
1257 
1258 	else if (!fAllowZombies) {
1259 		// There was no view, and we're not allowed to have any zombies
1260 		// in the house
1261 		return send_reply(data, B_ERROR, uniqueID);
1262 	}
1263 
1264 	// Update use count for image
1265 	const char* signature = NULL;
1266 	if (data->FindString("add_on", &signature) == B_OK && signature != NULL) {
1267 		AutoLock<BLocker> lock(sLoadedImageMapLocker);
1268 		if (lock.IsLocked()) {
1269 			LoadedImageMap::iterator it = sLoadedImages.find(BString(signature));
1270 
1271 			if (it == sLoadedImages.end())
1272 				sLoadedImages.insert(LoadedImageMap::value_type(
1273 					BString(signature), std::pair<image_id, int>(image, 1)));
1274 			else
1275 				(*it).second.second++;
1276 		}
1277 	}
1278 
1279 	data->RemoveName("_drop_point_");
1280 	data->RemoveName("_drop_offset_");
1281 
1282 	replicant_data *item = new replicant_data(data, replicant, dragger,
1283 		relation, uniqueID);
1284 
1285 	item->error = B_OK;
1286 	item->zombie_view = zombie;
1287 
1288 	fReplicants.AddItem(item);
1289 
1290 	return send_reply(data, B_OK, uniqueID);
1291 }
1292 
1293 
1294 BView *
1295 BShelf::_GetReplicant(BMessage *data, BView *view, const BPoint &point,
1296 		BDragger *&dragger, BDragger::relation &relation)
1297 {
1298 	// TODO: test me -- there seems to be lots of bugs parked here!
1299 	BView *replicant = NULL;
1300 	_GetReplicantData(data, view, replicant, dragger, relation);
1301 
1302 	if (dragger != NULL)
1303 		dragger->_SetViewToDrag(replicant);
1304 
1305 	BRect frame = view->Frame().OffsetToCopy(point);
1306 	if (!CanAcceptReplicantView(frame, replicant, data)) {
1307 		// the view has not been accepted
1308 		if (relation == BDragger::TARGET_IS_PARENT
1309 			|| relation == BDragger::TARGET_IS_SIBLING) {
1310 			delete replicant;
1311 		}
1312 		if (relation == BDragger::TARGET_IS_CHILD
1313 			|| relation == BDragger::TARGET_IS_SIBLING) {
1314 			delete dragger;
1315 		}
1316 		return NULL;
1317 	}
1318 
1319 	BPoint adjust = AdjustReplicantBy(frame, data);
1320 
1321 	if (dragger != NULL)
1322 		dragger->_SetShelf(this);
1323 
1324 	// TODO: could be not correct for some relations
1325 	view->MoveTo(point + adjust);
1326 
1327 	// if it's a sibling or a child, we need to add the dragger
1328 	if (relation == BDragger::TARGET_IS_SIBLING
1329 		|| relation == BDragger::TARGET_IS_CHILD)
1330 		fContainerView->AddChild(dragger);
1331 
1332 	if (relation != BDragger::TARGET_IS_CHILD)
1333 		fContainerView->AddChild(replicant);
1334 
1335 	replicant->AddFilter(new ReplicantViewFilter(this, replicant));
1336 
1337 	return replicant;
1338 }
1339 
1340 
1341 /* static */
1342 void
1343 BShelf::_GetReplicantData(BMessage *data, BView *view, BView *&replicant,
1344 			BDragger *&dragger, BDragger::relation &relation)
1345 {
1346 	// Check if we have a dragger archived as "__widget" inside the message
1347 	BMessage widget;
1348 	if (data->FindMessage("__widget", &widget) == B_OK) {
1349 		image_id draggerImage = B_ERROR;
1350 		replicant = view;
1351 		dragger = dynamic_cast<BDragger*>(_InstantiateObject(&widget, &draggerImage));
1352 		// Replicant is a sibling, or unknown, if there isn't a dragger
1353 		if (dragger != NULL)
1354 			relation = BDragger::TARGET_IS_SIBLING;
1355 
1356 	} else if ((dragger = dynamic_cast<BDragger*>(view)) != NULL) {
1357 		// Replicant is child of the dragger
1358 		relation = BDragger::TARGET_IS_CHILD;
1359 		replicant = dragger->ChildAt(0);
1360 
1361 	} else {
1362 		// Replicant is parent of the dragger
1363 		relation = BDragger::TARGET_IS_PARENT;
1364 		replicant = view;
1365 		dragger = dynamic_cast<BDragger *>(replicant->FindView("_dragger_"));
1366 			// can be NULL, the replicant could not have a dragger at all
1367 	}
1368 }
1369 
1370 
1371 _BZombieReplicantView_ *
1372 BShelf::_CreateZombie(BMessage *data, BDragger *&dragger)
1373 {
1374 	// TODO: the zombies must be adjusted and moved as well!
1375 	BRect frame;
1376 	if (data->FindRect("_frame", &frame) != B_OK)
1377 		frame = BRect();
1378 
1379 	_BZombieReplicantView_ *zombie = NULL;
1380 	if (data->WasDropped()) {
1381 		BPoint offset;
1382 		BPoint dropPoint = data->DropPoint(&offset);
1383 
1384 		frame.OffsetTo(fContainerView->ConvertFromScreen(dropPoint) - offset);
1385 
1386 		zombie = new _BZombieReplicantView_(frame, B_ERROR);
1387 
1388 		frame.OffsetTo(B_ORIGIN);
1389 
1390 		dragger = new BDragger(frame, zombie);
1391 		dragger->_SetShelf(this);
1392 		dragger->_SetZombied(true);
1393 
1394 		zombie->AddChild(dragger);
1395 		zombie->SetArchive(data);
1396 		zombie->AddFilter(new ReplicantViewFilter(this, zombie));
1397 
1398 		fContainerView->AddChild(zombie);
1399 	}
1400 
1401 	return zombie;
1402 }
1403 
1404 
1405 status_t
1406 BShelf::_GetProperty(BMessage *msg, BMessage *reply)
1407 {
1408 	uint32 ID;
1409 	status_t err = B_ERROR;
1410 	BView *replicant = NULL;
1411 	switch (msg->what) {
1412 		case B_INDEX_SPECIFIER:	{
1413 			int32 index = -1;
1414 			if (msg->FindInt32("index", &index)!=B_OK)
1415 				break;
1416 			ReplicantAt(index, &replicant, &ID, &err);
1417 			break;
1418 		}
1419 		case B_REVERSE_INDEX_SPECIFIER:	{
1420 			int32 rindex;
1421 			if (msg->FindInt32("index", &rindex) != B_OK)
1422 				break;
1423 			ReplicantAt(CountReplicants() - rindex, &replicant, &ID, &err);
1424 			break;
1425 		}
1426 		case B_NAME_SPECIFIER: {
1427 			const char *name;
1428 			if (msg->FindString("name", &name) != B_OK)
1429 				break;
1430 			for (int32 i=0; i<CountReplicants(); i++) {
1431 				BView *view = NULL;
1432 				ReplicantAt(i, &view, &ID, &err);
1433 				if (err == B_OK) {
1434 					if (view->Name() != NULL &&
1435 						strlen(view->Name()) == strlen(name) && !strcmp(view->Name(), name)) {
1436 						replicant = view;
1437 						break;
1438 					}
1439 					err = B_NAME_NOT_FOUND;
1440 				}
1441 			}
1442 			break;
1443 		}
1444 		case B_ID_SPECIFIER: {
1445 			uint32 id;
1446 			if (msg->FindInt32("id", (int32 *)&id) != B_OK)
1447 				break;
1448 			for (int32 i=0; i<CountReplicants(); i++) {
1449 				BView *view = NULL;
1450 				ReplicantAt(i, &view, &ID, &err);
1451 				if (err == B_OK) {
1452 					if (ID == id) {
1453 						replicant = view;
1454 						break;
1455 					}
1456 					err = B_NAME_NOT_FOUND;
1457 				}
1458 			}
1459 			break;
1460 		}
1461 		default:
1462 			break;
1463 	}
1464 
1465 	if (replicant) {
1466 		reply->AddInt32("index", IndexOf(replicant));
1467 		reply->AddInt32("ID", ID);
1468 	}
1469 
1470 	return err;
1471 }
1472 
1473 
1474 /* static */
1475 BArchivable *
1476 BShelf::_InstantiateObject(BMessage *archive, image_id *image)
1477 {
1478 	// Stay on the safe side. The constructor called by instantiate_object
1479 	// could throw an exception, which we catch here. Otherwise our calling app
1480 	// could die without notice.
1481 	try {
1482 		return instantiate_object(archive, image);
1483 	} catch (...) {
1484 		return NULL;
1485 	}
1486 }
1487 
1488