mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
fahhh
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "reflection/enumconverter.h"
|
||||
#include "V8Tree/Property.h"
|
||||
|
||||
namespace RBX {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,778 @@
|
||||
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Reflection/Reflection.h"
|
||||
#include "Reflection/Event.h"
|
||||
#include "V8Tree/Property.h"
|
||||
#include "V8Xml/Reference.h"
|
||||
#include "V8Tree/Verb.h"
|
||||
|
||||
#include "rbx/Countable.h"
|
||||
#include "Util/Guid.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "boost/weak_ptr.hpp"
|
||||
#include "boost/shared_ptr.hpp"
|
||||
#include "boost/enable_shared_from_this.hpp"
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <boost/flyweight.hpp>
|
||||
|
||||
namespace RBX {
|
||||
|
||||
class Instance;
|
||||
|
||||
// Convenience class
|
||||
template <
|
||||
class Class,
|
||||
class BaseClass,
|
||||
const char* const& sClassName,
|
||||
Reflection::ClassDescriptor::Functionality functionality = Reflection::ClassDescriptor::PERSISTENT,
|
||||
Security::Permissions security = Security::None
|
||||
>
|
||||
class DescribedCreatable : public Reflection::Described<Class, sClassName, FactoryProduct<Class, BaseClass, sClassName, Instance>, functionality, security>
|
||||
{
|
||||
protected:
|
||||
inline DescribedCreatable() {}
|
||||
template<class Arg0>
|
||||
inline DescribedCreatable(Arg0 arg0):Reflection::Described<Class, sClassName, FactoryProduct<Class, BaseClass, sClassName, Instance>, functionality, security>(arg0) {}
|
||||
template<class Arg0, class Arg1>
|
||||
inline DescribedCreatable(Arg0 arg0, Arg1 arg1):Reflection::Described<Class, sClassName, FactoryProduct<Class, BaseClass, sClassName, Instance>, functionality, security>(arg0, arg1) {}
|
||||
};
|
||||
|
||||
// Convenience class
|
||||
template <
|
||||
class Class,
|
||||
class BaseClass,
|
||||
const char* const& sClassName,
|
||||
Reflection::ClassDescriptor::Functionality functionality = Reflection::ClassDescriptor::PERSISTENT,
|
||||
Security::Permissions security = Security::None
|
||||
>
|
||||
class DescribedNonCreatable : public Reflection::Described<Class, sClassName, NonFactoryProduct<BaseClass, sClassName>, functionality, security>
|
||||
{
|
||||
protected:
|
||||
inline DescribedNonCreatable() {}
|
||||
template<class Arg0>
|
||||
inline DescribedNonCreatable(Arg0 arg0):Reflection::Described<Class, sClassName, NonFactoryProduct<BaseClass, sClassName>, functionality, security>(arg0) {}
|
||||
template<class Arg0, class Arg1>
|
||||
inline DescribedNonCreatable(Arg0 arg0, Arg1 arg1):Reflection::Described<Class, sClassName, NonFactoryProduct<BaseClass, sClassName>, functionality, security>(arg0, arg1) {}
|
||||
template<class Arg0, class Arg1, class Arg2>
|
||||
inline DescribedNonCreatable(Arg0 arg0, Arg1 arg1, Arg2 arg2):Reflection::Described<Class, sClassName, NonFactoryProduct<BaseClass, sClassName>, functionality, security>(arg0, arg1, arg2) {}
|
||||
template<class Arg0, class Arg1, class Arg2, class Arg3>
|
||||
inline DescribedNonCreatable(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3):Reflection::Described<Class, sClassName, NonFactoryProduct<BaseClass, sClassName>, functionality, security>(arg0, arg1, arg2, arg3) {}
|
||||
};
|
||||
|
||||
class Instance;
|
||||
class ServiceProvider;
|
||||
|
||||
|
||||
/// A child has been added to the Notifier
|
||||
struct ChildAdded {
|
||||
public:
|
||||
shared_ptr<Instance> const child;
|
||||
ChildAdded(Instance* child);
|
||||
ChildAdded(const ChildAdded& event);
|
||||
private:
|
||||
ChildAdded& operator=(const ChildAdded&);
|
||||
};
|
||||
|
||||
/// A child has been removed from the Notifier
|
||||
struct ChildRemoved {
|
||||
public:
|
||||
shared_ptr<Instance> const child;
|
||||
ChildRemoved(Instance* child);
|
||||
ChildRemoved(const ChildRemoved& event);
|
||||
private:
|
||||
ChildRemoved& operator=(const ChildAdded&);
|
||||
};
|
||||
|
||||
/// A descendant has been added to the Notifier
|
||||
struct DescendantAdded {
|
||||
friend class Instance;
|
||||
public:
|
||||
shared_ptr<Instance> const instance;
|
||||
shared_ptr<Instance> const parent; // The direct parent of the descendant
|
||||
private:
|
||||
DescendantAdded(shared_ptr<Instance> instance, shared_ptr<Instance> parent)
|
||||
:instance(instance),parent(parent)
|
||||
{}
|
||||
DescendantAdded(Instance* instance, Instance* parent)
|
||||
:instance(shared_from(instance)),parent(shared_from(parent))
|
||||
{}
|
||||
};
|
||||
|
||||
/// A descendant of the Notifier is about to be removed
|
||||
struct DescendantRemoving {
|
||||
public:
|
||||
shared_ptr<Instance> const instance;
|
||||
shared_ptr<Instance> const parent; // The direct parent of the descendant before it is removed
|
||||
DescendantRemoving(const shared_ptr<Instance>& instance, const shared_ptr<Instance>& parent)
|
||||
:instance(instance),parent(parent)
|
||||
{}
|
||||
};
|
||||
|
||||
struct AncestorChanged {
|
||||
public:
|
||||
Instance* const child;
|
||||
Instance* const oldParent;
|
||||
Instance* const newParent;
|
||||
AncestorChanged(Instance* child, Instance* oldParent, Instance* newParent)
|
||||
:child(child),oldParent(oldParent),newParent(newParent)
|
||||
{}
|
||||
};
|
||||
|
||||
extern const char* const sInstance;
|
||||
|
||||
typedef std::vector<shared_ptr<Instance> > Instances;
|
||||
|
||||
class OnDemandInstance : public Allocator<OnDemandInstance>
|
||||
{
|
||||
public:
|
||||
rbx::signal<void(shared_ptr<Instance>)> childAddedSignal;
|
||||
rbx::signal<void(shared_ptr<Instance>)> childRemovedSignal;
|
||||
rbx::signal<void(shared_ptr<Instance>)> descendantAddedSignal;
|
||||
rbx::signal<void(shared_ptr<Instance>)> descendantRemovingSignal;
|
||||
rbx::signal<void(shared_ptr<Instance>)> instanceClonedSignal;
|
||||
|
||||
struct ThreadWaitingForChild
|
||||
{
|
||||
std::string childName;
|
||||
boost::function<void(shared_ptr<Instance>)> resumeFunction;
|
||||
};
|
||||
std::vector<ThreadWaitingForChild> threadsWaitingForChildren;
|
||||
|
||||
virtual ~OnDemandInstance(){};
|
||||
};
|
||||
|
||||
class Instance
|
||||
: public Reflection::Described<Instance, sInstance>
|
||||
, public GuidItem<Instance>
|
||||
, public Diagnostics::Countable<Instance>
|
||||
, public boost::noncopyable
|
||||
{
|
||||
friend class SetParentSentry;
|
||||
private:
|
||||
static void predelete(Instance* instance);
|
||||
void predelete();
|
||||
friend class Creatable<Instance>::Deleter;
|
||||
|
||||
bool archivable;
|
||||
bool isParentLocked;
|
||||
bool robloxLocked;
|
||||
bool isSettingParent;
|
||||
|
||||
boost::flyweight<std::string> name;
|
||||
|
||||
copy_on_write_ptr<Instances> children;
|
||||
Instance* parent; // this field is set after initialization by Instance::addChild
|
||||
|
||||
protected:
|
||||
Instance();
|
||||
Instance(const char* name);
|
||||
|
||||
// Destructor is protected. Call "destroy(instance)" instead of "delete instance".
|
||||
virtual ~Instance();
|
||||
|
||||
boost::scoped_ptr<OnDemandInstance> onDemandPtr;
|
||||
|
||||
virtual OnDemandInstance* initOnDemand();
|
||||
|
||||
public:
|
||||
const OnDemandInstance* onDemandRead() const;
|
||||
OnDemandInstance* onDemandWrite();
|
||||
|
||||
virtual void onGuidChanged();
|
||||
|
||||
void lockParent() { isParentLocked = true; }
|
||||
void unlockParent() { isParentLocked = false; }
|
||||
bool getIsParentLocked() const { return isParentLocked; }
|
||||
void securityCheck() const;
|
||||
void securityCheck(RBX::Security::Context& context) const;
|
||||
|
||||
static Reflection::PropDescriptor<Instance, bool> propArchivable;
|
||||
bool getIsArchivable() const { return archivable; }
|
||||
virtual void setIsArchivable(bool value);
|
||||
|
||||
// Call "destroy" to delete an Instance fully so it can't be used again
|
||||
virtual void destroy();
|
||||
void remove();
|
||||
|
||||
enum SaveFilter
|
||||
{
|
||||
SAVE_WORLD = 0,
|
||||
SAVE_GAME = 1,
|
||||
SAVE_ALL = 2,
|
||||
};
|
||||
|
||||
static const Reflection::PropDescriptor<Instance, std::string> desc_Name;
|
||||
static const Reflection::RefPropDescriptor<Instance, Instance> propParent;
|
||||
static const Reflection::PropDescriptor<Instance, bool> propRobloxLocked;
|
||||
|
||||
enum CombinedSignalType
|
||||
{
|
||||
CHILD_ADDED,
|
||||
CHILD_REMOVED,
|
||||
PROPERTY_CHANGED,
|
||||
EVENT_INVOCATION,
|
||||
OUTFIT_CHANGED,
|
||||
ANCESTRY_CHANGED,
|
||||
CLUMP_CHANGED,
|
||||
SLEEPING_CHANGED,
|
||||
HUMANOID_CHANGED
|
||||
};
|
||||
|
||||
class ICombinedSignalData
|
||||
{
|
||||
public:
|
||||
virtual ~ICombinedSignalData() {};
|
||||
};
|
||||
|
||||
class ChildAddedSignalData : public ICombinedSignalData
|
||||
{
|
||||
public:
|
||||
ChildAddedSignalData(const shared_ptr<Instance>& child)
|
||||
: child(child)
|
||||
{}
|
||||
|
||||
shared_ptr<Instance> child;
|
||||
};
|
||||
class ChildRemovedSignalData : public ICombinedSignalData
|
||||
{
|
||||
public:
|
||||
ChildRemovedSignalData(const shared_ptr<Instance>& child)
|
||||
: child(child)
|
||||
{}
|
||||
|
||||
shared_ptr<Instance> child;
|
||||
};
|
||||
class AncestryChangedSignalData: public ICombinedSignalData
|
||||
{
|
||||
public:
|
||||
AncestryChangedSignalData(const shared_ptr<Instance>& child, const shared_ptr<Instance>& newParent)
|
||||
: child(child)
|
||||
, newParent(newParent)
|
||||
{}
|
||||
|
||||
shared_ptr<Instance> child;
|
||||
shared_ptr<Instance> newParent;
|
||||
};
|
||||
class OutfitChangedSignalData: public ICombinedSignalData
|
||||
{
|
||||
public:
|
||||
OutfitChangedSignalData()
|
||||
{}
|
||||
};
|
||||
|
||||
class PropertyChangedSignalData : public ICombinedSignalData
|
||||
{
|
||||
public:
|
||||
PropertyChangedSignalData(const Reflection::PropertyDescriptor* propertyDescriptor)
|
||||
:propertyDescriptor(propertyDescriptor)
|
||||
{}
|
||||
const Reflection::PropertyDescriptor* propertyDescriptor;
|
||||
};
|
||||
|
||||
class EventInvocationSignalData : public ICombinedSignalData
|
||||
{
|
||||
public:
|
||||
EventInvocationSignalData(const Reflection::EventDescriptor* eventDescriptor, const Reflection::EventArguments* eventArguments, const SystemAddress* target)
|
||||
:eventDescriptor(eventDescriptor)
|
||||
,eventArguments(eventArguments)
|
||||
,target(target)
|
||||
{}
|
||||
const Reflection::EventDescriptor* eventDescriptor;
|
||||
const Reflection::EventArguments* eventArguments;
|
||||
const SystemAddress* target;
|
||||
};
|
||||
|
||||
class HumanoidChangedSignalData: public ICombinedSignalData
|
||||
{
|
||||
public:
|
||||
HumanoidChangedSignalData()
|
||||
{}
|
||||
};
|
||||
|
||||
virtual void humanoidChanged();
|
||||
|
||||
|
||||
void childAddedSignal(shared_ptr<Instance>& inst) { if(onDemandRead()) onDemandWrite()->childAddedSignal(inst); }
|
||||
rbx::signal<void(shared_ptr<Instance>)>* getOrCreateChildAddedSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->childAddedSignal : NULL; };
|
||||
|
||||
void childRemovedSignal(shared_ptr<Instance>& inst) { if(onDemandRead()) onDemandWrite()->childRemovedSignal(inst); }
|
||||
rbx::signal<void(shared_ptr<Instance>)>* getOrCreateChildRemovedSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->childRemovedSignal : NULL; };
|
||||
|
||||
void descendantAddedSignal(shared_ptr<Instance>& inst) { if(onDemandRead()) onDemandWrite()->descendantAddedSignal(inst); }
|
||||
rbx::signal<void(shared_ptr<Instance>)>* getOrCreateDescendantAddedSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->descendantAddedSignal : NULL; };
|
||||
|
||||
void descendantRemovingSignal(const shared_ptr<Instance>& inst) { if(onDemandRead()) onDemandWrite()->descendantRemovingSignal(inst); }
|
||||
rbx::signal<void(shared_ptr<Instance>)>* getOrCreateDescendantRemovingSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->descendantRemovingSignal : NULL; };
|
||||
|
||||
rbx::signal<void(shared_ptr<Instance>, shared_ptr<Instance>)> ancestryChangedSignal;
|
||||
rbx::signal<void(const Reflection::PropertyDescriptor*)> propertyChangedSignal;
|
||||
|
||||
// combinedSignal is an optimization. You could use the regular signals, like childAddedSignal.
|
||||
// However, if you are listening to many signals then you can save memory by listening to just the combinedSignal
|
||||
rbx::signal<void(CombinedSignalType, const ICombinedSignalData*)> combinedSignal;
|
||||
|
||||
shared_ptr<Instance> clone(CreatorRole creatorRole);
|
||||
virtual shared_ptr<Instance> luaClone(); //Just like regular clone, but it enforces the Instance limits
|
||||
static XmlElement* toNewXmlRoot(Instance* instance, RBX::CreatorRole creatorRole); // DB 12/5/05 - added here to centralize the spawner behavior - to, from XML
|
||||
|
||||
void removeAllChildren();
|
||||
|
||||
// security: this call has checks that prevent it from being called from most code.
|
||||
void destroyAllChildrenLua();
|
||||
|
||||
const std::string& getClassNameStr() const { return getClassName().toString(); } // used by reflection
|
||||
|
||||
std::string getReadableDebugId(int scopeLength) const { return getGuid().readableString(scopeLength); }
|
||||
std::string getReadableDebugId() const { return getGuid().readableString(); }
|
||||
std::string getReadableDebugId(int scopeLength) { return getGuid().readableString(scopeLength); }
|
||||
|
||||
inline Instance* getParent() {return parent;}
|
||||
inline const Instance* getParent() const {return parent;}
|
||||
private:
|
||||
inline Instance* getParentDangerous() const {return parent;} // only used by refProp descriptor
|
||||
|
||||
public:
|
||||
inline Instance* getRootAncestor() {return parent ? parent->getRootAncestor() : this;}
|
||||
inline const Instance* getRootAncestor() const {return parent ? parent->getRootAncestor() : this;}
|
||||
|
||||
static Instance* getRootAncestor(Instance* instance) { return instance ? instance->getRootAncestor() : NULL; }
|
||||
static const Instance* getRootAncestor(const Instance* instance) { return instance ? instance->getRootAncestor() : NULL; }
|
||||
|
||||
bool getRobloxLocked() const { return robloxLocked; }
|
||||
void setRobloxLocked(bool value);
|
||||
|
||||
bool contains(const Instance* child) const; // Recursive. Also returns true if this==child
|
||||
|
||||
void setParent(Instance* instance) { setParentInternal(instance, false); }
|
||||
bool setLockedParent(Instance* instance) { return setParentInternal(instance, true); }
|
||||
void setParent2(shared_ptr<Instance> instance) {
|
||||
setParent(instance.get());
|
||||
}
|
||||
void promoteChildren();
|
||||
static const void* getSetParentAddr();
|
||||
|
||||
// locks the parent and then sets it
|
||||
// if this fails to set the parent it will unlock the parent before returning
|
||||
// if the parent is successfully set the parent will stay locked
|
||||
void setAndLockParent(Instance* instance);
|
||||
|
||||
const std::string& getName() const {return name.get(); }
|
||||
virtual void setName(const std::string& value);
|
||||
|
||||
std::string getFullName() const; // Render up to (but not including) the root node
|
||||
std::string getFullNameForReflection() { return getFullName(); }
|
||||
|
||||
bool isAncestorOf(const Instance* descendant) const
|
||||
{
|
||||
if (descendant==NULL)
|
||||
return false;
|
||||
else if (descendant->getParent()==this)
|
||||
return true;
|
||||
else
|
||||
return isAncestorOf(descendant->getParent());
|
||||
}
|
||||
|
||||
bool isAncestorOf2(shared_ptr<Instance> descendant) { return isAncestorOf(descendant.get()); }
|
||||
bool isDescendantOf2(shared_ptr<Instance> ancestor) { return isDescendantOf(ancestor.get()); }
|
||||
|
||||
// isDescendantOf will return true if parent==NULL (consistent with the fact that a top-level Instance has parent==NULL)
|
||||
bool isDescendantOf(const Instance* ancestor) const
|
||||
{
|
||||
const Instance* parent = getParent();
|
||||
if (ancestor==parent)
|
||||
return true;
|
||||
else if (parent!=NULL)
|
||||
return parent->isDescendantOf(ancestor);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
Type* findFirstAncestorOfType()
|
||||
{
|
||||
Instance* parent = getParent();
|
||||
if (Type* parentType = this->fastDynamicCast<Type>(parent))
|
||||
return parentType;
|
||||
else if (parent!=NULL)
|
||||
return parent->findFirstAncestorOfType<Type>();
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t numChildren() const {return children ? children->size() : 0;}
|
||||
|
||||
int findChildIndex(const Instance* instance) const;
|
||||
virtual int getPersistentDataCost() const ;
|
||||
static int computeStringCost(const std::string& value)
|
||||
{
|
||||
return std::max<int>(1, value.length() / 100);
|
||||
}
|
||||
|
||||
|
||||
Instance* getChild(size_t index) {return (*children)[index].get();}
|
||||
const Instance* getChild(size_t index) const {return (*children)[index].get();}
|
||||
|
||||
void waitForChild(std::string childName, boost::function<void(shared_ptr<Instance>)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void checkParentWaitingForChildren();
|
||||
|
||||
// Find the first ancestor of a given descendant
|
||||
shared_ptr<Instance> findFirstAncestorOf(const Instance* descendant) const;
|
||||
|
||||
// TODO: findFirstChildByName should return const Instance*
|
||||
|
||||
const Instance* findConstFirstChildByName(const std::string& findName) const;
|
||||
Instance* findFirstChildByName(const std::string& findName) {
|
||||
return const_cast<Instance*>(findConstFirstChildByName(findName));
|
||||
}
|
||||
Instance* findFirstChildByNameDangerous(const std::string& findName) const { // used by reflection - breaking from const to non const
|
||||
return const_cast<Instance*>(findConstFirstChildByName(findName));
|
||||
}
|
||||
|
||||
// breadth-first search
|
||||
Instance* findFirstChildByNameRecursive(const std::string& findName);
|
||||
|
||||
// Used for Reflection:
|
||||
shared_ptr<Instance> findFirstChildByName2(std::string findName, bool recursive) {
|
||||
return recursive ? shared_from(findFirstChildByNameRecursive(findName)) : shared_from(findFirstChildByName(findName));
|
||||
}
|
||||
|
||||
// queryTypedChild
|
||||
template<class Type>
|
||||
Type* queryTypedChild(int index) {
|
||||
return dynamic_cast<Type*>((*children)[index].get());
|
||||
}
|
||||
template<class Type>
|
||||
const Type* queryTypedChild(int index) const {
|
||||
return dynamic_cast<const Type*>((*children)[index].get());
|
||||
}
|
||||
|
||||
// getTypedChild
|
||||
template<class Type>
|
||||
Type* getTypedChild(int index) {
|
||||
return rbx_static_cast<Type*>((*children)[index].get());
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
const Type* getTypedChild(int index) const {
|
||||
return rbx_static_cast<const Type*>((*children)[index].get());
|
||||
}
|
||||
|
||||
// queryTypedParent
|
||||
template<class Type>
|
||||
Type* queryTypedParent() {
|
||||
return dynamic_cast<Type*>(parent);
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
const Type* queryTypedParent() const {
|
||||
return dynamic_cast<const Type*>(parent);
|
||||
}
|
||||
|
||||
// getTypedParent
|
||||
template<class Type>
|
||||
Type* getTypedParent() {
|
||||
return rbx_static_cast<Type*>(parent);
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
const Type* getTypedParent() const {
|
||||
return rbx_static_cast<const Type*>(parent);
|
||||
}
|
||||
|
||||
// getTypedRoot
|
||||
template<class Type>
|
||||
Type* getTypedRoot() {
|
||||
RBXASSERT(dynamic_cast<Type*>(this));
|
||||
if (Type* typedParent = dynamic_cast<Type*>(parent)) {
|
||||
return typedParent->template getTypedRoot<Type>();
|
||||
}
|
||||
else {
|
||||
return static_cast<Type*>(this);
|
||||
}
|
||||
}
|
||||
template<class Type>
|
||||
const Type* getTypedRoot() const {
|
||||
RBXASSERT(dynamic_cast<const Type*>(this));
|
||||
if (const Type* typedParent = dynamic_cast<const Type*>(parent)) {
|
||||
return typedParent->template getTypedRoot<Type>();
|
||||
}
|
||||
else {
|
||||
return static_cast<const Type*>(this);
|
||||
}
|
||||
}
|
||||
|
||||
const copy_on_write_ptr<Instances>& getChildren() const { return children; }
|
||||
|
||||
// Used for reflection. Note that it might return NULL (or an empty container)
|
||||
// TODO - this is dangerous? getting const children?
|
||||
shared_ptr<const Instances> getChildren2() { return children.read(); }
|
||||
|
||||
template<class Func>
|
||||
inline void visitChildren(const Func& func) const {
|
||||
if (children)
|
||||
{
|
||||
boost::shared_ptr<const Instances> c(children.read());
|
||||
Instances::const_iterator end = c->end();
|
||||
for (Instances::const_iterator iter = c->begin(); iter!=end; ++iter)
|
||||
{
|
||||
const_cast<Func&>(func)(*iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
inline int countDescendantsOfType() const {
|
||||
int total = fastDynamicCast<Type>() ? 1 : 0;
|
||||
if (children)
|
||||
{
|
||||
boost::shared_ptr<const Instances> c(children.read());
|
||||
Instances::const_iterator end = c->end();
|
||||
for (Instances::const_iterator iter = c->begin(); iter!=end; ++iter)
|
||||
{
|
||||
total += (*iter)->countDescendantsOfType<Type>();
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
template<class Func>
|
||||
inline void visitDescendants(const Func& func) const {
|
||||
if (children)
|
||||
{
|
||||
boost::shared_ptr<const Instances> c(children.read());
|
||||
Instances::const_iterator end = c->end();
|
||||
for (Instances::const_iterator iter = c->begin(); iter!=end; ++iter)
|
||||
{
|
||||
const_cast<Func&>(func)(*iter);
|
||||
(*iter)->visitDescendants(func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class C>
|
||||
const C* findConstFirstChildOfType() const
|
||||
{
|
||||
if (children)
|
||||
{
|
||||
Instances::const_iterator end = children->end();
|
||||
for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) {
|
||||
const C* c = fastDynamicCast<C>(iter->get());
|
||||
if (c!=NULL)
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<class C>
|
||||
C* findFirstChildOfType()
|
||||
{
|
||||
return const_cast<C*>(findConstFirstChildOfType<C>());
|
||||
}
|
||||
|
||||
Instance* findFirstChildOfType(const std::string& className)
|
||||
{
|
||||
if (children)
|
||||
{
|
||||
Instances::const_iterator end = children->end();
|
||||
for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) {
|
||||
if(iter->get()->getClassNameStr() == className)
|
||||
return iter->get();
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<class C>
|
||||
C* findFirstDescendantOfType()
|
||||
{
|
||||
if (children)
|
||||
{
|
||||
Instances::const_iterator end = children->end();
|
||||
for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) {
|
||||
Instance* i = iter->get();
|
||||
if (C* foundChild = fastDynamicCast<C>(i)) {
|
||||
return foundChild;
|
||||
}
|
||||
if (C* foundDesc = i->findFirstDescendantOfType<C>()) {
|
||||
return foundDesc;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<class C>
|
||||
const C* findConstFirstDescendantOfType() const
|
||||
{
|
||||
if (children)
|
||||
{
|
||||
Instances::const_iterator end = children->end();
|
||||
for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) {
|
||||
const Instance* i = iter->get();
|
||||
if (const C* foundChild = fastDynamicCast<C>(i)) {
|
||||
return foundChild;
|
||||
}
|
||||
if (const C* foundDesc = i->findConstFirstDescendantOfType<C>()) {
|
||||
return foundDesc;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<class C>
|
||||
void destroyDescendantsOfType()
|
||||
{
|
||||
if (children)
|
||||
{
|
||||
Instances::const_iterator end = children->end();
|
||||
for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) {
|
||||
if (const C* c = fastDynamicCast<C>(iter->get()))
|
||||
{
|
||||
iter->get()->destroy();
|
||||
} else {
|
||||
iter->get()->destroyDescendantsOfType<C>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Instance* findCommonNode(Instance* i1, Instance* i2)
|
||||
{
|
||||
if (i1 == i2)
|
||||
return i1;
|
||||
|
||||
if (!i1)
|
||||
return NULL;
|
||||
if (i1->isAncestorOf(i2))
|
||||
return i1;
|
||||
|
||||
if (!i2)
|
||||
return NULL;
|
||||
if (i2->isAncestorOf(i1))
|
||||
return i2;
|
||||
|
||||
return findCommonNode(i1->getParent(), i2->getParent());
|
||||
}
|
||||
|
||||
// Override with class-specific rules about what can be a child or parent
|
||||
bool canAddChild(const Instance* instance, bool checkParent = true) const {
|
||||
if (instance->contains(this))
|
||||
return false; // No circular ownership, please!
|
||||
if (checkParent && instance->getParent() == this)
|
||||
return false; // Already a child!
|
||||
if (this->askForbidChild(instance))
|
||||
return false;
|
||||
if (instance->askForbidParent(this))
|
||||
return false;
|
||||
if (this->askAddChild(instance))
|
||||
return true;
|
||||
if (instance->askSetParent(this))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool canAddChild(const shared_ptr<Instance>& instance) const {
|
||||
return (instance!=NULL) && canAddChild(instance.get());
|
||||
}
|
||||
bool canSetParent(const Instance* instance) const {
|
||||
return (instance==NULL) || instance->canAddChild(this);
|
||||
}
|
||||
|
||||
template<typename Iter>
|
||||
bool canSetChildren(Iter first, Iter last) const
|
||||
{
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
if (!canAddChild(*first))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Iter>
|
||||
void setChildren(Iter first, Iter last)
|
||||
{
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
Instance* instance = *first;
|
||||
RBXASSERT(canAddChild(instance));
|
||||
instance->setParent(this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool canClientCreate() { return false; }
|
||||
// This convenience function gets called when this Instances is added to or removed from the DataModel.
|
||||
// You can use it to find Services, connect to and disconnect from Notifiers, etc.
|
||||
virtual void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider) {}
|
||||
|
||||
void readProperties(const XmlElement* container, IReferenceBinder& binder);
|
||||
|
||||
virtual shared_ptr<Instance> createChild(const RBX::Name& className, RBX::CreatorRole creatorRole);
|
||||
void read(const XmlElement* element, IReferenceBinder& binder, RBX::CreatorRole creatorRole);
|
||||
void readChildren(const XmlElement* element, IReferenceBinder& binder, RBX::CreatorRole creatorRole);
|
||||
void readChild(const XmlElement* childElement, IReferenceBinder& binder, RBX::CreatorRole creatorRole);
|
||||
|
||||
virtual XmlElement* writeXml(const boost::function<bool(Instance*)>& isInScope, RBX::CreatorRole creatorRole);
|
||||
|
||||
void writeChildren(XmlElement* container, const boost::function<bool(Instance*)>& isInScope, RBX::CreatorRole creatorRole, const SaveFilter saveFilter = SAVE_ALL);
|
||||
void writeChildren(XmlElement* container, RBX::CreatorRole creatorRole, const SaveFilter saveFilter = SAVE_ALL);
|
||||
|
||||
void raisePropertyChanged(const RBX::Reflection::PropertyDescriptor& descriptor);
|
||||
void raiseEventInvocation(const RBX::Reflection::EventDescriptor& descriptor, const RBX::Reflection::EventArguments& args, const SystemAddress* target);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected:
|
||||
//Actively prevent the adding
|
||||
virtual void verifySetParent(const Instance* newParent) const {};
|
||||
virtual void verifySetAncestor(const Instance* const newParent, const Instance* const instanceGettingNewParent) const;
|
||||
//Actively prevent the adding (parent side)
|
||||
virtual void verifyAddChild(const Instance* newChild) const {};
|
||||
virtual void verifyAddDescendant(const Instance* newParent, const Instance* instanceGettingNewParent) const;
|
||||
|
||||
// Don't call this directly. Call canAddChild
|
||||
virtual bool askAddChild(const Instance* instance) const;
|
||||
// Don't call this directly. Call canAddChild
|
||||
virtual bool askForbidChild(const Instance* instance) const;
|
||||
|
||||
// Don't call this directly. Call canSetParent
|
||||
virtual bool askForbidParent(const Instance* instance) const;
|
||||
// Don't call this directly. Call canSetParent
|
||||
virtual bool askSetParent(const Instance* instance) const;
|
||||
|
||||
|
||||
virtual void onAncestorChanged(const AncestorChanged& event);
|
||||
|
||||
virtual void onDescendantAdded(Instance* instance);
|
||||
virtual void onDescendantRemoving(const shared_ptr<Instance>& instance);
|
||||
|
||||
virtual void onChildAdded(Instance* child) {}
|
||||
virtual void onChildRemoving(Instance* child) {}
|
||||
virtual void onChildRemoved(Instance* child) {}
|
||||
virtual void onChildChanged(Instance* instance, const PropertyChanged& event);
|
||||
|
||||
virtual void onPropertyChanged(const Reflection::PropertyDescriptor& descriptor) {}
|
||||
|
||||
|
||||
|
||||
virtual void readProperty(const XmlElement* propertyElement, IReferenceBinder& binder);
|
||||
|
||||
void raiseChanged(const RBX::Reflection::PropertyDescriptor& descriptor)
|
||||
{
|
||||
raisePropertyChanged(descriptor);
|
||||
}
|
||||
|
||||
private:
|
||||
static void signalDescendantAdded(Instance* instance, Instance* beginParent, Instance* endParent);
|
||||
static void signalDescendantRemoving(const shared_ptr<Instance>& instance, Instance* beginParent, Instance* endParent);
|
||||
|
||||
void writeProperties(XmlElement* container) const;
|
||||
bool setParentInternal(Instance* instance, bool ignoreLock);
|
||||
};
|
||||
|
||||
} // namespace RBX
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "Reflection/Property.h"
|
||||
#include "V8Xml/Reference.h"
|
||||
#include "V8Xml/XmlElement.h"
|
||||
#include "Util/Object.h"
|
||||
|
||||
#include "G3D/Vector3.h"
|
||||
#include "G3D/Color3.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
namespace RBX {
|
||||
|
||||
class PropertyChanged
|
||||
{
|
||||
RBX::Reflection::Property property;
|
||||
public:
|
||||
const RBX::Reflection::Property& getProperty() const { return property; }
|
||||
const RBX::Reflection::PropertyDescriptor& getDescriptor() const { return property.getDescriptor(); }
|
||||
const RBX::Name& getName() { return property.getName(); }
|
||||
|
||||
PropertyChanged(const PropertyChanged& other)
|
||||
: property(other.property)
|
||||
{}
|
||||
|
||||
private:
|
||||
friend class Instance;
|
||||
PropertyChanged(const RBX::Reflection::Property& p)
|
||||
: property(p)
|
||||
{}
|
||||
};
|
||||
|
||||
// TODO: Move this out of Reflection?
|
||||
// Some standard categories
|
||||
#define category_Data "Data"
|
||||
#define category_Behavior "Behavior"
|
||||
#define category_State "State"
|
||||
#define category_Appearance "Appearance"
|
||||
#define category_Team "Team"
|
||||
#define category_Image "Image"
|
||||
#define category_Video "Video"
|
||||
#define category_Control "Control"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
#pragma once
|
||||
|
||||
#include "V8Tree/Instance.h"
|
||||
#include <boost/type_traits/is_base_of.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
// A Service is an instance that is "Singleton" in the scope of its containing
|
||||
// ServiceProvider. In other words, a Service-derived class is unique within the
|
||||
// child tree of a ServiceProvider.
|
||||
class Service
|
||||
{
|
||||
public:
|
||||
const bool isPublic;
|
||||
protected:
|
||||
Service(bool isPublic=true)
|
||||
:isPublic(isPublic)
|
||||
{
|
||||
}
|
||||
~Service()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Design decision: ServiceProvider liberally uses mutable members and declares const
|
||||
// member functions that change data. The idea here is that a ServiceProvider doesn't really
|
||||
// change fundamentally when creating and providing Services. It lets clients get a service
|
||||
// when const
|
||||
// TODO: consider the relative merits of returning Services as shared_ptr: safer, but more likely to lead to ptr leaks?
|
||||
extern const char* const sServiceProvider;
|
||||
class ServiceProvider
|
||||
: public DescribedNonCreatable<ServiceProvider, Instance, sServiceProvider>
|
||||
{
|
||||
private:
|
||||
typedef DescribedNonCreatable<ServiceProvider, Instance, sServiceProvider> Super;
|
||||
static Reflection::BoundFuncDesc<ServiceProvider, shared_ptr<Instance>(std::string)> func_FindService;
|
||||
static Reflection::BoundFuncDesc<ServiceProvider, shared_ptr<Instance>(std::string)> func_GetService;
|
||||
static Reflection::BoundFuncDesc<ServiceProvider, shared_ptr<Instance>(std::string)> dep_service;
|
||||
static Reflection::BoundFuncDesc<ServiceProvider, shared_ptr<Instance>(std::string)> dep_GetService;
|
||||
typedef std::vector< shared_ptr<Instance> > ServiceArray;
|
||||
mutable ServiceArray serviceArray;
|
||||
mutable std::map<const RBX::Name*, shared_ptr<Instance> > serviceMap;
|
||||
public:
|
||||
rbx::signal<void()> closingSignal;
|
||||
rbx::signal<void()> closingLateSignal;
|
||||
rbx::signal<void(shared_ptr<Instance> service)> serviceAddedSignal;
|
||||
rbx::signal<void(shared_ptr<Instance> service)> serviceRemovingSignal;
|
||||
|
||||
ServiceProvider();
|
||||
ServiceProvider(const char* name);
|
||||
|
||||
template<class ServiceClass>
|
||||
ServiceClass* find() const
|
||||
{
|
||||
BOOST_STATIC_ASSERT((boost::is_base_of<Service, ServiceClass>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_base_of<Instance, ServiceClass>::value));
|
||||
|
||||
size_t index = getClassIndex<ServiceClass>();
|
||||
|
||||
ServiceClass* service;
|
||||
if (index+1>serviceArray.size())
|
||||
{
|
||||
// serviceArray has an empty entry for this service now
|
||||
serviceArray.resize(index+1, shared_ptr<Instance>());
|
||||
service = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
service = static_cast<ServiceClass*>(serviceArray[index].get());
|
||||
if (service!=NULL)
|
||||
return service;
|
||||
}
|
||||
|
||||
if (ServiceClass::isNullClassName())
|
||||
return NULL;
|
||||
|
||||
// See if the service is in the className map
|
||||
shared_ptr<Instance> i = findServiceByClassName(ServiceClass::className());
|
||||
if (!i)
|
||||
return NULL;
|
||||
service = boost::polymorphic_downcast<ServiceClass*>(i.get());
|
||||
serviceArray[index] = i;
|
||||
return service;
|
||||
}
|
||||
|
||||
//mutable RBX::reentrant_concurrency_catcher threadGuard;
|
||||
template<class ServiceClass>
|
||||
ServiceClass* create() const
|
||||
{
|
||||
//RBX::reentrant_concurrency_catcher::scoped_lock lock(threadGuard);
|
||||
|
||||
ServiceClass* service = this->find<ServiceClass>();
|
||||
if (service==NULL)
|
||||
{
|
||||
// If all else fails, create the service and put it in the table and map
|
||||
shared_ptr<ServiceClass> s = Creatable<Instance>::create<ServiceClass>();
|
||||
|
||||
service = s.get();
|
||||
|
||||
// setParent can throw, so do not mutate any internal state until
|
||||
// setParent has returned without throwing.
|
||||
service->setAndLockParent(const_cast<ServiceProvider*>(this));
|
||||
size_t index = getClassIndex<ServiceClass>();
|
||||
serviceArray[index] = s;
|
||||
|
||||
// By now onDescendantAdded will have been called, which will add the service to serviceMap:
|
||||
RBXASSERT((ServiceClass::className()==RBX::Name::getNullName()) || serviceMap.find(&ServiceClass::className())!=serviceMap.end());
|
||||
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
static const ServiceProvider* findServiceProvider(const Instance* context)
|
||||
{
|
||||
if (const Instance* root = Instance::getRootAncestor(context)) {
|
||||
if (const ServiceProvider* serviceProvider = Instance::fastDynamicCast<ServiceProvider>(root)) {
|
||||
return serviceProvider;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<class ServiceClass>
|
||||
static ServiceClass* find(const Instance* context)
|
||||
{
|
||||
if (const ServiceProvider* serviceProvider = findServiceProvider(context))
|
||||
return serviceProvider->find<ServiceClass>();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<class ServiceClass>
|
||||
static ServiceClass* find(const ServiceProvider* serviceProvider)
|
||||
{
|
||||
if (serviceProvider!=NULL)
|
||||
return serviceProvider->find<ServiceClass>();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<class ServiceClass>
|
||||
static ServiceClass* create(const Instance* context)
|
||||
{
|
||||
if (const ServiceProvider* serviceProvider = findServiceProvider(context))
|
||||
return serviceProvider->create<ServiceClass>();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<class ServiceClass>
|
||||
static ServiceClass* create(const ServiceProvider* serviceProvider)
|
||||
{
|
||||
if (serviceProvider!=NULL)
|
||||
return serviceProvider->create<ServiceClass>();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Less efficient factory functions that use classNames. Use only if
|
||||
// you don't know the type of Service you want.
|
||||
static shared_ptr<Instance> create(Instance* context, const RBX::Name& name);
|
||||
shared_ptr<Instance> getPublicServiceByClassNameString(std::string name);
|
||||
|
||||
protected:
|
||||
/*override*/ shared_ptr<Instance> createChild(const RBX::Name& className, RBX::CreatorRole creatorRole);
|
||||
/*override*/ void onDescendantRemoving(const shared_ptr<Instance>& instance);
|
||||
/*override*/ void onDescendantAdded(Instance* instance);
|
||||
/*override*/ void onChildAdded(Instance* child);
|
||||
/*override*/ void onChildRemoving(Instance* child);
|
||||
|
||||
/* override */ bool askAddChild(const Instance* instance) const
|
||||
{
|
||||
return dynamic_cast<const Service*>(instance) != NULL;
|
||||
}
|
||||
|
||||
void clearServices();
|
||||
|
||||
private:
|
||||
// Returns a new number each time it is called (starting at 0)
|
||||
static size_t newIndex();
|
||||
|
||||
template<class ServiceClass>
|
||||
static size_t doGetClassIndex()
|
||||
{
|
||||
static size_t index = newIndex();
|
||||
return index;
|
||||
}
|
||||
|
||||
template<class ServiceClass>
|
||||
static void callDoGetClassIndex()
|
||||
{
|
||||
doGetClassIndex<ServiceClass>();
|
||||
}
|
||||
|
||||
shared_ptr<Instance> findServiceByClassName(const RBX::Name& className) const;
|
||||
shared_ptr<Instance> findPublicServiceByClassNameString(std::string name);
|
||||
|
||||
private:
|
||||
template<class ServiceClass>
|
||||
static size_t getClassIndex()
|
||||
{
|
||||
static boost::once_flag flag = BOOST_ONCE_INIT;
|
||||
boost::call_once(&callDoGetClassIndex<ServiceClass>, flag);
|
||||
return doGetClassIndex<ServiceClass>();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// A convenience class. Given an Instance* context, it will find the Service. You can treat it
|
||||
// like a Service*. Please note that it might be NULL if the context isn't a child of a ServiceProvider (like DataModel)
|
||||
template<class S>
|
||||
class ServiceClient
|
||||
{
|
||||
Instance* context;
|
||||
mutable shared_ptr<S> service;
|
||||
public:
|
||||
ServiceClient(Instance* context)
|
||||
:context(context)
|
||||
{
|
||||
}
|
||||
bool isNull() const
|
||||
{
|
||||
return findService()==NULL;
|
||||
}
|
||||
operator S*()
|
||||
{
|
||||
return createService();
|
||||
}
|
||||
operator const S*() const
|
||||
{
|
||||
return createService();
|
||||
}
|
||||
const S* operator->() const
|
||||
{
|
||||
return createService();
|
||||
}
|
||||
S* operator->()
|
||||
{
|
||||
return createService();
|
||||
}
|
||||
private:
|
||||
S* findService() const
|
||||
{
|
||||
if (!service)
|
||||
service = shared_from(ServiceProvider::find<S>(context));
|
||||
return service.get();
|
||||
}
|
||||
S* createService() const
|
||||
{
|
||||
if (!service)
|
||||
service = shared_from(ServiceProvider::create<S>(context));
|
||||
return service.get();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include "rbx/Debug.h"
|
||||
#include "security/FuzzyTokens.h"
|
||||
#include "v8datamodel/HackDefines.h"
|
||||
|
||||
LOGGROUP(Verbs)
|
||||
|
||||
namespace RBX {
|
||||
|
||||
class Name;
|
||||
class VerbContainer;
|
||||
|
||||
class RBXInterface IDataState
|
||||
{
|
||||
public:
|
||||
// Sets the document's "dirty" bit
|
||||
virtual void setDirty(bool dirty) = 0;
|
||||
virtual bool isDirty() const = 0;
|
||||
};
|
||||
|
||||
class RBXBaseClass Verb
|
||||
{
|
||||
const Name& name;
|
||||
protected:
|
||||
VerbContainer* const container;
|
||||
Verb(VerbContainer* container, const std::string& name, bool blacklisted = false);
|
||||
Verb(VerbContainer* container, const Name& name, bool blacklisted = false);
|
||||
bool verbSecurity;
|
||||
public:
|
||||
virtual ~Verb();
|
||||
virtual bool isEnabled() const { return true; }
|
||||
virtual bool isChecked() const { return false; }
|
||||
virtual bool isSelected() const { return false; }
|
||||
|
||||
virtual std::string getText() const { return std::string(); }
|
||||
const Name& getName() const {return name;}
|
||||
|
||||
virtual void doIt(IDataState* dataState) = 0;
|
||||
|
||||
VerbContainer* getContainer() const { return container; }
|
||||
|
||||
bool getVerbSecurity() const { return verbSecurity; }
|
||||
|
||||
static inline void doItWithChecks(Verb* const verb, IDataState* dataState)
|
||||
{
|
||||
#if !defined(RBX_STUDIO_BUILD)
|
||||
if (verb->getVerbSecurity())
|
||||
{
|
||||
RBX::Security::setHackFlagVs<LINE_RAND1>(RBX::Security::hackFlag9, HATE_VERB_SNATCH);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
verb->doIt(dataState);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This version of Verb lets you pass in callbacks for the implementation
|
||||
class BoundVerb : public Verb
|
||||
{
|
||||
boost::function<void(VerbContainer*)> doItFunction;
|
||||
boost::function<bool(VerbContainer*)> isEnabledFunction;
|
||||
boost::function<bool(VerbContainer*)> isCheckedFunction;
|
||||
boost::function<std::string(VerbContainer*)> getTextFunction;
|
||||
|
||||
public:
|
||||
BoundVerb(VerbContainer* container, const char* name,
|
||||
boost::function<void(VerbContainer*)> doItFunction,
|
||||
boost::function<bool(VerbContainer*)> isEnabledFunction = NULL,
|
||||
boost::function<bool(VerbContainer*)> isCheckedFunction = NULL,
|
||||
boost::function<std::string(VerbContainer*)> getTextFunction = NULL)
|
||||
: Verb(container, name)
|
||||
, doItFunction(doItFunction)
|
||||
, isEnabledFunction(isEnabledFunction)
|
||||
, isCheckedFunction(isCheckedFunction)
|
||||
, getTextFunction(getTextFunction)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool isEnabled() const
|
||||
{
|
||||
if (!isEnabledFunction)
|
||||
return Verb::isEnabled();
|
||||
return isEnabledFunction(container);
|
||||
}
|
||||
|
||||
virtual bool isChecked() const
|
||||
{
|
||||
if (!isCheckedFunction)
|
||||
return Verb::isChecked();
|
||||
return isCheckedFunction(container);
|
||||
}
|
||||
|
||||
virtual std::string getText() const
|
||||
{
|
||||
if (!getTextFunction)
|
||||
return Verb::getText();
|
||||
return getTextFunction(container);
|
||||
}
|
||||
|
||||
virtual void doIt(IDataState* dataState)
|
||||
{
|
||||
if (doItFunction)
|
||||
doItFunction(container);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class NullVerb : public Verb
|
||||
{
|
||||
public:
|
||||
NullVerb(VerbContainer* container, const std::string& name)
|
||||
: Verb(container, name)
|
||||
{
|
||||
}
|
||||
|
||||
/*override*/ bool isEnabled() const { return false; }
|
||||
/*override*/ void doIt(IDataState* dataState) {}
|
||||
};
|
||||
|
||||
class VerbContainer
|
||||
{
|
||||
friend class Verb;
|
||||
typedef std::map<const Name*, Verb*> Verbs;
|
||||
Verbs whitelistVerbs; // used for UI verbs
|
||||
Verbs blacklistVerbs; // used for tools
|
||||
VerbContainer* parent;
|
||||
|
||||
void addWhitelistVerb(Verb* verb);
|
||||
void addBlacklistVerb(Verb* verb);
|
||||
void removeVerb(Verb* verb);
|
||||
|
||||
public:
|
||||
VerbContainer(VerbContainer* parent);
|
||||
virtual ~VerbContainer();
|
||||
Verb* getVerb(const Name& name);
|
||||
Verb* getVerb(const std::string& name);
|
||||
Verb* getWhitelistVerb(const Name& name);
|
||||
Verb* getWhitelistVerb(const std::string& name);
|
||||
// This version of getVerb is used as a minor obsfucation of a string.
|
||||
Verb* getWhitelistVerb(const std::string& prefix, const std::string& name, const std::string& suffix);
|
||||
void setVerbParent(VerbContainer* parent);
|
||||
VerbContainer* getVerbParent() const { return parent; }
|
||||
|
||||
template<class F>
|
||||
void eachVerbName(F f, bool includeParent = true)
|
||||
{
|
||||
for (Verbs::const_iterator iter = whitelistVerbs.begin(); iter != whitelistVerbs.end(); ++iter)
|
||||
f(iter->first);
|
||||
for (Verbs::const_iterator iter = blacklistVerbs.begin(); iter != blacklistVerbs.end(); ++iter)
|
||||
f(iter->first);
|
||||
if (includeParent && parent)
|
||||
parent->eachVerbName(f);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace RBX
|
||||
Reference in New Issue
Block a user