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