1 /*                                                     -*- linux-c -*-
2     Copyright (C) 2007 Tom Szilagyi
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18     $Id: utils_gui.c 1295 2014-05-04 22:07:31Z tszilagyi $
19 */
20 
21 #include <config.h>
22 
23 #include <ctype.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include <glib.h>
29 #include <glib-object.h>
30 #include <gdk/gdk.h>
31 #include <gtk/gtk.h>
32 
33 #include "common.h"
34 #include "i18n.h"
35 #include "options.h"
36 #include "utils.h"
37 #include "utils_gui.h"
38 
39 
40 extern options_t options;
41 
42 #ifdef HAVE_SNDFILE
43 extern char * valid_extensions_sndfile[];
44 #endif /* HAVE_SNDFILE */
45 
46 #ifdef HAVE_MPEG
47 extern char * valid_extensions_mpeg[];
48 #endif /* HAVE_MPEG */
49 
50 #ifdef HAVE_MOD
51 extern char * valid_extensions_mod[];
52 #endif /* HAVE_MOD */
53 
54 GSList * toplevel_windows;
55 
56 typedef struct {
57 	GtkWidget * window;
58 	int flag;
59 } top_win_t;
60 
61 void
unregister_toplevel_window(GtkWidget * window)62 unregister_toplevel_window(GtkWidget * window) {
63 
64 	GSList * node;
65 
66 	for (node = toplevel_windows; node; node = node->next) {
67 
68 		top_win_t * tw = (top_win_t *)node->data;
69 
70 		if (tw->window == window) {
71 			toplevel_windows = g_slist_remove(toplevel_windows, tw);
72 			free(tw);
73 			break;
74 		}
75 	}
76 }
77 
78 void
register_toplevel_window(GtkWidget * window,int flag)79 register_toplevel_window(GtkWidget * window, int flag) {
80 
81 	top_win_t * tw;
82 
83 	if ((tw = (top_win_t *)calloc(1, sizeof(top_win_t))) == NULL) {
84 		fprintf(stderr, "register_toplevel_window: calloc error\n");
85 		return;
86 	}
87 
88 	tw->window = window;
89 	tw->flag = flag;
90 
91 	unregister_toplevel_window(window);
92 	toplevel_windows = g_slist_append(toplevel_windows, tw);
93 }
94 
95 void
toplevel_window_foreach(int flag,void (* callback)(GtkWidget * window))96 toplevel_window_foreach(int flag, void (* callback)(GtkWidget * window)) {
97 
98 	GSList * node;
99 
100 	for (node = toplevel_windows; node; node = node->next) {
101 
102 		top_win_t * tw = (top_win_t *)node->data;
103 
104 		if (tw->flag & flag) {
105 			callback(tw->window);
106 		}
107 	}
108 }
109 
110 
111 #ifdef HAVE_SYSTRAY
112 int systray_semaphore = 0;
113 
114 int
get_systray_semaphore()115 get_systray_semaphore() {
116 
117 	return systray_semaphore;
118 }
119 
120 #endif /* HAVE_SYSTRAY */
121 
122 
123 gint
aqualung_dialog_run(GtkDialog * dialog)124 aqualung_dialog_run(GtkDialog * dialog) {
125 #ifdef HAVE_SYSTRAY
126 	int ret;
127 	systray_semaphore++;
128 	gtk_window_present(GTK_WINDOW(dialog));
129 	ret = gtk_dialog_run(dialog);
130 	systray_semaphore--;
131 	return ret;
132 #else
133 	gtk_window_present(GTK_WINDOW(dialog));
134 	return gtk_dialog_run(dialog);
135 #endif /* HAVE_SYSTRAY */
136 }
137 
138 typedef struct {
139 	GSourceFunc func;
140 	gpointer data;
141 	GDestroyNotify destroy;
142 } threads_dispatch_t;
143 
144 static gboolean
threads_dispatch(gpointer data)145 threads_dispatch(gpointer data) {
146 
147 	threads_dispatch_t * dispatch = data;
148 	gboolean ret = FALSE;
149 
150 	GDK_THREADS_ENTER();
151 
152 	if (!g_source_is_destroyed(g_main_current_source())) {
153 		ret = dispatch->func(dispatch->data);
154 	}
155 
156 	GDK_THREADS_LEAVE();
157 
158 	return ret;
159 }
160 
161 static void
threads_dispatch_free(gpointer data)162 threads_dispatch_free(gpointer data) {
163 
164 	threads_dispatch_t * dispatch = data;
165 
166 	if (dispatch->destroy && dispatch->data) {
167 		dispatch->destroy(dispatch->data);
168 	}
169 
170 	g_slice_free(threads_dispatch_t, data);
171 }
172 
173 guint
aqualung_idle_add(GSourceFunc function,gpointer data)174 aqualung_idle_add(GSourceFunc function, gpointer data) {
175 
176 	threads_dispatch_t * dispatch;
177 
178 	dispatch = g_slice_new(threads_dispatch_t);
179 	dispatch->func = function;
180 	dispatch->data = data;
181 	dispatch->destroy = NULL;
182 
183 	return g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
184 			       threads_dispatch,
185 			       dispatch,
186 			       threads_dispatch_free);
187 }
188 
189 guint
aqualung_timeout_add(guint interval,GSourceFunc function,gpointer data)190 aqualung_timeout_add(guint interval, GSourceFunc function, gpointer data) {
191 
192 	threads_dispatch_t * dispatch;
193 
194 	dispatch = g_slice_new(threads_dispatch_t);
195 	dispatch->func = function;
196 	dispatch->data = data;
197 	dispatch->destroy = NULL;
198 
199 	if (interval % 1000 == 0) {
200 		return g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
201 						  interval / 1000,
202 						  threads_dispatch,
203 						  dispatch,
204 						  threads_dispatch_free);
205 	}
206 	return g_timeout_add_full(G_PRIORITY_DEFAULT,
207 				  interval,
208 				  threads_dispatch,
209 				  dispatch,
210 				  threads_dispatch_free);
211 }
212 
213 void
aqualung_tooltips_set_enabled(gboolean enabled)214 aqualung_tooltips_set_enabled(gboolean enabled) {
215 
216 	GtkSettings * settings = gtk_settings_get_default();
217 
218 	if (settings != NULL) {
219 		g_object_set(settings, "gtk-enable-tooltips", enabled, NULL);
220 	}
221 
222 }
223 
224 #ifdef HAVE_SYSTRAY
225 void
aqualung_status_icon_set_tooltip_text(GtkStatusIcon * icon,const gchar * text)226 aqualung_status_icon_set_tooltip_text(GtkStatusIcon * icon, const gchar * text) {
227 	gtk_status_icon_set_tooltip_text(icon, text);
228 }
229 #endif /* HAVE_SYSTRAY */
230 
231 void
aqualung_widget_set_tooltip_text(GtkWidget * widget,const gchar * text)232 aqualung_widget_set_tooltip_text(GtkWidget * widget, const gchar * text) {
233 
234 	gtk_widget_set_tooltip_text(widget, text);
235 
236 }
237 
238 /* create button with stock item
239  *
240  * in: label - label for button        (label=NULL  to disable label, label=-1 to disable button relief)
241  *     stock - stock icon identifier
242  */
243 GtkWidget*
gui_stock_label_button(gchar * label,const gchar * stock)244 gui_stock_label_button(gchar *label, const gchar *stock) {
245 
246 	GtkWidget *button;
247 	GtkWidget *alignment;
248 	GtkWidget *hbox;
249 	GtkWidget *image;
250 
251 	button = g_object_new (GTK_TYPE_BUTTON, "visible", TRUE, NULL);
252 
253         if (label== (gchar *)-1) {
254                 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
255         }
256 
257 	alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
258 	hbox = gtk_hbox_new (FALSE, 2);
259 	gtk_container_add (GTK_CONTAINER (alignment), hbox);
260 
261 	image = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_BUTTON);
262 
263         if (image) {
264 		gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0);
265         }
266 
267         if (label != NULL && label != (gchar *)-1) {
268 		gtk_box_pack_start (GTK_BOX (hbox),
269 		g_object_new (GTK_TYPE_LABEL, "label", label, "use_underline", TRUE, NULL),
270 		FALSE, TRUE, 0);
271         }
272 
273 	gtk_widget_show_all (alignment);
274 	gtk_container_add (GTK_CONTAINER (button), alignment);
275 
276 	return button;
277 }
278 
279 
280 void
assign_etf_fc_filters(GtkFileChooser * fc)281 assign_etf_fc_filters(GtkFileChooser *fc) {
282 
283         GtkFileFilter *filter_1, *filter_2;
284 
285         /* all files filter */
286         filter_1 = gtk_file_filter_new();
287         gtk_file_filter_add_pattern(filter_1, "*");
288         gtk_file_filter_set_name(GTK_FILE_FILTER(filter_1), _("All Files"));
289         gtk_file_chooser_add_filter(fc, filter_1);
290 
291         /* music store files filter */
292         filter_2 = gtk_file_filter_new();
293         gtk_file_filter_add_pattern(filter_2, "*.[lL][uU][aA]");
294         gtk_file_filter_set_name(GTK_FILE_FILTER(filter_2), _("Lua Extension Files (*.lua)"));
295         gtk_file_chooser_add_filter(fc, filter_2);
296 
297         gtk_file_chooser_set_filter(fc, filter_2);
298 }
299 
300 
301 void
assign_store_fc_filters(GtkFileChooser * fc)302 assign_store_fc_filters(GtkFileChooser *fc) {
303 
304         GtkFileFilter *filter_1, *filter_2;
305 
306         /* all files filter */
307         filter_1 = gtk_file_filter_new();
308         gtk_file_filter_add_pattern(filter_1, "*");
309         gtk_file_filter_set_name(GTK_FILE_FILTER(filter_1), _("All Files"));
310         gtk_file_chooser_add_filter(fc, filter_1);
311 
312         /* music store files filter */
313         filter_2 = gtk_file_filter_new();
314         gtk_file_filter_add_pattern(filter_2, "*.[xX][mM][lL]");
315         gtk_file_filter_set_name(GTK_FILE_FILTER(filter_2), _("Music Store Files (*.xml)"));
316         gtk_file_chooser_add_filter(fc, filter_2);
317 
318         gtk_file_chooser_set_filter(fc, filter_2);
319 }
320 
321 
322 void
assign_playlist_fc_filters(GtkFileChooser * fc)323 assign_playlist_fc_filters(GtkFileChooser *fc) {
324 
325         gchar *file_filters[] = {
326                 _("Aqualung playlist (*.xml)"),      "*.[xX][mM][lL]",
327                 _("MP3 Playlist (*.m3u)"),           "*.[mM]3[uU]",
328                 _("Multimedia Playlist (*.pls)"),    "*.[pP][lL][sS]",
329         };
330 
331         gint i, len;
332         GtkFileFilter *filter_1, *filter_2, *filter_3;
333 
334         len = sizeof(file_filters)/sizeof(gchar*)/2;
335 
336         /* all files filter */
337         filter_1 = gtk_file_filter_new();
338         gtk_file_filter_add_pattern(filter_1, "*");
339         gtk_file_filter_set_name(GTK_FILE_FILTER(filter_1), _("All Files"));
340         gtk_file_chooser_add_filter(fc, filter_1);
341 
342         /* all playlist files filter */
343         filter_2 = gtk_file_filter_new();
344 
345         for (i = 0; i < len; i++) {
346                 gtk_file_filter_add_pattern(filter_2, file_filters[2*i+1]);
347         }
348 
349         gtk_file_filter_set_name(GTK_FILE_FILTER(filter_2), _("All Playlist Files"));
350         gtk_file_chooser_add_filter(fc, filter_2);
351         gtk_file_chooser_set_filter(fc, filter_2);
352 
353         /* single extensions */
354         for (i = 0; i < len; i++) {
355 
356                 filter_3 = gtk_file_filter_new();
357                 gtk_file_filter_add_pattern(filter_3, file_filters[2*i+1]);
358                 gtk_file_filter_set_name(GTK_FILE_FILTER(filter_3), file_filters[2*i]);
359                 gtk_file_chooser_add_filter(fc, filter_3);
360         }
361 }
362 
363 
364 void
build_filter_from_extensions(GtkFileFilter * f1,GtkFileFilter * f2,char * extensions[])365 build_filter_from_extensions(GtkFileFilter * f1, GtkFileFilter * f2, char * extensions[]) {
366 
367 	int i, j, k;
368 
369 	for (i = 0; extensions[i]; i++) {
370 		char buf[32];
371 		buf[0] = '*';
372 		buf[1] = '.';
373 		k = 2;
374 		for (j = 0; extensions[i][j]; j++) {
375 			if (isalpha(extensions[i][j]) && k < 28) {
376 				buf[k++] = '[';
377 				buf[k++] = tolower(extensions[i][j]);
378 				buf[k++] = toupper(extensions[i][j]);
379 				buf[k++] = ']';
380 			} else if (k < 31) {
381 				buf[k++] = extensions[i][j];
382 			}
383 		}
384 		buf[k] = '\0';
385 		gtk_file_filter_add_pattern(f1, buf);
386 		gtk_file_filter_add_pattern(f2, buf);
387 	}
388 }
389 
390 
391 void
assign_audio_fc_filters(GtkFileChooser * fc)392 assign_audio_fc_filters(GtkFileChooser * fc) {
393 
394         GtkFileFilter * filter;
395         GtkFileFilter * filter_all;
396 
397         filter = gtk_file_filter_new();
398         gtk_file_filter_set_name(filter, _("All Files"));
399         gtk_file_filter_add_pattern(filter, "*");
400         gtk_file_chooser_add_filter(fc, filter);
401 
402         filter_all = gtk_file_filter_new();
403         gtk_file_filter_set_name(filter_all, _("All Audio Files"));
404         gtk_file_chooser_add_filter(fc, filter_all);
405         gtk_file_chooser_set_filter(fc, filter_all);
406 
407 #ifdef HAVE_SNDFILE
408         filter = gtk_file_filter_new();
409         gtk_file_filter_set_name(filter, _("Sound Files (*.wav, *.aiff, *.au, ...)"));
410 	build_filter_from_extensions(filter, filter_all, valid_extensions_sndfile);
411         gtk_file_chooser_add_filter(fc, filter);
412 #endif /* HAVE_SNDFILE */
413 
414 #ifdef HAVE_FLAC
415         filter = gtk_file_filter_new();
416         gtk_file_filter_set_name(filter, _("Free Lossless Audio Codec (*.flac)"));
417         gtk_file_filter_add_pattern(filter, "*.[fF][lL][aA][cC]");
418         gtk_file_filter_add_pattern(filter_all, "*.[fF][lL][aA][cC]");
419         gtk_file_chooser_add_filter(fc, filter);
420 #endif /* HAVE_FLAC */
421 
422 #ifdef HAVE_MPEG
423         filter = gtk_file_filter_new();
424         gtk_file_filter_set_name(filter, _("MPEG Audio (*.mp3, *.mpa, *.mpega, ...)"));
425 	build_filter_from_extensions(filter, filter_all, valid_extensions_mpeg);
426         gtk_file_chooser_add_filter(fc, filter);
427 #endif /* HAME_MPEG */
428 
429 #ifdef HAVE_VORBIS
430         filter = gtk_file_filter_new();
431         gtk_file_filter_set_name(filter, _("Ogg Vorbis (*.ogg)"));
432         gtk_file_filter_add_pattern(filter, "*.[oO][gG][gG]");
433         gtk_file_filter_add_pattern(filter_all, "*.[oO][gG][gG]");
434         gtk_file_chooser_add_filter(fc, filter);
435 #endif /* HAVE_VORBIS */
436 
437 #ifdef HAVE_SPEEX
438         filter = gtk_file_filter_new();
439         gtk_file_filter_set_name(filter, _("Ogg Speex (*.spx)"));
440         gtk_file_filter_add_pattern(filter, "*.[sS][pP][xX]");
441         gtk_file_filter_add_pattern(filter_all, "*.[sS][pP][xX]");
442         gtk_file_chooser_add_filter(fc, filter);
443 #endif /* HAVE_SPEEX */
444 
445 #ifdef HAVE_MPC
446         filter = gtk_file_filter_new();
447         gtk_file_filter_set_name(filter, _("Musepack (*.mpc)"));
448         gtk_file_filter_add_pattern(filter, "*.[mM][pP][cC]");
449         gtk_file_filter_add_pattern(filter_all, "*.[mM][pP][cC]");
450         gtk_file_chooser_add_filter(fc, filter);
451 #endif /* HAVE_MPC */
452 
453 #ifdef HAVE_MAC
454         filter = gtk_file_filter_new();
455         gtk_file_filter_set_name(filter, _("Monkey's Audio Codec (*.ape)"));
456         gtk_file_filter_add_pattern(filter, "*.[aA][pP][eE]");
457         gtk_file_filter_add_pattern(filter_all, "*.[aA][pP][eE]");
458         gtk_file_chooser_add_filter(fc, filter);
459 #endif /* HAVE_MAC */
460 
461 #ifdef HAVE_MOD
462         filter = gtk_file_filter_new();
463         gtk_file_filter_set_name(filter, _("Modules (*.xm, *.mod, *.it, *.s3m, ...)"));
464 	build_filter_from_extensions(filter, filter_all, valid_extensions_mod);
465         gtk_file_chooser_add_filter(fc, filter);
466 #endif /* HAVE_MOD */
467 
468 #if defined(HAVE_MOD) && (defined(HAVE_LIBZ) || defined(HAVE_LIBBZ2))
469         filter = gtk_file_filter_new();
470 #if defined(HAVE_LIBZ) && defined(HAVE_LIBBZ2)
471         gtk_file_filter_set_name(filter, _("Compressed modules (*.gz, *.bz2)"));
472 #elif defined(HAVE_LIBZ)
473         gtk_file_filter_set_name(filter, _("Compressed modules (*.gz)"));
474 #elif defined(HAVE_LIBBZ2)
475         gtk_file_filter_set_name(filter, _("Compressed modules (*.bz2)"));
476 #endif /* HAVE_LIBZ, HAVE_LIBBZ2 */
477 
478 #ifdef HAVE_LIBZ
479         gtk_file_filter_add_pattern(filter, "*.[gG][zZ]");
480         gtk_file_filter_add_pattern(filter_all, "*.[gG][zZ]");
481 #endif /* HAVE_LIBZ */
482 #ifdef HAVE_LIBBZ2
483         gtk_file_filter_add_pattern(filter, "*.[bB][zZ]2");
484         gtk_file_filter_add_pattern(filter_all, "*.[bB][zZ]2");
485 #endif /* HAVE_LIBBZ2 */
486         gtk_file_chooser_add_filter(fc, filter);
487 #endif /* (HAVE_MOD && HAVE LIBZ) */
488 
489 #ifdef HAVE_WAVPACK
490         filter = gtk_file_filter_new();
491         gtk_file_filter_set_name(filter, _("WavPack (*.wv)"));
492         gtk_file_filter_add_pattern(filter, "*.[wW][vV]");
493         gtk_file_filter_add_pattern(filter_all, "*.[wW][vV]");
494         gtk_file_chooser_add_filter(fc, filter);
495 #endif /* HAVE_WAVPACK */
496 
497 #ifdef HAVE_LAVC
498         filter = gtk_file_filter_new();
499         gtk_file_filter_set_name(filter, _("LAVC audio/video files"));
500 	{
501 		char * valid_ext_lavc[] = {
502 			"aac", "ac3", "asf", "avi", "mpeg", "mpg", "mp3", "ra",
503 			"wav", "wma", "wv", NULL };
504 		build_filter_from_extensions(filter, filter_all, valid_ext_lavc);
505 	}
506         gtk_file_chooser_add_filter(fc, filter);
507 #endif /* HAVE_LAVC */
508 }
509 
510 
511 void
assign_fc_filters(GtkFileChooser * fc,int filter)512 assign_fc_filters(GtkFileChooser * fc, int filter) {
513 
514         gtk_widget_realize(GTK_WIDGET(fc));
515 
516 	if (filter == FILE_CHOOSER_FILTER_AUDIO) {
517 		assign_audio_fc_filters(fc);
518 	}
519 	if (filter == FILE_CHOOSER_FILTER_PLAYLIST) {
520 		assign_playlist_fc_filters(fc);
521 	}
522 	if (filter == FILE_CHOOSER_FILTER_STORE) {
523 		assign_store_fc_filters(fc);
524 	}
525 	if (filter == FILE_CHOOSER_FILTER_ETF) {
526 		assign_etf_fc_filters(fc);
527 	}
528 }
529 
530 GSList *
file_chooser(char * title,GtkWidget * parent,GtkFileChooserAction action,int filter,gint multiple,char * destpath)531 file_chooser(char * title, GtkWidget * parent, GtkFileChooserAction action, int filter,
532 	     gint multiple, char * destpath) {
533 
534         GtkWidget * dialog;
535 	GSList * files = NULL;
536 
537         dialog = gtk_file_chooser_dialog_new(title,
538                                              GTK_WINDOW(parent),
539                                              action,
540 					     (action == GTK_FILE_CHOOSER_ACTION_SAVE) ? GTK_STOCK_SAVE : GTK_STOCK_OPEN,
541 					     GTK_RESPONSE_ACCEPT,
542                                              GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
543                                              NULL);
544 
545         gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
546         gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), multiple);
547         gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog), destpath);
548 
549 	if (action == GTK_FILE_CHOOSER_ACTION_SAVE) {
550 		char * bname = g_path_get_basename(destpath);
551 		gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), bname);
552 		g_free(bname);
553 	}
554 
555         gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
556 	assign_fc_filters(GTK_FILE_CHOOSER(dialog), filter);
557 
558 	if (options.show_hidden) {
559 		gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE);
560 	}
561 
562         if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
563 
564 		strncpy(destpath,
565 			gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)),
566 			MAXLEN-1);
567 
568 		files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
569         }
570 
571         gtk_widget_destroy(dialog);
572 
573 	return files;
574 }
575 
576 void
file_chooser_with_entry(char * title,GtkWidget * parent,GtkFileChooserAction action,int filter,GtkWidget * entry,char * destpath)577 file_chooser_with_entry(char * title, GtkWidget * parent, GtkFileChooserAction action, int filter,
578 			GtkWidget * entry, char * destpath) {
579 
580         GtkWidget * dialog;
581 	const gchar * selected_filename = gtk_entry_get_text(GTK_ENTRY(entry));
582 	char path[MAXLEN];
583 
584 	path[0] = '\0';
585 
586         dialog = gtk_file_chooser_dialog_new(title,
587                                              GTK_WINDOW(parent),
588 					     action,
589                                              GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT,
590                                              GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
591                                              NULL);
592 
593 	if (options.show_hidden) {
594 		gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), options.show_hidden);
595 	}
596 
597         if (strlen(selected_filename)) {
598       		char * filename = g_filename_from_utf8(selected_filename, -1, NULL, NULL, NULL);
599 
600 		if (filename == NULL) {
601 			gtk_widget_destroy(dialog);
602 			return;
603 		}
604 
605 		normalize_filename(filename, path);
606 		g_free(filename);
607 	} else {
608 		strncpy(path, destpath, MAXLEN-1);
609 	}
610 
611 	gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), path);
612 
613 	if (action == GTK_FILE_CHOOSER_ACTION_SAVE) {
614 		char * bname = g_path_get_basename(path);
615 		gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), bname);
616 		g_free(bname);
617 	}
618 
619         gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
620         gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
621 	assign_fc_filters(GTK_FILE_CHOOSER(dialog), filter);
622 
623         if (aqualung_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
624 
625 		char * utf8;
626 
627                 selected_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
628 		utf8 = g_filename_to_utf8(selected_filename, -1, NULL, NULL, NULL);
629 
630 		if (utf8 == NULL) {
631 			gtk_widget_destroy(dialog);
632 		}
633 
634 		gtk_entry_set_text(GTK_ENTRY(entry), utf8);
635 
636                 strncpy(destpath, selected_filename, MAXLEN-1);
637 		g_free(utf8);
638         }
639 
640         gtk_widget_destroy(dialog);
641 }
642 
643 int
message_dialog(char * title,GtkWidget * parent,GtkMessageType type,GtkButtonsType buttons,GtkWidget * extra,char * text,...)644 message_dialog(char * title, GtkWidget * parent, GtkMessageType type, GtkButtonsType buttons,
645 	       GtkWidget * extra, char * text, ...) {
646 
647 	GtkWidget * dialog;
648 	va_list args;
649 	gchar * msg = NULL;
650 	int res;
651 
652 
653 	va_start(args, text);
654 	msg = g_strdup_vprintf(text, args);
655 	va_end(args);
656 
657 	dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
658 					GTK_DIALOG_DESTROY_WITH_PARENT,
659 					type,
660 					buttons,
661 					"%s",
662 					msg);
663 	g_free(msg);
664 
665 	if (extra != NULL) {
666 		gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
667 				   extra, FALSE, FALSE, 5);
668 		gtk_widget_show_all(extra);
669 	}
670 
671 	gtk_window_set_title(GTK_WINDOW(dialog), title);
672 	res = aqualung_dialog_run(GTK_DIALOG(dialog));
673 	gtk_widget_destroy(dialog);
674 
675 	return res;
676 }
677 
678 void
insert_label_entry(GtkWidget * table,char * ltext,GtkWidget ** entry,char * etext,int y1,int y2,gboolean editable)679 insert_label_entry(GtkWidget * table, char * ltext, GtkWidget ** entry, char * etext,
680 		   int y1, int y2, gboolean editable) {
681 
682 	GtkWidget * label;
683 	GtkWidget * hbox;
684 
685 	label = gtk_label_new(ltext);
686         hbox = gtk_hbox_new(FALSE, 0);
687         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
688 	gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, y1, y2, GTK_FILL, GTK_FILL, 5, 5);
689 
690         *entry = gtk_entry_new();
691         gtk_entry_set_max_length(GTK_ENTRY(*entry), MAXLEN-1);
692         gtk_editable_set_editable(GTK_EDITABLE (*entry), editable ? TRUE : FALSE);
693 	if (etext != NULL) {
694 		gtk_entry_set_text(GTK_ENTRY(*entry), etext);
695 	}
696 	gtk_table_attach(GTK_TABLE(table), *entry, 1, 2, y1, y2,
697 			 GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 5);
698 }
699 
700 void
insert_label_entry_button(GtkWidget * table,char * ltext,GtkWidget ** entry,char * etext,GtkWidget * button,int y1,int y2)701 insert_label_entry_button(GtkWidget * table, char * ltext, GtkWidget ** entry, char * etext,
702 			  GtkWidget * button, int y1, int y2) {
703 
704 	GtkWidget * label;
705 	GtkWidget * hbox;
706 
707 	label = gtk_label_new(ltext);
708 	hbox = gtk_hbox_new(FALSE, 0);
709 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
710 	gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, y1, y2, GTK_FILL, GTK_FILL, 5, 5);
711 
712 	hbox = gtk_hbox_new(FALSE, 0);
713 	gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, y1, y2,
714 			 GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 5);
715 
716 	*entry = gtk_entry_new();
717 	gtk_entry_set_max_length(GTK_ENTRY(*entry), MAXLEN-1);
718 	if (etext != NULL) {
719 		gtk_entry_set_text(GTK_ENTRY(*entry), etext);
720 	}
721 	gtk_box_pack_start(GTK_BOX(hbox), *entry, TRUE, TRUE, 0);
722 
723 	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 5);
724 }
725 
726 void
insert_label_entry_browse(GtkWidget * table,char * ltext,GtkWidget ** entry,char * etext,int y1,int y2,void (* browse_cb)(GtkButton * button,gpointer data))727 insert_label_entry_browse(GtkWidget * table, char * ltext, GtkWidget ** entry, char * etext,
728 			  int y1, int y2,
729 			  void (* browse_cb)(GtkButton * button, gpointer data)) {
730 
731 	GtkWidget * button = gui_stock_label_button(_("_Browse..."), GTK_STOCK_OPEN);
732 	insert_label_entry_button(table, ltext, entry, etext, button, y1, y2);
733 	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(browse_cb), *entry);
734 }
735 
736 void
insert_label_progbar_button(GtkWidget * table,char * ltext,GtkWidget ** progbar,GtkWidget * button,int y1,int y2)737 insert_label_progbar_button(GtkWidget * table, char * ltext, GtkWidget ** progbar,
738 			    GtkWidget * button, int y1, int y2) {
739 
740 	GtkWidget * label;
741 	GtkWidget * hbox;
742 
743 	label = gtk_label_new(ltext);
744 	hbox = gtk_hbox_new(FALSE, 0);
745 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
746 	gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, y1, y2, GTK_FILL, GTK_FILL, 5, 5);
747 
748 	hbox = gtk_hbox_new(FALSE, 0);
749 	gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, y1, y2,
750 			 GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 5);
751 
752 	*progbar = gtk_progress_bar_new();
753 	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(*progbar), 0.0f);
754 	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(*progbar), "0%");
755 	gtk_box_pack_start(GTK_BOX(hbox), *progbar, TRUE, TRUE, 0);
756 
757 	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 5);
758 }
759 
760 void
insert_label_spin(GtkWidget * table,char * ltext,GtkWidget ** spin,int spinval,int y1,int y2)761 insert_label_spin(GtkWidget * table, char * ltext, GtkWidget ** spin, int spinval,
762 		   int y1, int y2) {
763 
764 	GtkWidget * label;
765 	GtkWidget * hbox;
766 
767 	label = gtk_label_new(ltext);
768         hbox = gtk_hbox_new(FALSE, 0);
769         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
770 	gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, y1, y2, GTK_FILL, GTK_FILL, 5, 5);
771 
772 	*spin = gtk_spin_button_new_with_range(YEAR_MIN, YEAR_MAX, 1);
773 	gtk_spin_button_set_value(GTK_SPIN_BUTTON(*spin), spinval);
774         gtk_table_attach(GTK_TABLE(table), *spin, 1, 2, y1, y2,
775                          GTK_EXPAND | GTK_FILL, GTK_FILL, 2, 5);
776 }
777 
778 void
insert_label_spin_with_limits(GtkWidget * table,char * ltext,GtkWidget ** spin,double spinval,double min,double max,int y1,int y2)779 insert_label_spin_with_limits(GtkWidget * table, char * ltext, GtkWidget ** spin, double spinval,
780 			      double min, double max, int y1, int y2) {
781 
782 
783 	insert_label_spin(table, ltext, spin, spinval, y1, y2);
784 	gtk_spin_button_set_range(GTK_SPIN_BUTTON(*spin), min, max);
785 	gtk_spin_button_set_value(GTK_SPIN_BUTTON(*spin), spinval);
786 }
787 
788 
789 void
set_option_from_toggle(GtkWidget * widget,int * opt)790 set_option_from_toggle(GtkWidget * widget, int * opt) {
791 
792 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
793 		*opt = 1;
794 	} else {
795 		*opt = 0;
796 	}
797 }
798 
799 void
set_option_from_combo(GtkWidget * widget,int * opt)800 set_option_from_combo(GtkWidget * widget, int * opt) {
801 
802 	*opt = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
803 }
804 
805 void
set_option_from_spin(GtkWidget * widget,int * opt)806 set_option_from_spin(GtkWidget * widget, int * opt) {
807 
808 	*opt = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));
809 }
810 
811 void
set_option_from_entry(GtkWidget * widget,char * opt,int n)812 set_option_from_entry(GtkWidget * widget, char * opt, int n) {
813 
814 	strncpy(opt, gtk_entry_get_text(GTK_ENTRY(widget)), n-1);
815 }
816 
817 void
set_option_bit_from_toggle(GtkWidget * toggle,int * opt,int bit)818 set_option_bit_from_toggle(GtkWidget * toggle, int * opt, int bit) {
819 
820 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle))) {
821 		*opt |= bit;
822 	} else {
823 		*opt &= ~bit;
824 	}
825 }
826 
827 /* Functions to traverse leafs of a generic GtkTreeModel
828  * (used by file info dialog's prev/next file feature)
829  */
830 gboolean
tree_model_leaf_iter(GtkTreeModel * model,GtkTreeIter * iter,gboolean last,GtkTreeIter * out)831 tree_model_leaf_iter(GtkTreeModel * model, GtkTreeIter * iter, gboolean last, GtkTreeIter * out) {
832 	if (gtk_tree_model_iter_has_child(model, iter)) {
833 		GtkTreeIter child_iter;
834 		gint nth = 0;
835 		if (last) {
836 			nth = gtk_tree_model_iter_n_children(model, iter) - 1;
837 		}
838 		if (!gtk_tree_model_iter_nth_child(model, &child_iter, iter, nth)) {
839 			return FALSE;
840 		} else {
841 			return tree_model_leaf_iter(model, &child_iter, last, out);
842 		}
843 	} else {
844 		*out = *iter;
845 		return TRUE;
846 	}
847 }
848 
849 gboolean
tree_model_next_iter(GtkTreeModel * model,GtkTreeIter * iter,GtkTreeIter * next,int mindepth)850 tree_model_next_iter(GtkTreeModel * model, GtkTreeIter * iter, GtkTreeIter * next, int mindepth) {
851 	GtkTreeIter copy = *iter;
852 	if (gtk_tree_model_iter_next(model, &copy)) {
853 		*iter = copy;
854 		return tree_model_leaf_iter(model, iter, FALSE, next);
855 	} else {
856 		GtkTreeIter parent_iter;
857 		if (!gtk_tree_model_iter_parent(model, &parent_iter, iter)) {
858 			return FALSE;
859 		} else {
860 			GtkTreePath * path = gtk_tree_model_get_path(model, &parent_iter);
861 			if (gtk_tree_path_get_depth(path) <= mindepth) {
862 				gtk_tree_path_free(path);
863 				return FALSE;
864 			}
865 			gtk_tree_path_free(path);
866 			return tree_model_next_iter(model, &parent_iter, next, mindepth);
867 		}
868 	}
869 }
870 
871 gboolean
tree_model_prev_iter(GtkTreeModel * model,GtkTreeIter * iter,GtkTreeIter * prev,int mindepth)872 tree_model_prev_iter(GtkTreeModel * model, GtkTreeIter * iter, GtkTreeIter * prev, int mindepth) {
873 	GtkTreePath * path = gtk_tree_model_get_path(model, iter);
874 	if (gtk_tree_path_prev(path)) {
875 		if (!gtk_tree_model_get_iter(model, iter, path)) {
876 			gtk_tree_path_free(path);
877 			return FALSE;
878 		} else {
879 			gtk_tree_path_free(path);
880 			return tree_model_leaf_iter(model, iter, TRUE, prev);
881 		}
882 	} else {
883 		GtkTreeIter parent_iter;
884 		gtk_tree_path_free(path);
885 		if (!gtk_tree_model_iter_parent(model, &parent_iter, iter)) {
886 			return FALSE;
887 		} else {
888 			path = gtk_tree_model_get_path(model, &parent_iter);
889 			if (gtk_tree_path_get_depth(path) <= mindepth) {
890 				gtk_tree_path_free(path);
891 				return FALSE;
892 			}
893 			gtk_tree_path_free(path);
894 			return tree_model_prev_iter(model, &parent_iter, prev, mindepth);
895 		}
896 	}
897 }
898 
899 // vim: shiftwidth=8:tabstop=8:softtabstop=8:
900 
901