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