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