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