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