⛏️ index : haiku.git

/*
 * Copyright 2001-2010, Haiku, Inc.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Marc Flerackers (mflerackers@androme.be)
 *		Axel Dörfler, axeld@pinc-software.de
 *		Jérôme Duval
 *		René Gollent
 *		Alexandre Deckner, alex@zappotek.com
 */

/*!	BShelf stores replicant views that are dropped onto it */

#include <Shelf.h>

#include <pthread.h>

#include <AutoDeleter.h>
#include <AutoLock.h>
#include <Beep.h>
#include <Dragger.h>
#include <Entry.h>
#include <File.h>
#include <Looper.h>
#include <Message.h>
#include <MessageFilter.h>
#include <Messenger.h>
#include <Point.h>
#include <PropertyInfo.h>
#include <Rect.h>
#include <String.h>
#include <View.h>

#include <ViewPrivate.h>

#include "ZombieReplicantView.h"

#include <stdio.h>
#include <string.h>

#include <map>
#include <utility>


namespace {

typedef std::map<BString, std::pair<image_id, int32> > LoadedImageMap;

struct LoadedImages {
	LoadedImageMap			images;

	LoadedImages()
		:
		fLock("BShelf loaded image map")
	{
	}

	bool Lock()
	{
		return fLock.Lock();
	}

	void Unlock()
	{
		fLock.Unlock();
	}

	static LoadedImages* Default()
	{
		if (sDefaultInstance == NULL)
			pthread_once(&sDefaultInitOnce, &_InitSingleton);

		return sDefaultInstance;
	}

private:
	static void _InitSingleton()
	{
		sDefaultInstance = new LoadedImages;
	}

private:
	BLocker					fLock;

	static pthread_once_t	sDefaultInitOnce;
	static LoadedImages*	sDefaultInstance;
};

pthread_once_t LoadedImages::sDefaultInitOnce = PTHREAD_ONCE_INIT;
LoadedImages* LoadedImages::sDefaultInstance = NULL;

}	// unnamed namespace


static property_info sShelfPropertyList[] = {
	{
		"Replicant",
		{ B_COUNT_PROPERTIES, B_CREATE_PROPERTY },
		{ B_DIRECT_SPECIFIER },
		NULL, 0,
	},

	{
		"Replicant",
		{ B_DELETE_PROPERTY, B_GET_PROPERTY },
		{ B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, B_NAME_SPECIFIER, B_ID_SPECIFIER },
		NULL, 0,
	},

	{
		"Replicant",
		{},
		{ B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, B_NAME_SPECIFIER, B_ID_SPECIFIER },
		"... of Replicant {index | name | id} of ...", 0,
	},

	{ 0 }
};

static property_info sReplicantPropertyList[] = {
	{
		"ID",
		{ B_GET_PROPERTY },
		{ B_DIRECT_SPECIFIER },
		NULL, 0, { B_INT32_TYPE }
	},

	{
		"Name",
		{ B_GET_PROPERTY },
		{ B_DIRECT_SPECIFIER },
		NULL, 0, { B_STRING_TYPE }
	},

	{
		"Signature",
		{ B_GET_PROPERTY },
		{ B_DIRECT_SPECIFIER },
		NULL, 0, { B_STRING_TYPE }
	},

	{
		"Suites",
		{ B_GET_PROPERTY },
		{ B_DIRECT_SPECIFIER },
		NULL, 0, { B_PROPERTY_INFO_TYPE }
	},

	{
		"View",
		{ },
		{ B_DIRECT_SPECIFIER },
		NULL, 0,
	},

	{ 0 }
};


namespace BPrivate {

struct replicant_data {
	replicant_data(BMessage *message, BView *view, BDragger *dragger,
		BDragger::relation relation, unsigned long id);
	replicant_data();
	~replicant_data();

	static replicant_data* Find(BList const *list, BMessage const *msg);
	static replicant_data* Find(BList const *list, BView const *view, bool allowZombie);
	static replicant_data* Find(BList const *list, unsigned long id);

	static int32 IndexOf(BList const *list, BMessage const *msg);
	static int32 IndexOf(BList const *list, BView const *view, bool allowZombie);
	static int32 IndexOf(BList const *list, unsigned long id);

	status_t Archive(BMessage *msg);

	BMessage*			message;
	BView*				view;
	BDragger*			dragger;
	BDragger::relation	relation;
	unsigned long		id;
	status_t			error;
	BView*				zombie_view;
};

class ShelfContainerViewFilter : public BMessageFilter {
	public:
		ShelfContainerViewFilter(BShelf *shelf, BView *view);

		filter_result	Filter(BMessage *msg, BHandler **handler);

	private:
		filter_result	_ObjectDropFilter(BMessage *msg, BHandler **handler);

		BShelf	*fShelf;
		BView	*fView;
};

class ReplicantViewFilter : public BMessageFilter {
	public:
		ReplicantViewFilter(BShelf *shelf, BView *view);

		filter_result Filter(BMessage *message, BHandler **handler);

	private:
		BShelf	*fShelf;
		BView	*fView;
};

}	// namespace BPrivate


using BPrivate::replicant_data;
using BPrivate::ReplicantViewFilter;
using BPrivate::ShelfContainerViewFilter;


//	#pragma mark -


/*!	\brief Helper function for BShelf::_AddReplicant()
*/
static status_t
send_reply(BMessage* message, status_t status, uint32 uniqueID)
{
	if (message->IsSourceWaiting()) {
		BMessage reply(B_REPLY);
		reply.AddInt32("id", uniqueID);
		reply.AddInt32("error", status);
		message->SendReply(&reply);
	}

	return status;
}


static bool
find_replicant(BList &list, const char *className, const char *addOn)
{
	int32 i = 0;
	replicant_data *item;
	while ((item = (replicant_data *)list.ItemAt(i++)) != NULL) {
		const char *replicantClassName;
		const char *replicantAddOn;
		if (item->message->FindString("class", &replicantClassName) == B_OK
			&& item->message->FindString("add_on", &replicantAddOn) == B_OK
			&& !strcmp(className, replicantClassName)
			&& addOn != NULL && replicantAddOn != NULL
			&& !strcmp(addOn, replicantAddOn))
		return true;
	}
	return false;
}


//	#pragma mark -


replicant_data::replicant_data(BMessage *_message, BView *_view, BDragger *_dragger,
	BDragger::relation _relation, unsigned long _id)
	:
	message(_message),
	view(_view),
	dragger(NULL),
	relation(_relation),
	id(_id),
	error(B_OK),
	zombie_view(NULL)
{
}


replicant_data::replicant_data()
	:
	message(NULL),
	view(NULL),
	dragger(NULL),
	relation(BDragger::TARGET_UNKNOWN),
	id(0),
	error(B_ERROR),
	zombie_view(NULL)
{
}

replicant_data::~replicant_data()
{
	delete message;
}

status_t
replicant_data::Archive(BMessage* msg)
{
	status_t result = B_OK;
	BMessage archive;
	if (view)
		result = view->Archive(&archive);
	else if (zombie_view)
		result = zombie_view->Archive(&archive);

	if (result != B_OK)
		return result;

	msg->AddInt32("uniqueid", id);
	BPoint pos (0,0);
	msg->AddMessage("message", &archive);
	if (view)
		pos = view->Frame().LeftTop();
	else if (zombie_view)
		pos = zombie_view->Frame().LeftTop();
	msg->AddPoint("position", pos);

	return result;
}

//static
replicant_data *
replicant_data::Find(BList const *list, BMessage const *msg)
{
	int32 i = 0;
	replicant_data *item;
	while ((item = (replicant_data*)list->ItemAt(i++)) != NULL) {
		if (item->message == msg)
			return item;
	}

	return NULL;
}


//static
replicant_data *
replicant_data::Find(BList const *list, BView const *view, bool allowZombie)
{
	int32 i = 0;
	replicant_data *item;
	while ((item = (replicant_data*)list->ItemAt(i++)) != NULL) {
		if (item->view == view)
			return item;

		if (allowZombie && item->zombie_view == view)
			return item;
	}

	return NULL;
}


//static
replicant_data *
replicant_data::Find(BList const *list, unsigned long id)
{
	int32 i = 0;
	replicant_data *item;
	while ((item = (replicant_data*)list->ItemAt(i++)) != NULL) {
		if (item->id == id)
			return item;
	}

	return NULL;
}


//static
int32
replicant_data::IndexOf(BList const *list, BMessage const *msg)
{
	int32 i = 0;
	replicant_data *item;
	while ((item = (replicant_data*)list->ItemAt(i)) != NULL) {
		if (item->message == msg)
			return i;
		i++;
	}

	return -1;
}


//static
int32
replicant_data::IndexOf(BList const *list, BView const *view, bool allowZombie)
{
	int32 i = 0;
	replicant_data *item;
	while ((item = (replicant_data*)list->ItemAt(i)) != NULL) {
		if (item->view == view)
			return i;

		if (allowZombie && item->zombie_view == view)
			return i;
		i++;
	}

	return -1;
}


//static
int32
replicant_data::IndexOf(BList const *list, unsigned long id)
{
	int32 i = 0;
	replicant_data *item;
	while ((item = (replicant_data*)list->ItemAt(i)) != NULL) {
		if (item->id == id)
			return i;
		i++;
	}

	return -1;
}


//	#pragma mark -


ShelfContainerViewFilter::ShelfContainerViewFilter(BShelf *shelf, BView *view)
	: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
	fShelf(shelf),
	fView(view)
{
}


filter_result
ShelfContainerViewFilter::Filter(BMessage *msg, BHandler **handler)
{
	filter_result filter = B_DISPATCH_MESSAGE;

	if (msg->what == B_ARCHIVED_OBJECT
		|| msg->what == B_ABOUT_REQUESTED)
		return _ObjectDropFilter(msg, handler);

	return filter;
}


filter_result
ShelfContainerViewFilter::_ObjectDropFilter(BMessage *msg, BHandler **_handler)
{
	BView *mouseView = NULL;
	if (*_handler)
		mouseView = dynamic_cast<BView*>(*_handler);

	if (msg->WasDropped()) {
		if (!fShelf->fAllowDragging)
			return B_SKIP_MESSAGE;
	}

	BPoint point;
	BPoint offset;

	if (msg->WasDropped()) {
		point = msg->DropPoint(&offset);
		point = mouseView->ConvertFromScreen(point - offset);
	}

	BLooper *looper = NULL;
	BHandler *handler = msg->ReturnAddress().Target(&looper);

	if (Looper() == looper) {
		BDragger *dragger = NULL;
		if (handler)
			dragger = dynamic_cast<BDragger*>(handler);
		else
			return B_SKIP_MESSAGE;

		BRect rect;
		if (dragger->fRelation == BDragger::TARGET_IS_CHILD)
			rect = dragger->Frame();
		else
			rect = dragger->fTarget->Frame();
		rect.OffsetTo(point);
		point = rect.LeftTop() + fShelf->AdjustReplicantBy(rect, msg);

		if (dragger->fRelation == BDragger::TARGET_IS_PARENT)
			dragger->fTarget->MoveTo(point);
		else if (dragger->fRelation == BDragger::TARGET_IS_CHILD)
			dragger->MoveTo(point);
		else {
			//TODO: TARGET_UNKNOWN/TARGET_SIBLING
		}

	} else {
		if (fShelf->_AddReplicant(msg, &point, fShelf->fGenCount++) == B_OK)
			Looper()->DetachCurrentMessage();
	}

	return B_SKIP_MESSAGE;
}


//	#pragma mark -


ReplicantViewFilter::ReplicantViewFilter(BShelf *shelf, BView *view)
	: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
	fShelf(shelf),
	fView(view)
{
}


filter_result
ReplicantViewFilter::Filter(BMessage *message, BHandler **handler)
{
	if (message->what == kDeleteReplicant) {
		if (handler != NULL)
			*handler = fShelf;
		message->AddPointer("_target", fView);
	}
	return B_DISPATCH_MESSAGE;
}


//	#pragma mark -


BShelf::BShelf(BView *view, bool allowDrags, const char *shelfType)
	: BHandler(shelfType)
{
	_InitData(NULL, NULL, view, allowDrags);
}


BShelf::BShelf(const entry_ref *ref, BView *view, bool allowDrags,
	const char *shelfType)
	: BHandler(shelfType)
{
	_InitData(new BEntry(ref), NULL, view, allowDrags);
}


BShelf::BShelf(BDataIO *stream, BView *view, bool allowDrags,
	const char *shelfType)
	: BHandler(shelfType)
{
	_InitData(NULL, stream, view, allowDrags);
}


BShelf::BShelf(BMessage *data)
	: BHandler(data)
{
	// TODO: Implement ?
}


BShelf::~BShelf()
{
	Save();

	// we own fStream only when fEntry is set
	if (fEntry != NULL) {
		delete fEntry;
		delete fStream;
	}

	while (fReplicants.CountItems() > 0) {
		replicant_data *data = (replicant_data *)fReplicants.ItemAt(0);
		fReplicants.RemoveItem((int32)0);
		delete data;
	}

	fContainerView->_SetShelf(NULL);
}


status_t
BShelf::Archive(BMessage *data, bool deep) const
{
	return B_ERROR;
}


BArchivable *
BShelf::Instantiate(BMessage *data)
{
	return NULL;
}


void
BShelf::MessageReceived(BMessage *msg)
{
	if (msg->what == kDeleteReplicant) {
		BHandler *replicant = NULL;
		if (msg->FindPointer("_target", (void **)&replicant) == B_OK) {
			BView *view = dynamic_cast<BView *>(replicant);
			if (view != NULL)
				DeleteReplicant(view);
		}
		return;
	}

	BMessage replyMsg(B_REPLY);
	status_t err = B_BAD_SCRIPT_SYNTAX;

	BMessage specifier;
	int32 what;
	const char *prop;
	int32 index;
	if (msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK)
		return BHandler::MessageReceived(msg);

	switch (msg->what) {
		case B_DELETE_PROPERTY:
		case B_GET_PROPERTY:
		case B_GET_SUPPORTED_SUITES:
			if (strcmp(prop, "Replicant") == 0) {
				BMessage reply;
				int32 i;
				uint32 ID;
				BView *replicant = NULL;
				BMessage *repMessage = NULL;
				err = _GetProperty(&specifier, &reply);
				if (err == B_OK)
					err = reply.FindInt32("index", &i);

				if (err == B_OK && msg->what == B_DELETE_PROPERTY) { // Delete Replicant
					err = DeleteReplicant(i);
					break;
				}
				if (err == B_OK && msg->what == B_GET_SUPPORTED_SUITES) {
					err = replyMsg.AddString("suites", "suite/vnd.Be-replicant");
					if (err == B_OK) {
						BPropertyInfo propInfo(sReplicantPropertyList);
						err = replyMsg.AddFlat("messages", &propInfo);
					}
					break;
				}
				if (err == B_OK )
					repMessage = ReplicantAt(i, &replicant, &ID, &err);
				if (err == B_OK && replicant) {
					msg->PopSpecifier();
					BMessage archive;
					err = replicant->Archive(&archive);
					if (err == B_OK && msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK) {
						err = replyMsg.AddMessage("result", &archive);
						break;
					}
					// now handles the replicant suite
					err = B_BAD_SCRIPT_SYNTAX;
					if (msg->what != B_GET_PROPERTY)
						break;
					if (strcmp(prop, "ID") == 0) {
						err = replyMsg.AddInt32("result", ID);
					} else if (strcmp(prop, "Name") == 0) {
						err = replyMsg.AddString("result", replicant->Name());
					} else if (strcmp(prop, "Signature") == 0) {
						const char *add_on = NULL;
						err = repMessage->FindString("add_on", &add_on);
						if (err == B_OK)
							err = replyMsg.AddString("result", add_on);
					} else if (strcmp(prop, "Suites") == 0) {
						err = replyMsg.AddString("suites", "suite/vnd.Be-replicant");
						if (err == B_OK) {
							BPropertyInfo propInfo(sReplicantPropertyList);
							err = replyMsg.AddFlat("messages", &propInfo);
						}
					}
				}
				break;
			}
			return BHandler::MessageReceived(msg);

		case B_COUNT_PROPERTIES:
			if (strcmp(prop, "Replicant") == 0) {
				err = replyMsg.AddInt32("result", CountReplicants());
				break;
			}
			return BHandler::MessageReceived(msg);

		case B_CREATE_PROPERTY:
		{
			BMessage replicantMsg;
			BPoint pos;
			if (msg->FindMessage("data", &replicantMsg) == B_OK
				&& msg->FindPoint("location", &pos) == B_OK) {
					err = AddReplicant(&replicantMsg, pos);
			}
		}
		break;
	}

	if (err < B_OK) {
		replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;

		if (err == B_BAD_SCRIPT_SYNTAX)
			replyMsg.AddString("message", "Didn't understand the specifier(s)");
		else
			replyMsg.AddString("message", strerror(err));
	}

	replyMsg.AddInt32("error", err);
	msg->SendReply(&replyMsg);
}


status_t
BShelf::Save()
{
	status_t status = B_ERROR;
	if (fEntry != NULL) {
		BFile *file = new BFile(fEntry, B_READ_WRITE | B_ERASE_FILE);
		status = file->InitCheck();
		if (status != B_OK) {
			delete file;
			return status;
		}
		delete fStream;
		fStream = file;
	}

	if (fStream != NULL) {
		BMessage message;
		status = _Archive(&message);
		if (status == B_OK)
			status = message.Flatten(fStream);
	}

	return status;
}


void
BShelf::SetDirty(bool state)
{
	fDirty = state;
}


bool
BShelf::IsDirty() const
{
	return fDirty;
}


BHandler *
BShelf::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
						int32 form, const char *property)
{
	BPropertyInfo shelfPropInfo(sShelfPropertyList);
	BHandler *target = NULL;
	BView *replicant = NULL;

	switch (shelfPropInfo.FindMatch(msg, 0, specifier, form, property)) {
		case 0:
			target = this;
			break;
		case 1:
			if (msg->PopSpecifier() != B_OK) {
				target = this;
				break;
			}
			msg->SetCurrentSpecifier(index);
			// fall through
		case 2: {
			BMessage reply;
			status_t err = _GetProperty(specifier, &reply);
			int32 i;
			uint32 ID;
			if (err == B_OK)
				err = reply.FindInt32("index", &i);
			if (err == B_OK)
				ReplicantAt(i, &replicant, &ID, &err);

			if (err == B_OK && replicant != NULL) {
				if (index == 0)
					return this;
			} else {
				BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
				replyMsg.AddInt32("error", B_BAD_INDEX);
				replyMsg.AddString("message", "Cannot find replicant at/with specified index/name.");
				msg->SendReply(&replyMsg);
			}
			}
			msg->PopSpecifier();
			break;
	}

	if (!replicant) {
		if (target)
			return target;
		return BHandler::ResolveSpecifier(msg, index, specifier, form,
			property);
	}

	int32 repIndex;
	status_t err = msg->GetCurrentSpecifier(&repIndex, specifier, &form, &property);
	if (err) {
		BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
		reply.AddInt32("error", err);
		msg->SendReply(&reply);
		return NULL;
	}

	BPropertyInfo replicantPropInfo(sReplicantPropertyList);
	switch (replicantPropInfo.FindMatch(msg, 0, specifier, form, property)) {
		case 0:
		case 1:
		case 2:
		case 3:
			msg->SetCurrentSpecifier(index);
			target = this;
			break;
		case 4:
			target = replicant;
			msg->PopSpecifier();
			break;
		default:
			break;
	}
	if (!target) {
		BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
		replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX);
		replyMsg.AddString("message", "Didn't understand the specifier(s)");
		msg->SendReply(&replyMsg);
	}
	return target;
}


status_t
BShelf::GetSupportedSuites(BMessage *message)
{
	status_t err;
	err = message->AddString("suites", "suite/vnd.Be-shelf");
	if (err == B_OK) {
		BPropertyInfo propInfo(sShelfPropertyList);
		err = message->AddFlat("messages", &propInfo);
	}
	if (err == B_OK)
		return BHandler::GetSupportedSuites(message);
	return err;
}


status_t
BShelf::Perform(perform_code d, void *arg)
{
	return BHandler::Perform(d, arg);
}


bool
BShelf::AllowsDragging() const
{
	return fAllowDragging;
}


void
BShelf::SetAllowsDragging(bool state)
{
	fAllowDragging = state;
}


bool
BShelf::AllowsZombies() const
{
	return fAllowZombies;
}


void
BShelf::SetAllowsZombies(bool state)
{
	fAllowZombies = state;
}


bool
BShelf::DisplaysZombies() const
{
	return fDisplayZombies;
}


void
BShelf::SetDisplaysZombies(bool state)
{
	fDisplayZombies = state;
}


bool
BShelf::IsTypeEnforced() const
{
	return fTypeEnforced;
}


void
BShelf::SetTypeEnforced(bool state)
{
	fTypeEnforced = state;
}


status_t
BShelf::SetSaveLocation(BDataIO *data_io)
{
	fDirty = true;

	if (fEntry != NULL) {
		delete fEntry;
		fEntry = NULL;
	}

	fStream = data_io;

	return B_OK;
}


status_t
BShelf::SetSaveLocation(const entry_ref *ref)
{
	fDirty = true;

	if (fEntry)
		delete fEntry;
	else
		fStream = NULL;

	fEntry = new BEntry(ref);

	return B_OK;
}


BDataIO *
BShelf::SaveLocation(entry_ref *ref) const
{
	if (fStream) {
		if (ref)
			*ref = entry_ref();
		return fStream;
	} else if (fEntry && ref)
		fEntry->GetRef(ref);

	return NULL;
}


status_t
BShelf::AddReplicant(BMessage *data, BPoint location)
{
	return _AddReplicant(data, &location, fGenCount++);
}


status_t
BShelf::DeleteReplicant(BView *replicant)
{
	int32 index = replicant_data::IndexOf(&fReplicants, replicant, true);

	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
	if (item == NULL)
		return B_BAD_VALUE;

	return _DeleteReplicant(item);
}


status_t
BShelf::DeleteReplicant(BMessage *data)
{
	int32 index = replicant_data::IndexOf(&fReplicants, data);

	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
	if (!item)
		return B_BAD_VALUE;

	return _DeleteReplicant(item);
}


status_t
BShelf::DeleteReplicant(int32 index)
{
	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
	if (!item)
		return B_BAD_INDEX;

	return _DeleteReplicant(item);
}


int32
BShelf::CountReplicants() const
{
	return fReplicants.CountItems();
}


BMessage *
BShelf::ReplicantAt(int32 index, BView **_view, uint32 *_uniqueID,
	status_t *_error) const
{
	replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
	if (item == NULL) {
		// no replicant found
		if (_view)
			*_view = NULL;
		if (_uniqueID)
			*_uniqueID = ~(uint32)0;
		if (_error)
			*_error = B_BAD_INDEX;

		return NULL;
	}

	if (_view)
		*_view = item->view;
	if (_uniqueID)
		*_uniqueID = item->id;
	if (_error)
		*_error = item->error;

	return item->message;
}


int32
BShelf::IndexOf(const BView* replicantView) const
{
	return replicant_data::IndexOf(&fReplicants, replicantView, false);
}


int32
BShelf::IndexOf(const BMessage *archive) const
{
	return replicant_data::IndexOf(&fReplicants, archive);
}


int32
BShelf::IndexOf(uint32 id) const
{
	return replicant_data::IndexOf(&fReplicants, id);
}


bool
BShelf::CanAcceptReplicantMessage(BMessage*) const
{
	return true;
}


bool
BShelf::CanAcceptReplicantView(BRect, BView*, BMessage*) const
{
	return true;
}


BPoint
BShelf::AdjustReplicantBy(BRect, BMessage*) const
{
	return B_ORIGIN;
}


void
BShelf::ReplicantDeleted(int32 index, const BMessage *archive,
	const BView *replicant)
{
}


extern "C" void
_ReservedShelf1__6BShelfFv(BShelf *const, int32, const BMessage*, const BView*)
{
	// is not contained in BeOS R5's libbe, so we leave it empty
}


void BShelf::_ReservedShelf2() {}
void BShelf::_ReservedShelf3() {}
void BShelf::_ReservedShelf4() {}
void BShelf::_ReservedShelf5() {}
void BShelf::_ReservedShelf6() {}
void BShelf::_ReservedShelf7() {}
void BShelf::_ReservedShelf8() {}


BShelf::BShelf(const BShelf&)
{
}


BShelf &
BShelf::operator=(const BShelf &)
{
	return *this;
}


status_t
BShelf::_Archive(BMessage *data) const
{
	status_t status = BHandler::Archive(data);
	if (status != B_OK)
		return status;

	status = data->AddBool("_zom_dsp", DisplaysZombies());
	if (status != B_OK)
		return status;

	status = data->AddBool("_zom_alw", AllowsZombies());
	if (status != B_OK)
		return status;

	status = data->AddInt32("_sg_cnt", fGenCount);
	if (status != B_OK)
		return status;

	BMessage archive('ARCV');
	for (int32 i = 0; i < fReplicants.CountItems(); i++) {
		if (((replicant_data *)fReplicants.ItemAt(i))->Archive(&archive) == B_OK)
			status = data->AddMessage("replicant", &archive);
		if (status != B_OK)
			break;
		archive.MakeEmpty();
	}

	return status;
}


void
BShelf::_InitData(BEntry *entry, BDataIO *stream, BView *view,
	bool allowDrags)
{
	fContainerView = view;
	fStream = NULL;
	fEntry = entry;
	fFilter = NULL;
	fGenCount = 1;
	fAllowDragging = allowDrags;
	fDirty = true;
	fDisplayZombies = false;
	fAllowZombies = true;
	fTypeEnforced = false;

	if (fEntry != NULL)
		fStream = new BFile(entry, B_READ_ONLY);
	else
		fStream = stream;

	fFilter = new ShelfContainerViewFilter(this, fContainerView);

	fContainerView->AddFilter(fFilter);
	fContainerView->_SetShelf(this);

	if (fStream != NULL) {
		BMessage archive;

		if (archive.Unflatten(fStream) == B_OK) {
			bool allowZombies;
			if (archive.FindBool("_zom_dsp", &allowZombies) != B_OK)
				allowZombies = false;

			SetDisplaysZombies(allowZombies);

			if (archive.FindBool("_zom_alw", &allowZombies) != B_OK)
				allowZombies = true;

			SetAllowsZombies(allowZombies);

			int32 genCount;
			if (!archive.FindInt32("_sg_cnt", &genCount))
				genCount = 1;

			BMessage replicant;
			for (int32 i = 0; archive.FindMessage("replicant", i, &replicant)
				== B_OK; i++) {
				BPoint point;
				BMessage *replMsg = new BMessage();
				ObjectDeleter<BMessage> deleter(replMsg);
				replicant.FindPoint("position", &point);
				if (replicant.FindMessage("message", replMsg) == B_OK)
					if (AddReplicant(replMsg, point) == B_OK) {
						// Detach the deleter since AddReplicant is taking
						// ownership on success. In R2 API this should be
						// changed to take always ownership on the message.
						deleter.Detach();
					}
			}
		}
	}
}


status_t
BShelf::_DeleteReplicant(replicant_data* item)
{
	BView *view = item->view;
	if (view == NULL)
		view = item->zombie_view;

	if (view != NULL)
		view->RemoveSelf();

	if (item->dragger != NULL)
		item->dragger->RemoveSelf();

	int32 index = replicant_data::IndexOf(&fReplicants, item->message);

	ReplicantDeleted(index, item->message, view);

	fReplicants.RemoveItem(item);

	if (item->relation == BDragger::TARGET_IS_PARENT
		|| item->relation == BDragger::TARGET_IS_SIBLING) {
		delete view;
	}
	if (item->relation == BDragger::TARGET_IS_CHILD
		|| item->relation == BDragger::TARGET_IS_SIBLING) {
		delete item->dragger;
	}

	// Update use count for image and unload if necessary
	const char* signature = NULL;
	if (item->message->FindString("add_on", &signature) == B_OK
		&& signature != NULL) {
		LoadedImages* loadedImages = LoadedImages::Default();
		AutoLock<LoadedImages> lock(loadedImages);
		if (lock.IsLocked()) {
			LoadedImageMap::iterator it = loadedImages->images.find(
				BString(signature));

			if (it != loadedImages->images.end()) {
				(*it).second.second--;
				if ((*it).second.second <= 0) {
					unload_add_on((*it).second.first);
					loadedImages->images.erase(it);
				}
			}
		}
	}

	delete item;

	return B_OK;
}


//! Takes over ownership of \a data on success only
status_t
BShelf::_AddReplicant(BMessage *data, BPoint *location, uint32 uniqueID)
{
	// Check shelf types if needed
	if (fTypeEnforced) {
		const char *shelfType = NULL;
		if (data->FindString("shelf_type", &shelfType) == B_OK
			&& shelfType != NULL) {
			if (Name() && strcmp(shelfType, Name()) != 0) {
				printf("Replicant was rejected by BShelf: The BShelf's type and the Replicant's type don't match.");
				return send_reply(data, B_ERROR, uniqueID);
			} else {
				printf("Replicant was rejected by BShelf: Replicant indicated a <type> (%s), but the shelf does not.", shelfType);
				return send_reply(data, B_ERROR, uniqueID);
			}
		} else {
			printf("Replicant was rejected by BShelf: Replicant did not have a <type>");
			return send_reply(data, B_ERROR, uniqueID);
		}
	}

	// Check if we can accept this message
	if (!CanAcceptReplicantMessage(data)) {
		printf("Replicant was rejected by BShelf::CanAcceptReplicantMessage()");
		return send_reply(data, B_ERROR, uniqueID);
	}

	// Check if we can create multiple instances
	if (data->FindBool("be:load_each_time")) {
		const char *className = NULL;
		const char *addOn = NULL;

		if (data->FindString("class", &className) == B_OK
			&& data->FindString("add_on", &addOn) == B_OK) {
			if (find_replicant(fReplicants, className, addOn)) {
				printf("Replicant was rejected. Unique replicant already exists. class=%s, signature=%s",
					className, addOn);
				return send_reply(data, B_ERROR, uniqueID);
			}
		}
	}

	// Instantiate the object, if this fails we have a zombie
	image_id image = -1;
	BArchivable *archivable = _InstantiateObject(data, &image);

	BView *view = NULL;

	if (archivable != NULL) {
		view = dynamic_cast<BView*>(archivable);

		if (view == NULL)
			return send_reply(data, B_ERROR, uniqueID);
	}

	BDragger* dragger = NULL;
	BView* replicant = NULL;
	BDragger::relation relation = BDragger::TARGET_UNKNOWN;
	_BZombieReplicantView_* zombie = NULL;
	if (view != NULL) {
		const BPoint point = location ? *location : view->Frame().LeftTop();
		replicant = _GetReplicant(data, view, point, dragger, relation);
		if (replicant == NULL)
			return send_reply(data, B_ERROR, uniqueID);
	} else if (fDisplayZombies && fAllowZombies) {
		zombie = _CreateZombie(data, dragger);
	} else if (!fAllowZombies) {
		// There was no view, and we're not allowed to have any zombies
		// in the house
		return send_reply(data, B_ERROR, uniqueID);
	}

	// Update use count for image
	const char* signature = NULL;
	if (data->FindString("add_on", &signature) == B_OK && signature != NULL) {
		LoadedImages* loadedImages = LoadedImages::Default();
		AutoLock<LoadedImages> lock(loadedImages);
		if (lock.IsLocked()) {
			LoadedImageMap::iterator it = loadedImages->images.find(
				BString(signature));

			if (it == loadedImages->images.end())
				loadedImages->images.insert(LoadedImageMap::value_type(
					BString(signature), std::pair<image_id, int>(image, 1)));
			else
				(*it).second.second++;
		}
	}

	if (zombie == NULL) {
		data->RemoveName("_drop_point_");
		data->RemoveName("_drop_offset_");
	}

	replicant_data *item = new replicant_data(data, replicant, dragger,
		relation, uniqueID);

	item->error = B_OK;
	item->zombie_view = zombie;

	fReplicants.AddItem(item);

	return send_reply(data, B_OK, uniqueID);
}


BView *
BShelf::_GetReplicant(BMessage *data, BView *view, const BPoint &point,
	BDragger *&dragger, BDragger::relation &relation)
{
	// TODO: test me -- there seems to be lots of bugs parked here!
	BView *replicant = NULL;
	_GetReplicantData(data, view, replicant, dragger, relation);

	if (dragger != NULL)
		dragger->_SetViewToDrag(replicant);

	BRect frame = view->Frame().OffsetToCopy(point);
	if (!CanAcceptReplicantView(frame, replicant, data)) {
		// the view has not been accepted
		if (relation == BDragger::TARGET_IS_PARENT
			|| relation == BDragger::TARGET_IS_SIBLING) {
			delete replicant;
		}
		if (relation == BDragger::TARGET_IS_CHILD
			|| relation == BDragger::TARGET_IS_SIBLING) {
			delete dragger;
		}
		return NULL;
	}

	BPoint adjust = AdjustReplicantBy(frame, data);

	if (dragger != NULL)
		dragger->_SetShelf(this);

	// TODO: could be not correct for some relations
	view->MoveTo(point + adjust);

	// if it's a sibling or a child, we need to add the dragger
	if (relation == BDragger::TARGET_IS_SIBLING
		|| relation == BDragger::TARGET_IS_CHILD)
		fContainerView->AddChild(dragger);

	if (relation != BDragger::TARGET_IS_CHILD)
		fContainerView->AddChild(replicant);

	replicant->AddFilter(new ReplicantViewFilter(this, replicant));

	return replicant;
}


/* static */
void
BShelf::_GetReplicantData(BMessage *data, BView *view, BView *&replicant,
	BDragger *&dragger, BDragger::relation &relation)
{
	// Check if we have a dragger archived as "__widget" inside the message
	BMessage widget;
	if (data->FindMessage("__widget", &widget) == B_OK) {
		image_id draggerImage = B_ERROR;
		replicant = view;
		dragger = dynamic_cast<BDragger*>(_InstantiateObject(&widget, &draggerImage));
		// Replicant is a sibling, or unknown, if there isn't a dragger
		if (dragger != NULL)
			relation = BDragger::TARGET_IS_SIBLING;

	} else if ((dragger = dynamic_cast<BDragger*>(view)) != NULL) {
		// Replicant is child of the dragger
		relation = BDragger::TARGET_IS_CHILD;
		replicant = dragger->ChildAt(0);

	} else {
		// Replicant is parent of the dragger
		relation = BDragger::TARGET_IS_PARENT;
		replicant = view;
		dragger = dynamic_cast<BDragger *>(replicant->FindView("_dragger_"));
			// can be NULL, the replicant could not have a dragger at all
	}
}


_BZombieReplicantView_ *
BShelf::_CreateZombie(BMessage *data, BDragger *&dragger)
{
	// TODO: the zombies must be adjusted and moved as well!
	BRect frame;
	if (data->FindRect("_frame", &frame) != B_OK)
		frame = BRect();

	_BZombieReplicantView_ *zombie = NULL;
	if (data->WasDropped()) {
		BPoint offset;
		BPoint dropPoint = data->DropPoint(&offset);

		frame.OffsetTo(fContainerView->ConvertFromScreen(dropPoint) - offset);

		zombie = new _BZombieReplicantView_(frame, B_ERROR);

		frame.OffsetTo(B_ORIGIN);

		dragger = new BDragger(frame, zombie);
		dragger->_SetShelf(this);
		dragger->_SetZombied(true);

		zombie->AddChild(dragger);
		zombie->SetArchive(data);
		zombie->AddFilter(new ReplicantViewFilter(this, zombie));

		fContainerView->AddChild(zombie);
	}

	return zombie;
}


status_t
BShelf::_GetProperty(BMessage *msg, BMessage *reply)
{
	uint32 ID;
	status_t err = B_ERROR;
	BView *replicant = NULL;
	switch (msg->what) {
		case B_INDEX_SPECIFIER:	{
			int32 index = -1;
			if (msg->FindInt32("index", &index)!=B_OK)
				break;
			ReplicantAt(index, &replicant, &ID, &err);
			break;
		}
		case B_REVERSE_INDEX_SPECIFIER:	{
			int32 rindex;
			if (msg->FindInt32("index", &rindex) != B_OK)
				break;
			ReplicantAt(CountReplicants() - rindex, &replicant, &ID, &err);
			break;
		}
		case B_NAME_SPECIFIER: {
			const char *name;
			if (msg->FindString("name", &name) != B_OK)
				break;
			for (int32 i = 0; i < CountReplicants(); i++) {
				BView *view = NULL;
				ReplicantAt(i, &view, &ID, &err);
				if (err != B_OK || view == NULL)
					continue;
				if (view->Name() != NULL && strcmp(view->Name(), name) == 0) {
					replicant = view;
					break;
				}
				err = B_NAME_NOT_FOUND;
			}
			break;
		}
		case B_ID_SPECIFIER: {
			uint32 id;
			if (msg->FindInt32("id", (int32 *)&id) != B_OK)
				break;
			for (int32 i = 0; i < CountReplicants(); i++) {
				BView *view = NULL;
				ReplicantAt(i, &view, &ID, &err);
				if (err != B_OK || view == NULL)
					continue;
				if (ID == id) {
					replicant = view;
					break;
				}
				err = B_NAME_NOT_FOUND;
			}
			break;
		}
		default:
			break;
	}

	if (replicant) {
		reply->AddInt32("index", IndexOf(replicant));
		reply->AddInt32("ID", ID);
	}

	return err;
}


/* static */
BArchivable *
BShelf::_InstantiateObject(BMessage *archive, image_id *image)
{
	// Stay on the safe side. The constructor called by instantiate_object
	// could throw an exception, which we catch here. Otherwise our calling app
	// could die without notice.
	try {
		return instantiate_object(archive, image);
	} catch (...) {
		return NULL;
	}
}