#ifndef ERIS_UI_FACTORY_H
#define ERIS_UI_FACTORY_H
#include <Atlas/Message/Element.h>
#include <sigc++/object.h>
#include <sigc++/signal.h>
#include <list>
namespace Atlas {
namespace Objects {
namespace Operation {
class Create;
}
namespace Entity {
class UIEntity;
class Frame;
class Slot;
}
}
}
namespace Eris { namespace UI {
// Element is a virtual class because we expect to
// get an inheritance hierarchy like:
//
// Element
// | |
// FrameElement MyElement
// | |
// MyFrameElement
//
// where MyElement and MyFrameElement are classes
// in client-side bindings. There should only be
// one Element instance in MyFrameElement, so
// we want children of Element to inherit
// virtually.
/// an instantiated dialog element
class Element
{
public:
virtual ~Element() {}
// implementations of this function should emit PropertiesChanged for
// valid properties
virtual void setProperties(const Atlas::Message::Element::MapType&) = 0;
void setProperty(const std::string& name, const Atlas::Message::Element& arg)
{
Atlas::Message::Element::MapType map;
map[name] = arg;
setProperties(map);
}
SigC::Signal1<void,const Atlas::Message::Element::MapType&> PropertiesChanged;
};
class SlotElement : public Element, virtual public SigC::Object
{
public:
virtual void action(const Atlas::Message::Element::MapType&) = 0;
};
class FrameElement : virtual public Element
{
public:
~FrameElement()
{
for(SlotList::iterator I = _list.begin(); I!= _list.end(); ++I)
delete *I;
}
virtual void pack(Element*) = 0;
void packSlot(SlotElement* slot) {_list.push_back(slot);}
private:
typedef std::list<SlotElement*> SlotList;
SlotList _list;
};
class Bindings;
class Factory
{
public:
class BaseGen
{
public:
virtual ~BaseGen() {}
virtual Element* create(const Atlas::Message::Element::MapType&) = 0;
};
template<class E>
class Gen : public BaseGen
{
public:
virtual Element* create(const Atlas::Message::Element::MapType& attrs)
{return E(attrs);}
};
Factory(const std::string& id, BaseGen* gen)
: _id(id), _gen(gen), _persistent(true), _refcount(1)
{_id_list.push_back(id);}
virtual ~Factory() {if(_persistent && _gen) delete _gen;}
typedef std::map<std::string,Element*> IDMap;
// create a dialog element
virtual Element* create(IDMap&) const;
// create a new factory
virtual Factory* parse(const Atlas::Message::Element::MapType&, const Bindings&) const;
void ref() {++_refcount;}
void unref() {if(--_refcount == 0) delete this;}
bool unique() const {return _refcount == 1;}
bool persistent() const {return _persistent;}
const std::string& id() const {return _id;}
protected:
typedef std::list<std::string> IDList;
Factory(const Atlas::Objects::Entity::UIEntity&, const std::string&,
const IDList&, const Atlas::Message::Element::MapType&, BaseGen*);
const Atlas::Message::Element::MapType& attrs() const {return _attrs;}
const IDList& idlist() const {return _id_list;}
BaseGen* gen() const {return _gen;}
private:
Factory(const Factory&);
Factory& operator=(const Factory&);
std::string _id;
IDList _id_list; // _id plus all parent ids, for use in connecting slots
BaseGen* _gen;
bool _persistent; // factories not created by the server
unsigned long _refcount;
Atlas::Message::Element::MapType _attrs;
};
// factory for children of Atlas::Objects::Entity::Frame
class FrameFactory : public Factory
{
public:
class BaseGen
{
public:
virtual ~BaseGen() {}
virtual FrameElement* create(const std::string& valign,
const std::string& halign, const std::string& rel_pos,
const Atlas::Message::Element::MapType&) = 0;
};
template<class FE>
class Gen : public BaseGen
{
public:
virtual FrameElement* create(const std::string& valign,
const std::string& halign, const std::string& rel_pos,
const Atlas::Message::Element::MapType& attrs)
{return new FE(valign, halign, rel_pos, attrs);}
};
FrameFactory(BaseGen* gen) : Factory("frame", 0), _valign("center"),
_halign("center"), _rel_pos("right"), _gen(gen) {}
virtual ~FrameFactory();
virtual Element* create(IDMap&) const;
virtual Factory* parse(const Atlas::Message::Element::MapType&, const Bindings&) const;
private:
FrameFactory(const Atlas::Objects::Entity::Frame&, const IDList&,
const Bindings&, const Atlas::Message::Element::MapType&, BaseGen*);
std::string _valign, _halign, _rel_pos;
typedef std::list<Factory*> ChildList;
ChildList _children;
BaseGen* _gen;
};
class SlotFactory : public Factory
{
public:
// for the base slot classes
SlotFactory(const std::string& id, BaseGen* gen) : Factory(id, gen) {}
virtual Element* create(IDMap&) const;
virtual Factory* parse(const Atlas::Message::Element::MapType&, const Bindings&) const;
const Atlas::Message::Element::ListType& target() const {return _target;}
private:
SlotFactory(const Atlas::Objects::Entity::Slot&, const IDList&,
const Atlas::Message::Element::MapType&, BaseGen*);
Atlas::Message::Element::ListType _target;
};
/// the dialog generator/handler
class Bindings
{
public:
Bindings();
virtual ~Bindings();
void parse(const Atlas::Message::Element&);
/// purge all server-generated classes
void clear();
Factory* findFactory(const Atlas::Message::Element& id) const
{
return id.isString() ? findFactory(id.asString()) : 0;
}
Factory* findFactory(const std::string& id) const
{
FactoryMap::const_iterator I = _factory_map.find(id);
return (I != _factory_map.end()) ? I->second : 0;
}
bool bind(Factory* f) {return _factory_map.insert(
FactoryMap::value_type(f->id(), f)).second;}
// a trick to get reasonable syntax for the interface,
// e.g. Bind<Foo>(bindings).slot("foo");
template<class C>
friend struct Bind
{
public:
Bind(Bindings& b) : _b(b) {}
bool slot(const std::string& id)
{return _b.bind(new SlotFactory(id, new SlotFactory::Gen<C>()));}
bool element(const std::string& id)
{return _b.bind(new Factory(id, new Factory::Gen<C>()));}
bool frame()
{return _b.bind(new FrameFactory(new FrameFactory::Gen<C>()));}
private:
Bindings& _b;
};
// put an Element on the screen as a dialog
virtual void display(Element*, const std::string& id) = 0;
virtual void createConsoleElement(const Atlas::Objects::Entity::UIEntity&) = 0;
private:
typedef std::map<std::string,Factory*> FactoryMap;
FactoryMap _factory_map;
};
}} // namespace Eris::UI
#endif
syntax highlighted by Code2HTML, v. 0.9.1