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