Newer
Older
Import / research / ui / TweakableProperties / toolkit / include / SignalSlot.h
#ifndef SIGNAL_SLOT_H
#define SIGNAL_SLOT_H


#include <vector>
#include <cstdio>
#include "Variant.h"
#include "Pointer.h"
#include "Containers.h"


#include "Namespace.h"
BEGIN_NAMESPACE


class Invokable
{
public:
	virtual ~Invokable() {}
    virtual void operator()(Variant& a_params) = 0;
};


template <typename Method>
class StaticFunctionCallback : public Invokable
{
public:
    StaticFunctionCallback(Method a_method)
		: m_method(a_method)
    {
	}
	virtual void operator()(Variant& a_params)
	{
		(*m_method)(a_params);
	}
private:
	Method   m_method;
};


template <class Class, typename Method>
class MethodCallback : public Invokable
{
public:
    MethodCallback(Class* a_obj, Method a_method)
        : m_object(a_obj)
        , m_method(a_method)
    {
	}
	MethodCallback(MethodCallback const& a_other)
	{
		m_object = a_other.m_object;
		//m_object.addRef();
		m_method = a_other.m_method;
	}
	MethodCallback& operator=(MethodCallback const& a_other)
	{
		m_object = a_other.m_object;
		//m_object.addRef();
		m_method = a_other.m_method;
		return *this;
	}
    virtual void operator()(Variant& a_params)
    {
		(m_object->*m_method)(a_params);
    }
private:
    Class*   m_object;
    Method   m_method;
};


class Slot
{
public:
/*
	// rule of three
	// How to handle copying of slot objects
	// Also need to ensure the safety of the object and method this slot holds going out of scope or being deleted
	// Really need object passed in to be a SharedPtr or something that will mean that this slot will know about it
	// being deleted

    Slot(Slot const& a_other);
    Slot& operator=(Slot const & a_other)
	{
		a_other.swap(*this);
		return *this;
	}
    void swap(Slot & a_other) {
		std::swap(placementNewBuffer, a_other.placementNewBuffer);
		std::swap(m_callback, a_other.m_callback);
		std::swap(m_variant, a_other.m_variant);
	}
*/
	
	Slot() : m_callback(0)
	{
	}
	~Slot(void)
	{
	}
    
	/*
	Slot(Slot const& a_other) {
		memcpy(placementNewBuffer, a_other.placementNewBuffer, sizeof(placementNewBuffer));
		m_callback = a_other.m_callback;
		MethodCallback* cb = dynamic_cast<MethodCallback>(m_callback);
		if (cb)
			cb->m
		m_variant = a_other.m_variant;
	}
    
	Slot& operator=(Slot const & a_other)
	{
		memcpy(placementNewBuffer, a_other.placementNewBuffer, sizeof(placementNewBuffer));
		m_callback = a_other.m_callback;
		m_variant = a_other.m_variant;
		return *this;
	}
	*/

	template <typename Method>
	Slot(Method a_method, Variant a_v = Variant())
	  : m_callback(new(placementNewBuffer) StaticFunctionCallback<Method>(a_method)), m_variant(a_v)
	{
		STATIC_ASSERT(sizeof(StaticFunctionCallback<Method>) <= sizeof(placementNewBuffer), increaseBufferBize);
	}

	template <class Class, typename Method>
	Slot(Class* object, Method a_method, Variant a_v = Variant())
	  : m_callback(new(placementNewBuffer) MethodCallback<Class,Method>(object, a_method)), m_variant(a_v)
	{
		STATIC_ASSERT(sizeof(MethodCallback<Class,Method>) <= sizeof(placementNewBuffer), increaseBufferBize);
	}

	// Need some kind of way to notify when object gets destroyed so that it can remove this slot

	void operator()()
	{
		(*m_callback)(m_variant);
	}

	void operator()(Variant& a_params)
	{
		if (m_variant.isValid())
			(*m_callback)(m_variant);
		else
			(*m_callback)(a_params);
	}

	template <typename T>
	void operator()(const T& a_params)
	{
		Variant v(a_params);
		(*this)(v);
	}

private:
	char placementNewBuffer[24]; // by trial and error, 24 was min to compile x64 using MSVC
	Invokable *m_callback;
	Variant m_variant;
};


class Signal
{
public:
	// Because a Signal holds a list of Slots, the same needs to be
	// thought through about the life time of objects and the places that hold references

	Signal() : m_insideInvokation(false) {}
	//~Signal() { m_connectionList.clear(); }
	void addConnection(SharedPtr<Slot>& a_delegate)    { m_connectionList.push_back(a_delegate); }

	// Need some kind of callback or something that when a slot with an object has that object deleted,
	// then the slot get destroyed, and when that happens it disconnects the slot from this signal

	void disconnectAll()                               { m_connectionList.clear(); }
	bool disconnectOne(SharedPtr<Slot>& a_delegate)    { /* TODO */ return true; }

	void operator()()
	{
		if (!m_insideInvokation) // prevent recursion
		{
			m_insideInvokation = true;
			for (uint32_t i = 0; i < m_connectionList.size(); i++)
				(*m_connectionList[i])();
			m_insideInvokation = false;
		}
	}

	void operator()(Variant& a_param)
	{
		if (!m_insideInvokation) // prevent recursion
		{
			m_insideInvokation = true;
			for (uint32_t i = 0; i < m_connectionList.size(); i++)
				(*m_connectionList[i])(a_param);
			m_insideInvokation = false;
		}
	}

private:
    Signal(Signal const& a_other);
    Signal& operator=(Signal const & a_other);
	bool m_insideInvokation;
	Vector<SharedPtr<Slot> > m_connectionList;
};


//typedef Signal   Event;
typedef Slot     Delegate;


static inline void connect(Signal& a_signal, Slot& a_slot)
{
	SharedPtr<Slot> slot(new Slot(a_slot));
	a_signal.addConnection(slot);
}


template <class Class, typename Method>
static inline void connect(Signal& a_signal, Class *a_object, Method a_method, Variant a_v = Variant())
{
	SharedPtr<Slot> slot(new Slot(a_object, a_method, a_v));
	a_signal.addConnection(slot);
}


END_NAMESPACE


#endif // SIGNAL_SLOT_H