2 * Copyright (C) 2006 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
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.
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.
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.
23 #include "preferences.h"
25 #include "bar-keywords.h"
29 #include "filefilter.h"
30 #include "fullscreen.h"
32 #include "image-overlay.h"
33 #include "color-man.h"
35 #include "layout-util.h"
39 #include "pixbuf-util.h"
41 #include "slideshow.h"
45 #include "ui-fileops.h"
47 #include "ui-spinner.h"
48 #include "ui-tabcomp.h"
50 #include "zonedetect.h"
61 #include <gspell/gspell.h>
64 #define EDITOR_NAME_MAX_LENGTH 32
65 #define EDITOR_COMMAND_MAX_LENGTH 1024
67 static void image_overlay_set_text_colours();
69 GtkWidget *keyword_text;
70 static void config_tab_keywords_save();
78 static ThumbSize thumb_size_list[] =
113 FILETYPES_COLUMN_ENABLED = 0,
114 FILETYPES_COLUMN_FILTER,
115 FILETYPES_COLUMN_DESCRIPTION,
116 FILETYPES_COLUMN_CLASS,
117 FILETYPES_COLUMN_WRITABLE,
118 FILETYPES_COLUMN_SIDECAR,
119 FILETYPES_COLUMN_COUNT
122 const gchar *format_class_list[] = {
133 /* config memory values */
134 static ConfOptions *c_options = nullptr;
141 static GtkWidget *configwindow = nullptr;
142 static GtkListStore *filter_store = nullptr;
143 static GtkTreeStore *accel_store = nullptr;
145 static GtkWidget *safe_delete_path_entry;
147 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
148 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
149 static GtkWidget *color_profile_screen_file_entry;
150 static GtkWidget *external_preview_select_entry;
151 static GtkWidget *external_preview_extract_entry;
153 static GtkWidget *sidecar_ext_entry;
154 static GtkWidget *help_search_engine_entry;
157 #define CONFIG_WINDOW_DEF_WIDTH 700
158 #define CONFIG_WINDOW_DEF_HEIGHT 600
161 *-----------------------------------------------------------------------------
162 * option widget callbacks (private)
163 *-----------------------------------------------------------------------------
166 static void zoom_increment_cb(GtkWidget *spin, gpointer)
168 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
171 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
173 gint mins_secs_tenths, delay;
175 mins_secs_tenths = c_options->slideshow.delay %
176 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
178 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
179 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
182 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
183 SLIDESHOW_SUBSECOND_PRECISION;
186 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
188 gint hours, secs_tenths, delay;
190 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
191 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
193 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
194 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
195 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
197 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
198 SLIDESHOW_SUBSECOND_PRECISION;
201 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
203 gint hours_mins, delay;
205 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
207 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
208 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
209 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
211 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
212 SLIDESHOW_SUBSECOND_PRECISION;
216 *-----------------------------------------------------------------------------
217 * sync program to config window routine (private)
218 *-----------------------------------------------------------------------------
221 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
227 buf = gtk_entry_get_text(GTK_ENTRY(entry));
228 if (buf && strlen(buf) > 0)
233 *option = g_strdup(buf);
238 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
240 gchar *accel_path, *accel;
242 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
244 if (accel_path && accel_path[0])
247 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
248 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
258 static void config_window_apply()
260 gboolean refresh = FALSE;
265 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
267 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
268 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
269 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
270 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
271 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
272 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
274 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
275 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
276 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
277 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
278 options->file_ops.no_trash = c_options->file_ops.no_trash;
279 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
280 options->tools_restore_state = c_options->tools_restore_state;
281 options->save_window_positions = c_options->save_window_positions;
282 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
283 options->save_window_workspace = c_options->save_window_workspace;
284 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
285 options->show_window_ids = c_options->show_window_ids;
286 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
287 options->image.zoom_2pass = c_options->image.zoom_2pass;
288 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
289 options->image.limit_window_size = c_options->image.limit_window_size;
290 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
291 options->image.max_window_size = c_options->image.max_window_size;
292 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
293 options->image.max_autofit_size = c_options->image.max_autofit_size;
294 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
295 options->image.tile_size = c_options->image.tile_size;
296 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
297 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
299 if (options->thumbnails.max_width != c_options->thumbnails.max_width
300 || options->thumbnails.max_height != c_options->thumbnails.max_height
301 || options->thumbnails.quality != c_options->thumbnails.quality)
303 thumb_format_changed = TRUE;
305 options->thumbnails.max_width = c_options->thumbnails.max_width;
306 options->thumbnails.max_height = c_options->thumbnails.max_height;
307 options->thumbnails.quality = c_options->thumbnails.quality;
309 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
310 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
311 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
312 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
313 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
314 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
315 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
316 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
317 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
318 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
319 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
320 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
322 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
323 options->file_filter.disable = c_options->file_filter.disable;
325 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
326 sidecar_ext_parse(options->sidecar.ext);
328 options->slideshow.random = c_options->slideshow.random;
329 options->slideshow.repeat = c_options->slideshow.repeat;
330 options->slideshow.delay = c_options->slideshow.delay;
332 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
333 options->image_lm_click_nav = c_options->image_lm_click_nav;
334 options->image_l_click_archive = c_options->image_l_click_archive;
335 options->image_l_click_video = c_options->image_l_click_video;
336 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
338 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
340 options->image.tile_cache_max = c_options->image.tile_cache_max;
341 options->image.image_cache_max = c_options->image.image_cache_max;
343 options->image.zoom_quality = c_options->image.zoom_quality;
345 options->image.zoom_increment = c_options->image.zoom_increment;
347 options->image.zoom_style = c_options->image.zoom_style;
349 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
351 options->appimage_notifications = c_options->appimage_notifications;
354 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
355 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
356 || !gdk_color_equal(&options->image.border_color, &c_options->image.border_color))
358 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
359 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
360 options->image.border_color = c_options->image.border_color;
361 layout_colors_update();
362 view_window_colors_update();
365 options->image.alpha_color_1 = c_options->image.alpha_color_1;
366 options->image.alpha_color_2 = c_options->image.alpha_color_2;
368 options->fullscreen.screen = c_options->fullscreen.screen;
369 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
370 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
371 options->fullscreen.above = c_options->fullscreen.above;
372 if (c_options->image_overlay.template_string)
373 set_image_overlay_template_string(&options->image_overlay.template_string,
374 c_options->image_overlay.template_string);
375 if (c_options->image_overlay.font)
376 set_image_overlay_font_string(&options->image_overlay.font,
377 c_options->image_overlay.font);
378 options->image_overlay.text_red = c_options->image_overlay.text_red;
379 options->image_overlay.text_green = c_options->image_overlay.text_green;
380 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
381 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
382 options->image_overlay.background_red = c_options->image_overlay.background_red;
383 options->image_overlay.background_green = c_options->image_overlay.background_green;
384 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
385 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
386 options->update_on_time_change = c_options->update_on_time_change;
388 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
389 options->rot_invariant_sim = c_options->rot_invariant_sim;
391 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
393 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
394 options->circular_selection_lists = c_options->circular_selection_lists;
396 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
397 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
398 options->dnd_icon_size = c_options->dnd_icon_size;
399 options->clipboard_selection = c_options->clipboard_selection;
400 options->dnd_default_action = c_options->dnd_default_action;
402 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
403 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
404 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
405 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
406 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
407 options->metadata.confirm_write = c_options->metadata.confirm_write;
408 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
409 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
410 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
411 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
412 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
413 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
414 options->metadata.write_orientation = c_options->metadata.write_orientation;
415 options->metadata.check_spelling = c_options->metadata.check_spelling;
416 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
417 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
418 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
419 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
420 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
421 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
422 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
423 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
424 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
425 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
426 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
427 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
428 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
429 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
430 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
431 options->stereo.fixed_w = c_options->stereo.fixed_w;
432 options->stereo.fixed_h = c_options->stereo.fixed_h;
433 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
434 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
435 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
436 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
438 options->info_keywords.height = c_options->info_keywords.height;
439 options->info_title.height = c_options->info_title.height;
440 options->info_comment.height = c_options->info_comment.height;
441 options->info_rating.height = c_options->info_rating.height;
443 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
444 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
446 options->marks_save = c_options->marks_save;
447 options->with_rename = c_options->with_rename;
448 options->collections_on_top = c_options->collections_on_top;
449 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
450 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
452 options->external_preview.enable = c_options->external_preview.enable;
453 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
454 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
456 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
458 options->star_rating.star = c_options->star_rating.star;
459 options->star_rating.rejected = c_options->star_rating.rejected;
461 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
464 set_debug_level(debug_c);
468 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
470 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
471 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
473 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
474 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
475 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
477 options->color_profile.render_intent = c_options->color_profile.render_intent;
482 options->mouse_button_8 = c_options->mouse_button_8;
483 options->mouse_button_9 = c_options->mouse_button_9;
485 options->override_disable_gpu = c_options->override_disable_gpu;
487 config_tab_keywords_save();
489 image_options_sync();
494 layout_refresh(nullptr);
497 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
499 toolbar_apply(TOOLBAR_MAIN);
500 toolbar_apply(TOOLBAR_STATUS);
504 *-----------------------------------------------------------------------------
505 * config window main button callbacks (private)
506 *-----------------------------------------------------------------------------
509 static void config_window_close_cb(GtkWidget *, gpointer)
511 gtk_widget_destroy(configwindow);
512 configwindow = nullptr;
513 filter_store = nullptr;
516 static void config_window_help_cb(GtkWidget *, gpointer data)
518 auto notebook = static_cast<GtkWidget *>(data);
521 static const gchar *html_section[] =
523 "GuideOptionsGeneral.html",
524 "GuideOptionsImage.html",
525 "GuideOptionsOSD.html",
526 "GuideOptionsWindow.html",
527 "GuideOptionsKeyboard.html",
528 "GuideOptionsFiltering.html",
529 "GuideOptionsMetadata.html",
530 "GuideOptionsKeywords.html",
531 "GuideOptionsColor.html",
532 "GuideOptionsStereo.html",
533 "GuideOptionsBehavior.html",
534 "GuideOptionsToolbar.html",
535 "GuideOptionsToolbar.html",
536 "GuideOptionsAdvanced.html"
539 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
540 help_window_show(html_section[i]);
543 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
545 config_window_close_cb(nullptr, nullptr);
549 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
552 auto notebook = static_cast<GtkNotebook *>(data);
560 lw = static_cast<LayoutWindow *>(layout_window_list->data);
562 window = gtk_widget_get_window(widget);
563 gdk_window_get_root_origin(window, &x, &y);
564 w = gdk_window_get_width(window);
565 h = gdk_window_get_height(window);
566 page_number = gtk_notebook_get_current_page(notebook);
568 lw->options.preferences_window.x = x;
569 lw->options.preferences_window.y = y;
570 lw->options.preferences_window.w = w;
571 lw->options.preferences_window.h = h;
572 lw->options.preferences_window.page_number = page_number;
574 config_window_apply();
575 layout_util_sync(lw);
576 save_options(options);
577 config_window_close_cb(nullptr, nullptr);
581 *-----------------------------------------------------------------------------
582 * config window setup (private)
583 *-----------------------------------------------------------------------------
586 static void quality_menu_cb(GtkWidget *combo, gpointer data)
588 auto option = static_cast<gint *>(data);
590 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
594 *option = GDK_INTERP_NEAREST;
597 *option = GDK_INTERP_TILES;
600 *option = GDK_INTERP_BILINEAR;
605 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
607 auto option = static_cast<gint *>(data);
609 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
613 *option = DND_ACTION_ASK;
616 *option = DND_ACTION_COPY;
619 *option = DND_ACTION_MOVE;
623 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
625 auto option = static_cast<gint *>(data);
627 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
630 *option = CLIPBOARD_PRIMARY;
633 *option = CLIPBOARD_CLIPBOARD;
636 *option = CLIPBOARD_BOTH;
639 *option = CLIPBOARD_BOTH;
643 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
644 guint option, guint *option_c)
651 pref_table_label(table, column, row, text, GTK_ALIGN_START);
653 combo = gtk_combo_box_text_new();
655 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
656 if (option == GDK_INTERP_NEAREST) current = 0;
657 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
658 if (option == GDK_INTERP_TILES) current = 1;
659 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
660 if (option == GDK_INTERP_BILINEAR) current = 2;
662 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
664 g_signal_connect(G_OBJECT(combo), "changed",
665 G_CALLBACK(quality_menu_cb), option_c);
667 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
668 gtk_widget_show(combo);
671 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
678 pref_table_label(table, column, row, text, GTK_ALIGN_START);
680 combo = gtk_combo_box_text_new();
682 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
683 if (option == DND_ACTION_ASK) current = 0;
684 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
685 if (option == DND_ACTION_COPY) current = 1;
686 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
687 if (option == DND_ACTION_MOVE) current = 2;
689 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
691 g_signal_connect(G_OBJECT(combo), "changed",
692 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
694 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
695 gtk_widget_show(combo);
698 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
699 gint option, gint *option_c)
706 pref_table_label(table, column, row, text, GTK_ALIGN_START);
708 combo = gtk_combo_box_text_new();
710 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
711 if (option == CLIPBOARD_PRIMARY) current = 0;
712 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
713 if (option == CLIPBOARD_CLIPBOARD) current = 1;
714 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
715 if (option == CLIPBOARD_BOTH) current = 2;
717 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
719 g_signal_connect(G_OBJECT(combo), "changed",
720 G_CALLBACK(clipboard_selection_menu_cb), option_c);
722 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
723 gtk_widget_show(combo);
726 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
728 auto option = static_cast<gint *>(data);
730 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
733 *option = ZOOM_GEOMETRIC;
736 *option = ZOOM_ARITHMETIC;
739 *option = ZOOM_GEOMETRIC;
743 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
750 pref_table_label(table, column, row, text, GTK_ALIGN_START);
752 combo = gtk_combo_box_text_new();
754 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
755 if (option == ZOOM_GEOMETRIC) current = 0;
756 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
757 if (option == ZOOM_ARITHMETIC) current = 1;
759 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
761 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
763 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
764 gtk_widget_show(combo);
767 struct UseableMouseItems
769 const gchar *name; /* GtkActionEntry terminology */
771 const gchar *icon_name;
774 static const UseableMouseItems useable_mouse_items[] = {
776 {"FirstImage", N_("First Image"), GQ_ICON_GO_TOP},
777 {"PrevImage", N_("Previous Image"), GQ_ICON_GO_UP},
778 {"NextImage", N_("Next Image"), GQ_ICON_GO_DOWN},
779 {"LastImage", N_("Last Image"), GQ_ICON_GO_BOTTOM},
780 {"Back", N_("Back"), GQ_ICON_GO_PREV},
781 {"Forward", N_("Forward"), GQ_ICON_GO_NEXT},
782 {"Home", N_("Home"), GQ_ICON_HOME},
783 {"Up", N_("Up"), GQ_ICON_GO_UP},
784 {"FirstPage", N_("First page"), GQ_ICON_PREV_PAGE},
785 {"LastPage", N_("Last Page"), GQ_ICON_NEXT_PAGE},
786 {"NextPage", N_("Next page"), GQ_ICON_FORWARD_PAGE},
787 {"PrevPage", N_("Previous Page"), GQ_ICON_BACK_PAGE},
788 {"NewWindow", N_("New _window"), GQ_ICON_NEW},
789 {"NewCollection", N_("New collection"), GQ_ICON_COLLECTION},
790 {"OpenCollection", N_("Open collection"), GQ_ICON_OPEN},
791 {"Search", N_("Search"), GQ_ICON_FIND},
792 {"FindDupes", N_("Find duplicates"), GQ_ICON_FIND},
793 {"NewFolder", N_("New folder"),GQ_ICON_DIRECTORY},
794 {"Copy", N_("Copy"), GQ_ICON_COPY},
795 {"Move", N_("Move"), PIXBUF_INLINE_ICON_MOVE},
796 {"Rename", N_("Rename"), PIXBUF_INLINE_ICON_RENAME},
797 {"Delete", N_("Delete"), GQ_ICON_DELETE},
798 {"CloseWindow", N_("Close Window"), GQ_ICON_CLOSE},
799 {"PanView", N_("Pan view"), PIXBUF_INLINE_ICON_PANORAMA},
800 {"SelectAll", N_("Select all"), PIXBUF_INLINE_ICON_SELECT_ALL},
801 {"SelectNone", N_("Select none"), PIXBUF_INLINE_ICON_SELECT_NONE},
802 {"SelectInvert", N_("Select invert"), PIXBUF_INLINE_ICON_SELECT_INVERT},
803 {"ShowFileFilter", N_("Show file filter"), PIXBUF_INLINE_ICON_FILE_FILTER},
804 {"RectangularSelection", N_("Select rectangle"), PIXBUF_INLINE_ICON_SELECT_RECTANGLE},
805 {"Print", N_("Print"), GQ_ICON_PRINT},
806 {"Preferences", N_("Preferences"), GQ_ICON_PREFERENCES},
807 {"LayoutConfig", N_("Configure this window"), GQ_ICON_PREFERENCES},
808 {"Maintenance", N_("Cache maintenance"), PIXBUF_INLINE_ICON_MAINTENANCE},
809 {"RotateCW", N_("Rotate clockwise 90°"), PIXBUF_INLINE_ICON_CW},
810 {"RotateCCW", N_("Rotate counterclockwise 90°"), PIXBUF_INLINE_ICON_CCW},
811 {"Rotate180", N_("Rotate 180°"), PIXBUF_INLINE_ICON_180},
812 {"Mirror", N_("Mirror"), PIXBUF_INLINE_ICON_MIRROR},
813 {"Flip", N_("Flip"), PIXBUF_INLINE_ICON_FLIP},
814 {"AlterNone", N_("Original state"), PIXBUF_INLINE_ICON_ORIGINAL},
815 {"ZoomIn", N_("Zoom in"), GQ_ICON_ZOOM_IN},
816 {"ZoomOut", N_("Zoom out"), GQ_ICON_ZOOM_OUT},
817 {"Zoom100", N_("Zoom 1:1"), GQ_ICON_ZOOM_100},
818 {"ZoomFit", N_("Zoom to fit"), GQ_ICON_ZOOM_FIT},
819 {"ZoomFillHor", N_("Fit Horizontaly"), PIXBUF_INLINE_ICON_ZOOMFILLHOR},
820 {"ZoomFillVert", N_("Fit vertically"), PIXBUF_INLINE_ICON_ZOOMFILLVERT},
821 {"Zoom200", N_("Zoom 2:1"), GQ_ICON_GENERIC},
822 {"Zoom300", N_("Zoom 3:1"), GQ_ICON_GENERIC},
823 {"Zoom400", N_("Zoom 4:1"), GQ_ICON_GENERIC},
824 {"Zoom50", N_("Zoom 1:2"), GQ_ICON_GENERIC},
825 {"Zoom33", N_("Zoom 1:3"), GQ_ICON_GENERIC},
826 {"Zoom25", N_("Zoom 1:4"), GQ_ICON_GENERIC},
827 {"ConnectZoomIn", N_("Connected Zoom in"), GQ_ICON_ZOOM_IN},
828 {"SplitPaneSync", N_("Split Pane Sync"), PIXBUF_INLINE_SPLIT_PANE_SYNC},
829 {"Grayscale", N_("Grayscale"), PIXBUF_INLINE_ICON_GRAYSCALE},
830 {"OverUnderExposed", N_("Over Under Exposed"), PIXBUF_INLINE_ICON_EXPOSURE},
831 {"HideTools", N_("Hide file list"), PIXBUF_INLINE_ICON_HIDETOOLS},
832 {"SlideShowPause", N_("Pause slideshow"), GQ_ICON_PAUSE},
833 {"SlideShowFaster", N_("Slideshow Faster"), GQ_ICON_GENERIC},
834 {"SlideShowSlower", N_("Slideshow Slower"), GQ_ICON_GENERIC},
835 {"Refresh", N_("Refresh"), GQ_ICON_REFRESH},
836 {"HelpContents", N_("Help"), GQ_ICON_HELP},
837 {"ExifWin", N_("Exif window"), PIXBUF_INLINE_ICON_EXIF},
838 {"Thumbnails", N_("Show thumbnails"), PIXBUF_INLINE_ICON_THUMB},
839 {"ShowMarks", N_("Show marks"), PIXBUF_INLINE_ICON_MARKS},
840 {"DrawRectangle", N_("Draw Rectangle"), PIXBUF_INLINE_ICON_DRAW_RECTANGLE},
841 {"FloatTools", N_("Float file list"), PIXBUF_INLINE_ICON_FLOAT},
842 {"SBar", N_("Info sidebar"), PIXBUF_INLINE_ICON_INFO},
843 {"SBarSort", N_("Sort manager"), PIXBUF_INLINE_ICON_SORT},
844 {"Quit", N_("Quit"), GQ_ICON_QUIT},
845 {nullptr, nullptr, nullptr}
848 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
850 auto option = static_cast<gchar **>(data);
853 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
855 const UseableMouseItems *list = useable_mouse_items;
859 if (g_strcmp0(list->label, label) == 0)
867 *option = g_strdup(list->name);
871 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
872 gchar *option, gchar **option_c)
880 pref_table_label(table, column, row, text, GTK_ALIGN_START);
882 combo = gtk_combo_box_text_new();
884 const UseableMouseItems *list = useable_mouse_items;
888 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), list->label);
889 if (g_strcmp0(list->name, option) == 0)
897 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
899 g_signal_connect(G_OBJECT(combo), "changed",
900 G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
902 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
903 gtk_widget_show(combo);
906 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
910 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
913 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
915 c_options->thumbnails.max_width = thumb_size_list[n].w;
916 c_options->thumbnails.max_height = thumb_size_list[n].h;
920 c_options->thumbnails.max_width = options->thumbnails.max_width;
921 c_options->thumbnails.max_height = options->thumbnails.max_height;
925 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
931 c_options->thumbnails.max_width = options->thumbnails.max_width;
932 c_options->thumbnails.max_height = options->thumbnails.max_height;
934 pref_table_label(table, column, row, text, GTK_ALIGN_START);
936 combo = gtk_combo_box_text_new();
939 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
944 w = thumb_size_list[i].w;
945 h = thumb_size_list[i].h;
947 buf = g_strdup_printf("%d x %d", w, h);
948 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
951 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
958 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
959 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
965 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
966 g_signal_connect(G_OBJECT(combo), "changed",
967 G_CALLBACK(thumb_size_menu_cb), NULL);
969 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
970 gtk_widget_show(combo);
973 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
975 auto option = static_cast<gint *>(data);
977 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
981 *option = PR_STEREO_NONE;
984 *option = PR_STEREO_ANAGLYPH_RC;
987 *option = PR_STEREO_ANAGLYPH_GM;
990 *option = PR_STEREO_ANAGLYPH_YB;
993 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
996 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
999 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
1002 *option = PR_STEREO_ANAGLYPH_DB_RC;
1005 *option = PR_STEREO_ANAGLYPH_DB_GM;
1008 *option = PR_STEREO_ANAGLYPH_DB_YB;
1011 *option = PR_STEREO_HORIZ;
1014 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
1017 *option = PR_STEREO_VERT;
1020 *option = PR_STEREO_VERT | PR_STEREO_HALF;
1023 *option = PR_STEREO_FIXED;
1028 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1029 gint option, gint *option_c, gboolean add_fixed)
1036 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1038 combo = gtk_combo_box_text_new();
1040 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
1042 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
1043 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
1044 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
1045 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
1046 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
1047 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
1048 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
1049 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
1050 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
1051 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
1052 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
1053 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
1054 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
1055 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
1056 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
1057 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
1058 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1059 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1061 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1062 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1063 if (option & PR_STEREO_HORIZ)
1066 if (option & PR_STEREO_HALF) current = 11;
1069 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1070 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1071 if (option & PR_STEREO_VERT)
1074 if (option & PR_STEREO_HALF) current = 13;
1079 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1080 if (option & PR_STEREO_FIXED) current = 14;
1083 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1085 g_signal_connect(G_OBJECT(combo), "changed",
1086 G_CALLBACK(stereo_mode_menu_cb), option_c);
1088 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1089 gtk_widget_show(combo);
1092 static void video_menu_cb(GtkWidget *combo, gpointer data)
1094 auto option = static_cast<gchar **>(data);
1096 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1100 static void video_menu_populate(gpointer data, gpointer user_data)
1102 auto combo = static_cast<GtkWidget *>(user_data);
1103 auto ed = static_cast<EditorDescription *>(data);
1105 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1108 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1109 gchar *option, gchar **option_c)
1113 /* use lists since they are sorted */
1114 GList *eds = editor_list_get();
1118 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1120 combo = gtk_combo_box_text_new();
1121 g_list_foreach(eds,video_menu_populate,combo);
1122 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1124 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1126 g_signal_connect(G_OBJECT(combo), "changed",
1127 G_CALLBACK(video_menu_cb), option_c);
1129 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1130 gtk_widget_show(combo);
1133 static void filter_store_populate()
1137 if (!filter_store) return;
1139 gtk_list_store_clear(filter_store);
1141 work = filter_get_list();
1147 fe = static_cast<FilterEntry *>(work->data);
1150 gtk_list_store_append(filter_store, &iter);
1151 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1155 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1157 auto model = static_cast<GtkWidget *>(data);
1158 auto fe = static_cast<FilterEntry *>(data);
1162 if (!new_text || strlen(new_text) < 1) return;
1164 tpath = gtk_tree_path_new_from_string(path_str);
1165 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1166 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1168 g_free(fe->extensions);
1169 fe->extensions = g_strdup(new_text);
1171 gtk_tree_path_free(tpath);
1175 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1177 auto model = static_cast<GtkWidget *>(data);
1178 auto fe = static_cast<FilterEntry *>(data);
1183 if (!new_text || !new_text[0]) return;
1185 tpath = gtk_tree_path_new_from_string(path_str);
1186 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1187 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1189 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1191 if (strcmp(new_text, _(format_class_list[i])) == 0)
1193 fe->file_class = static_cast<FileFormatClass>(i);
1198 gtk_tree_path_free(tpath);
1202 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1204 auto model = static_cast<GtkWidget *>(data);
1209 if (!new_text || !new_text[0]) return;
1211 tpath = gtk_tree_path_new_from_string(path_str);
1212 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1213 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1215 g_free(fe->description);
1216 fe->description = g_strdup(new_text);
1218 gtk_tree_path_free(tpath);
1221 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1223 auto model = static_cast<GtkWidget *>(data);
1228 tpath = gtk_tree_path_new_from_string(path_str);
1229 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1230 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1232 fe->enabled = !fe->enabled;
1234 gtk_tree_path_free(tpath);
1238 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1240 auto model = static_cast<GtkWidget *>(data);
1245 tpath = gtk_tree_path_new_from_string(path_str);
1246 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1247 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1249 fe->writable = !fe->writable;
1250 if (fe->writable) fe->allow_sidecar = FALSE;
1252 gtk_tree_path_free(tpath);
1256 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1258 auto model = static_cast<GtkWidget *>(data);
1263 tpath = gtk_tree_path_new_from_string(path_str);
1264 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1265 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1267 fe->allow_sidecar = !fe->allow_sidecar;
1268 if (fe->allow_sidecar) fe->writable = FALSE;
1270 gtk_tree_path_free(tpath);
1274 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1275 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1279 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1281 switch (GPOINTER_TO_INT(data))
1284 g_object_set(GTK_CELL_RENDERER(cell),
1285 "active", fe->enabled, NULL);
1288 g_object_set(GTK_CELL_RENDERER(cell),
1289 "text", fe->extensions, NULL);
1291 case FE_DESCRIPTION:
1292 g_object_set(GTK_CELL_RENDERER(cell),
1293 "text", fe->description, NULL);
1296 g_object_set(GTK_CELL_RENDERER(cell),
1297 "text", _(format_class_list[fe->file_class]), NULL);
1300 g_object_set(GTK_CELL_RENDERER(cell),
1301 "active", fe->writable, NULL);
1303 case FE_ALLOW_SIDECAR:
1304 g_object_set(GTK_CELL_RENDERER(cell),
1305 "active", fe->allow_sidecar, NULL);
1310 static gboolean filter_add_scroll(gpointer data)
1314 GtkCellRenderer *cell;
1315 GtkTreeViewColumn *column;
1318 GtkTreeModel *store;
1320 FilterEntry *filter;
1322 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1323 path = gtk_tree_path_new_from_indices(rows-1, -1);
1325 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1327 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1328 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1330 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1331 valid = gtk_tree_model_get_iter_first(store, &iter);
1335 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1337 if (g_strcmp0(filter->extensions, ".new") == 0)
1339 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1343 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1346 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1347 path, column, FALSE, 0.0, 0.0 );
1348 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1349 path, column, cell, TRUE);
1351 gtk_tree_path_free(path);
1352 g_list_free(list_cells);
1354 return(G_SOURCE_REMOVE);
1357 static void filter_add_cb(GtkWidget *, gpointer data)
1359 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1360 filter_store_populate();
1362 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1365 static void filter_remove_cb(GtkWidget *, gpointer data)
1367 auto filter_view = static_cast<GtkWidget *>(data);
1368 GtkTreeSelection *selection;
1372 if (!filter_store) return;
1373 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1374 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1375 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1378 filter_remove_entry(fe);
1380 filter_store_populate();
1383 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1387 GtkTreeViewColumn *column;
1389 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1390 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1391 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1393 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1397 gtk_tree_path_free(path);
1399 return(G_SOURCE_REMOVE);
1402 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1405 filter_add_defaults();
1407 filter_store_populate();
1409 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1412 static void dummy_cancel_cb(GenericDialog *, gpointer)
1414 /* no op, only so cancel button appears */
1417 static void filter_default_cb(GtkWidget *widget, gpointer data)
1421 gd = generic_dialog_new(_("Reset filters"),
1422 "reset_filter", widget, TRUE,
1423 dummy_cancel_cb, data);
1424 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1425 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1426 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1427 gtk_widget_show(gd->dialog);
1430 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1432 auto frame = static_cast<GtkWidget *>(data);
1434 gtk_widget_set_sensitive(frame,
1435 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1438 static void safe_delete_view_cb(GtkWidget *, gpointer)
1440 layout_set_path(nullptr, gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1443 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1445 file_util_trash_clear();
1448 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1452 gd = generic_dialog_new(_("Clear trash"),
1453 "clear_trash", widget, TRUE,
1454 dummy_cancel_cb, nullptr);
1455 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1456 _("This will remove the trash contents."), FALSE);
1457 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1458 entry = gtk_entry_new();
1459 gtk_widget_set_can_focus(entry, FALSE);
1460 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1461 if (options->file_ops.safe_delete_path) gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1462 gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1463 gtk_widget_show(entry);
1464 gtk_widget_show(gd->dialog);
1467 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1469 GtkWidget *pTextView;
1470 GtkTextBuffer *pTextBuffer;
1474 pTextView = GTK_WIDGET(data);
1476 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1477 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1478 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1480 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1481 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1484 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1486 auto text_view = static_cast<GtkTextView *>(data);
1487 GtkTextBuffer *buffer;
1489 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1490 if (!configwindow) return;
1492 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1493 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1496 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1500 gd = generic_dialog_new(_("Reset image overlay template string"),
1501 "reset_image_overlay_template_string", widget, TRUE,
1502 dummy_cancel_cb, data);
1503 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1504 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1505 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1506 gtk_widget_show(gd->dialog);
1509 static void image_overlay_help_cb(GtkWidget *, gpointer)
1511 help_window_show("GuideOptionsOSD.html");
1514 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1518 PangoFontDescription *font_desc;
1520 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1521 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1523 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1525 font_desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog));
1526 font = pango_font_description_to_string(font_desc);
1527 g_free(c_options->image_overlay.font);
1528 c_options->image_overlay.font = g_strdup(font);
1532 gtk_widget_destroy(dialog);
1535 static void image_overlay_set_text_colour_cb(GtkWidget *widget, gpointer)
1540 dialog = gtk_color_chooser_dialog_new("Image Overlay Text Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1541 colour.red = options->image_overlay.text_red;
1542 colour.green = options->image_overlay.text_green;
1543 colour.blue = options->image_overlay.text_blue;
1544 colour.alpha = options->image_overlay.text_alpha;
1545 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1546 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1548 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1550 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1551 c_options->image_overlay.text_red = colour.red*255;
1552 c_options->image_overlay.text_green = colour.green*255;
1553 c_options->image_overlay.text_blue = colour.blue*255;
1554 c_options->image_overlay.text_alpha = colour.alpha*255;
1556 gtk_widget_destroy(dialog);
1560 static void image_overlay_set_background_colour_cb(GtkWidget *widget, gpointer)
1565 dialog = gtk_color_chooser_dialog_new("Image Overlay Background Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1566 colour.red = options->image_overlay.background_red;
1567 colour.green = options->image_overlay.background_green;
1568 colour.blue = options->image_overlay.background_blue;
1569 colour.alpha = options->image_overlay.background_alpha;
1570 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1571 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1573 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1575 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1576 c_options->image_overlay.background_red = colour.red*255;
1577 c_options->image_overlay.background_green = colour.green*255;
1578 c_options->image_overlay.background_blue = colour.blue*255;
1579 c_options->image_overlay.background_alpha = colour.alpha*255;
1581 gtk_widget_destroy(dialog);
1584 static void accel_store_populate()
1587 GList *groups, *actions;
1589 const gchar *accel_path;
1593 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1595 gtk_tree_store_clear(accel_store);
1596 lw = static_cast<LayoutWindow *>(layout_window_list->data); /* get the actions from the first window, it should not matter, they should be the same in all windows */
1598 g_assert(lw && lw->ui_manager);
1599 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1602 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1605 action = GTK_ACTION(actions->data);
1606 accel_path = gtk_action_get_accel_path(action);
1607 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1609 gchar *label, *label2, *tooltip, *accel;
1610 g_object_get(action,
1611 "tooltip", &tooltip,
1615 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1621 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1625 gtk_tree_store_append(accel_store, &iter, nullptr);
1626 gtk_tree_store_set(accel_store, &iter,
1629 AE_TOOLTIP, tooltip ? tooltip : "",
1630 AE_ACCEL, accel_path,
1638 actions = actions->next;
1641 groups = groups->next;
1645 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1650 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1652 auto accel1 = static_cast<gchar *>(data);
1657 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1659 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1660 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1662 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1664 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1665 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1674 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1676 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1680 GtkAccelKey old_key, key;
1681 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1683 gtk_tree_model_get_iter(model, &iter, path);
1684 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1686 /* test if the accelerator can be stored without conflicts*/
1687 gtk_accel_map_lookup_entry(accel_path, &old_key);
1689 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1690 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1691 gtk_accel_map_lookup_entry(accel_path, &key);
1693 /* restore the original for now, the key will be really changed when the changes are confirmed */
1694 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1696 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1697 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1699 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1700 gtk_tree_path_free(path);
1704 static gboolean accel_default_scroll(GtkTreeView *data)
1708 GtkTreeViewColumn *column;
1710 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1711 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1712 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1714 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1718 gtk_tree_path_free(path);
1720 return(G_SOURCE_REMOVE);
1723 static void accel_default_cb(GtkWidget *, gpointer data)
1725 accel_store_populate();
1727 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1730 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1732 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1735 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1738 gchar *accel_path, *accel;
1740 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1741 gtk_accel_map_lookup_entry(accel_path, &key);
1742 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1744 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1746 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1751 static void accel_clear_cb(GtkWidget *, gpointer data)
1753 GtkTreeSelection *selection;
1755 if (!accel_store) return;
1756 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1757 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1760 static void accel_reset_cb(GtkWidget *, gpointer data)
1762 GtkTreeSelection *selection;
1764 if (!accel_store) return;
1765 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1766 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1771 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1775 GtkWidget *scrolled;
1776 GtkWidget *viewport;
1778 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
1779 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1780 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1781 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1782 label = gtk_label_new(title);
1783 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1784 gtk_widget_show(scrolled);
1786 viewport = gtk_viewport_new(nullptr, nullptr);
1787 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1788 gtk_container_add(GTK_CONTAINER(scrolled), viewport);
1789 gtk_widget_show(viewport);
1791 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1792 gtk_container_add(GTK_CONTAINER(viewport), vbox);
1793 gtk_widget_show(vbox);
1798 static void cache_standard_cb(GtkWidget *widget, gpointer)
1800 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1802 c_options->thumbnails.spec_standard =TRUE;
1803 c_options->thumbnails.cache_into_dirs = FALSE;
1807 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1809 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1811 c_options->thumbnails.spec_standard =FALSE;
1812 c_options->thumbnails.cache_into_dirs = FALSE;
1816 static void cache_local_cb(GtkWidget *widget, gpointer)
1818 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1820 c_options->thumbnails.cache_into_dirs = TRUE;
1821 c_options->thumbnails.spec_standard =FALSE;
1825 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1827 if (pos == GTK_ENTRY_ICON_PRIMARY)
1829 gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1833 gtk_entry_set_text(GTK_ENTRY(userdata), "");
1837 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1839 gchar *rating_symbol;
1841 if (pos == GTK_ENTRY_ICON_PRIMARY)
1843 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1844 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1845 g_free(rating_symbol);
1849 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1850 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1851 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1855 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1857 gchar *rating_symbol;
1859 if (pos == GTK_ENTRY_ICON_PRIMARY)
1861 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1862 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1863 g_free(rating_symbol);
1867 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1868 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1869 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1873 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1875 auto hbox = static_cast<GtkContainer *>(data);
1876 GString *str = g_string_new(nullptr);
1877 GtkEntry *hex_code_entry;
1878 gchar *hex_code_full;
1881 guint64 hex_value = 0;
1883 list = gtk_container_get_children(hbox);
1885 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1886 hex_code_full = g_strdup(gtk_entry_get_text(hex_code_entry));
1888 hex_code = g_strsplit(hex_code_full, "+", 2);
1889 if (hex_code[0] && hex_code[1])
1891 hex_value = strtoull(hex_code[1], nullptr, 16);
1893 if (!hex_value || hex_value > 0x10FFFF)
1895 hex_value = 0x003F; // Unicode 'Question Mark'
1897 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1898 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1900 g_strfreev(hex_code);
1901 g_string_free(str, TRUE);
1902 g_free(hex_code_full);
1907 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1909 guint64 star_symbol;
1911 star_symbol = star_rating_symbol_test(widget, data);
1912 c_options->star_rating.star = star_symbol;
1915 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1917 guint64 rejected_symbol;
1919 rejected_symbol = star_rating_symbol_test(widget, data);
1920 c_options->star_rating.rejected = rejected_symbol;
1923 /* general options tab */
1924 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1928 GCancellable *cancellable;
1930 GtkWidget *progress;
1932 GFile *timezone_database_gq;
1933 gchar *timezone_database_user;
1936 static void config_tab_general(GtkWidget *notebook)
1941 GtkWidget *group_frame;
1942 GtkWidget *subgroup;
1944 GtkWidget *ct_button;
1947 gint hours, minutes, remainder;
1949 GtkWidget *star_rating_entry;
1951 gchar *rating_symbol;
1954 gchar *download_locn;
1955 GNetworkMonitor *net_mon;
1956 GSocketConnectable *tz_org;
1957 gboolean internet_available = FALSE;
1960 vbox = scrolled_notebook_page(notebook, _("General"));
1962 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1964 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1965 add_thumb_size_menu(table, 0, 0, _("Size:"));
1966 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1968 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1969 pref_label_new(hbox, _("Custom size: "));
1970 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1971 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1973 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1974 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1976 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1977 pref_checkbox_link_sensitivity(ct_button, subgroup);
1979 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1980 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1981 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1982 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1983 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
1984 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1985 G_CALLBACK(cache_geeqie_cb), nullptr);
1987 group_frame = pref_frame_new(subgroup, TRUE,
1988 _("Store thumbnails local to image folder (non-standard)"),
1989 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1990 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
1991 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
1992 G_CALLBACK(cache_local_cb), nullptr);
1994 group_frame = pref_frame_new(subgroup, TRUE,
1995 _("Use standard thumbnail style and cache, shared with other applications"),
1996 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1997 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
1998 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1999 G_CALLBACK(cache_standard_cb), nullptr);
2001 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2002 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2004 pref_checkbox_new_int(group, _("Thumbnail color management"),
2005 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2007 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2009 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2010 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2012 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
2013 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2014 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2017 pref_spacer(group, PREF_PAD_GROUP);
2019 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2021 c_options->star_rating.star = options->star_rating.star;
2022 c_options->star_rating.rejected = options->star_rating.rejected;
2024 str = g_string_new(nullptr);
2025 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2026 pref_label_new(hbox, _("Star character: "));
2027 str = g_string_append_unichar(str, options->star_rating.star);
2028 pref_label_new(hbox, g_strdup(str->str));
2029 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2030 star_rating_entry = gtk_entry_new();
2031 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2032 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2033 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2034 gtk_widget_show(star_rating_entry);
2035 button = pref_button_new(nullptr, nullptr, _("Set"),
2036 G_CALLBACK(star_rating_star_test_cb), hbox);
2037 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2038 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2039 gtk_widget_show(button);
2040 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."));
2041 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2042 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2043 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2044 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2045 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2046 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2047 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2048 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2049 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2050 G_CALLBACK(star_rating_star_icon_cb),
2053 g_string_free(str, TRUE);
2054 g_free(rating_symbol);
2056 str = g_string_new(nullptr);
2057 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2058 pref_label_new(hbox, _("Rejected character: "));
2059 str = g_string_append_unichar(str, options->star_rating.rejected);
2060 pref_label_new(hbox, g_strdup(str->str));
2061 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2062 star_rating_entry = gtk_entry_new();
2063 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2064 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2065 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2066 gtk_widget_show(star_rating_entry);
2067 button = pref_button_new(nullptr, nullptr, _("Set"),
2068 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2069 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2070 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2071 gtk_widget_show(button);
2072 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."));
2073 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2074 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2075 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2076 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2077 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2078 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2079 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2080 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2081 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2082 G_CALLBACK(star_rating_rejected_icon_cb),
2085 g_string_free(str, TRUE);
2086 g_free(rating_symbol);
2088 pref_spacer(group, PREF_PAD_GROUP);
2090 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2092 c_options->slideshow.delay = options->slideshow.delay;
2093 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2094 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2095 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2096 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2097 SLIDESHOW_SUBSECOND_PRECISION;
2099 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2101 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2103 options->slideshow.delay ? hours : 0.0,
2104 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2105 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2106 spin = pref_spin_new(hbox, ":" , nullptr,
2108 options->slideshow.delay ? minutes: 0.0,
2109 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2110 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2111 spin = pref_spin_new(hbox, ":", nullptr,
2112 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2113 options->slideshow.delay ? seconds : 10.0,
2114 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2115 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2117 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2118 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2120 pref_spacer(group, PREF_PAD_GROUP);
2122 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2124 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2125 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2126 pref_checkbox_new_int(group, _("Preload next image"),
2127 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2129 pref_checkbox_new_int(group, _("Refresh on file change"),
2130 options->update_on_time_change, &c_options->update_on_time_change);
2133 pref_spacer(group, PREF_PAD_GROUP);
2135 group = pref_group_new(vbox, FALSE, _("Expand menu and toolbar"), GTK_ORIENTATION_VERTICAL);
2137 pref_checkbox_new_int(group, _("Expand menu and toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2138 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2139 gtk_widget_set_tooltip_text(group, _("Expand the menu and toolbar to the full width of the window"));
2141 pref_spacer(group, PREF_PAD_GROUP);
2143 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2145 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2146 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2147 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2148 gtk_widget_set_tooltip_text(group, _("Show a notification on start-up if the server has a newer version than the current. Requires an Internet connection"));
2150 pref_spacer(group, PREF_PAD_GROUP);
2154 net_mon = g_network_monitor_get_default();
2155 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2158 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2159 g_object_unref(tz_org);
2162 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2163 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2165 if (!internet_available)
2167 gtk_widget_set_sensitive(group, FALSE);
2170 tz = g_new0(TZData, 1);
2172 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2173 basename = g_path_get_basename(path);
2174 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2178 if (isfile(tz->timezone_database_user))
2180 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2184 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2187 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2188 pref_label_new(GTK_WIDGET(hbox), download_locn);
2189 g_free(download_locn);
2191 if (!internet_available)
2193 gtk_widget_set_tooltip_text(button, _("No Internet connection!\nThe timezone database is used to display exif time and date\ncorrected for UTC offset and Daylight Saving Time"));
2197 gtk_widget_set_tooltip_text(button, _("The timezone database is used to display exif time and date\ncorrected for UTC offset and Daylight Saving Time"));
2199 gtk_widget_show(button);
2201 pref_spacer(group, PREF_PAD_GROUP);
2203 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2205 help_search_engine_entry = gtk_entry_new();
2206 gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2207 gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2208 gtk_widget_show(help_search_engine_entry);
2210 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"));
2212 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2213 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2214 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2215 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2216 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2217 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2218 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2219 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2220 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2221 G_CALLBACK(help_search_engine_entry_icon_cb),
2222 help_search_engine_entry);
2226 static void config_tab_image(GtkWidget *notebook)
2231 GtkWidget *ct_button;
2232 GtkWidget *enlargement_button;
2236 vbox = scrolled_notebook_page(notebook, _("Image"));
2238 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2240 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2241 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2243 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2244 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2246 c_options->image.zoom_increment = options->image.zoom_increment;
2247 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2248 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2249 G_CALLBACK(zoom_increment_cb), nullptr);
2250 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2252 c_options->image.zoom_style = options->image.zoom_style;
2253 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2254 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2256 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2258 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2259 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2260 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2261 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2263 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2264 pref_checkbox_link_sensitivity(enlargement_button, spin);
2265 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."));
2267 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2268 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2269 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2270 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2272 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2273 pref_checkbox_link_sensitivity(ct_button, spin);
2274 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."));
2276 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2278 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2279 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2281 options->image.tile_size, &c_options->image.tile_size);
2282 gtk_widget_set_tooltip_text(GTK_WIDGET(hbox), _("This value changes the size of the tiles large images are split into. Increasing the size of the tiles will reduce the tiling effect seen on image changes, but will also slightly increase the delay before the first part of a large image is seen."));
2284 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2286 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2287 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2289 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2290 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2292 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2293 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2295 c_options->image.border_color = options->image.border_color;
2297 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2298 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2300 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2301 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2303 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2304 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2309 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2311 LayoutWindow *lw = nullptr;
2312 gchar *default_path;
2315 /* Get current lw */
2318 tmp_id = lw->options.id;
2319 lw->options.id = g_strdup("lw_default");
2321 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2322 save_default_layout_options_to_file(default_path, options, lw);
2323 g_free(lw->options.id);
2324 lw->options.id = tmp_id;
2325 g_free(default_path);
2328 static gboolean popover_cb(gpointer data)
2330 auto popover = static_cast<GtkPopover *>(data);
2332 gtk_popover_popdown(popover);
2337 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2339 gtk_popover_popup(popover);
2341 g_timeout_add(2000, popover_cb, popover);
2344 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2348 popover = gtk_popover_new(parent);
2349 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2350 gtk_container_add (GTK_CONTAINER(popover), child);
2351 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2352 gtk_widget_show (child);
2357 static void config_tab_windows(GtkWidget *notebook)
2362 GtkWidget *subgroup;
2364 GtkWidget *ct_button;
2367 vbox = scrolled_notebook_page(notebook, _("Windows"));
2369 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2371 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2372 options->save_window_positions, &c_options->save_window_positions);
2374 button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2375 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2376 pref_checkbox_link_sensitivity(ct_button, button);
2378 button = pref_checkbox_new_int(group, _("Remember window workspace"),
2379 options->save_window_workspace, &c_options->save_window_workspace);
2380 pref_checkbox_link_sensitivity(ct_button, button);
2382 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2383 options->tools_restore_state, &c_options->tools_restore_state);
2385 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2386 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2388 pref_checkbox_new_int(group, _("Show window IDs"),
2389 options->show_window_ids, &c_options->show_window_ids);
2391 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2392 pref_label_new(subgroup, _("Use current layout for default: "));
2393 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2397 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2398 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2399 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2401 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2403 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2404 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2406 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2407 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2408 options->image.limit_window_size, &c_options->image.limit_window_size);
2409 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2411 options->image.max_window_size, &c_options->image.max_window_size);
2412 pref_checkbox_link_sensitivity(ct_button, spin);
2414 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2416 c_options->fullscreen.screen = options->fullscreen.screen;
2417 c_options->fullscreen.above = options->fullscreen.above;
2418 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2419 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2420 gtk_widget_show(hbox);
2422 pref_checkbox_new_int(group, _("Smooth image flip"),
2423 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2424 pref_checkbox_new_int(group, _("Disable screen saver"),
2425 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2428 #define PRE_FORMATTED_COLUMNS 5
2429 static void config_tab_osd(GtkWidget *notebook)
2435 GtkWidget *image_overlay_template_view;
2436 GtkWidget *scrolled;
2437 GtkWidget *scrolled_pre_formatted;
2438 GtkTextBuffer *buffer;
2440 GtkWidget *subgroup;
2442 vbox = scrolled_notebook_page(notebook, _("OSD"));
2444 image_overlay_template_view = gtk_text_view_new();
2446 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2448 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2450 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2451 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2452 gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2453 gtk_widget_show(scrolled_pre_formatted);
2454 gtk_widget_show(subgroup);
2456 pref_line(group, PREF_PAD_GAP);
2458 pref_label_new(group, _("Image overlay template"));
2460 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2461 gtk_widget_set_size_request(scrolled, 200, 150);
2462 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2463 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2464 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2465 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2466 gtk_widget_show(scrolled);
2468 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2469 _("Extensive formatting options are shown in the Help file"));
2471 gtk_container_add(GTK_CONTAINER(scrolled), image_overlay_template_view);
2472 gtk_widget_show(image_overlay_template_view);
2474 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2476 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2477 G_CALLBACK(image_overlay_set_font_cb), notebook);
2479 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2480 gtk_widget_show(button);
2482 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"),
2483 G_CALLBACK(image_overlay_set_text_colour_cb), nullptr);
2484 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2485 gtk_widget_show(button);
2487 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"),
2488 G_CALLBACK(image_overlay_set_background_colour_cb), nullptr);
2489 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2490 gtk_widget_show(button);
2491 image_overlay_set_text_colours();
2493 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2494 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2495 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2496 gtk_widget_show(button);
2498 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2499 G_CALLBACK(image_overlay_help_cb), nullptr);
2500 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2501 gtk_widget_show(button);
2503 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2504 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2505 g_signal_connect(G_OBJECT(buffer), "changed",
2506 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2508 pref_line(group, PREF_PAD_GAP);
2510 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2511 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2512 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2513 gtk_widget_show(hbox);
2514 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2515 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2516 gtk_widget_show(label);
2517 pref_spacer(group,TRUE);
2519 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2520 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2521 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2522 gtk_widget_show(hbox);
2523 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2524 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2525 gtk_widget_show(label);
2526 pref_spacer(group,TRUE);
2528 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2529 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2530 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2531 gtk_widget_show(hbox);
2532 label = gtk_label_new(_("%path:39%"));
2533 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2534 gtk_widget_show(label);
2535 pref_spacer(group,TRUE);
2537 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2538 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2539 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2540 gtk_widget_show(hbox);
2541 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%"));
2542 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2543 gtk_widget_show(label);
2544 pref_spacer(group,TRUE);
2546 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2547 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2548 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2549 gtk_widget_show(hbox);
2550 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2551 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2552 gtk_widget_show(label);
2555 static GtkTreeModel *create_class_model()
2557 GtkListStore *model;
2561 /* create list store */
2562 model = gtk_list_store_new(1, G_TYPE_STRING);
2563 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2565 gtk_list_store_append(model, &iter);
2566 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2568 return GTK_TREE_MODEL (model);
2573 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2575 gint n = GPOINTER_TO_INT(data);
2577 FilterEntry *filter_a;
2578 FilterEntry *filter_b;
2580 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2581 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2585 case FILETYPES_COLUMN_ENABLED:
2587 ret = filter_a->enabled - filter_b->enabled;
2590 case FILETYPES_COLUMN_FILTER:
2592 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2595 case FILETYPES_COLUMN_DESCRIPTION:
2597 ret = g_utf8_collate(filter_a->description, filter_b->description);
2600 case FILETYPES_COLUMN_CLASS:
2602 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2605 case FILETYPES_COLUMN_WRITABLE:
2607 ret = filter_a->writable - filter_b->writable;
2610 case FILETYPES_COLUMN_SIDECAR:
2612 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2616 g_return_val_if_reached(0);
2622 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2625 gboolean ret = TRUE;
2627 gtk_tree_model_get(model, iter, 0, &fe, -1);
2629 if (g_strstr_len(fe->extensions, -1, key))
2637 static void config_tab_files(GtkWidget *notebook)
2644 GtkWidget *ct_button;
2645 GtkWidget *scrolled;
2646 GtkWidget *filter_view;
2647 GtkCellRenderer *renderer;
2648 GtkTreeSelection *selection;
2649 GtkTreeViewColumn *column;
2651 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2653 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2655 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2656 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2657 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2658 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2659 pref_checkbox_new_int(group, _("Case sensitive sort (Search and Collection windows, and tab completion)"), options->file_sort.case_sensitive, &c_options->file_sort.case_sensitive);
2660 pref_checkbox_new_int(group, _("Disable file extension checks"),
2661 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2663 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2664 options->file_filter.disable, &c_options->file_filter.disable);
2667 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2669 sidecar_ext_entry = gtk_entry_new();
2670 gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2671 gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2672 gtk_widget_show(sidecar_ext_entry);
2674 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2676 frame = pref_group_parent(group);
2677 g_signal_connect(G_OBJECT(ct_button), "toggled",
2678 G_CALLBACK(filter_disable_cb), frame);
2679 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2681 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2682 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2683 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2684 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2685 gtk_widget_show(scrolled);
2687 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2688 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2689 g_object_unref(filter_store);
2690 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2691 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2693 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2695 column = gtk_tree_view_column_new();
2696 gtk_tree_view_column_set_title(column, _("Enabled"));
2697 gtk_tree_view_column_set_resizable(column, TRUE);
2699 renderer = gtk_cell_renderer_toggle_new();
2700 g_signal_connect(G_OBJECT(renderer), "toggled",
2701 G_CALLBACK(filter_store_enable_cb), filter_store);
2702 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2703 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2704 GINT_TO_POINTER(FE_ENABLE), nullptr);
2705 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_ENABLED, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_ENABLED), nullptr);
2706 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2707 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2709 column = gtk_tree_view_column_new();
2710 gtk_tree_view_column_set_title(column, _("Filter"));
2711 gtk_tree_view_column_set_resizable(column, TRUE);
2712 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_FILTER, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_FILTER), nullptr);
2713 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2715 renderer = gtk_cell_renderer_text_new();
2716 g_signal_connect(G_OBJECT(renderer), "edited",
2717 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2718 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2719 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2720 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2721 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2722 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2724 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2725 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2726 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2728 column = gtk_tree_view_column_new();
2729 gtk_tree_view_column_set_title(column, _("Description"));
2730 gtk_tree_view_column_set_resizable(column, TRUE);
2731 gtk_tree_view_column_set_fixed_width(column, 200);
2732 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2734 renderer = gtk_cell_renderer_text_new();
2735 g_signal_connect(G_OBJECT(renderer), "edited",
2736 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2737 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2738 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2739 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2740 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2741 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2742 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_DESCRIPTION, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_DESCRIPTION), nullptr);
2743 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2745 column = gtk_tree_view_column_new();
2746 gtk_tree_view_column_set_title(column, _("Class"));
2747 gtk_tree_view_column_set_resizable(column, TRUE);
2748 renderer = gtk_cell_renderer_combo_new();
2749 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2750 "model", create_class_model(),
2755 g_signal_connect(G_OBJECT(renderer), "edited",
2756 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2757 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2758 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2759 GINT_TO_POINTER(FE_CLASS), nullptr);
2760 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2761 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_CLASS, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_CLASS), nullptr);
2762 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2764 column = gtk_tree_view_column_new();
2765 gtk_tree_view_column_set_title(column, _("Writable"));
2766 gtk_tree_view_column_set_resizable(column, FALSE);
2767 renderer = gtk_cell_renderer_toggle_new();
2768 g_signal_connect(G_OBJECT(renderer), "toggled",
2769 G_CALLBACK(filter_store_writable_cb), filter_store);
2770 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2771 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2772 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2773 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2774 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_WRITABLE, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_WRITABLE), nullptr);
2775 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2777 column = gtk_tree_view_column_new();
2778 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2779 gtk_tree_view_column_set_resizable(column, FALSE);
2780 renderer = gtk_cell_renderer_toggle_new();
2781 g_signal_connect(G_OBJECT(renderer), "toggled",
2782 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2783 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2784 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2785 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2786 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2787 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_SIDECAR, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_SIDECAR), nullptr);
2788 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2790 filter_store_populate();
2791 gtk_container_add(GTK_CONTAINER(scrolled), filter_view);
2792 gtk_widget_show(filter_view);
2794 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2796 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2797 G_CALLBACK(filter_default_cb), filter_view);
2798 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2799 gtk_widget_show(button);
2801 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2802 G_CALLBACK(filter_remove_cb), filter_view);
2803 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2804 gtk_widget_show(button);
2806 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2807 G_CALLBACK(filter_add_cb), filter_view);
2808 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2809 gtk_widget_show(button);
2813 static void config_tab_metadata(GtkWidget *notebook)
2818 GtkWidget *ct_button;
2820 GtkWidget *tmp_widget;
2822 GtkWidget *text_label;
2824 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2827 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2829 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2831 label = pref_label_new(group, _("When writing metadata, Geeqie will follow these steps, if selected. This process will stop when the first successful write occurs."));
2832 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2833 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2835 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2837 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2838 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2839 markup = g_markup_printf_escaped ("<span weight=\"bold\">%s</span>%s", _("Step 1"), _(") Save metadata in either the image file or the sidecar file, according to the XMP standard"));
2840 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2842 markup = g_markup_printf_escaped ("%s<span style=\"italic\">%s</span>%s<span style=\"italic\">%s</span>%s", _("The destination is dependent on the settings in the "), _("Writable"), _(" and "), _("Sidecar Is Allowed"), _(" columns of the File Filters tab)"));
2843 gtk_widget_set_tooltip_markup(ct_button, markup);
2847 gtk_widget_set_sensitive(ct_button, FALSE);
2850 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2851 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2852 markup = g_markup_printf_escaped ("<span weight=\"bold\">%s</span>%s<span style=\"italic\">%s</span>%s", _("Step 2"), _(") Save metadata in the folder "),".metadata,", _(" local to the image folder (non-standard)"));
2853 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2856 label = pref_label_new(group, "");
2857 markup = g_markup_printf_escaped ("<span weight=\"bold\">%s</span>%s<span style=\"italic\">%s</span>%s", _("Step 3"), _(") Save metadata in Geeqie private directory "), get_metadata_cache_dir(), "/");
2858 gtk_label_set_markup (GTK_LABEL(label), markup);
2861 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2862 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2863 gtk_misc_set_padding(GTK_MISC(label), 22, 0);
2864 pref_spacer(group, PREF_PAD_GROUP);
2866 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2868 gtk_widget_set_sensitive(group, FALSE);
2871 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2872 pref_checkbox_link_sensitivity(ct_button, hbox);
2874 tmp_widget= pref_checkbox_new_int(hbox, _("Store metadata also in IPTC tags (converted according to the IPTC4XMP standard)"), options->metadata.save_legacy_IPTC, &c_options->metadata.save_legacy_IPTC);
2875 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2877 pref_checkbox_new_int(hbox, _("Warn if the image or sidecar file is not writable"), options->metadata.warn_on_write_problems, &c_options->metadata.warn_on_write_problems);
2879 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2881 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2882 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2883 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2884 markup = g_markup_printf_escaped ("%s<span style=\"italic\">%s</span>%s<span style=\"italic\">%s</span>%s", _("Create sidecar files named "), "image.ext.xmp", _(" (as opposed to the normal "), "image.xmp", ")");
2885 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2888 pref_spacer(group, PREF_PAD_GROUP);
2890 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2892 gtk_widget_set_sensitive(group, FALSE);
2895 pref_checkbox_new_int(group, _("Use GQview legacy metadata format instead of XMP (supports only Keywords and Comments)"), options->metadata.save_legacy_format, &c_options->metadata.save_legacy_format);
2897 pref_spacer(group, PREF_PAD_GROUP);
2899 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2900 tmp_widget = pref_checkbox_new_int(group, _("Write the same description tags to all grouped sidecars"), options->metadata.sync_grouped_files, &c_options->metadata.sync_grouped_files);
2901 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2903 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2904 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2906 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2907 gtk_widget_set_tooltip_text(ct_button, _("If checked, the results of orientation commands (Rotate, Mirror and Flip) issued on an image will be written to metadata\nNote: If this option is not checked, the results of orientation commands will be lost when Geeqie closes"));
2910 gtk_widget_set_sensitive(ct_button, FALSE);
2913 pref_spacer(group, PREF_PAD_GROUP);
2915 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2917 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2919 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2920 pref_checkbox_link_sensitivity(ct_button, hbox);
2922 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2924 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2926 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2928 pref_spacer(group, PREF_PAD_GROUP);
2931 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2933 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2934 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2937 pref_spacer(group, PREF_PAD_GROUP);
2939 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2941 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2942 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");
2947 struct KeywordFindData
2954 GtkWidget *button_close;
2955 GtkWidget *button_stop;
2956 GtkWidget *button_start;
2957 GtkWidget *progress;
2965 guint idle_id; /* event source id */
2968 #define KEYWORD_DIALOG_WIDTH 400
2970 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
2972 GList *list_d = nullptr;
2973 GList *list_f = nullptr;
2977 filelist_read(dir_fd, &list_f, &list_d);
2981 filelist_read(dir_fd, &list_f, nullptr);
2984 list_f = filelist_filter(list_f, FALSE);
2985 list_d = filelist_filter(list_d, TRUE);
2987 kfd->list = g_list_concat(list_f, kfd->list);
2988 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
2991 static void keywords_find_reset(KeywordFindData *kfd)
2993 filelist_free(kfd->list);
2994 kfd->list = nullptr;
2996 filelist_free(kfd->list_dir);
2997 kfd->list_dir = nullptr;
3000 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3002 auto kfd = static_cast<KeywordFindData *>(data);
3004 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3006 keywords_find_reset(kfd);
3007 generic_dialog_close(kfd->gd);
3011 static void keywords_find_finish(KeywordFindData *kfd)
3013 keywords_find_reset(kfd);
3015 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3016 spinner_set_interval(kfd->spinner, -1);
3018 gtk_widget_set_sensitive(kfd->group, TRUE);
3019 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3020 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3021 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3024 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3026 auto kfd = static_cast<KeywordFindData *>(data);
3028 g_idle_remove_by_data(kfd);
3030 keywords_find_finish(kfd);
3033 static gboolean keywords_find_file(gpointer data)
3035 auto kfd = static_cast<KeywordFindData *>(data);
3037 GtkTextBuffer *buffer;
3045 fd = static_cast<FileData *>(kfd->list->data);
3046 kfd->list = g_list_remove(kfd->list, fd);
3048 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3049 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3053 gtk_text_buffer_get_end_iter(buffer, &iter);
3054 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3055 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3057 keywords = keywords->next;
3060 gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3061 file_data_unref(fd);
3062 g_list_free_full(keywords, g_free);
3064 return (G_SOURCE_CONTINUE);
3066 else if (kfd->list_dir)
3070 fd = static_cast<FileData *>(kfd->list_dir->data);
3071 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3073 keywords_find_folder(kfd, fd);
3075 file_data_unref(fd);
3077 return G_SOURCE_CONTINUE;
3080 keywords_find_finish(kfd);
3082 return G_SOURCE_REMOVE;
3085 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3087 auto kfd = static_cast<KeywordFindData *>(data);
3090 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3092 path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3093 parse_out_relatives(path);
3097 warning_dialog(_("Invalid folder"),
3098 _("The specified folder can not be found."),
3099 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3105 gtk_widget_set_sensitive(kfd->group, FALSE);
3106 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3107 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3108 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3109 spinner_set_interval(kfd->spinner, SPINNER_SPEED);
3111 dir_fd = file_data_new_dir(path);
3112 keywords_find_folder(kfd, dir_fd);
3113 file_data_unref(dir_fd);
3114 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3120 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3122 KeywordFindData *kfd;
3126 kfd = g_new0(KeywordFindData, 1);
3128 kfd->gd = generic_dialog_new(_("Search for keywords"),
3129 "search_for_keywords",
3132 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3133 kfd->gd->cancel_cb = keywords_find_close_cb;
3134 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3135 keywords_find_close_cb, FALSE);
3136 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3137 keywords_find_start_cb, FALSE);
3138 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3139 keywords_find_stop_cb, FALSE);
3140 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3142 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3144 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3145 pref_spacer(hbox, PREF_PAD_INDENT);
3146 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3148 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3149 pref_label_new(hbox, _("Folder:"));
3151 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3152 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3153 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3154 gtk_widget_show(label);
3156 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3158 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3159 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3161 kfd->progress = gtk_entry_new();
3162 gtk_widget_set_can_focus(kfd->progress, FALSE);
3163 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3164 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3165 gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3166 gtk_widget_show(kfd->progress);
3168 kfd->spinner = spinner_new(nullptr, -1);
3169 gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3170 gtk_widget_show(kfd->spinner);
3172 kfd->list = nullptr;
3174 gtk_widget_show(kfd->gd->dialog);
3177 static void keywords_find_cb(GtkWidget *widget, gpointer)
3179 const gchar *path = layout_get_path(nullptr);
3181 if (!path || !*path) path = homedir();
3182 keywords_find_dialog(widget, path);
3185 static void config_tab_keywords_save()
3187 GtkTextIter start, end;
3188 GtkTextBuffer *buffer;
3189 GList *kw_list = nullptr;
3195 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3196 gtk_text_buffer_get_bounds(buffer, &start, &end);
3198 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3200 kw_split = strtok(buffer_text, "\n");
3201 while (kw_split != nullptr)
3207 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3216 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3218 kw_split = strtok(nullptr, "\n");
3221 keyword_list_set(kw_list);
3223 g_list_free_full(kw_list, g_free);
3224 g_free(buffer_text);
3227 static void config_tab_keywords(GtkWidget *notebook)
3233 GtkWidget *scrolled;
3235 GtkTextBuffer *buffer;
3238 GspellTextView *gspell_view;
3241 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3243 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3245 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3247 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3248 G_CALLBACK(keywords_find_cb), keyword_text);
3249 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3252 keyword_text = gtk_text_view_new();
3253 gtk_widget_set_size_request(keyword_text, 20, 20);
3254 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3255 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3256 gtk_widget_show(scrolled);
3259 if (options->metadata.check_spelling)
3261 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3262 gspell_text_view_basic_setup(gspell_view);
3266 gtk_container_add(GTK_CONTAINER(scrolled), keyword_text);
3267 gtk_widget_show(keyword_text);
3269 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3271 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3272 gtk_text_buffer_create_tag(buffer, "monospace",
3273 "family", "monospace", NULL);
3275 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3276 gtk_text_buffer_get_start_iter(buffer, &iter);
3277 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3280 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3282 GList *kwl = keyword_list_get();
3283 kwl = g_list_first(kwl);
3286 gtk_text_buffer_get_end_iter (buffer, &iter);
3287 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3288 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3293 gtk_text_buffer_set_modified(buffer, FALSE);
3300 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3302 auto option = static_cast<gint *>(data);
3304 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3308 *option = INTENT_PERCEPTUAL;
3311 *option = INTENT_RELATIVE_COLORIMETRIC;
3314 *option = INTENT_SATURATION;
3317 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3322 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3323 gint option, gint *option_c)
3330 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3332 combo = gtk_combo_box_text_new();
3334 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3335 if (option == INTENT_PERCEPTUAL) current = 0;
3336 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3337 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3338 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3339 if (option == INTENT_SATURATION) current = 2;
3340 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3341 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3343 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3345 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3347 g_signal_connect(G_OBJECT(combo), "changed",
3348 G_CALLBACK(intent_menu_cb), option_c);
3350 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3351 gtk_widget_show(combo);
3355 static void config_tab_color(GtkWidget *notebook)
3364 vbox = scrolled_notebook_page(notebook, _("Color management"));
3366 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3368 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3371 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3372 gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
3374 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3375 pref_label_bold(label, TRUE, FALSE);
3377 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3378 pref_label_bold(label, TRUE, FALSE);
3380 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3381 pref_label_bold(label, TRUE, FALSE);
3383 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3388 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3389 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3392 entry = gtk_entry_new();
3393 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3394 if (options->color_profile.input_name[i])
3396 gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3398 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
3399 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3400 gtk_widget_show(entry);
3401 color_profile_input_name_entry[i] = entry;
3403 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3404 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3405 gtk_widget_set_size_request(entry, 160, -1);
3406 gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
3407 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3408 gtk_widget_show(tabcomp);
3409 color_profile_input_file_entry[i] = entry;
3412 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3414 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3416 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3417 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3419 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3421 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3422 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3423 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3424 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3425 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3427 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3429 gtk_table_attach(GTK_TABLE(table), tabcomp, 1, 2,
3431 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3433 gtk_widget_show(tabcomp);
3436 /* advanced entry tab */
3437 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3439 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3441 c_options->file_ops.use_system_trash = FALSE;
3442 c_options->file_ops.no_trash = FALSE;
3446 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3448 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3450 c_options->file_ops.use_system_trash = TRUE;
3451 c_options->file_ops.no_trash = FALSE;
3455 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3457 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3459 c_options->file_ops.no_trash = TRUE;
3463 static void config_tab_behavior(GtkWidget *notebook)
3470 GtkWidget *ct_button;
3474 GtkWidget *with_rename;
3475 GtkWidget *collections_on_top;
3476 GtkWidget *hide_window_in_fullscreen;
3477 GtkWidget *checkbox;
3480 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3482 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3484 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3485 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3486 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3487 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3488 pref_checkbox_new_int(group, _("Enable Delete key"),
3489 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3491 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3492 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3494 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3495 pref_checkbox_link_sensitivity(ct_button, hbox);
3497 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3498 pref_label_new(hbox, _("Folder:"));
3500 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3501 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3502 gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3503 gtk_widget_show(tabcomp);
3505 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3506 pref_checkbox_link_sensitivity(ct_button, hbox);
3508 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3509 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3510 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3511 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3512 button = pref_button_new(nullptr, nullptr, _("View"),
3513 G_CALLBACK(safe_delete_view_cb), nullptr);
3514 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3515 gtk_widget_show(button);
3517 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3518 G_CALLBACK(safe_delete_clear_cb), nullptr);
3519 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3521 c_options->file_ops.no_trash = options->file_ops.no_trash;
3522 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3524 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3525 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3527 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3528 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3530 gtk_widget_show(button);
3532 pref_spacer(group, PREF_PAD_GROUP);
3535 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3537 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3538 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3540 pref_checkbox_new_int(group, _("In place renaming"),
3541 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3543 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3544 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3546 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3547 options->circular_selection_lists, &c_options->circular_selection_lists);
3548 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3550 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3551 options->marks_save, &c_options->marks_save);
3552 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3554 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3555 options->with_rename, &c_options->with_rename);
3556 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3558 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3559 options->collections_on_top, &c_options->collections_on_top);
3560 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3562 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3563 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3564 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3566 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3567 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3569 tmp = pref_spin_new_int(group, _("Recent folder-image list maximum size"), nullptr, 0, 50, 1, options->recent_folder_image_list_maxsize, &c_options->recent_folder_image_list_maxsize);
3570 gtk_widget_set_tooltip_text(tmp, _("List of the last image viewed in each recent folder.\nRe-opening a folder will set focus to the last image viewed."));
3572 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3573 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3575 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3576 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3578 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3579 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3581 pref_spacer(group, PREF_PAD_GROUP);
3583 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3585 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3586 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3587 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3588 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3589 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3590 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3591 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3592 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3593 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3594 options->image_l_click_archive, &c_options->image_l_click_archive);
3595 pref_checkbox_new_int(group, _("Play video by left click on image"),
3596 options->image_l_click_video, &c_options->image_l_click_video);
3597 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3598 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3600 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3601 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3602 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3603 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3604 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3606 pref_spacer(group, PREF_PAD_GROUP);
3608 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3610 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3611 options->override_disable_gpu, &c_options->override_disable_gpu);
3612 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3615 pref_spacer(group, PREF_PAD_GROUP);
3617 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3619 pref_spin_new_int(group, _("Debug level:"), nullptr,
3620 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3622 pref_checkbox_new_int(group, _("Timer data"),
3623 options->log_window.timer_data, &c_options->log_window.timer_data);
3625 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3626 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3630 /* accelerators tab */
3632 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3634 gboolean ret = TRUE;
3639 gtk_tree_model_get(model, iter, column, &text, -1);
3640 text_nocase = g_utf8_casefold(text, -1);
3641 key_nocase = g_utf8_casefold(key, -1);
3643 if (g_strstr_len(text_nocase, -1, key_nocase))
3650 g_free(text_nocase);
3655 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3660 list = gtk_tree_view_get_columns(tree_view);
3661 col_num = g_list_index(list, column);
3665 gtk_tree_view_set_search_column(tree_view, col_num);
3668 static void config_tab_accelerators(GtkWidget *notebook)
3674 GtkWidget *scrolled;
3675 GtkWidget *accel_view;
3676 GtkCellRenderer *renderer;
3677 GtkTreeSelection *selection;
3678 GtkTreeViewColumn *column;
3680 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3682 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3684 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3685 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3686 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3687 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3688 gtk_widget_show(scrolled);
3690 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3692 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3693 g_object_unref(accel_store);
3694 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3695 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3697 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3699 renderer = gtk_cell_renderer_text_new();
3701 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3706 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3707 gtk_tree_view_column_set_resizable(column, TRUE);
3708 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3711 renderer = gtk_cell_renderer_accel_new();
3712 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3713 G_CALLBACK(accel_store_cleared_cb), accel_store);
3714 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3715 G_CALLBACK(accel_store_edited_cb), accel_store);
3718 g_object_set (renderer,
3720 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3723 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3728 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3729 gtk_tree_view_column_set_resizable(column, TRUE);
3730 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3732 renderer = gtk_cell_renderer_text_new();
3734 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3739 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3740 gtk_tree_view_column_set_resizable(column, TRUE);
3741 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3743 renderer = gtk_cell_renderer_text_new();
3745 column = gtk_tree_view_column_new_with_attributes("Accel",
3750 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3751 gtk_tree_view_column_set_resizable(column, TRUE);
3752 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3754 /* Search on text in column */
3755 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3756 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3757 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3758 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3759 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3761 accel_store_populate();
3762 gtk_container_add(GTK_CONTAINER(scrolled), accel_view);
3763 gtk_widget_show(accel_view);
3765 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3767 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3768 G_CALLBACK(accel_default_cb), accel_view);
3769 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3770 gtk_widget_show(button);
3772 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3773 G_CALLBACK(accel_reset_cb), accel_view);
3774 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3775 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3776 gtk_widget_show(button);
3778 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3779 G_CALLBACK(accel_clear_cb), accel_view);
3780 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3781 gtk_widget_show(button);
3784 /* toolbar main tab */
3785 static void config_tab_toolbar_main(GtkWidget *notebook)
3788 GtkWidget *toolbardata;
3791 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3793 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3795 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3796 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3797 gtk_widget_show(vbox);
3800 /* toolbar status tab */
3801 static void config_tab_toolbar_status(GtkWidget *notebook)
3804 GtkWidget *toolbardata;
3807 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3809 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3811 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3812 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3813 gtk_widget_show(vbox);
3817 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3819 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3822 static void config_tab_advanced(GtkWidget *notebook)
3826 GSList *formats_list;
3827 GList *extensions_list = nullptr;
3830 GdkPixbufFormat *fm;
3832 GString *types_string = g_string_new(nullptr);
3833 GtkWidget *types_string_label;
3834 GtkWidget *threads_string_label;
3835 GtkWidget *dupes_threads_spin;
3837 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3838 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3840 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3842 pref_spacer(group, PREF_PAD_GROUP);
3844 formats_list = gdk_pixbuf_get_formats();
3846 while (formats_list)
3848 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3849 extensions = gdk_pixbuf_format_get_extensions(fm);
3852 while (extensions[i])
3854 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3858 g_strfreev(extensions);
3859 formats_list = formats_list->next;
3862 while (extensions_list)
3864 if (types_string->len == 0)
3866 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3870 types_string = g_string_append(types_string, ", ");
3871 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3874 extensions_list = extensions_list->next;
3877 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3878 types_string_label = pref_label_new(group, types_string->str);
3879 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3881 pref_spacer(group, PREF_PAD_GROUP);
3883 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3884 external_preview_select_entry = gtk_entry_new();
3885 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3886 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3887 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3888 gtk_widget_show(tabcomp);
3890 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3891 external_preview_extract_entry = gtk_entry_new();
3892 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3893 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3894 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3895 gtk_widget_show(tabcomp);
3897 gtk_widget_show(vbox);
3899 g_slist_free(formats_list);
3900 g_list_free_full(extensions_list, g_free);
3901 g_string_free(types_string, TRUE);
3903 pref_spacer(group, PREF_PAD_GROUP);
3905 pref_line(vbox, PREF_PAD_SPACE);
3906 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3908 threads_string_label = pref_label_new(group, "This option limits the number of threads (or cpu cores)\nthat Geeqie will use when running duplicate checks. The default value is 0, which means all available cores will be used.");
3909 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3911 pref_spacer(vbox, PREF_PAD_GROUP);
3913 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3914 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3918 static void config_tab_stereo(GtkWidget *notebook)
3926 GtkWidget *fs_button;
3927 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3929 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3931 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3932 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3934 table = pref_table_new(group, 2, 2, TRUE, FALSE);
3935 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3936 pref_checkbox_new_int(box, _("Mirror left image"),
3937 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3938 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3939 pref_checkbox_new_int(box, _("Flip left image"),
3940 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3941 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3942 pref_checkbox_new_int(box, _("Mirror right image"),
3943 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3944 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3945 pref_checkbox_new_int(box, _("Flip right image"),
3946 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3947 pref_checkbox_new_int(group, _("Swap left and right images"),
3948 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3949 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3950 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3952 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3953 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3954 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3955 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3956 pref_checkbox_link_sensitivity(fs_button, box2);
3957 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3958 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3959 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3960 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3961 pref_checkbox_new_int(box, _("Mirror left image"),
3962 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3963 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3964 pref_checkbox_new_int(box, _("Flip left image"),
3965 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
3966 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3967 pref_checkbox_new_int(box, _("Mirror right image"),
3968 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
3969 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3970 pref_checkbox_new_int(box, _("Flip right image"),
3971 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
3972 pref_checkbox_new_int(box2, _("Swap left and right images"),
3973 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
3974 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
3975 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
3977 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
3978 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
3979 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
3980 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
3981 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
3982 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
3983 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
3984 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
3985 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
3986 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
3987 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
3988 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
3989 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
3990 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
3994 /* Main preferences window */
3995 static void config_window_create(LayoutWindow *lw)
3997 GtkWidget *win_vbox;
3999 GtkWidget *notebook;
4001 GtkWidget *ct_button;
4003 if (!c_options) c_options = init_options(nullptr);
4005 configwindow = window_new(GTK_WINDOW_TOPLEVEL, "preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4006 DEBUG_NAME(configwindow);
4007 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4008 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4009 G_CALLBACK(config_window_delete), NULL);
4010 if (options->save_dialog_window_positions)
4012 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4013 gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4017 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4019 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4020 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4022 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4023 gtk_container_add(GTK_CONTAINER(configwindow), win_vbox);
4024 gtk_widget_show(win_vbox);
4026 notebook = gtk_notebook_new();
4027 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4028 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4029 gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4031 config_tab_general(notebook);
4032 config_tab_image(notebook);
4033 config_tab_osd(notebook);
4034 config_tab_windows(notebook);
4035 config_tab_accelerators(notebook);
4036 config_tab_files(notebook);
4037 config_tab_metadata(notebook);
4038 config_tab_keywords(notebook);
4039 config_tab_color(notebook);
4040 config_tab_stereo(notebook);
4041 config_tab_behavior(notebook);
4042 config_tab_toolbar_main(notebook);
4043 config_tab_toolbar_status(notebook);
4044 config_tab_advanced(notebook);
4046 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4048 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4049 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4050 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4051 gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4052 gtk_widget_show(hbox);
4054 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4055 G_CALLBACK(config_window_help_cb), notebook);
4056 gtk_container_add(GTK_CONTAINER(hbox), button);
4057 gtk_widget_set_can_default(button, TRUE);
4058 gtk_widget_show(button);
4060 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4061 G_CALLBACK(config_window_ok_cb), notebook);
4062 gtk_container_add(GTK_CONTAINER(hbox), button);
4063 gtk_widget_set_can_default(button, TRUE);
4064 gtk_widget_grab_default(button);
4065 gtk_widget_show(button);
4069 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4070 G_CALLBACK(config_window_close_cb), nullptr);
4071 gtk_container_add(GTK_CONTAINER(hbox), button);
4072 gtk_widget_set_can_default(button, TRUE);
4073 gtk_widget_show(button);
4075 if (!generic_dialog_get_alternative_button_order(configwindow))
4077 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4080 gtk_widget_show(notebook);
4082 gtk_widget_show(configwindow);
4086 *-----------------------------------------------------------------------------
4087 * config window show (public)
4088 *-----------------------------------------------------------------------------
4091 void show_config_window(LayoutWindow *lw)
4095 gtk_window_present(GTK_WINDOW(configwindow));
4099 config_window_create(lw);
4108 void show_about_window(LayoutWindow *lw)
4110 GDataInputStream *data_stream;
4111 GInputStream *in_stream_authors;
4112 GInputStream *in_stream_translators;
4114 GdkPixbuf *pixbuf_icon;
4115 GdkPixbuf *pixbuf_logo;
4119 gchar *authors[1000];
4120 gchar *authors_path;
4122 gchar *timezone_path;
4124 gchar *translators_path;
4132 copyright = g_string_new(nullptr);
4133 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");
4135 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4136 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4138 cd = ZDOpenDatabase(timezone_path);
4141 copyright = g_string_append(copyright, ZDGetNotice(cd));
4145 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4147 ZDCloseDatabase(cd);
4149 g_free(timezone_path);
4151 authors_path = g_build_filename(GQ_RESOURCE_PATH_CREDITS, "authors", NULL);
4153 in_stream_authors = g_resources_open_stream(authors_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4155 data_stream = g_data_input_stream_new(in_stream_authors);
4157 authors[0] = nullptr;
4158 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4160 authors[i_authors] = g_strdup(author_line);
4162 g_free(author_line);
4164 authors[i_authors] = nullptr;
4166 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4168 translators_path = g_build_filename(GQ_RESOURCE_PATH_CREDITS, "translators", NULL);
4170 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4172 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4173 translators = static_cast<gchar *>(g_malloc0(size));
4174 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4175 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4177 comment = g_strconcat("Project created by John Ellis\nGQview 1998\nGeeqie 2007\n\n\nDevelopment and bug reports:\n", GQ_EMAIL_ADDRESS, "\nhttps://github.com/BestImageViewer/geeqie/issues",NULL);
4179 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4180 artists[1] = nullptr;
4182 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4183 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4184 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4185 "title", _("About Geeqie"),
4187 "program-name", GQ_APPNAME,
4189 "logo", pixbuf_logo,
4190 "icon", pixbuf_icon,
4191 "website", GQ_WEBSITE,
4192 "website-label", "Website",
4193 "comments", comment,
4196 "translator-credits", translators,
4197 "wrap-license", TRUE,
4198 "license", copyright->str,
4201 g_string_free(copyright, TRUE);
4203 while(n < i_authors)
4210 g_free(authors_path);
4212 g_free(translators);
4213 g_free(translators_path);
4214 g_object_unref(data_stream);
4215 g_object_unref(in_stream_authors);
4216 g_object_unref(in_stream_translators);
4219 static void image_overlay_set_text_colours()
4221 c_options->image_overlay.text_red = options->image_overlay.text_red;
4222 c_options->image_overlay.text_green = options->image_overlay.text_green;
4223 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4224 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4225 c_options->image_overlay.background_red = options->image_overlay.background_red;
4226 c_options->image_overlay.background_green = options->image_overlay.background_green;
4227 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4228 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4232 *-----------------------------------------------------------------------------
4233 * timezone database routines
4234 *-----------------------------------------------------------------------------
4237 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4239 GError *error = nullptr;
4240 auto tz = static_cast<TZData *>(data);
4241 gchar *tmp_filename;
4242 gchar *timezone_bin;
4243 gchar *tmp_dir = nullptr;
4246 if (!g_cancellable_is_cancelled(tz->cancellable))
4248 generic_dialog_close(tz->gd);
4252 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4254 tmp_filename = g_file_get_path(tz->tmp_g_file);
4255 fd = file_data_new_simple(tmp_filename);
4256 tmp_dir = open_archive(fd);
4260 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4261 if (isfile(timezone_bin))
4263 move_file(timezone_bin, tz->timezone_database_user);
4267 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4270 g_free(timezone_bin);
4271 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4275 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4277 g_free(tmp_filename);
4278 file_data_unref(fd);
4282 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4285 g_file_delete(tz->tmp_g_file, nullptr, &error);
4286 g_object_unref(tz->tmp_g_file);
4287 tz->tmp_g_file = nullptr;
4288 g_object_unref(tz->cancellable);
4289 g_object_unref(tz->timezone_database_gq);
4292 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4294 auto tz = static_cast<TZData *>(data);
4296 if (!g_cancellable_is_cancelled(tz->cancellable))
4298 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4302 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4304 auto tz = static_cast<TZData *>(data);
4306 g_cancellable_cancel(tz->cancellable);
4309 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4311 auto tz = static_cast<TZData *>(data);
4312 GError *error = nullptr;
4313 GFileIOStream *io_stream;
4320 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4324 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4325 log_printf("Error: Download timezone database failed:\n%s", error->message);
4326 g_error_free(error);
4327 g_object_unref(tz->tmp_g_file);
4331 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4333 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4335 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4337 tz->progress = gtk_progress_bar_new();
4338 gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4339 gtk_widget_show(tz->progress);
4341 gtk_widget_show(tz->gd->dialog);
4342 tz->cancellable = g_cancellable_new();
4343 g_file_copy_async(tz->timezone_database_gq, tz->tmp_g_file, G_FILE_COPY_OVERWRITE, G_PRIORITY_LOW, tz->cancellable, timezone_progress_cb, tz, timezone_async_ready_cb, tz);
4345 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4348 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */