/*  Inti: Integrated Foundation Classes
 *  Copyright (C) 2002 The Inti Development Team.
 *  Copyright (C) 2000 Red Hat, Inc.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

//! @file inti/glib/signals.h
//! @brief C++ signal interface for GTK+ signals.
//!
//! Provides a set of signal class templates that wrap the GTK+ signal system.
//! Each class derived from G::Object declares a C++ signal for each of the GTK+
//! signals it emits. These signals are protected, are declared static, and 
//! require an object pointer; for example the clicked_signal in the Gtk::Button
//! class. A public accessor method is declared for each protected signal; for 
//! example sig_clicked() for the <EM>clicked_signal</EM>. These accessor methods
//! pass the object <EM>this</EM> pointer to the protected signal for you. The
//! signal classes are named Signal0 to Signal6, where 0 to 6 specifies the number
//! of arguments passed to a connected slot. (see inti/slot.h).

#ifndef INTI_G_SIGNALS_H
#define INTI_G_SIGNALS_H

#ifndef INTI_SLOT_H
#include <inti/slot.h>
#endif

#ifndef INTI_CONNECTION_H
#include <inti/connection.h>
#endif

#ifndef __GLIB_GOBJECT_H__
#include <glib-object.h>
#endif

namespace Inti {
	
namespace G {

class TypeInstance;

//! @class SlotNode signals.h inti/glib/signals.h
//! @brief A node class for managing slots connected to Inti::G::Signal's.

class SlotNode : public Node
{
	SlotNode(GObject *object, const Slot *slot);
	~SlotNode();

	GObject *object_;
	unsigned long connect_id;

public:
	static SlotNode* connect(TypeInstance *instance, const char *name, GCallback callback, const Slot *slot, bool after);
	
	static void destroy(void *data);

	virtual void block();
	//!< Block signal emission to the slot until unblock is called.

	virtual void unblock();
	//!< Unblock the slot so signal emmissions can be received.

	virtual void disconnect();
	//!< Disconnect the slot. The slot will no longer recieve signal emissions.
};

//! @class Signal signals.h inti/glib/signals.h
//! @brief Base class for the GTK+ C++ signal interface.

class Signal
{
	Signal(const Signal&);
	Signal& operator=(const Signal&);

	const char *const name_;

protected:
	SlotNode* connect(TypeInstance *instance, GCallback callback, const Slot *slot, bool after) const;

public:
	Signal(const char *name);
	//!< Constructor.

	~Signal();
	//!< Destructor.

	const char* name() const;
	//!< Returns the name of the signal
};

//! @class SignalProxy signals.h inti/glib/signals.h
//! @brief A proxy for manipulating a protected signal (connection, mostly).
//! 
//! SignalProxy is used to access the connection method of a protected signal.

template<typename ObjectType, typename SignalType>
class SignalProxy
{
	ObjectType *const object_;
	const SignalType *const signal_;

public:
	typedef typename SignalType::SlotType SlotType;

	SignalProxy(ObjectType *object, const SignalType *signal) : object_(object), signal_(signal)
	{
	}
	//! Constructor.

	Connection connect(const SlotType *slot, bool after = false) const
	{
		return signal_->connect(object_, slot, after);
	}
	//!< Connect a slot to the signal.
	//!< @param slot The slot object for a class method or static function.
	//!< @param after Set <EM>true</EM> to call the user-defined slot after the signal,
	//!<              or <EM>false</EM> to let the signal's default behavior preside.
	//!< @return A connection object.
	//!<
	//!< <BR>The connection object can be used to control the signal connection, either by
	//!< calling block(), unblock() or disconnect(). You don't need to call discconnect()
	//!< unless you have good reason to. GTK+ signals are automatically disconnected when
	//!< an object gets destroyed.
};

//! @class Signal0 signals.h inti/glib/signals.h
//! @brief A template for a signal passing no arguments and returning a value of type R.

template<typename R>
class Signal0 : Signal
{
public:
	typedef Slot0<R> SlotType;
	//!< Function signature for handlers connecting to the signal.

private:
	static R gtk_callback(void *data)
	{
		SlotType *slot = dynamic_cast<SlotType*>(static_cast<Node*>(data)->slot());
		return slot->call();
	}

public:
	Signal0(const char *name) : Signal(name)
	{
	}
	//!< Constructor.
	//!< @param name The GTK+ name for the signal.

	~Signal0()
	{
	}
	//!< Destructor.

	Connection connect(TypeInstance *instance, const SlotType *slot, bool after = false) const
	{
		return Signal::connect(instance, (GCallback)gtk_callback, slot, after);
	}
	//!< Connect a slot to the signal.
	//!< @param instance An object derived from G::TypeInstance.
	//!< @param slot A slot of type Slot0<R>.
	//!< @param after Set <EM>true</EM> to call the user-defined slot after the signal,
	//!<              or <EM>false</EM> to let the signal's default behavior preside.
	//!< @return A connection object.
	//!<
	//!< <BR>The connection object can be used to control the signal connection, either by
	//!< calling block(), unblock() or disconnect(). You don't need to call discconnect()
	//!< unless you have good reason to. GTK+ signals are automatically disconnected when
	//!< an object gets destroyed.
};


//! @class Signal1 signals.h inti/glib/signals.h
//! @brief A template for a signal passing one argument of type P1 and returning a value of type R.

template <typename R, typename P1>
class Signal1 : public Signal
{
public:
	typedef Slot1<R, P1> SlotType;
	//!< Function signature for handlers connecting to the signal.

private:
	static R gtk_callback(void *data, P1 p1)
	{
		SlotType *slot = dynamic_cast<SlotType*>(static_cast<Node*>(data)->slot());
		return slot->call(p1);
	}

public:
	Signal1(const char *name) : Signal(name)
	{
	}
	//!< Constructor.
	//!< @param name The GTK+ name for the signal.

	~Signal1()
	{
	}
	//!< Destructor.

	Connection connect(TypeInstance *instance, const SlotType *slot, bool after = false) const
	{
		return Signal::connect(instance, (GCallback)gtk_callback, slot, after);
	}
	//!< Connect a slot to the signal.
	//!< @param instance An object derived from G::TypeInstance.
	//!< @param slot A slot of type Slot1<R, P1>.
	//!< @param after Set <EM>true</EM> to call the user-defined slot after the signal,
	//!<              or <EM>false</EM> to let the signal's default behavior preside.
	//!< @return A connection object.
	//!<
	//!< <BR>The connection object can be used to control the signal connection, either by
	//!< calling block(), unblock() or disconnect(). You don't need to call discconnect()
	//!< unless you have good reason to. GTK+ signals are automatically disconnected when
	//!< an object gets destroyed.
};

//! @class Signal2 signals.h inti/glib/signals.h
//! @brief A template for a signal passing two arguments of type P1 and P2,
//! and returning a value of type R.

template <typename R, typename P1, typename P2>
class Signal2 : public Signal
{
public:
	typedef Slot2<R, P1, P2> SlotType;
	//!< Function signature for handlers connecting to the signal.

private:
	static R gtk_callback(void *data, P1 p1, P2 p2)
	{
		SlotType *slot = dynamic_cast<SlotType*>(static_cast<Node*>(data)->slot());
		return slot->call(p1, p2);
	}
	
public:
	Signal2(const char *name) : Signal(name)
	{
	}
	//!< Constructor.
	//!< @param name The GTK+ name for the signal.

	~Signal2()
	{
	}
	//!< Destructor.

	Connection connect(TypeInstance *instance, const SlotType *slot, bool after = false) const
	{
		return Signal::connect(instance, (GCallback)gtk_callback, slot, after);
	}
	//!< Connect a slot to the signal.
	//!< @param instance An object derived from G::TypeInstance.
	//!< @param slot A slot of type Slot2<R, P1, P2>.
	//!< @param after Set <EM>true</EM> to call the user-defined slot after the signal,
	//!<              or <EM>false</EM> to let the signal's default behavior preside.
	//!< @return A connection object.
	//!<
	//!< <BR>The connection object can be used to control the signal connection, either by
	//!< calling block(), unblock() or disconnect(). You don't need to call discconnect()
	//!< unless you have good reason to. GTK+ signals are automatically disconnected when
	//!< an object gets destroyed.
};

//! @class Signal3 signals.h inti/glib/signals.h
//! @brief A template for a signal passing three arguments of type P1, P2 and P3,
//! and returning a value of type R.

template <typename R, typename P1, typename P2, typename P3>
class Signal3 : public Signal
{
public:
	typedef Slot3<R, P1, P2, P3> SlotType;
	//!< Function signature for handlers connecting to the signal.

private:
	static R gtk_callback(void *data, P1 p1, P2 p2, P3 p3)
	{
		SlotType *slot = dynamic_cast<SlotType*>(static_cast<Node*>(data)->slot());
		return slot->call(p1, p2, p3);
	}
	
public:
	Signal3(const char *name) : Signal(name)
	{
	}
	//!< Constructor.
	//!< @param name The GTK+ name for the signal.

	~Signal3()
	{
	}
	//!< Destructor.

	Connection connect(TypeInstance *instance, const SlotType *slot, bool after = false) const
	{
		return Signal::connect(instance, (GCallback)gtk_callback, slot, after);
	}
	//!< Connect a slot to the signal.
	//!< @param instance An object derived from G::TypeInstance.
	//!< @param slot A slot of type Slot3<R, P1, P2, P3>.
	//!< @param after Set <EM>true</EM> to call the user-defined slot after the signal,
	//!<              or <EM>false</EM> to let the signal's default behavior preside.
	//!< @return A connection object.
	//!<
	//!< <BR>The connection object can be used to control the signal connection, either by
	//!< calling block(), unblock() or disconnect(). You don't need to call discconnect()
	//!< unless you have good reason to. GTK+ signals are automatically disconnected when
	//!< an object gets destroyed.
};

//! @class Signal4 signals.h inti/glib/signals.h
//! @brief A template for a signal passing four arguments of type P1, P2, P3 and P4,
//! and returning a value of type R.

template <typename R, typename P1, typename P2, typename P3, typename P4>
class Signal4 : public Signal
{
public:
	typedef Slot4<R, P1, P2, P3, P4> SlotType;
	//!< Function signature for handlers connecting to the signal.

private:
	static R gtk_callback(void *data, P1 p1, P2 p2, P3 p3, P4 p4)
	{
		SlotType *slot = dynamic_cast<SlotType*>(static_cast<Node*>(data)->slot());
		return slot->call(p1, p2, p3, p4);
	}
	
public:
	Signal4(const char *name) : Signal(name)
	{
	}
	//!< Constructor.
	//!< @param name The GTK+ name for the signal.

	~Signal4()
	{
	}
	//!< Destructor.

	Connection connect(TypeInstance *instance, const SlotType *slot, bool after = false) const
	{
		return Signal::connect(instance, (GCallback)gtk_callback, slot, after);
	}
	//!< Connect a slot to the signal.
	//!< @param instance An object derived from G::TypeInstance.
	//!< @param slot A slot of type Slot4<R, P1, P2, P3, P4>.
	//!< @param after Set <EM>true</EM> to call the user-defined slot after the signal,
	//!<              or <EM>false</EM> to let the signal's default behavior preside.
	//!< @return A connection object.
	//!<
	//!< <BR>The connection object can be used to control the signal connection, either by
	//!< calling block(), unblock() or disconnect(). You don't need to call discconnect()
	//!< unless you have good reason to. GTK+ signals are automatically disconnected when
	//!< an object gets destroyed.
};

//! @class Signal5 signals.h inti/glib/signals.h
//! @brief A template for a signal passing five arguments of type P1, P2, P3, P4 and P5,
//! and returning a value of type R.

template <typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
class Signal5 : public Signal
{
public:
	typedef Slot5<R, P1, P2, P3, P4, P5> SlotType;
	//!< Function signature for handlers connecting to the signal.

private:
	static R gtk_callback(void *data, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5)
	{
		SlotType *slot = dynamic_cast<SlotType*>(static_cast<Node*>(data)->slot());
		return slot->call(p1, p2, p3, p4, p5);
	}
	
public:
	Signal5(const char *name) : Signal(name)
	{
	}
	//!< Constructor.
	//!< @param name The GTK+ name for the signal.

	~Signal5()
	{
	}
	//!< Destructor.

	Connection connect(TypeInstance *instance, const SlotType *slot, bool after = false) const
	{
		return Signal::connect(instance, (GCallback)gtk_callback, slot, after);
	}
	//!< Connect a slot to the signal.
	//!< @param instance An object derived from G::TypeInstance.
	//!< @param slot A slot of type Slot5<R, P1, P2, P3, P4, P5>.
	//!< @param after Set <EM>true</EM> to call the user-defined slot after the signal,
	//!<              or <EM>false</EM> to let the signal's default behavior preside.
	//!< @return A connection object.
	//!<
	//!< <BR>The connection object can be used to control the signal connection, either by
	//!< calling block(), unblock() or disconnect(). You don't need to call discconnect()
	//!< unless you have good reason to. GTK+ signals are automatically disconnected when
	//!< an object gets destroyed.
};

//! @class Signal6 signals.h inti/glib/signals.h
//! @brief A template for a signal passing six arguments of type P1, P2, P3, P4, P5 and P6,
//! and returning a value of type R.

template <typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
class Signal6 : public Signal
{
public:
	typedef Slot6<R, P1, P2, P3, P4, P5, P6> SlotType;
	//!< Functiaon signature for handlers connecting to the signal.

private:
	static R gtk_callback(void *data, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6)
	{
		SlotType *slot = dynamic_cast<SlotType*>(static_cast<Node*>(data)->slot());
		return slot->call(p1, p2, p3, p4, p5, p6);
	}
	
public:
	Signal6(const char *name) : Signal(name)
	{
	}
	//!< Constructor.
	//!< @param name The GTK+ name for the signal.

	~Signal6()
	{
	}
	//!< Destructor.

	Connection connect(TypeInstance *instance, const SlotType *slot, bool after = false) const
	{
		return Signal::connect(instance, (GCallback)gtk_callback, slot, after);
	}
	//!< Connect a slot to the signal.
	//!< @param instance An object derived from G::TypeInstance.
	//!< @param slot A slot of type Slot6<R, P1, P2, P3, P4, P5, P6>.
	//!< @param after Set <EM>true</EM> to call the user-defined slot after the signal,
	//!<              or <EM>false</EM> to let the signal's default behavior preside.
	//!< @return A connection object.
	//!<
	//!< <BR>The connection object can be used to control the signal connection, either by
	//!< calling block(), unblock() or disconnect(). You don't need to call discconnect()
	//!< unless you have good reason to. GTK+ signals are automatically disconnected when
	//!< an object gets destroyed.
};

} // namespace G

} // namespace Inti

#endif // INTI_G_SIGNALS_H

