1 /*
2  * glade-named-icon-chooser-widget.c - Named icon chooser widget
3  *
4  * Copyright (C) 2007 Vincent Geddes
5  *
6  * Author:  Vincent Geddes <vgeddes@gnome.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANNAMED_ICON_CHOOSERILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  */
22 
23 #include <config.h>
24 
25 #include "glade-private.h"
26 #include "glade-named-icon-chooser-dialog.h"
27 #include "icon-naming-spec.c"
28 
29 #include <gtk/gtk.h>
30 #include <glib/gi18n-lib.h>
31 #include <string.h>
32 #include <errno.h>
33 
34 
35 #define DEFAULT_SETTING_LIST_STANDARD_ONLY   TRUE
36 
37 enum
38 {
39   CONTEXTS_ID_COLUMN,
40   CONTEXTS_NAME_COLUMN,
41   CONTEXTS_TITLE_COLUMN,
42 
43   CONTEXTS_N_COLUMS
44 };
45 
46 enum
47 {
48   ICONS_CONTEXT_COLUMN,
49   ICONS_STANDARD_COLUMN,
50   ICONS_NAME_COLUMN,
51 
52   ICONS_N_COLUMNS
53 };
54 
55 enum
56 {
57   GLADE_NAMED_ICON
58 };
59 
60 enum
61 {
62   ICON_ACTIVATED,
63   SELECTION_CHANGED,
64 
65   LAST_SIGNAL
66 };
67 
68 struct _GladeNamedIconChooserDialogPrivate
69 {
70   GtkWidget *icons_view;
71   GtkTreeModel *filter_model;   /* filtering model  */
72   GtkListStore *icons_store;    /* data store       */
73   GtkTreeSelection *selection;
74 
75   GtkWidget *contexts_view;
76   GtkListStore *contexts_store;
77 
78   GtkWidget *entry;
79   GtkEntryCompletion *entry_completion;
80 
81   GtkWidget *button;            /* list-standard-only checkbutton */
82 
83   gint context_id;              /* current icon name context for icon filtering */
84 
85   gchar *pending_select_name;   /* an icon name for a pending treeview selection.
86                                  * can only select name after model is loaded
87                                  * and the widget is mapped */
88 
89   GtkIconTheme *icon_theme;     /* the current icon theme */
90   guint load_id;                /* id of the idle function for loading data into model */
91 
92   gboolean settings_list_standard;      /* whether to list standard icon names only */
93 
94   GtkWidget *last_focus_widget;
95 
96   gboolean icons_loaded;        /* whether the icons have been loaded into the model */
97 };
98 
99 static GHashTable *standard_icon_quarks = NULL;
100 
101 static guint dialog_signals[LAST_SIGNAL] = { 0, };
102 
103 gchar *
104 glade_named_icon_chooser_dialog_get_icon_name (GladeNamedIconChooserDialog *dialog);
105 
106 void
107 glade_named_icon_chooser_dialog_set_icon_name (GladeNamedIconChooserDialog *dialog,
108                                                const gchar                 *icon_name);
109 
110 gboolean
111 glade_named_icon_chooser_dialog_set_context (GladeNamedIconChooserDialog *dialog,
112                                              const gchar                 *context);
113 
114 gchar *
115 glade_named_icon_chooser_dialog_get_context (GladeNamedIconChooserDialog *dialog);
116 
117 static gboolean should_respond (GladeNamedIconChooserDialog *dialog);
118 
119 static void filter_icons_model (GladeNamedIconChooserDialog *dialog);
120 
121 static gboolean scan_for_name_func (GtkTreeModel *model,
122                                     GtkTreePath  *path,
123                                     GtkTreeIter  *iter,
124                                     gpointer      data);
125 
126 static gboolean scan_for_context_func (GtkTreeModel *model,
127                                        GtkTreePath  *path,
128                                        GtkTreeIter  *iter,
129                                        gpointer      data);
130 
131 static void settings_load (GladeNamedIconChooserDialog *dialog);
132 
133 static void settings_save (GladeNamedIconChooserDialog *dialog);
134 
135 
136 G_DEFINE_TYPE_WITH_PRIVATE (GladeNamedIconChooserDialog,
137                             glade_named_icon_chooser_dialog,
138                             GTK_TYPE_DIALOG);
139 
140 
141 static void
entry_set_name(GladeNamedIconChooserDialog * dialog,const gchar * name)142 entry_set_name (GladeNamedIconChooserDialog *dialog, const gchar *name)
143 {
144   /* Must disable completion before setting text, in order to avoid
145    * spurious warnings (possible GTK+ bug).
146    */
147   gtk_entry_set_completion (GTK_ENTRY (dialog->priv->entry), NULL);
148 
149   gtk_entry_set_text (GTK_ENTRY (dialog->priv->entry), name);
150 
151   gtk_entry_set_completion (GTK_ENTRY (dialog->priv->entry),
152                             dialog->priv->entry_completion);
153 }
154 
155 static GtkIconTheme *
get_icon_theme_for_widget(GtkWidget * widget)156 get_icon_theme_for_widget (GtkWidget *widget)
157 {
158   if (gtk_widget_has_screen (widget))
159     return gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
160 
161   return gtk_icon_theme_get_default ();
162 }
163 
164 /* validates name according to the icon naming spec (en_US.US_ASCII [a-z1-9_-.])  */
165 static gboolean
is_well_formed(const gchar * name)166 is_well_formed (const gchar * name)
167 {
168   gchar *c = (gchar *) name;
169   for (; *c; c++)
170     {
171       if (g_ascii_isalnum (*c))
172         {
173           if (g_ascii_isalpha (*c) && !g_ascii_islower (*c))
174             return FALSE;
175         }
176       else if (*c != '_' && *c != '-' && *c != '.')
177         {
178           return FALSE;
179         }
180     }
181   return TRUE;
182 }
183 
184 static void
check_entry_text(GladeNamedIconChooserDialog * dialog,gchar ** name_ret,gboolean * is_wellformed_ret,gboolean * is_empty_ret)185 check_entry_text (GladeNamedIconChooserDialog *dialog,
186                   gchar                      **name_ret,
187                   gboolean                    *is_wellformed_ret,
188                   gboolean                    *is_empty_ret)
189 {
190   if (strlen (gtk_entry_get_text (GTK_ENTRY (dialog->priv->entry))) == 0)
191     {
192       *name_ret = NULL;
193       *is_wellformed_ret = TRUE;
194       *is_empty_ret = TRUE;
195 
196       return;
197     }
198 
199   *is_empty_ret = FALSE;
200 
201   *is_wellformed_ret =
202       is_well_formed (gtk_entry_get_text (GTK_ENTRY (dialog->priv->entry)));
203 
204   if (*is_wellformed_ret)
205     *name_ret = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->priv->entry)));
206   else
207     *name_ret = NULL;
208 }
209 
210 static void
changed_text_handler(GtkEditable * editable,GladeNamedIconChooserDialog * dialog)211 changed_text_handler (GtkEditable                 *editable,
212                       GladeNamedIconChooserDialog *dialog)
213 {
214   g_signal_emit_by_name (dialog, "selection-changed", NULL);
215 }
216 
217 /* ensure that only valid text can be inserted into entry */
218 static void
insert_text_handler(GtkEditable * editable,const gchar * text,gint length,gint * position,GladeNamedIconChooserDialog * dialog)219 insert_text_handler (GtkEditable                 *editable,
220                      const gchar                 *text,
221                      gint                         length,
222                      gint                        *position,
223                      GladeNamedIconChooserDialog *dialog)
224 {
225   if (is_well_formed (text))
226     {
227 
228       g_signal_handlers_block_by_func (editable, (gpointer) insert_text_handler,
229                                        dialog);
230 
231       gtk_editable_insert_text (editable, text, length, position);
232 
233       g_signal_handlers_unblock_by_func (editable,
234                                          (gpointer) insert_text_handler,
235                                          dialog);
236 
237     }
238   else
239     {
240       gdk_display_beep (gtk_widget_get_display (GTK_WIDGET (dialog)));
241     }
242 
243   g_signal_stop_emission_by_name (editable, "insert-text");
244 }
245 
246 typedef struct
247 {
248   gchar *name;                  /* the name of the icon or context */
249 
250   guint found:1;                /* whether an item matching `name' was found */
251   guint do_select:1;            /* select the matched row */
252   guint do_cursor:1;            /* put cursor at the matched row */
253   guint do_activate:1;          /* activate the matched row */
254 
255   GladeNamedIconChooserDialog *dialog;
256 } ForEachFuncData;
257 
258 void
glade_named_icon_chooser_dialog_set_icon_name(GladeNamedIconChooserDialog * dialog,const gchar * name)259 glade_named_icon_chooser_dialog_set_icon_name (GladeNamedIconChooserDialog *dialog,
260                                                const gchar                 *name)
261 {
262   ForEachFuncData *data;
263   gboolean located_in_theme;
264 
265   g_return_if_fail (GLADE_IS_NAMED_ICON_CHOOSER_DIALOG (dialog));
266   g_return_if_fail (gtk_widget_has_screen (GTK_WIDGET (dialog)));
267 
268   if (name == NULL)
269     {
270       gtk_tree_selection_unselect_all (dialog->priv->selection);
271       entry_set_name (dialog, "");
272       return;
273     }
274 
275   located_in_theme =
276       gtk_icon_theme_has_icon (get_icon_theme_for_widget (GTK_WIDGET (dialog)),
277                                name);
278 
279   if (located_in_theme)
280     {
281 
282       if (dialog->priv->icons_loaded && dialog->priv->filter_model)
283         {
284 
285           data = g_slice_new0 (ForEachFuncData);
286           data->name = g_strdup (name);
287           data->found = FALSE;
288           data->do_activate = FALSE;
289           data->do_select = TRUE;
290           data->do_cursor = TRUE;
291           data->dialog = dialog;
292 
293           gtk_tree_model_foreach (dialog->priv->filter_model,
294                                   scan_for_name_func, data);
295 
296           g_free (data->name);
297           g_slice_free (ForEachFuncData, data);
298 
299         }
300       else
301         {
302           dialog->priv->pending_select_name = g_strdup (name);
303         }
304 
305       /* selecting a treeview row will set the entry text,
306        * but we must have this here in case the row has been filtered out
307        */
308       entry_set_name (dialog, name);
309 
310     }
311   else if (is_well_formed (name))
312     {
313 
314       gtk_tree_selection_unselect_all (dialog->priv->selection);
315 
316       entry_set_name (dialog, name);
317     }
318   else
319     {
320       g_warning ("invalid icon name: '%s' is not well formed", name);
321     }
322 }
323 
324 gboolean
glade_named_icon_chooser_dialog_set_context(GladeNamedIconChooserDialog * dialog,const gchar * name)325 glade_named_icon_chooser_dialog_set_context (GladeNamedIconChooserDialog *dialog,
326                                              const gchar                 *name)
327 {
328   ForEachFuncData *data;
329 
330   g_return_val_if_fail (GLADE_IS_NAMED_ICON_CHOOSER_DIALOG (dialog), FALSE);
331 
332   data = g_slice_new0 (ForEachFuncData);
333 
334   if (name)
335     data->name = g_strdup (name);
336   else
337     data->name = g_strdup ("All Contexts");
338 
339   data->found = FALSE;
340   data->do_select = TRUE;
341   data->do_activate = FALSE;
342   data->do_cursor = FALSE;
343   data->dialog = dialog;
344 
345   gtk_tree_model_foreach (GTK_TREE_MODEL (dialog->priv->contexts_store),
346                           (GtkTreeModelForeachFunc) scan_for_context_func,
347                           data);
348 
349   g_free (data->name);
350   g_slice_free (ForEachFuncData, data);
351 
352   return TRUE;
353 }
354 
355 gchar *
glade_named_icon_chooser_dialog_get_context(GladeNamedIconChooserDialog * dialog)356 glade_named_icon_chooser_dialog_get_context (GladeNamedIconChooserDialog *dialog)
357 {
358   GtkTreeSelection *sel;
359   GtkTreeIter iter;
360   gchar *context_name;
361 
362   g_return_val_if_fail (GLADE_IS_NAMED_ICON_CHOOSER_DIALOG (dialog), NULL);
363 
364   sel =
365       gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->contexts_view));
366 
367   if (gtk_tree_selection_get_selected (sel, NULL, &iter))
368     {
369 
370       gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->contexts_store), &iter,
371                           CONTEXTS_NAME_COLUMN, &context_name, -1);
372 
373       /* if context_name is NULL, then it is the 'all categories' special context */
374       return context_name;
375 
376     }
377   else
378     {
379       return NULL;
380     }
381 }
382 
383 static gchar *
get_icon_name_from_selection(GladeNamedIconChooserDialog * dialog)384 get_icon_name_from_selection (GladeNamedIconChooserDialog *dialog)
385 {
386   GtkTreeIter iter;
387   GtkTreeModel *model;
388   gchar *name;
389 
390   if (!gtk_tree_selection_get_selected (dialog->priv->selection, &model, &iter))
391     return NULL;
392 
393   gtk_tree_model_get (model, &iter, ICONS_NAME_COLUMN, &name, -1);
394 
395   return name;
396 }
397 
398 gchar *
glade_named_icon_chooser_dialog_get_icon_name(GladeNamedIconChooserDialog * dialog)399 glade_named_icon_chooser_dialog_get_icon_name (GladeNamedIconChooserDialog *dialog)
400 {
401   GtkWidget *current_focus;
402   gchar *name;
403 
404   g_return_val_if_fail (GLADE_IS_NAMED_ICON_CHOOSER_DIALOG (dialog), NULL);
405 
406   current_focus = gtk_window_get_focus (GTK_WINDOW (dialog));
407 
408   if (current_focus == dialog->priv->icons_view)
409     {
410 
411     view:
412       name = get_icon_name_from_selection (dialog);
413 
414       if (name == NULL)
415         goto entry;
416 
417     }
418   else if (current_focus == dialog->priv->entry)
419     {
420       gboolean is_wellformed, is_empty;
421     entry:
422       check_entry_text (dialog, &name, &is_wellformed, &is_empty);
423 
424       if (!is_wellformed || is_empty)
425         return NULL;
426 
427     }
428   else if (dialog->priv->last_focus_widget == dialog->priv->icons_view)
429     {
430       goto view;
431     }
432   else if (dialog->priv->last_focus_widget == dialog->priv->entry)
433     {
434       goto entry;
435     }
436   else
437     {
438       goto view;
439     }
440 
441   return name;
442 }
443 
444 static void
set_busy_cursor(GladeNamedIconChooserDialog * dialog,gboolean busy)445 set_busy_cursor (GladeNamedIconChooserDialog *dialog, gboolean busy)
446 {
447   GdkDisplay *display;
448   GdkCursor *cursor;
449 
450   if (!gtk_widget_get_realized (GTK_WIDGET (dialog)))
451     return;
452 
453   display = gtk_widget_get_display (GTK_WIDGET (dialog));
454 
455   if (busy)
456     cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
457   else
458     cursor = NULL;
459 
460   gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (dialog)), cursor);
461   gdk_display_flush (display);
462 
463   if (cursor)
464     g_object_unref (cursor);
465 }
466 
467 static GtkListStore *
populate_icon_contexts_model(void)468 populate_icon_contexts_model (void)
469 {
470   GtkListStore *store;
471   GtkTreeIter iter;
472   guint i;
473 
474   store = gtk_list_store_new (CONTEXTS_N_COLUMS,
475                               G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
476 
477 
478   gtk_list_store_append (store, &iter);
479   gtk_list_store_set (store, &iter,
480                       CONTEXTS_ID_COLUMN, -1,
481                       CONTEXTS_NAME_COLUMN, "All Contexts",
482                       CONTEXTS_TITLE_COLUMN, _("All Contexts"), -1);
483 
484   gtk_list_store_append (store, &iter);
485   gtk_list_store_set (store, &iter,
486                       CONTEXTS_ID_COLUMN, -1,
487                       CONTEXTS_NAME_COLUMN, NULL,
488                       CONTEXTS_TITLE_COLUMN, NULL, -1);
489 
490   for (i = 0; i < G_N_ELEMENTS (standard_contexts); i++)
491     {
492 
493       gtk_list_store_append (store, &iter);
494       gtk_list_store_set (store, &iter,
495                           CONTEXTS_ID_COLUMN, i,
496                           CONTEXTS_NAME_COLUMN, standard_contexts[i].name,
497                           CONTEXTS_TITLE_COLUMN, _(standard_contexts[i].title),
498                           -1);
499     }
500 
501   return store;
502 }
503 
504 static void
icons_row_activated_cb(GtkTreeView * view,GtkTreePath * path,GtkTreeViewColumn * column,GladeNamedIconChooserDialog * dialog)505 icons_row_activated_cb (GtkTreeView                 *view,
506                         GtkTreePath                 *path,
507                         GtkTreeViewColumn           *column,
508                         GladeNamedIconChooserDialog *dialog)
509 {
510   g_signal_emit_by_name (dialog, "icon-activated", NULL);
511 }
512 
513 static void
icons_selection_changed_cb(GtkTreeSelection * selection,GladeNamedIconChooserDialog * dialog)514 icons_selection_changed_cb (GtkTreeSelection * selection,
515                             GladeNamedIconChooserDialog * dialog)
516 {
517   GtkTreeModel *model;
518   GtkTreeIter iter;
519   gchar *name;
520 
521   if (gtk_tree_selection_get_selected (selection, &model, &iter))
522     {
523       gtk_tree_model_get (model, &iter, ICONS_NAME_COLUMN, &name, -1);
524       if (name)
525         entry_set_name (dialog, name);
526 
527       g_free (name);
528     }
529   else
530     {
531       /* entry_set_name (dialog, ""); */
532     }
533 
534   /* we emit "selection-changed" for chooser in insert_text_handler()
535    * to avoid emitting the signal twice */
536 }
537 
538 static void
contexts_row_activated_cb(GtkTreeView * view,GtkTreePath * cpath,GtkTreeViewColumn * column,GladeNamedIconChooserDialog * dialog)539 contexts_row_activated_cb (GtkTreeView                 *view,
540                            GtkTreePath                 *cpath,
541                            GtkTreeViewColumn           *column,
542                            GladeNamedIconChooserDialog *dialog)
543 {
544   GtkTreeIter iter;
545   GtkTreePath *path;
546 
547   if (gtk_tree_model_get_iter_first (dialog->priv->filter_model, &iter))
548     {
549 
550       gtk_tree_selection_select_iter (dialog->priv->selection, &iter);
551 
552       path = gtk_tree_model_get_path (dialog->priv->filter_model, &iter);
553 
554       gtk_tree_selection_select_path (dialog->priv->selection, path);
555 
556       gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (dialog->priv->icons_view),
557                                      -1, 0);
558 
559       gtk_tree_path_free (path);
560 
561     }
562   gtk_widget_grab_focus (dialog->priv->icons_view);
563 }
564 
565 static void
contexts_selection_changed_cb(GtkTreeSelection * selection,GladeNamedIconChooserDialog * dialog)566 contexts_selection_changed_cb (GtkTreeSelection            *selection,
567                                GladeNamedIconChooserDialog *dialog)
568 {
569   GtkTreeIter iter;
570   GtkTreeModel *model;
571   gboolean retval;
572   gint context_id;
573 
574   retval = gtk_tree_selection_get_selected (selection, &model, &iter);
575 
576   if (retval)
577     {
578 
579       gtk_tree_model_get (model, &iter, CONTEXTS_ID_COLUMN, &context_id, -1);
580 
581       dialog->priv->context_id = context_id;
582 
583       if (!dialog->priv->filter_model)
584         return;
585 
586       filter_icons_model (dialog);
587     }
588 
589   entry_set_name (dialog, "");
590 
591 }
592 
593 static gboolean
row_separator_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer unused)594 row_separator_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer unused)
595 {
596   gboolean retval;
597   gchar *name, *title;
598 
599   gtk_tree_model_get (model, iter,
600                       CONTEXTS_NAME_COLUMN, &name,
601                       CONTEXTS_TITLE_COLUMN, &title, -1);
602 
603   retval = !name && !title;
604 
605   g_free (name);
606   g_free (title);
607 
608   return retval;
609 }
610 
611 static GtkWidget *
create_contexts_view(GladeNamedIconChooserDialog * dialog)612 create_contexts_view (GladeNamedIconChooserDialog *dialog)
613 {
614   GtkTreeView *view;
615   GtkTreeViewColumn *column;
616   GtkTreePath *path;
617 
618   dialog->priv->contexts_store = populate_icon_contexts_model ();
619 
620   view =
621       GTK_TREE_VIEW (gtk_tree_view_new_with_model
622                      (GTK_TREE_MODEL (dialog->priv->contexts_store)));
623 
624   column = gtk_tree_view_column_new_with_attributes (NULL,
625                                                      gtk_cell_renderer_text_new
626                                                      (), "text",
627                                                      CONTEXTS_TITLE_COLUMN,
628                                                      NULL);
629 
630   gtk_tree_view_append_column (view, column);
631   gtk_tree_view_set_headers_visible (view, FALSE);
632 
633   gtk_tree_view_set_row_separator_func (view,
634                                         (GtkTreeViewRowSeparatorFunc)
635                                         row_separator_func, NULL, NULL);
636 
637   gtk_tree_selection_set_mode (gtk_tree_view_get_selection (view),
638                                GTK_SELECTION_BROWSE);
639 
640   path = gtk_tree_path_new_from_indices (0, -1);
641   gtk_tree_selection_select_path (gtk_tree_view_get_selection (view), path);
642   gtk_tree_path_free (path);
643 
644   g_signal_connect (view, "row-activated",
645                     G_CALLBACK (contexts_row_activated_cb), dialog);
646 
647   g_signal_connect (gtk_tree_view_get_selection (view), "changed",
648                     G_CALLBACK (contexts_selection_changed_cb), dialog);
649 
650   gtk_widget_show (GTK_WIDGET (view));
651 
652   return GTK_WIDGET (view);
653 }
654 
655 /* filters the icons model based on the current state */
656 static void
filter_icons_model(GladeNamedIconChooserDialog * dialog)657 filter_icons_model (GladeNamedIconChooserDialog *dialog)
658 {
659 
660   set_busy_cursor (dialog, TRUE);
661 
662   g_object_ref (dialog->priv->filter_model);
663   gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->priv->icons_view), NULL);
664   gtk_entry_completion_set_model (dialog->priv->entry_completion, NULL);
665 
666   gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER
667                                   (dialog->priv->filter_model));
668 
669   gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->priv->icons_view),
670                            dialog->priv->filter_model);
671   gtk_entry_completion_set_model (dialog->priv->entry_completion,
672                                   GTK_TREE_MODEL (dialog->priv->icons_store));
673   gtk_entry_completion_set_text_column (dialog->priv->entry_completion,
674                                         ICONS_NAME_COLUMN);
675   g_object_unref (dialog->priv->filter_model);
676 
677   set_busy_cursor (dialog, FALSE);
678 }
679 
680 static gboolean
filter_visible_func(GtkTreeModel * model,GtkTreeIter * iter,GladeNamedIconChooserDialog * dialog)681 filter_visible_func (GtkTreeModel                *model,
682                      GtkTreeIter                 *iter,
683                      GladeNamedIconChooserDialog *dialog)
684 {
685   gboolean standard;
686   gint context_id;
687 
688   gtk_tree_model_get (model, iter,
689                       ICONS_CONTEXT_COLUMN, &context_id,
690                       ICONS_STANDARD_COLUMN, &standard, -1);
691 
692   if (dialog->priv->context_id == -1)
693     return (dialog->priv->settings_list_standard) ? TRUE && standard : TRUE;
694 
695   if (context_id == dialog->priv->context_id)
696     return (dialog->priv->settings_list_standard) ? TRUE && standard : TRUE;
697   else
698     return FALSE;
699 }
700 
701 
702 static gboolean
search_equal_func(GtkTreeModel * model,gint column,const gchar * key,GtkTreeIter * iter,GladeNamedIconChooserDialog * dialog)703 search_equal_func (GtkTreeModel                *model,
704                    gint                         column,
705                    const gchar                 *key,
706                    GtkTreeIter                 *iter,
707                    GladeNamedIconChooserDialog *dialog)
708 {
709   gchar *name;
710   gboolean retval;
711 
712   gtk_tree_model_get (model, iter, ICONS_NAME_COLUMN, &name, -1);
713 
714   retval = !g_str_has_prefix (name, key);
715 
716   g_free (name);
717 
718   return retval;
719 
720 }
721 
722 static gboolean
scan_for_context_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)723 scan_for_context_func (GtkTreeModel *model,
724                        GtkTreePath  *path,
725                        GtkTreeIter  *iter,
726                        gpointer      user_data)
727 {
728   ForEachFuncData *data = (ForEachFuncData *) user_data;
729   GtkTreeSelection *selection =
730       gtk_tree_view_get_selection (GTK_TREE_VIEW
731                                    (data->dialog->priv->contexts_view));
732   gchar *name = NULL;
733 
734   gtk_tree_model_get (model, iter, CONTEXTS_NAME_COLUMN, &name, -1);
735   if (!name)
736     return FALSE;
737 
738   if (strcmp (name, data->name) == 0)
739     {
740 
741       data->found = TRUE;
742 
743       if (data->do_activate)
744         gtk_tree_view_row_activated (GTK_TREE_VIEW
745                                      (data->dialog->priv->contexts_view), path,
746                                      gtk_tree_view_get_column (GTK_TREE_VIEW
747                                                                (data->dialog->
748                                                                 priv->
749                                                                 contexts_view),
750                                                                0));
751 
752       if (data->do_select)
753         gtk_tree_selection_select_path (selection, path);
754       else
755         gtk_tree_selection_unselect_path (selection, path);
756 
757       if (data->do_cursor)
758         gtk_tree_view_set_cursor (GTK_TREE_VIEW
759                                   (data->dialog->priv->contexts_view), path,
760                                   NULL, FALSE);
761 
762       g_free (name);
763 
764       return TRUE;
765     }
766 
767   g_free (name);
768 
769   return FALSE;
770 }
771 
772 gboolean
scan_for_name_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)773 scan_for_name_func (GtkTreeModel *model,
774                     GtkTreePath  *path,
775                     GtkTreeIter  *iter,
776                     gpointer      user_data)
777 {
778   ForEachFuncData *data = (ForEachFuncData *) user_data;
779   gchar *name = NULL;
780 
781   gtk_tree_model_get (model, iter, ICONS_NAME_COLUMN, &name, -1);
782   if (!name)
783     return FALSE;
784 
785   if (strcmp (name, data->name) == 0)
786     {
787 
788       data->found = TRUE;
789 
790       if (data->do_activate)
791         gtk_tree_view_row_activated (GTK_TREE_VIEW
792                                      (data->dialog->priv->icons_view), path,
793                                      gtk_tree_view_get_column (GTK_TREE_VIEW
794                                                                (data->dialog->
795                                                                 priv->
796                                                                 icons_view),
797                                                                0));
798 
799       if (data->do_select)
800         gtk_tree_selection_select_path (data->dialog->priv->selection, path);
801       else
802         gtk_tree_selection_unselect_path (data->dialog->priv->selection, path);
803 
804       if (data->do_cursor)
805         gtk_tree_view_set_cursor (GTK_TREE_VIEW
806                                   (data->dialog->priv->icons_view), path, NULL,
807                                   FALSE);
808 
809       g_free (name);
810 
811       return TRUE;
812     }
813 
814   g_free (name);
815 
816   return FALSE;
817 }
818 
819 static void
centre_selected_row(GladeNamedIconChooserDialog * dialog)820 centre_selected_row (GladeNamedIconChooserDialog *dialog)
821 {
822   GList *l;
823 
824   g_assert (dialog->priv->icons_store != NULL);
825   g_assert (dialog->priv->selection != NULL);
826 
827   l = gtk_tree_selection_get_selected_rows (dialog->priv->selection, NULL);
828 
829   if (l)
830     {
831       g_assert (gtk_widget_get_mapped (GTK_WIDGET (dialog)));
832       g_assert (gtk_widget_get_visible (GTK_WIDGET (dialog)));
833 
834       gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (dialog->priv->icons_view),
835                                     (GtkTreePath *) l->data,
836                                     NULL, TRUE, 0.5, 0.0);
837 
838 /*		gtk_tree_view_set_cursor (GTK_TREE_VIEW (dialog->priv->icons_view),
839 					 (GtkTreePath *) l->data,
840 					 0,
841 					 FALSE);
842 
843 		gtk_widget_grab_focus (dialog->priv->icons_view);
844 */
845       g_list_foreach (l, (GFunc) gtk_tree_path_free, NULL);
846       g_list_free (l);
847     }
848 }
849 
850 static void
select_first_row(GladeNamedIconChooserDialog * dialog)851 select_first_row (GladeNamedIconChooserDialog *dialog)
852 {
853   GtkTreePath *path;
854 
855   if (!dialog->priv->filter_model)
856     return;
857 
858   path = gtk_tree_path_new_from_indices (0, -1);
859   gtk_tree_view_set_cursor (GTK_TREE_VIEW (dialog->priv->icons_view), path,
860                             NULL, FALSE);
861   gtk_tree_path_free (path);
862 }
863 
864 static void
pending_select_name_process(GladeNamedIconChooserDialog * dialog)865 pending_select_name_process (GladeNamedIconChooserDialog *dialog)
866 {
867   ForEachFuncData *data;
868 
869   g_assert (dialog->priv->icons_store != NULL);
870   g_assert (dialog->priv->selection != NULL);
871 
872   if (dialog->priv->pending_select_name)
873     {
874 
875       data = g_slice_new0 (ForEachFuncData);
876 
877       data->name = dialog->priv->pending_select_name;
878       data->do_select = TRUE;
879       data->do_activate = FALSE;
880       data->dialog = dialog;
881 
882       gtk_tree_model_foreach (dialog->priv->filter_model,
883                               scan_for_name_func, data);
884 
885       g_free (dialog->priv->pending_select_name);
886       dialog->priv->pending_select_name = NULL;
887 
888       g_slice_free (ForEachFuncData, data);
889 
890     }
891   else
892     {
893       if (strlen (gtk_entry_get_text (GTK_ENTRY (dialog->priv->entry))) == 0)
894         {
895           select_first_row (dialog);
896         }
897     }
898 
899   centre_selected_row (dialog);
900 }
901 
902 static gboolean
is_standard_icon_name(const gchar * icon_name)903 is_standard_icon_name (const gchar *icon_name)
904 {
905   GQuark quark;
906 
907   quark = g_quark_try_string (icon_name);
908 
909   if (quark == 0)
910     return FALSE;
911 
912   return (g_hash_table_lookup (standard_icon_quarks, GUINT_TO_POINTER (quark))
913           != NULL);
914 
915 }
916 
917 static void
cleanup_after_load(GladeNamedIconChooserDialog * dialog)918 cleanup_after_load (GladeNamedIconChooserDialog *dialog)
919 {
920   dialog->priv->load_id = 0;
921 
922   pending_select_name_process (dialog);
923 
924   set_busy_cursor (dialog, FALSE);
925 }
926 
927 static void
chooser_set_model(GladeNamedIconChooserDialog * dialog)928 chooser_set_model (GladeNamedIconChooserDialog *dialog)
929 {
930 
931   /* filter model */
932   dialog->priv->filter_model =
933       gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->icons_store),
934                                  NULL);
935 
936   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER
937                                           (dialog->priv->filter_model),
938                                           (GtkTreeModelFilterVisibleFunc)
939                                           filter_visible_func, dialog, NULL);
940 
941   gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->priv->icons_view),
942                            dialog->priv->filter_model);
943   g_object_unref (dialog->priv->filter_model);
944 
945   gtk_entry_completion_set_model (dialog->priv->entry_completion,
946                                   GTK_TREE_MODEL (dialog->priv->icons_store));
947   gtk_entry_completion_set_text_column (dialog->priv->entry_completion,
948                                         ICONS_NAME_COLUMN);
949 
950   gtk_tree_view_set_search_column (GTK_TREE_VIEW (dialog->priv->icons_view),
951                                    ICONS_NAME_COLUMN);
952 
953   dialog->priv->icons_loaded = TRUE;
954 }
955 
956 typedef struct
957 {
958   gchar *name;
959   gint context;
960 } IconData;
961 
962 static gint
icon_data_compare(IconData * a,IconData * b)963 icon_data_compare (IconData *a, IconData *b)
964 {
965   return g_ascii_strcasecmp (a->name, b->name);
966 }
967 
968 static gboolean
reload_icons(GladeNamedIconChooserDialog * dialog)969 reload_icons (GladeNamedIconChooserDialog *dialog)
970 {
971   GtkListStore *store = dialog->priv->icons_store;
972   GtkTreeIter iter;
973   guint i;
974   GList *l, *icons = NULL;
975 
976   /* retrieve icon names from each context */
977   for (i = 0; i < G_N_ELEMENTS (standard_contexts); i++)
978     {
979 
980       GList *icons_in_context =
981           gtk_icon_theme_list_icons (dialog->priv->icon_theme,
982                                      standard_contexts[i].name);
983 
984       for (l = icons_in_context; l; l = l->next)
985         {
986 
987           IconData *data = g_slice_new (IconData);
988 
989           data->name = (gchar *) l->data;
990           data->context = i;
991 
992           icons = g_list_prepend (icons, data);
993         }
994 
995       g_list_free (icons_in_context);
996     }
997 
998   /* sort icon names */
999   icons = g_list_sort (icons, (GCompareFunc) icon_data_compare);
1000 
1001   /* put into to model */
1002   for (l = icons; l; l = l->next)
1003     {
1004 
1005       IconData *data = (IconData *) l->data;
1006 
1007       gtk_list_store_append (store, &iter);
1008       gtk_list_store_set (store, &iter,
1009                           ICONS_CONTEXT_COLUMN, data->context,
1010                           ICONS_STANDARD_COLUMN,
1011                           is_standard_icon_name (data->name), ICONS_NAME_COLUMN,
1012                           data->name, -1);
1013 
1014       g_free (data->name);
1015       g_slice_free (IconData, data);
1016     }
1017 
1018   g_list_free (icons);
1019 
1020   chooser_set_model (dialog);
1021 
1022   return FALSE;
1023 }
1024 
1025 static void
change_icon_theme(GladeNamedIconChooserDialog * dialog)1026 change_icon_theme (GladeNamedIconChooserDialog *dialog)
1027 {
1028   if (dialog->priv->icon_theme == NULL)
1029     dialog->priv->icon_theme = get_icon_theme_for_widget (GTK_WIDGET (dialog));
1030 
1031   gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->priv->icons_view), NULL);
1032   gtk_list_store_clear (dialog->priv->icons_store);
1033 
1034   set_busy_cursor (dialog, TRUE);
1035 
1036   dialog->priv->load_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 300,
1037                                            (GSourceFunc) reload_icons,
1038                                            dialog,
1039                                            (GDestroyNotify) cleanup_after_load);
1040 
1041 }
1042 
1043 static void
glade_named_icon_chooser_dialog_screen_changed(GtkWidget * widget,GdkScreen * previous_screen)1044 glade_named_icon_chooser_dialog_screen_changed (GtkWidget *widget,
1045                                                 GdkScreen *previous_screen)
1046 {
1047   GladeNamedIconChooserDialog *dialog;
1048 
1049   dialog = GLADE_NAMED_ICON_CHOOSER_DIALOG (widget);
1050 
1051   if (GTK_WIDGET_CLASS (glade_named_icon_chooser_dialog_parent_class)->
1052       screen_changed)
1053     GTK_WIDGET_CLASS (glade_named_icon_chooser_dialog_parent_class)->
1054         screen_changed (widget, previous_screen);
1055 
1056   if (gtk_widget_get_mapped (widget))
1057     change_icon_theme (dialog);
1058 
1059 }
1060 
1061 static GtkWidget *
create_icons_view(GladeNamedIconChooserDialog * dialog)1062 create_icons_view (GladeNamedIconChooserDialog *dialog)
1063 {
1064   GtkTreeView *view;
1065   GtkTreeViewColumn *column;
1066   GtkCellRenderer *pixbuf_renderer, *text_renderer;
1067 
1068   view = GTK_TREE_VIEW (gtk_tree_view_new ());
1069 
1070   column = gtk_tree_view_column_new ();
1071   gtk_tree_view_column_set_min_width (column, 56);
1072   gtk_tree_view_column_set_title (column, NULL);
1073   pixbuf_renderer = gtk_cell_renderer_pixbuf_new ();
1074 
1075   gtk_tree_view_column_pack_start (column, pixbuf_renderer, TRUE);
1076 
1077   gtk_tree_view_column_set_attributes (column,
1078                                        pixbuf_renderer,
1079                                        "icon-name", ICONS_NAME_COLUMN, NULL);
1080 
1081   gtk_tree_view_append_column (view, column);
1082   g_object_set (pixbuf_renderer,
1083                 "xpad", 2,
1084                 "xalign", 1.0, "stock-size", GTK_ICON_SIZE_MENU, NULL);
1085 
1086   column = gtk_tree_view_column_new ();
1087   gtk_tree_view_column_set_title (column, "Name");
1088   text_renderer = gtk_cell_renderer_text_new ();
1089   g_object_set (G_OBJECT (text_renderer),
1090                 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0, NULL);
1091 
1092   gtk_tree_view_column_pack_start (column, text_renderer, TRUE);
1093 
1094   gtk_tree_view_column_set_attributes (column,
1095                                        text_renderer,
1096                                        "text", ICONS_NAME_COLUMN, NULL);
1097 
1098 
1099   gtk_tree_view_append_column (view, column);
1100   gtk_tree_view_column_set_expand (column, TRUE);
1101   gtk_tree_view_column_set_resizable (column, FALSE);
1102 
1103   gtk_tree_view_set_headers_visible (view, FALSE);
1104 
1105   gtk_tree_view_set_enable_search (view, TRUE);
1106   gtk_tree_view_set_search_equal_func (view,
1107                                        (GtkTreeViewSearchEqualFunc)
1108                                        search_equal_func, dialog, NULL);
1109 
1110   g_signal_connect (view, "row-activated",
1111                     G_CALLBACK (icons_row_activated_cb), dialog);
1112 
1113   g_signal_connect (gtk_tree_view_get_selection (view), "changed",
1114                     G_CALLBACK (icons_selection_changed_cb), dialog);
1115 
1116   gtk_tree_selection_set_mode (gtk_tree_view_get_selection (view),
1117                                GTK_SELECTION_BROWSE);
1118 
1119   dialog->priv->selection = gtk_tree_view_get_selection (view);
1120 
1121   gtk_tree_view_set_rules_hint (view, TRUE);
1122 
1123   gtk_widget_show (GTK_WIDGET (view));
1124 
1125   return GTK_WIDGET (view);
1126 }
1127 
1128 /* sets the 'list-standard' state and refilters the icons model */
1129 static void
button_toggled(GtkToggleButton * button,GladeNamedIconChooserDialog * dialog)1130 button_toggled (GtkToggleButton *button, GladeNamedIconChooserDialog *dialog)
1131 {
1132   dialog->priv->settings_list_standard = gtk_toggle_button_get_active (button);
1133 
1134   if (dialog->priv->filter_model != NULL)
1135     filter_icons_model (dialog);
1136 }
1137 
1138 static GHashTable *
create_standard_icon_quarks(void)1139 create_standard_icon_quarks (void)
1140 {
1141   GHashTable *table;
1142   GQuark quark;
1143   guint i;
1144 
1145   table = g_hash_table_new (NULL, NULL);
1146 
1147   for (i = 0; i < G_N_ELEMENTS (standard_icon_names); i++)
1148     {
1149 
1150       quark = g_quark_from_static_string (standard_icon_names[i]);
1151 
1152       g_hash_table_insert (table,
1153                            GUINT_TO_POINTER (quark), GUINT_TO_POINTER (quark));
1154     }
1155 
1156   return table;
1157 }
1158 
1159 static void
glade_named_icon_chooser_dialog_style_set(GtkWidget * widget,GtkStyle * previous_style)1160 glade_named_icon_chooser_dialog_style_set (GtkWidget *widget,
1161                                            GtkStyle  *previous_style)
1162 {
1163   if (gtk_widget_has_screen (widget) && gtk_widget_get_mapped (widget))
1164     change_icon_theme (GLADE_NAMED_ICON_CHOOSER_DIALOG (widget));
1165 }
1166 
1167 /* override GtkWidget::show_all since we have internal widgets we wish to keep
1168  * hidden unless we decide otherwise, like the list-standard-icons-only checkbox.
1169  */
1170 static void
glade_named_icon_chooser_dialog_show_all(GtkWidget * widget)1171 glade_named_icon_chooser_dialog_show_all (GtkWidget *widget)
1172 {
1173   gtk_widget_show (widget);
1174 }
1175 
1176 /* Handler for GtkWindow::set-focus; this is where we save the last-focused
1177  * widget on our toplevel.  See glade_named_icon_chooser_dialog_hierarchy_changed()
1178  */
1179 static void
glade_named_icon_chooser_dialog_set_focus(GtkWindow * window,GtkWidget * focus)1180 glade_named_icon_chooser_dialog_set_focus (GtkWindow *window, GtkWidget *focus)
1181 {
1182 
1183   GTK_WINDOW_CLASS (glade_named_icon_chooser_dialog_parent_class)->
1184       set_focus (window, focus);
1185 
1186   GLADE_NAMED_ICON_CHOOSER_DIALOG (window)->priv->last_focus_widget =
1187       gtk_window_get_focus (window);
1188 }
1189 
1190 static void
glade_named_icon_chooser_dialog_finalize(GObject * object)1191 glade_named_icon_chooser_dialog_finalize (GObject *object)
1192 {
1193   GladeNamedIconChooserDialog *dialog =
1194       GLADE_NAMED_ICON_CHOOSER_DIALOG (object);
1195 
1196   if (dialog->priv->pending_select_name)
1197     {
1198       g_free (dialog->priv->pending_select_name);
1199       dialog->priv->pending_select_name = NULL;
1200     }
1201 
1202   G_OBJECT_CLASS (glade_named_icon_chooser_dialog_parent_class)->
1203     finalize (object);
1204 }
1205 
1206 static void
glade_named_icon_chooser_dialog_map(GtkWidget * widget)1207 glade_named_icon_chooser_dialog_map (GtkWidget *widget)
1208 {
1209   GladeNamedIconChooserDialog *dialog =
1210       GLADE_NAMED_ICON_CHOOSER_DIALOG (widget);
1211 
1212   GTK_WIDGET_CLASS (glade_named_icon_chooser_dialog_parent_class)->map (widget);
1213 
1214   settings_load (dialog);
1215 
1216   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->button),
1217                                 dialog->priv->settings_list_standard);
1218 
1219   gtk_widget_grab_focus (dialog->priv->icons_view);
1220 }
1221 
1222 static void
glade_named_icon_chooser_dialog_unmap(GtkWidget * widget)1223 glade_named_icon_chooser_dialog_unmap (GtkWidget *widget)
1224 {
1225   GladeNamedIconChooserDialog *dialog =
1226       GLADE_NAMED_ICON_CHOOSER_DIALOG (widget);
1227 
1228   settings_save (dialog);
1229 
1230   GTK_WIDGET_CLASS (glade_named_icon_chooser_dialog_parent_class)->
1231       unmap (widget);
1232 }
1233 
1234 /* we load the icons in expose() because we want the widget
1235  * to be fully painted before loading begins
1236  */
1237 static gboolean
glade_named_icon_chooser_dialog_draw(GtkWidget * widget,cairo_t * cr)1238 glade_named_icon_chooser_dialog_draw (GtkWidget *widget, cairo_t *cr)
1239 {
1240   GladeNamedIconChooserDialog *dialog =
1241       GLADE_NAMED_ICON_CHOOSER_DIALOG (widget);
1242   gboolean retval;
1243 
1244   retval =
1245       GTK_WIDGET_CLASS (glade_named_icon_chooser_dialog_parent_class)->
1246       draw (widget, cr);
1247   if (!dialog->priv->icons_loaded)
1248     {
1249       change_icon_theme (GLADE_NAMED_ICON_CHOOSER_DIALOG (widget));
1250       dialog->priv->icons_loaded = TRUE;
1251     }
1252 
1253   return retval;
1254 }
1255 
1256 static void
response_cb(GtkDialog * dialog,gint response_id)1257 response_cb (GtkDialog *dialog, gint response_id)
1258 {
1259   /* Act only on response IDs we recognize */
1260   if (!(response_id == GTK_RESPONSE_ACCEPT
1261         || response_id == GTK_RESPONSE_OK
1262         || response_id == GTK_RESPONSE_YES
1263         || response_id == GTK_RESPONSE_APPLY))
1264     return;
1265 
1266   if (!should_respond (GLADE_NAMED_ICON_CHOOSER_DIALOG (dialog)))
1267     {
1268       g_signal_stop_emission_by_name (dialog, "response");
1269     }
1270 }
1271 
1272 /* we intercept the GladeNamedIconChooser::icon-activated signal and try to
1273  * make the dialog emit a valid response signal
1274  */
1275 static void
icon_activated_cb(GladeNamedIconChooserDialog * dialog)1276 icon_activated_cb (GladeNamedIconChooserDialog *dialog)
1277 {
1278   GList *children, *l;
1279 
1280   children =
1281       gtk_container_get_children (GTK_CONTAINER
1282                                   (gtk_dialog_get_action_area
1283                                    (GTK_DIALOG (dialog))));
1284 
1285   for (l = children; l; l = l->next)
1286     {
1287       GtkWidget *widget;
1288       gint response_id;
1289 
1290       widget = GTK_WIDGET (l->data);
1291       response_id =
1292           gtk_dialog_get_response_for_widget (GTK_DIALOG (dialog), widget);
1293 
1294       if (response_id == GTK_RESPONSE_ACCEPT ||
1295           response_id == GTK_RESPONSE_OK ||
1296           response_id == GTK_RESPONSE_YES || response_id == GTK_RESPONSE_APPLY)
1297         {
1298           g_list_free (children);
1299 
1300           gtk_dialog_response (GTK_DIALOG (dialog), response_id);
1301 
1302           return;
1303         }
1304     }
1305   g_list_free (children);
1306 }
1307 
1308 /* we intercept the GladeNamedIconChooser::selection-changed signal and try to
1309  * make the affirmative response button insensitive when the selection is empty
1310  */
1311 static void
selection_changed_cb(GladeNamedIconChooserDialog * dialog)1312 selection_changed_cb (GladeNamedIconChooserDialog *dialog)
1313 {
1314   GList *children, *l;
1315   gchar *icon_name;
1316 
1317   children =
1318       gtk_container_get_children (GTK_CONTAINER
1319                                   (gtk_dialog_get_action_area
1320                                    (GTK_DIALOG (dialog))));
1321 
1322   for (l = children; l; l = l->next)
1323     {
1324       GtkWidget *widget;
1325       gint response_id;
1326 
1327       widget = GTK_WIDGET (l->data);
1328       response_id =
1329           gtk_dialog_get_response_for_widget (GTK_DIALOG (dialog), widget);
1330 
1331       if (response_id == GTK_RESPONSE_ACCEPT ||
1332           response_id == GTK_RESPONSE_OK ||
1333           response_id == GTK_RESPONSE_YES || response_id == GTK_RESPONSE_APPLY)
1334         {
1335           icon_name = glade_named_icon_chooser_dialog_get_icon_name (dialog);
1336 
1337           gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
1338                                              response_id, icon_name != NULL);
1339           g_free (icon_name);
1340           g_list_free (children);
1341           return;
1342         }
1343     }
1344   g_list_free (children);
1345 }
1346 
1347 static void
glade_named_icon_chooser_dialog_init(GladeNamedIconChooserDialog * dialog)1348 glade_named_icon_chooser_dialog_init (GladeNamedIconChooserDialog *dialog)
1349 {
1350   GtkWidget *contents;
1351   GtkWidget *hbox;
1352   GtkWidget *vbox;
1353   GtkWidget *sw;
1354   GtkWidget *label;
1355   GtkWidget *hpaned;
1356   GtkWidget *content_area;
1357   GtkSizeGroup *group;
1358 
1359   dialog->priv = glade_named_icon_chooser_dialog_get_instance_private (dialog);
1360 
1361   dialog->priv->filter_model = NULL;
1362   dialog->priv->icons_store = NULL;
1363   dialog->priv->context_id = -1;
1364   dialog->priv->pending_select_name = NULL;
1365   dialog->priv->last_focus_widget = NULL;
1366   dialog->priv->icons_loaded = FALSE;
1367 
1368 
1369   gtk_window_set_title (GTK_WINDOW (dialog), _("Named Icon Chooser"));
1370 
1371   gtk_window_set_default_size (GTK_WINDOW (dialog), 610, 480);
1372 
1373   _glade_util_dialog_set_hig (GTK_DIALOG (dialog));
1374   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
1375 
1376   /* We do a signal connection here rather than overriding the method in
1377    * class_init because GtkDialog::response is a RUN_LAST signal.  We want *our*
1378    * handler to be run *first*, regardless of whether the user installs response
1379    * handlers of his own.
1380    */
1381   g_signal_connect (dialog, "response", G_CALLBACK (response_cb), NULL);
1382 
1383   g_signal_connect (dialog, "icon-activated",
1384                     G_CALLBACK (icon_activated_cb), NULL);
1385 
1386   g_signal_connect (dialog, "selection-changed",
1387                     G_CALLBACK (selection_changed_cb), NULL);
1388 
1389 
1390   if (standard_icon_quarks == NULL)
1391     standard_icon_quarks = create_standard_icon_quarks ();
1392 
1393   contents = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
1394   gtk_container_set_border_width (GTK_CONTAINER (contents), 5);
1395   gtk_widget_show (contents);
1396 
1397   label = gtk_label_new_with_mnemonic (_("Icon _Name:"));
1398   gtk_widget_set_halign (label, GTK_ALIGN_START);
1399   gtk_widget_show (label);
1400 
1401   dialog->priv->entry = gtk_entry_new ();
1402   gtk_entry_set_activates_default (GTK_ENTRY (dialog->priv->entry), TRUE);
1403   gtk_entry_set_width_chars (GTK_ENTRY (dialog->priv->entry), 40);
1404   g_object_set (G_OBJECT (dialog->priv->entry), "truncate-multiline", TRUE,
1405                 NULL);
1406   g_signal_connect (G_OBJECT (dialog->priv->entry), "changed",
1407                     G_CALLBACK (changed_text_handler), dialog);
1408   g_signal_connect (G_OBJECT (dialog->priv->entry), "insert-text",
1409                     G_CALLBACK (insert_text_handler), dialog);
1410   gtk_widget_show (dialog->priv->entry);
1411 
1412   dialog->priv->entry_completion = gtk_entry_completion_new ();
1413   gtk_entry_set_completion (GTK_ENTRY (dialog->priv->entry),
1414                             dialog->priv->entry_completion);
1415   gtk_entry_completion_set_popup_completion (dialog->priv->entry_completion,
1416                                              FALSE);
1417   gtk_entry_completion_set_inline_completion (dialog->priv->entry_completion,
1418                                               TRUE);
1419 
1420   gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->priv->entry);
1421 
1422   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
1423   gtk_widget_show (hbox);
1424 
1425   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1426   gtk_box_pack_start (GTK_BOX (hbox), dialog->priv->entry, TRUE, TRUE, 0);
1427   gtk_box_pack_start (GTK_BOX (contents), hbox, FALSE, FALSE, 6);
1428 
1429   hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
1430   gtk_paned_set_position (GTK_PANED (hpaned), 150);
1431   gtk_widget_show (hpaned);
1432 
1433   dialog->priv->contexts_view = create_contexts_view (dialog);
1434   dialog->priv->icons_view = create_icons_view (dialog);
1435 
1436   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
1437   gtk_widget_show (vbox);
1438 
1439   group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
1440 
1441   label = gtk_label_new_with_mnemonic (_("C_ontexts:"));
1442   gtk_label_set_mnemonic_widget (GTK_LABEL (label),
1443                                  dialog->priv->contexts_view);
1444   gtk_widget_set_halign (label, GTK_ALIGN_START);
1445   gtk_size_group_add_widget (group, label);
1446   gtk_widget_show (label);
1447 
1448   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
1449 
1450   sw = gtk_scrolled_window_new (NULL, NULL);
1451   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER,
1452                                   GTK_POLICY_AUTOMATIC);
1453   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
1454   gtk_widget_show (sw);
1455 
1456   gtk_container_add (GTK_CONTAINER (sw), dialog->priv->contexts_view);
1457   gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
1458   gtk_paned_pack1 (GTK_PANED (hpaned), vbox, FALSE, FALSE);
1459 
1460 
1461   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
1462   gtk_widget_show (vbox);
1463 
1464   label = gtk_label_new_with_mnemonic (_("Icon Na_mes:"));
1465   gtk_label_set_mnemonic_widget (GTK_LABEL (label), dialog->priv->icons_view);
1466   gtk_widget_set_halign (label, GTK_ALIGN_START);
1467   gtk_size_group_add_widget (group, label);
1468   gtk_widget_show (label);
1469 
1470   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
1471 
1472   sw = gtk_scrolled_window_new (NULL, NULL);
1473   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER,
1474                                   GTK_POLICY_AUTOMATIC);
1475   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
1476   gtk_widget_show (sw);
1477 
1478   gtk_container_add (GTK_CONTAINER (sw), dialog->priv->icons_view);
1479   gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
1480   gtk_paned_pack2 (GTK_PANED (hpaned), vbox, TRUE, FALSE);
1481 
1482   gtk_box_pack_start (GTK_BOX (contents), hpaned, TRUE, TRUE, 0);
1483 
1484 
1485   g_object_unref (G_OBJECT (group));
1486 
1487   dialog->priv->button =
1488       gtk_check_button_new_with_mnemonic (_("_List standard icons only"));
1489   gtk_widget_show (dialog->priv->button);
1490 
1491   g_signal_connect (dialog->priv->button, "toggled",
1492                     G_CALLBACK (button_toggled), dialog);
1493 
1494   gtk_box_pack_start (GTK_BOX (contents), dialog->priv->button, FALSE, FALSE,
1495                       0);
1496   gtk_box_pack_start (GTK_BOX (content_area), contents, TRUE, TRUE, 0);
1497 
1498   /* underlying model */
1499   dialog->priv->icons_store = gtk_list_store_new (ICONS_N_COLUMNS,
1500                                                   G_TYPE_UINT,
1501                                                   G_TYPE_BOOLEAN,
1502                                                   G_TYPE_STRING);
1503 }
1504 
1505 static void
glade_named_icon_chooser_dialog_class_init(GladeNamedIconChooserDialogClass * klass)1506 glade_named_icon_chooser_dialog_class_init (GladeNamedIconChooserDialogClass *klass)
1507 {
1508   GObjectClass *object_class;
1509   GtkWidgetClass *widget_class;
1510   GtkWindowClass *window_class;
1511 
1512   object_class = G_OBJECT_CLASS (klass);
1513   widget_class = GTK_WIDGET_CLASS (klass);
1514   window_class = GTK_WINDOW_CLASS (klass);
1515 
1516   object_class->finalize = glade_named_icon_chooser_dialog_finalize;
1517 
1518   widget_class->map = glade_named_icon_chooser_dialog_map;
1519   widget_class->unmap = glade_named_icon_chooser_dialog_unmap;
1520   widget_class->draw = glade_named_icon_chooser_dialog_draw;
1521   widget_class->show_all = glade_named_icon_chooser_dialog_show_all;
1522   widget_class->style_set = glade_named_icon_chooser_dialog_style_set;
1523   widget_class->screen_changed = glade_named_icon_chooser_dialog_screen_changed;
1524 
1525   window_class->set_focus = glade_named_icon_chooser_dialog_set_focus;
1526 
1527         /**
1528 	 * GladeNamedIconChooserDialog::icon-activated
1529 	 * @chooser: the object which received the signal
1530 	 *
1531 	 * This signal is emitted when the user "activates" an icon
1532 	 * in the named icon chooser.  This can happen by double-clicking on an item
1533 	 * in the recently used resources list, or by pressing
1534 	 * <keycap>Enter</keycap>.
1535 	*/
1536   dialog_signals[ICON_ACTIVATED] =
1537       g_signal_new ("icon-activated",
1538                     G_TYPE_FROM_CLASS (object_class),
1539                     G_SIGNAL_RUN_LAST,
1540                     G_STRUCT_OFFSET (GladeNamedIconChooserDialogClass,
1541                                      icon_activated), NULL, NULL,
1542                     g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1543 
1544         /**
1545 	 * GladeNamedIconChooserDialog::selection-changed
1546 	 * @chooser: the object which received the signal
1547 	 *
1548 	 * This signal is emitted when there is a change in the set of
1549 	 * selected icon names.  This can happen when a user
1550 	 * modifies the selection with the mouse or the keyboard, or when
1551 	 * explicitely calling functions to change the selection.
1552 	*/
1553   dialog_signals[SELECTION_CHANGED] =
1554       g_signal_new ("selection-changed",
1555                     G_TYPE_FROM_CLASS (object_class),
1556                     G_SIGNAL_RUN_LAST,
1557                     G_STRUCT_OFFSET (GladeNamedIconChooserDialogClass,
1558                                      selection_changed), NULL, NULL,
1559                     g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1560 }
1561 
1562 static gboolean
should_respond(GladeNamedIconChooserDialog * dialog)1563 should_respond (GladeNamedIconChooserDialog *dialog)
1564 {
1565   gchar *icon_name;
1566 
1567   /* is there an icon selected? */
1568   icon_name = glade_named_icon_chooser_dialog_get_icon_name (dialog);
1569   if (!icon_name)
1570     return FALSE;
1571 
1572   g_free (icon_name);
1573   return TRUE;
1574 }
1575 
1576 /* get's the name of the configuration file */
1577 static gchar *
get_config_filename(void)1578 get_config_filename (void)
1579 {
1580   return g_build_filename (g_get_user_config_dir (), "gladeui", "config", NULL);
1581 }
1582 
1583 /* get's the name of the directory that contains the config file */
1584 static char *
get_config_dirname(void)1585 get_config_dirname (void)
1586 {
1587   return g_build_filename (g_get_user_config_dir (), "gladeui", NULL);
1588 }
1589 
1590 /* loads the configuration settings */
1591 static void
settings_load(GladeNamedIconChooserDialog * dialog)1592 settings_load (GladeNamedIconChooserDialog *dialog)
1593 {
1594   GKeyFile *keyfile;
1595   gboolean success, boolean_value;
1596   gchar *filename;
1597   GError *error = NULL;
1598 
1599   keyfile = g_key_file_new ();
1600 
1601   filename = get_config_filename ();
1602   success = g_key_file_load_from_file (keyfile,
1603                                        filename, G_KEY_FILE_NONE, &error);
1604   g_free (filename);
1605 
1606   if (!success)
1607     {
1608 
1609       dialog->priv->settings_list_standard = DEFAULT_SETTING_LIST_STANDARD_ONLY;
1610 
1611       g_clear_error (&error);
1612       g_key_file_free (keyfile);
1613       return;
1614     }
1615 
1616 
1617   boolean_value = g_key_file_get_boolean (keyfile,
1618                                           "Named Icon Chooser",
1619                                           "ListStandardOnly", &error);
1620   if (error)
1621     {
1622       dialog->priv->settings_list_standard = DEFAULT_SETTING_LIST_STANDARD_ONLY;
1623       g_clear_error (&error);
1624     }
1625   else
1626     {
1627       dialog->priv->settings_list_standard = boolean_value;
1628     }
1629 
1630   g_key_file_free (keyfile);
1631 }
1632 
1633 /* creates a GKeyFile based on the current settings */
1634 static GKeyFile *
settings_to_keyfile(GladeNamedIconChooserDialog * dialog)1635 settings_to_keyfile (GladeNamedIconChooserDialog *dialog)
1636 {
1637   GKeyFile *keyfile;
1638   gchar *filename;
1639 
1640   keyfile = g_key_file_new ();
1641 
1642   filename = get_config_filename ();
1643   g_key_file_load_from_file (keyfile,
1644                              get_config_filename (),
1645                              G_KEY_FILE_NONE, NULL);
1646   g_free (filename);
1647 
1648   g_key_file_set_boolean (keyfile,
1649                           "Named Icon Chooser",
1650                           "ListStandardOnly",
1651                           dialog->priv->settings_list_standard);
1652 
1653   return keyfile;
1654 }
1655 
1656 /* serializes the the current configuration to the config file */
1657 static void
settings_save(GladeNamedIconChooserDialog * dialog)1658 settings_save (GladeNamedIconChooserDialog *dialog)
1659 {
1660   GKeyFile *keyfile;
1661   gchar *contents;
1662   gsize contents_length;
1663   gchar *filename = NULL, *dirname = NULL;
1664   GError *error = NULL;
1665 
1666   keyfile = settings_to_keyfile (dialog);
1667 
1668   contents = g_key_file_to_data (keyfile, &contents_length, &error);
1669 
1670   if (error)
1671     goto out;
1672 
1673   filename = get_config_filename ();
1674 
1675   if (!g_file_set_contents (filename, contents, contents_length, NULL))
1676     {
1677       gchar *dirname;
1678       gint saved_errno;
1679 
1680       dirname = get_config_dirname ();
1681       if (g_mkdir_with_parents (dirname, 0700) != 0)    /* 0700 per the XDG basedir spec */
1682         {
1683 
1684           saved_errno = errno;
1685           g_set_error (&error,
1686                        G_FILE_ERROR,
1687                        g_file_error_from_errno (saved_errno),
1688                        _("Could not create directory: %s"), dirname);
1689           goto out;
1690         }
1691 
1692       if (!g_file_set_contents (filename, contents, contents_length, &error))
1693         {
1694           goto out;
1695         }
1696     }
1697 
1698 out:
1699 
1700   g_free (contents);
1701   g_free (dirname);
1702   g_free (filename);
1703   g_clear_error (&error);
1704   g_key_file_free (keyfile);
1705 }
1706 
1707 static GtkWidget *
glade_named_icon_chooser_dialog_new_valist(const gchar * title,GtkWindow * parent,const gchar * first_button_text,va_list varargs)1708 glade_named_icon_chooser_dialog_new_valist (const gchar *title,
1709                                             GtkWindow   *parent,
1710                                             const gchar *first_button_text,
1711                                             va_list      varargs)
1712 {
1713   GtkWidget *result;
1714   const char *button_text = first_button_text;
1715   gint response_id;
1716 
1717   result = g_object_new (GLADE_TYPE_NAMED_ICON_CHOOSER_DIALOG,
1718                          "title", title, "transient-for", parent, NULL);
1719 
1720   while (button_text)
1721     {
1722       response_id = va_arg (varargs, gint);
1723       gtk_dialog_add_button (GTK_DIALOG (result), button_text, response_id);
1724       button_text = va_arg (varargs, const gchar *);
1725     }
1726 
1727   return result;
1728 }
1729 
1730 /**
1731  * glade_named_icon_chooser_dialog_new:
1732  * @title: Title of the dialog, or %NULL
1733  * @parent: Transient parent of the dialog, or %NULL,
1734  * @first_button_text: stock ID or text to go in the first button, or %NULL
1735  * @Varargs: response ID for the first button, then additional (button, id)
1736  *   pairs, ending with %NULL
1737  *
1738  * Creates a new #GladeNamedIconChooserDialog.  This function is analogous to
1739  * gtk_dialog_new_with_buttons().
1740  *
1741  * Return value: a new #GladeNamedIconChooserDialog
1742  */
1743 GtkWidget *
glade_named_icon_chooser_dialog_new(const gchar * title,GtkWindow * parent,const gchar * first_button_text,...)1744 glade_named_icon_chooser_dialog_new (const gchar *title,
1745                                      GtkWindow   *parent,
1746                                      const gchar *first_button_text,
1747                                      ...)
1748 {
1749   GtkWidget *result;
1750   va_list varargs;
1751 
1752   va_start (varargs, first_button_text);
1753   result = glade_named_icon_chooser_dialog_new_valist (title,
1754                                                        parent,
1755                                                        first_button_text,
1756                                                        varargs);
1757   va_end (varargs);
1758 
1759   return result;
1760 }
1761