/*  Inti: Integrated Foundation Classes
 *  Copyright (C) 2002 The Inti Development Team.
 *
 *  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/object.h
//! @brief A GObject C++ wrapper interface.
//!
//! Provides G::Object, the base class for all wrapped GTK+ objects.

#ifndef INTI_G_OBJECT_H
#define INTI_G_OBJECT_H

#ifndef INTI_MEMORY_HANDLER_H
#include <inti/memoryhandler.h>
#endif

#ifndef INTI_G_TYPE_H
#include <inti/glib/type.h>
#endif

#ifndef INTI_G_QUARK_H
#include <inti/glib/quark.h>
#endif

#ifndef INTI_G_PROPERTY_H
#include <inti/glib/property.h>
#endif

namespace Inti {

namespace G {

class ObjectClass;
class Quark;
class Value;

//! @class Object object.h inti/glib/object.h
//! @brief Base class for wrapped GTK+ objects and widgets.
//!
//! G::Object wraps the GTK+ stucture GObject and its associated functions. It is the base class
//! from which most C++ wrappers for the various GTK+ objects and widgets are dervied. G::Object
//! is derived from G::TypeInstance and MemoryHandler and is a reference counted object. You
//! reference a G::Object by calling ref(). This increments the internal reference counter. You
//! unreference a G::Object by calling unref(). This decrements the internal reference counter.
//! Objects that derive directly from G::Object are created with a reference count of 1. This
//! initial reference count is owned by you and must be unreferenced. Because you own the initial
//! reference count you don't need to reference the object before using it, but you  must remember
//! to unreference it when your finished. When the reference count drops to zero the object is
//! finialized and destroyed. If the object was a created on the heap, operator delete is called
//! to destroy the object. <B>You must not call operator delete</B>. To destroy an object you own
//! a reference to you call unref(). To destroy an object you don't own a reference to you must
//! call dispose();

class Object : virtual public TypeInstance, virtual public MemoryHandler
{
	friend class ObjectClass;

	Object(const Object&);
	Object& operator=(const Object&);

	static const Quark pointer_quark;
	
	static void destroy_notify(void *data);

protected:
//! @name Constructors
//! @{

	explicit Object(GObject *object, bool reference = true);
	//!< Construct a new G::Object from an existing GObject.
	//!< @param object A pointer to a GObject.
	//!< @param reference Set false if the initial reference count is floating, set true if it's not.
	//!<
	//!< <BR>This constructor is used to wrap GTK+ objects. <EM>object</EM> can be a pointer to 
	//!< newly created GObject or an existing GObject. If <EM>reference</EM> is false the object's
	//!< initial reference count is floating and must not be unreferenced, unless the object was
	//!< first referenced. If <EM>reference</EM> is true the intial reference is owned by you and
	//!< must be unreferenced. 
	//!<
	//!< Each derived class declares a similar constructor with a default value for 
	//!< <EM>reference</EM> that indicates whether or not the intial reference count
	//!< is floating. The Inti smart pointer, Inti::Pointer, uses this value to determine
	//!< if the object's initial reference count needs to be cleared.

//! @}
//  Override these do_ methods when you want to change the default behaviour of the GObject.

	virtual void do_set_property(unsigned int property_id, const GValue *value, GParamSpec *pspec);

	virtual void do_get_property(unsigned int property_id, GValue *value, GParamSpec *pspec);

	virtual void do_dispose();

	virtual void do_finalize();

//! @name Signal Handlers
//! @{

	virtual void on_notify(GParamSpec *pspec);
	//!< Called when a property on an object is changed.
	//!< @param pspec A GParamSpec object that holds the meta data specifying the new property.

//! @}
//  Signals

	typedef Signal1<void, GParamSpec*> NotifySignalType;
	typedef SignalProxy<TypeInstance, NotifySignalType> NotifySignalProxy;
	static const NotifySignalType notify_signal;

public:
//! @name Constructors
//! @{

	virtual ~Object();
	//!< Destructor.

//! @}
//! @name Accessors
//! @{

	GObject* g_object() const { return (GObject*)instance; }
	//!< Get a pointer to the GObject structure.

	GObjectClass* g_object_class() const;
	//!< Get a pointer to the GObjectClass structure.

	operator GObject* () const;
	//!< Conversion operator; safely converts a G::Object to a GObject pointer.

	void get(const char *first_property_name, ...) const;
	//!< Gets properties of an object.
	//!< @param first_property_name Name of first property to get the value for.
	//!< @param ... Null-terminated list of name-return location pairs.

	void get_property(const char *property_name, Value& value) const;
	//!< Get a property of an object.
	//!< @param property_name Name of property to get the value for.
	//!< @param value Reference to a G::Value object that will hold the value for property_name.

	void* get_data(const Quark& quark) const;
	//!< Get a user data pointer set by calling set().
	//!< @param quark A G::Quark naming the user data pointer.
	//!< @return The user data pointer set, or null if none exists.

	void* get_data(const char *key) const;
	void* get_data(const String& key) const;
	//!< Get a user data pointer set by calling set().
	//!< @param key A string naming the user data pointer.
	//!< @return The user data pointer set, or null if none exists.

//! @}
//! @name Methods
//! @{

	virtual void ref();
	//!< Increase the reference count of the object.
	//!< This method is declared virtual for Inti's use only.

	virtual void unref();
	//!< Decrease the reference count of the object.
	//!< This method is declared virtual for Inti's use only.

	virtual void dispose();
	//!< Finalize the object.
	//!< This method is declared virtual for Inti's use only.

	void set(const char *first_property_name, ...);
	//!< Sets properties on an object.
	//!< @param first_property_name Name of the first property to set the value for.
	//!< @param ... The first property value, followed optionally by more name/value pairs, followed by null.

	void set_property(const char *property_name, const Value& value);
	//!< Set a property on an object.
	//!< @param property_name Name of the property to set the value for.
	//!< @param value A G::Value that holds the value of the propery being set.

	void set_data(const Quark& quark, void *data, GDestroyNotify destroy = 0);
	//!< Set an opaque named pointer on an object.
	//!< @param quark A G::Quark, naming the user data pointer.
	//!< @param data An opaque user data pointer.
	//!< @param destroy A static function to invoke with data as its argument, when data needs to be freed.
	//!<
	//!< <BR>This sets an opaque, named pointer on an object. The name is specified through a G::Quark,
	//!< and the pointer can be gotten back from the object with get_data() until the object is finalized.
	//!< Setting a previously set user data pointer, overrides (frees) the old pointer set, using null as
	//!< the pointer essentially removes the data stored.

	void set_data(const char *key, void *data, GDestroyNotify destroy = 0);
	void set_data(const String& key, void *data, GDestroyNotify destroy = 0);
	//!< Set an opaque named pointer on an object.
	//!< @param key A string naming the user data pointer.
	//!< @param data An opaque user data pointer.
	//!< @param destroy A static function to invoke with data as its argument, when data needs to be freed.
	//!<
	//!< <BR>This sets an opaque, named pointer on an object. The name is specified through a String,
	//!< and the pointer can be gotten back from the object with get_data() until the object is finalized.
	//!< Setting a previously set user data pointer, overrides (frees) the old pointer set, using null as
	//!< the pointer essentially removes the data stored.

	void* remove_data(const Quark& quark, bool notify = false);
	//!< Remove an opaque named pointer previously set on an object.
	//!< @param quark A G::Quark, naming the user data pointer.
	//!< @param notify Set <EM>true</EM> if the destroy function should be invoked (if any was set).
	//!< @return The user data pointer set, or null if none exists.
	//!<
	//!< <BR>This function gets back user data pointers stored via set_data() and removes the data
	//!< from object. If <EM>notify</EM> is true its destroy function is called (if any was set).
	//!< Usually you wont use a destroy function so you can ignore <EM>notify</EM>.

	void* remove_data(const char *key, bool notify = false);
	void* remove_data(const String& key, bool notify = false);
	//!< Remove an opaque named pointer previously set on an object.
	//!< @param key A string naming the user data pointer.
	//!< @param notify Set <EM>true</EM> if the destroy function should be invoked (if any was set).
	//!< @return the user data pointer set, or null if none exists.
	//!<
	//!< <BR>This function gets back user data pointers stored via set_data() and removes the data
	//!< from object. If <EM>notify</EM> is true its destroy function is called (if any was set).
	//!< Usually you wont use a destroy function so you can ignore <EM>notify</EM>.

	unsigned long connect(const char *signal_name, GCallback handler, void *data, GClosureNotify destroy_data = 0);
	//!< Connect a function pointer and user data to a signal for a particular object.
	//!< @param signal_name Name of the signal.
	//!< @param handler Function pointer to attach to the signal.
	//!< @param data Value to pass to your handler.
	//!< @param destroy_data Function to call when this particular handler is disconnected.
	//!< @return The connection id.
	//!<
	//!< <BR>Usually you wont need to use this connection function. It's defined only for completeness.

	unsigned long connect_after(const char *signal_name, GCallback handler, void *data, GClosureNotify destroy_data = 0);
	//!< Connect a function pointer and user data to a signal for a particular object.
	//!< @param signal_name Name of the signal.
	//!< @param handler Function pointer to attach to the signal.
	//!< @param data Value to pass to your handler.
	//!< @param destroy_data Function to call when this particular handler is disconnected.
	//!< @return The connection id.
	//!<
	//!< <BR>This connection overrides the default signal behaviour (i.e. GTK_RUN_FIRST and GTK_RUN_LAST)
	//!< and invokes the user-defined handler after the signal. Usually you wont need to use this
	//!< connection function. It's defined only for completeness.

	unsigned long connect_swapped(const char *signal_name, GCallback handler, void *data, GClosureNotify destroy_data = 0);
	//!< Connect a function pointer and user data to a signal for a particular object.
	//!< @param signal_name Name of the signal.
	//!< @param handler Function pointer to attach to the signal.
	//!< @param data The user data associated with the handler.
	//!< @param destroy_data Function to call when this particular handler is disconnected.
	//!< @return The connection id.
	//!<
	//!< <BR>This connection calls the specified handler with the object and data fields swapped.

	void emit_by_name(const char *signal_name, ...);
	//!< Emit a signal by name.
	//!< @param signal_name The name of the signal.
	//!< @param ... The parameters to pass to the signal handler, followed by a pointer to the
	//!<            return type, if any.
	//!<
	//!< <BR>This causes the default handler and user-connected handlers to be run.
	
	void stop_emission_by_name(const char *detailed_signal);
	//!< Aborts a signal's current emission by name.
	//!< @param detailed_signal The name of the signal you wish to stop.
	//!<
	//!< <BR>The name of the signal you wish to stop is the GTK+ name, not its Inti name.
	//!< Signals in Inti are named by adding the "_signal" suffix to the GTK+ name. For
	//!< example, the Inti name for the GTK+ "clicked" signal is "clicked_signal".

	bool disconnect_by_name(const char* signal_name);
	//!< Disconnect a signal by name. 
	//!< @param signal_name The name of the signal.
	//!< @return <EM>true</EM> if the signal was disconnected, <EM>false</EM> if <EM>signal_name</EM>
	//!< is not connected to this object.
	//!<
	//!< <BR>This is a convenience method that can be used to disconnect a signal when you don't
	//!< want to use a Connection object. It looks up the signal_id for <EM>signal name</EM> and
	//!< uses it to search for the first signal handler connected to the object. If found, it
	//!< disconnects the handler and returns true.

//! @}
//! @name Templates
//! @{

	template <typename T>
	static T* pointer(void *obj);
	//!< Get the C++ pointer for a GTK+ object.
	//!< @param obj A pointer to a GTK+ C object.
	//!< @return The C++ pointer cast to a pointer of type T, or null if no pointer exists.
	
	template<typename T, typename gObj>
	static T* wrap(gObj *obj, bool reference = false);
	//!< Wrap a GTK+ C object in as C++ wrapper class.
	//!< @param obj A pointer to a GTK+ C object.
	//!< @param reference Set <EM>true</EM> if the GTK+ object must be unreferenced.
	//!< @return A pointer to the C++ class cast to a pointer of type T.
	//!<
	//!< <BR>If <EM>obj</EM> was previously wrapped this function returns the existing C++ pointer
	//!< cast to a pointer of type T. If no wrapper exists a new one is created. Each GTK+ C object
	//!< can have only one wrapper. A smart pointer can be used to handle the returned pointer.
	//!< Otherwise if T->is_referenced() is true, T->unref() must be explicitly called.

	template<typename T, typename gObj>
	static T* wrap_new(gObj *obj, bool reference = false);
	//!< Wrap a GTK+ C object in a C++ wrapper class.
	//!< @param obj A pointer to a GTK+ C object.
	//!< @param reference Set <EM>true</EM> if the GTK+ object must be unreferenced.
	//!< @return A pointer to the C++ class cast to a pointer of type T.
	//!<
	//!< <BR>Only use this if <EM>obj</EM> has not been wrapped before. Usually <EM>reference</EM>
	//!< is false. Set <EM>reference</EM> to true if the wrapped object must be unreferenced when
	//!< finished.

//! @}
//! @name Signal Proxies
//! @{

	const NotifySignalProxy sig_notify()
	{
		return NotifySignalProxy(this, &notify_signal);
	}
	//!< Connect to the notify_signal; emitted when a property on an object is changed.

//! @}
};

} // namespace G

template<typename T>
inline T*
G::Object::pointer(void *obj)
{
	void *data = 0;
	if (obj)
	{
		data = g_object_get_qdata(G_OBJECT(obj), G::Object::pointer_quark);
	}
	return data ? static_cast<T*>(data) : 0;
}

template<typename T, typename gObj>
inline T*
G::Object::wrap(gObj *obj, bool reference)
{
	T *t = 0;
	if (obj)
	{
		void *data = g_object_get_qdata(G_OBJECT(obj), G::Object::pointer_quark);
		t = data ? static_cast<T*>(data) : new T(obj, reference);
	}
	return t;
}

template<typename T, typename gObj>
inline T*
G::Object::wrap_new(gObj *obj, bool reference)
{
	return obj ? new T(obj, reference) : 0;
}

} // namespace Inti

#endif // INTI_G_OBJECT_H

