diff options
author | Tristan Van Berkom <tvb@src.gnome.org> | 2007-08-14 18:59:56 +0000 |
---|---|---|
committer | Tristan Van Berkom <tvb@src.gnome.org> | 2007-08-14 18:59:56 +0000 |
commit | b6ffd5f6a25418fb94ab457c304015a993272d26 (patch) | |
tree | dd5a85ee07abf2057960eab5514f7aa628b1f48f /gladeui/glade-named-icon-chooser-dialog.c | |
parent | d46816c4669f7e56d05709d470e9db4a66dee59a (diff) | |
download | glade-b6ffd5f6a25418fb94ab457c304015a993272d26.tar.gz |
Marked some properties to be "themed-icon"
* plugins/gtk+/gtk+.xml.in: Marked some properties to be "themed-icon"
* gladeui/glade-xml-utils.h, gladeui/glade-property-class.[ch],
gladeui/glade-editor-property.c: Added a tag to mark string properties
that are "themed-icon", use the themed icon chooser for those properties.
* gladeui/glade-name-icon-chooser.[ch], gladeui/icon-naming-spec.c: Added
themed icon chooser (bug 359640).
svn path=/trunk/; revision=1552
Diffstat (limited to 'gladeui/glade-named-icon-chooser-dialog.c')
-rw-r--r-- | gladeui/glade-named-icon-chooser-dialog.c | 1775 |
1 files changed, 1775 insertions, 0 deletions
diff --git a/gladeui/glade-named-icon-chooser-dialog.c b/gladeui/glade-named-icon-chooser-dialog.c new file mode 100644 index 00000000..6c375391 --- /dev/null +++ b/gladeui/glade-named-icon-chooser-dialog.c @@ -0,0 +1,1775 @@ +/* + * 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_REALIZED (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 (dialog)->window, 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_MAPPED (dialog)); + g_assert (GTK_WIDGET_VISIBLE (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_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_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 (dialog)->action_area)); + + 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 (dialog)->action_area)); + + 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; + 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); + + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + + gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), 12); + gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 12); + gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 0); + gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->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 (GTK_DIALOG (dialog)->vbox), 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; +} |