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