/*
 * glade-named-icon-chooser-widget.c - Named icon chooser widget
 *
 * Copyright (C) 2007 Vincent Geddes
 *
 * Author:  Vincent Geddes <vgeddes@gnome.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANNAMED_ICON_CHOOSERILITY 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>

#include "glade-named-icon-chooser-dialog.h"
#include "icon-naming-spec.c"

#include <gtk/gtk.h>
#include <glib/gi18n-lib.h>
#include <string.h>
#include <errno.h>


#define DEFAULT_SETTING_LIST_STANDARD_ONLY   TRUE

#define GLADE_NAMED_ICON_CHOOSER_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o),     \
							GLADE_TYPE_NAMED_ICON_CHOOSER_DIALOG, \
							GladeNamedIconChooserDialogPrivate))

enum
{
	CONTEXTS_ID_COLUMN,
	CONTEXTS_NAME_COLUMN,
	CONTEXTS_TITLE_COLUMN,
	
	CONTEXTS_N_COLUMS
};

enum
{
	ICONS_CONTEXT_COLUMN,
	ICONS_STANDARD_COLUMN,
	ICONS_NAME_COLUMN,
	
	ICONS_N_COLUMNS
};

enum
{
	GLADE_NAMED_ICON
};

enum {
	ICON_ACTIVATED,
	SELECTION_CHANGED,
	
	LAST_SIGNAL
};

struct _GladeNamedIconChooserDialogPrivate
{
	GtkWidget          *icons_view; 
	GtkTreeModel       *filter_model;       /* filtering model  */
	GtkListStore       *icons_store;        /* data store       */
	GtkTreeSelection   *selection;

	GtkWidget          *contexts_view;
	GtkListStore       *contexts_store;
	
	GtkWidget          *entry;
	GtkEntryCompletion *entry_completion;
	
	GtkWidget          *button;              /* list-standard-only checkbutton */
	
	gint                context_id;          /* current icon name context for icon filtering */
	
	gchar              *pending_select_name; /* an icon name for a pending treeview selection.
	                                          * can only select name after model is loaded
	                                          * and the widget is mapped */
	
	GtkIconTheme       *icon_theme;          /* the current icon theme */
	guint               load_id;             /* id of the idle function for loading data into model */
	
	gboolean            settings_list_standard;   /* whether to list standard icon names only */
	
	GtkWidget          *last_focus_widget;
	
	gboolean            icons_loaded; /* whether the icons have been loaded into the model */
};

static GHashTable *standard_icon_quarks = NULL;

static guint       dialog_signals[LAST_SIGNAL] = { 0, };


gchar      *glade_named_icon_chooser_dialog_get_icon_name  (GladeNamedIconChooserDialog *dialog);

void        glade_named_icon_chooser_dialog_set_icon_name  (GladeNamedIconChooserDialog *dialog,
							    const gchar           *icon_name);

gboolean    glade_named_icon_chooser_dialog_set_context    (GladeNamedIconChooserDialog *dialog,
							    const gchar           *context);

gchar      *glade_named_icon_chooser_dialog_get_context    (GladeNamedIconChooserDialog *dialog);

static gboolean    should_respond                          (GladeNamedIconChooserDialog *dialog);

static void        filter_icons_model                      (GladeNamedIconChooserDialog *dialog);

static gboolean    scan_for_name_func                      (GtkTreeModel *model,
 							    GtkTreePath  *path,
 							    GtkTreeIter *iter,
 							    gpointer data);

static gboolean    scan_for_context_func                   (GtkTreeModel *model,
 							    GtkTreePath  *path,
 							    GtkTreeIter *iter,
 							    gpointer data);
 								 
static void        settings_load                           (GladeNamedIconChooserDialog *dialog);

static void        settings_save                           (GladeNamedIconChooserDialog *dialog);


G_DEFINE_TYPE (GladeNamedIconChooserDialog, glade_named_icon_chooser_dialog, GTK_TYPE_DIALOG);


static void
entry_set_name (GladeNamedIconChooserDialog *dialog, const gchar *name)
{
	/* Must disable completion before setting text, in order to avoid
	 * spurious warnings (possible GTK+ bug).
	 */
	gtk_entry_set_completion (GTK_ENTRY (dialog->priv->entry), NULL);
	
	gtk_entry_set_text (GTK_ENTRY (dialog->priv->entry), name);
	
	gtk_entry_set_completion (GTK_ENTRY (dialog->priv->entry), dialog->priv->entry_completion);
}

static GtkIconTheme *
get_icon_theme_for_widget (GtkWidget *widget)
{
	if (gtk_widget_has_screen (widget))
		return gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));

	return gtk_icon_theme_get_default ();
}

/* validates name according to the icon naming spec (en_US.US_ASCII [a-z1-9_-.])  */
static gboolean
is_well_formed (const gchar *name)
{
	gchar *c = (gchar *) name;
	for (; *c; c++)
	{
		if (g_ascii_isalnum (*c))
		{
			if (g_ascii_isalpha (*c) && !g_ascii_islower (*c))
				return FALSE;
		}
		else if (*c != '_' && *c != '-' && *c != '.')
		{
			return FALSE;
		}
	}
	return TRUE;
}

static void
check_entry_text (GladeNamedIconChooserDialog *dialog,
		  gchar   **name_ret,
		  gboolean *is_wellformed_ret, 
		  gboolean *is_empty_ret) 
{
	if (strlen (gtk_entry_get_text (GTK_ENTRY (dialog->priv->entry))) == 0)
	{
		*name_ret = NULL;
		*is_wellformed_ret = TRUE;
		*is_empty_ret = TRUE;
		
		return;
	}

	*is_empty_ret = FALSE;

	*is_wellformed_ret = is_well_formed (gtk_entry_get_text (GTK_ENTRY (dialog->priv->entry)));

	if (*is_wellformed_ret)
		*name_ret = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->priv->entry)));
	else
		*name_ret = NULL;
}

static void
changed_text_handler (GtkEditable                 *editable,
                      GladeNamedIconChooserDialog *dialog)
{
	g_signal_emit_by_name (dialog, "selection-changed", NULL);
}   

/* ensure that only valid text can be inserted into entry */
static void
insert_text_handler (GtkEditable *editable,
                     const gchar *text,
                     gint         length,
                     gint        *position,
                     GladeNamedIconChooserDialog *dialog)
{
	if (is_well_formed (text))
	{

		g_signal_handlers_block_by_func (editable, (gpointer) insert_text_handler, dialog);
					         
		gtk_editable_insert_text (editable, text, length, position);
		
		g_signal_handlers_unblock_by_func (editable, (gpointer) insert_text_handler, dialog);

	}
	else
	{
		gdk_display_beep (gtk_widget_get_display (GTK_WIDGET (dialog)));
	}
	
	g_signal_stop_emission_by_name (editable, "insert-text"); 
}

typedef struct
{
	gchar *name;           /* the name of the icon or context */

	guint found : 1;       /* whether an item matching `name' was found */
	guint do_select : 1;   /* select the matched row */
	guint do_cursor : 1;   /* put cursor at the matched row */
	guint do_activate : 1; /* activate the matched row */

	GladeNamedIconChooserDialog *dialog;
} ForEachFuncData;

void
glade_named_icon_chooser_dialog_set_icon_name (GladeNamedIconChooserDialog *dialog,
					       const gchar                 *name)
{
	ForEachFuncData *data;
	gboolean located_in_theme;

	g_return_if_fail (GLADE_IS_NAMED_ICON_CHOOSER_DIALOG (dialog));
	g_return_if_fail (gtk_widget_has_screen (GTK_WIDGET (dialog)));
	 
	if (name == NULL)
	{
		gtk_tree_selection_unselect_all (dialog->priv->selection);
		entry_set_name (dialog, "");	
		return;
	}
	
	located_in_theme = gtk_icon_theme_has_icon (get_icon_theme_for_widget (GTK_WIDGET (dialog)),
		 				    name);

	if (located_in_theme)
	{
		
		if (dialog->priv->icons_loaded && dialog->priv->filter_model)
		{

			data = g_slice_new0(ForEachFuncData);
			data->name = g_strdup (name);
			data->found = FALSE;
			data->do_activate = FALSE;
			data->do_select  = TRUE;
			data->do_cursor  = TRUE;
			data->dialog = dialog;
  
			gtk_tree_model_foreach (dialog->priv->filter_model,
						scan_for_name_func,
						data);
		
			g_free (data->name);
			g_slice_free (ForEachFuncData, data);
		
		}
		else
		{
			dialog->priv->pending_select_name = g_strdup (name);
		}
		
		/* selecting a treeview row will set the entry text,
		 * but we must have this here in case the row has been filtered out
		 */ 
		entry_set_name (dialog, name);
		
	}
	else if (is_well_formed (name))
	{
		
		gtk_tree_selection_unselect_all (dialog->priv->selection);
	
		entry_set_name (dialog, name);
	}
	else
	{
		g_warning ("invalid icon name: '%s' is not well formed", name);
	}
}

gboolean
glade_named_icon_chooser_dialog_set_context (GladeNamedIconChooserDialog *dialog,
					     const gchar                 *name)
{
	ForEachFuncData *data;

	g_return_val_if_fail (GLADE_IS_NAMED_ICON_CHOOSER_DIALOG (dialog), FALSE);

	data = g_slice_new0 (ForEachFuncData);
	
	if (name)	
		data->name = g_strdup (name);
	else
		data->name = g_strdup ("All Contexts");

	data->found = FALSE;
	data->do_select = TRUE;
	data->do_activate = FALSE;
	data->do_cursor = FALSE;
	data->dialog = dialog;
	
	gtk_tree_model_foreach (GTK_TREE_MODEL (dialog->priv->contexts_store),
				(GtkTreeModelForeachFunc) scan_for_context_func,
				data);
				
	g_free (data->name);			
	g_slice_free (ForEachFuncData, data);

	return TRUE;
}

gchar *
glade_named_icon_chooser_dialog_get_context (GladeNamedIconChooserDialog *dialog)
{
	GtkTreeSelection *sel;
	GtkTreeIter iter;
	gchar *context_name;

	g_return_val_if_fail (GLADE_IS_NAMED_ICON_CHOOSER_DIALOG (dialog), NULL);

	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->contexts_view));

	if (gtk_tree_selection_get_selected (sel, NULL, &iter))
	{
	
		gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->contexts_store), &iter,
				    CONTEXTS_NAME_COLUMN, &context_name,
				    -1);
	
		/* if context_name is NULL, then it is the 'all categories' special context */
		return context_name;
	
	}
	else
	{
		return NULL;
	}
}

static gchar *
get_icon_name_from_selection (GladeNamedIconChooserDialog *dialog)
{
	GtkTreeIter  iter;
	GtkTreeModel *model;
	gchar        *name;

	if (!gtk_tree_selection_get_selected (dialog->priv->selection, &model, &iter))
		return NULL;
	
	gtk_tree_model_get (model, &iter,
			    ICONS_NAME_COLUMN, &name,
			    -1);

	return name;
}

gchar*
glade_named_icon_chooser_dialog_get_icon_name (GladeNamedIconChooserDialog *dialog)
{
	GtkWidget *current_focus;
	gchar *name;

	g_return_val_if_fail (GLADE_IS_NAMED_ICON_CHOOSER_DIALOG (dialog), NULL);

	current_focus = gtk_window_get_focus (GTK_WINDOW (dialog));
	
	if (current_focus == dialog->priv->icons_view)
	{
		
	view:	
		name = get_icon_name_from_selection (dialog);

		if (name == NULL)
			goto entry;
	
	}
	else if (current_focus == dialog->priv->entry)
	{
		gboolean is_wellformed, is_empty;	
	entry:
		check_entry_text (dialog, &name, &is_wellformed, &is_empty);

		if (!is_wellformed || is_empty)
			return NULL;
	
	}
	else if (dialog->priv->last_focus_widget == dialog->priv->icons_view)
	{
		goto view;
	}
	else if (dialog->priv->last_focus_widget == dialog->priv->entry)
	{
		goto entry;	
	}
	else
	{
		goto view;
	}
	
	return name;
}

static void
set_busy_cursor (GladeNamedIconChooserDialog *dialog,
		 gboolean                      busy)
{
	GdkDisplay *display;
	GdkCursor  *cursor;

	if (!gtk_widget_get_realized (GTK_WIDGET (dialog)))
		return;

	display = gtk_widget_get_display (GTK_WIDGET (dialog));

	if (busy)
		cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
	else
		cursor = NULL;

	gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (dialog)), cursor);
	gdk_display_flush (display);

	if (cursor)
		gdk_cursor_unref (cursor);
}

static GtkListStore*
populate_icon_contexts_model (void)
{
	GtkListStore *store;
	GtkTreeIter iter;	
	guint i;
	
  	store = gtk_list_store_new (CONTEXTS_N_COLUMS,
  				    G_TYPE_INT,
  	                            G_TYPE_STRING,
  	                            G_TYPE_STRING);


	gtk_list_store_append (store, &iter);
	gtk_list_store_set (store, &iter,
			    CONTEXTS_ID_COLUMN,    -1,
			    CONTEXTS_NAME_COLUMN,  "All Contexts",
			    CONTEXTS_TITLE_COLUMN, _("All Contexts"),
		    	    -1);
		    	    
	gtk_list_store_append (store, &iter);
	gtk_list_store_set (store, &iter,
			    CONTEXTS_ID_COLUMN,     -1,
			    CONTEXTS_NAME_COLUMN,   NULL,
			    CONTEXTS_TITLE_COLUMN,   NULL,
		    	    -1);
				
	for (i = 0; i < G_N_ELEMENTS (standard_contexts); i++)
	{

		gtk_list_store_append (store, &iter);
		gtk_list_store_set (store, &iter,
				    CONTEXTS_ID_COLUMN,    i,
				    CONTEXTS_NAME_COLUMN,  standard_contexts[i].name,
				    CONTEXTS_TITLE_COLUMN, _(standard_contexts[i].title),
			    	    -1);
	} 
		
	return store;	    
}

static void
icons_row_activated_cb (GtkTreeView                  *view,
			GtkTreePath                  *path,
			GtkTreeViewColumn            *column,
			GladeNamedIconChooserDialog *dialog)
{
	g_signal_emit_by_name (dialog, "icon-activated", NULL);
}

static void
icons_selection_changed_cb (GtkTreeSelection             *selection,
			    GladeNamedIconChooserDialog *dialog)
{	
	GtkTreeModel *model;
	GtkTreeIter iter;
	gchar *name;

	if (gtk_tree_selection_get_selected (selection, &model, &iter))
	{
		gtk_tree_model_get (model, &iter,
				    ICONS_NAME_COLUMN, &name,
				    -1);
		if (name) 
			entry_set_name (dialog, name);	

		g_free (name);
	}
	else
	{
		/* entry_set_name (dialog, ""); */
	}
	
	/* we emit "selection-changed" for chooser in insert_text_handler()
	 * to avoid emitting the signal twice */
}

static void
contexts_row_activated_cb (GtkTreeView                  *view,
			   GtkTreePath                  *cpath,
			   GtkTreeViewColumn            *column,
			   GladeNamedIconChooserDialog *dialog)
{
	GtkTreeIter iter;
	GtkTreePath *path;
	
	if (gtk_tree_model_get_iter_first (dialog->priv->filter_model, &iter))
	{
	
		gtk_tree_selection_select_iter (dialog->priv->selection, &iter);
		
		path = gtk_tree_model_get_path (dialog->priv->filter_model, &iter);	
			
		gtk_tree_selection_select_path (dialog->priv->selection, path);
		
		gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (dialog->priv->icons_view), -1, 0);

		gtk_tree_path_free (path);

	}
	gtk_widget_grab_focus (dialog->priv->icons_view);
}

static void
contexts_selection_changed_cb (GtkTreeSelection *selection,
			       GladeNamedIconChooserDialog *dialog)
{
	GtkTreeIter iter;
	GtkTreeModel *model;
	gboolean retval;
	gint context_id;

	retval = gtk_tree_selection_get_selected (selection, &model, &iter);

	if (retval)
	{	
	
		gtk_tree_model_get (model, &iter,
				    CONTEXTS_ID_COLUMN, &context_id,
				    -1);
		
		dialog->priv->context_id = context_id;			
			
		if (!dialog->priv->filter_model)
			return;
		
		filter_icons_model (dialog);
	}
	
	entry_set_name (dialog, "");

}

static gboolean
row_separator_func (GtkTreeModel *model,
		    GtkTreeIter  *iter,
		    gpointer      unused)
{
	gboolean retval;
	gchar *name, *title;

	gtk_tree_model_get (model, iter,
			    CONTEXTS_NAME_COLUMN, &name,
			    CONTEXTS_TITLE_COLUMN, &title,
			    -1);

	retval = !name && !title;

	g_free (name);
	g_free (title);
	
	return retval;
}

static GtkWidget*
create_contexts_view (GladeNamedIconChooserDialog *dialog)
{
	GtkTreeView *view;
	GtkTreeViewColumn *column;
	GtkTreePath *path;
	
	dialog->priv->contexts_store = populate_icon_contexts_model ();
	
	view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (GTK_TREE_MODEL (dialog->priv->contexts_store)));
	
	column = gtk_tree_view_column_new_with_attributes (NULL,
						           gtk_cell_renderer_text_new (), 
						           "text",   CONTEXTS_TITLE_COLUMN,
						           NULL);
						           
	gtk_tree_view_append_column (view, column);
	gtk_tree_view_set_headers_visible (view, FALSE);
	
	gtk_tree_view_set_row_separator_func (view,
					      (GtkTreeViewRowSeparatorFunc) row_separator_func,
					      NULL,
					      NULL);

	gtk_tree_selection_set_mode (gtk_tree_view_get_selection (view), GTK_SELECTION_BROWSE);
	
	path = gtk_tree_path_new_from_indices (0, -1);
	gtk_tree_selection_select_path (gtk_tree_view_get_selection (view), path);
	gtk_tree_path_free (path);	
	
	g_signal_connect (view, "row-activated",
			  G_CALLBACK (contexts_row_activated_cb),
			  dialog);		
	
	g_signal_connect (gtk_tree_view_get_selection (view), "changed",
			  G_CALLBACK (contexts_selection_changed_cb),
			  dialog);
	
	gtk_widget_show (GTK_WIDGET (view));
	
	return GTK_WIDGET (view);
}

/* filters the icons model based on the current state */
static void
filter_icons_model (GladeNamedIconChooserDialog *dialog)
{

	set_busy_cursor (dialog, TRUE);
	
	g_object_ref (dialog->priv->filter_model);
	gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->priv->icons_view), NULL);
	gtk_entry_completion_set_model (dialog->priv->entry_completion, NULL);
	
	gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (dialog->priv->filter_model));

	gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->priv->icons_view), dialog->priv->filter_model);
	gtk_entry_completion_set_model (dialog->priv->entry_completion, GTK_TREE_MODEL (dialog->priv->icons_store));
	gtk_entry_completion_set_text_column (dialog->priv->entry_completion, ICONS_NAME_COLUMN);
	g_object_unref (dialog->priv->filter_model);

	set_busy_cursor (dialog, FALSE);
}

static gboolean
filter_visible_func (GtkTreeModel  *model,
                     GtkTreeIter   *iter,
                     GladeNamedIconChooserDialog *dialog)
{
	gboolean standard;
	gint     context_id;

	gtk_tree_model_get (model, iter,
                            ICONS_CONTEXT_COLUMN,  &context_id,
			    ICONS_STANDARD_COLUMN, &standard,
                            -1);  

	if (dialog->priv->context_id == -1)
		return (dialog->priv->settings_list_standard) ? TRUE && standard : TRUE;

	if (context_id == dialog->priv->context_id)
		return (dialog->priv->settings_list_standard) ? TRUE && standard : TRUE;
	else
		return FALSE;
}


static gboolean
search_equal_func (GtkTreeModel *model,
                   gint column,
                   const gchar *key,
                   GtkTreeIter *iter,
                   GladeNamedIconChooserDialog *dialog)
{
	gchar *name; 
	gboolean retval;

	gtk_tree_model_get (model, iter, 
                            ICONS_NAME_COLUMN, &name,
                            -1);
        
        retval = ! g_str_has_prefix (name, key);           

	g_free (name);	
	
        return retval;

}

static gboolean
scan_for_context_func (GtkTreeModel *model,
                       GtkTreePath  *path,
                       GtkTreeIter  *iter,
                       gpointer      user_data)
{
	ForEachFuncData   *data = (ForEachFuncData *) user_data;
	GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->dialog->priv->contexts_view));
	gchar *name = NULL;

	gtk_tree_model_get (model, iter, CONTEXTS_NAME_COLUMN, &name, -1);	    
	if (!name)
		return FALSE;
			    
	if (strcmp (name, data->name) == 0)
	{
	
		data->found = TRUE;
	
		if (data->do_activate)
			gtk_tree_view_row_activated (GTK_TREE_VIEW (data->dialog->priv->contexts_view),
						     path,
						     gtk_tree_view_get_column 
						     	(GTK_TREE_VIEW (data->dialog->priv->contexts_view), 0));		

		if (data->do_select)
			gtk_tree_selection_select_path (selection, path);
		else
			gtk_tree_selection_unselect_path (selection, path);
			
		if (data->do_cursor)
			gtk_tree_view_set_cursor (GTK_TREE_VIEW (data->dialog->priv->contexts_view),
						  path, NULL, FALSE);

		g_free (name);
		
		return TRUE;
	}

	g_free (name);

	return FALSE;
}

gboolean
scan_for_name_func (GtkTreeModel *model,
                    GtkTreePath  *path,
                    GtkTreeIter  *iter,
                    gpointer      user_data)
{
	ForEachFuncData *data = (ForEachFuncData *) user_data;	
	gchar *name = NULL;

	gtk_tree_model_get (model, iter, ICONS_NAME_COLUMN, &name, -1);	    
	if (!name)
		return FALSE;
			    
	if (strcmp (name, data->name) == 0)
	{
	
		data->found = TRUE;
	
		if (data->do_activate)
			gtk_tree_view_row_activated (GTK_TREE_VIEW (data->dialog->priv->icons_view),
						     path,
						     gtk_tree_view_get_column 
						     	(GTK_TREE_VIEW (data->dialog->priv->icons_view), 0));		

		if (data->do_select)
			gtk_tree_selection_select_path (data->dialog->priv->selection, path);
		else
			gtk_tree_selection_unselect_path (data->dialog->priv->selection, path);
			
		if (data->do_cursor)
			gtk_tree_view_set_cursor (GTK_TREE_VIEW (data->dialog->priv->icons_view),
						  path, NULL, FALSE);
				
		g_free (name);
		
		return TRUE;
	}

	g_free (name);

	return FALSE;
}

static void
centre_selected_row (GladeNamedIconChooserDialog *dialog)
{
	GList *l;
	
	g_assert (dialog->priv->icons_store != NULL);	
	g_assert (dialog->priv->selection != NULL);
	
	l = gtk_tree_selection_get_selected_rows (dialog->priv->selection, NULL);
	
	if (l) {
		g_assert (gtk_widget_get_mapped (GTK_WIDGET (dialog)));
		g_assert (gtk_widget_get_visible (GTK_WIDGET (dialog)));
		
		gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (dialog->priv->icons_view),
					      (GtkTreePath *) l->data,
					      NULL,
					      TRUE,
					      0.5, 0.0);
		
/*		gtk_tree_view_set_cursor (GTK_TREE_VIEW (dialog->priv->icons_view),
					 (GtkTreePath *) l->data,
					 0,
					 FALSE);
					 
		gtk_widget_grab_focus (dialog->priv->icons_view);		
*/					      
		g_list_foreach (l, (GFunc) gtk_tree_path_free, NULL);
		g_list_free (l);
	}
}

static void
select_first_row (GladeNamedIconChooserDialog *dialog)
{
	GtkTreePath *path;

	if (!dialog->priv->filter_model)
		return;

	path = gtk_tree_path_new_from_indices (0, -1);
	gtk_tree_view_set_cursor (GTK_TREE_VIEW (dialog->priv->icons_view), path, NULL, FALSE);
	gtk_tree_path_free (path);
}

static void
pending_select_name_process (GladeNamedIconChooserDialog *dialog)
{
	ForEachFuncData *data;

	g_assert (dialog->priv->icons_store != NULL);	
	g_assert (dialog->priv->selection != NULL);

	if (dialog->priv->pending_select_name)
	{
		
		data = g_slice_new0 (ForEachFuncData);
		
		data->name = dialog->priv->pending_select_name;
		data->do_select = TRUE;
		data->do_activate = FALSE;
		data->dialog = dialog;
		
		gtk_tree_model_foreach (dialog->priv->filter_model,
					scan_for_name_func,
					data);
		
		g_free (dialog->priv->pending_select_name);
		dialog->priv->pending_select_name = NULL;
		
		g_slice_free (ForEachFuncData, data);
	
	}
	else
	{
		if (strlen (gtk_entry_get_text (GTK_ENTRY (dialog->priv->entry))) == 0)
		{
			select_first_row (dialog);
		}		
	}
	
	centre_selected_row (dialog);
}

static gboolean
is_standard_icon_name (const gchar *icon_name)
{
	GQuark quark;
	
	quark = g_quark_try_string (icon_name);
	
	if (quark == 0)
		return FALSE;

	return (g_hash_table_lookup (standard_icon_quarks, GUINT_TO_POINTER (quark)) != NULL);

}

static void
cleanup_after_load (GladeNamedIconChooserDialog *dialog)
{
	GDK_THREADS_ENTER ();

	dialog->priv->load_id = 0;
	
	pending_select_name_process (dialog);

	set_busy_cursor (dialog, FALSE);
	
	GDK_THREADS_LEAVE ();
}

static void
chooser_set_model (GladeNamedIconChooserDialog *dialog)
{

	/* filter model */
	dialog->priv->filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->icons_store), NULL);	
	
	gtk_tree_model_filter_set_visible_func  (GTK_TREE_MODEL_FILTER (dialog->priv->filter_model),
						(GtkTreeModelFilterVisibleFunc) filter_visible_func,
						 dialog,
						 NULL);
	
	gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->priv->icons_view), dialog->priv->filter_model);
	g_object_unref (dialog->priv->filter_model);
	
	gtk_entry_completion_set_model (dialog->priv->entry_completion, GTK_TREE_MODEL (dialog->priv->icons_store));
	gtk_entry_completion_set_text_column (dialog->priv->entry_completion, ICONS_NAME_COLUMN);
	
	gtk_tree_view_set_search_column	(GTK_TREE_VIEW (dialog->priv->icons_view), ICONS_NAME_COLUMN);
		 
	dialog->priv->icons_loaded = TRUE;   
}

typedef struct
{
	gchar *name;
	gint  context;
} IconData;

static gint
icon_data_compare (IconData *a, IconData *b)
{
	return g_ascii_strcasecmp (a->name, b->name);
}

static gboolean
reload_icons (GladeNamedIconChooserDialog *dialog)
{
	GtkListStore *store = dialog->priv->icons_store;
	GtkTreeIter   iter;
	guint         i;
	GList        *l, *icons = NULL;

	GDK_THREADS_ENTER ();

	/* retrieve icon names from each context */
	for (i = 0; i < G_N_ELEMENTS (standard_contexts); i++) {

		GList *icons_in_context = gtk_icon_theme_list_icons (dialog->priv->icon_theme,
								     standard_contexts[i].name);

		for (l = icons_in_context; l; l = l->next) {

			IconData *data = g_slice_new (IconData);

			data->name = (gchar *) l->data;
			data->context = i;

			icons = g_list_prepend (icons, data);
		}
		
		g_list_free (icons_in_context);
	} 
	
	/* sort icon names */
	icons = g_list_sort (icons, (GCompareFunc) icon_data_compare);

	/* put into to model */
	for (l = icons; l; l = l->next) {
	
		IconData *data = (IconData *) l->data;
		
		gtk_list_store_append (store, &iter);
		gtk_list_store_set (store, &iter,
				    ICONS_CONTEXT_COLUMN,  data->context,
				    ICONS_STANDARD_COLUMN, is_standard_icon_name (data->name), 
				    ICONS_NAME_COLUMN,     data->name,
			    	    -1);
		
		g_free (data->name);
		g_slice_free (IconData, data);
	}	
	
	g_list_free (icons);
	
	chooser_set_model (dialog);	
	
	GDK_THREADS_LEAVE ();	
	
	return FALSE;
}

static void
change_icon_theme (GladeNamedIconChooserDialog *dialog)
{
	if (dialog->priv->icon_theme == NULL)
		dialog->priv->icon_theme = get_icon_theme_for_widget (GTK_WIDGET (dialog));

	gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->priv->icons_view), NULL);
	gtk_list_store_clear (dialog->priv->icons_store);

	set_busy_cursor (dialog, TRUE);

	dialog->priv->load_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 300,
						 (GSourceFunc) reload_icons,
						 dialog,
						 (GDestroyNotify) cleanup_after_load);

}

static void
glade_named_icon_chooser_dialog_screen_changed (GtkWidget *widget,
					   	GdkScreen *previous_screen)
{
	GladeNamedIconChooserDialog *dialog;
	
	dialog = GLADE_NAMED_ICON_CHOOSER_DIALOG (widget);

	if (GTK_WIDGET_CLASS (glade_named_icon_chooser_dialog_parent_class)->screen_changed)
		GTK_WIDGET_CLASS (glade_named_icon_chooser_dialog_parent_class)->screen_changed (widget, previous_screen);

	if (gtk_widget_get_mapped (widget))
		change_icon_theme (dialog);

}

static GtkWidget*
create_icons_view (GladeNamedIconChooserDialog *dialog)
{
	GtkTreeView *view;
	GtkTreeViewColumn *column;
	GtkCellRenderer *pixbuf_renderer, *text_renderer;
	
	view = GTK_TREE_VIEW (gtk_tree_view_new ());
	
	column = gtk_tree_view_column_new ();
	gtk_tree_view_column_set_min_width (column, 56);	
	gtk_tree_view_column_set_title (column, NULL);
	pixbuf_renderer = gtk_cell_renderer_pixbuf_new ();	

	gtk_tree_view_column_pack_start (column,
					 pixbuf_renderer,
					 TRUE);
					 
	gtk_tree_view_column_set_attributes (column, 
					     pixbuf_renderer,
					     "icon-name", ICONS_NAME_COLUMN,
					     NULL);

	gtk_tree_view_append_column (view, column);	
	g_object_set (pixbuf_renderer,
		      "xpad", 2,
		      "xalign", 1.0,
		      "stock-size",GTK_ICON_SIZE_MENU,
		      NULL);
	
	column = gtk_tree_view_column_new ();
	gtk_tree_view_column_set_title (column, "Name");
	text_renderer = gtk_cell_renderer_text_new ();
	g_object_set (G_OBJECT (text_renderer),
		      "ellipsize", PANGO_ELLIPSIZE_END, 
		      "yalign", 0.0,
		      NULL);
	
	gtk_tree_view_column_pack_start (column,
					 text_renderer,
					 TRUE);

	gtk_tree_view_column_set_attributes (column, 
					     text_renderer,
					     "text", ICONS_NAME_COLUMN,
					     NULL);	
						   
						           
	gtk_tree_view_append_column (view, column);
	gtk_tree_view_column_set_expand (column, TRUE);
	gtk_tree_view_column_set_resizable (column, FALSE);
	
	gtk_tree_view_set_headers_visible (view, FALSE);
	
	gtk_tree_view_set_enable_search (view, TRUE);
	gtk_tree_view_set_search_equal_func (view,
					     (GtkTreeViewSearchEqualFunc) search_equal_func,
					     dialog,
					     NULL);
	
	g_signal_connect (view, "row-activated",
			  G_CALLBACK (icons_row_activated_cb),
			  dialog);	
	
	g_signal_connect (gtk_tree_view_get_selection (view), "changed",
			  G_CALLBACK (icons_selection_changed_cb),
			  dialog);
			  
	gtk_tree_selection_set_mode (gtk_tree_view_get_selection (view), GTK_SELECTION_BROWSE);
	
	dialog->priv->selection = gtk_tree_view_get_selection (view);
	
	gtk_tree_view_set_rules_hint (view, TRUE);	
	
	gtk_widget_show (GTK_WIDGET (view));
	
	return GTK_WIDGET (view);
}

/* sets the 'list-standard' state and refilters the icons model */
static void
button_toggled (GtkToggleButton *button, GladeNamedIconChooserDialog *dialog)
{
	dialog->priv->settings_list_standard = gtk_toggle_button_get_active (button);

	if (dialog->priv->filter_model != NULL)
		filter_icons_model (dialog);
}

static GHashTable*
create_standard_icon_quarks ()
{
	GHashTable *table;
	GQuark quark;
	guint i;

	table = g_hash_table_new (NULL, NULL);
	
	for (i = 0; i < G_N_ELEMENTS (standard_icon_names); i++)
	{
	
		quark = g_quark_from_static_string (standard_icon_names[i]);
	
		g_hash_table_insert (table,
				     GUINT_TO_POINTER (quark),
				     GUINT_TO_POINTER (quark));
	}
	
	return table;
}

static void
glade_named_icon_chooser_dialog_style_set (GtkWidget *widget,
			                   GtkStyle  *previous_style)
{
	if (gtk_widget_has_screen (widget) && gtk_widget_get_mapped (widget))
		change_icon_theme (GLADE_NAMED_ICON_CHOOSER_DIALOG (widget));
}

/* override GtkWidget::show_all since we have internal widgets we wish to keep
 * hidden unless we decide otherwise, like the list-standard-icons-only checkbox.
 */
static void
glade_named_icon_chooser_dialog_show_all (GtkWidget *widget)
{
	gtk_widget_show (widget);
}

/* Handler for GtkWindow::set-focus; this is where we save the last-focused
 * widget on our toplevel.  See glade_named_icon_chooser_dialog_hierarchy_changed()
 */
static void
glade_named_icon_chooser_dialog_set_focus (GtkWindow                    *window,
					   GtkWidget                    *focus)
{
	
	GTK_WINDOW_CLASS (glade_named_icon_chooser_dialog_parent_class)->set_focus (window, focus);

	GLADE_NAMED_ICON_CHOOSER_DIALOG (window)->priv->last_focus_widget = gtk_window_get_focus (window);
}

static void
glade_named_icon_chooser_dialog_finalize (GObject *object)
{
	GladeNamedIconChooserDialog *dialog = GLADE_NAMED_ICON_CHOOSER_DIALOG (object);
	
	if (dialog->priv->pending_select_name)
	{
		g_free (dialog->priv->pending_select_name);
		dialog->priv->pending_select_name = NULL;
	}
	
	G_OBJECT_CLASS (glade_named_icon_chooser_dialog_parent_class)->finalize (object);
}

static void
glade_named_icon_chooser_dialog_map (GtkWidget *widget)
{
	GladeNamedIconChooserDialog *dialog = GLADE_NAMED_ICON_CHOOSER_DIALOG (widget);

	GTK_WIDGET_CLASS (glade_named_icon_chooser_dialog_parent_class)->map (widget);
	
	settings_load (dialog);
	
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->button),
				      dialog->priv->settings_list_standard);

	gtk_widget_grab_focus (dialog->priv->icons_view);
}

static void
glade_named_icon_chooser_dialog_unmap (GtkWidget *widget)
{
	GladeNamedIconChooserDialog *dialog = GLADE_NAMED_ICON_CHOOSER_DIALOG (widget);

	settings_save (dialog);

	GTK_WIDGET_CLASS (glade_named_icon_chooser_dialog_parent_class)->unmap (widget);
}

/* we load the icons in expose() because we want the widget
 * to be fully painted before loading begins
 */ 
static gboolean
glade_named_icon_chooser_dialog_expose_event (GtkWidget      *widget,
					      GdkEventExpose *event)
{
	GladeNamedIconChooserDialog *dialog = GLADE_NAMED_ICON_CHOOSER_DIALOG (widget);
	gboolean retval;

	retval = GTK_WIDGET_CLASS (glade_named_icon_chooser_dialog_parent_class)->expose_event (widget,
												event);
	if (!dialog->priv->icons_loaded)
	{
		change_icon_theme (GLADE_NAMED_ICON_CHOOSER_DIALOG (widget));
		dialog->priv->icons_loaded = TRUE;
	}

	return retval;
}

static void
response_cb (GtkDialog *dialog,
	     gint       response_id)
{
	GladeNamedIconChooserDialogPrivate *priv;

	priv = GLADE_NAMED_ICON_CHOOSER_DIALOG_GET_PRIVATE (dialog);

	/* Act only on response IDs we recognize */
	if (!(response_id == GTK_RESPONSE_ACCEPT
	    || response_id == GTK_RESPONSE_OK
	    || response_id == GTK_RESPONSE_YES
	    || response_id == GTK_RESPONSE_APPLY))
		return;
		
	if (!should_respond (GLADE_NAMED_ICON_CHOOSER_DIALOG (dialog)))
	{
		g_signal_stop_emission_by_name (dialog, "response");
	}
}

/* we intercept the GladeNamedIconChooser::icon-activated signal and try to
 * make the dialog emit a valid response signal
 */
static void
icon_activated_cb (GladeNamedIconChooserDialog *dialog)
{
	GList *children, *l;

	children = gtk_container_get_children (GTK_CONTAINER (gtk_dialog_get_action_area (GTK_DIALOG (dialog))));

	for (l = children; l; l = l->next)
	{
		GtkWidget *widget;
		gint response_id;

		widget = GTK_WIDGET (l->data);
		response_id = gtk_dialog_get_response_for_widget (GTK_DIALOG (dialog), widget);

		if (response_id == GTK_RESPONSE_ACCEPT ||
		    response_id == GTK_RESPONSE_OK     ||
		    response_id == GTK_RESPONSE_YES    ||
		    response_id == GTK_RESPONSE_APPLY)
		{
		        g_list_free (children);
		        
		        gtk_dialog_response (GTK_DIALOG (dialog), response_id);
		        
		        return;
		}
	}
	g_list_free (children);
}

/* we intercept the GladeNamedIconChooser::selection-changed signal and try to
 * make the affirmative response button insensitive when the selection is empty 
 */
static void
selection_changed_cb (GladeNamedIconChooserDialog *dialog)
{
	GList *children, *l;
	gchar *icon_name;

	children = gtk_container_get_children (GTK_CONTAINER (gtk_dialog_get_action_area (GTK_DIALOG (dialog))));

	for (l = children; l; l = l->next)
	{
		GtkWidget *widget;
		gint response_id;

		widget = GTK_WIDGET (l->data);
		response_id = gtk_dialog_get_response_for_widget (GTK_DIALOG (dialog), widget);

		if (response_id == GTK_RESPONSE_ACCEPT ||
		    response_id == GTK_RESPONSE_OK     ||
		    response_id == GTK_RESPONSE_YES    ||
		    response_id == GTK_RESPONSE_APPLY)
		{
			icon_name = glade_named_icon_chooser_dialog_get_icon_name (dialog);
			
		        gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
		        				   response_id,
		        				   icon_name != NULL);
		        g_free (icon_name);
		        g_list_free (children);	 
		        return;
		}
	}
	g_list_free (children);
}

static void
glade_named_icon_chooser_dialog_init (GladeNamedIconChooserDialog *dialog)
{
	GtkWidget *contents;
	GtkWidget *hbox;
	GtkWidget *vbox;
	GtkWidget *sw;
	GtkWidget *label;
	GtkWidget *hpaned;
	GtkWidget *content_area;
	GtkWidget *action_area;
	GtkSizeGroup *group;
	
	dialog->priv = GLADE_NAMED_ICON_CHOOSER_DIALOG_GET_PRIVATE (dialog);
	
	dialog->priv->filter_model = NULL;
	dialog->priv->icons_store = NULL;
	dialog->priv->context_id = -1;
	dialog->priv->pending_select_name = NULL;
	dialog->priv->last_focus_widget = NULL;
	dialog->priv->icons_loaded = FALSE;
	

	gtk_window_set_title (GTK_WINDOW (dialog), _("Named Icon Chooser"));
	
	gtk_window_set_default_size (GTK_WINDOW (dialog), 610, 480);
#if !GTK_CHECK_VERSION (2, 21, 8)
	gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);	
#endif
	content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
	gtk_container_set_border_width (GTK_CONTAINER (content_area), 12);
	gtk_box_set_spacing (GTK_BOX (content_area), 12);
	action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
	gtk_container_set_border_width (GTK_CONTAINER (action_area), 0);
	gtk_box_set_spacing (GTK_BOX (action_area), 6);

	/* 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);
			 
	g_signal_connect (dialog, "icon-activated",
			  G_CALLBACK (icon_activated_cb),
			  NULL);
			  
	g_signal_connect (dialog, "selection-changed",
			  G_CALLBACK (selection_changed_cb),
			  NULL);

	
	if (standard_icon_quarks == NULL)
		standard_icon_quarks = create_standard_icon_quarks ();
	
	
	gtk_widget_push_composite_child ();
	
	contents = gtk_vbox_new (FALSE, 6);
	gtk_widget_show (contents);	
	
	label = gtk_label_new_with_mnemonic (_("Icon _Name:"));
	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
	gtk_widget_show (label);
	
	dialog->priv->entry = gtk_entry_new ();
	gtk_entry_set_activates_default (GTK_ENTRY (dialog->priv->entry), TRUE);
	gtk_entry_set_width_chars (GTK_ENTRY (dialog->priv->entry), 40);
	g_object_set (G_OBJECT (dialog->priv->entry), "truncate-multiline", TRUE, NULL);
	g_signal_connect (G_OBJECT (dialog->priv->entry), "changed",
			  G_CALLBACK (changed_text_handler), dialog);
	g_signal_connect (G_OBJECT (dialog->priv->entry), "insert-text",
			  G_CALLBACK (insert_text_handler), dialog);
	gtk_widget_show (dialog->priv->entry);

	dialog->priv->entry_completion = gtk_entry_completion_new ();
	gtk_entry_set_completion (GTK_ENTRY (dialog->priv->entry), dialog->priv->entry_completion);
	gtk_entry_completion_set_popup_completion (dialog->priv->entry_completion, FALSE);
	gtk_entry_completion_set_inline_completion (dialog->priv->entry_completion, TRUE);
	
	gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->priv->entry);
	
	hbox = gtk_hbox_new (FALSE, 12);
	gtk_widget_show (hbox);
	
	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);	
	gtk_box_pack_start (GTK_BOX (hbox), dialog->priv->entry, TRUE, TRUE, 0);	
	gtk_box_pack_start (GTK_BOX (contents), hbox, FALSE, FALSE, 6);

	hpaned = gtk_hpaned_new ();
	gtk_paned_set_position (GTK_PANED (hpaned), 150);
	gtk_widget_show (hpaned);	

	dialog->priv->contexts_view = create_contexts_view (dialog);
	dialog->priv->icons_view  = create_icons_view (dialog);
	
	vbox = gtk_vbox_new (FALSE, 6);
	gtk_widget_show (vbox);

	group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);

	label = gtk_label_new_with_mnemonic (_("C_ontexts:"));
	gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->priv->contexts_view);
	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
	gtk_size_group_add_widget (group, label);
	gtk_widget_show (label);	
	
	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);		
	
	sw = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); 
	gtk_widget_show (sw);

	gtk_container_add (GTK_CONTAINER (sw), dialog->priv->contexts_view);
	gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);	
	gtk_paned_pack1 (GTK_PANED (hpaned), vbox, FALSE, FALSE);
	
	
	vbox = gtk_vbox_new (FALSE, 6);
	gtk_widget_show (vbox);	
	
	label = gtk_label_new_with_mnemonic (_("Icon Na_mes:"));
	gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->priv->icons_view);
	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
	gtk_size_group_add_widget (group, label);	
	gtk_widget_show (label);
	
	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
	
	sw = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); 
	gtk_widget_show (sw);
	
	gtk_container_add (GTK_CONTAINER (sw), dialog->priv->icons_view);
	gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);	
	gtk_paned_pack2 (GTK_PANED (hpaned), vbox, TRUE, FALSE);
	
	gtk_box_pack_start (GTK_BOX (contents), hpaned, TRUE, TRUE, 0);	
	
	
	g_object_unref (G_OBJECT (group));
	
	dialog->priv->button = gtk_check_button_new_with_mnemonic (_("_List standard icons only"));
	gtk_widget_show (dialog->priv->button);
	
	g_signal_connect (dialog->priv->button, "toggled",
			  G_CALLBACK (button_toggled),
			  dialog);
	
	gtk_box_pack_start (GTK_BOX (contents), dialog->priv->button, FALSE, FALSE, 0);
	gtk_box_pack_start (GTK_BOX (content_area), contents, TRUE, TRUE, 0);
					
	gtk_widget_pop_composite_child ();
	
	/* underlying model */
  	dialog->priv->icons_store = gtk_list_store_new (ICONS_N_COLUMNS,
  		                                        G_TYPE_UINT,
  		                                        G_TYPE_BOOLEAN, 
  		                                        G_TYPE_STRING);
  	                                        
                                       
}

static void
glade_named_icon_chooser_dialog_class_init (GladeNamedIconChooserDialogClass *klass)
{
	GObjectClass   *object_class;
	GtkWidgetClass *widget_class;	
	GtkWindowClass *window_class;

	object_class = G_OBJECT_CLASS (klass);
	widget_class = GTK_WIDGET_CLASS (klass);
	window_class = GTK_WINDOW_CLASS (klass);
	
	object_class->finalize           = glade_named_icon_chooser_dialog_finalize;
	
	widget_class->map                = glade_named_icon_chooser_dialog_map;
	widget_class->unmap              = glade_named_icon_chooser_dialog_unmap;
	widget_class->expose_event       = glade_named_icon_chooser_dialog_expose_event;
	widget_class->show_all           = glade_named_icon_chooser_dialog_show_all;
	widget_class->style_set          = glade_named_icon_chooser_dialog_style_set;
	widget_class->screen_changed     = glade_named_icon_chooser_dialog_screen_changed;
	
	window_class->set_focus          = glade_named_icon_chooser_dialog_set_focus;

	/**
	 * GladeNamedIconChooserDialog::icon-activated
	 * @chooser: the object which received the signal
	 *
	 * This signal is emitted when the user "activates" an icon
	 * in the named icon chooser.  This can happen by double-clicking on an item
	 * in the recently used resources list, or by pressing
	 * <keycap>Enter</keycap>.
	*/
	dialog_signals[ICON_ACTIVATED] =
		g_signal_new ("icon-activated",
			      G_TYPE_FROM_CLASS (object_class),
			      G_SIGNAL_RUN_LAST,
			      G_STRUCT_OFFSET (GladeNamedIconChooserDialogClass, icon_activated),
			      NULL, NULL,
			      g_cclosure_marshal_VOID__VOID,
			      G_TYPE_NONE, 0);

	/**
	 * GladeNamedIconChooserDialog::selection-changed
	 * @chooser: the object which received the signal
	 *
	 * This signal is emitted when there is a change in the set of
	 * selected icon names.  This can happen when a user
	 * modifies the selection with the mouse or the keyboard, or when
	 * explicitely calling functions to change the selection.
	*/
	dialog_signals[SELECTION_CHANGED] =
		g_signal_new ("selection-changed",
		              G_TYPE_FROM_CLASS (object_class),
		              G_SIGNAL_RUN_LAST,
		              G_STRUCT_OFFSET (GladeNamedIconChooserDialogClass, selection_changed),
		              NULL, NULL,
		              g_cclosure_marshal_VOID__VOID,
		              G_TYPE_NONE, 0);	
	
	
	g_type_class_add_private (klass, sizeof (GladeNamedIconChooserDialogPrivate));
}

static gboolean
should_respond (GladeNamedIconChooserDialog *dialog)
{
	gchar *icon_name;
	
	/* is there an icon selected? */
	icon_name = glade_named_icon_chooser_dialog_get_icon_name (dialog);
	if (!icon_name)
		return FALSE;
	
	g_free (icon_name);			
	return TRUE;
}

/* get's the name of the configuration file */
static gchar *
get_config_filename (void)
{
	return g_build_filename (g_get_user_config_dir (), "gladeui", "config", NULL);
}

/* get's the name of the directory that contains the config file */
static char *
get_config_dirname (void)
{
	return g_build_filename (g_get_user_config_dir (), "gladeui", NULL);
}

/* loads the configuration settings */
static void
settings_load (GladeNamedIconChooserDialog *dialog)
{
	GKeyFile *keyfile;
	gboolean  success, boolean_value;
	gchar    *filename;
	GError   *error = NULL;	

	keyfile = g_key_file_new ();

	filename = get_config_filename ();
	success = g_key_file_load_from_file (keyfile,
				 	     filename,
					     G_KEY_FILE_NONE,
					     &error);    
	g_free (filename);
					    
	if (!success)
	{
	
		dialog->priv->settings_list_standard = DEFAULT_SETTING_LIST_STANDARD_ONLY;
		
		g_clear_error (&error);
		g_key_file_free (keyfile);
		return;	
	}
	
	
	boolean_value = g_key_file_get_boolean (keyfile,
						"Named Icon Chooser",
						"ListStandardOnly",
						&error);
	if (error)
	{
		dialog->priv->settings_list_standard = DEFAULT_SETTING_LIST_STANDARD_ONLY;	
		g_clear_error (&error);
	}
	else
	{
		dialog->priv->settings_list_standard = boolean_value;	
	}
	
	g_key_file_free (keyfile);
}

/* creates a GKeyFile based on the current settings */
static GKeyFile *
settings_to_keyfile (GladeNamedIconChooserDialog *dialog)
{
	GKeyFile *keyfile;
	gboolean  retval;
	gchar    *filename;
	
	keyfile = g_key_file_new ();
		
	filename = get_config_filename ();
	retval = g_key_file_load_from_file (keyfile,
					    get_config_filename (),
					    G_KEY_FILE_NONE,
					    NULL);
	g_free (filename);

	g_key_file_set_boolean (keyfile,
				"Named Icon Chooser",
				"ListStandardOnly",
				dialog->priv->settings_list_standard);
	
	return keyfile;
}

/* serializes the the current configuration to the config file */
static void
settings_save  (GladeNamedIconChooserDialog *dialog)
{
	GKeyFile *keyfile;
	gchar    *contents;
	gsize     contents_length;
	gchar    *filename = NULL, *dirname = NULL;
	GError   *error = NULL;

	keyfile = settings_to_keyfile (dialog);

	contents = g_key_file_to_data (keyfile,
				       &contents_length,
				       &error);

	if (error)
		goto out;

	filename = get_config_filename ();

	if (!g_file_set_contents (filename, contents, contents_length, NULL))
	{
		gchar *dirname;
		gint saved_errno;

		dirname = get_config_dirname ();
		if (g_mkdir_with_parents (dirname, 0700) != 0) /* 0700 per the XDG basedir spec */
		{
		
	  		saved_errno = errno;
	  		g_set_error (&error,
				     G_FILE_ERROR,
				     g_file_error_from_errno (saved_errno),
				     _("Could not create directory: %s"),
				     dirname);
	  		goto out;
		}

		if (!g_file_set_contents (filename, contents, contents_length, &error))
		{
			goto out;
		}
	}

	out:

	g_free (contents);
	g_free (dirname);
	g_free (filename);
	g_clear_error (&error);
	g_key_file_free (keyfile);
}

static GtkWidget *
glade_named_icon_chooser_dialog_new_valist (const gchar      *title,
					    GtkWindow        *parent,
					    const gchar      *first_button_text,
					    va_list           varargs)
{
	GtkWidget *result;
	const char *button_text = first_button_text;
	gint response_id;

	result = g_object_new (GLADE_TYPE_NAMED_ICON_CHOOSER_DIALOG,
	                       "title", title,
	                       "transient-for", parent,
	                       NULL);

	while (button_text)
	{
		response_id = va_arg (varargs, gint);
		gtk_dialog_add_button (GTK_DIALOG (result), button_text, response_id);
		button_text = va_arg (varargs, const gchar *);
	}

	return result;
}

/**
 * glade_named_icon_chooser_dialog_new:
 * @title: Title of the dialog, or %NULL
 * @parent: Transient parent of the dialog, or %NULL,
 * @first_button_text: stock ID or text to go in the first button, or %NULL
 * @Varargs: response ID for the first button, then additional (button, id)
 *   pairs, ending with %NULL
 *
 * Creates a new #GladeNamedIconChooserDialog.  This function is analogous to
 * gtk_dialog_new_with_buttons().
 *
 * Return value: a new #GladeNamedIconChooserDialog
 */
GtkWidget *
glade_named_icon_chooser_dialog_new (const gchar      *title,
				     GtkWindow        *parent,
				     const gchar      *first_button_text,
				     ...)
{
	GtkWidget *result;
	va_list varargs;

	va_start (varargs, first_button_text);
	result = glade_named_icon_chooser_dialog_new_valist (title,
						             parent,
						             first_button_text,
						             varargs);
	va_end (varargs);

	return result;
}
