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
46namespace {
47
48typedef std::map<BString, std::pair<image_id, int32> > LoadedImageMap;
49
50struct LoadedImages {
51	LoadedImageMap			images;
52
53	LoadedImages()
54		:
55		fLock("BShelf loaded image map")
56	{
57	}
58
59	bool Lock()
60	{
61		return fLock.Lock();
62	}
63
64	void Unlock()
65	{
66		fLock.Unlock();
67	}
68
69	static LoadedImages* Default()
70	{
71		if (sDefaultInstance == NULL)
72			pthread_once(&sDefaultInitOnce, &_InitSingleton);
73
74		return sDefaultInstance;
75	}
76
77private:
78	static void _InitSingleton()
79	{
80		sDefaultInstance = new LoadedImages;
81	}
82
83private:
84	BLocker					fLock;
85
86	static pthread_once_t	sDefaultInitOnce;
87	static LoadedImages*	sDefaultInstance;
88};
89
90pthread_once_t LoadedImages::sDefaultInitOnce = PTHREAD_ONCE_INIT;
91LoadedImages* LoadedImages::sDefaultInstance = NULL;
92
93}	// unnamed namespace
94
95
96static 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
121static 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
161namespace BPrivate {
162
163struct 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
188class 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
201class 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
215using BPrivate::replicant_data;
216using BPrivate::ReplicantViewFilter;
217using BPrivate::ShelfContainerViewFilter;
218
219
220//	#pragma mark -
221
222
223/*!	\brief Helper function for BShelf::_AddReplicant()
224*/
225static status_t
226send_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
239static bool
240find_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
261replicant_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
275replicant_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
287replicant_data::~replicant_data()
288{
289	delete message;
290}
291
292status_t
293replicant_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
318replicant_data *
319replicant_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
333replicant_data *
334replicant_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
351replicant_data *
352replicant_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
366int32
367replicant_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
382int32
383replicant_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
401int32
402replicant_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
419ShelfContainerViewFilter::ShelfContainerViewFilter(BShelf *shelf, BView *view)
420	: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
421	fShelf(shelf),
422	fView(view)
423{
424}
425
426
427filter_result
428ShelfContainerViewFilter::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
440filter_result
441ShelfContainerViewFilter::_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
498ReplicantViewFilter::ReplicantViewFilter(BShelf *shelf, BView *view)
499	: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
500	fShelf(shelf),
501	fView(view)
502{
503}
504
505
506filter_result
507ReplicantViewFilter::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
521BShelf::BShelf(BView *view, bool allowDrags, const char *shelfType)
522	: BHandler(shelfType)
523{
524	_InitData(NULL, NULL, view, allowDrags);
525}
526
527
528BShelf::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
536BShelf::BShelf(BDataIO *stream, BView *view, bool allowDrags,
537	const char *shelfType)
538	: BHandler(shelfType)
539{
540	_InitData(NULL, stream, view, allowDrags);
541}
542
543
544BShelf::BShelf(BMessage *data)
545	: BHandler(data)
546{
547	// TODO: Implement ?
548}
549
550
551BShelf::~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
571status_t
572BShelf::Archive(BMessage *data, bool deep) const
573{
574	return B_ERROR;
575}
576
577
578BArchivable *
579BShelf::Instantiate(BMessage *data)
580{
581	return NULL;
582}
583
584
585void
586BShelf::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
702status_t
703BShelf::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
728void
729BShelf::SetDirty(bool state)
730{
731	fDirty = state;
732}
733
734
735bool
736BShelf::IsDirty() const
737{
738	return fDirty;
739}
740
741
742BHandler *
743BShelf::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
827status_t
828BShelf::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
842status_t
843BShelf::Perform(perform_code d, void *arg)
844{
845	return BHandler::Perform(d, arg);
846}
847
848
849bool
850BShelf::AllowsDragging() const
851{
852	return fAllowDragging;
853}
854
855
856void
857BShelf::SetAllowsDragging(bool state)
858{
859	fAllowDragging = state;
860}
861
862
863bool
864BShelf::AllowsZombies() const
865{
866	return fAllowZombies;
867}
868
869
870void
871BShelf::SetAllowsZombies(bool state)
872{
873	fAllowZombies = state;
874}
875
876
877bool
878BShelf::DisplaysZombies() const
879{
880	return fDisplayZombies;
881}
882
883
884void
885BShelf::SetDisplaysZombies(bool state)
886{
887	fDisplayZombies = state;
888}
889
890
891bool
892BShelf::IsTypeEnforced() const
893{
894	return fTypeEnforced;
895}
896
897
898void
899BShelf::SetTypeEnforced(bool state)
900{
901	fTypeEnforced = state;
902}
903
904
905status_t
906BShelf::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
921status_t
922BShelf::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
937BDataIO *
938BShelf::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
951status_t
952BShelf::AddReplicant(BMessage *data, BPoint location)
953{
954	return _AddReplicant(data, &location, fGenCount++);
955}
956
957
958status_t
959BShelf::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
971status_t
972BShelf::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
984status_t
985BShelf::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
995int32
996BShelf::CountReplicants() const
997{
998	return fReplicants.CountItems();
999}
1000
1001
1002BMessage *
1003BShelf::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
1030int32
1031BShelf::IndexOf(const BView* replicantView) const
1032{
1033	return replicant_data::IndexOf(&fReplicants, replicantView, false);
1034}
1035
1036
1037int32
1038BShelf::IndexOf(const BMessage *archive) const
1039{
1040	return replicant_data::IndexOf(&fReplicants, archive);
1041}
1042
1043
1044int32
1045BShelf::IndexOf(uint32 id) const
1046{
1047	return replicant_data::IndexOf(&fReplicants, id);
1048}
1049
1050
1051bool
1052BShelf::CanAcceptReplicantMessage(BMessage*) const
1053{
1054	return true;
1055}
1056
1057
1058bool
1059BShelf::CanAcceptReplicantView(BRect, BView*, BMessage*) const
1060{
1061	return true;
1062}
1063
1064
1065BPoint
1066BShelf::AdjustReplicantBy(BRect, BMessage*) const
1067{
1068	return B_ORIGIN;
1069}
1070
1071
1072void
1073BShelf::ReplicantDeleted(int32 index, const BMessage *archive,
1074	const BView *replicant)
1075{
1076}
1077
1078
1079extern "C" void
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
1086void BShelf::_ReservedShelf2() {}
1087void BShelf::_ReservedShelf3() {}
1088void BShelf::_ReservedShelf4() {}
1089void BShelf::_ReservedShelf5() {}
1090void BShelf::_ReservedShelf6() {}
1091void BShelf::_ReservedShelf7() {}
1092void BShelf::_ReservedShelf8() {}
1093
1094
1095BShelf::BShelf(const BShelf&)
1096{
1097}
1098
1099
1100BShelf &
1101BShelf::operator=(const BShelf &)
1102{
1103	return *this;
1104}
1105
1106
1107status_t
1108BShelf::_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
1139void
1140BShelf::_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
1203status_t
1204BShelf::_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
1258status_t
1259BShelf::_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
1364BView *
1365BShelf::_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 */
1412void
1413BShelf::_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_ *
1442BShelf::_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
1475status_t
1476BShelf::_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 */
1544BArchivable *
1545BShelf::_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