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