Merge branch 'master' of http://www.geeqie.org/git/geeqie
[geeqie.git] / src / preferences.c
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "preferences.h"
24
25 #include "bar_exif.h"
26 #include "bar_keywords.h"
27 #include "cache.h"
28 #include "cache_maint.h"
29 #include "dnd.h"
30 #include "editors.h"
31 #include "exif.h"
32 #include "filedata.h"
33 #include "filefilter.h"
34 #include "fullscreen.h"
35 #include "image.h"
36 #include "image-overlay.h"
37 #include "color-man.h"
38 #include "img-view.h"
39 #include "layout_config.h"
40 #include "layout_util.h"
41 #include "metadata.h"
42 #include "osd.h"
43 #include "pixbuf_util.h"
44 #include "slideshow.h"
45 #include "toolbar.h"
46 #include "trash.h"
47 #include "utilops.h"
48 #include "ui_fileops.h"
49 #include "ui_misc.h"
50 #include "ui_spinner.h"
51 #include "ui_tabcomp.h"
52 #include "ui_utildlg.h"
53 #include "window.h"
54 #include "zonedetect.h"
55
56 #ifdef HAVE_LCMS
57 #ifdef HAVE_LCMS2
58 #include <lcms2.h>
59 #else
60 #include <lcms.h>
61 #endif
62 #endif
63
64 #define EDITOR_NAME_MAX_LENGTH 32
65 #define EDITOR_COMMAND_MAX_LENGTH 1024
66
67 static void image_overlay_set_text_colours();
68
69 GtkWidget *keyword_text;
70 static void config_tab_keywords_save();
71
72 typedef struct _ThumbSize ThumbSize;
73 struct _ThumbSize
74 {
75         gint w;
76         gint h;
77 };
78
79 static ThumbSize thumb_size_list[] =
80 {
81         { 24, 24 },
82         { 32, 32 },
83         { 48, 48 },
84         { 64, 64 },
85         { 96, 72 },
86         { 96, 96 },
87         { 128, 96 },
88         { 128, 128 },
89         { 160, 120 },
90         { 160, 160 },
91         { 192, 144 },
92         { 192, 192 },
93         { 256, 192 },
94         { 256, 256 }
95 };
96
97 enum {
98         FE_ENABLE,
99         FE_EXTENSION,
100         FE_DESCRIPTION,
101         FE_CLASS,
102         FE_WRITABLE,
103         FE_ALLOW_SIDECAR
104 };
105
106 enum {
107         AE_ACTION,
108         AE_KEY,
109         AE_TOOLTIP,
110         AE_ACCEL
111 };
112
113 gchar *format_class_list[] = {
114         N_("Unknown"),
115         N_("Image"),
116         N_("RAW Image"),
117         N_("Metadata"),
118         N_("Video"),
119         N_("Collection"),
120         N_("Document"),
121         N_("HEIF")
122         };
123
124 /* config memory values */
125 static ConfOptions *c_options = NULL;
126
127
128 #ifdef DEBUG
129 static gint debug_c;
130 #endif
131
132 static GtkWidget *configwindow = NULL;
133 static GtkListStore *filter_store = NULL;
134 static GtkTreeStore *accel_store = NULL;
135
136 static GtkWidget *safe_delete_path_entry;
137
138 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
139 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
140 static GtkWidget *color_profile_screen_file_entry;
141
142 static GtkWidget *sidecar_ext_entry;
143 static GtkWidget *help_search_engine_entry;
144
145
146 #define CONFIG_WINDOW_DEF_WIDTH         700
147 #define CONFIG_WINDOW_DEF_HEIGHT        600
148
149 /*
150  *-----------------------------------------------------------------------------
151  * option widget callbacks (private)
152  *-----------------------------------------------------------------------------
153  */
154
155 static void zoom_increment_cb(GtkWidget *spin, gpointer data)
156 {
157         c_options->image.zoom_increment = (gint)(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
158 }
159
160 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer data)
161 {
162         gint mins_secs_tenths, delay;
163
164         mins_secs_tenths = c_options->slideshow.delay %
165                                                 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
166
167         delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
168                                                                 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
169                                                                 mins_secs_tenths);
170
171         c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
172                                                                                                         SLIDESHOW_SUBSECOND_PRECISION;
173 }
174
175 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer data)
176 {
177         gint hours, secs_tenths, delay;
178
179         hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
180         secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
181
182         delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
183                                         (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
184                                         (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
185
186         c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
187                                                                                                         SLIDESHOW_SUBSECOND_PRECISION;
188 }
189
190 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer data)
191 {
192         gint hours_mins, delay;
193
194         hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
195
196         delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
197                                                         (gint)(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
198                                                         (gdouble)(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
199
200         c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
201                                                                                                         SLIDESHOW_SUBSECOND_PRECISION;
202 }
203
204 /*
205  *-----------------------------------------------------------------------------
206  * sync progam to config window routine (private)
207  *-----------------------------------------------------------------------------
208  */
209
210 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
211 {
212         const gchar *buf;
213
214         g_free(*option);
215         *option = NULL;
216         buf = gtk_entry_get_text(GTK_ENTRY(entry));
217         if (buf && strlen(buf) > 0)
218                 {
219                 if (func)
220                         *option = func(buf);
221                 else
222                         *option = g_strdup(buf);
223                 }
224 }
225
226
227 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
228 {
229         gchar *accel_path, *accel;
230
231         gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
232
233         if (accel_path && accel_path[0])
234                 {
235                 GtkAccelKey key;
236                 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
237                 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
238                 }
239
240         g_free(accel_path);
241         g_free(accel);
242
243         return FALSE;
244 }
245
246
247 static void config_window_apply(void)
248 {
249         gint i;
250         gboolean refresh = FALSE;
251
252         config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
253
254         if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
255         if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
256         if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
257         if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
258         if (options->file_sort.natural != c_options->file_sort.natural) refresh = TRUE;
259         if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
260         if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
261
262         options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
263         options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
264         options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
265         options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
266         options->file_ops.no_trash = c_options->file_ops.no_trash;
267         options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
268         options->tools_restore_state = c_options->tools_restore_state;
269         options->save_window_positions = c_options->save_window_positions;
270         options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
271         options->save_dialog_window_positions = c_options->save_dialog_window_positions;
272         options->show_window_ids = c_options->show_window_ids;
273         options->image.scroll_reset_method = c_options->image.scroll_reset_method;
274         options->image.zoom_2pass = c_options->image.zoom_2pass;
275         options->image.fit_window_to_image = c_options->image.fit_window_to_image;
276         options->image.limit_window_size = c_options->image.limit_window_size;
277         options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
278         options->image.max_window_size = c_options->image.max_window_size;
279         options->image.limit_autofit_size = c_options->image.limit_autofit_size;
280         options->image.max_autofit_size = c_options->image.max_autofit_size;
281         options->image.max_enlargement_size = c_options->image.max_enlargement_size;
282         options->image.use_clutter_renderer = c_options->image.use_clutter_renderer;
283         options->progressive_key_scrolling = c_options->progressive_key_scrolling;
284         options->keyboard_scroll_step = c_options->keyboard_scroll_step;
285
286         if (options->thumbnails.max_width != c_options->thumbnails.max_width
287             || options->thumbnails.max_height != c_options->thumbnails.max_height
288             || options->thumbnails.quality != c_options->thumbnails.quality)
289                 {
290                 thumb_format_changed = TRUE;
291                 refresh = TRUE;
292                 options->thumbnails.max_width = c_options->thumbnails.max_width;
293                 options->thumbnails.max_height = c_options->thumbnails.max_height;
294                 options->thumbnails.quality = c_options->thumbnails.quality;
295                 }
296         options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
297         options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
298         options->thumbnails.use_exif = c_options->thumbnails.use_exif;
299         options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
300         options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
301 //      options->thumbnails.use_ft_metadata_small = c_options->thumbnails.use_ft_metadata_small;
302         options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
303         options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
304         options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
305         options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
306         options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
307         options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
308
309         options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
310         options->file_sort.natural = c_options->file_sort.natural;
311         options->file_filter.disable = c_options->file_filter.disable;
312
313         config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, NULL);
314         sidecar_ext_parse(options->sidecar.ext);
315
316         options->slideshow.random = c_options->slideshow.random;
317         options->slideshow.repeat = c_options->slideshow.repeat;
318         options->slideshow.delay = c_options->slideshow.delay;
319
320         options->mousewheel_scrolls = c_options->mousewheel_scrolls;
321         options->image_lm_click_nav = c_options->image_lm_click_nav;
322         options->image_l_click_video = c_options->image_l_click_video;
323         options->image_l_click_video_editor = c_options->image_l_click_video_editor;
324
325         options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
326
327         options->image.tile_cache_max = c_options->image.tile_cache_max;
328         options->image.image_cache_max = c_options->image.image_cache_max;
329
330         options->image.zoom_quality = c_options->image.zoom_quality;
331
332         options->image.zoom_increment = c_options->image.zoom_increment;
333
334         options->image.enable_read_ahead = c_options->image.enable_read_ahead;
335
336
337         if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
338             || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
339             || !gdk_color_equal(&options->image.border_color, &c_options->image.border_color))
340                 {
341                 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
342                 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
343                 options->image.border_color = c_options->image.border_color;
344                 layout_colors_update();
345                 view_window_colors_update();
346                 }
347
348         options->image.alpha_color_1 = c_options->image.alpha_color_1;
349         options->image.alpha_color_2 = c_options->image.alpha_color_2;
350
351         options->fullscreen.screen = c_options->fullscreen.screen;
352         options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
353         options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
354         options->fullscreen.above = c_options->fullscreen.above;
355         if (c_options->image_overlay.template_string)
356                 set_image_overlay_template_string(&options->image_overlay.template_string,
357                                                   c_options->image_overlay.template_string);
358         if (c_options->image_overlay.font)
359                 set_image_overlay_font_string(&options->image_overlay.font,
360                                                   c_options->image_overlay.font);
361         options->image_overlay.text_red = c_options->image_overlay.text_red;
362         options->image_overlay.text_green = c_options->image_overlay.text_green;
363         options->image_overlay.text_blue = c_options->image_overlay.text_blue;
364         options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
365         options->image_overlay.background_red = c_options->image_overlay.background_red;
366         options->image_overlay.background_green = c_options->image_overlay.background_green;
367         options->image_overlay.background_blue = c_options->image_overlay.background_blue;
368         options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
369         options->update_on_time_change = c_options->update_on_time_change;
370         options->image.exif_proof_rotate_enable = c_options->image.exif_proof_rotate_enable;
371
372         options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
373         options->rot_invariant_sim = c_options->rot_invariant_sim;
374
375         options->tree_descend_subdirs = c_options->tree_descend_subdirs;
376
377         options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
378
379         options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
380         options->dnd_icon_size = c_options->dnd_icon_size;
381         options->clipboard_selection = c_options->clipboard_selection;
382
383         options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
384         options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
385         options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
386         options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
387         options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
388         options->metadata.confirm_write = c_options->metadata.confirm_write;
389         options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
390         options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
391         options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
392         options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
393         options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
394         options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
395         options->metadata.write_orientation = c_options->metadata.write_orientation;
396         options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
397                                (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
398                                (c_options->stereo.tmp.flip_right   ? PR_STEREO_FLIP_RIGHT : 0) |
399                                (c_options->stereo.tmp.mirror_left  ? PR_STEREO_MIRROR_LEFT : 0) |
400                                (c_options->stereo.tmp.flip_left    ? PR_STEREO_FLIP_LEFT : 0) |
401                                (c_options->stereo.tmp.swap         ? PR_STEREO_SWAP : 0) |
402                                (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
403         options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
404                                (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
405                                (c_options->stereo.tmp.fs_flip_right   ? PR_STEREO_FLIP_RIGHT : 0) |
406                                (c_options->stereo.tmp.fs_mirror_left  ? PR_STEREO_MIRROR_LEFT : 0) |
407                                (c_options->stereo.tmp.fs_flip_left    ? PR_STEREO_FLIP_LEFT : 0) |
408                                (c_options->stereo.tmp.fs_swap         ? PR_STEREO_SWAP : 0) |
409                                (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
410         options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
411         options->stereo.fixed_w = c_options->stereo.fixed_w;
412         options->stereo.fixed_h = c_options->stereo.fixed_h;
413         options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
414         options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
415         options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
416         options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
417
418         options->info_keywords.height = c_options->info_keywords.height;
419         options->info_title.height = c_options->info_title.height;
420         options->info_comment.height = c_options->info_comment.height;
421         options->info_rating.height = c_options->info_rating.height;
422
423         options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
424
425         options->marks_save = c_options->marks_save;
426         options->with_rename = c_options->with_rename;
427         options->collections_on_top = c_options->collections_on_top;
428         config_entry_to_option(help_search_engine_entry, &options->help_search_engine, NULL);
429
430         options->read_metadata_in_idle = c_options->read_metadata_in_idle;
431
432         options->star_rating.star = c_options->star_rating.star;
433         options->star_rating.rejected = c_options->star_rating.rejected;
434 #ifdef DEBUG
435         set_debug_level(debug_c);
436 #endif
437
438 #ifdef HAVE_LCMS
439         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
440                 {
441                 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], NULL);
442                 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], NULL);
443                 }
444         config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, NULL);
445         options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
446         if (options->color_profile.render_intent != c_options->color_profile.render_intent)
447                 {
448                 options->color_profile.render_intent = c_options->color_profile.render_intent;
449                 color_man_update();
450                 }
451 #endif
452
453         options->mouse_button_8 = c_options->mouse_button_8;
454         options->mouse_button_9 = c_options->mouse_button_9;
455
456         config_tab_keywords_save();
457
458         image_options_sync();
459
460         if (refresh)
461                 {
462                 filter_rebuild();
463                 layout_refresh(NULL);
464                 }
465
466         if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, NULL);
467
468         toolbar_apply(TOOLBAR_MAIN);
469         toolbar_apply(TOOLBAR_STATUS);
470 }
471
472 /*
473  *-----------------------------------------------------------------------------
474  * config window main button callbacks (private)
475  *-----------------------------------------------------------------------------
476  */
477
478 static void config_window_close_cb(GtkWidget *widget, gpointer data)
479 {
480         gtk_widget_destroy(configwindow);
481         configwindow = NULL;
482         filter_store = NULL;
483 }
484
485 static void config_window_help_cb(GtkWidget *widget, gpointer data)
486 {
487         GtkWidget *notebook = data;
488         gint i;
489
490         static gchar *html_section[] =
491         {
492         "GuideOptionsGeneral.html",
493         "GuideOptionsImage.html",
494         "GuideOptionsOSD.html",
495         "GuideOptionsWindow.html",
496         "GuideOptionsKeyboard.html",
497         "GuideOptionsFiltering.html",
498         "GuideOptionsMetadata.html",
499         "GuideOptionsKeywords.html",
500         "GuideOptionsColor.html",
501         "GuideOptionsStereo.html",
502         "GuideOptionsBehavior.html",
503         "GuideOptionsToolbar.html"
504         };
505
506         i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
507         help_window_show(html_section[i]);
508 }
509
510 static gboolean config_window_delete(GtkWidget *widget, GdkEventAny *event, gpointer data)
511 {
512         config_window_close_cb(NULL, NULL);
513         return TRUE;
514 }
515
516 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
517 {
518         config_window_apply();
519         config_window_close_cb(NULL, NULL);
520 }
521
522 static void config_window_apply_cb(GtkWidget *widget, gpointer data)
523 {
524         LayoutWindow *lw;
525         lw = layout_window_list->data;
526
527         config_window_apply();
528         layout_util_sync(lw);
529 }
530
531 static void config_window_save_cb(GtkWidget *widget, gpointer data)
532 {
533         config_window_apply();
534         save_options(options);
535 }
536
537 /*
538  *-----------------------------------------------------------------------------
539  * config window setup (private)
540  *-----------------------------------------------------------------------------
541  */
542
543 static void quality_menu_cb(GtkWidget *combo, gpointer data)
544 {
545         gint *option = data;
546
547         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
548                 {
549                 case 0:
550                 default:
551                         *option = GDK_INTERP_NEAREST;
552                         break;
553                 case 1:
554                         *option = GDK_INTERP_TILES;
555                         break;
556                 case 2:
557                         *option = GDK_INTERP_BILINEAR;
558                         break;
559                 case 3:
560                         *option = GDK_INTERP_HYPER;
561                         break;
562                 }
563 }
564
565 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
566 {
567         gint *option = data;
568
569         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
570                 {
571                 case 0:
572                 default:
573                         *option = PRIMARY;
574                         break;
575                 case 1:
576                         *option = CLIPBOARD;
577                         break;
578                 }
579 }
580
581 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
582                              guint option, guint *option_c)
583 {
584         GtkWidget *combo;
585         gint current = 0;
586
587         *option_c = option;
588
589         pref_table_label(table, column, row, text, 0.0);
590
591         combo = gtk_combo_box_text_new();
592
593         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
594         if (option == GDK_INTERP_NEAREST) current = 0;
595         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
596         if (option == GDK_INTERP_TILES) current = 1;
597         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear"));
598         if (option == GDK_INTERP_BILINEAR) current = 2;
599         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Hyper (best, but slowest)"));
600         if (option == GDK_INTERP_HYPER) current = 3;
601
602         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
603
604         g_signal_connect(G_OBJECT(combo), "changed",
605                          G_CALLBACK(quality_menu_cb), option_c);
606
607         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
608                          GTK_EXPAND | GTK_FILL, 0, 0, 0);
609         gtk_widget_show(combo);
610 }
611
612 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
613                              gint option, gint *option_c)
614 {
615         GtkWidget *combo;
616         gint current = 0;
617
618         *option_c = option;
619
620         pref_table_label(table, column, row, text, 0.0);
621
622         combo = gtk_combo_box_text_new();
623
624         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("PRIMARY"));
625         if (option == PRIMARY) current = 0;
626         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("CLIPBOARD"));
627         if (option == CLIPBOARD) current = 1;
628
629         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
630
631         g_signal_connect(G_OBJECT(combo), "changed",
632                          G_CALLBACK(clipboard_selection_menu_cb), option_c);
633
634         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
635                          GTK_EXPAND | GTK_FILL, 0, 0, 0);
636         gtk_widget_show(combo);
637 }
638
639 typedef struct _UseableMouseItems UseableMouseItems;
640 struct _UseableMouseItems
641 {
642         gchar *name; /* GtkActionEntry terminology */
643         gchar *label;
644         gchar *stock_id;
645 };
646
647 static const UseableMouseItems useable_mouse_items[] = {
648         {"", "", NULL},
649         {"FirstImage",  N_("First Image"), GTK_STOCK_GOTO_TOP},
650         {"PrevImage",   N_("Previous Image"), GTK_STOCK_GO_UP},
651         {"NextImage",   N_("Next Image"), GTK_STOCK_GO_DOWN},
652         {"LastImage",   N_("Last Image"), GTK_STOCK_GOTO_BOTTOM},
653         {"Back",        N_("Back"), GTK_STOCK_GO_BACK},
654         {"Forward",     N_("Forward"), GTK_STOCK_GO_FORWARD},
655         {"Home",        N_("Home"), GTK_STOCK_HOME},
656         {"Up",  N_("Up"), GTK_STOCK_GO_UP},
657         {"NextPage",    N_("Next Page"), PIXBUF_INLINE_ICON_NEXT_PAGE},
658         {"PrevPage",    N_("Previous Page"), PIXBUF_INLINE_ICON_PREVIOUS_PAGE},
659         {"NewWindow",   N_("New _window"), GTK_STOCK_NEW},
660         {"NewCollection",       N_("New collection"), GTK_STOCK_INDEX},
661         {"OpenCollection",      N_("Open collection"), GTK_STOCK_OPEN},
662         {"Search",      N_("Search"), GTK_STOCK_FIND},
663         {"FindDupes",   N_("Find duplicates"), GTK_STOCK_FIND},
664         {"NewFolder",   N_("New folder"),GTK_STOCK_DIRECTORY},
665         {"Copy",        N_("Copy"), GTK_STOCK_COPY},
666         {"Move",        N_("Move"), PIXBUF_INLINE_ICON_MOVE},
667         {"Rename",      N_("Rename"), PIXBUF_INLINE_ICON_RENAME},
668         {"Delete",      N_("Delete"), GTK_STOCK_DELETE},
669         {"CloseWindow", N_("Close Window"), GTK_STOCK_CLOSE},
670         {"PanView",     N_("Pan view"), PIXBUF_INLINE_ICON_PANORAMA},
671         {"SelectAll",   N_("Select all"), PIXBUF_INLINE_ICON_SELECT_ALL},
672         {"SelectNone",  N_("Select none"), PIXBUF_INLINE_ICON_SELECT_NONE},
673         {"SelectInvert",        N_("Select invert"), PIXBUF_INLINE_ICON_SELECT_INVERT},
674         {"ShowFileFilter",      N_("Show file filter"), PIXBUF_INLINE_ICON_FILE_FILTER},
675         {"RectangularSelection",        N_("Select rectangle"), PIXBUF_INLINE_ICON_SELECT_RECTANGLE},
676         {"Print",       N_("Print"), GTK_STOCK_PRINT},
677         {"Preferences", N_("Preferences"), GTK_STOCK_PREFERENCES},
678         {"LayoutConfig",        N_("Configure this window"), GTK_STOCK_PREFERENCES},
679         {"Maintenance", N_("Cache maintenance"), PIXBUF_INLINE_ICON_MAINTENANCE},
680         {"RotateCW",    N_("Rotate clockwise"), PIXBUF_INLINE_ICON_CW},
681         {"RotateCCW",   N_("Rotate counterclockwise"), PIXBUF_INLINE_ICON_CCW},
682         {"Rotate180",   N_("Rotate 180"), PIXBUF_INLINE_ICON_180},
683         {"Mirror",      N_("Mirror"), PIXBUF_INLINE_ICON_MIRROR},
684         {"Flip",        N_("Flip"), PIXBUF_INLINE_ICON_FLIP},
685         {"AlterNone",   N_("Original state"), PIXBUF_INLINE_ICON_ORIGINAL},
686         {"ZoomIn",      N_("Zoom in"), GTK_STOCK_ZOOM_IN},
687         {"ZoomOut",     N_("Zoom out"), GTK_STOCK_ZOOM_OUT},
688         {"Zoom100",     N_("Zoom 1:1"), GTK_STOCK_ZOOM_100},
689         {"ZoomFit",     N_("Zoom to fit"), GTK_STOCK_ZOOM_FIT},
690         {"ZoomFillHor", N_("Fit Horizontaly"), PIXBUF_INLINE_ICON_ZOOMFILLHOR},
691         {"ZoomFillVert",        N_("Fit vertically"), PIXBUF_INLINE_ICON_ZOOMFILLVERT},
692         {"Zoom200",     N_("Zoom 2:1"), GTK_STOCK_FILE},
693         {"Zoom300",     N_("Zoom 3:1"), GTK_STOCK_FILE},
694         {"Zoom400",     N_("Zoom 4:1"), GTK_STOCK_FILE},
695         {"Zoom50",      N_("Zoom 1:2"), GTK_STOCK_FILE},
696         {"Zoom33",      N_("Zoom1:3"), GTK_STOCK_FILE},
697         {"Zoom25",      N_("Zoom 1:4"), GTK_STOCK_FILE},
698         {"ConnectZoomIn",       N_("Connected Zoom in"), GTK_STOCK_ZOOM_IN},
699         {"Grayscale",   N_("Grayscale"), PIXBUF_INLINE_ICON_GRAYSCALE},
700         {"OverUnderExposed",    N_("Over Under Exposed"), PIXBUF_INLINE_ICON_EXPOSURE},
701         {"HideTools",   N_("Hide file list"), PIXBUF_INLINE_ICON_HIDETOOLS},
702         {"SlideShowPause",      N_("Pause slideshow"), GTK_STOCK_MEDIA_PAUSE},
703         {"SlideShowFaster",     N_("Slideshow Faster"), GTK_STOCK_FILE},
704         {"SlideShowSlower",     N_("Slideshow Slower"), GTK_STOCK_FILE},
705         {"Refresh",     N_("Refresh"), GTK_STOCK_REFRESH},
706         {"HelpContents",        N_("Help"), GTK_STOCK_HELP},
707         {"ExifWin",     N_("Exif window"), PIXBUF_INLINE_ICON_EXIF},
708         {"Thumbnails",  N_("Show thumbnails"), PIXBUF_INLINE_ICON_THUMB},
709         {"ShowMarks",   N_("Show marks"), PIXBUF_INLINE_ICON_MARKS},
710         {"ImageGuidelines",     N_("Show guidelines"), PIXBUF_INLINE_ICON_GUIDELINES},
711         {"DrawRectangle",       N_("Draw Rectangle"), PIXBUF_INLINE_ICON_DRAW_RECTANGLE},
712         {"FloatTools",  N_("Float file list"), PIXBUF_INLINE_ICON_FLOAT},
713         {"SBar",        N_("Info sidebar"), PIXBUF_INLINE_ICON_INFO},
714         {"SBarSort",    N_("Sort manager"), PIXBUF_INLINE_ICON_SORT},
715         {"Quit",        N_("Quit"), GTK_STOCK_QUIT},
716         {NULL,          NULL, NULL}
717 };
718
719 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
720 {
721         gchar **option = data;
722         gchar *label;
723
724         label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
725
726         const UseableMouseItems *list = useable_mouse_items;
727
728         while (list->name)
729                 {
730                 if (g_strcmp0(list->label, label) == 0)
731                         {
732                         break;
733                         }
734                 list++;
735                 }
736
737         g_free(*option);
738         *option = g_strdup(list->name);
739         g_free(label);
740 }
741
742 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
743                              gchar *option, gchar **option_c)
744 {
745         GtkWidget *combo;
746         gint current = 0;
747         gint i = 0;
748
749         *option_c = option;
750
751         pref_table_label(table, column, row, text, 0.0);
752
753         combo = gtk_combo_box_text_new();
754
755         const UseableMouseItems *list = useable_mouse_items;
756
757         while (list->name)
758                 {
759                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), list->label);
760                 if (g_strcmp0(list->name, option) == 0)
761                         {
762                         current = i;
763                         }
764                 i++;
765                 list++;
766                 }
767
768         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
769
770         g_signal_connect(G_OBJECT(combo), "changed",
771                          G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
772
773         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
774                          GTK_EXPAND | GTK_FILL, 0, 0, 0);
775         gtk_widget_show(combo);
776 }
777
778 static void thumb_size_menu_cb(GtkWidget *combo, gpointer data)
779 {
780         gint n;
781
782         n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
783         if (n < 0) return;
784
785         if ((guint) n < sizeof(thumb_size_list) / sizeof(ThumbSize))
786                 {
787                 c_options->thumbnails.max_width = thumb_size_list[n].w;
788                 c_options->thumbnails.max_height = thumb_size_list[n].h;
789                 }
790         else
791                 {
792                 c_options->thumbnails.max_width = options->thumbnails.max_width;
793                 c_options->thumbnails.max_height = options->thumbnails.max_height;
794                 }
795 }
796
797 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
798 {
799         GtkWidget *combo;
800         gint current;
801         gint i;
802
803         c_options->thumbnails.max_width = options->thumbnails.max_width;
804         c_options->thumbnails.max_height = options->thumbnails.max_height;
805
806         pref_table_label(table, column, row, text, 0.0);
807
808         combo = gtk_combo_box_text_new();
809
810         current = -1;
811         for (i = 0; (guint) i < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
812                 {
813                 gint w, h;
814                 gchar *buf;
815
816                 w = thumb_size_list[i].w;
817                 h = thumb_size_list[i].h;
818
819                 buf = g_strdup_printf("%d x %d", w, h);
820                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
821                 g_free(buf);
822
823                 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
824                 }
825
826         if (current == -1)
827                 {
828                 gchar *buf;
829
830                 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
831                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
832                 g_free(buf);
833
834                 current = i;
835                 }
836
837         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
838         g_signal_connect(G_OBJECT(combo), "changed",
839                          G_CALLBACK(thumb_size_menu_cb), NULL);
840
841         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
842                          GTK_EXPAND | GTK_FILL, 0, 0, 0);
843         gtk_widget_show(combo);
844 }
845
846 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
847 {
848         gint *option = data;
849
850         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
851                 {
852                 case 0:
853                 default:
854                         *option = PR_STEREO_NONE;
855                         break;
856                 case 1:
857                         *option = PR_STEREO_ANAGLYPH_RC;
858                         break;
859                 case 2:
860                         *option = PR_STEREO_ANAGLYPH_GM;
861                         break;
862                 case 3:
863                         *option = PR_STEREO_ANAGLYPH_YB;
864                         break;
865                 case 4:
866                         *option = PR_STEREO_ANAGLYPH_GRAY_RC;
867                         break;
868                 case 5:
869                         *option = PR_STEREO_ANAGLYPH_GRAY_GM;
870                         break;
871                 case 6:
872                         *option = PR_STEREO_ANAGLYPH_GRAY_YB;
873                         break;
874                 case 7:
875                         *option = PR_STEREO_ANAGLYPH_DB_RC;
876                         break;
877                 case 8:
878                         *option = PR_STEREO_ANAGLYPH_DB_GM;
879                         break;
880                 case 9:
881                         *option = PR_STEREO_ANAGLYPH_DB_YB;
882                         break;
883                 case 10:
884                         *option = PR_STEREO_HORIZ;
885                         break;
886                 case 11:
887                         *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
888                         break;
889                 case 12:
890                         *option = PR_STEREO_VERT;
891                         break;
892                 case 13:
893                         *option = PR_STEREO_VERT | PR_STEREO_HALF;
894                         break;
895                 case 14:
896                         *option = PR_STEREO_FIXED;
897                         break;
898                 }
899 }
900
901 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
902                              gint option, gint *option_c, gboolean add_fixed)
903 {
904         GtkWidget *combo;
905         gint current = 0;
906
907         *option_c = option;
908
909         pref_table_label(table, column, row, text, 0.0);
910
911         combo = gtk_combo_box_text_new();
912
913         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
914
915         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
916         if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
917         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
918         if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
919         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
920         if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
921         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
922         if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
923         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
924         if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
925         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
926         if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
927         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
928         if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
929         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
930         if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
931         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
932         if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
933
934         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
935         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
936         if (option & PR_STEREO_HORIZ)
937                 {
938                 current = 10;
939                 if (option & PR_STEREO_HALF) current = 11;
940                 }
941
942         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
943         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
944         if (option & PR_STEREO_VERT)
945                 {
946                 current = 12;
947                 if (option & PR_STEREO_HALF) current = 13;
948                 }
949
950         if (add_fixed)
951                 {
952                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
953                 if (option & PR_STEREO_FIXED) current = 14;
954                 }
955
956         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
957
958         g_signal_connect(G_OBJECT(combo), "changed",
959                          G_CALLBACK(stereo_mode_menu_cb), option_c);
960
961         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
962                          GTK_EXPAND | GTK_FILL, 0, 0, 0);
963         gtk_widget_show(combo);
964 }
965
966 static void video_menu_cb(GtkWidget *combo, gpointer data)
967 {
968         gchar **option = data;
969
970         EditorDescription *ed = g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo)));
971         *option = ed->key;
972 }
973
974 static void video_menu_populate(gpointer data, gpointer user_data)
975 {
976         GtkWidget *combo = user_data;
977         EditorDescription *ed = data;
978
979         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
980 }
981
982 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
983                              gchar *option, gchar **option_c)
984 {
985         GtkWidget *combo;
986         gint current;
987 /* use lists since they are sorted */
988         GList *eds = editor_list_get();
989
990         *option_c = option;
991
992         pref_table_label(table, column, row, text, 0.0);
993
994         combo = gtk_combo_box_text_new();
995         g_list_foreach(eds,video_menu_populate,(gpointer)combo);
996         current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
997
998         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
999
1000         g_signal_connect(G_OBJECT(combo), "changed",
1001                          G_CALLBACK(video_menu_cb), option_c);
1002
1003         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
1004                          GTK_EXPAND | GTK_FILL, 0, 0, 0);
1005         gtk_widget_show(combo);
1006 }
1007
1008 static void filter_store_populate(void)
1009 {
1010         GList *work;
1011
1012         if (!filter_store) return;
1013
1014         gtk_list_store_clear(filter_store);
1015
1016         work = filter_get_list();
1017         while (work)
1018                 {
1019                 FilterEntry *fe;
1020                 GtkTreeIter iter;
1021
1022                 fe = work->data;
1023                 work = work->next;
1024
1025                 gtk_list_store_append(filter_store, &iter);
1026                 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1027                 }
1028 }
1029
1030 static void filter_store_ext_edit_cb(GtkCellRendererText *cell, gchar *path_str,
1031                                      gchar *new_text, gpointer data)
1032 {
1033         GtkWidget *model = data;
1034         FilterEntry *fe = data;
1035         GtkTreePath *tpath;
1036         GtkTreeIter iter;
1037
1038         if (!new_text || strlen(new_text) < 1) return;
1039
1040         tpath = gtk_tree_path_new_from_string(path_str);
1041         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1042         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1043
1044         g_free(fe->extensions);
1045         fe->extensions = g_strdup(new_text);
1046
1047         gtk_tree_path_free(tpath);
1048         filter_rebuild();
1049 }
1050
1051 static void filter_store_class_edit_cb(GtkCellRendererText *cell, gchar *path_str,
1052                                        gchar *new_text, gpointer data)
1053 {
1054         GtkWidget *model = data;
1055         FilterEntry *fe = data;
1056         GtkTreePath *tpath;
1057         GtkTreeIter iter;
1058         gint i;
1059
1060         if (!new_text || !new_text[0]) return;
1061
1062         tpath = gtk_tree_path_new_from_string(path_str);
1063         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1064         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1065
1066         for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1067                 {
1068                 if (strcmp(new_text, _(format_class_list[i])) == 0)
1069                         {
1070                         fe->file_class = i;
1071                         break;
1072                         }
1073                 }
1074
1075         gtk_tree_path_free(tpath);
1076         filter_rebuild();
1077 }
1078
1079 static void filter_store_desc_edit_cb(GtkCellRendererText *cell, gchar *path_str,
1080                                       gchar *new_text, gpointer data)
1081 {
1082         GtkWidget *model = data;
1083         FilterEntry *fe;
1084         GtkTreePath *tpath;
1085         GtkTreeIter iter;
1086
1087         if (!new_text || !new_text[0]) return;
1088
1089         tpath = gtk_tree_path_new_from_string(path_str);
1090         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1091         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1092
1093         g_free(fe->description);
1094         fe->description = g_strdup(new_text);
1095
1096         gtk_tree_path_free(tpath);
1097 }
1098
1099 static void filter_store_enable_cb(GtkCellRendererToggle *renderer,
1100                                    gchar *path_str, gpointer data)
1101 {
1102         GtkWidget *model = data;
1103         FilterEntry *fe;
1104         GtkTreePath *tpath;
1105         GtkTreeIter iter;
1106
1107         tpath = gtk_tree_path_new_from_string(path_str);
1108         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1109         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1110
1111         fe->enabled = !fe->enabled;
1112
1113         gtk_tree_path_free(tpath);
1114         filter_rebuild();
1115 }
1116
1117 static void filter_store_writable_cb(GtkCellRendererToggle *renderer,
1118                                      gchar *path_str, gpointer data)
1119 {
1120         GtkWidget *model = data;
1121         FilterEntry *fe;
1122         GtkTreePath *tpath;
1123         GtkTreeIter iter;
1124
1125         tpath = gtk_tree_path_new_from_string(path_str);
1126         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1127         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1128
1129         fe->writable = !fe->writable;
1130         if (fe->writable) fe->allow_sidecar = FALSE;
1131
1132         gtk_tree_path_free(tpath);
1133         filter_rebuild();
1134 }
1135
1136 static void filter_store_sidecar_cb(GtkCellRendererToggle *renderer,
1137                                     gchar *path_str, gpointer data)
1138 {
1139         GtkWidget *model = data;
1140         FilterEntry *fe;
1141         GtkTreePath *tpath;
1142         GtkTreeIter iter;
1143
1144         tpath = gtk_tree_path_new_from_string(path_str);
1145         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1146         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1147
1148         fe->allow_sidecar = !fe->allow_sidecar;
1149         if (fe->allow_sidecar) fe->writable = FALSE;
1150
1151         gtk_tree_path_free(tpath);
1152         filter_rebuild();
1153 }
1154
1155 static void filter_set_func(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1156                             GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1157 {
1158         FilterEntry *fe;
1159
1160         gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1161
1162         switch (GPOINTER_TO_INT(data))
1163                 {
1164                 case FE_ENABLE:
1165                         g_object_set(GTK_CELL_RENDERER(cell),
1166                                      "active", fe->enabled, NULL);
1167                         break;
1168                 case FE_EXTENSION:
1169                         g_object_set(GTK_CELL_RENDERER(cell),
1170                                      "text", fe->extensions, NULL);
1171                         break;
1172                 case FE_DESCRIPTION:
1173                         g_object_set(GTK_CELL_RENDERER(cell),
1174                                      "text", fe->description, NULL);
1175                         break;
1176                 case FE_CLASS:
1177                         g_object_set(GTK_CELL_RENDERER(cell),
1178                                      "text", _(format_class_list[fe->file_class]), NULL);
1179                         break;
1180                 case FE_WRITABLE:
1181                         g_object_set(GTK_CELL_RENDERER(cell),
1182                                      "active", fe->writable, NULL);
1183                         break;
1184                 case FE_ALLOW_SIDECAR:
1185                         g_object_set(GTK_CELL_RENDERER(cell),
1186                                      "active", fe->allow_sidecar, NULL);
1187                         break;
1188                 }
1189 }
1190
1191 static gboolean filter_add_scroll(gpointer data)
1192 {
1193         GtkTreePath *path;
1194         GList *list_cells;
1195         GtkCellRenderer *cell;
1196         GtkTreeViewColumn *column;
1197         const gchar *title;
1198         guint i = 0;
1199         gint rows;
1200
1201         rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), NULL);
1202         path = gtk_tree_path_new_from_indices(rows-1, -1);
1203
1204         column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1205
1206         list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1207         cell = g_list_last(list_cells)->data;
1208
1209         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1210                                                                 path, column, FALSE, 0.0, 0.0 );
1211         gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1212                                                                 path, column, cell, TRUE);
1213
1214         gtk_tree_path_free(path);
1215         g_list_free(list_cells);
1216
1217         return(FALSE);
1218 }
1219
1220 static void filter_add_cb(GtkWidget *widget, gpointer data)
1221 {
1222         filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1223         filter_store_populate();
1224
1225         g_idle_add((GSourceFunc)filter_add_scroll, data);
1226 }
1227
1228 static void filter_remove_cb(GtkWidget *widget, gpointer data)
1229 {
1230         GtkWidget *filter_view = data;
1231         GtkTreeSelection *selection;
1232         GtkTreeIter iter;
1233         FilterEntry *fe;
1234
1235         if (!filter_store) return;
1236         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1237         if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) return;
1238         gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1239         if (!fe) return;
1240
1241         filter_remove_entry(fe);
1242         filter_rebuild();
1243         filter_store_populate();
1244 }
1245
1246 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1247 {
1248         GtkTreeIter iter;
1249         GtkTreePath *path;
1250         GtkTreeViewColumn *column;
1251
1252         gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1253         path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1254         column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1255
1256         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1257                                      path, column,
1258                                      FALSE, 0.0, 0.0);
1259
1260         gtk_tree_path_free(path);
1261
1262         return(FALSE);
1263 }
1264
1265 static void filter_default_ok_cb(GenericDialog *gd, gpointer data)
1266 {
1267         filter_reset();
1268         filter_add_defaults();
1269         filter_rebuild();
1270         filter_store_populate();
1271
1272         g_idle_add((GSourceFunc)filter_default_ok_scroll, gd->data);
1273 }
1274
1275 static void dummy_cancel_cb(GenericDialog *gd, gpointer data)
1276 {
1277         /* no op, only so cancel button appears */
1278 }
1279
1280 static void filter_default_cb(GtkWidget *widget, gpointer data)
1281 {
1282         GenericDialog *gd;
1283
1284         gd = generic_dialog_new(_("Reset filters"),
1285                                 "reset_filter", widget, TRUE,
1286                                 dummy_cancel_cb, data);
1287         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Reset filters"),
1288                                    _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1289         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, filter_default_ok_cb, TRUE);
1290         gtk_widget_show(gd->dialog);
1291 }
1292
1293 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1294 {
1295         GtkWidget *frame = data;
1296
1297         gtk_widget_set_sensitive(frame,
1298                                  !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1299 }
1300
1301 static void safe_delete_view_cb(GtkWidget *widget, gpointer data)
1302 {
1303         layout_set_path(NULL, gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1304 }
1305
1306 static void safe_delete_clear_ok_cb(GenericDialog *gd, gpointer data)
1307 {
1308         file_util_trash_clear();
1309 }
1310
1311 static void safe_delete_clear_cb(GtkWidget *widget, gpointer data)
1312 {
1313         GenericDialog *gd;
1314         GtkWidget *entry;
1315         gd = generic_dialog_new(_("Clear trash"),
1316                                 "clear_trash", widget, TRUE,
1317                                 dummy_cancel_cb, NULL);
1318         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Clear trash"),
1319                                     _("This will remove the trash contents."), FALSE);
1320         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, safe_delete_clear_ok_cb, TRUE);
1321         entry = gtk_entry_new();
1322         gtk_widget_set_can_focus(entry, FALSE);
1323         gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1324         if (options->file_ops.safe_delete_path) gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1325         gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1326         gtk_widget_show(entry);
1327         gtk_widget_show(gd->dialog);
1328 }
1329
1330 static void image_overlay_template_view_changed_cb(GtkWidget *widget, gpointer data)
1331 {
1332         GtkWidget *pTextView;
1333         GtkTextBuffer *pTextBuffer;
1334         GtkTextIter iStart;
1335         GtkTextIter iEnd;
1336
1337         pTextView = GTK_WIDGET(data);
1338
1339         pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1340         gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1341         gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1342
1343         set_image_overlay_template_string(&c_options->image_overlay.template_string,
1344                                           gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1345 }
1346
1347 static void image_overlay_default_template_ok_cb(GenericDialog *gd, gpointer data)
1348 {
1349         GtkTextView *text_view = data;
1350         GtkTextBuffer *buffer;
1351
1352         set_default_image_overlay_template_string(&options->image_overlay.template_string);
1353         if (!configwindow) return;
1354
1355         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1356         gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1357 }
1358
1359 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1360 {
1361         GenericDialog *gd;
1362
1363         gd = generic_dialog_new(_("Reset image overlay template string"),
1364                                 "reset_image_overlay_template_string", widget, TRUE,
1365                                 dummy_cancel_cb, data);
1366         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Reset image overlay template string"),
1367                                    _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1368         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, image_overlay_default_template_ok_cb, TRUE);
1369         gtk_widget_show(gd->dialog);
1370 }
1371
1372 static void image_overlay_help_cb(GtkWidget *widget, gpointer data)
1373 {
1374         help_window_show("GuideOptionsOSD.html");
1375 }
1376
1377 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer data)
1378 {
1379 #if GTK_CHECK_VERSION(3,4,0)
1380         GtkWidget *dialog;
1381         char *font;
1382         PangoFontDescription *font_desc;
1383
1384         dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1385         gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1386
1387         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1388                 {
1389                 font_desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog));
1390                 font = pango_font_description_to_string(font_desc);
1391                 g_free(c_options->image_overlay.font);
1392                 c_options->image_overlay.font = g_strdup(font);
1393                 g_free(font);
1394                 }
1395
1396         gtk_widget_destroy(dialog);
1397 #else
1398         const char *font;
1399
1400         font = gtk_font_button_get_font_name(GTK_FONT_BUTTON(widget));
1401         c_options->image_overlay.font = g_strdup(font);
1402 #endif
1403 }
1404
1405 static void image_overlay_set_text_colour_cb(GtkWidget *widget, gpointer data)
1406 {
1407         GtkWidget *dialog;
1408 #if GTK_CHECK_VERSION(3,4,0)
1409         GdkRGBA colour;
1410
1411         dialog = gtk_color_chooser_dialog_new("Image Overlay Text Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1412         colour.red = options->image_overlay.text_red;
1413         colour.green = options->image_overlay.text_green;
1414         colour.blue = options->image_overlay.text_blue;
1415         colour.alpha = options->image_overlay.text_alpha;
1416         gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1417         gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1418
1419         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1420                 {
1421                 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1422                 c_options->image_overlay.text_red = colour.red*255;
1423                 c_options->image_overlay.text_green = colour.green*255;
1424                 c_options->image_overlay.text_blue = colour.blue*255;
1425                 c_options->image_overlay.text_alpha = colour.alpha*255;
1426                 }
1427         gtk_widget_destroy(dialog);
1428 #else
1429         GdkColor colour;
1430         GtkColorSelection *colorsel;
1431
1432         dialog = gtk_color_selection_dialog_new("Image Overlay Text Colour");
1433         gtk_window_set_keep_above(GTK_WINDOW(dialog),TRUE);
1434         colour.red = options->image_overlay.text_red*257;
1435         colour.green = options->image_overlay.text_green*257;
1436         colour.blue = options->image_overlay.text_blue*257;
1437         colorsel = GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(dialog)));
1438         gtk_color_selection_set_has_opacity_control(colorsel, TRUE);
1439         gtk_color_selection_set_current_color(colorsel, &colour);
1440         gtk_color_selection_set_current_alpha(colorsel, options->image_overlay.text_alpha*257);
1441
1442         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
1443                 {
1444                 gtk_color_selection_get_current_color(colorsel, &colour);
1445                 c_options->image_overlay.text_red = colour.red/257;
1446                 c_options->image_overlay.text_green = colour.green/257;
1447                 c_options->image_overlay.text_blue = colour.blue/257;
1448                 c_options->image_overlay.text_alpha = gtk_color_selection_get_current_alpha(colorsel)/257;
1449                 }
1450         gtk_widget_destroy (dialog);
1451 #endif
1452 }
1453
1454
1455 static void image_overlay_set_background_colour_cb(GtkWidget *widget, gpointer data)
1456 {
1457         GtkWidget *dialog;
1458 #if GTK_CHECK_VERSION(3,4,0)
1459         GdkRGBA colour;
1460
1461         dialog = gtk_color_chooser_dialog_new("Image Overlay Background Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1462         colour.red = options->image_overlay.background_red;
1463         colour.green = options->image_overlay.background_green;
1464         colour.blue = options->image_overlay.background_blue;
1465         colour.alpha = options->image_overlay.background_alpha;
1466         gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1467         gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1468
1469         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1470                 {
1471                 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1472                 c_options->image_overlay.background_red = colour.red*255;
1473                 c_options->image_overlay.background_green = colour.green*255;
1474                 c_options->image_overlay.background_blue = colour.blue*255;
1475                 c_options->image_overlay.background_alpha = colour.alpha*255;
1476                 }
1477         gtk_widget_destroy(dialog);
1478 #else
1479         GdkColor colour;
1480         GtkColorSelection *colorsel;
1481
1482         dialog = gtk_color_selection_dialog_new("Image Overlay Background Colour");
1483         gtk_window_set_keep_above(GTK_WINDOW(dialog),TRUE);
1484         colour.red = options->image_overlay.background_red*257;
1485         colour.green = options->image_overlay.background_green*257;
1486         colour.blue = options->image_overlay.background_blue*257;
1487         colorsel = GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(dialog)));
1488         gtk_color_selection_set_has_opacity_control(colorsel, TRUE);
1489         gtk_color_selection_set_current_color(colorsel, &colour);
1490         gtk_color_selection_set_current_alpha(colorsel, options->image_overlay.background_alpha*257);
1491
1492         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
1493                 {
1494                 gtk_color_selection_get_current_color(colorsel, &colour);
1495                 c_options->image_overlay.background_red = colour.red/257;
1496                 c_options->image_overlay.background_green = colour.green/257;
1497                 c_options->image_overlay.background_blue = colour.blue/257;
1498                 c_options->image_overlay.background_alpha = gtk_color_selection_get_current_alpha(colorsel)/257;
1499                 }
1500         gtk_widget_destroy(dialog);
1501 #endif
1502 }
1503
1504 static void accel_store_populate(void)
1505 {
1506         LayoutWindow *lw;
1507         GList *groups, *actions;
1508         GtkAction *action;
1509         const gchar *accel_path;
1510         GtkAccelKey key;
1511         GtkTreeIter iter;
1512
1513         if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1514
1515         gtk_tree_store_clear(accel_store);
1516         lw = layout_window_list->data; /* get the actions from the first window, it should not matter, they should be the same in all windows */
1517
1518         g_assert(lw && lw->ui_manager);
1519         groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1520         while (groups)
1521                 {
1522                 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1523                 while (actions)
1524                         {
1525                         action = GTK_ACTION(actions->data);
1526                         accel_path = gtk_action_get_accel_path(action);
1527                         if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1528                                 {
1529                                 gchar *label, *label2, *tooltip, *accel;
1530                                 g_object_get(action,
1531                                              "tooltip", &tooltip,
1532                                              "label", &label,
1533                                              NULL);
1534
1535                                 if (pango_parse_markup(label, -1, '_', NULL, &label2, NULL, NULL) && label2)
1536                                         {
1537                                         g_free(label);
1538                                         label = label2;
1539                                         }
1540
1541                                 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1542
1543                                 if (tooltip)
1544                                         {
1545                                         gtk_tree_store_append(accel_store, &iter, NULL);
1546                                         gtk_tree_store_set(accel_store, &iter,
1547                                                            AE_ACTION, label,
1548                                                            AE_KEY, accel,
1549                                                            AE_TOOLTIP, tooltip ? tooltip : "",
1550                                                            AE_ACCEL, accel_path,
1551                                                            -1);
1552                                         }
1553
1554                                 g_free(accel);
1555                                 g_free(label);
1556                                 g_free(tooltip);
1557                                 }
1558                         actions = actions->next;
1559                         }
1560
1561                 groups = groups->next;
1562                 }
1563 }
1564
1565 static void accel_store_cleared_cb(GtkCellRendererAccel *accel, gchar *path_string, gpointer user_data)
1566 {
1567
1568 }
1569
1570 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
1571 {
1572         gchar *accel1 = data;
1573         gchar *accel2;
1574         GtkAccelKey key1;
1575         GtkAccelKey key2;
1576
1577         gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1578
1579         gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1580         gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1581
1582         if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1583                 {
1584                 gtk_tree_store_set(accel_store, iter, AE_KEY, "",  -1);
1585                 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1586                 }
1587
1588         g_free(accel2);
1589
1590         return FALSE;
1591 }
1592
1593
1594 static void accel_store_edited_cb(GtkCellRendererAccel *accel, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint hardware_keycode, gpointer user_data)
1595 {
1596         GtkTreeModel *model = (GtkTreeModel *)accel_store;
1597         GtkTreeIter iter;
1598         gchar *acc;
1599         gchar *accel_path;
1600         GtkAccelKey old_key, key;
1601         GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1602
1603         gtk_tree_model_get_iter(model, &iter, path);
1604         gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1605
1606         /* test if the accelerator can be stored without conflicts*/
1607         gtk_accel_map_lookup_entry(accel_path, &old_key);
1608
1609         /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1610         gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1611         gtk_accel_map_lookup_entry(accel_path, &key);
1612
1613         /* restore the original for now, the key will be really changed when the changes are confirmed */
1614         gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1615
1616         acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1617         gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1618
1619         gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1620         gtk_tree_path_free(path);
1621         g_free(acc);
1622 }
1623
1624 static gboolean accel_default_scroll(GtkTreeView *data)
1625 {
1626         GtkTreeIter iter;
1627         GtkTreePath *path;
1628         GtkTreeViewColumn *column;
1629
1630         gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1631         path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1632         column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1633
1634         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1635                                      path, column,
1636                                      FALSE, 0.0, 0.0);
1637
1638         gtk_tree_path_free(path);
1639
1640         return(FALSE);
1641 }
1642
1643 static void accel_default_cb(GtkWidget *widget, gpointer data)
1644 {
1645         accel_store_populate();
1646
1647         g_idle_add((GSourceFunc)accel_default_scroll, data);
1648 }
1649
1650 void accel_remove_selection(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
1651 {
1652         gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1653 }
1654
1655 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
1656 {
1657         GtkAccelKey key;
1658         gchar *accel_path, *accel;
1659
1660         gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1661         gtk_accel_map_lookup_entry(accel_path, &key);
1662         accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1663
1664         gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1665
1666         gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1667         g_free(accel_path);
1668         g_free(accel);
1669 }
1670
1671 static void accel_reset_cb(GtkWidget *widget, gpointer data)
1672 {
1673         GtkTreeSelection *selection;
1674
1675         if (!accel_store) return;
1676         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1677         gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, NULL);
1678 }
1679
1680
1681
1682 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1683 {
1684         GtkWidget *label;
1685         GtkWidget *vbox;
1686         GtkWidget *scrolled;
1687         GtkWidget *viewport;
1688
1689         scrolled = gtk_scrolled_window_new(NULL, NULL);
1690         gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1691         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1692                                        GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1693         label = gtk_label_new(title);
1694         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1695         gtk_widget_show(scrolled);
1696
1697         viewport = gtk_viewport_new(NULL, NULL);
1698         gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1699         gtk_container_add(GTK_CONTAINER(scrolled), viewport);
1700         gtk_widget_show(viewport);
1701
1702         vbox = gtk_vbox_new(FALSE, 0);
1703         gtk_container_add(GTK_CONTAINER(viewport), vbox);
1704         gtk_widget_show(vbox);
1705
1706         return vbox;
1707 }
1708
1709 static void cache_standard_cb(GtkWidget *widget, gpointer data)
1710 {
1711         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1712                 {
1713                 c_options->thumbnails.spec_standard =TRUE;
1714                 c_options->thumbnails.cache_into_dirs = FALSE;
1715                 }
1716 }
1717
1718 static void cache_geeqie_cb(GtkWidget *widget, gpointer data)
1719 {
1720         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1721                 {
1722                 c_options->thumbnails.spec_standard =FALSE;
1723                 c_options->thumbnails.cache_into_dirs = FALSE;
1724                 }
1725 }
1726
1727 static void cache_local_cb(GtkWidget *widget, gpointer data)
1728 {
1729         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1730                 {
1731                 c_options->thumbnails.cache_into_dirs = TRUE;
1732                 c_options->thumbnails.spec_standard =FALSE;
1733                 }
1734 }
1735
1736 static void help_search_engine_entry_icon_cb(GtkEntry *entry, GtkEntryIconPosition pos,
1737                                                                         GdkEvent *event, gpointer userdata)
1738 {
1739         if (pos == GTK_ENTRY_ICON_PRIMARY)
1740                 {
1741                 gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1742                 }
1743         else
1744                 {
1745                 gtk_entry_set_text(GTK_ENTRY(userdata), "");
1746                 }
1747 }
1748
1749 static void star_rating_star_icon_cb(GtkEntry *entry, GtkEntryIconPosition pos,
1750                                                                         GdkEvent *event, gpointer userdata)
1751 {
1752         gchar *rating_symbol;
1753
1754         if (pos == GTK_ENTRY_ICON_PRIMARY)
1755                 {
1756                 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1757                 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1758                 g_free(rating_symbol);
1759                 }
1760         else
1761                 {
1762                 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1763                 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1764                 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1765                 }
1766 }
1767
1768 static void star_rating_rejected_icon_cb(GtkEntry *entry, GtkEntryIconPosition pos,
1769                                                                         GdkEvent *event, gpointer userdata)
1770 {
1771         gchar *rating_symbol;
1772
1773         if (pos == GTK_ENTRY_ICON_PRIMARY)
1774                 {
1775                 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1776                 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1777                 g_free(rating_symbol);
1778                 }
1779         else
1780                 {
1781                 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1782                 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1783                 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1784                 }
1785 }
1786
1787 static guint star_rating_symbol_test(GtkWidget *widget, gpointer data)
1788 {
1789         GtkContainer *hbox = data;
1790         GString *str = g_string_new(NULL);
1791         GtkEntry *hex_code_entry;
1792         gchar *hex_code_full;
1793         gchar **hex_code;
1794         GList *list;
1795         guint64 hex_value = 0;
1796
1797         list = gtk_container_get_children(hbox);
1798
1799         hex_code_entry = g_list_nth_data(list, 2);
1800         hex_code_full = g_strdup(gtk_entry_get_text(hex_code_entry));
1801
1802         hex_code = g_strsplit(hex_code_full, "+", 2);
1803         if (hex_code[0] && hex_code[1])
1804                 {
1805                 hex_value = strtoull(hex_code[1], NULL, 16);
1806                 }
1807         if (!hex_value || hex_value > 0x10FFFF)
1808                 {
1809                 hex_value = 0x003F; // Unicode 'Question Mark'
1810                 }
1811         str = g_string_append_unichar(str, (gunichar)hex_value);
1812         gtk_label_set_text(g_list_nth_data(list, 1), str->str);
1813
1814         g_strfreev(hex_code);
1815         g_string_free(str, TRUE);
1816         g_free(hex_code_full);
1817
1818         return hex_value;
1819 }
1820
1821 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1822 {
1823         guint64 star_symbol;
1824
1825         star_symbol = star_rating_symbol_test(widget, data);
1826         c_options->star_rating.star = star_symbol;
1827 }
1828
1829 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1830 {
1831         guint64 rejected_symbol;
1832
1833         rejected_symbol = star_rating_symbol_test(widget, data);
1834         c_options->star_rating.rejected = rejected_symbol;
1835 }
1836
1837 /* general options tab */
1838 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1839 typedef struct _TZData TZData;
1840 struct _TZData
1841 {
1842         GenericDialog *gd;
1843         GCancellable *cancellable;
1844
1845         GtkWidget *progress;
1846         GFile *tmp_g_file;
1847         GFile *timezone_database_gq;
1848         gchar *timezone_database_user;
1849 };
1850
1851 static void config_tab_general(GtkWidget *notebook)
1852 {
1853         GtkWidget *vbox;
1854         GtkWidget *hbox;
1855         GtkWidget *group;
1856         GtkWidget *group_frame;
1857         GtkWidget *subgroup;
1858         GtkWidget *button;
1859         GtkWidget *ct_button;
1860         GtkWidget *table;
1861         GtkWidget *spin;
1862         gint hours, minutes, remainder;
1863         gdouble seconds;
1864         GtkWidget *star_rating_entry;
1865         GString *str;
1866         gchar *rating_symbol;
1867         gchar *path;
1868         gchar *basename;
1869         GNetworkMonitor *net_mon;
1870         GSocketConnectable *geeqie_org;
1871         gboolean internet_available;
1872         TZData *tz;
1873
1874         vbox = scrolled_notebook_page(notebook, _("General"));
1875
1876         group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1877
1878         table = pref_table_new(group, 2, 2, FALSE, FALSE);
1879         add_thumb_size_menu(table, 0, 0, _("Size:"));
1880         add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1881
1882         ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
1883                                           options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1884
1885         subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1886         pref_checkbox_link_sensitivity(ct_button, subgroup);
1887
1888         c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1889         c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1890         group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1891                                                                                 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1892         button = pref_radiobutton_new(group_frame, NULL,  get_thumbnails_cache_dir(),
1893                                                         !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1894                                                         G_CALLBACK(cache_geeqie_cb), NULL);
1895
1896         group_frame = pref_frame_new(subgroup, TRUE,
1897                                                         _("Store thumbnails local to image folder (non-standard)"),
1898                                                         GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1899         pref_radiobutton_new(group_frame, button, "*/.thumbnails",
1900                                                         !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
1901                                                         G_CALLBACK(cache_local_cb), NULL);
1902
1903         group_frame = pref_frame_new(subgroup, TRUE,
1904                                                         _("Use standard thumbnail style and cache, shared with other applications"),
1905                                                         GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1906         pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
1907                                                         options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1908                                                         G_CALLBACK(cache_standard_cb), NULL);
1909
1910         pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
1911                               options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
1912
1913         spin = pref_spin_new_int(group, _("Collection preview:"), NULL,
1914                                  1, 999, 1,
1915                                  options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
1916         gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
1917
1918 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
1919         pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
1920                               options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
1921
1922 //      pref_checkbox_new_int(group, _("Ignore embedded metadata if size is too small"),
1923 //                            options->thumbnails.use_ft_metadata_small, &c_options->thumbnails.use_ft_metadata_small);
1924 #endif
1925
1926         pref_spacer(group, PREF_PAD_GROUP);
1927
1928         group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
1929
1930         c_options->star_rating.star = options->star_rating.star;
1931         c_options->star_rating.rejected = options->star_rating.rejected;
1932
1933         str = g_string_new(NULL);
1934         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1935         pref_label_new(hbox, "Star character: ");
1936         str = g_string_append_unichar(str, options->star_rating.star);
1937         pref_label_new(hbox, g_strdup(str->str));
1938         rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
1939         star_rating_entry = gtk_entry_new();
1940         gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
1941         gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
1942         gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
1943         gtk_widget_show(star_rating_entry);
1944         button = pref_button_new(NULL, NULL, _("Set"), FALSE,
1945                                         G_CALLBACK(star_rating_star_test_cb), hbox);
1946         gtk_widget_set_tooltip_text(button, _("Display selected character"));
1947         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1948         gtk_widget_show(button);
1949         gtk_widget_set_tooltip_text(star_rating_entry, _("Hexadecimal representation of a Unicode character. A list of all Unicode characters may be found on the Internet."));
1950         gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
1951                                                 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
1952         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1953                                                 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
1954         gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
1955                                                 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_REVERT_TO_SAVED);
1956         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1957                                                 GTK_ENTRY_ICON_PRIMARY, _("Default"));
1958         g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
1959                                                 G_CALLBACK(star_rating_star_icon_cb),
1960                                                 star_rating_entry);
1961
1962         g_string_free(str, TRUE);
1963         g_free(rating_symbol);
1964
1965         str = g_string_new(NULL);
1966         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1967         pref_label_new(hbox, "Rejected character: ");
1968         str = g_string_append_unichar(str, options->star_rating.rejected);
1969         pref_label_new(hbox, g_strdup(str->str));
1970         rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
1971         star_rating_entry = gtk_entry_new();
1972         gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
1973         gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
1974         gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
1975         gtk_widget_show(star_rating_entry);
1976         button = pref_button_new(NULL, NULL, _("Set"), FALSE,
1977                                         G_CALLBACK(star_rating_rejected_test_cb), hbox);
1978         gtk_widget_set_tooltip_text(button, _("Display selected character"));
1979         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1980         gtk_widget_show(button);
1981         gtk_widget_set_tooltip_text(star_rating_entry, _("Hexadecimal representation of a Unicode character. A list of all Unicode characters may be found on the Internet."));
1982         gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
1983                                                 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
1984         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1985                                                 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
1986         gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
1987                                                 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_REVERT_TO_SAVED);
1988         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1989                                                 GTK_ENTRY_ICON_PRIMARY, _("Default"));
1990         g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
1991                                                 G_CALLBACK(star_rating_rejected_icon_cb),
1992                                                 star_rating_entry);
1993
1994         g_string_free(str, TRUE);
1995         g_free(rating_symbol);
1996
1997         pref_spacer(group, PREF_PAD_GROUP);
1998
1999         group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2000
2001         c_options->slideshow.delay = options->slideshow.delay;
2002         hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2003         remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2004         minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2005         seconds = (gdouble)(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2006                                                                                         SLIDESHOW_SUBSECOND_PRECISION;
2007
2008         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2009
2010         spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), NULL,
2011                                                                                 0, 23, 1.0, 0,
2012                                                                                 options->slideshow.delay ? hours : 0.0,
2013                                                                                 G_CALLBACK(slideshow_delay_hours_cb), NULL);
2014         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2015         spin = pref_spin_new(hbox, ":" , NULL,
2016                                                                                 0, 59, 1.0, 0,
2017                                                                                 options->slideshow.delay ? minutes: 0.0,
2018                                                                                 G_CALLBACK(slideshow_delay_minutes_cb), NULL);
2019         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2020         spin = pref_spin_new(hbox, ":", NULL,
2021                                                                                 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2022                                                                                 options->slideshow.delay ? seconds : 10.0,
2023                                                                                 G_CALLBACK(slideshow_delay_seconds_cb), NULL);
2024         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2025
2026         pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2027         pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2028
2029         pref_spacer(group, PREF_PAD_GROUP);
2030
2031         group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2032
2033         pref_spin_new_int(group, _("Decoded image cache size (Mb):"), NULL,
2034                           0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2035         pref_checkbox_new_int(group, _("Preload next image"),
2036                               options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2037
2038         pref_checkbox_new_int(group, _("Refresh on file change"),
2039                               options->update_on_time_change, &c_options->update_on_time_change);
2040
2041         pref_spacer(group, PREF_PAD_GROUP);
2042
2043         group = pref_group_new(vbox, FALSE, _("Info sidebar heights"), GTK_ORIENTATION_VERTICAL);
2044         pref_label_new(group, _("NOTE! Geeqie must be restarted for changes to take effect"));
2045         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2046         pref_spin_new_int(hbox, _("Keywords:"), NULL,
2047                                  1, 9999, 1,
2048                                  options->info_keywords.height, &c_options->info_keywords.height);
2049         pref_spin_new_int(hbox, _("Title:"), NULL,
2050                                  1, 9999, 1,
2051                                  options->info_title.height, &c_options->info_title.height);
2052         pref_spin_new_int(hbox, _("Comment:"), NULL,
2053                                  1, 9999, 1,
2054                                  options->info_comment.height, &c_options->info_comment.height);
2055         pref_spin_new_int(hbox, _("Rating:"), NULL,
2056                                  1, 9999, 1,
2057                                  options->info_rating.height, &c_options->info_rating.height);
2058
2059         pref_spacer(group, PREF_PAD_GROUP);
2060
2061         group = pref_group_new(vbox, FALSE, _("Show predefined keyword tree"), GTK_ORIENTATION_VERTICAL);
2062
2063         pref_checkbox_new_int(group, _("Show predefined keyword tree (NOTE! Geeqie must be restarted for change to take effect)"),
2064                                 options->show_predefined_keyword_tree, &c_options->show_predefined_keyword_tree);
2065
2066         pref_spacer(group, PREF_PAD_GROUP);
2067
2068         net_mon = g_network_monitor_get_default();
2069         geeqie_org = g_network_address_parse_uri(GQ_WEBSITE, 80, NULL);
2070         internet_available = g_network_monitor_can_reach(net_mon, geeqie_org, NULL, NULL);
2071         g_object_unref(geeqie_org);
2072
2073         group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2074         hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2075
2076         if (!internet_available)
2077                 {
2078                 gtk_widget_set_sensitive(group, FALSE);
2079                 }
2080
2081         tz = g_new0(TZData, 1);
2082
2083         path = path_from_utf8(TIMEZONE_DATABASE);
2084         basename = g_path_get_basename(path);
2085         tz->timezone_database_user = g_build_filename(get_rc_dir(), basename, NULL);
2086         g_free(path);
2087         g_free(basename);
2088
2089         if (isfile(tz->timezone_database_user))
2090                 {
2091                 button = pref_button_new(GTK_WIDGET(hbox), NULL, _("Update"), FALSE, G_CALLBACK(timezone_database_install_cb), tz);
2092                 }
2093         else
2094                 {
2095                 button = pref_button_new(GTK_WIDGET(hbox), NULL, _("Install"), FALSE, G_CALLBACK(timezone_database_install_cb), tz);
2096                 }
2097
2098         if (!internet_available)
2099                 {
2100                 gtk_widget_set_tooltip_text(button, _("No Internet connection!\nThe timezone database is used to display exif time and date\ncorrected for UTC offset and Daylight Saving Time"));
2101                 }
2102         else
2103                 {
2104                 gtk_widget_set_tooltip_text(button, _("The timezone database is used to display exif time and date\ncorrected for UTC offset and Daylight Saving Time"));
2105                 }
2106         gtk_widget_show(button);
2107
2108         pref_spacer(group, PREF_PAD_GROUP);
2109
2110         group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2111
2112         help_search_engine_entry = gtk_entry_new();
2113         gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2114         gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2115         gtk_widget_show(help_search_engine_entry);
2116
2117         gtk_widget_set_tooltip_text(help_search_engine_entry, _("The format varies between search engines, e.g the format may be:\nhttps://www.search_engine.com/search?q=site:geeqie.org/help\nhttps://www.search_engine.com/?q=site:geeqie.org/help"));
2118
2119         gtk_entry_set_icon_from_stock(GTK_ENTRY(help_search_engine_entry),
2120                                                 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
2121         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2122                                                 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2123         gtk_entry_set_icon_from_stock(GTK_ENTRY(help_search_engine_entry),
2124                                                 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_REVERT_TO_SAVED);
2125         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2126                                                 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2127         g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2128                                                 G_CALLBACK(help_search_engine_entry_icon_cb),
2129                                                 help_search_engine_entry);
2130 }
2131
2132 /* image tab */
2133 static void config_tab_image(GtkWidget *notebook)
2134 {
2135         GtkWidget *hbox;
2136         GtkWidget *vbox;
2137         GtkWidget *group;
2138         GtkWidget *ct_button;
2139         GtkWidget *enlargement_button;
2140         GtkWidget *table;
2141         GtkWidget *spin;
2142
2143         vbox = scrolled_notebook_page(notebook, _("Image"));
2144
2145         group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2146
2147         table = pref_table_new(group, 2, 1, FALSE, FALSE);
2148         add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2149
2150 #ifdef HAVE_CLUTTER
2151         pref_checkbox_new_int(group, _("Use GPU acceleration via Clutter library"),
2152                               options->image.use_clutter_renderer, &c_options->image.use_clutter_renderer);
2153 #endif
2154
2155         pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2156                               options->image.zoom_2pass, &c_options->image.zoom_2pass);
2157
2158         c_options->image.zoom_increment = options->image.zoom_increment;
2159         spin = pref_spin_new(group, _("Zoom increment:"), NULL,
2160                              0.01, 4.0, 0.01, 2, (gdouble)options->image.zoom_increment / 100.0,
2161                              G_CALLBACK(zoom_increment_cb), NULL);
2162         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2163
2164         group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2165
2166         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2167         enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2168                               options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2169         spin = pref_spin_new_int(hbox, NULL, NULL,
2170                                  100, 999, 1,
2171                                  options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2172         pref_checkbox_link_sensitivity(enlargement_button, spin);
2173         gtk_widget_set_tooltip_text(GTK_WIDGET(hbox), _("Enable this to allow Geeqie to increase the image size for images that are smaller than the current view area when the zoom is set to \"Fit image to window\". This value sets the maximum expansion permitted in percent i.e. 100% is full-size."));
2174
2175         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2176         ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2177                                           options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2178         spin = pref_spin_new_int(hbox, NULL, NULL,
2179                                  10, 150, 1,
2180                                  options->image.max_autofit_size, &c_options->image.max_autofit_size);
2181         pref_checkbox_link_sensitivity(ct_button, spin);
2182         gtk_widget_set_tooltip_text(GTK_WIDGET(hbox), _("This value will set the virtual size of the window when \"Fit image to window\" is set. Instead of using the actual size of the window, the specified percentage of the window will be used. It allows one to keep a border around the image (values lower than 100%) or to auto zoom the image (values greater than 100%). It affects fullscreen mode too."));
2183
2184         group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2185
2186         pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2187                               options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2188
2189         pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2190                               options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2191
2192         pref_color_button_new(group, _("Border color"), &options->image.border_color,
2193                               G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2194
2195         c_options->image.border_color = options->image.border_color;
2196
2197         pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2198                               G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2199
2200         pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2201                               G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2202
2203         c_options->image.alpha_color_1 = options->image.alpha_color_1;
2204         c_options->image.alpha_color_2 = options->image.alpha_color_2;
2205
2206         group = pref_group_new(vbox, FALSE, _("Convenience"), GTK_ORIENTATION_VERTICAL);
2207
2208         pref_checkbox_new_int(group, _("Auto rotate proofs using Exif information"),
2209                               options->image.exif_proof_rotate_enable, &c_options->image.exif_proof_rotate_enable);
2210 }
2211
2212 /* windows tab */
2213 static void config_tab_windows(GtkWidget *notebook)
2214 {
2215         GtkWidget *hbox;
2216         GtkWidget *vbox;
2217         GtkWidget *group;
2218         GtkWidget *button;
2219         GtkWidget *ct_button;
2220         GtkWidget *spin;
2221
2222         vbox = scrolled_notebook_page(notebook, _("Windows"));
2223
2224         group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2225
2226         ct_button = pref_checkbox_new_int(group, _("Remember window positions"),
2227                                           options->save_window_positions, &c_options->save_window_positions);
2228
2229         button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2230                                        options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2231         pref_checkbox_link_sensitivity(ct_button, button);
2232
2233         pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2234                               options->tools_restore_state, &c_options->tools_restore_state);
2235
2236         pref_checkbox_new_int(group, _("Remember dialog window positions"),
2237                               options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2238
2239         pref_checkbox_new_int(group, _("Show window IDs"),
2240                               options->show_window_ids, &c_options->show_window_ids);
2241
2242         group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2243
2244         pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2245                               options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2246
2247         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2248         ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2249                                           options->image.limit_window_size, &c_options->image.limit_window_size);
2250         spin = pref_spin_new_int(hbox, NULL, NULL,
2251                                  10, 150, 1,
2252                                  options->image.max_window_size, &c_options->image.max_window_size);
2253         pref_checkbox_link_sensitivity(ct_button, spin);
2254
2255         group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2256
2257         c_options->fullscreen.screen = options->fullscreen.screen;
2258         c_options->fullscreen.above = options->fullscreen.above;
2259         hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2260         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2261         gtk_widget_show(hbox);
2262
2263         pref_checkbox_new_int(group, _("Smooth image flip"),
2264                               options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2265         pref_checkbox_new_int(group, _("Disable screen saver"),
2266                               options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2267 }
2268
2269 #define PRE_FORMATTED_COLUMNS 5
2270 static void config_tab_osd(GtkWidget *notebook)
2271 {
2272         GtkWidget *hbox;
2273         GtkWidget *vbox;
2274         GtkWidget *vbox_buttons;
2275         GtkWidget *group;
2276         GtkWidget *button;
2277         GtkWidget *image_overlay_template_view;
2278         GtkWidget *scrolled;
2279         GtkWidget *scrolled_pre_formatted;
2280         GtkTextBuffer *buffer;
2281         GtkWidget *label;
2282         GtkWidget *     subgroup;
2283         gint i = 0;
2284         gint rows = 0;
2285         gint cols = 0;
2286
2287         vbox = scrolled_notebook_page(notebook, _("OSD"));
2288
2289         image_overlay_template_view = gtk_text_view_new();
2290
2291         group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2292
2293         subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2294
2295         scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2296         gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2297         gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2298         gtk_widget_show(scrolled_pre_formatted);
2299         gtk_widget_show(subgroup);
2300
2301         pref_line(group, PREF_PAD_GAP);
2302
2303         pref_label_new(group, _("Image overlay template"));
2304
2305         scrolled = gtk_scrolled_window_new(NULL, NULL);
2306         gtk_widget_set_size_request(scrolled, 200, 150);
2307         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2308         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2309                                                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2310         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2311         gtk_widget_show(scrolled);
2312
2313         gtk_widget_set_tooltip_markup(image_overlay_template_view,
2314                                         _("Extensive formatting options are shown in the Help file"));
2315
2316         gtk_container_add(GTK_CONTAINER(scrolled), image_overlay_template_view);
2317         gtk_widget_show(image_overlay_template_view);
2318
2319         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2320
2321 #if GTK_CHECK_VERSION(3,4,0)
2322         button = pref_button_new(NULL, GTK_STOCK_SELECT_FONT, _("Font"), FALSE,
2323                                  G_CALLBACK(image_overlay_set_font_cb), notebook);
2324 #else
2325         button = gtk_font_button_new();
2326         gtk_font_button_set_title(GTK_FONT_BUTTON(button), "Image Overlay Font");
2327         gtk_font_button_set_font_name(GTK_FONT_BUTTON(button), options->image_overlay.font);
2328         g_signal_connect(G_OBJECT(button), "font-set",
2329                                  G_CALLBACK(image_overlay_set_font_cb),NULL);
2330 #endif
2331         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2332         gtk_widget_show(button);
2333
2334         button = pref_button_new(NULL, GTK_STOCK_COLOR_PICKER, _("Text"), FALSE,
2335                                  G_CALLBACK(image_overlay_set_text_colour_cb), NULL);
2336         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2337         gtk_widget_show(button);
2338
2339         button = pref_button_new(NULL, GTK_STOCK_COLOR_PICKER, _("Background"), FALSE,
2340                                  G_CALLBACK(image_overlay_set_background_colour_cb), NULL);
2341         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2342         gtk_widget_show(button);
2343         image_overlay_set_text_colours();
2344
2345         button = pref_button_new(NULL, NULL, _("Defaults"), FALSE,
2346                                  G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2347         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2348         gtk_widget_show(button);
2349
2350         button = pref_button_new(NULL, GTK_STOCK_HELP, NULL, FALSE,
2351                                  G_CALLBACK(image_overlay_help_cb), NULL);
2352         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2353         gtk_widget_show(button);
2354
2355         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2356         if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2357         g_signal_connect(G_OBJECT(buffer), "changed",
2358                          G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2359
2360         pref_line(group, PREF_PAD_GAP);
2361
2362         group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2363         hbox = gtk_hbox_new(FALSE, 0);
2364         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2365         gtk_widget_show(hbox);
2366         label = gtk_label_new(_("%Exif.Image.Orientation%"));
2367         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2368         gtk_widget_show(label);
2369         pref_spacer(group,TRUE);
2370
2371         group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2372         hbox = gtk_hbox_new(FALSE, 0);
2373         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2374         gtk_widget_show(hbox);
2375         label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2376         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2377         gtk_widget_show(label);
2378         pref_spacer(group,TRUE);
2379
2380         group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2381         hbox = gtk_hbox_new(FALSE, 0);
2382         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2383         gtk_widget_show(hbox);
2384         label = gtk_label_new(_("%path:39%"));
2385         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2386         gtk_widget_show(label);
2387         pref_spacer(group,TRUE);
2388
2389         group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2390         hbox = gtk_hbox_new(FALSE, 0);
2391         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2392         gtk_widget_show(hbox);
2393         label = gtk_label_new(_("Text shown only if the field is non-null:\n%formatted.Aperture:F no. * setting%\n %formatted.Aperture:10:F no. * setting%"));
2394         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2395         gtk_widget_show(label);
2396         pref_spacer(group,TRUE);
2397
2398         group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2399         hbox = gtk_hbox_new(FALSE, 0);
2400         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2401         gtk_widget_show(hbox);
2402         label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2403         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2404         gtk_widget_show(label);
2405 }
2406
2407 static GtkTreeModel *create_class_model(void)
2408 {
2409         GtkListStore *model;
2410         GtkTreeIter iter;
2411         gint i;
2412
2413         /* create list store */
2414         model = gtk_list_store_new(1, G_TYPE_STRING);
2415         for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2416                 {
2417                 gtk_list_store_append(model, &iter);
2418                 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2419                 }
2420         return GTK_TREE_MODEL (model);
2421 }
2422
2423
2424 /* filtering tab */
2425 static void config_tab_files(GtkWidget *notebook)
2426 {
2427         GtkWidget *hbox;
2428         GtkWidget *frame;
2429         GtkWidget *vbox;
2430         GtkWidget *group;
2431         GtkWidget *button;
2432         GtkWidget *ct_button;
2433         GtkWidget *scrolled;
2434         GtkWidget *filter_view;
2435         GtkCellRenderer *renderer;
2436         GtkTreeSelection *selection;
2437         GtkTreeViewColumn *column;
2438
2439         vbox = scrolled_notebook_page(notebook, _("Files"));
2440
2441         group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2442
2443         pref_checkbox_new_int(group, _("Show hidden files or folders"),
2444                               options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2445         pref_checkbox_new_int(group, _("Show parent folder (..)"),
2446                               options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2447         pref_checkbox_new_int(group, _("Case sensitive sort"),
2448                               options->file_sort.case_sensitive, &c_options->file_sort.case_sensitive);
2449         pref_checkbox_new_int(group, _("Natural sort order"),
2450                                           options->file_sort.natural, &c_options->file_sort.natural);
2451         pref_checkbox_new_int(group, _("Disable file extension checks"),
2452                               options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2453
2454         ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2455                                           options->file_filter.disable, &c_options->file_filter.disable);
2456
2457
2458         group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2459
2460         sidecar_ext_entry = gtk_entry_new();
2461         gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2462         gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2463         gtk_widget_show(sidecar_ext_entry);
2464
2465         group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2466
2467         frame = pref_group_parent(group);
2468         g_signal_connect(G_OBJECT(ct_button), "toggled",
2469                          G_CALLBACK(filter_disable_cb), frame);
2470         gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2471
2472         scrolled = gtk_scrolled_window_new(NULL, NULL);
2473         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2474         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2475         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2476         gtk_widget_show(scrolled);
2477
2478         filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2479         filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2480         g_object_unref(filter_store);
2481         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2482         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2483
2484         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2485
2486         column = gtk_tree_view_column_new();
2487         gtk_tree_view_column_set_title(column, _("Filter"));
2488         gtk_tree_view_column_set_resizable(column, TRUE);
2489
2490         renderer = gtk_cell_renderer_toggle_new();
2491         g_signal_connect(G_OBJECT(renderer), "toggled",
2492                          G_CALLBACK(filter_store_enable_cb), filter_store);
2493         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2494         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2495                                                 GINT_TO_POINTER(FE_ENABLE), NULL);
2496
2497         renderer = gtk_cell_renderer_text_new();
2498         g_signal_connect(G_OBJECT(renderer), "edited",
2499                          G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2500         gtk_tree_view_column_pack_start(column, renderer, TRUE);
2501         g_object_set(G_OBJECT(renderer), "editable", (gboolean)TRUE, NULL);
2502         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2503                                                 GINT_TO_POINTER(FE_EXTENSION), NULL);
2504         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2505
2506         column = gtk_tree_view_column_new();
2507         gtk_tree_view_column_set_title(column, _("Description"));
2508         gtk_tree_view_column_set_resizable(column, TRUE);
2509         gtk_tree_view_column_set_fixed_width(column, 200);
2510         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2511
2512         renderer = gtk_cell_renderer_text_new();
2513         g_signal_connect(G_OBJECT(renderer), "edited",
2514                          G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2515         g_object_set(G_OBJECT(renderer), "editable", (gboolean)TRUE, NULL);
2516         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2517         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2518                                                 GINT_TO_POINTER(FE_DESCRIPTION), NULL);
2519         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2520
2521         column = gtk_tree_view_column_new();
2522         gtk_tree_view_column_set_title(column, _("Class"));
2523         gtk_tree_view_column_set_resizable(column, TRUE);
2524         renderer = gtk_cell_renderer_combo_new();
2525         g_object_set(G_OBJECT(renderer), "editable", (gboolean)TRUE,
2526                                          "model", create_class_model(),
2527                                          "text-column", 0,
2528                                          "has-entry", FALSE,
2529                                          NULL);
2530
2531         g_signal_connect(G_OBJECT(renderer), "edited",
2532                          G_CALLBACK(filter_store_class_edit_cb), filter_store);
2533         gtk_tree_view_column_pack_start(column, renderer, TRUE);
2534         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2535                                                 GINT_TO_POINTER(FE_CLASS), NULL);
2536         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2537
2538         column = gtk_tree_view_column_new();
2539         gtk_tree_view_column_set_title(column, _("Writable"));
2540         gtk_tree_view_column_set_resizable(column, FALSE);
2541         renderer = gtk_cell_renderer_toggle_new();
2542         g_signal_connect(G_OBJECT(renderer), "toggled",
2543                          G_CALLBACK(filter_store_writable_cb), filter_store);
2544         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2545         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2546                                                 GINT_TO_POINTER(FE_WRITABLE), NULL);
2547         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2548
2549         column = gtk_tree_view_column_new();
2550         gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2551         gtk_tree_view_column_set_resizable(column, FALSE);
2552         renderer = gtk_cell_renderer_toggle_new();
2553         g_signal_connect(G_OBJECT(renderer), "toggled",
2554                          G_CALLBACK(filter_store_sidecar_cb), filter_store);
2555         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2556         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2557                                                 GINT_TO_POINTER(FE_ALLOW_SIDECAR), NULL);
2558         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2559
2560
2561         filter_store_populate();
2562         gtk_container_add(GTK_CONTAINER(scrolled), filter_view);
2563         gtk_widget_show(filter_view);
2564
2565         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2566
2567         button = pref_button_new(NULL, NULL, _("Defaults"), FALSE,
2568                                  G_CALLBACK(filter_default_cb), filter_view);
2569         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2570         gtk_widget_show(button);
2571
2572         button = pref_button_new(NULL, GTK_STOCK_REMOVE, NULL, FALSE,
2573                                  G_CALLBACK(filter_remove_cb), filter_view);
2574         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2575         gtk_widget_show(button);
2576
2577         button = pref_button_new(NULL, GTK_STOCK_ADD, NULL, FALSE,
2578                                  G_CALLBACK(filter_add_cb), filter_view);
2579         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2580         gtk_widget_show(button);
2581 }
2582
2583 /* metadata tab */
2584 static void config_tab_metadata(GtkWidget *notebook)
2585 {
2586         GtkWidget *vbox;
2587         GtkWidget *hbox;
2588         GtkWidget *group;
2589         GtkWidget *ct_button;
2590         GtkWidget *label;
2591         gchar *text;
2592
2593         vbox = scrolled_notebook_page(notebook, _("Metadata"));
2594
2595
2596         group = pref_group_new(vbox, FALSE, _("Metadata writing process"), GTK_ORIENTATION_VERTICAL);
2597 #ifndef HAVE_EXIV2
2598         label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2599 #endif
2600         label = pref_label_new(group, _("Metadata are written in the following order. The process ends after first success."));
2601         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2602
2603         ct_button = pref_checkbox_new_int(group, _("1) Save metadata in image files, or sidecar files, according to the XMP standard"),
2604                               options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2605 #ifndef HAVE_EXIV2
2606         gtk_widget_set_sensitive(ct_button, FALSE);
2607 #endif
2608
2609         pref_checkbox_new_int(group, _("2) Save metadata in '.metadata' folder, local to image folder (non-standard)"),
2610                               options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2611
2612         text = g_strdup_printf(_("3) Save metadata in Geeqie private directory '%s'"), get_metadata_cache_dir());
2613         label = pref_label_new(group, text);
2614         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2615         gtk_misc_set_padding(GTK_MISC(label), 22, 0);
2616         g_free(text);
2617
2618         group = pref_group_new(vbox, FALSE, _("Step 1: Write to image files"), GTK_ORIENTATION_VERTICAL);
2619 #ifndef HAVE_EXIV2
2620         gtk_widget_set_sensitive(group, FALSE);
2621 #endif
2622
2623         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2624         pref_checkbox_link_sensitivity(ct_button, hbox);
2625
2626         pref_checkbox_new_int(hbox, _("Store metadata also in legacy IPTC tags (converted according to IPTC4XMP standard)"),
2627                               options->metadata.save_legacy_IPTC, &c_options->metadata.save_legacy_IPTC);
2628
2629         pref_checkbox_new_int(hbox, _("Warn if the image files are unwritable"),
2630                               options->metadata.warn_on_write_problems, &c_options->metadata.warn_on_write_problems);
2631
2632         pref_checkbox_new_int(hbox, _("Ask before writing to image files"),
2633                               options->metadata.confirm_write, &c_options->metadata.confirm_write);
2634
2635         pref_checkbox_new_int(hbox, _("Create sidecar files named image.ext.xmp (as opposed to image.xmp)"),
2636                               options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2637
2638         group = pref_group_new(vbox, FALSE, _("Step 2 and 3: write to Geeqie private files"), GTK_ORIENTATION_VERTICAL);
2639 #ifndef HAVE_EXIV2
2640         gtk_widget_set_sensitive(group, FALSE);
2641 #endif
2642
2643         pref_checkbox_new_int(group, _("Use GQview legacy metadata format (supports only keywords and comments) instead of XMP"),
2644                               options->metadata.save_legacy_format, &c_options->metadata.save_legacy_format);
2645
2646
2647         group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2648         pref_checkbox_new_int(group, _("Write the same description tags (keywords, comment, etc.) to all grouped sidecars"),
2649                               options->metadata.sync_grouped_files, &c_options->metadata.sync_grouped_files);
2650
2651         pref_checkbox_new_int(group, _("Allow keywords to differ only in case"),
2652                               options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2653
2654         ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"),
2655                               options->metadata.write_orientation, &c_options->metadata.write_orientation);
2656 #ifndef HAVE_EXIV2
2657         gtk_widget_set_sensitive(ct_button, FALSE);
2658 #endif
2659
2660         group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2661
2662         ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"),
2663                               options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2664
2665         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2666         pref_checkbox_link_sensitivity(ct_button, hbox);
2667
2668         pref_spin_new_int(hbox, _("Timeout (seconds):"), NULL, 0, 900, 1,
2669                               options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2670
2671         pref_checkbox_new_int(group, _("Write metadata on image change"),
2672                               options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2673
2674         pref_checkbox_new_int(group, _("Write metadata on directory change"),
2675                               options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2676
2677         group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2678
2679         ct_button = pref_checkbox_new_int(group, _("Read metadata in background"),
2680                                           options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2681         gtk_widget_set_tooltip_text(ct_button,"On folder change, read DateTimeOriginal, DateTimeDigitized and Star Rating in the idle loop.\nIf this is not selected, initial loading of the folder will be faster but sorting on these items will be slower");
2682 }
2683
2684 /* keywords tab */
2685
2686 typedef struct _KeywordFindData KeywordFindData;
2687 struct _KeywordFindData
2688 {
2689         GenericDialog *gd;
2690
2691         GList *list;
2692         GList *list_dir;
2693
2694         GtkWidget *button_close;
2695         GtkWidget *button_stop;
2696         GtkWidget *button_start;
2697         GtkWidget *progress;
2698         GtkWidget *spinner;
2699
2700         GtkWidget *group;
2701         GtkWidget *entry;
2702
2703         gboolean recurse;
2704
2705         guint idle_id; /* event source id */
2706 };
2707
2708 #define KEYWORD_DIALOG_WIDTH 400
2709
2710 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
2711 {
2712         GList *list_d = NULL;
2713         GList *list_f = NULL;
2714
2715         if (kfd->recurse)
2716                 {
2717                 filelist_read(dir_fd, &list_f, &list_d);
2718                 }
2719         else
2720                 {
2721                 filelist_read(dir_fd, &list_f, NULL);
2722                 }
2723
2724         list_f = filelist_filter(list_f, FALSE);
2725         list_d = filelist_filter(list_d, TRUE);
2726
2727         kfd->list = g_list_concat(list_f, kfd->list);
2728         kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
2729 }
2730
2731 static void keywords_find_reset(KeywordFindData *kfd)
2732 {
2733         filelist_free(kfd->list);
2734         kfd->list = NULL;
2735
2736         filelist_free(kfd->list_dir);
2737         kfd->list_dir = NULL;
2738 }
2739
2740 static void keywords_find_close_cb(GenericDialog *fd, gpointer data)
2741 {
2742         KeywordFindData *kfd = data;
2743
2744         if (!gtk_widget_get_sensitive(kfd->button_close)) return;
2745
2746         keywords_find_reset(kfd);
2747         generic_dialog_close(kfd->gd);
2748         g_free(kfd);
2749 }
2750
2751 static void keywords_find_finish(KeywordFindData *kfd)
2752 {
2753         keywords_find_reset(kfd);
2754
2755         gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
2756         spinner_set_interval(kfd->spinner, -1);
2757
2758         gtk_widget_set_sensitive(kfd->group, TRUE);
2759         gtk_widget_set_sensitive(kfd->button_start, TRUE);
2760         gtk_widget_set_sensitive(kfd->button_stop, FALSE);
2761         gtk_widget_set_sensitive(kfd->button_close, TRUE);
2762 }
2763
2764 static void keywords_find_stop_cb(GenericDialog *fd, gpointer data)
2765 {
2766         KeywordFindData *kfd = data;
2767
2768         g_idle_remove_by_data(kfd);
2769
2770         keywords_find_finish(kfd);
2771 }
2772
2773 static gboolean keywords_find_file(gpointer data)
2774 {
2775         KeywordFindData *kfd = data;
2776         GtkTextIter iter;
2777         GtkTextBuffer *buffer;
2778         gchar *tmp;
2779         GList *keywords;
2780
2781         if (kfd->list)
2782                 {
2783                 FileData *fd;
2784
2785                 fd = kfd->list->data;
2786                 kfd->list = g_list_remove(kfd->list, fd);
2787
2788                 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
2789                 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
2790
2791                 while (keywords)
2792                         {
2793                         gtk_text_buffer_get_end_iter(buffer, &iter);
2794                         tmp = g_strconcat(keywords->data, "\n", NULL);
2795                         gtk_text_buffer_insert(buffer, &iter, tmp, -1);
2796                         g_free(tmp);
2797                         keywords = keywords->next;
2798                         }
2799
2800                 gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
2801                 file_data_unref(fd);
2802                 string_list_free(keywords);
2803
2804                 return (TRUE);
2805                 }
2806         else if (kfd->list_dir)
2807                 {
2808                 FileData *fd;
2809
2810                 fd = kfd->list_dir->data;
2811                 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
2812
2813                 keywords_find_folder(kfd, fd);
2814
2815                 file_data_unref(fd);
2816
2817                 return TRUE;
2818                 }
2819
2820         keywords_find_finish(kfd);
2821
2822         return FALSE;
2823 }
2824
2825 static void keywords_find_start_cb(GenericDialog *fd, gpointer data)
2826 {
2827         KeywordFindData *kfd = data;
2828         gchar *path;
2829
2830         if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
2831
2832         path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
2833         parse_out_relatives(path);
2834
2835         if (!isdir(path))
2836                 {
2837                 warning_dialog(_("Invalid folder"),
2838                                 _("The specified folder can not be found."),
2839                                 GTK_STOCK_DIALOG_WARNING, kfd->gd->dialog);
2840                 }
2841         else
2842                 {
2843                 FileData *dir_fd;
2844
2845                 gtk_widget_set_sensitive(kfd->group, FALSE);
2846                 gtk_widget_set_sensitive(kfd->button_start, FALSE);
2847                 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
2848                 gtk_widget_set_sensitive(kfd->button_close, FALSE);
2849                 spinner_set_interval(kfd->spinner, SPINNER_SPEED);
2850
2851                 dir_fd = file_data_new_dir(path);
2852                 keywords_find_folder(kfd, dir_fd);
2853                 file_data_unref(dir_fd);
2854                 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
2855                 }
2856
2857         g_free(path);
2858 }
2859
2860 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
2861 {
2862         KeywordFindData *kfd;
2863         GtkWidget *hbox;
2864         GtkWidget *label;
2865
2866         kfd = g_new0(KeywordFindData, 1);
2867
2868         kfd->gd = generic_dialog_new(_("Search for keywords"),
2869                                                                         "search_for_keywords",
2870                                                                         widget, FALSE,
2871                                                                         NULL, kfd);
2872         gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
2873         kfd->gd->cancel_cb = keywords_find_close_cb;
2874         kfd->button_close = generic_dialog_add_button(kfd->gd, GTK_STOCK_CLOSE, NULL,
2875                                                      keywords_find_close_cb, FALSE);
2876         kfd->button_start = generic_dialog_add_button(kfd->gd, GTK_STOCK_OK, _("S_tart"),
2877                                                      keywords_find_start_cb, FALSE);
2878         kfd->button_stop = generic_dialog_add_button(kfd->gd, GTK_STOCK_STOP, NULL,
2879                                                     keywords_find_stop_cb, FALSE);
2880         gtk_widget_set_sensitive(kfd->button_stop, FALSE);
2881
2882         generic_dialog_add_message(kfd->gd, NULL, _("Search for keywords"), NULL, FALSE);
2883
2884         hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
2885         pref_spacer(hbox, PREF_PAD_INDENT);
2886         kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2887
2888         hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2889         pref_label_new(hbox, _("Folder:"));
2890
2891         label = tab_completion_new(&kfd->entry, path, NULL, NULL, NULL, NULL);
2892         tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
2893         gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
2894         gtk_widget_show(label);
2895
2896         pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
2897
2898         pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
2899         hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2900
2901         kfd->progress = gtk_entry_new();
2902         gtk_widget_set_can_focus(kfd->progress, FALSE);
2903         gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
2904         gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
2905         gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
2906         gtk_widget_show(kfd->progress);
2907
2908         kfd->spinner = spinner_new(NULL, -1);
2909         gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
2910         gtk_widget_show(kfd->spinner);
2911
2912         kfd->list = NULL;
2913
2914         gtk_widget_show(kfd->gd->dialog);
2915 }
2916
2917 static void keywords_find_cb(GtkWidget *widget, gpointer data)
2918 {
2919         const gchar *path = layout_get_path(NULL);
2920
2921         if (!path || !*path) path = homedir();
2922         keywords_find_dialog(widget, path);
2923 }
2924
2925 static void config_tab_keywords_save()
2926 {
2927         GtkTextIter start, end;
2928         GtkTextBuffer *buffer;
2929         GList *kw_list = NULL;
2930         GList *work;
2931         gchar *buffer_text;
2932         gchar *kw_split;
2933         gboolean found;
2934
2935         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
2936         gtk_text_buffer_get_bounds(buffer, &start, &end);
2937
2938         buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
2939
2940         kw_split = strtok(buffer_text, "\n");
2941         while (kw_split != NULL)
2942                 {
2943                 work = kw_list;
2944                 found = FALSE;
2945                 while (work)
2946                         {
2947                         if (g_strcmp0(work->data, kw_split) == 0)
2948                                 {
2949                                 found = TRUE;
2950                                 break;
2951                                 }
2952                         work = work->next;
2953                         }
2954                 if (!found)
2955                         {
2956                         kw_list = g_list_append(kw_list, g_strdup(kw_split));
2957                         }
2958                 kw_split = strtok(NULL, "\n");
2959                 }
2960
2961         keyword_list_set(kw_list);
2962
2963         string_list_free(kw_list);
2964         g_free(buffer_text);
2965 }
2966
2967 static void config_tab_keywords(GtkWidget *notebook)
2968 {
2969         GtkWidget *hbox;
2970         GtkWidget *vbox;
2971         GtkWidget *group;
2972         GtkWidget *button;
2973         GtkWidget *scrolled;
2974         GtkTextIter iter;
2975         GtkTextBuffer *buffer;
2976         gchar *tmp;
2977
2978         vbox = scrolled_notebook_page(notebook, _("Keywords"));
2979
2980         group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
2981
2982         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2983
2984         button = pref_button_new(hbox, GTK_STOCK_EXECUTE, _("Search"), FALSE,
2985                                    G_CALLBACK(keywords_find_cb), keyword_text);
2986         gtk_widget_set_tooltip_text(button, "Search for existing keywords");
2987
2988
2989         keyword_text = gtk_text_view_new();
2990         gtk_widget_set_size_request(keyword_text, 20, 20);
2991         scrolled = gtk_scrolled_window_new(NULL, NULL);
2992         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2993         gtk_widget_show(scrolled);
2994
2995         gtk_container_add(GTK_CONTAINER(scrolled), keyword_text);
2996         gtk_widget_show(keyword_text);
2997
2998         gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
2999
3000         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3001         gtk_text_buffer_create_tag(buffer, "monospace",
3002                                 "family", "monospace", NULL);
3003
3004         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3005         gtk_text_buffer_get_start_iter(buffer, &iter);
3006         gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3007         gchar *path;
3008
3009         path = g_build_filename(get_rc_dir(), "keywords", NULL);
3010
3011         GList *kwl = keyword_list_get();
3012         kwl = g_list_first(kwl);
3013         while (kwl)
3014         {
3015                 gtk_text_buffer_get_end_iter (buffer, &iter);
3016             tmp = g_strconcat(kwl->data, "\n", NULL);
3017                 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3018                 kwl = kwl->next;
3019                 g_free(tmp);
3020         }
3021
3022         gtk_text_buffer_set_modified(buffer, FALSE);
3023
3024         g_free(path);
3025 }
3026
3027 /* metadata tab */
3028 #ifdef HAVE_LCMS
3029 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3030 {
3031         gint *option = data;
3032
3033         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3034                 {
3035                 case 0:
3036                 default:
3037                         *option = INTENT_PERCEPTUAL;
3038                         break;
3039                 case 1:
3040                         *option = INTENT_RELATIVE_COLORIMETRIC;
3041                         break;
3042                 case 2:
3043                         *option = INTENT_SATURATION;
3044                         break;
3045                 case 3:
3046                         *option = INTENT_ABSOLUTE_COLORIMETRIC;
3047                         break;
3048                 }
3049 }
3050
3051 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3052                              gint option, gint *option_c)
3053 {
3054         GtkWidget *combo;
3055         gint current = 0;
3056
3057         *option_c = option;
3058
3059         pref_table_label(table, column, row, text, 0.0);
3060
3061         combo = gtk_combo_box_text_new();
3062
3063         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3064         if (option == INTENT_PERCEPTUAL) current = 0;
3065         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3066         if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3067         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3068         if (option == INTENT_SATURATION) current = 2;
3069         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3070         if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3071
3072         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3073
3074         gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3075
3076         g_signal_connect(G_OBJECT(combo), "changed",
3077                          G_CALLBACK(intent_menu_cb), option_c);
3078
3079         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
3080                          GTK_EXPAND | GTK_FILL, 0, 0, 0);
3081         gtk_widget_show(combo);
3082 }
3083 #endif
3084
3085 static void config_tab_color(GtkWidget *notebook)
3086 {
3087         GtkWidget *label;
3088         GtkWidget *vbox;
3089         GtkWidget *group;
3090         GtkWidget *tabcomp;
3091         GtkWidget *table;
3092         gint i;
3093
3094         vbox = scrolled_notebook_page(notebook, _("Color management"));
3095
3096         group =  pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3097 #ifndef HAVE_LCMS
3098         gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3099 #endif
3100
3101         table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3102         gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
3103
3104         label = pref_table_label(table, 0, 0, _("Type"), 0.0);
3105         pref_label_bold(label, TRUE, FALSE);
3106
3107         label = pref_table_label(table, 1, 0, _("Menu name"), 0.0);
3108         pref_label_bold(label, TRUE, FALSE);
3109
3110         label = pref_table_label(table, 2, 0, _("File"), 0.0);
3111         pref_label_bold(label, TRUE, FALSE);
3112
3113         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3114                 {
3115                 GtkWidget *entry;
3116                 gchar *buf;
3117
3118                 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3119                 pref_table_label(table, 0, i + 1, buf, 1.0);
3120                 g_free(buf);
3121
3122                 entry = gtk_entry_new();
3123                 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3124                 if (options->color_profile.input_name[i])
3125                         {
3126                         gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3127                         }
3128                 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
3129                                  GTK_FILL | GTK_EXPAND, 0, 0, 0);
3130                 gtk_widget_show(entry);
3131                 color_profile_input_name_entry[i] = entry;
3132
3133                 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], NULL, ".icc", "ICC Files", NULL);
3134                 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3135                 gtk_widget_set_size_request(entry, 160, -1);
3136                 gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
3137                                  GTK_FILL | GTK_EXPAND, 0, 0, 0);
3138                 gtk_widget_show(tabcomp);
3139                 color_profile_input_file_entry[i] = entry;
3140                 }
3141
3142         group =  pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3143 #ifndef HAVE_LCMS
3144         gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3145 #endif
3146         pref_checkbox_new_int(group, _("Use system screen profile if available"),
3147                               options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3148
3149         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3150
3151         pref_table_label(table, 0, 0, _("Screen:"), 1.0);
3152         tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3153                                      options->color_profile.screen_file, NULL, ".icc", "ICC Files", NULL);
3154         tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3155         gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3156 #ifdef HAVE_LCMS
3157         add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3158 #endif
3159         gtk_table_attach(GTK_TABLE(table), tabcomp, 1, 2,
3160                          0, 1,
3161                          GTK_FILL | GTK_EXPAND, 0, 0, 0);
3162
3163         gtk_widget_show(tabcomp);
3164 }
3165
3166 /* advanced entry tab */
3167 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer data)
3168 {
3169         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3170                 {
3171                 c_options->file_ops.use_system_trash = FALSE;
3172                 c_options->file_ops.no_trash = FALSE;
3173                 }
3174 }
3175
3176 static void use_system_trash_cb(GtkWidget *widget, gpointer data)
3177 {
3178         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3179                 {
3180                 c_options->file_ops.use_system_trash = TRUE;
3181                 c_options->file_ops.no_trash = FALSE;
3182                 }
3183 }
3184
3185 static void use_no_cache_cb(GtkWidget *widget, gpointer data)
3186 {
3187         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3188                 {
3189                 c_options->file_ops.no_trash = TRUE;
3190                 }
3191 }
3192
3193 static void config_tab_behavior(GtkWidget *notebook)
3194 {
3195         GtkWidget *hbox;
3196         GtkWidget *vbox;
3197         GtkWidget *group;
3198         GtkWidget *button;
3199         GtkWidget *tabcomp;
3200         GtkWidget *ct_button;
3201         GtkWidget *spin;
3202         GtkWidget *table;
3203         GtkWidget *marks;
3204         GtkWidget *with_rename;
3205         GtkWidget *collections_on_top;
3206
3207         vbox = scrolled_notebook_page(notebook, _("Behavior"));
3208
3209         group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3210
3211         pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3212                               options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3213         pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3214                               options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3215         pref_checkbox_new_int(group, _("Enable Delete key"),
3216                               options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3217
3218         ct_button = pref_radiobutton_new(group, NULL, _("Use Geeqie trash location"),
3219                                         !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),NULL);
3220
3221         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3222         pref_checkbox_link_sensitivity(ct_button, hbox);
3223
3224         pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3225         pref_label_new(hbox, _("Folder:"));
3226
3227         tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, NULL, NULL, NULL, NULL);
3228         tab_completion_add_select_button(safe_delete_path_entry, NULL, TRUE);
3229         gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3230         gtk_widget_show(tabcomp);
3231
3232         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3233         pref_checkbox_link_sensitivity(ct_button, hbox);
3234
3235         pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3236         spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MB"),
3237                                  0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3238         gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3239         button = pref_button_new(NULL, NULL, _("View"), FALSE,
3240                                  G_CALLBACK(safe_delete_view_cb), NULL);
3241         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3242         gtk_widget_show(button);
3243
3244         button = pref_button_new(NULL, GTK_STOCK_CLEAR, NULL, FALSE,
3245                                  G_CALLBACK(safe_delete_clear_cb), NULL);
3246         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3247         pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3248                                         options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), NULL);
3249
3250         pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3251                         options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), NULL);
3252
3253         gtk_widget_show(button);
3254
3255         pref_spacer(group, PREF_PAD_GROUP);
3256
3257
3258         group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3259
3260         pref_checkbox_new_int(group, _("Descend folders in tree view"),
3261                               options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3262
3263         pref_checkbox_new_int(group, _("In place renaming"),
3264                               options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3265
3266         pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3267                               options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3268
3269         marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3270                                 options->marks_save, &c_options->marks_save);
3271         gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3272
3273         with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3274                                 options->with_rename, &c_options->with_rename);
3275         gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3276
3277         collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3278                                 options->collections_on_top, &c_options->collections_on_top);
3279         gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3280
3281         pref_spin_new_int(group, _("Recent folder list maximum size"), NULL,
3282                           1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3283
3284         pref_spin_new_int(group, _("Drag'n drop icon size"), NULL,
3285                           16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3286
3287         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3288         add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3289
3290         pref_spacer(group, PREF_PAD_GROUP);
3291
3292         group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3293
3294         pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3295                               options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3296         pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), NULL,
3297                           1, 32, 1, options->keyboard_scroll_step, (int *)&c_options->keyboard_scroll_step);
3298         pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3299                               options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3300         pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3301                               options->image_lm_click_nav, &c_options->image_lm_click_nav);
3302         pref_checkbox_new_int(group, _("Play video by left click on image"),
3303                               options->image_l_click_video, &c_options->image_l_click_video);
3304         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3305         add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3306
3307         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3308         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3309         add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3310         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3311         add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3312
3313 #ifdef DEBUG
3314         pref_spacer(group, PREF_PAD_GROUP);
3315
3316         group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3317
3318         pref_spin_new_int(group, _("Debug level:"), NULL,
3319                           DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3320
3321         pref_checkbox_new_int(group, _("Timer data"),
3322                         options->log_window.timer_data, &c_options->log_window.timer_data);
3323
3324         pref_spin_new_int(group, _("Log Window max. lines:"), NULL,
3325                           1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3326 #endif
3327 }
3328
3329 /* accelerators tab */
3330 static void config_tab_accelerators(GtkWidget *notebook)
3331 {
3332         GtkWidget *hbox;
3333         GtkWidget *vbox;
3334         GtkWidget *group;
3335         GtkWidget *button;
3336         GtkWidget *scrolled;
3337         GtkWidget *accel_view;
3338         GtkCellRenderer *renderer;
3339         GtkTreeSelection *selection;
3340         GtkTreeViewColumn *column;
3341
3342         vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3343
3344         group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3345
3346         scrolled = gtk_scrolled_window_new(NULL, NULL);
3347         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3348         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3349         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3350         gtk_widget_show(scrolled);
3351
3352         accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3353
3354         accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3355         g_object_unref(accel_store);
3356         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3357         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3358
3359         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3360
3361         renderer = gtk_cell_renderer_text_new();
3362
3363         column = gtk_tree_view_column_new_with_attributes(_("Action"),
3364                                                           renderer,
3365                                                           "text", AE_ACTION,
3366                                                           NULL);
3367
3368         gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3369         gtk_tree_view_column_set_resizable(column, TRUE);
3370         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3371
3372
3373         renderer = gtk_cell_renderer_accel_new();
3374         g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3375                          G_CALLBACK(accel_store_cleared_cb), accel_store);
3376         g_signal_connect(G_OBJECT(renderer), "accel-edited",
3377                          G_CALLBACK(accel_store_edited_cb), accel_store);
3378
3379
3380         g_object_set (renderer,
3381                       "editable", TRUE,
3382                       "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3383                       NULL);
3384
3385         column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3386                                                           renderer,
3387                                                           "text", AE_KEY,
3388                                                           NULL);
3389
3390         gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3391         gtk_tree_view_column_set_resizable(column, TRUE);
3392         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3393
3394         renderer = gtk_cell_renderer_text_new();
3395
3396         column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3397                                                           renderer,
3398                                                           "text", AE_TOOLTIP,
3399                                                           NULL);
3400
3401         gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3402         gtk_tree_view_column_set_resizable(column, TRUE);
3403         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3404
3405         renderer = gtk_cell_renderer_text_new();
3406
3407         column = gtk_tree_view_column_new_with_attributes("Accel",
3408                                                           renderer,
3409                                                           "text", AE_ACCEL,
3410                                                           NULL);
3411
3412         gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3413         gtk_tree_view_column_set_resizable(column, TRUE);
3414         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3415
3416         accel_store_populate();
3417         gtk_container_add(GTK_CONTAINER(scrolled), accel_view);
3418         gtk_widget_show(accel_view);
3419
3420         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3421
3422         button = pref_button_new(NULL, NULL, _("Defaults"), FALSE,
3423                                  G_CALLBACK(accel_default_cb), accel_view);
3424         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3425         gtk_widget_show(button);
3426
3427         button = pref_button_new(NULL, NULL, _("Reset selected"), FALSE,
3428                                  G_CALLBACK(accel_reset_cb), accel_view);
3429         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3430         gtk_widget_show(button);
3431 }
3432
3433 /* toolbar main tab */
3434 static void config_tab_toolbar_main(GtkWidget *notebook)
3435 {
3436         GtkWidget *vbox;
3437         GtkWidget *toolbardata;
3438         LayoutWindow *lw;
3439
3440         lw = layout_window_list->data;
3441
3442         vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3443
3444         toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3445         gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3446         gtk_widget_show(vbox);
3447 }
3448
3449 /* toolbar status tab */
3450 static void config_tab_toolbar_status(GtkWidget *notebook)
3451 {
3452         GtkWidget *vbox;
3453         GtkWidget *toolbardata;
3454         LayoutWindow *lw;
3455
3456         lw = layout_window_list->data;
3457
3458         vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3459
3460         toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3461         gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3462         gtk_widget_show(vbox);
3463 }
3464
3465 /* stereo tab */
3466 static void config_tab_stereo(GtkWidget *notebook)
3467 {
3468         GtkWidget *vbox;
3469         GtkWidget *group;
3470         GtkWidget *group2;
3471         GtkWidget *table;
3472         GtkWidget *box;
3473         GtkWidget *box2;
3474         GtkWidget *fs_button;
3475         vbox = scrolled_notebook_page(notebook, _("Stereo"));
3476
3477         group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3478
3479         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3480         add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3481
3482         table = pref_table_new(group, 2, 2, TRUE, FALSE);
3483         box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3484         pref_checkbox_new_int(box, _("Mirror left image"),
3485                               options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3486         box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3487         pref_checkbox_new_int(box, _("Flip left image"),
3488                               options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3489         box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3490         pref_checkbox_new_int(box, _("Mirror right image"),
3491                               options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3492         box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3493         pref_checkbox_new_int(box, _("Flip right image"),
3494                               options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3495         pref_checkbox_new_int(group, _("Swap left and right images"),
3496                               options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3497         pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3498                               options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3499
3500         group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3501         fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3502                               options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3503         box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3504         pref_checkbox_link_sensitivity(fs_button, box2);
3505         table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3506         add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3507         table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3508         box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3509         pref_checkbox_new_int(box, _("Mirror left image"),
3510                               options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3511         box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3512         pref_checkbox_new_int(box, _("Flip left image"),
3513                               options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
3514         box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3515         pref_checkbox_new_int(box, _("Mirror right image"),
3516                               options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
3517         box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3518         pref_checkbox_new_int(box, _("Flip right image"),
3519                               options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
3520         pref_checkbox_new_int(box2, _("Swap left and right images"),
3521                               options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
3522         pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
3523                               options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
3524
3525         group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
3526         table = pref_table_new(group2, 5, 3, FALSE, FALSE);
3527         pref_table_spin_new_int(table, 0, 0, _("Width"), NULL,
3528                           1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
3529         pref_table_spin_new_int(table, 3, 0,  _("Height"), NULL,
3530                           1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
3531         pref_table_spin_new_int(table, 0, 1,  _("Left X"), NULL,
3532                           0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
3533         pref_table_spin_new_int(table, 3, 1,  _("Left Y"), NULL,
3534                           0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
3535         pref_table_spin_new_int(table, 0, 2,  _("Right X"), NULL,
3536                           0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
3537         pref_table_spin_new_int(table, 3, 2,  _("Right Y"), NULL,
3538                           0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
3539
3540 }
3541
3542 /* Main preferences window */
3543 static void config_window_create(void)
3544 {
3545         GtkWidget *win_vbox;
3546         GtkWidget *hbox;
3547         GtkWidget *notebook;
3548         GtkWidget *button;
3549         GtkWidget *ct_button;
3550
3551         if (!c_options) c_options = init_options(NULL);
3552
3553         configwindow = window_new(GTK_WINDOW_TOPLEVEL, "preferences", PIXBUF_INLINE_ICON_CONFIG, NULL, _("Preferences"));
3554         DEBUG_NAME(configwindow);
3555         gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
3556         g_signal_connect(G_OBJECT(configwindow), "delete_event",
3557                          G_CALLBACK(config_window_delete), NULL);
3558         gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
3559         gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
3560         gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
3561
3562         win_vbox = gtk_vbox_new(FALSE, PREF_PAD_SPACE);
3563         gtk_container_add(GTK_CONTAINER(configwindow), win_vbox);
3564         gtk_widget_show(win_vbox);
3565
3566         notebook = gtk_notebook_new();
3567         gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
3568         gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
3569         gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
3570
3571         config_tab_general(notebook);
3572         config_tab_image(notebook);
3573         config_tab_osd(notebook);
3574         config_tab_windows(notebook);
3575         config_tab_accelerators(notebook);
3576         config_tab_files(notebook);
3577         config_tab_metadata(notebook);
3578         config_tab_keywords(notebook);
3579         config_tab_color(notebook);
3580         config_tab_stereo(notebook);
3581         config_tab_behavior(notebook);
3582         config_tab_toolbar_main(notebook);
3583         config_tab_toolbar_status(notebook);
3584
3585         hbox = gtk_hbutton_box_new();
3586         gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
3587         gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
3588         gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
3589         gtk_widget_show(hbox);
3590
3591         button = pref_button_new(NULL, GTK_STOCK_HELP, NULL, FALSE,
3592                                  G_CALLBACK(config_window_help_cb), notebook);
3593         gtk_container_add(GTK_CONTAINER(hbox), button);
3594         gtk_widget_set_can_default(button, TRUE);
3595         gtk_widget_show(button);
3596
3597         button = pref_button_new(NULL, GTK_STOCK_OK, NULL, FALSE,
3598                                  G_CALLBACK(config_window_ok_cb), NULL);
3599         gtk_container_add(GTK_CONTAINER(hbox), button);
3600         gtk_widget_set_can_default(button, TRUE);
3601         gtk_widget_grab_default(button);
3602         gtk_widget_show(button);
3603
3604         ct_button = button;
3605
3606         button = pref_button_new(NULL, GTK_STOCK_SAVE, NULL, FALSE,
3607                                  G_CALLBACK(config_window_save_cb), NULL);
3608         gtk_container_add(GTK_CONTAINER(hbox), button);
3609         gtk_widget_set_can_default(button, TRUE);
3610         gtk_widget_show(button);
3611
3612         button = pref_button_new(NULL, GTK_STOCK_APPLY, NULL, FALSE,
3613                                  G_CALLBACK(config_window_apply_cb), NULL);
3614         gtk_container_add(GTK_CONTAINER(hbox), button);
3615         gtk_widget_set_can_default(button, TRUE);
3616         gtk_widget_show(button);
3617
3618         button = pref_button_new(NULL, GTK_STOCK_CANCEL, NULL, FALSE,
3619                                  G_CALLBACK(config_window_close_cb), NULL);
3620         gtk_container_add(GTK_CONTAINER(hbox), button);
3621         gtk_widget_set_can_default(button, TRUE);
3622         gtk_widget_show(button);
3623
3624         if (!generic_dialog_get_alternative_button_order(configwindow))
3625                 {
3626                 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
3627                 }
3628
3629         gtk_widget_show(notebook);
3630
3631         gtk_widget_show(configwindow);
3632 }
3633
3634 /*
3635  *-----------------------------------------------------------------------------
3636  * config window show (public)
3637  *-----------------------------------------------------------------------------
3638  */
3639
3640 void show_config_window(void)
3641 {
3642         if (configwindow)
3643                 {
3644                 gtk_window_present(GTK_WINDOW(configwindow));
3645                 return;
3646                 }
3647
3648         config_window_create();
3649 }
3650
3651 /*
3652  *-----------------
3653  * about window
3654  *-----------------
3655  */
3656
3657 void show_about_window(LayoutWindow *lw)
3658 {
3659         GdkPixbuf *pixbuf_logo;
3660         GdkPixbuf *pixbuf_icon;
3661         gchar *authors[1000];
3662         gchar *comment;
3663         gint i_authors = 0;
3664         gchar *path;
3665         GString *copyright;
3666         gchar *zd_path;
3667         ZoneDetect *cd;
3668         FILE *fp = NULL;
3669 #define LINE_LENGTH 1000
3670         gchar line[LINE_LENGTH];
3671
3672         copyright = g_string_new(NULL);
3673         copyright = g_string_append(copyright, "This program comes with absolutely no warranty.\nGNU General Public License, version 2 or later.\nSee https://www.gnu.org/licenses/old-licenses/gpl-2.0.html\n\n");
3674
3675         zd_path = g_build_filename(GQ_BIN_DIR, TIMEZONE_DATABASE, NULL);
3676         cd = ZDOpenDatabase(zd_path);
3677         if (cd)
3678                 {
3679                 copyright = g_string_append(copyright, ZDGetNotice(cd));
3680                 }
3681         ZDCloseDatabase(cd);
3682         g_free(zd_path);
3683
3684         authors[0] = NULL;
3685         path = g_build_filename(GQ_HELPDIR, "AUTHORS", NULL);
3686         fp = fopen(path, "r");
3687         if (fp)
3688                 {
3689                 while(fgets(line, LINE_LENGTH, fp))
3690                         {
3691                         /* get rid of ending \n from fgets */
3692                         line[strlen(line) - 1] = '\0';
3693                         authors[i_authors] = g_strdup(line);
3694                         i_authors++;
3695                         }
3696                 authors[i_authors] = NULL;
3697                 fclose(fp);
3698                 }
3699         g_free(path);
3700
3701         comment = g_strconcat("Development and bug reports:\n", GQ_EMAIL_ADDRESS,
3702                                                 "\nhttps://github.com/BestImageViewer/geeqie/issues",NULL);
3703
3704         pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
3705         pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
3706         gtk_show_about_dialog(GTK_WINDOW(lw->window),
3707                 "title", _("About Geeqie"),
3708                 "resizable", TRUE,
3709                 "program-name", GQ_APPNAME,
3710                 "version", VERSION,
3711                 "logo", pixbuf_logo,
3712                 "icon", pixbuf_icon,
3713                 "website", GQ_WEBSITE,
3714                 "website-label", "Website",
3715                 "comments", comment,
3716                 "authors", authors,
3717                 "translator-credits", _("translator-credits"),
3718                 "wrap-license", TRUE,
3719                 "license", copyright->str,
3720                 NULL);
3721
3722         g_string_free(copyright, TRUE);
3723
3724         gint n = 0;
3725         while(n < i_authors)
3726                 {
3727                 g_free(authors[n]);
3728                 n++;
3729                 }
3730         g_free(comment);
3731
3732         return;
3733 }
3734
3735 static void image_overlay_set_text_colours()
3736 {
3737         c_options->image_overlay.text_red = options->image_overlay.text_red;
3738         c_options->image_overlay.text_green = options->image_overlay.text_green;
3739         c_options->image_overlay.text_blue = options->image_overlay.text_blue;
3740         c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
3741         c_options->image_overlay.background_red = options->image_overlay.background_red;
3742         c_options->image_overlay.background_green = options->image_overlay.background_green;
3743         c_options->image_overlay.background_blue = options->image_overlay.background_blue;
3744         c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
3745 }
3746
3747 /*
3748  *-----------------------------------------------------------------------------
3749  * timezone database routines
3750  *-----------------------------------------------------------------------------
3751  */
3752
3753 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
3754 {
3755         GError *error = NULL;
3756         TZData *tz = data;
3757         gchar *tmp_filename;
3758
3759         if (!g_cancellable_is_cancelled(tz->cancellable))
3760                 {
3761                 generic_dialog_close(tz->gd);
3762                 }
3763
3764
3765         if (g_file_copy_finish(G_FILE(source_object), res, &error))
3766                 {
3767                 tmp_filename = g_file_get_parse_name(tz->tmp_g_file);
3768                 move_file(tmp_filename, tz->timezone_database_user);
3769                 g_free(tmp_filename);
3770                 }
3771         else
3772                 {
3773                 file_util_warning_dialog(_("Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, NULL);
3774                 }
3775
3776         g_file_delete(tz->tmp_g_file, NULL, &error);
3777         g_object_unref(tz->tmp_g_file);
3778         tz->tmp_g_file = NULL;
3779         g_object_unref(tz->cancellable);
3780         g_object_unref(tz->timezone_database_gq);
3781 }
3782
3783 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
3784 {
3785         TZData *tz = data;
3786
3787         if (!g_cancellable_is_cancelled(tz->cancellable))
3788                 {
3789                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), (gdouble)current_num_bytes / total_num_bytes);
3790                 }
3791 }
3792
3793 static void timezone_cancel_button_cb(GenericDialog *gd, gpointer data)
3794 {
3795         TZData *tz = data;
3796         GError *error = NULL;
3797
3798         g_cancellable_cancel(tz->cancellable);
3799 }
3800
3801 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
3802 {
3803         TZData *tz = data;
3804         GError *error = NULL;
3805         GFileIOStream *io_stream;
3806
3807         if (tz->tmp_g_file)
3808                 {
3809                 return;
3810                 }
3811
3812         tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
3813
3814         if (error)
3815                 {
3816                 file_util_warning_dialog(_("Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, NULL);
3817                 log_printf("Error: Download timezone database failed:\n%s", error->message);
3818                 g_error_free(error);
3819                 g_object_unref(tz->tmp_g_file);
3820                 }
3821         else
3822                 {
3823                 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE);
3824
3825                 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", NULL, TRUE, timezone_cancel_button_cb, tz);
3826
3827                 generic_dialog_add_message(tz->gd, GTK_STOCK_DIALOG_INFO, _("Downloading timezone database"), NULL, FALSE);
3828
3829                 tz->progress = gtk_progress_bar_new();
3830                 gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
3831                 gtk_widget_show(tz->progress);
3832
3833                 gtk_widget_show(tz->gd->dialog);
3834                 tz->cancellable = g_cancellable_new();
3835                 g_file_copy_async(tz->timezone_database_gq, tz->tmp_g_file, G_FILE_COPY_OVERWRITE, G_PRIORITY_LOW, tz->cancellable, timezone_progress_cb, tz, timezone_async_ready_cb, tz);
3836
3837                 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
3838                 }
3839 }
3840 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */