/*
 *  Copyright (C) 2000 Marco Pesenti Gritti
 *
 *  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, 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU 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.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "gul-glade.h"
#include "gul-general.h"

#include <gtk/gtktogglebutton.h>
#include <gtk/gtkmenu.h>
#include <gmodule.h>

static void
glade_signal_connect_func (const gchar *cb_name, GObject *obj, 
			   const gchar *signal_name, const gchar *signal_data,
			   GObject *conn_obj, gboolean conn_after,
			   gpointer user_data);

/**
 * glade_lookup_widget
 */
GtkWidget *
gul_glade_lookup_widget (GtkWidget *widget, const gchar *widget_name)
{
	GtkWidget *found_widget, *parent;
	GladeXML *xml = NULL;

	while (xml == NULL)
	{
		/* the following line is to allow to override the
		 * GladeXML object or to set it to an object which was
		 * not created by libglade. Useful for popup menus */
		xml = g_object_get_data (G_OBJECT(widget), "widget_tree");
		if (!xml) xml = glade_get_widget_tree(widget);

		if (GTK_IS_MENU(widget))
			parent = gtk_menu_get_attach_widget (GTK_MENU(widget));
		else
			parent = widget->parent;

		if (parent == NULL) 
			break;

		widget = parent;
	}

	found_widget = glade_xml_get_widget(xml, widget_name);

	if (!found_widget)
		g_warning ("Widget not found: %s", widget_name);
 
	return found_widget;
}

/**
 * glade_widget_new: build a new widget of the provided name, with all
 * signals attached and data set to the provided parameter.
 */
GladeXML *
gul_glade_widget_new (const char *file, const char *widget_name, 
		      GtkWidget **root, gpointer data)
{
	GladeXML *gxml;
	gchar *glade_file;

	glade_file = gul_general_user_file (file, TRUE);
	g_return_val_if_fail (glade_file != NULL, NULL);

	/* build the widget */
	/* note that libglade automatically caches the parsed file,
	 * so we don't need to worry about the efficiency of this */
	gxml = glade_xml_new (glade_file, widget_name, NULL);
	g_free (glade_file);
	g_return_val_if_fail (gxml != NULL, NULL);

	/* lookup the root widget if requested */
	if (root != NULL)
	{
		*root = glade_xml_get_widget (gxml, widget_name);
	}

	/* connect signals and data */
	glade_xml_signal_autoconnect_full
		(gxml, (GladeXMLConnectFunc)glade_signal_connect_func, data);

	/* return xml document for subsequent widget lookups */
	return gxml;
}

/*
 * glade_signal_connect_func: used by glade_xml_signal_autoconnect_full
 */
static void
glade_signal_connect_func (const gchar *cb_name, GObject *obj, 
			   const gchar *signal_name, const gchar *signal_data,
			   GObject *conn_obj, gboolean conn_after,
			   gpointer user_data)
{
	/** Module with all the symbols of the program */
	static GModule *mod_self = NULL;
	gpointer handler_func;

 	/* initialize gmodule */
	if (mod_self == NULL)
	{
		mod_self = g_module_open (NULL, 0);
		g_assert (mod_self != NULL);
	}

	/*g_print( "glade_signal_connect_func: cb_name = '%s', signal_name = '%s', signal_data = '%s'\n",
	  cb_name, signal_name, signal_data ); */
	
	if (g_module_symbol (mod_self, cb_name, &handler_func))
	{
		/* found callback */
		if (conn_obj)
		{
			if (conn_after)
			{
				g_signal_connect_object
                                        (obj, signal_name, 
                                         handler_func, conn_obj,
                                         G_CONNECT_AFTER);
			}
			else
			{
				g_signal_connect_object
                                        (obj, signal_name, 
                                         handler_func, conn_obj,
                                         G_CONNECT_SWAPPED);
			}
		}
		else
		{
			/* no conn_obj; use standard connect */
			gpointer data = NULL;
			
			data = user_data;
			
			if (conn_after)
			{
				g_signal_connect_after
					(obj, signal_name, 
					 handler_func, data);
			}
			else
			{
				g_signal_connect
					(obj, signal_name, 
					 handler_func, data);
			}
		}
	}
	else
	{
		g_warning("callback function not found: %s", cb_name);
	}
}

void
gul_glade_lookup_widgets (GladeXML *gxml, WidgetLookup *lookup_table)
{
	gint i;
	GtkWidget *value;

	/* check */
	g_return_if_fail (gxml != NULL);
	g_return_if_fail (lookup_table != NULL);

        /* lookup all of the widgets */
        for (i = 0; lookup_table[i].name != NULL; i++)
        {
		g_return_if_fail (lookup_table[i].name != NULL);
		g_return_if_fail (lookup_table[i].ptr != NULL);

		value = glade_xml_get_widget (gxml, lookup_table[i].name);

		if (value == NULL)
		{
			/* for debugging */
			g_warning ("failed to find widget `%s'\n",
				   lookup_table[i].name);
		}
		*(lookup_table[i].ptr) = value;
	}
}

/**
 * gul_glade_get_widgets:
 * @gxml: a GladeXML object
 * @widget_name: the name of the widget
 * @Varargs: pairs of widget names and locations to return the widgets,
 *   starting with the location for @widget_name, terminated by %NULL
 *
 * This is a convenience function for getting pointers to multiple GtkWidgets
 * in the Glade interface description.
 */
void
gul_glade_get_widgets (GladeXML *gxml, const char *widget_name, ...)
{
	va_list var_args;

	g_return_if_fail (GLADE_IS_XML(gxml));
	g_return_if_fail (widget_name != NULL);

	va_start (var_args, widget_name);

	while (widget_name != NULL)
	{
		GtkWidget **widget;

		widget = va_arg (var_args, GtkWidget **);
		g_assert (widget != NULL);

		*widget = glade_xml_get_widget (gxml, widget_name);
		if (*widget == NULL)
		{
			g_warning ("%s: failed to find widget `%s'",
				   G_STRLOC, widget_name);
		}

		widget_name = va_arg (var_args, const char *);
	}

	va_end (var_args);
}

/**
 * gul_glade_size_group_widgets:
 * @gxml: a GladeXML object
 * @mode: the mode for the new size group
 * @widget_name: the name of the widget
 * @Varargs: the names of GtkWidgets to include in the same size group as the
 *   widget named @widget_name, terminated by %NULL
 *
 * This is a convenience function for adding multiple widgets into a
 * #GtkSizeGroup directly from a Glade interface description.
 */
void
gul_glade_size_group_widgets (GladeXML *gxml,
		              GtkSizeGroupMode mode,
		              const char *widget_name, ...)
{
	GtkSizeGroup *size_group;
	va_list       args;

	g_return_if_fail (GLADE_IS_XML(gxml));
	g_return_if_fail (widget_name != NULL);

	size_group = gtk_size_group_new (mode);

	va_start (args, widget_name);

	while (widget_name != NULL)
	{
		GtkWidget *widget;

		widget = glade_xml_get_widget (gxml, widget_name);
		if (widget != NULL)
		{
			gtk_size_group_add_widget (size_group, widget);
		}
		else
		{
			g_warning ("%s: failed to find widget `%s'\n",
				   G_STRLOC, widget_name);
		}

		widget_name = va_arg (args, const char *);
	}

	va_end (args);

	g_object_unref (size_group);
}

static void
on_sensitive_widget_toggled (GtkWidget *widget, GSList *slaves)
{
	gboolean         sensitive;
	GulSensitiveType type;

	sensitive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));

	type = GPOINTER_TO_UINT(slaves->data);
	if (type == GUL_SENSITIVE_INVERT)
	{
		sensitive = !sensitive;
	}

	g_slist_foreach (slaves->next, (GFunc)gtk_widget_set_sensitive,
			 GUINT_TO_POINTER(sensitive));
}

/**
 * gul_glade_group_sensitive:
 * @gxml: a GladeXML object
 * @master_name: the name of the master GtkToggleButton
 * @type: the mode for slave widgets
 * @slave_name: the name of a slave GtkWidget
 * @Varargs: the names of GtkWidgets to include in the same group, terminated
 *   by %NULL
 *
 * Group widgets so that the slave widgets are sensitive when the master
 * GtkToggleButton is active (%GUL_SENSITIVE_FOLLOW), or 
 * inactive (%GUL_SENSITIVE_INVERT)
 */
void
gul_glade_group_sensitive (GladeXML *gxml,
		           const char *master_name,
			   GulSensitiveType type, 
			   const char *slave_name, ...)
{
	GSList    *slaves;
	GtkWidget *master_widget;
	va_list    args;

	g_return_if_fail (GLADE_IS_XML(gxml));
	g_return_if_fail (master_name != NULL);
	g_return_if_fail (slave_name != NULL);
	g_return_if_fail (type == GUL_SENSITIVE_FOLLOW || type == GUL_SENSITIVE_INVERT);

	master_widget = glade_xml_get_widget (gxml, master_name);
	g_return_if_fail (GTK_IS_TOGGLE_BUTTON(master_widget));

	va_start (args, slave_name);

	slaves = NULL;
	while (slave_name != NULL)
	{
		GtkWidget *slave_widget;

		slave_widget = glade_xml_get_widget (gxml, slave_name);
		if (slave_widget != NULL)
		{
			slaves = g_slist_prepend (slaves, slave_widget);
		}
		else
		{
			g_warning ("%s: failed to find widget `%s'",
				   G_STRLOC, slave_name);
		}

		slave_name = va_arg (args, const char *);
	}

	va_end (args);

	if (slaves != NULL)
	{
		/* piggyback the sensitivity mode within the widget list */
		slaves = g_slist_prepend (slaves, GUINT_TO_POINTER(type));
		g_signal_connect_data (master_widget, "toggled",
				       G_CALLBACK(on_sensitive_widget_toggled),
				       slaves, (GClosureNotify)g_slist_free, 0);
	}
}
