Index: .cvsignore
===================================================================
RCS file: /cvs/gnome/gnome-desktop/libgnome-desktop/.cvsignore,v
retrieving revision 1.9
diff -u -p -r1.9 .cvsignore
--- .cvsignore	2 Mar 2004 17:23:49 -0000	1.9
+++ .cvsignore	27 Mar 2006 19:40:23 -0000
@@ -16,4 +16,4 @@ bonobo-moniker-ditem
 gnome-desktop-2.0.pc
 gnome-desktop-2.0-uninstalled.pc
 test-ditem-edit
-test-icon-theme
+test-ditem-editor
Index: Makefile.am
===================================================================
RCS file: /cvs/gnome/gnome-desktop/libgnome-desktop/Makefile.am,v
retrieving revision 1.36
diff -u -p -r1.36 Makefile.am
--- Makefile.am	18 Nov 2004 17:42:53 -0000	1.36
+++ Makefile.am	27 Mar 2006 19:40:23 -0000
@@ -14,11 +14,12 @@ INCLUDES =							\
 
 lib_LTLIBRARIES = libgnome-desktop-2.la
 
-noinst_PROGRAMS = test-ditem test-hint test-ditem-edit
+noinst_PROGRAMS = test-ditem test-hint test-ditem-edit test-ditem-editor
 
 libgnome_desktop_2_la_SOURCES = \
 	gnome-desktop-item.c	\
 	gnome-ditem-edit.c	\
+	gnome-ditem-editor.c \
 	gnome-hint.c
 
 libgnome_desktop_2_la_LIBADD = \
@@ -45,6 +46,12 @@ test_hint_LDADD = \
 test_ditem_edit_SOURCES = test-ditem-edit.c
 
 test_ditem_edit_LDADD =		\
+	libgnome-desktop-2.la	\
+	$(GNOME_DESKTOP_LIBS)
+
+test_ditem_editor_SOURCES = test-ditem-editor.c
+
+test_ditem_editor_LDADD =	\
 	libgnome-desktop-2.la	\
 	$(GNOME_DESKTOP_LIBS)
 
Index: gnome-ditem-editor.c
===================================================================
RCS file: gnome-ditem-editor.c
diff -N gnome-ditem-editor.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-ditem-editor.c	27 Mar 2006 19:40:23 -0000
@@ -0,0 +1,1331 @@
+/*
+ * gnome-ditem-editor.c:
+ *
+ * Copyright (C) 2004, 2006 Vincent Untz
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ *	Vincent Untz <vuntz@gnome.org>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <libgnome/gnome-desktop-item.h>
+#include <libgnomeui/gnome-icon-entry.h>
+#include <libgnomeui/gnome-help.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include "gnome-ditem-editor.h"
+
+struct _GnomeDItemEditorPrivate
+{
+	/* we keep a ditem around, since we can never have absolutely
+	   everything in the display so we load a file, or get a ditem,
+	   sync the display and ref the ditem */
+	GnomeDesktopItem *ditem;
+
+	GnomeDesktopItem *revert_ditem;
+
+	gboolean ui_dirty; /* TRUE if something got changed, and ditem
+			    * was not yet synced */
+	guint    save_timeout;
+
+	char     *uri; /* file location */
+	gboolean  new_file;
+	gboolean  combo_setuped;
+
+	GtkWidget *table;
+	GtkWidget *type_label;
+	GtkWidget *type_combo;
+	GtkWidget *name_label;
+	GtkWidget *name_entry;
+	GtkWidget *command_hbox;
+	GtkWidget *command_label;
+	GtkWidget *command_entry;
+	GtkWidget *command_browse_button;
+	GtkWidget *command_browse_filechooser;
+	GtkWidget *comment_label;
+	GtkWidget *comment_entry;
+	GtkWidget *icon_entry;
+
+	GtkWidget *help_button;
+	GtkWidget *revert_button;
+	GtkWidget *close_button;
+	GtkWidget *cancel_button;
+	GtkWidget *ok_button;
+
+	/* the directory of the theme for the icon, see bug #119208 */
+	char *icon_theme_dir;
+};
+
+/* Time in milliseconds after which we save the file on the disk */
+#define SAVE_FREQUENCY 2000
+
+enum {
+	REVERT_BUTTON
+};
+
+typedef enum {
+	GNOME_DITEM_EDITOR_TYPE_NULL,
+	GNOME_DITEM_EDITOR_TYPE_APPLICATION,
+	GNOME_DITEM_EDITOR_TYPE_TERMINAL_APPLICATION,
+	GNOME_DITEM_EDITOR_TYPE_LINK
+} GnomeDItemEditorType;
+
+enum {
+	COLUMN_TEXT,
+	COLUMN_TYPE,
+	NUMBER_COLUMNS
+};
+
+typedef struct {
+	const char           *name;
+	GnomeDItemEditorType  type;
+	GnomeDesktopItemType  show_for;
+} ComboItem;
+
+static ComboItem type_items [] = {
+	{ N_("Application"),             GNOME_DITEM_EDITOR_TYPE_APPLICATION         , GNOME_DESKTOP_ITEM_TYPE_APPLICATION },
+	{ N_("Application in Terminal"), GNOME_DITEM_EDITOR_TYPE_TERMINAL_APPLICATION, GNOME_DESKTOP_ITEM_TYPE_APPLICATION },
+	{ N_("File"),                    GNOME_DITEM_EDITOR_TYPE_LINK                , GNOME_DESKTOP_ITEM_TYPE_LINK }
+};
+
+enum {
+	CHANGED,
+	NAME_CHANGED,
+	COMMAND_CHANGED,
+	COMMENT_CHANGED,
+	ICON_CHANGED,
+	ERROR_REPORTED,
+	LAST_SIGNAL
+};
+
+enum {
+	PROP_0,
+	PROP_URI,
+};
+
+static guint ditem_edit_signals[LAST_SIGNAL] = { 0 };
+
+#define GNOME_DITEM_EDITOR_GET_PRIVATE(o)  (GNOME_DITEM_EDITOR (o)->priv)
+
+G_DEFINE_TYPE (GnomeDItemEditor, gnome_ditem_editor, GTK_TYPE_DIALOG);
+
+static void type_combo_changed (GnomeDItemEditor *dialog);
+
+static gboolean gnome_ditem_editor_load_uri (GnomeDItemEditor  *dialog,
+					     GError           **error);
+static gboolean gnome_ditem_editor_save_timeout (gpointer data);
+static gboolean gnome_ditem_editor_save         (GnomeDItemEditor *dialog);
+static void response_cb (GtkDialog *dialog,
+			 gint       response_id);
+static void gnome_ditem_editor_set_ditem (GnomeDItemEditor       *dialog,
+					  const GnomeDesktopItem *ditem);
+
+static GnomeDesktopItemType
+map_type_to_desktop_item (GnomeDItemEditorType type)
+{
+	switch (type) {
+	case GNOME_DITEM_EDITOR_TYPE_APPLICATION:
+	case GNOME_DITEM_EDITOR_TYPE_TERMINAL_APPLICATION:
+		return GNOME_DESKTOP_ITEM_TYPE_APPLICATION;
+	case GNOME_DITEM_EDITOR_TYPE_LINK:
+		return GNOME_DESKTOP_ITEM_TYPE_LINK;
+	default:
+		return GNOME_DESKTOP_ITEM_TYPE_NULL;
+	}
+}
+
+static GnomeDItemEditorType
+map_type_from_desktop_item (GnomeDesktopItemType type,
+			    gboolean             terminal)
+{
+	switch (type) {
+	case GNOME_DESKTOP_ITEM_TYPE_APPLICATION:
+		if (terminal)
+			return GNOME_DITEM_EDITOR_TYPE_TERMINAL_APPLICATION;
+		else
+			return GNOME_DITEM_EDITOR_TYPE_APPLICATION;
+	case GNOME_DESKTOP_ITEM_TYPE_LINK:
+		return GNOME_DITEM_EDITOR_TYPE_LINK;
+	default:
+		return GNOME_DITEM_EDITOR_TYPE_NULL;
+	}
+}
+
+static GObject *
+gnome_ditem_editor_constructor (GType                  type,
+				guint                  n_construct_properties,
+				GObjectConstructParam *construct_properties)
+{
+	GObject          *obj;
+	GnomeDItemEditor *dialog;
+	GnomeVFSURI      *vfs_uri;
+
+	obj = G_OBJECT_CLASS (gnome_ditem_editor_parent_class)->constructor (type,
+									     n_construct_properties,
+									     construct_properties);
+
+	dialog = GNOME_DITEM_EDITOR (obj);
+
+	if (dialog->priv->uri) {
+		vfs_uri = gnome_vfs_uri_new (dialog->priv->uri);
+		if (gnome_vfs_uri_exists (vfs_uri)) {
+			//FIXME what if there's an error?
+			gnome_ditem_editor_load_uri (dialog, NULL);
+			dialog->priv->new_file = FALSE;
+		} else {
+			dialog->priv->new_file = TRUE;
+		}
+		gnome_vfs_uri_unref (vfs_uri);
+	}
+
+	return obj;
+}
+
+static void
+gnome_ditem_editor_get_property (GObject    *object,
+				 guint	     prop_id,
+				 GValue	    *value,
+				 GParamSpec *pspec)
+{
+	GnomeDItemEditor *dialog;
+
+	g_return_if_fail (GNOME_IS_DITEM_EDITOR (object));
+
+	dialog = GNOME_DITEM_EDITOR (object);
+
+	switch (prop_id) {
+	case PROP_URI:
+		g_value_set_string (value, gnome_ditem_editor_get_uri (dialog));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gnome_ditem_editor_set_property (GObject       *object,
+				 guint	       prop_id,
+				 const GValue *value,
+				 GParamSpec   *pspec)
+{
+	GnomeDItemEditor *dialog;
+
+	g_return_if_fail (GNOME_IS_DITEM_EDITOR (object));
+
+	dialog = GNOME_DITEM_EDITOR (object);
+
+	switch (prop_id) {
+	case PROP_URI:
+		gnome_ditem_editor_set_uri (dialog, g_value_get_string (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gnome_ditem_editor_destroy (GtkObject *object)
+{
+	GnomeDItemEditor *dialog;
+	
+	dialog = GNOME_DITEM_EDITOR (object);
+
+	/* If there was a timeout, then something changed after last save,
+	 * so we must save again now */
+	if (dialog->priv->save_timeout) {
+		g_source_remove (dialog->priv->save_timeout);
+		dialog->priv->save_timeout = 0;
+		gnome_ditem_editor_save (dialog);
+	}
+
+	/* remember, destroy can be run multiple times! */
+
+	if (dialog->priv->ditem != NULL)
+		gnome_desktop_item_unref (dialog->priv->ditem);
+	dialog->priv->ditem = NULL;
+
+	if (dialog->priv->revert_ditem != NULL)
+		gnome_desktop_item_unref (dialog->priv->revert_ditem);
+	dialog->priv->revert_ditem = NULL;
+
+	if (dialog->priv->uri != NULL)
+		g_free (dialog->priv->uri);
+	dialog->priv->uri = NULL;
+
+	if (dialog->priv->icon_theme_dir != NULL)
+		g_free (dialog->priv->icon_theme_dir);
+	dialog->priv->icon_theme_dir = NULL;
+
+	GTK_OBJECT_CLASS (gnome_ditem_editor_parent_class)->destroy (object);
+}
+
+static void
+gnome_ditem_editor_class_init (GnomeDItemEditorClass *class)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+	GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (class);
+
+	gobject_class->constructor = gnome_ditem_editor_constructor;
+	gobject_class->get_property = gnome_ditem_editor_get_property;
+        gobject_class->set_property = gnome_ditem_editor_set_property;
+
+	gtkobject_class->destroy = gnome_ditem_editor_destroy;
+
+	g_type_class_add_private (class,
+				  sizeof (GnomeDItemEditorPrivate));
+
+	ditem_edit_signals[CHANGED] =
+		g_signal_new ("changed",
+			      G_TYPE_FROM_CLASS (gobject_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GnomeDItemEditorClass,
+					       changed),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE, 0);
+
+	ditem_edit_signals[NAME_CHANGED] =
+		g_signal_new ("name_changed",
+			      G_TYPE_FROM_CLASS (gobject_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GnomeDItemEditorClass,
+					       name_changed),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE, 1,
+			      G_TYPE_STRING);
+
+	ditem_edit_signals[COMMAND_CHANGED] =
+		g_signal_new ("command_changed",
+			      G_TYPE_FROM_CLASS (gobject_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GnomeDItemEditorClass,
+					       command_changed),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE, 1,
+			      G_TYPE_STRING);
+
+	ditem_edit_signals[COMMENT_CHANGED] =
+		g_signal_new ("comment_changed",
+			      G_TYPE_FROM_CLASS (gobject_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GnomeDItemEditorClass,
+					       comment_changed),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE, 1,
+			      G_TYPE_STRING);
+
+	ditem_edit_signals[ICON_CHANGED] =
+		g_signal_new ("icon_changed",
+			      G_TYPE_FROM_CLASS (gobject_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GnomeDItemEditorClass,
+					       icon_changed),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE, 1,
+			      G_TYPE_STRING);
+
+	ditem_edit_signals[ERROR_REPORTED] =
+		g_signal_new ("error_reported",
+			      G_TYPE_FROM_CLASS (gobject_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GnomeDItemEditorClass,
+					       error_reported),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE, 1,
+			      G_TYPE_STRING);
+
+	g_object_class_install_property (
+		gobject_class,
+		PROP_URI,
+		g_param_spec_string ("uri",
+				     "URI",
+				     "The URI of the .desktop file",
+				     NULL,
+				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+}
+
+static GtkWidget *
+label_new_with_mnemonic (const char *text)
+{
+	GtkWidget *label;
+	char      *bold;
+
+	bold = g_strdup_printf ("<b>%s</b>", text);
+	label = gtk_label_new_with_mnemonic (bold);
+	g_free (bold);
+
+	gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+	gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
+
+	gtk_widget_show (label);
+
+	return label;
+}
+
+static inline void
+table_attach_label (GtkTable  *table,
+		    GtkWidget *label,
+		    int        left,
+		    int        right,
+		    int        top,
+		    int        bottom)
+{
+	gtk_table_attach (table, label, left, right, top, bottom,
+			  GTK_FILL, GTK_FILL,
+			  0, 0);
+}
+
+static inline void
+table_attach_entry (GtkTable  *table,
+		    GtkWidget *entry,
+		    int        left,
+		    int        right,
+		    int        top,
+		    int        bottom)
+{
+	gtk_table_attach (table, entry, left, right, top, bottom,
+			  GTK_EXPAND | GTK_FILL | GTK_SHRINK, GTK_FILL,
+			  0, 0);
+}
+
+static void
+setup_combo (GtkWidget            *combo_box,
+	     ComboItem            *items,
+	     int                   nb_items,
+	     GnomeDesktopItemType for_type)
+{
+	GtkListStore          *model;
+	GtkTreeIter            iter;
+	GtkCellRenderer       *renderer;
+	int                    i;
+
+	model = gtk_list_store_new (NUMBER_COLUMNS,
+				    G_TYPE_STRING,
+				    G_TYPE_INT);
+
+	gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box),
+				 GTK_TREE_MODEL (model));
+
+	for (i = 0; i < nb_items; i++) {
+		if (for_type != GNOME_DESKTOP_ITEM_TYPE_NULL &&
+		    for_type != items [i].show_for)
+			continue;
+
+		gtk_list_store_append (model, &iter);
+		gtk_list_store_set (model, &iter,
+				    COLUMN_TEXT, _(items [i].name),
+				    COLUMN_TYPE, items [i].type,
+				    -1);
+	}
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
+				    renderer, TRUE);
+	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box),
+					renderer, "text", COLUMN_TEXT, NULL);
+
+	gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
+}
+
+static GnomeDItemEditorType
+gnome_ditem_editor_get_item_type (GnomeDItemEditor *dialog)
+{
+	GtkTreeIter           iter;
+	GtkTreeModel         *model;
+	GnomeDItemEditorType  type;
+
+	if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (dialog->priv->type_combo),
+					    &iter))
+		return GNOME_DITEM_EDITOR_TYPE_NULL;
+
+	model = gtk_combo_box_get_model (GTK_COMBO_BOX (dialog->priv->type_combo));
+	gtk_tree_model_get (model, &iter, COLUMN_TYPE, &type, -1);
+
+	return type;
+}
+
+static void
+gnome_ditem_editor_make_ui (GnomeDItemEditor *dialog)
+{
+	GnomeDItemEditorPrivate *priv;
+
+	priv = dialog->priv;
+
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
+	gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+
+	priv->table = gtk_table_new (4, 3, FALSE);
+	gtk_container_set_border_width (GTK_CONTAINER (priv->table), 12);
+	gtk_table_set_row_spacings (GTK_TABLE (priv->table), 6);
+	gtk_table_set_col_spacings (GTK_TABLE (priv->table), 12);
+	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
+			    priv->table, TRUE, TRUE, 0);
+	gtk_widget_show (priv->table);
+
+	/* Type */
+	priv->type_label = label_new_with_mnemonic (_("_Type:"));
+	priv->type_combo = gtk_combo_box_new ();
+	gtk_widget_show (priv->type_combo);
+	gtk_label_set_mnemonic_widget (GTK_LABEL (priv->type_label),
+				       priv->type_combo);
+
+	/* Name */
+	priv->name_label = label_new_with_mnemonic (_("_Name:"));
+	priv->name_entry = gtk_entry_new ();
+	gtk_widget_show (priv->name_entry);
+	gtk_label_set_mnemonic_widget (GTK_LABEL (priv->name_label),
+				       priv->name_entry);
+
+	/* Icon */
+	priv->icon_entry = gnome_icon_entry_new ("desktop-icon",
+						 _("Browse icons"));
+	gtk_table_attach (GTK_TABLE (priv->table), priv->icon_entry,
+			  0, 1, 0, 2,
+			  0, 0, 0, 0);
+	gtk_widget_show (priv->icon_entry);
+
+	/* Command */
+	priv->command_label = label_new_with_mnemonic ("");
+
+	priv->command_hbox = gtk_hbox_new (FALSE, 12);
+	gtk_widget_show (priv->command_hbox);
+
+	priv->command_entry = gtk_entry_new ();
+	gtk_box_pack_start (GTK_BOX (priv->command_hbox),
+			    priv->command_entry,
+			    TRUE, TRUE, 0);
+	gtk_widget_show (priv->command_entry);
+
+	priv->command_browse_button = gtk_button_new_with_mnemonic (_("_Browse..."));
+	gtk_box_pack_start (GTK_BOX (priv->command_hbox),
+			    priv->command_browse_button,
+			    FALSE, FALSE, 0);
+	gtk_widget_show (priv->command_browse_button);
+
+	/* Comment */
+	priv->comment_label = label_new_with_mnemonic (_("Co_mment:"));
+	priv->comment_entry = gtk_entry_new ();
+	gtk_widget_show (priv->comment_entry);
+	gtk_label_set_mnemonic_widget (GTK_LABEL (priv->comment_label),
+				       priv->comment_entry);
+
+	priv->help_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
+						   GTK_STOCK_HELP,
+						   GTK_RESPONSE_HELP);
+	priv->revert_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
+						     GTK_STOCK_REVERT_TO_SAVED,
+						     REVERT_BUTTON);
+	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+					   REVERT_BUTTON,
+					   FALSE);
+	priv->close_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
+						    GTK_STOCK_CLOSE,
+						    GTK_RESPONSE_CLOSE);
+	priv->cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
+						     GTK_STOCK_CANCEL,
+						     GTK_RESPONSE_CANCEL);
+	priv->ok_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
+						 GTK_STOCK_OK,
+						 GTK_RESPONSE_OK);
+
+	/* FIXME: There needs to be a way to edit ALL keys/sections */
+}
+
+static void
+map_cb (GnomeDItemEditor *dialog)
+{
+	GnomeDItemEditorPrivate *priv;
+	gboolean                 show_combo;
+	GList                   *focus_chain;
+
+	priv = dialog->priv;
+
+	if (priv->new_file) {
+		gtk_widget_hide (priv->revert_button);
+		gtk_widget_hide (priv->close_button);
+		gtk_widget_show (priv->cancel_button);
+		gtk_widget_show (priv->ok_button);
+
+		setup_combo (priv->type_combo,
+			     type_items, G_N_ELEMENTS (type_items),
+			     GNOME_DESKTOP_ITEM_TYPE_NULL);
+		priv->combo_setuped = TRUE;
+
+		show_combo = TRUE;
+	} else {
+		gtk_widget_show (priv->revert_button);
+		gtk_widget_show (priv->close_button);
+		gtk_widget_hide (priv->cancel_button);
+		gtk_widget_hide (priv->ok_button);
+
+		show_combo = gnome_ditem_editor_get_item_type (dialog) != GNOME_DITEM_EDITOR_TYPE_LINK;
+	}
+
+	if (show_combo) {
+		table_attach_label (GTK_TABLE (priv->table), priv->type_label,
+				    1, 2, 0, 1);
+		table_attach_entry (GTK_TABLE (priv->table), priv->type_combo,
+				    2, 3, 0, 1);
+
+		table_attach_label (GTK_TABLE (priv->table), priv->name_label,
+				    1, 2, 1, 2);
+		table_attach_entry (GTK_TABLE (priv->table), priv->name_entry,
+				    2, 3, 1, 2);
+
+		table_attach_label (GTK_TABLE (priv->table), priv->command_label,
+				    1, 2, 2, 3);
+		table_attach_entry (GTK_TABLE (priv->table), priv->command_hbox,
+				    2, 3, 2, 3);
+
+		table_attach_label (GTK_TABLE (priv->table), priv->comment_label,
+				    1, 2, 3, 4);
+		table_attach_entry (GTK_TABLE (priv->table), priv->comment_entry,
+				    2, 3, 3, 4);
+
+		gtk_widget_grab_focus (priv->type_combo);
+	} else {
+		table_attach_label (GTK_TABLE (priv->table), priv->name_label,
+				    1, 2, 0, 1);
+		table_attach_entry (GTK_TABLE (priv->table), priv->name_entry,
+				    2, 3, 0, 1);
+
+		table_attach_label (GTK_TABLE (priv->table), priv->command_label,
+				    1, 2, 1, 2);
+		table_attach_entry (GTK_TABLE (priv->table), priv->command_hbox,
+				    2, 3, 1, 2);
+
+		table_attach_label (GTK_TABLE (priv->table), priv->comment_label,
+				    1, 2, 2, 3);
+		table_attach_entry (GTK_TABLE (priv->table), priv->comment_entry,
+				    2, 3, 2, 3);
+
+		gtk_widget_grab_focus (priv->name_entry);
+	}
+
+	type_combo_changed (dialog);
+
+	/* set a focus chain since GTK+ doesn't want to put the icon entry
+	 * as the first widget in the chain */
+	focus_chain = NULL;
+	focus_chain = g_list_prepend (focus_chain, priv->icon_entry);
+	focus_chain = g_list_prepend (focus_chain, priv->type_combo);
+	focus_chain = g_list_prepend (focus_chain, priv->name_entry);
+	focus_chain = g_list_prepend (focus_chain, priv->command_hbox);
+	focus_chain = g_list_prepend (focus_chain, priv->comment_entry);
+	focus_chain = g_list_reverse (focus_chain);
+	gtk_container_set_focus_chain (GTK_CONTAINER (priv->table),
+				       focus_chain);
+	g_list_free (focus_chain);
+}
+
+/*
+ * Will save after SAVE_FREQUENCY milliseconds of no changes. If something is
+ * changed, the save is postponed to another SAVE_FREQUENCY seconds. This seems
+ * to be a saner behaviour than just saving every N seconds.
+ */
+static void
+gnome_ditem_editor_changed (GnomeDItemEditor *dialog)
+{
+	/* We're not in sync with what's on the disk */
+	dialog->priv->ui_dirty = TRUE;
+
+	if (!dialog->priv->new_file) {
+		if (dialog->priv->save_timeout != 0)
+			g_source_remove (dialog->priv->save_timeout);
+
+		dialog->priv->save_timeout = g_timeout_add (SAVE_FREQUENCY,
+							    gnome_ditem_editor_save_timeout,
+							    dialog);
+
+		/* We can revert to the orignal state */
+		if (dialog->priv->revert_ditem != NULL)
+			gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+							   REVERT_BUTTON,
+							   TRUE);
+	}
+
+	g_signal_emit (G_OBJECT (dialog), ditem_edit_signals[CHANGED], 0);
+}
+
+static void
+gnome_ditem_editor_name_changed (GnomeDItemEditor *dialog)
+{
+	g_signal_emit (G_OBJECT (dialog), ditem_edit_signals[NAME_CHANGED], 0,
+		       gtk_entry_get_text (GTK_ENTRY (dialog->priv->name_entry)));
+}
+
+static void
+gnome_ditem_editor_command_changed (GnomeDItemEditor *dialog)
+{
+	g_signal_emit (G_OBJECT (dialog), ditem_edit_signals[COMMAND_CHANGED], 0,
+		       gtk_entry_get_text (GTK_ENTRY (dialog->priv->command_entry)));
+}
+
+static void
+gnome_ditem_editor_comment_changed (GnomeDItemEditor *dialog)
+{
+	g_signal_emit (G_OBJECT (dialog), ditem_edit_signals[COMMENT_CHANGED], 0,
+		       gtk_entry_get_text (GTK_ENTRY (dialog->priv->comment_entry)));
+}
+
+static void
+gnome_ditem_editor_icon_changed (GnomeDItemEditor *dialog)
+{
+	char *icon;
+	icon = gnome_icon_entry_get_filename (GNOME_ICON_ENTRY (dialog->priv->icon_entry));
+	g_signal_emit (G_OBJECT (dialog), ditem_edit_signals[ICON_CHANGED], 0,
+		       icon);
+	g_free (icon);
+}
+
+static void
+command_browse_chooser_response (GtkFileChooser   *chooser,
+				 gint              response_id,
+				 GnomeDItemEditor *dialog)
+{
+	char *uri;
+
+	if (response_id == GTK_RESPONSE_ACCEPT) {
+		switch (gnome_ditem_editor_get_item_type (dialog)) {
+		case GNOME_DITEM_EDITOR_TYPE_APPLICATION:
+		case GNOME_DITEM_EDITOR_TYPE_TERMINAL_APPLICATION:
+			uri = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
+			break;
+		case GNOME_DITEM_EDITOR_TYPE_LINK:
+			uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (chooser));
+			break;
+		default:
+			g_assert_not_reached ();
+		}
+
+		gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry),
+				    uri);
+		g_free (uri);
+	}
+
+	gtk_widget_destroy (GTK_WIDGET (chooser));
+	dialog->priv->command_browse_filechooser = NULL;
+}
+
+static void
+update_chooser_for_type (GnomeDItemEditor *dialog)
+{
+	const char *title;
+	gboolean    local_only;
+	GtkWidget  *chooser;
+
+	if (!dialog->priv->command_browse_filechooser)
+		return;
+
+	switch (gnome_ditem_editor_get_item_type (dialog)) {
+	case GNOME_DITEM_EDITOR_TYPE_APPLICATION:
+	case GNOME_DITEM_EDITOR_TYPE_TERMINAL_APPLICATION:
+		title = _("Choose an application...");
+		local_only = TRUE;
+		break;
+	case GNOME_DITEM_EDITOR_TYPE_LINK:
+		title = _("Choose a file...");
+		local_only = FALSE;
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+
+	chooser = dialog->priv->command_browse_filechooser;
+
+	gtk_window_set_title (GTK_WINDOW (chooser),
+			      title);
+	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser),
+					 local_only);
+}
+
+static void
+command_browse_button_clicked (GnomeDItemEditor *dialog)
+{
+	GtkWidget *chooser;
+
+	if (dialog->priv->command_browse_filechooser) {
+		gtk_window_present (GTK_WINDOW (dialog->priv->command_browse_filechooser));
+		return;
+	}
+
+	chooser = gtk_file_chooser_dialog_new ("", GTK_WINDOW (dialog),
+					       GTK_FILE_CHOOSER_ACTION_OPEN,
+					       GTK_STOCK_CANCEL,
+					       GTK_RESPONSE_CANCEL,
+					       GTK_STOCK_OPEN,
+					       GTK_RESPONSE_ACCEPT,
+					       NULL);
+	gtk_window_set_transient_for (GTK_WINDOW (chooser),
+				      GTK_WINDOW (dialog));
+	gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE);
+
+	g_signal_connect (chooser, "response",
+			  G_CALLBACK (command_browse_chooser_response), dialog);
+
+	dialog->priv->command_browse_filechooser = chooser;
+	update_chooser_for_type (dialog);
+
+	gtk_widget_show (chooser);
+}
+
+static void
+gnome_ditem_editor_connect_signals (GnomeDItemEditor *dialog)
+{
+	GnomeDItemEditorPrivate *priv;
+
+	priv = dialog->priv;
+
+#define CONNECT_CHANGED(widget, callback) \
+	g_signal_connect_swapped (G_OBJECT (widget), "changed", \
+				  G_CALLBACK (gnome_ditem_editor_changed), \
+				  dialog); \
+	g_signal_connect_swapped (G_OBJECT (widget), "changed", \
+				  G_CALLBACK (callback), \
+				  dialog);
+
+	CONNECT_CHANGED (priv->type_combo, type_combo_changed);
+	CONNECT_CHANGED (priv->name_entry, gnome_ditem_editor_name_changed);
+	CONNECT_CHANGED (priv->command_entry, gnome_ditem_editor_command_changed);
+	CONNECT_CHANGED (priv->comment_entry, gnome_ditem_editor_comment_changed);
+	CONNECT_CHANGED (priv->icon_entry, gnome_ditem_editor_icon_changed);
+
+	g_signal_connect_swapped (priv->command_browse_button, "clicked",
+				  G_CALLBACK (command_browse_button_clicked),
+				  dialog);
+
+	g_signal_connect (dialog, "map", G_CALLBACK (map_cb), NULL);
+
+	/* We do a signal connection here rather than overriding the method in
+	 * class_init because GtkDialog::response is a RUN_LAST signal. We
+	 * want *our* handler to be run *first*, regardless of whether the user
+	 * installs response handlers of his own.
+	 */
+	g_signal_connect (dialog, "response", G_CALLBACK (response_cb), NULL);
+}
+
+static void
+gnome_ditem_editor_init (GnomeDItemEditor *dialog)
+{
+	GnomeDItemEditorPrivate *priv;
+
+	priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
+					    GNOME_TYPE_DITEM_EDITOR,
+					    GnomeDItemEditorPrivate);
+
+	dialog->priv = priv;
+
+	priv->ditem = NULL;
+	priv->revert_ditem = NULL;
+	priv->ui_dirty = FALSE;
+	priv->save_timeout = 0;
+	priv->uri = NULL;
+	priv->new_file = FALSE;
+	priv->combo_setuped = FALSE;
+	priv->icon_theme_dir = NULL;
+	priv->command_browse_filechooser = NULL;
+
+	gnome_ditem_editor_make_ui (dialog);
+	gnome_ditem_editor_connect_signals (dialog);
+}
+
+static void
+type_combo_changed (GnomeDItemEditor *dialog)
+{
+	const char *text;
+	char       *bold;
+
+	switch (gnome_ditem_editor_get_item_type (dialog)) {
+	case GNOME_DITEM_EDITOR_TYPE_APPLICATION:
+	case GNOME_DITEM_EDITOR_TYPE_TERMINAL_APPLICATION:
+		text = _("_Command:");
+		break;
+	case GNOME_DITEM_EDITOR_TYPE_LINK:
+		text = _("_URL:");
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+
+	bold = g_strdup_printf ("<b>%s</b>", text);
+	gtk_label_set_markup_with_mnemonic (GTK_LABEL (dialog->priv->command_label),
+					    bold);
+	g_free (bold);
+
+	gtk_label_set_mnemonic_widget (GTK_LABEL (dialog->priv->command_label),
+				       dialog->priv->command_entry);
+
+	update_chooser_for_type (dialog);
+}
+
+static void
+gnome_ditem_editor_clear (GnomeDItemEditor *dialog)
+{
+	g_return_if_fail (GNOME_IS_DITEM_EDITOR (dialog));
+
+	if (dialog->priv->ditem != NULL)
+		gnome_desktop_item_unref (dialog->priv->ditem);
+	dialog->priv->ditem = NULL;
+
+	dialog->priv->ui_dirty = TRUE;
+	if (!dialog->priv->new_file && dialog->priv->revert_ditem != NULL)
+		gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+						   REVERT_BUTTON,
+						   TRUE);
+
+	gtk_entry_set_text (GTK_ENTRY (dialog->priv->name_entry), "");
+	gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->priv->type_combo), 0);
+	gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry), "");
+	gtk_entry_set_text (GTK_ENTRY (dialog->priv->comment_entry),"");
+	gnome_icon_entry_set_filename (GNOME_ICON_ENTRY (dialog->priv->icon_entry), "");
+}
+
+/* Conform display to ditem */
+static void
+gnome_ditem_editor_sync_display (GnomeDItemEditor *dialog)
+{
+	GnomeDItemEditorType  type;
+	gboolean              run_in_terminal;
+	GnomeDesktopItem     *ditem;
+	const char           *buffer;
+	char                 *tmpstr;
+	GtkTreeIter           iter;
+	GtkTreeModel         *model;
+	GnomeDItemEditorType  buf_type;
+
+	g_return_if_fail (GNOME_IS_DITEM_EDITOR (dialog));
+
+	ditem = dialog->priv->ditem;
+
+	if (ditem == NULL) {
+		gnome_ditem_editor_clear (dialog);
+		return;
+	}
+
+	/* Name */
+	buffer = gnome_desktop_item_get_localestring (ditem,
+						      GNOME_DESKTOP_ITEM_NAME);
+	gtk_entry_set_text (GTK_ENTRY (dialog->priv->name_entry),
+			    buffer ? buffer : "");
+
+	/* Type */
+	run_in_terminal = gnome_desktop_item_get_boolean (ditem,
+							  GNOME_DESKTOP_ITEM_TERMINAL);
+	type = map_type_from_desktop_item (gnome_desktop_item_get_entry_type (ditem),
+					   run_in_terminal);
+
+	if (!dialog->priv->combo_setuped) {
+		setup_combo (dialog->priv->type_combo,
+			     type_items, G_N_ELEMENTS (type_items),
+			     map_type_to_desktop_item (type));
+		dialog->priv->combo_setuped = TRUE;
+	}
+
+	model = gtk_combo_box_get_model (GTK_COMBO_BOX (dialog->priv->type_combo));
+	if (!gtk_tree_model_get_iter_first (model, &iter))
+		g_assert_not_reached ();
+	do {
+		gtk_tree_model_get (model, &iter, COLUMN_TYPE, &buf_type, -1);
+		if (type == buf_type) {
+			gtk_combo_box_set_active_iter (GTK_COMBO_BOX (dialog->priv->type_combo),
+						       &iter);
+			break;
+		}
+	} while (gtk_tree_model_iter_next (model, &iter));
+
+	g_assert (type == buf_type || type == GNOME_DITEM_EDITOR_TYPE_NULL);
+
+	/* Command */
+	if (type == GNOME_DITEM_EDITOR_TYPE_LINK) {
+		buffer = gnome_desktop_item_get_string (ditem,
+							GNOME_DESKTOP_ITEM_URL);
+	} else {
+		buffer = gnome_desktop_item_get_string (ditem,
+							GNOME_DESKTOP_ITEM_EXEC);
+	}
+	gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry),
+			    buffer ? buffer : "");
+
+	/* Comment */
+	buffer = gnome_desktop_item_get_localestring (ditem,
+						      GNOME_DESKTOP_ITEM_COMMENT);
+	gtk_entry_set_text (GTK_ENTRY (dialog->priv->comment_entry),
+			    buffer ? buffer : "");
+
+
+	/* Icon */
+	buffer = gnome_desktop_item_get_string (ditem, GNOME_DESKTOP_ITEM_ICON);
+	tmpstr = gnome_desktop_item_get_icon (ditem, NULL);
+	gnome_icon_entry_set_filename (GNOME_ICON_ENTRY (dialog->priv->icon_entry),
+				       tmpstr);
+
+	g_free (dialog->priv->icon_theme_dir);
+	if (buffer != NULL && !g_path_is_absolute (buffer)) {
+		/* this is a themed icon, see bug #119208 */
+		dialog->priv->icon_theme_dir = g_path_get_dirname (tmpstr);
+		/* FIXME: what about theme changes when the dialog is up */
+	} else {
+		/* use the default pixmap directory as the standard
+		 * icon_theme_dir, since the standard directory is themed */
+		g_object_get (G_OBJECT (dialog->priv->icon_entry), "pixmap_subdir",
+			      &(dialog->priv->icon_theme_dir), NULL);
+	}
+
+	g_free (tmpstr);
+
+	/* ui can't be dirty, I mean, damn we just synced it from the ditem */
+	dialog->priv->ui_dirty = FALSE;
+	if (dialog->priv->save_timeout != 0) {
+		g_source_remove (dialog->priv->save_timeout);
+		dialog->priv->save_timeout = 0;
+	}
+}
+
+static void
+gnome_ditem_editor_sync_ditem (GnomeDItemEditor *dialog)
+{
+	GnomeDesktopItem     *ditem;
+	const char           *buffer;
+	const char           *C_value;
+	char                 *file;
+	GnomeDesktopItemType  type;
+	const char           *attr;
+
+	g_return_if_fail (GNOME_IS_DITEM_EDITOR (dialog));
+
+	if (!dialog->priv->ditem) {
+		dialog->priv->ditem = gnome_desktop_item_new ();
+		/* Bad gnome_desktop_item_new() initialises the Name to "No
+		 * name". Just kill this because we'll set it ourselves. */
+		gnome_desktop_item_set_string (dialog->priv->ditem,
+					       GNOME_DESKTOP_ITEM_NAME,
+					       "");
+	}
+
+	ditem = dialog->priv->ditem;
+
+	/* Name */
+	buffer = gtk_entry_get_text (GTK_ENTRY (dialog->priv->name_entry));
+	gnome_desktop_item_set_localestring (ditem, GNOME_DESKTOP_ITEM_NAME,
+					     buffer);
+	/* Make sure we set the "C" locale strings to the terms we set here.
+	 * This is so that if the user logs into another locale they get their
+	 * own description there rather then empty. It is not the C locale
+	 * however, but the user created this entry herself so it's OK */
+	C_value = gnome_desktop_item_get_string (ditem,
+						 GNOME_DESKTOP_ITEM_NAME);
+	if (C_value == NULL || C_value [0] == '\0')
+		gnome_desktop_item_set_string (ditem, GNOME_DESKTOP_ITEM_NAME,
+					       buffer);
+
+	/* Type */
+	type = map_type_to_desktop_item (gnome_ditem_editor_get_item_type (dialog));
+	gnome_desktop_item_set_entry_type (ditem, type);
+
+	/* Command */
+	buffer = gtk_entry_get_text (GTK_ENTRY (dialog->priv->command_entry));
+	/* hack really */
+	if (type == GNOME_DESKTOP_ITEM_TYPE_LINK)
+		attr = GNOME_DESKTOP_ITEM_URL;
+	else
+		attr = GNOME_DESKTOP_ITEM_EXEC;
+	gnome_desktop_item_set_string (ditem, attr, buffer);
+
+	/* Comment */
+	buffer = gtk_entry_get_text (GTK_ENTRY (dialog->priv->comment_entry));
+	gnome_desktop_item_set_localestring (ditem, GNOME_DESKTOP_ITEM_COMMENT,
+					     buffer);
+	/* See the comment when we set the name for an explanation */
+	C_value = gnome_desktop_item_get_string (ditem,
+						 GNOME_DESKTOP_ITEM_COMMENT);
+	if (C_value == NULL || C_value [0] == '\0')
+		gnome_desktop_item_set_string (ditem, GNOME_DESKTOP_ITEM_COMMENT,
+					       buffer);
+
+	/* Icon */
+	file = gnome_icon_entry_get_filename (GNOME_ICON_ENTRY (dialog->priv->icon_entry));
+	if (file != NULL && file[0] != '\0') {
+		/* if the icon_theme_dir is the same as the directory name of
+		 * this icon, then just use the basename as we've just picked
+		 * another icon from the theme.  See bug #119208 */
+		char *dn = g_path_get_dirname (file);
+		if (dialog->priv->icon_theme_dir != NULL &&
+		    strcmp (dn, dialog->priv->icon_theme_dir) == 0) {
+			char *base = g_path_get_basename (file);
+			g_free (file);
+			file = base;
+		}
+		g_free (dn);
+	}
+	gnome_desktop_item_set_string (ditem, GNOME_DESKTOP_ITEM_ICON, file);
+	g_free (file);
+			
+	if (type == GNOME_DESKTOP_ITEM_TYPE_APPLICATION) {
+		gboolean run_in_terminal;
+
+		run_in_terminal = (gnome_ditem_editor_get_item_type (dialog) == GNOME_DITEM_EDITOR_TYPE_TERMINAL_APPLICATION);
+		gnome_desktop_item_set_boolean (ditem,
+						GNOME_DESKTOP_ITEM_TERMINAL,
+						run_in_terminal);
+	}
+
+	dialog->priv->ui_dirty = FALSE;
+}
+
+static gboolean
+gnome_ditem_editor_save (GnomeDItemEditor *dialog)
+{
+	const char       *buffer;
+	GError           *error;
+
+	if (dialog->priv->save_timeout != 0)
+		g_source_remove (dialog->priv->save_timeout);
+	dialog->priv->save_timeout = 0;
+
+	if (dialog->priv->ui_dirty)
+		gnome_ditem_editor_sync_ditem (GNOME_DITEM_EDITOR (dialog));
+
+	/* Verify that the required informations are set */
+	buffer = gtk_entry_get_text (GTK_ENTRY (dialog->priv->name_entry));
+	if (buffer == NULL || buffer [0] == '\0') {
+		g_signal_emit (G_OBJECT (dialog),
+			       ditem_edit_signals[ERROR_REPORTED], 0,
+			       _("The name of the launcher is not set."));
+		return FALSE;
+	}
+
+	buffer = gtk_entry_get_text (GTK_ENTRY (dialog->priv->command_entry));
+	if (buffer == NULL || buffer [0] == '\0') {
+		char *error;
+
+		switch (gnome_ditem_editor_get_item_type (dialog)) {
+		case GNOME_DITEM_EDITOR_TYPE_APPLICATION:
+		case GNOME_DITEM_EDITOR_TYPE_TERMINAL_APPLICATION:
+			error = _("The command of the launcher is not set.");
+			break;
+		case GNOME_DITEM_EDITOR_TYPE_LINK:
+			error = _("The URI of the launcher is not set.");
+			break;
+		default:
+			g_assert_not_reached ();
+		}
+
+		g_signal_emit (G_OBJECT (dialog),
+			       ditem_edit_signals[ERROR_REPORTED], 0,
+			       error);
+		return FALSE;
+	}
+
+	/* And now, try to save */
+	error = NULL;
+	gnome_desktop_item_save (dialog->priv->ditem,
+				 dialog->priv->uri,
+				 TRUE /* force */,
+				 &error);
+	if (error != NULL) {
+		g_signal_emit (G_OBJECT (dialog),
+			       ditem_edit_signals[ERROR_REPORTED], 0,
+			       error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+gnome_ditem_editor_save_timeout (gpointer data)
+{
+	GnomeDItemEditor *dialog;
+
+	dialog = GNOME_DITEM_EDITOR (data);
+	gnome_ditem_editor_save (dialog);
+
+	return FALSE;
+}
+
+/* GtkDialog::response handler */
+static void
+response_cb (GtkDialog *dialog,
+	     gint       response_id)
+{
+	GnomeDesktopItem *ditem;
+	GError *error = NULL;
+
+	switch (response_id) {
+	case GTK_RESPONSE_HELP:
+		if (!gnome_help_display_desktop_on_screen (NULL, "user-guide",
+							   "user-guide",
+							   "gospanel-52",
+							   gtk_window_get_screen (GTK_WINDOW (dialog)),
+							   &error)) {
+			g_signal_emit (G_OBJECT (dialog),
+				       ditem_edit_signals[ERROR_REPORTED], 0,
+				       _("Could not display help document."));
+			/* FIXME we should probably use error->message */
+			g_error_free (error);
+		}
+		break;
+	case REVERT_BUTTON:
+		ditem = GNOME_DITEM_EDITOR_GET_PRIVATE (dialog)->revert_ditem;
+		if (ditem != NULL)
+			gnome_ditem_editor_set_ditem (GNOME_DITEM_EDITOR (dialog),
+						      ditem);
+		else
+			gnome_ditem_editor_clear (GNOME_DITEM_EDITOR (dialog));
+
+		gtk_dialog_set_response_sensitive (dialog,
+						   REVERT_BUTTON,
+						   FALSE);
+		break;
+	case GTK_RESPONSE_OK:
+	case GTK_RESPONSE_CLOSE:
+		if (gnome_ditem_editor_save (GNOME_DITEM_EDITOR (dialog)))
+			gtk_widget_destroy (GTK_WIDGET (dialog));
+		break;
+	case GTK_RESPONSE_DELETE_EVENT:
+		if (!GNOME_DITEM_EDITOR (dialog)->priv->new_file)
+			/* We need to revert the changes */
+			gtk_dialog_response (dialog, REVERT_BUTTON);
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		break;
+	case GTK_RESPONSE_CANCEL:
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+}
+
+static void
+gnome_ditem_editor_set_ditem (GnomeDItemEditor       *dialog,
+			      const GnomeDesktopItem *ditem)
+{
+	g_return_if_fail (GNOME_IS_DITEM_EDITOR (dialog));
+	g_return_if_fail (ditem != NULL);
+
+	if (dialog->priv->ditem)
+		gnome_desktop_item_unref (dialog->priv->ditem);
+
+	dialog->priv->ditem = gnome_desktop_item_copy (ditem);
+	dialog->priv->ui_dirty = TRUE;
+
+	gnome_ditem_editor_sync_display (dialog);
+}
+
+static gboolean
+gnome_ditem_editor_load_uri (GnomeDItemEditor  *dialog,
+			     GError           **error)
+{
+        GnomeDesktopItem *newentry;
+
+	g_return_val_if_fail (GNOME_IS_DITEM_EDITOR (dialog), FALSE);
+        g_return_val_if_fail (dialog->priv->uri != NULL, FALSE);
+
+        newentry = gnome_desktop_item_new_from_uri (dialog->priv->uri, 0, error);
+
+        if (!newentry)
+		return FALSE;
+
+	if (dialog->priv->ditem)
+		gnome_desktop_item_unref (dialog->priv->ditem);
+
+	dialog->priv->ditem = newentry;
+	dialog->priv->ui_dirty = TRUE;
+
+	gnome_ditem_editor_sync_display (dialog);
+
+	/* This should be after gnome_ditem_editor_sync_display ()
+	 * so the revert button is insensitive */
+	if (dialog->priv->revert_ditem != NULL)
+		gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+						   REVERT_BUTTON,
+						   TRUE);
+	else
+		dialog->priv->revert_ditem = gnome_desktop_item_copy (newentry);
+
+	return TRUE;
+}
+
+GtkWidget *
+gnome_ditem_editor_new (GtkWindow   *parent,
+			const char  *uri,
+			const char  *title)
+{
+	GtkWidget *dialog;
+
+	dialog = g_object_new (GNOME_TYPE_DITEM_EDITOR,
+			       "title", title,
+			       "uri", uri,
+			       NULL);
+
+	if (parent)
+		gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
+
+	return dialog;
+}
+
+void
+gnome_ditem_editor_set_uri (GnomeDItemEditor *dialog,
+			    const char       *uri)
+{
+	g_return_if_fail (GNOME_IS_DITEM_EDITOR (dialog));
+
+	if (!dialog->priv->uri && (!uri || !uri [0]))
+		return;
+
+	if (dialog->priv->uri && uri && uri [0] &&
+	    !strcmp (dialog->priv->uri, uri))
+		return;
+
+	if (dialog->priv->uri)
+		g_free (dialog->priv->uri);
+	dialog->priv->uri = NULL;
+
+	if (uri && uri [0])
+		dialog->priv->uri = g_strdup (uri);
+
+	g_object_notify (G_OBJECT (dialog), "uri");
+}
+
+G_CONST_RETURN char *
+gnome_ditem_editor_get_uri (GnomeDItemEditor *dialog)
+{
+	g_return_val_if_fail (GNOME_IS_DITEM_EDITOR (dialog), NULL);
+
+	return dialog->priv->uri;
+}
Index: gnome-ditem-editor.h
===================================================================
RCS file: gnome-ditem-editor.h
diff -N gnome-ditem-editor.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnome-ditem-editor.h	27 Mar 2006 19:40:23 -0000
@@ -0,0 +1,91 @@
+/*
+ * gnome-ditem-editor.h:
+ *
+ * Copyright (C) 2004, 2006 Vincent Untz
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ *	Vincent Untz <vuntz@gnome.org>
+ */
+
+#ifndef GNOME_DITEM_EDITOR_H
+#define GNOME_DITEM_EDITOR_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GNOME_TYPE_DITEM_EDITOR			(gnome_ditem_editor_get_type ())
+#define GNOME_DITEM_EDITOR(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_DITEM_EDITOR, GnomeDItemEditor))
+#define GNOME_DITEM_EDITOR_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_DITEM_EDITOR, GnomeDItemEditorClass))
+#define GNOME_IS_DITEM_EDITOR(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_DITEM_EDITOR))
+#define GNOME_IS_DITEM_EDITOR_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_DITEM_EDITOR))
+#define GNOME_DITEM_EDITOR_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_DITEM_EDITOR, GnomeDItemEditorClass))
+
+typedef struct _GnomeDItemEditor      GnomeDItemEditor;
+typedef struct _GnomeDItemEditorClass GnomeDItemEditorClass;
+
+typedef struct _GnomeDItemEditorPrivate GnomeDItemEditorPrivate;
+
+struct _GnomeDItemEditorClass
+{
+	GtkDialogClass parent_class;
+
+	/* Any information changed */
+	void (* changed)         (GnomeDItemEditor *dialog);
+
+	/* These more specific signals are provided since they 
+	 * will likely require a display update */
+	/* The name of the item has changed. */
+	void (* name_changed)    (GnomeDItemEditor *dialog,
+				  const char       *error);
+	/* The command of the item has changed. */
+	void (* command_changed) (GnomeDItemEditor *dialog,
+				  const char       *error);
+	/* The comment of the item has changed. */
+	void (* comment_changed) (GnomeDItemEditor *dialog,
+				  const char       *error);
+	/* The icon in particular has changed. */
+	void (* icon_changed)    (GnomeDItemEditor *dialog,
+				  const char       *error);
+
+	/* An error is reported. */
+	void (* error_reported)  (GnomeDItemEditor *dialog,
+				  const char       *error);
+};
+
+struct _GnomeDItemEditor
+{
+	GtkDialog parent_instance;
+
+	GnomeDItemEditorPrivate *priv;
+};
+
+GType      gnome_ditem_editor_get_type (void);
+
+GtkWidget *gnome_ditem_editor_new (GtkWindow   *parent,
+				   const char  *uri,
+				   const char  *title);
+
+void gnome_ditem_editor_set_uri (GnomeDItemEditor *dialog,
+				 const char       *uri);
+
+G_CONST_RETURN char * gnome_ditem_editor_get_uri (GnomeDItemEditor *dialog);
+
+G_END_DECLS
+
+#endif /* GNOME_DITEM_EDITOR_H */
Index: test-ditem-editor.c
===================================================================
RCS file: test-ditem-editor.c
diff -N test-ditem-editor.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ test-ditem-editor.c	27 Mar 2006 19:40:23 -0000
@@ -0,0 +1,103 @@
+#include "config.h"
+
+#include <libgnomeui/libgnomeui.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include "gnome-ditem-editor.h"
+
+static void
+changed_callback (GnomeDItemEditor *editor,
+		  gpointer          data)
+{
+	g_printerr ("changed_callback invoked\n");
+}
+
+static void
+name_changed_callback (GnomeDItemEditor *editor,
+		       const char       *name,
+		       gpointer          data)
+{
+	g_printerr ("name_changed_callback invoked: %s\n", name);
+}
+
+static void
+command_changed_callback (GnomeDItemEditor *editor,
+			  const char       *command,
+			  gpointer          data)
+{
+	g_printerr ("command_changed_callback invoked: %s\n", command);
+}
+
+static void
+comment_changed_callback (GnomeDItemEditor *editor,
+			  const char       *comment,
+			  gpointer          data)
+{
+	g_printerr ("comment_changed_callback invoked: %s\n", comment);
+}
+
+static void
+icon_changed_callback (GnomeDItemEditor *editor,
+		       const char       *icon,
+		       gpointer          data)
+{
+	g_printerr ("icon_changed_callback invoked: %s\n", icon);
+}
+
+static void
+error_reported_callback (GnomeDItemEditor *editor,
+			 const char       *error,
+			 gpointer          data)
+{
+	g_printerr ("error: %s\n", error);
+}
+
+int
+main(int argc, char * argv[])
+{
+        GtkWidget *editor;
+	char      *uri;
+
+	if (argc != 2) {
+		g_printerr ("usage: test-ditem-edit <.desktop-file>\n");
+		return 1;
+	}
+
+	gnome_program_init ("gnome-ditem-editor", "0.1",
+			    LIBGNOMEUI_MODULE,
+			    argc, argv, NULL);
+
+	uri = gnome_vfs_make_uri_from_shell_arg (argv [1]);
+	editor = gnome_ditem_editor_new (NULL, uri, NULL);
+	g_free (uri);
+
+        g_signal_connect_swapped (editor, "delete_event",
+				  G_CALLBACK (gtk_widget_destroy), editor);
+
+        g_signal_connect (editor, "destroy",
+			  G_CALLBACK (gtk_main_quit),
+			  NULL);
+
+        g_signal_connect (editor, "changed",
+                          G_CALLBACK (changed_callback), NULL);
+
+        g_signal_connect (editor, "name_changed",
+                         G_CALLBACK (name_changed_callback), NULL);
+
+        g_signal_connect (editor, "command_changed",
+                         G_CALLBACK (command_changed_callback), NULL);
+
+        g_signal_connect (editor, "comment_changed",
+                         G_CALLBACK (comment_changed_callback), NULL);
+
+        g_signal_connect (editor, "icon_changed",
+                         G_CALLBACK (icon_changed_callback), NULL);
+
+        g_signal_connect (editor, "error_reported",
+                         G_CALLBACK (error_reported_callback), NULL);
+
+        gtk_widget_show (editor);
+
+        gtk_main();
+
+        return 0;
+}
