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