/* -*- mode: c; style: linux -*- */

/* gconf-property-editor.c
 * Copyright (C) 2001 Ximian, Inc.
 *
 * Written by Bradford Hovinen <hovinen@ximian.com>
 *
 * 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 <string.h>
#include <stdarg.h>

#include "gconf-property-editor.h"
#include "gconf-property-editor-marshal.h"

#include "preview-file-selection.h"

enum {
	VALUE_CHANGED,
	LAST_SIGNAL
};

enum {
	PROP_0,
	PROP_KEY,
	PROP_CALLBACK,
	PROP_CHANGESET,
	PROP_CONV_TO_WIDGET_CB,
	PROP_CONV_FROM_WIDGET_CB,
	PROP_UI_CONTROL,
	PROP_DATA,
	PROP_DATA_FREE_CB
};

typedef void (*GConfPropertyEditorDataFreeCb) (gpointer data);

struct _GConfPropertyEditorPrivate 
{
	gchar                   *key;
	guint                    handler_id;
	GConfChangeSet          *changeset;
	GObject                 *ui_control;
	GConfPEditorValueConvFn  conv_to_widget_cb;
	GConfPEditorValueConvFn  conv_from_widget_cb;
	GConfClientNotifyFunc    callback;
	gboolean                 inited;

	gpointer 		      data;
	GConfPropertyEditorDataFreeCb data_free_cb;
};

typedef struct
{
	GType			 enum_type;
	GConfPEditorGetValueFn   enum_val_true_fn;
	gpointer		 enum_val_true_fn_data;
	guint			 enum_val_false;
	gboolean		 use_nick;
} GConfPropertyEditorEnumData;

static guint peditor_signals[LAST_SIGNAL];

static GObjectClass *parent_class;

static void gconf_property_editor_init        (GConfPropertyEditor      *gconf_property_editor,
					       GConfPropertyEditorClass *class);
static void gconf_property_editor_class_init  (GConfPropertyEditorClass *class);
static void gconf_property_editor_base_init   (GConfPropertyEditorClass *class);

static void gconf_property_editor_set_prop    (GObject      *object, 
					       guint         prop_id,
					       const GValue *value, 
					       GParamSpec   *pspec);
static void gconf_property_editor_get_prop    (GObject      *object,
					       guint         prop_id,
					       GValue       *value,
					       GParamSpec   *pspec);

static void gconf_property_editor_finalize    (GObject      *object);

static GObject *gconf_peditor_new             (gchar                 *key,
					       GConfClientNotifyFunc  cb,
					       GConfChangeSet        *changeset,
					       GObject               *ui_control,
					       const gchar           *first_prop_name,
					       va_list                var_args,
					       const gchar	     *first_custom,
					       ...);

GType
gconf_property_editor_get_type (void)
{
	static GType gconf_property_editor_type = 0;

	if (!gconf_property_editor_type) {
		GTypeInfo gconf_property_editor_info = {
			sizeof (GConfPropertyEditorClass),
			(GBaseInitFunc) gconf_property_editor_base_init,
			NULL, /* GBaseFinalizeFunc */
			(GClassInitFunc) gconf_property_editor_class_init,
			NULL, /* GClassFinalizeFunc */
			NULL, /* user-supplied data */
			sizeof (GConfPropertyEditor),
			0, /* n_preallocs */
			(GInstanceInitFunc) gconf_property_editor_init,
			NULL
		};

		gconf_property_editor_type = 
			g_type_register_static (G_TYPE_OBJECT, 
						"GConfPropertyEditor",
						&gconf_property_editor_info, 0);
	}

	return gconf_property_editor_type;
}

static GConfValue* 
gconf_property_editor_conv_default (GConfPropertyEditor *peditor,
				    const GConfValue *value)
{
	return gconf_value_copy (value);
}

static void
gconf_property_editor_init (GConfPropertyEditor      *gconf_property_editor,
			    GConfPropertyEditorClass *class)
{
	gconf_property_editor->p = g_new0 (GConfPropertyEditorPrivate, 1);
	gconf_property_editor->p->conv_to_widget_cb = gconf_property_editor_conv_default;
	gconf_property_editor->p->conv_from_widget_cb = gconf_property_editor_conv_default;
	gconf_property_editor->p->inited = FALSE;
}

static void
gconf_property_editor_base_init (GConfPropertyEditorClass *class) 
{
}

static void
gconf_property_editor_class_init (GConfPropertyEditorClass *class) 
{
	GObjectClass *object_class;

	object_class = G_OBJECT_CLASS (class);

	object_class->finalize = gconf_property_editor_finalize;
	object_class->set_property = gconf_property_editor_set_prop;
	object_class->get_property = gconf_property_editor_get_prop;

	g_object_class_install_property
		(object_class, PROP_KEY,
		 g_param_spec_string ("key",
				      _("Key"),
				      _("GConf key to which this property editor is attached"),
				      NULL,
				      G_PARAM_READWRITE));
	g_object_class_install_property
		(object_class, PROP_CALLBACK,
		 g_param_spec_pointer ("callback",
				       _("Callback"),
				       _("Issue this callback when the value associated with key gets changed"),
				       G_PARAM_WRITABLE));
	g_object_class_install_property
		(object_class, PROP_CHANGESET,
		 g_param_spec_pointer ("changeset",
				       _("Change set"),
				       _("GConf change set containing data to be forwarded to the gconf client on apply"),
				       G_PARAM_READWRITE));
	g_object_class_install_property
		(object_class, PROP_CONV_TO_WIDGET_CB,
		 g_param_spec_pointer ("conv-to-widget-cb",
				       _("Conversion to widget callback"),
				       _("Callback to be issued when data are to be converted from GConf to the widget"),
				       G_PARAM_WRITABLE));
	g_object_class_install_property
		(object_class, PROP_CONV_FROM_WIDGET_CB,
		 g_param_spec_pointer ("conv-from-widget-cb",
				       _("Conversion from widget callback"),
				       _("Callback to be issued when data are to be converted to GConf from the widget"),
				       G_PARAM_WRITABLE));
	g_object_class_install_property
		(object_class, PROP_UI_CONTROL,
		 g_param_spec_object ("ui-control",
				      _("UI Control"),
				      _("Object that controls the property (normally a widget)"),
				      G_TYPE_OBJECT,
				      G_PARAM_WRITABLE));

	peditor_signals[VALUE_CHANGED] =
		g_signal_new ("value-changed",
			      G_TYPE_FROM_CLASS (object_class), 0,
			      G_STRUCT_OFFSET (GConfPropertyEditorClass, value_changed),
			      NULL, NULL,
			      (GSignalCMarshaller) gconf_property_editor_marshal_VOID__STRING_POINTER,
			      G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);

	g_object_class_install_property
		(object_class, PROP_DATA,
		 g_param_spec_pointer ("data",
				       _("Property editor object data"),
				       _("Custom data required by the specific property editor"),
				       G_PARAM_WRITABLE));

	g_object_class_install_property
		(object_class, PROP_DATA_FREE_CB,
		 g_param_spec_pointer ("data-free-cb",
				       _("Property editor data freeing callback"),
				       _("Callback to be issued when property editor object data is to be freed"),
				       G_PARAM_WRITABLE));

	parent_class = G_OBJECT_CLASS
		(g_type_class_ref (G_TYPE_OBJECT));
}

static void
gconf_property_editor_set_prop (GObject      *object,
				guint         prop_id,
				const GValue *value,
				GParamSpec   *pspec) 
{
	GConfPropertyEditor *peditor;
	GConfClient         *client;
	GConfNotifyFunc      cb;

	g_return_if_fail (object != NULL);
	g_return_if_fail (IS_GCONF_PROPERTY_EDITOR (object));

	peditor = GCONF_PROPERTY_EDITOR (object);

	switch (prop_id) {
	case PROP_KEY:
		peditor->p->key = g_value_dup_string (value);
		break;

	case PROP_CALLBACK:
		client = gconf_client_get_default ();
		cb = g_value_get_pointer (value);
		peditor->p->callback = (GConfClientNotifyFunc) cb;
		if (peditor->p->handler_id != 0) {
			gconf_client_notify_remove (client,
						    peditor->p->handler_id);
		}
		peditor->p->handler_id =
			gconf_client_notify_add (client, peditor->p->key,
						 peditor->p->callback,
						 peditor, NULL, NULL);
		break;

	case PROP_CHANGESET:
		peditor->p->changeset = g_value_get_pointer (value);
		break;

	case PROP_CONV_TO_WIDGET_CB:
		peditor->p->conv_to_widget_cb = g_value_get_pointer (value);
		break;

	case PROP_CONV_FROM_WIDGET_CB:
		peditor->p->conv_from_widget_cb = g_value_get_pointer (value);
		break;

	case PROP_UI_CONTROL:
		peditor->p->ui_control = g_value_get_object (value);
		g_object_weak_ref (peditor->p->ui_control, (GWeakNotify) g_object_unref, object);
		break;
	case PROP_DATA:
		peditor->p->data = g_value_get_pointer (value);
		break;
	case PROP_DATA_FREE_CB:
		peditor->p->data_free_cb = g_value_get_pointer (value);
		break;
	default:
		g_warning ("Bad argument set");
		break;
	}
}

static void
gconf_property_editor_get_prop (GObject    *object,
				guint       prop_id,
				GValue     *value,
				GParamSpec *pspec) 
{
	GConfPropertyEditor *peditor;

	g_return_if_fail (object != NULL);
	g_return_if_fail (IS_GCONF_PROPERTY_EDITOR (object));

	peditor = GCONF_PROPERTY_EDITOR (object);

	switch (prop_id) {
	case PROP_KEY:
		g_value_set_string (value, peditor->p->key);
		break;

	case PROP_CHANGESET:
		g_value_set_pointer (value, peditor->p->changeset);
		break;

	default:
		g_warning ("Bad argument get");
		break;
	}
}

static void
gconf_property_editor_finalize (GObject *object) 
{
	GConfPropertyEditor *gconf_property_editor;

	g_return_if_fail (object != NULL);
	g_return_if_fail (IS_GCONF_PROPERTY_EDITOR (object));

	gconf_property_editor = GCONF_PROPERTY_EDITOR (object);
	
	if (gconf_property_editor->p->data_free_cb)
		gconf_property_editor->p->data_free_cb (gconf_property_editor->p->data);

 	if (gconf_property_editor->p->handler_id != 0) {
		GConfClient *client;

		client = gconf_client_get_default ();		
		gconf_client_notify_remove (client,
					    gconf_property_editor->p->handler_id);
	}
	
	g_free (gconf_property_editor->p);

	G_OBJECT_CLASS (parent_class)->finalize (object);
}

static GObject *
gconf_peditor_new (gchar                 *key,
		   GConfClientNotifyFunc  cb,
		   GConfChangeSet        *changeset,
		   GObject               *ui_control,
		   const gchar           *first_prop_name,
		   va_list                var_args,
		   const gchar		 *first_custom,
		   ...) 
{
	GObject     *obj;
	GConfClient *client;
	GConfEntry  *gconf_entry;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (cb != NULL, NULL);

	obj = g_object_new (gconf_property_editor_get_type (),
			    "key",        key,
			    "callback",   cb,
			    "changeset",  changeset,
			    "ui-control", ui_control,
			    NULL);

	g_object_set_valist (obj, first_prop_name, var_args);

	if (first_custom)
	{
		va_list custom_args;
		va_start (custom_args, first_custom);
		g_object_set_valist (obj, first_custom, custom_args);
		va_end (custom_args);
	}

	client = gconf_client_get_default ();
	gconf_entry = gconf_client_get_entry (client, GCONF_PROPERTY_EDITOR (obj)->p->key, NULL, TRUE, NULL);
	GCONF_PROPERTY_EDITOR (obj)->p->callback (client, 0, gconf_entry, obj);
	GCONF_PROPERTY_EDITOR (obj)->p->inited = TRUE;
	gconf_entry_free (gconf_entry);
	g_object_unref (G_OBJECT (client));

	return obj;
}

const gchar *
gconf_property_editor_get_key (GConfPropertyEditor *peditor)
{
	return peditor->p->key;
}

GObject *
gconf_property_editor_get_ui_control (GConfPropertyEditor  *peditor)
{
	return peditor->p->ui_control;
}

static void
peditor_set_gconf_value (GConfPropertyEditor *peditor,
			 const gchar         *key,
			 GConfValue          *value) 
{
	if (peditor->p->changeset != NULL)
		gconf_change_set_set (peditor->p->changeset, peditor->p->key, value);
	else
		gconf_client_set (gconf_client_get_default (), peditor->p->key, value, NULL);
}

static void
peditor_boolean_value_changed (GConfClient         *client,
			       guint                cnxn_id,
			       GConfEntry          *entry,
			       GConfPropertyEditor *peditor) 
{
	GConfValue *value, *value_wid;

	if (peditor->p->changeset != NULL)
		gconf_change_set_remove (peditor->p->changeset, peditor->p->key);

	value = gconf_entry_get_value (entry);

	if (value != NULL) {
		value_wid = peditor->p->conv_to_widget_cb (peditor, value);
		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (peditor->p->ui_control), gconf_value_get_bool (value_wid));
		gconf_value_free (value_wid);
	}
}

static void
peditor_boolean_widget_changed (GConfPropertyEditor *peditor,
				GtkToggleButton     *tb)
{
	GConfValue *value, *value_wid;

	if (!peditor->p->inited) return;
	value_wid = gconf_value_new (GCONF_VALUE_BOOL);
	gconf_value_set_bool (value_wid, gtk_toggle_button_get_active (tb));
	value = peditor->p->conv_from_widget_cb (peditor, value_wid);
	peditor_set_gconf_value (peditor, peditor->p->key, value);
	g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
	gconf_value_free (value_wid);
	gconf_value_free (value);
}

GObject *
gconf_peditor_new_boolean (GConfChangeSet *changeset,
			   gchar          *key,
			   GtkWidget      *checkbox,
			   gchar          *first_property_name,
			   ...)
{
	GObject *peditor;
	va_list var_args;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (checkbox != NULL, NULL);
	g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (checkbox), NULL);

	va_start (var_args, first_property_name);

	peditor = gconf_peditor_new
		(key,
		 (GConfClientNotifyFunc) peditor_boolean_value_changed,
		 changeset,
		 G_OBJECT (checkbox),
		 first_property_name,
		 var_args,
		 NULL);

	va_end (var_args);

	g_signal_connect_swapped (G_OBJECT (checkbox), "toggled",
				  (GCallback) peditor_boolean_widget_changed, peditor);

	return peditor;
}

static void
peditor_integer_value_changed (GConfClient         *client,
			       guint                cnxn_id,
			       GConfEntry          *entry,
			       GConfPropertyEditor *peditor) 
{
	GConfValue *value, *value_wid;
	const char *entry_current_text;
	int         entry_current_integer;

	if (peditor->p->changeset != NULL)
		gconf_change_set_remove (peditor->p->changeset, peditor->p->key);

	value = gconf_entry_get_value (entry);

	if (value != NULL) {
		value_wid = peditor->p->conv_to_widget_cb (peditor, value);
		entry_current_text = gtk_entry_get_text (GTK_ENTRY (peditor->p->ui_control));
		entry_current_integer = strtol (entry_current_text, NULL, 10);
		if (entry_current_integer != gconf_value_get_int (value)) {
			char *buf = g_strdup_printf ("%d", gconf_value_get_int (value_wid));
			gtk_entry_set_text (GTK_ENTRY (peditor->p->ui_control), buf);
			g_free (buf);
		}
		gconf_value_free (value_wid);
	}
}

static void
peditor_integer_widget_changed (GConfPropertyEditor *peditor,
				GtkEntry            *entry)
{
	GConfValue *value, *value_wid;
	  
	if (!peditor->p->inited) return;

	value_wid = gconf_value_new (GCONF_VALUE_INT);

	gconf_value_set_int (value_wid, strtol (gtk_entry_get_text (entry), NULL, 10));
	value = peditor->p->conv_from_widget_cb (peditor, value_wid);

	peditor_set_gconf_value (peditor, peditor->p->key, value);

	g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
	gconf_value_free (value_wid);
	gconf_value_free (value);
}

static GObject *
gconf_peditor_new_integer_valist (GConfChangeSet *changeset,
				  gchar          *key,
				  GtkWidget      *entry,
				  gchar          *first_property_name,
				  va_list         var_args)
{
	GObject *peditor;

	peditor = gconf_peditor_new
		(key,
		 (GConfClientNotifyFunc) peditor_integer_value_changed,
		 changeset,
		 G_OBJECT (entry),
		 first_property_name,
		 var_args, NULL);

	g_signal_connect_swapped (G_OBJECT (entry), "changed",
				  (GCallback) peditor_integer_widget_changed, peditor);

	return peditor;
}

GObject *
gconf_peditor_new_integer (GConfChangeSet *changeset,
			   gchar          *key,
			   GtkWidget      *entry,
			   gchar          *first_property_name,
			   ...)
{
	GObject *peditor;
	va_list var_args;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (entry != NULL, NULL);
	g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);

	va_start (var_args, first_property_name);

	peditor = gconf_peditor_new_integer_valist
		(changeset, key, entry,
		 first_property_name, var_args);

	va_end (var_args);

	return peditor;
}

static void
peditor_string_value_changed (GConfClient         *client,
			      guint                cnxn_id,
			      GConfEntry          *entry,
			      GConfPropertyEditor *peditor) 
{
	GConfValue *value, *value_wid;
	const char *entry_current_text;

	if (peditor->p->changeset != NULL)
		gconf_change_set_remove (peditor->p->changeset, peditor->p->key);

	value = gconf_entry_get_value (entry);

	if (value != NULL) {
		value_wid = peditor->p->conv_to_widget_cb (peditor, value);
		entry_current_text = gtk_entry_get_text (GTK_ENTRY (peditor->p->ui_control));
		if (strcmp (entry_current_text, gconf_value_get_string (value)) != 0) {
		  gtk_entry_set_text (GTK_ENTRY (peditor->p->ui_control), gconf_value_get_string (value_wid));
		}
		gconf_value_free (value_wid);
	}
}

static void
peditor_string_widget_changed (GConfPropertyEditor *peditor,
			       GtkEntry            *entry)
{
	  GConfValue *value, *value_wid;
	  
	if (!peditor->p->inited) return;

	value_wid = gconf_value_new (GCONF_VALUE_STRING);

	gconf_value_set_string (value_wid, gtk_entry_get_text (entry));
	value = peditor->p->conv_from_widget_cb (peditor, value_wid);

	peditor_set_gconf_value (peditor, peditor->p->key, value);

	g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
	gconf_value_free (value_wid);
	gconf_value_free (value);
}

static GObject *
gconf_peditor_new_string_valist (GConfChangeSet *changeset,
				 gchar          *key,
				 GtkWidget      *entry,
				 gchar          *first_property_name,
				 va_list         var_args)
{
	GObject *peditor;

	peditor = gconf_peditor_new
		(key,
		 (GConfClientNotifyFunc) peditor_string_value_changed,
		 changeset,
		 G_OBJECT (entry),
		 first_property_name,
		 var_args, NULL);

	g_signal_connect_swapped (G_OBJECT (entry), "changed",
				  (GCallback) peditor_string_widget_changed, peditor);

	return peditor;
}

GObject *
gconf_peditor_new_string (GConfChangeSet *changeset,
			  gchar          *key,
			  GtkWidget      *entry,
			  gchar          *first_property_name,
			  ...)
{
	GObject *peditor;
	va_list var_args;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (entry != NULL, NULL);
	g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);

	va_start (var_args, first_property_name);

	peditor = gconf_peditor_new_string_valist
		(changeset, key, entry,
		 first_property_name, var_args);

	va_end (var_args);

	return peditor;
}

GObject *
gconf_peditor_new_filename (GConfChangeSet *changeset,
			    gchar          *key,
			    GtkWidget      *file_entry,
			    gchar          *first_property_name,
			    ...)
{
	GObject *peditor;
	va_list var_args;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (file_entry != NULL, NULL);
	g_return_val_if_fail (GNOME_IS_FILE_ENTRY (file_entry), NULL);

	va_start (var_args, first_property_name);

	peditor = gconf_peditor_new_string_valist
		(changeset, key,
		 gnome_file_entry_gtk_entry (GNOME_FILE_ENTRY (file_entry)),
		 first_property_name, var_args);

	va_end (var_args);

	return peditor;
}

static void
peditor_color_value_changed (GConfClient         *client,
			     guint                cnxn_id,
			     GConfEntry          *entry,
			     GConfPropertyEditor *peditor) 
{
	GConfValue *value, *value_wid;
	GdkColor color;

	if (peditor->p->changeset != NULL)
		gconf_change_set_remove (peditor->p->changeset, peditor->p->key);

	value = gconf_entry_get_value (entry);

	if (value != NULL) {
		value_wid = peditor->p->conv_to_widget_cb (peditor, value);
		gdk_color_parse (gconf_value_get_string (value_wid), &color);
		gnome_color_picker_set_i16 (GNOME_COLOR_PICKER (peditor->p->ui_control), color.red, color.green, color.blue, 65535);
		gconf_value_free (value_wid);
	}
}

static void
peditor_color_widget_changed (GConfPropertyEditor *peditor,
			      guint                r,
			      guint                g,
			      guint                b,
			      guint                a,
			      GnomeColorPicker    *cp)
{
	gchar *str;
	GConfValue *value, *value_wid;

	if (!peditor->p->inited) return;

	value_wid = gconf_value_new (GCONF_VALUE_STRING);
	str = g_strdup_printf ("#%02x%02x%02x", r >> 8, g >> 8, b >> 8);
	gconf_value_set_string (value_wid, str);
	g_free (str);

	value = peditor->p->conv_from_widget_cb (peditor, value_wid);

	peditor_set_gconf_value (peditor, peditor->p->key, value);
	g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);

	gconf_value_free (value_wid);
	gconf_value_free (value);
}

GObject *
gconf_peditor_new_color (GConfChangeSet *changeset,
			 gchar          *key,
			 GtkWidget      *cp,
			 gchar          *first_property_name,
			 ...)
{
	GObject *peditor;
	va_list var_args;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (cp != NULL, NULL);
	g_return_val_if_fail (GNOME_IS_COLOR_PICKER (cp), NULL);

	va_start (var_args, first_property_name);

	peditor = gconf_peditor_new
		(key,
		 (GConfClientNotifyFunc) peditor_color_value_changed,
		 changeset,
		 G_OBJECT (cp),
		 first_property_name,
		 var_args, NULL);

	va_end (var_args);

	g_signal_connect_swapped (G_OBJECT (cp), "color_set",
				  (GCallback) peditor_color_widget_changed, peditor);

	return peditor;
}

static int
peditor_enum_int_from_string (GType type, const gchar *str, gboolean use_nick)
{
	GEnumClass *klass;
	GEnumValue *val;
	int ret = -1;

	klass = g_type_class_ref (type);
	if (use_nick)
		val = g_enum_get_value_by_nick (klass, str);
	else
		val = g_enum_get_value_by_name (klass, str);
	
	g_type_class_unref (klass);

	if (val)
		ret = val->value;
	
	return ret;
}

static gchar*
peditor_enum_string_from_int (GType type, const int index, gboolean use_nick)
{
	GEnumClass *klass;
	GEnumValue *val;
	gchar *ret = NULL;

	klass = g_type_class_ref (type);
	val = g_enum_get_value (klass, index);
	if (val)
	{
		if (val->value_nick && use_nick)
			ret = g_strdup (val->value_nick);
		else
			ret = g_strdup (val->value_name);
	}
	
	g_type_class_unref (klass);

	return ret;
}

static GConfValue*
peditor_enum_conv_to_widget (GConfPropertyEditor *peditor,
			     const GConfValue *value)
{
	GConfValue *ret;
	GConfPropertyEditorEnumData *data = peditor->p->data;
	int index;
	
	if (value->type == GCONF_VALUE_INT)
		return gconf_value_copy (value);
	
	ret = gconf_value_new (GCONF_VALUE_INT);
	
	index = peditor_enum_int_from_string (data->enum_type,
		    		    	      gconf_value_get_string (value),
					      data->use_nick);
	
	gconf_value_set_int (ret, index);

	return ret;
}

static GConfValue*
peditor_enum_conv_from_widget (GConfPropertyEditor *peditor,
			       const GConfValue *value)
{
	GConfValue *ret;
	GConfPropertyEditorEnumData *data = peditor->p->data;
	gchar *str;

	if (value->type == GCONF_VALUE_STRING)
		return gconf_value_copy (value);

	ret = gconf_value_new (GCONF_VALUE_STRING);
	str = peditor_enum_string_from_int (data->enum_type,
					    gconf_value_get_int (value),
					    data->use_nick);
	gconf_value_set_string (ret, str);
	g_free (str);

	return ret;
}

static void
peditor_select_menu_value_changed (GConfClient         *client,
				   guint                cnxn_id,
				   GConfEntry          *entry,
				   GConfPropertyEditor *peditor) 
{
	GConfValue *value, *value_wid;

	if (peditor->p->changeset != NULL)
		gconf_change_set_remove (peditor->p->changeset, peditor->p->key);

	value = gconf_entry_get_value (entry);

	if (value != NULL) {
		value_wid = peditor->p->conv_to_widget_cb (peditor, value);
		gtk_option_menu_set_history (GTK_OPTION_MENU (peditor->p->ui_control), gconf_value_get_int (value_wid));
		gconf_value_free (value_wid);
	}
}

static void
peditor_select_menu_widget_changed (GConfPropertyEditor *peditor,
				    GtkOptionMenu       *option_menu)
{
	GConfValue *value, *value_wid;

	if (!peditor->p->inited) return;
	value_wid = gconf_value_new (GCONF_VALUE_INT);
	gconf_value_set_int (value_wid, gtk_option_menu_get_history (option_menu));
	value = peditor->p->conv_from_widget_cb (peditor, value_wid);
	peditor_set_gconf_value (peditor, peditor->p->key, value);
	g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
	gconf_value_free (value_wid);
	gconf_value_free (value);
}

GObject *
gconf_peditor_new_select_menu (GConfChangeSet *changeset,
			       gchar          *key,
			       GtkWidget      *option_menu,
			       gchar          *first_property_name,
			       ...)
{
	GObject *peditor;
	va_list var_args;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (option_menu != NULL, NULL);
	g_return_val_if_fail (GTK_IS_OPTION_MENU (option_menu), NULL);

	va_start (var_args, first_property_name);

	peditor = gconf_peditor_new
		(key,
		 (GConfClientNotifyFunc) peditor_select_menu_value_changed,
		 changeset,
		 G_OBJECT (option_menu),
		 first_property_name,
		 var_args, NULL);

	va_end (var_args);

	g_signal_connect_swapped (G_OBJECT (option_menu), "changed",
				  (GCallback) peditor_select_menu_widget_changed, peditor);

	return peditor;
}

GObject *
gconf_peditor_new_select_menu_with_enum	(GConfChangeSet *changeset,
					 gchar 	        *key,
					 GtkWidget      *option_menu,
					 GType          enum_type,
					 gboolean	use_nick,
					 gchar          *first_property_name,
					 ...)
{
	GConfPropertyEditor *peditor;
	GConfPropertyEditorEnumData *data;
	va_list var_args;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (option_menu != NULL, NULL);
	g_return_val_if_fail (GTK_IS_OPTION_MENU (option_menu), NULL);
	g_return_val_if_fail (enum_type != G_TYPE_NONE, NULL);

	data = g_new0 (GConfPropertyEditorEnumData, 1);
	data->enum_type = enum_type;
	data->use_nick = use_nick;

	va_start (var_args, first_property_name);

	peditor = GCONF_PROPERTY_EDITOR (
		gconf_peditor_new
		(key,
		 (GConfClientNotifyFunc) peditor_select_menu_value_changed,
		 changeset,
		 G_OBJECT (option_menu),
		 first_property_name,
		 var_args,
		 "conv-to-widget-cb",
		 peditor_enum_conv_to_widget,
		 "conv-from-widget-cb",
		 peditor_enum_conv_from_widget,
		 "data",
		 data,
		 "data-free-cb",
		 g_free,
		 NULL
		 ));

	va_end (var_args);
	
	g_signal_connect_swapped (G_OBJECT (option_menu), "changed",
				  (GCallback) peditor_select_menu_widget_changed, peditor);

	return G_OBJECT (peditor);
}

static void
peditor_select_radio_value_changed (GConfClient         *client,
				    guint                cnxn_id,
				    GConfEntry          *entry,
				    GConfPropertyEditor *peditor) 
{
	GSList *group, *link;
	GConfValue *value, *value_wid;

	if (peditor->p->changeset != NULL)
		gconf_change_set_remove (peditor->p->changeset, peditor->p->key);

	value = gconf_entry_get_value (entry);

	if (value != NULL) {
		value_wid = peditor->p->conv_to_widget_cb (peditor, value);
		group = g_slist_copy (gtk_radio_button_get_group (GTK_RADIO_BUTTON (peditor->p->ui_control)));
		group = g_slist_reverse (group);
		link = g_slist_nth (group, gconf_value_get_int (value_wid));
		if (link && link->data)
			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (link->data), TRUE);
		gconf_value_free (value_wid);
		g_slist_free (group);
	}
}

static void
peditor_select_radio_widget_changed (GConfPropertyEditor *peditor,
				     GtkToggleButton     *tb)
{
	GSList *group;
	GConfValue *value, *value_wid;

	if (!peditor->p->inited) return;
	if (!tb->active) return;

	value_wid = gconf_value_new (GCONF_VALUE_INT);
	group = g_slist_copy (gtk_radio_button_get_group (GTK_RADIO_BUTTON (peditor->p->ui_control)));
	group = g_slist_reverse (group);
	
	gconf_value_set_int (value_wid, g_slist_index (group, tb));
	value = peditor->p->conv_from_widget_cb (peditor, value_wid);

	peditor_set_gconf_value (peditor, peditor->p->key, value);
	g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);

	gconf_value_free (value_wid);
	gconf_value_free (value);
	g_slist_free (group);
}

GObject *
gconf_peditor_new_select_radio (GConfChangeSet *changeset,
				gchar          *key,
				GSList         *radio_group,
				gchar          *first_property_name,
				...)
{
	GObject *peditor;
	GtkRadioButton *first_button;
	GSList *item;
	va_list var_args;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (radio_group != NULL, NULL);
	g_return_val_if_fail (radio_group->data != NULL, NULL);
	g_return_val_if_fail (GTK_IS_RADIO_BUTTON (radio_group->data), NULL);

	first_button = GTK_RADIO_BUTTON (radio_group->data);

	va_start (var_args, first_property_name);

	peditor = gconf_peditor_new
		(key,
		 (GConfClientNotifyFunc) peditor_select_radio_value_changed,
		 changeset,
		 G_OBJECT (first_button),
		 first_property_name,
		 var_args, NULL);

	va_end (var_args);

	for (item = radio_group; item != NULL; item = item->next)
		g_signal_connect_swapped (G_OBJECT (item->data), "toggled",
					  (GCallback) peditor_select_radio_widget_changed, peditor);

	return peditor;
}

static void
peditor_numeric_range_value_changed (GConfClient         *client,
				     guint                cnxn_id,
				     GConfEntry          *entry,
				     GConfPropertyEditor *peditor) 
{
	GConfValue *value, *value_wid;

	if (peditor->p->changeset != NULL)
		gconf_change_set_remove (peditor->p->changeset, peditor->p->key);

	value = gconf_entry_get_value (entry);

	if (value != NULL) {
		value_wid = peditor->p->conv_to_widget_cb (peditor, value);

		switch (value_wid->type) {
		case GCONF_VALUE_FLOAT:
			gtk_adjustment_set_value (GTK_ADJUSTMENT (peditor->p->ui_control), gconf_value_get_float (value_wid));
			break;
		case GCONF_VALUE_INT:
			gtk_adjustment_set_value (GTK_ADJUSTMENT (peditor->p->ui_control), gconf_value_get_int (value_wid));
			break;
		default:
			g_warning ("Unknown type in range peditor: %d\n", value_wid->type);
		}
		gconf_value_free (value_wid);
	}
}

static void
peditor_numeric_range_widget_changed (GConfPropertyEditor *peditor,
				      GtkAdjustment       *adjustment)
{
	GConfValue *value, *value_wid, *default_value;

	if (!peditor->p->inited) return;

	/* We try to get the default type from the schemas.  if not, we default
	 * to a float.
	 */
	default_value = gconf_client_get_default_from_schema (gconf_client_get_default (),
							      peditor->p->key,
							      NULL);
	if (default_value)
		value_wid = gconf_value_new (default_value->type);
	else
		value_wid = gconf_value_new (GCONF_VALUE_FLOAT);

	gconf_value_free (default_value);

	g_assert (value_wid);

	if (value_wid->type == GCONF_VALUE_INT)
		gconf_value_set_int (value_wid, gtk_adjustment_get_value (adjustment));
	else if (value_wid->type == GCONF_VALUE_FLOAT)
		gconf_value_set_float (value_wid, gtk_adjustment_get_value (adjustment));
	else {
		g_warning ("unable to set a gconf key for %s of type %d\n",
			   peditor->p->key,
			   value_wid->type);
		gconf_value_free (value_wid);
		return;
	}
	value = peditor->p->conv_from_widget_cb (peditor, value_wid);
	peditor_set_gconf_value (peditor, peditor->p->key, value);
	g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);
	gconf_value_free (value_wid);
	gconf_value_free (value);
}

GObject *
gconf_peditor_new_numeric_range (GConfChangeSet *changeset,
				 gchar          *key,
				 GtkWidget      *range,
				 gchar          *first_property_name,
				 ...)
{
	GObject *peditor;
	GObject *adjustment = NULL;
	va_list var_args;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (range != NULL, NULL);
	g_return_val_if_fail (GTK_IS_RANGE (range)||GTK_IS_SPIN_BUTTON (range), NULL);

	if (GTK_IS_RANGE (range))
		adjustment = G_OBJECT (gtk_range_get_adjustment (GTK_RANGE (range)));
	else if (GTK_IS_SPIN_BUTTON (range))
		adjustment = G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (range)));
	else
		g_assert_not_reached ();

	va_start (var_args, first_property_name);

	peditor = gconf_peditor_new
		(key,
		 (GConfClientNotifyFunc) peditor_numeric_range_value_changed,
		 changeset,
		 G_OBJECT (adjustment),
		 first_property_name,
		 var_args, NULL);

	va_end (var_args);

	g_signal_connect_swapped (adjustment, "value_changed",
				  (GCallback) peditor_numeric_range_widget_changed, peditor);

	return peditor;
}

static gboolean
guard_get_bool (GConfPropertyEditor *peditor, const GConfValue *value)
{
	if (value->type == GCONF_VALUE_BOOL)
		return gconf_value_get_bool (value);
	else
	{
		GConfPropertyEditorEnumData *data = peditor->p->data;
		int index = peditor_enum_int_from_string (data->enum_type, gconf_value_get_string (value), data->use_nick);
		return (index != data->enum_val_false);
	}
}

static void
guard_value_changed (GConfPropertyEditor *peditor,
		     const gchar         *key,
		     const GConfValue    *value,
		     GtkWidget           *widget) 
{
	gtk_widget_set_sensitive (widget, guard_get_bool (peditor, value));
}

void
gconf_peditor_widget_set_guard (GConfPropertyEditor *peditor,
				GtkWidget           *widget)
{
	GConfClient *client;
	GConfValue *value;

	g_return_if_fail (peditor != NULL);
	g_return_if_fail (IS_GCONF_PROPERTY_EDITOR (peditor));
	g_return_if_fail (widget != NULL);
	g_return_if_fail (GTK_IS_WIDGET (widget));

	client = gconf_client_get_default ();
	
	value = gconf_client_get (client, peditor->p->key, NULL);

	if (value) {
		gtk_widget_set_sensitive (widget, guard_get_bool (peditor, value));
        	gconf_value_free (value);
	} else {
                g_warning ("NULL GConf value: %s: possibly incomplete setup", peditor->p->key);
	}

	g_signal_connect (G_OBJECT (peditor), "value-changed", (GCallback) guard_value_changed, widget);
}

GConfValue *
gconf_value_int_to_float (GConfPropertyEditor *ignored, const GConfValue *value)
{
	GConfValue *new_value;

	new_value = gconf_value_new (GCONF_VALUE_FLOAT);
	gconf_value_set_float (new_value, gconf_value_get_int (value));
	return new_value;
}

GConfValue *
gconf_value_float_to_int (GConfPropertyEditor *ignored, const GConfValue *value)
{
	GConfValue *new_value;

	new_value = gconf_value_new (GCONF_VALUE_INT);
	gconf_value_set_int (new_value, gconf_value_get_float (value));
	return new_value;
}

static gint
peditor_font_get_size (const gchar *font_str)
{
	PangoFontDescription *desc;
	int size;
	
	g_return_val_if_fail (font_str != NULL, -1);
	
	desc = pango_font_description_from_string (font_str);
	size = pango_font_description_get_size (desc);
	pango_font_description_free (desc);

	return size / PANGO_SCALE;
}

static gchar* 
peditor_font_get_name (const gchar *font_str)
{
	PangoFontDescription *desc;
	gchar *name;

	g_return_val_if_fail (font_str != NULL, NULL);
	
	desc = pango_font_description_from_string (font_str);
	pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE);
	name = pango_font_description_to_string (desc);
	pango_font_description_free (desc);

	return name;
}

static void
peditor_font_merge_setting (GnomeFontPicker *font_picker, GConfValue *value, GConfPEditorFontType font_type)
{
	PangoFontDescription *desc;
	gchar *font_str;
	
	g_return_if_fail (GNOME_IS_FONT_PICKER (font_picker));
	g_return_if_fail (value != NULL);
	
	
	desc = pango_font_description_from_string (gnome_font_picker_get_font_name (font_picker));
	
	if (font_type == PEDITOR_FONT_SIZE)
	{
		pango_font_description_set_size (desc, gconf_value_get_int (value) * PANGO_SCALE);
	}
	else
	{
		PangoFontDescription *new_desc;
		new_desc = pango_font_description_from_string (gconf_value_get_string (value));
		if (font_type == PEDITOR_FONT_NAME)
			pango_font_description_set_size (new_desc, pango_font_description_get_size (desc));
		pango_font_description_free (desc);
		desc = new_desc;
	}

	font_str = pango_font_description_to_string (desc);
	g_object_set (G_OBJECT (font_picker),
		      "font_name", font_str,
		      "label-font-size", pango_font_description_get_size (desc) / PANGO_SCALE,
		      NULL);
	g_free (font_str);
	pango_font_description_free (desc);
}

static void
peditor_font_value_changed (GConfClient         *client,
			     guint                cnxn_id,
			     GConfEntry          *entry,
			     GConfPropertyEditor *peditor) 
{
	GConfValue *value, *value_wid;
	GConfPEditorFontType font_type;

	if (peditor->p->changeset != NULL)
		gconf_change_set_remove (peditor->p->changeset, peditor->p->key);

	font_type = GPOINTER_TO_UINT (peditor->p->data);
	value = gconf_entry_get_value (entry);

	if (value != NULL) {
		value_wid = peditor->p->conv_to_widget_cb (peditor, value);
		peditor_font_merge_setting (GNOME_FONT_PICKER (peditor->p->ui_control), value_wid, font_type);
		gconf_value_free (value_wid);
	}
}

static void
peditor_font_widget_changed (GConfPropertyEditor *peditor,
			      gchar              *font_str,
			      GnomeFontPicker    *font_picker)
{
	GConfValue *value, *value_wid = NULL;
	GConfPEditorFontType font_type;
	gchar *font_name;
	int font_size;

	if (!peditor->p->inited) return;

	font_type = GPOINTER_TO_UINT (peditor->p->data);

	font_size = peditor_font_get_size (font_str);
	g_object_set (G_OBJECT (peditor->p->ui_control),
		      "label-font-size", font_size,
		      NULL);
	
	switch (font_type)
	{
		case PEDITOR_FONT_NAME:
			value_wid = gconf_value_new (GCONF_VALUE_STRING);
			font_name = peditor_font_get_name (font_str);
			gconf_value_set_string (value_wid, font_name);
			g_free (font_name);
			break;
		case PEDITOR_FONT_SIZE:
			value_wid = gconf_value_new (GCONF_VALUE_INT);
			gconf_value_set_int (value_wid, font_size);
			break;
		case PEDITOR_FONT_COMBINED:
			value_wid = gconf_value_new (GCONF_VALUE_STRING);
			gconf_value_set_string (value_wid, font_str);
			break;
	}
	
	value = peditor->p->conv_from_widget_cb (peditor, value_wid);

	peditor_set_gconf_value (peditor, peditor->p->key, value);
	g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);

	gconf_value_free (value_wid);
	gconf_value_free (value);
}

GObject *
gconf_peditor_new_font (GConfChangeSet *changeset,
			gchar *key,
			GtkWidget *font_picker,
			GConfPEditorFontType font_type,
			gchar *first_property_name,
			...)
{
	GObject *peditor;
	va_list var_args;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (font_picker != NULL, NULL);
	g_return_val_if_fail (GNOME_IS_FONT_PICKER (font_picker), NULL);

	va_start (var_args, first_property_name);
	
	peditor = gconf_peditor_new
		(key,
		 (GConfClientNotifyFunc) peditor_font_value_changed,
		 changeset,
		 G_OBJECT (font_picker),
		 first_property_name,
		 var_args,
		 "data", GUINT_TO_POINTER (font_type),
		 NULL);

	va_end (var_args);

	g_signal_connect_swapped (G_OBJECT (font_picker), "font_set",
				  (GCallback) peditor_font_widget_changed, peditor);

	return peditor;
}

static GConfValue*
peditor_enum_toggle_conv_to_widget (GConfPropertyEditor *peditor,
				    const GConfValue *value)
{
	GConfValue *ret;
	GConfPropertyEditorEnumData *data = peditor->p->data;
	int index;
	
	if (value->type == GCONF_VALUE_BOOL)
		return gconf_value_copy (value);
	
	ret = gconf_value_new (GCONF_VALUE_BOOL);
	
	index = peditor_enum_int_from_string (data->enum_type,
		    		    	      gconf_value_get_string (value),
					      data->use_nick);
	gconf_value_set_bool (ret, (index != data->enum_val_false));

	return ret;
}

static GConfValue*
peditor_enum_toggle_conv_from_widget (GConfPropertyEditor *peditor,
				      const GConfValue *value)
{
	GConfValue *ret;
	GConfPropertyEditorEnumData *data = peditor->p->data;
	gchar *str;
	int index;

	if (value->type == GCONF_VALUE_STRING)
		return gconf_value_copy (value);

	ret = gconf_value_new (GCONF_VALUE_STRING);
	if (gconf_value_get_bool (value))
		index = data->enum_val_true_fn (peditor, data->enum_val_true_fn_data);
	else
		index = data->enum_val_false;

	str = peditor_enum_string_from_int (data->enum_type, index, data->use_nick);
	gconf_value_set_string (ret, str);
	g_free (str);

	return ret;
}

GObject *
gconf_peditor_new_enum_toggle  (GConfChangeSet 	 *changeset,
				gchar			 *key,
				GtkWidget		 *checkbox,
				GType			 enum_type,
				GConfPEditorGetValueFn   val_true_fn,
				guint			 val_false,
				gboolean		 use_nick,
				gpointer		 data,
				gchar 			 *first_property_name,
				...)
{
	GConfPropertyEditor *peditor;
	GConfPropertyEditorEnumData *enum_data;
	va_list var_args;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (checkbox != NULL, NULL);
	g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (checkbox), NULL);

	enum_data = g_new0 (GConfPropertyEditorEnumData, 1);
	enum_data->enum_type = enum_type;
	enum_data->enum_val_true_fn = val_true_fn;
	enum_data->enum_val_true_fn_data = data;
	enum_data->enum_val_false = val_false;
	enum_data->use_nick = use_nick;

	va_start (var_args, first_property_name);

	peditor = GCONF_PROPERTY_EDITOR (
		gconf_peditor_new
		(key,
		 (GConfClientNotifyFunc) peditor_boolean_value_changed,
		 changeset,
		 G_OBJECT (checkbox),
		 first_property_name,
		 var_args,
		 "conv-to-widget-cb",
		 peditor_enum_toggle_conv_to_widget,
		 "conv-from-widget-cb",
		 peditor_enum_toggle_conv_from_widget,
		 "data",
		 enum_data,
		 "data-free-cb",
		 g_free,
		 NULL));

	va_end (var_args);

	g_signal_connect_swapped (G_OBJECT (checkbox), "toggled",
				  (GCallback) peditor_boolean_widget_changed, peditor);

	return G_OBJECT (peditor);
}

gboolean
peditor_image_set_filename (GConfPropertyEditor *peditor, const gchar *filename)
{
	GdkPixbuf *pixbuf = NULL;
	GdkPixbuf *scaled;
	GtkImage *image = NULL;
	const int scale = 100;
	gchar *message = NULL;
	GList *l;
	
	/* NULL is not valid, however "" is, but not an error (it's
	 * the default) */
	g_return_val_if_fail (filename != NULL, FALSE);


	if (!g_file_test (filename, G_FILE_TEST_EXISTS))
	{
		message = g_strdup_printf (_("Couldn't find the file '%s'.\n\nPlease make "
					      "sure it exists and try again, " 
					      "or choose a different background picture."),
					   filename);

	}
	else if (!(pixbuf = gdk_pixbuf_new_from_file (filename, NULL)))
	{
		message = g_strdup_printf (_("I don't know how to open the file '%s'.\n"
					     "Perhaps it's "
					     "a kind of picture that is not yet supported.\n\n"
					     "Please select a different picture instead."),
					   filename);
	}

	if (GTK_IS_IMAGE (GTK_BIN (peditor->p->ui_control)->child))
		image = GTK_IMAGE (GTK_BIN (peditor->p->ui_control)->child);
	else
	{
		for (l = gtk_container_get_children (GTK_CONTAINER (GTK_BIN (peditor->p->ui_control)->child)); l != NULL; l = l->next)
		{	
			if (GTK_IS_IMAGE (l->data))
				image = GTK_IMAGE (l->data);
			else if (GTK_IS_LABEL (l->data) && message == NULL)
			{
				gchar *base = g_path_get_basename (filename);
				gtk_label_set_text (GTK_LABEL (l->data), base);
				g_free (base);
			}
		}
	}

	if (message)
	{
		if (peditor->p->inited) 
		{
			GtkWidget *box;
			
			box = gtk_message_dialog_new (NULL,
						      GTK_DIALOG_MODAL,
						      GTK_MESSAGE_ERROR,
						      GTK_BUTTONS_OK,
						      message);
			gtk_dialog_run (GTK_DIALOG (box));
			gtk_widget_destroy (box);
		} else {
			gtk_image_set_from_stock (image, GTK_STOCK_MISSING_IMAGE,
						  GTK_ICON_SIZE_BUTTON);
		}
		g_free (message);

		return FALSE;
	}

	scaled = preview_file_selection_intelligent_scale (pixbuf,
							   scale);

	gtk_image_set_from_pixbuf (image, scaled);
	g_object_unref (G_OBJECT (pixbuf));
	g_object_unref (G_OBJECT (scaled));

	return TRUE;
}

void
peditor_image_fsel_ok_cb (GtkFileSelection *fsel, gpointer data)
{
	GConfValue *value, *value_wid;
	GConfPropertyEditor *peditor;
	const gchar *filename;

	peditor = g_object_get_data (G_OBJECT (fsel), "peditor");

	if (!peditor->p->inited)
		return;

	filename = gtk_file_selection_get_filename (fsel);
	if (!(filename && peditor_image_set_filename (peditor, filename)))
		return;

	value_wid = gconf_value_new (GCONF_VALUE_STRING);
	gconf_value_set_string (value_wid, gtk_file_selection_get_filename (fsel));
	value = peditor->p->conv_from_widget_cb (peditor, value_wid);

	peditor_set_gconf_value (peditor, peditor->p->key, value);
	g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value);

	gconf_value_free (value_wid);
	gconf_value_free (value);
	gtk_widget_destroy (GTK_WIDGET (fsel));
}

void
peditor_image_clicked_cb (GConfPropertyEditor *peditor, GtkButton *button)
{
	GConfValue *value = NULL, *value_wid;
	const gchar *filename;
	GtkWidget *fsel;

	fsel = preview_file_selection_new (_("Please select an image."), TRUE);
	gtk_window_set_modal (GTK_WINDOW (fsel), TRUE);

	/* need the current filename */
	if (peditor->p->changeset)
		gconf_change_set_check_value (peditor->p->changeset, peditor->p->key, &value);

	if (value)
	{
		/* the one we got is not a copy */
		value = gconf_value_copy (value);
	}
	else
	{
		GConfClient *client = gconf_client_get_default ();
		value = gconf_client_get (client, peditor->p->key, NULL);
	}
	
	value_wid = peditor->p->conv_to_widget_cb (peditor, value);
	filename = gconf_value_get_string (value_wid);

	if (filename && strcmp (filename, ""))
		gtk_file_selection_set_filename (GTK_FILE_SELECTION (fsel), filename);

	g_object_set_data (G_OBJECT (fsel), "peditor", peditor);

	g_signal_connect_swapped  (G_OBJECT (GTK_FILE_SELECTION (fsel)->ok_button),
				   "clicked",
				   (GCallback) peditor_image_fsel_ok_cb,
				   fsel);
	
	g_signal_connect_swapped  (G_OBJECT (GTK_FILE_SELECTION (fsel)->cancel_button),
				   "clicked",
				   (GCallback) gtk_widget_destroy,
				   fsel);

	if (gtk_grab_get_current ())
		gtk_grab_add (fsel);
	
	gtk_widget_show (fsel);

	gconf_value_free (value);
	gconf_value_free (value_wid);
}

static void
peditor_image_value_changed (GConfClient         *client,
			     guint                cnxn_id,
			     GConfEntry          *entry,
			     GConfPropertyEditor *peditor) 
{
	GConfValue *value, *value_wid;

	if (peditor->p->changeset != NULL)
		gconf_change_set_remove (peditor->p->changeset, peditor->p->key);

	value = gconf_entry_get_value (entry);

	if (value != NULL) {
		const gchar *filename;

		value_wid = peditor->p->conv_to_widget_cb (peditor, value);
		filename = gconf_value_get_string (value_wid);
		peditor_image_set_filename (peditor, filename);
		gconf_value_free (value_wid);
	}
}

GObject *
gconf_peditor_new_image (GConfChangeSet	  *changeset,
			 gchar	          *key,
			 GtkWidget	  *button,
			 gchar		  *first_property_name,
			 ...)
{
	GObject *peditor;
	va_list var_args;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (button != NULL, NULL);
	g_return_val_if_fail (GTK_IS_BUTTON (button), NULL);

	va_start (var_args, first_property_name);
	
	peditor = gconf_peditor_new
		(key,
		 (GConfClientNotifyFunc) peditor_image_value_changed,
		 changeset,
		 G_OBJECT (button),
		 first_property_name,
		 var_args, NULL);

	va_end (var_args);

	g_signal_connect_swapped (G_OBJECT (button), "clicked",
				  (GCallback) peditor_image_clicked_cb, peditor);

	return peditor;
}

GObject *
gconf_peditor_new_select_radio_with_enum (GConfChangeSet *changeset,
					  gchar		 *key,
					  GSList 	 *radio_group,
					  GType 	 enum_type,
					  gboolean	 use_nick,
					  gchar          *first_property_name,
					  ...)
{
	GConfPropertyEditor *peditor;
	GConfPropertyEditorEnumData *enum_data;
	GtkRadioButton *first_button;
	GSList *item;
	va_list var_args;

	g_return_val_if_fail (key != NULL, NULL);
	g_return_val_if_fail (radio_group != NULL, NULL);
	g_return_val_if_fail (radio_group->data != NULL, NULL);
	g_return_val_if_fail (GTK_IS_RADIO_BUTTON (radio_group->data), NULL);

	enum_data = g_new0 (GConfPropertyEditorEnumData, 1);
	enum_data->enum_type = enum_type;
	enum_data->use_nick = use_nick;

	first_button = GTK_RADIO_BUTTON (radio_group->data);

	va_start (var_args, first_property_name);

	peditor = GCONF_PROPERTY_EDITOR (
		gconf_peditor_new
		(key,
		 (GConfClientNotifyFunc) peditor_select_radio_value_changed,
		 changeset,
		 G_OBJECT (first_button),
		 first_property_name,
		 var_args,
		 "conv-to-widget-cb",
		 peditor_enum_conv_to_widget,
		 "conv-from-widget-cb",
		 peditor_enum_conv_from_widget,
		 "data",
		 enum_data,
		 "data-free-cb",
		 g_free,
		 NULL));

	va_end (var_args);

	for (item = radio_group; item != NULL; item = item->next)
		g_signal_connect_swapped (G_OBJECT (item->data), "toggled",
					  (GCallback) peditor_select_radio_widget_changed, peditor);

	return G_OBJECT (peditor);
}

