1 /*
2 * Nautilus Actions
3 * A Nautilus extension which offers configurable context menu actions.
4 *
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009-2014 Pierre Wieser and others (see AUTHORS)
8 *
9 * Nautilus-Actions is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
13 *
14 * Nautilus-Actions is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Nautilus-Actions; see the file COPYING. If not, see
21 * <http://www.gnu.org/licenses/>.
22 *
23 * Authors:
24 * Frederic Ruaudel <grumz@grumz.net>
25 * Rodrigo Moya <rodrigo@gnome-db.org>
26 * Pierre Wieser <pwieser@trychlos.org>
27 * ... and many others (see AUTHORS)
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <core/na-gtk-utils.h>
35
36 #include "base-keysyms.h"
37 #include "nact-application.h"
38 #include "base-gtk-utils.h"
39 #include "nact-icon-chooser.h"
40
41 /* private class data
42 */
43 struct _NactIconChooserClassPrivate {
44 void *empty; /* so that gcc -pedantic is happy */
45 };
46
47 /* private instance data
48 */
49 struct _NactIconChooserPrivate {
50 gboolean dispose_has_run;
51 BaseWindow *main_window;
52 const gchar *initial_icon;
53 gchar *current_icon;
54 GtkWidget *path_preview;
55 };
56
57 #define VIEW_ICON_SIZE GTK_ICON_SIZE_DND
58 #define VIEW_ICON_DEFAULT_WIDTH 32 /* width of the GTK_ICON_SIZE_DND icon size */
59 #define PREVIEW_ICON_SIZE GTK_ICON_SIZE_DIALOG
60 #define PREVIEW_ICON_WIDTH 64
61 #define CURRENT_ICON_SIZE GTK_ICON_SIZE_DIALOG
62
63 /* column ordering in the Stock model
64 */
65 enum {
66 STOCK_NAME_COLUMN = 0,
67 STOCK_LABEL_COLUMN,
68 STOCK_PIXBUF_COLUMN,
69 STOCK_N_COLUMN
70 };
71
72 /* column ordering in the ThemeContext model
73 * this is the list store on the left which lets the user select the context
74 */
75 enum {
76 THEME_CONTEXT_LABEL_COLUMN = 0,
77 THEME_CONTEXT_STORE_COLUMN,
78 THEME_CONTEXT_LAST_SELECTED_COLUMN,
79 THEME_CONTEXT_N_COLUMN
80 };
81
82 /* column ordering in the ThemeIconView model
83 * foreach selected context, we display in the icon view the list of
84 * corresponding icons
85 */
86 enum {
87 THEME_ICON_LABEL_COLUMN = 0,
88 THEME_ICON_PIXBUF_COLUMN,
89 THEME_ICON_N_COLUMN
90 };
91
92 static const gchar *st_xmlui_filename = PKGUIDIR "/nact-icon-chooser.ui";
93 static const gchar *st_toplevel_name = "IconChooserDialog";
94 static const gchar *st_wsp_name = NA_IPREFS_ICON_CHOOSER_WSP;
95
96 static BaseDialogClass *st_parent_class = NULL;
97
98 static GType register_type( void );
99 static void class_init( NactIconChooserClass *klass );
100 static void instance_init( GTypeInstance *instance, gpointer klass );
101 static void instance_constructed( GObject *dialog );
102 static void instance_dispose( GObject *dialog );
103 static void instance_finalize( GObject *dialog );
104
105 static void on_base_initialize_gtk( NactIconChooser *editor, GtkDialog *toplevel, gpointer user_data );
106 static void do_initialize_themed_icons( NactIconChooser *editor );
107 static void do_initialize_icons_by_path( NactIconChooser *editor );
108 static void on_base_initialize_window( NactIconChooser *editor, gpointer user_data );
109 static void fillup_themed_icons( NactIconChooser *editor );
110 static void fillup_icons_by_path( NactIconChooser *editor );
111 static void on_base_show_widgets( NactIconChooser *editor, gpointer user_data );
112
113 static void on_cancel_clicked( GtkButton *button, NactIconChooser *editor );
114 static void on_ok_clicked( GtkButton *button, NactIconChooser *editor );
115 static void on_dialog_cancel( BaseDialog *dialog );
116
117 static void on_current_icon_changed( const NactIconChooser *editor );
118 static gboolean on_destroy( GtkWidget *widget, GdkEvent *event, void *foo );
119 static gboolean on_icon_view_button_press_event( GtkWidget *widget, GdkEventButton *event, NactIconChooser *editor );
120 static gboolean on_key_pressed_event( GtkWidget *widget, GdkEventKey *event, NactIconChooser *editor );
121 static void on_themed_context_changed( GtkTreeSelection *selection, NactIconChooser *editor );
122 static void on_themed_icon_changed( GtkIconView *icon_view, NactIconChooser *editor );
123 static void on_themed_apply_button_clicked( GtkButton *button, NactIconChooser *editor );
124 static void on_themed_apply_triggered( NactIconChooser *editor );
125 static void on_path_selection_changed( GtkFileChooser *chooser, NactIconChooser *editor );
126 static void on_path_update_preview( GtkFileChooser *chooser, NactIconChooser *editor );
127 static void on_path_apply_button_clicked( GtkButton *button, NactIconChooser *editor );
128 static GtkListStore *theme_context_load_icons( NactIconChooser *editor, const gchar *context );
129
130 GType
nact_icon_chooser_get_type(void)131 nact_icon_chooser_get_type( void )
132 {
133 static GType dialog_type = 0;
134
135 if( !dialog_type ){
136 dialog_type = register_type();
137 }
138
139 return( dialog_type );
140 }
141
142 static GType
register_type(void)143 register_type( void )
144 {
145 static const gchar *thisfn = "nact_icon_chooser_register_type";
146 GType type;
147
148 static GTypeInfo info = {
149 sizeof( NactIconChooserClass ),
150 ( GBaseInitFunc ) NULL,
151 ( GBaseFinalizeFunc ) NULL,
152 ( GClassInitFunc ) class_init,
153 NULL,
154 NULL,
155 sizeof( NactIconChooser ),
156 0,
157 ( GInstanceInitFunc ) instance_init
158 };
159
160 g_debug( "%s", thisfn );
161
162 type = g_type_register_static( BASE_TYPE_DIALOG, "NactIconChooser", &info, 0 );
163
164 return( type );
165 }
166
167 static void
class_init(NactIconChooserClass * klass)168 class_init( NactIconChooserClass *klass )
169 {
170 static const gchar *thisfn = "nact_icon_chooser_class_init";
171 GObjectClass *object_class;
172 BaseDialogClass *dialog_class;
173
174 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
175
176 st_parent_class = g_type_class_peek_parent( klass );
177
178 object_class = G_OBJECT_CLASS( klass );
179 object_class->constructed = instance_constructed;
180 object_class->dispose = instance_dispose;
181 object_class->finalize = instance_finalize;
182
183 klass->private = g_new0( NactIconChooserClassPrivate, 1 );
184
185 dialog_class = BASE_DIALOG_CLASS( klass );
186 dialog_class->cancel = on_dialog_cancel;
187 }
188
189 static void
instance_init(GTypeInstance * instance,gpointer klass)190 instance_init( GTypeInstance *instance, gpointer klass )
191 {
192 static const gchar *thisfn = "nact_icon_chooser_instance_init";
193 NactIconChooser *self;
194
195 g_return_if_fail( NACT_IS_ICON_CHOOSER( instance ));
196
197 g_debug( "%s: instance=%p (%s), klass=%p",
198 thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
199
200 self = NACT_ICON_CHOOSER( instance );
201
202 self->private = g_new0( NactIconChooserPrivate, 1 );
203
204 self->private->dispose_has_run = FALSE;
205 }
206
207 static void
instance_constructed(GObject * dialog)208 instance_constructed( GObject *dialog )
209 {
210 static const gchar *thisfn = "nact_icon_chooser_instance_constructed";
211 NactIconChooserPrivate *priv;
212
213 g_return_if_fail( NACT_IS_ICON_CHOOSER( dialog ));
214
215 priv = NACT_ICON_CHOOSER( dialog )->private;
216
217 if( !priv->dispose_has_run ){
218
219 /* chain up to the parent class */
220 if( G_OBJECT_CLASS( st_parent_class )->constructed ){
221 G_OBJECT_CLASS( st_parent_class )->constructed( dialog );
222 }
223
224 g_debug( "%s: dialog=%p (%s)", thisfn, ( void * ) dialog, G_OBJECT_TYPE_NAME( dialog ));
225
226 base_window_signal_connect(
227 BASE_WINDOW( dialog ),
228 G_OBJECT( dialog ),
229 BASE_SIGNAL_INITIALIZE_GTK,
230 G_CALLBACK( on_base_initialize_gtk ));
231
232 base_window_signal_connect(
233 BASE_WINDOW( dialog ),
234 G_OBJECT( dialog ),
235 BASE_SIGNAL_INITIALIZE_WINDOW,
236 G_CALLBACK( on_base_initialize_window ));
237
238 base_window_signal_connect(
239 BASE_WINDOW( dialog ),
240 G_OBJECT( dialog ),
241 BASE_SIGNAL_SHOW_WIDGETS,
242 G_CALLBACK( on_base_show_widgets ));
243 }
244 }
245
246 static void
instance_dispose(GObject * dialog)247 instance_dispose( GObject *dialog )
248 {
249 static const gchar *thisfn = "nact_icon_chooser_instance_dispose";
250 NactIconChooser *self;
251 guint pos;
252 GtkWidget *paned;
253
254 g_return_if_fail( NACT_IS_ICON_CHOOSER( dialog ));
255
256 self = NACT_ICON_CHOOSER( dialog );
257
258 if( !self->private->dispose_has_run ){
259 g_debug( "%s: dialog=%p (%s)", thisfn, ( void * ) dialog, G_OBJECT_TYPE_NAME( dialog ));
260
261 self->private->dispose_has_run = TRUE;
262
263 paned = base_window_get_widget( BASE_WINDOW( self ), "IconPaned" );
264 pos = gtk_paned_get_position( GTK_PANED( paned ));
265 na_settings_set_uint( NA_IPREFS_ICON_CHOOSER_PANED, pos );
266
267 /* chain up to the parent class */
268 if( G_OBJECT_CLASS( st_parent_class )->dispose ){
269 G_OBJECT_CLASS( st_parent_class )->dispose( dialog );
270 }
271 }
272 }
273
274 static void
instance_finalize(GObject * dialog)275 instance_finalize( GObject *dialog )
276 {
277 static const gchar *thisfn = "nact_icon_chooser_instance_finalize";
278 NactIconChooser *self;
279
280 g_return_if_fail( NACT_IS_ICON_CHOOSER( dialog ));
281
282 g_debug( "%s: dialog=%p (%s)", thisfn, ( void * ) dialog, G_OBJECT_TYPE_NAME( dialog ));
283
284 self = NACT_ICON_CHOOSER( dialog );
285
286 g_free( self->private->current_icon );
287
288 g_free( self->private );
289
290 /* chain call to parent class */
291 if( G_OBJECT_CLASS( st_parent_class )->finalize ){
292 G_OBJECT_CLASS( st_parent_class )->finalize( dialog );
293 }
294 }
295
296 /**
297 * nact_icon_chooser_choose_icon:
298 * @parent: the #BaseWindow parent of this dialog.
299 * @icon_name: the current icon at startup.
300 *
301 * Initializes and runs the dialog.
302 *
303 * This dialog lets the user choose an icon, either as the name of a
304 * themed icon, or as the path of an image.
305 *
306 * Returns: the selected icon, as a new string which should be g_free()
307 * by the caller.
308 */
309 gchar *
nact_icon_chooser_choose_icon(BaseWindow * parent,const gchar * icon_name)310 nact_icon_chooser_choose_icon( BaseWindow *parent, const gchar *icon_name )
311 {
312 static const gchar *thisfn = "nact_icon_chooser_choose_icon";
313 NactIconChooser *editor;
314 gchar *new_name;
315
316 g_return_val_if_fail( BASE_IS_WINDOW( parent ), NULL );
317
318 g_debug( "%s: parent=%p, icon_name=%s", thisfn, ( void * ) parent, icon_name );
319
320 editor = g_object_new( NACT_TYPE_ICON_CHOOSER,
321 BASE_PROP_PARENT, parent,
322 BASE_PROP_XMLUI_FILENAME, st_xmlui_filename,
323 BASE_PROP_TOPLEVEL_NAME, st_toplevel_name,
324 BASE_PROP_WSP_NAME, st_wsp_name,
325 NULL );
326
327 editor->private->main_window = parent;
328 editor->private->initial_icon = icon_name;
329
330 new_name = g_strdup( editor->private->initial_icon );
331
332 if( base_window_run( BASE_WINDOW( editor )) == GTK_RESPONSE_OK ){
333 g_free( new_name );
334 new_name = g_strdup( editor->private->current_icon );
335 }
336
337 g_object_unref( editor );
338
339 return( new_name );
340 }
341
342 static void
on_base_initialize_gtk(NactIconChooser * editor,GtkDialog * toplevel,gpointer user_data)343 on_base_initialize_gtk( NactIconChooser *editor, GtkDialog *toplevel, gpointer user_data )
344 {
345 static const gchar *thisfn = "nact_icon_chooser_on_base_initialize_gtk";
346
347 g_return_if_fail( NACT_IS_ICON_CHOOSER( editor ));
348
349 if( !editor->private->dispose_has_run ){
350
351 g_debug( "%s: dialog=%p, toplevel=%p, user_data=%p",
352 thisfn, ( void * ) editor, ( void * ) toplevel, ( void * ) user_data );
353
354 /* initialize the notebook
355 */
356 do_initialize_themed_icons( editor );
357 do_initialize_icons_by_path( editor );
358
359 /* destroy event
360 * this is here that we are going to release our stores
361 */
362 GtkDialog *dialog = GTK_DIALOG( base_window_get_gtk_toplevel( BASE_WINDOW( editor )));
363 g_signal_connect( G_OBJECT( dialog ), "destroy", G_CALLBACK( on_destroy ), NULL );
364
365 #if !GTK_CHECK_VERSION( 2,22,0 )
366 gtk_dialog_set_has_separator( toplevel, FALSE );
367 #endif
368 }
369 }
370
371 /*
372 * initialize the themed icon tab
373 * first, the listview which handles the context list
374 * each context carries a list store which handles the corresponding icons
375 * this store is initialized the first time the context is selected
376 */
377 static void
do_initialize_themed_icons(NactIconChooser * editor)378 do_initialize_themed_icons( NactIconChooser *editor )
379 {
380 GtkTreeView *context_view;
381 GtkTreeModel *context_model;
382 GtkCellRenderer *text_cell;
383 GtkTreeViewColumn *column;
384 GtkIconView *icon_view;
385 GtkTreeSelection *selection;
386 GtkIconTheme *icon_theme;
387 GList *theme_contexts, *it;
388 const gchar *context_label;
389 GtkTreeIter iter;
390
391 context_view = GTK_TREE_VIEW( base_window_get_widget( BASE_WINDOW( editor ), "ThemedTreeView" ));
392 context_model = GTK_TREE_MODEL(
393 gtk_list_store_new( THEME_CONTEXT_N_COLUMN,
394 G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_STRING ));
395 gtk_tree_view_set_model( context_view, context_model );
396 gtk_tree_view_set_headers_visible( context_view, FALSE );
397
398 text_cell = gtk_cell_renderer_text_new();
399 column = gtk_tree_view_column_new_with_attributes(
400 "theme-context",
401 text_cell,
402 "text", THEME_CONTEXT_LABEL_COLUMN,
403 NULL );
404 gtk_tree_view_append_column( context_view, column );
405
406 icon_view = GTK_ICON_VIEW( base_window_get_widget( BASE_WINDOW( editor ), "ThemedIconView" ));
407 gtk_icon_view_set_text_column( icon_view, THEME_ICON_LABEL_COLUMN );
408 gtk_icon_view_set_pixbuf_column( icon_view, THEME_ICON_PIXBUF_COLUMN );
409 gtk_icon_view_set_selection_mode( icon_view, GTK_SELECTION_BROWSE );
410
411 selection = gtk_tree_view_get_selection( context_view );
412 gtk_tree_selection_set_mode( selection, GTK_SELECTION_BROWSE );
413
414 icon_theme = gtk_icon_theme_get_default();
415 theme_contexts = g_list_sort(
416 gtk_icon_theme_list_contexts( icon_theme ), ( GCompareFunc ) g_utf8_collate );
417
418 for( it = theme_contexts ; it ; it = it->next ){
419 context_label = ( const gchar *) it->data;
420 gtk_list_store_append( GTK_LIST_STORE( context_model ), &iter );
421 gtk_list_store_set( GTK_LIST_STORE( context_model ), &iter,
422 THEME_CONTEXT_LABEL_COLUMN, context_label,
423 THEME_CONTEXT_STORE_COLUMN, NULL,
424 -1 );
425 }
426 g_list_foreach( theme_contexts, ( GFunc ) g_free, NULL );
427 g_list_free( theme_contexts );
428
429 g_object_unref( context_model );
430 }
431
432 static void
do_initialize_icons_by_path(NactIconChooser * editor)433 do_initialize_icons_by_path( NactIconChooser *editor )
434 {
435 GtkFileChooser *file_chooser;
436
437 file_chooser = GTK_FILE_CHOOSER( base_window_get_widget( BASE_WINDOW( editor ), "FileChooser" ));
438 gtk_file_chooser_set_action( file_chooser, GTK_FILE_CHOOSER_ACTION_OPEN );
439 gtk_file_chooser_set_select_multiple( file_chooser, FALSE );
440 }
441
442 static void
on_base_initialize_window(NactIconChooser * editor,gpointer user_data)443 on_base_initialize_window( NactIconChooser *editor, gpointer user_data )
444 {
445 static const gchar *thisfn = "nact_icon_chooser_on_base_initialize_window";
446 guint pos;
447 GtkWidget *paned;
448
449 g_return_if_fail( NACT_IS_ICON_CHOOSER( editor ));
450
451 if( !editor->private->dispose_has_run ){
452
453 g_debug( "%s: dialog=%p, user_data=%p", thisfn, ( void * ) editor, ( void * ) user_data );
454
455 pos = na_settings_get_uint( NA_IPREFS_ICON_CHOOSER_PANED, NULL, NULL );
456 if( pos ){
457 paned = base_window_get_widget( BASE_WINDOW( editor ), "IconPaned" );
458 gtk_paned_set_position( GTK_PANED( paned ), pos );
459 }
460
461 /* setup the initial icon
462 */
463 editor->private->current_icon = g_strdup( editor->private->initial_icon );
464 on_current_icon_changed( editor );
465
466 /* fillup the icon stores
467 */
468 fillup_themed_icons( editor );
469 fillup_icons_by_path( editor );
470
471 /* intercept Escape key: we do not quit on Esc.
472 */
473 base_window_signal_connect(
474 BASE_WINDOW( editor ),
475 G_OBJECT( base_window_get_gtk_toplevel( BASE_WINDOW( editor ))),
476 "key-press-event",
477 G_CALLBACK( on_key_pressed_event ));
478
479 /* OK/Cancel buttons
480 */
481 base_window_signal_connect_by_name(
482 BASE_WINDOW( editor ),
483 "CancelButton",
484 "clicked",
485 G_CALLBACK( on_cancel_clicked ));
486
487 base_window_signal_connect_by_name(
488 BASE_WINDOW( editor ),
489 "OKButton",
490 "clicked",
491 G_CALLBACK( on_ok_clicked ));
492 }
493 }
494
495 static void
fillup_themed_icons(NactIconChooser * editor)496 fillup_themed_icons( NactIconChooser *editor )
497 {
498 GtkTreeView *context_view;
499 GtkTreeSelection *selection;
500 GtkTreePath *path;
501 GtkIconView *icon_view;
502
503 icon_view = GTK_ICON_VIEW( base_window_get_widget( BASE_WINDOW( editor ), "ThemedIconView" ));
504 base_window_signal_connect(
505 BASE_WINDOW( editor ),
506 G_OBJECT( icon_view ),
507 "selection-changed",
508 G_CALLBACK( on_themed_icon_changed ));
509
510 /* catch double-click */
511 base_window_signal_connect(
512 BASE_WINDOW( editor ),
513 G_OBJECT( icon_view ),
514 "button-press-event",
515 G_CALLBACK( on_icon_view_button_press_event ));
516
517 context_view = GTK_TREE_VIEW( base_window_get_widget( BASE_WINDOW( editor ), "ThemedTreeView" ));
518 selection = gtk_tree_view_get_selection( context_view );
519 base_window_signal_connect(
520 BASE_WINDOW( editor ),
521 G_OBJECT( selection ),
522 "changed",
523 G_CALLBACK( on_themed_context_changed ));
524
525 path = gtk_tree_path_new_first();
526 gtk_tree_selection_select_path( selection, path );
527 gtk_tree_path_free( path );
528 base_window_signal_connect_by_name(
529 BASE_WINDOW( editor ),
530 "ThemedApplyButton",
531 "clicked",
532 G_CALLBACK( on_themed_apply_button_clicked ));
533 }
534
535 static void
fillup_icons_by_path(NactIconChooser * editor)536 fillup_icons_by_path( NactIconChooser *editor )
537 {
538 GtkFileChooser *file_chooser;
539 gchar *uri;
540
541 file_chooser = GTK_FILE_CHOOSER( base_window_get_widget( BASE_WINDOW( editor ), "FileChooser" ));
542 editor->private->path_preview = gtk_image_new();
543 gtk_file_chooser_set_preview_widget( file_chooser, editor->private->path_preview );
544
545 gtk_file_chooser_unselect_all( file_chooser );
546
547 uri = na_settings_get_string( NA_IPREFS_ICON_CHOOSER_URI, NULL, NULL );
548 if( uri ){
549 gtk_file_chooser_set_current_folder_uri( file_chooser, uri );
550 g_free( uri );
551 } else if( editor->private->current_icon ){
552 gtk_file_chooser_set_filename( file_chooser, editor->private->current_icon );
553 }
554
555 base_window_signal_connect(
556 BASE_WINDOW( editor ),
557 G_OBJECT( file_chooser ),
558 "selection-changed",
559 G_CALLBACK( on_path_selection_changed ));
560
561 base_window_signal_connect(
562 BASE_WINDOW( editor ),
563 G_OBJECT( file_chooser ),
564 "update-preview",
565 G_CALLBACK( on_path_update_preview ));
566
567 base_window_signal_connect_by_name(
568 BASE_WINDOW( editor ),
569 "PathApplyButton",
570 "clicked",
571 G_CALLBACK( on_path_apply_button_clicked ));
572 }
573
574 static void
on_base_show_widgets(NactIconChooser * editor,gpointer user_data)575 on_base_show_widgets( NactIconChooser *editor, gpointer user_data )
576 {
577 static const gchar *thisfn = "nact_icon_chooser_on_base_show_widgets";
578 GtkWidget *about_button;
579
580 g_return_if_fail( NACT_IS_ICON_CHOOSER( editor ));
581
582 if( !editor->private->dispose_has_run ){
583
584 g_debug( "%s: dialog=%p, user_data=%p", thisfn, ( void * ) editor, ( void * ) user_data );
585
586 /* hide about button not used here
587 */
588 about_button = base_window_get_widget( BASE_WINDOW( editor ), "AboutButton" );
589 gtk_widget_hide( about_button );
590 }
591 }
592
593 static void
on_cancel_clicked(GtkButton * button,NactIconChooser * editor)594 on_cancel_clicked( GtkButton *button, NactIconChooser *editor )
595 {
596 GtkWindow *toplevel = base_window_get_gtk_toplevel( BASE_WINDOW( editor ));
597 gtk_dialog_response( GTK_DIALOG( toplevel ), GTK_RESPONSE_CLOSE );
598 }
599
600 static void
on_ok_clicked(GtkButton * button,NactIconChooser * editor)601 on_ok_clicked( GtkButton *button, NactIconChooser *editor )
602 {
603 GtkWindow *toplevel = base_window_get_gtk_toplevel( BASE_WINDOW( editor ));
604 gtk_dialog_response( GTK_DIALOG( toplevel ), GTK_RESPONSE_OK );
605 }
606
607 static void
on_dialog_cancel(BaseDialog * dialog)608 on_dialog_cancel( BaseDialog *dialog )
609 {
610 static const gchar *thisfn = "nact_icon_chooser_on_dialog_cancel";
611 NactIconChooser *editor;
612
613 g_return_if_fail( NACT_IS_ICON_CHOOSER( dialog ));
614
615 editor = NACT_ICON_CHOOSER( dialog );
616
617 if( !editor->private->dispose_has_run ){
618 g_debug( "%s: dialog=%p", thisfn, ( void * ) dialog );
619
620 g_free( editor->private->current_icon );
621 editor->private->current_icon = g_strdup( editor->private->initial_icon );
622 }
623 }
624
625 /*
626 * display at the top of the dialog the icon addressed in @icon
627 * this is this icon which will be returned if the user validates
628 * this dialog
629 */
630 static void
on_current_icon_changed(const NactIconChooser * editor)631 on_current_icon_changed( const NactIconChooser *editor )
632 {
633 GtkImage *image;
634 gchar *icon_label;
635 GtkLabel *label;
636
637 image = GTK_IMAGE( base_window_get_widget( BASE_WINDOW( editor ), "IconImage" ));
638 base_gtk_utils_render( editor->private->current_icon, image, CURRENT_ICON_SIZE );
639
640 if( editor->private->current_icon ){
641 if( g_path_is_absolute( editor->private->current_icon )){
642 icon_label = g_filename_to_utf8( editor->private->current_icon, -1, NULL, NULL, NULL );
643 } else {
644 icon_label = g_strdup( editor->private->current_icon );
645 }
646 label = GTK_LABEL( base_window_get_widget( BASE_WINDOW( editor ), "IconLabel" ));
647 gtk_label_set_label( label, icon_label );
648 g_free( icon_label );
649 }
650 }
651
652 static gboolean
on_destroy(GtkWidget * widget,GdkEvent * event,void * foo)653 on_destroy( GtkWidget *widget, GdkEvent *event, void *foo )
654 {
655 static const gchar *thisfn = "nact_icon_chooser_on_destroy";
656 GtkTreeView *context_view;
657 GtkListStore *context_store;
658 GtkTreeIter context_iter;
659 GtkListStore *icon_store;
660 gchar *context_label;
661
662 g_debug( "%s: widget=%p", thisfn, ( void * ) widget );
663
664 /* clear the various models
665 */
666 context_view = GTK_TREE_VIEW( na_gtk_utils_find_widget_by_name( GTK_CONTAINER( widget ), "ThemedTreeView" ));
667 context_store = GTK_LIST_STORE( gtk_tree_view_get_model( context_view ));
668
669 if( gtk_tree_model_get_iter_first( GTK_TREE_MODEL( context_store ), &context_iter )){
670 while( TRUE ){
671
672 gtk_tree_model_get( GTK_TREE_MODEL( context_store ), &context_iter,
673 THEME_CONTEXT_LABEL_COLUMN, &context_label,
674 THEME_CONTEXT_STORE_COLUMN, &icon_store,
675 -1 );
676 if( icon_store ){
677 g_debug( "%s: context=%s, clearing store=%p", thisfn, context_label, ( void * ) icon_store );
678 gtk_list_store_clear( icon_store );
679 g_object_unref( icon_store );
680 }
681
682 g_free( context_label );
683
684 if( !gtk_tree_model_iter_next( GTK_TREE_MODEL( context_store ), &context_iter )){
685 break;
686 }
687 }
688 }
689
690 gtk_list_store_clear( context_store );
691
692 /* let other handlers get this message */
693 return( FALSE );
694 }
695
696 /*
697 * mouse click on the themed icons icon view
698 */
699 static gboolean
on_icon_view_button_press_event(GtkWidget * widget,GdkEventButton * event,NactIconChooser * editor)700 on_icon_view_button_press_event( GtkWidget *widget, GdkEventButton *event, NactIconChooser *editor )
701 {
702 gboolean stop = FALSE;
703
704 /* double-click of left button
705 * > triggers a 'Apply' action
706 */
707 if( event->type == GDK_2BUTTON_PRESS && event->button == 1 ){
708 on_themed_apply_triggered( editor );
709 stop = TRUE;
710 }
711
712 return( stop );
713 }
714
715 static gboolean
on_key_pressed_event(GtkWidget * widget,GdkEventKey * event,NactIconChooser * editor)716 on_key_pressed_event( GtkWidget *widget, GdkEventKey *event, NactIconChooser *editor )
717 {
718 gboolean stop = FALSE;
719
720 g_return_val_if_fail( NACT_IS_ICON_CHOOSER( editor ), FALSE );
721
722 if( !editor->private->dispose_has_run ){
723
724 /* inhibit Escape key */
725 if( event->keyval == NACT_KEY_Escape ){
726 stop = TRUE;
727 }
728 }
729
730 return( stop );
731 }
732
733 static void
on_themed_context_changed(GtkTreeSelection * selection,NactIconChooser * editor)734 on_themed_context_changed( GtkTreeSelection *selection, NactIconChooser *editor )
735 {
736 GtkTreeModel *model;
737 GtkTreeIter iter;
738 GtkListStore *store;
739 gchar *context, *last_path;
740 GtkTreePath *path;
741 GtkWidget *preview_image, *preview_label;
742
743 if( gtk_tree_selection_get_selected( selection, &model, &iter )){
744 gtk_tree_model_get( model, &iter,
745 THEME_CONTEXT_LABEL_COLUMN, &context,
746 THEME_CONTEXT_STORE_COLUMN, &store,
747 THEME_CONTEXT_LAST_SELECTED_COLUMN, &last_path,
748 -1 );
749
750 if( !store ){
751 store = theme_context_load_icons( editor, context );
752 gtk_list_store_set( GTK_LIST_STORE( model ), &iter, THEME_CONTEXT_STORE_COLUMN, store, -1 );
753 }
754
755 GtkIconView *iconview = GTK_ICON_VIEW( base_window_get_widget( BASE_WINDOW( editor ), "ThemedIconView" ));
756 gtk_icon_view_set_model( iconview, GTK_TREE_MODEL( store ));
757
758 if( last_path ){
759 path = gtk_tree_path_new_from_string( last_path );
760 gtk_icon_view_select_path( iconview, path );
761 gtk_tree_path_free( path );
762
763 } else {
764 preview_image = base_window_get_widget( BASE_WINDOW( editor ), "ThemedIconImage" );
765 gtk_image_set_from_pixbuf( GTK_IMAGE( preview_image ), NULL );
766 preview_label = base_window_get_widget( BASE_WINDOW( editor ), "ThemedIconName" );
767 gtk_label_set_text( GTK_LABEL( preview_label ), "" );
768 }
769
770 g_free( last_path );
771 g_free( context );
772 g_object_unref( store );
773 }
774 }
775
776 static void
on_themed_icon_changed(GtkIconView * icon_view,NactIconChooser * editor)777 on_themed_icon_changed( GtkIconView *icon_view, NactIconChooser *editor )
778 {
779 GList *selected;
780 GtkTreeModel *model;
781 GtkTreeIter iter;
782 gchar *label;
783 GtkWidget *preview_image, *preview_label;
784 GtkTreeView *context_view;
785 GtkListStore *context_store;
786 GtkTreeSelection *context_selection;
787 GtkTreeIter context_iter;
788 gchar *icon_path;
789
790 selected = gtk_icon_view_get_selected_items( icon_view );
791 if( selected ){
792 model = gtk_icon_view_get_model( icon_view );
793
794 if( gtk_tree_model_get_iter( model, &iter, ( GtkTreePath * ) selected->data )){
795 gtk_tree_model_get( model, &iter,
796 THEME_ICON_LABEL_COLUMN, &label,
797 -1 );
798
799 preview_image = base_window_get_widget( BASE_WINDOW( editor ), "ThemedIconImage" );
800 base_gtk_utils_render( label, GTK_IMAGE( preview_image ), PREVIEW_ICON_SIZE );
801 preview_label = base_window_get_widget( BASE_WINDOW( editor ), "ThemedIconName" );
802 gtk_label_set_text( GTK_LABEL( preview_label ), label );
803
804 /* record in context tree view the path to the last selected icon
805 */
806 context_view = GTK_TREE_VIEW( base_window_get_widget( BASE_WINDOW( editor ), "ThemedTreeView" ));
807 context_selection = gtk_tree_view_get_selection( context_view );
808 if( gtk_tree_selection_get_selected( context_selection, ( GtkTreeModel ** ) &context_store, &context_iter )){
809 icon_path = gtk_tree_model_get_string_from_iter( model, &iter );
810 gtk_list_store_set( context_store, &context_iter, THEME_CONTEXT_LAST_SELECTED_COLUMN, icon_path, -1 );
811 g_free( icon_path );
812 }
813
814 g_free( label );
815 }
816
817 g_list_foreach( selected, ( GFunc ) gtk_tree_path_free, NULL );
818 g_list_free( selected );
819 }
820 }
821
822 static void
on_themed_apply_button_clicked(GtkButton * button,NactIconChooser * editor)823 on_themed_apply_button_clicked( GtkButton *button, NactIconChooser *editor )
824 {
825 on_themed_apply_triggered( editor );
826 }
827
828 static void
on_themed_apply_triggered(NactIconChooser * editor)829 on_themed_apply_triggered( NactIconChooser *editor )
830 {
831 GtkWidget *icon_label;
832 const gchar *icon_name;
833
834 icon_label = base_window_get_widget( BASE_WINDOW( editor ), "ThemedIconName" );
835 icon_name = gtk_label_get_text( GTK_LABEL( icon_label ));
836
837 g_free( editor->private->current_icon );
838 editor->private->current_icon = g_strdup( icon_name );
839 on_current_icon_changed( editor );
840 }
841
842 static void
on_path_selection_changed(GtkFileChooser * file_chooser,NactIconChooser * editor)843 on_path_selection_changed( GtkFileChooser *file_chooser, NactIconChooser *editor )
844 {
845 gchar *uri;
846
847 uri = gtk_file_chooser_get_current_folder_uri( file_chooser );
848 if( uri ){
849 na_settings_set_string( NA_IPREFS_ICON_CHOOSER_URI, uri );
850 g_free( uri );
851 }
852 }
853
854 static void
on_path_update_preview(GtkFileChooser * file_chooser,NactIconChooser * editor)855 on_path_update_preview( GtkFileChooser *file_chooser, NactIconChooser *editor )
856 {
857 static const gchar *thisfn = "nact_icon_chooser_on_path_update_preview";
858 char *filename;
859 GdkPixbuf *pixbuf;
860 gboolean have_preview;
861 gint width, height;
862
863 if( !gtk_icon_size_lookup( PREVIEW_ICON_SIZE, &width, &height )){
864 width = PREVIEW_ICON_WIDTH;
865 height = PREVIEW_ICON_WIDTH;
866 }
867
868 have_preview = FALSE;
869 filename = gtk_file_chooser_get_preview_filename( file_chooser );
870 g_debug( "%s: file_chooser=%p, editor=%p, filename=%s",
871 thisfn, ( void * ) file_chooser, ( void * ) editor, filename );
872
873 if( filename ){
874 pixbuf = gdk_pixbuf_new_from_file_at_size( filename, width, height, NULL );
875 have_preview = ( pixbuf != NULL );
876 g_free( filename );
877 }
878
879 if( have_preview ){
880 gtk_image_set_from_pixbuf( GTK_IMAGE( editor->private->path_preview ), pixbuf );
881 g_object_unref( pixbuf );
882 }
883
884 gtk_file_chooser_set_preview_widget_active( file_chooser, TRUE );
885 }
886
887 static void
on_path_apply_button_clicked(GtkButton * button,NactIconChooser * editor)888 on_path_apply_button_clicked( GtkButton *button, NactIconChooser *editor )
889 {
890 GtkFileChooser *file_chooser = GTK_FILE_CHOOSER( base_window_get_widget( BASE_WINDOW( editor ), "FileChooser" ));
891
892 /* this is a filename in the character set specified by the G_FILENAME_ENCODING
893 * environment variable
894 */
895 g_free( editor->private->current_icon );
896 editor->private->current_icon = gtk_file_chooser_get_filename( file_chooser );
897 on_current_icon_changed( editor );
898 }
899
900 static GtkListStore *
theme_context_load_icons(NactIconChooser * editor,const gchar * context)901 theme_context_load_icons( NactIconChooser *editor, const gchar *context )
902 {
903 static const gchar *thisfn = "nact_icon_chooser_theme_context_load_icons";
904 GtkTreeIter iter;
905 GList *ic;
906 GError *error;
907 gint width, height;
908
909 g_debug( "%s: editor=%p, context=%s", thisfn, ( void * ) editor, context );
910
911 GtkIconTheme *icon_theme = gtk_icon_theme_get_default();
912 GtkListStore *store = gtk_list_store_new( THEME_ICON_N_COLUMN, G_TYPE_STRING, GDK_TYPE_PIXBUF );
913
914 GList *icon_list = g_list_sort( gtk_icon_theme_list_icons( icon_theme, context ), ( GCompareFunc ) g_utf8_collate );
915
916 if( !gtk_icon_size_lookup( VIEW_ICON_SIZE, &width, &height )){
917 width = VIEW_ICON_DEFAULT_WIDTH;
918 }
919 g_debug( "%s: width=%d", thisfn, width );
920
921 for( ic = icon_list ; ic ; ic = ic->next ){
922 const gchar *icon_name = ( const gchar * ) ic->data;
923 error = NULL;
924 GdkPixbuf *pixbuf = gtk_icon_theme_load_icon(
925 icon_theme, icon_name, width, GTK_ICON_LOOKUP_GENERIC_FALLBACK, &error );
926 if( error ){
927 g_warning( "%s: %s", thisfn, error->message );
928 g_error_free( error );
929 } else {
930 gtk_list_store_append( store, &iter );
931 gtk_list_store_set( store, &iter,
932 THEME_ICON_LABEL_COLUMN, icon_name,
933 THEME_ICON_PIXBUF_COLUMN, pixbuf,
934 -1 );
935 g_object_unref( pixbuf );
936 }
937 }
938 g_debug( "%s: %d loaded icons in store=%p", thisfn, g_list_length( icon_list ), ( void * ) store );
939 g_list_foreach( icon_list, ( GFunc ) g_free, NULL );
940 g_list_free( icon_list );
941
942 return( store );
943 }
944