/*
 *  Copyright (C) 2003  Tommi Komulainen
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation. 
 *
 *  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-gconf.h"

#include "eel-gconf-extensions.h"

#include <gtk/gtkcheckbutton.h>
#include <gtk/gtkradiobutton.h>
#include <gtk/gtkspinbutton.h>
#include <gtk/gtktogglebutton.h>

#include <string.h>

static void
on_entry_changed (GtkEntry *entry, const char *gconf_key)
{
	eel_gconf_set_string (gconf_key, gtk_entry_get_text (entry));
}

static void
on_spin_button_changed (GtkSpinButton *spin_button, const char *gconf_key)
{
	eel_gconf_set_integer (gconf_key, gtk_spin_button_get_value_as_int (spin_button));
}

static void
on_toggle_button_toggled (GtkToggleButton *button, const char *gconf_key)
{
	eel_gconf_set_boolean (gconf_key, gtk_toggle_button_get_active (button));
}

static void
gul_glade_gconf_connect (GladeXML *gxml, const char *widget_name, const char *gconf_key)
{
	GtkWidget *widget;

	widget = glade_xml_get_widget (gxml, widget_name);
	g_return_if_fail (widget != NULL);

	if (GTK_IS_SPIN_BUTTON(widget))
	{
		gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget),
				           eel_gconf_get_integer (gconf_key));
		g_signal_connect (G_OBJECT(widget), "value-changed",
				  G_CALLBACK(on_spin_button_changed), (gpointer)gconf_key);
	}
	else if (GTK_IS_ENTRY(widget))
	{
		char *value = eel_gconf_get_string (gconf_key);
		gtk_entry_set_text (GTK_ENTRY(widget), value);
		g_signal_connect (G_OBJECT(widget), "changed",
				  G_CALLBACK(on_entry_changed), (gpointer)gconf_key);
		g_free (value);
	}
	else if (GTK_IS_CHECK_BUTTON(widget))
	{
		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget),
				              eel_gconf_get_boolean (gconf_key));
		g_signal_connect (G_OBJECT(widget), "toggled",
				  G_CALLBACK(on_toggle_button_toggled), (gpointer)gconf_key);
	}
	else
	{
		g_warning ("%s: can't connect widget `%s`", 
			   G_STRLOC, widget_name);
	}
}

/**
 * gul_glade_gconf_connect_simple:
 * @gxml: a #GladeXML object
 * @widget_name: the name of the widget
 * @Varags: pairs of widget names and GConf keys for the widgets, starting
 *   with the GConf key for @widget_name, terminated by %NULL
 *
 * Connect a widget to a GConf key so that the changes in the widget are
 * written to GConf.  The initial value for the widget is read from GConf.
 *
 * Can be used for #GtkSpinButton, #GtkEntry, #GtkCheckButton.
 */
void
gul_glade_gconf_connect_simple (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)
	{
		const char *gconf_key;

		gconf_key = va_arg (var_args, const char *);
		if (gconf_key != NULL)
		{
			gul_glade_gconf_connect (gxml, widget_name, gconf_key);
		}
		else
		{
			g_warning ("%s: NULL gconf key for widget `%s'",
				   G_STRLOC, widget_name);
		}

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

	va_end (var_args);
}

typedef struct _GulGConfItem GulGConfItem;
struct _GulGConfItem
{
	char       *gconf_key;
	GConfValue *value;
};

/* Note that we take ownership of the @value */
static GulGConfItem *
gul_gconf_item_new (const char *gconf_key, GConfValue *value)
{
	GulGConfItem *item;

	item = g_new(GulGConfItem, 1);
	item->gconf_key = g_strdup(gconf_key);
	item->value     = value;

	return item;
}

static void
gul_gconf_item_free (GulGConfItem *item)
{
	if (item != NULL)
	{
		g_free (item->gconf_key);
		gconf_value_free (item->value);
		g_free (item);
	}
}

static void
on_radio_button_clicked (GtkRadioButton *button, const GulGConfItem *pref)
{
	GSList *group;

	if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)))
		return;

	eel_gconf_set_value (pref->gconf_key, pref->value);

	group = gtk_radio_button_get_group (button);
	g_slist_foreach (group, (GFunc)gtk_toggle_button_set_inconsistent,
			 GUINT_TO_POINTER(FALSE));
}

/**
 * gul_glade_gconf_connect_radio:
 * @gxml: a #GladeXML object
 * @gconf_key: the GConf key to update
 * @widget_name: the name of the radio button
 * @Varags: pairs of radio button names and values for the GConf key, starting
 *   with the value for @widget_name, terminated by %NULL
 *
 * Connect a group of #GtkRadioButton widgets to a GConf key so that when a
 * different #GtkRadioButton is selected, the corresponding value is written to
 * GConf.  The #GtkRadioButton that matches the current value in GConf is
 * initially selected.  If the current value matches none of the defined
 * values, the #GtkRadioButton group is set inconsistent. 
 */
void
gul_glade_gconf_connect_radio (GladeXML   *gxml,
			       const char *gconf_key, 
			       const char *widget_name, ...)
{
	GConfValue     *value;
	GConfValueType  value_type;
	char           *current;
	gboolean        active_set;
	GtkWidget      *widget;
	va_list         var_args;

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

	/* figure out the value type from the current value */
	value = eel_gconf_get_value (gconf_key);
	g_return_if_fail (value != NULL);

	g_return_if_fail (value->type == GCONF_VALUE_BOOL  || 
			  value->type == GCONF_VALUE_FLOAT ||
			  value->type == GCONF_VALUE_INT   || 
			  value->type == GCONF_VALUE_STRING);
	value_type = value->type;

	current = gconf_value_to_string (value);
	gconf_value_free (value);

	va_start (var_args, widget_name);

	widget = NULL;
	active_set = FALSE;

	for (; widget_name != NULL;
	       widget_name = va_arg (var_args, const char *))
	{
		const char *value_str;
		GError     *error;

		value_str = va_arg (var_args, const char *);
		if (value_str == NULL)
		{
			g_warning ("%s: NULL value for widget `%s'",
			           G_STRLOC, widget_name);
			continue;
		}

		widget = glade_xml_get_widget (gxml, widget_name);
		if (!GTK_IS_RADIO_BUTTON(widget))
		{
			g_warning ("%s: assertion `%s' failed for widget `%s'",
				   G_STRLOC, "GTK_IS_RADIO_BUTTON(widget)",
				   widget_name);
			continue;
		}

		error = NULL;
		value = gconf_value_new_from_string (value_type, value_str, &error);
		if (value != NULL)
		{
			GulGConfItem *item;

			item = gul_gconf_item_new (gconf_key, value);

			if (current != NULL && !strcmp (value_str, current))
			{
				gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget), TRUE);
				active_set = TRUE;
			}

			g_signal_connect_data (G_OBJECT(widget), "clicked",
					       G_CALLBACK(on_radio_button_clicked),
					       item, (GClosureNotify)gul_gconf_item_free, 0);
		}
		else
		{
			g_warning ("%s: %s", G_STRLOC, error->message);
			g_error_free (error);
		}
	}

	va_end (var_args);

	if (!active_set)
	{
		GSList *group;

		group = gtk_radio_button_get_group (GTK_RADIO_BUTTON(widget));
		g_slist_foreach (group, (GFunc)gtk_toggle_button_set_inconsistent,
				 GUINT_TO_POINTER(TRUE));
	}

	g_free (current);
}
