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"
27 #include "color-man.h"
30 #include "filefilter.h"
31 #include "fullscreen.h"
33 #include "image-overlay.h"
35 #include "layout-util.h"
39 #include "pixbuf-util.h"
41 #include "slideshow.h"
45 #include "ui-fileops.h"
47 #include "ui-tabcomp.h"
49 #include "zonedetect.h"
60 #include <gspell/gspell.h>
63 #define EDITOR_NAME_MAX_LENGTH 32
64 #define EDITOR_COMMAND_MAX_LENGTH 1024
66 static void image_overlay_set_text_colors();
68 GtkWidget *keyword_text;
69 static void config_tab_keywords_save();
77 static ThumbSize thumb_size_list[] =
112 FILETYPES_COLUMN_ENABLED = 0,
113 FILETYPES_COLUMN_FILTER,
114 FILETYPES_COLUMN_DESCRIPTION,
115 FILETYPES_COLUMN_CLASS,
116 FILETYPES_COLUMN_WRITABLE,
117 FILETYPES_COLUMN_SIDECAR,
118 FILETYPES_COLUMN_COUNT
121 const gchar *format_class_list[] = {
132 /* config memory values */
133 static ConfOptions *c_options = nullptr;
140 static GtkWidget *configwindow = nullptr;
141 static GtkListStore *filter_store = nullptr;
142 static GtkTreeStore *accel_store = nullptr;
144 static GtkWidget *safe_delete_path_entry;
146 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
147 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
148 static GtkWidget *color_profile_screen_file_entry;
149 static GtkWidget *external_preview_select_entry;
150 static GtkWidget *external_preview_extract_entry;
152 static GtkWidget *sidecar_ext_entry;
153 static GtkWidget *help_search_engine_entry;
156 #define CONFIG_WINDOW_DEF_WIDTH 700
157 #define CONFIG_WINDOW_DEF_HEIGHT 600
160 *-----------------------------------------------------------------------------
161 * option widget callbacks (private)
162 *-----------------------------------------------------------------------------
165 static void zoom_increment_cb(GtkWidget *spin, gpointer)
167 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
170 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
172 gint mins_secs_tenths, delay;
174 mins_secs_tenths = c_options->slideshow.delay %
175 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
177 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
178 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
181 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
182 SLIDESHOW_SUBSECOND_PRECISION;
185 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
187 gint hours, secs_tenths, delay;
189 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
190 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
192 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
193 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
194 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
196 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
197 SLIDESHOW_SUBSECOND_PRECISION;
200 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
202 gint hours_mins, delay;
204 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
206 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
207 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
208 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
210 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
211 SLIDESHOW_SUBSECOND_PRECISION;
215 *-----------------------------------------------------------------------------
216 * sync program to config window routine (private)
217 *-----------------------------------------------------------------------------
220 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
226 buf = gq_gtk_entry_get_text(GTK_ENTRY(entry));
227 if (buf && strlen(buf) > 0)
232 *option = g_strdup(buf);
237 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
239 gchar *accel_path, *accel;
241 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
243 if (accel_path && accel_path[0])
246 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
247 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
257 static void config_window_apply()
259 gboolean refresh = FALSE;
264 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
266 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
267 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
268 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
269 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
270 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
271 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
273 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
274 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
275 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
276 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
277 options->file_ops.no_trash = c_options->file_ops.no_trash;
278 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
279 options->tools_restore_state = c_options->tools_restore_state;
280 options->save_window_positions = c_options->save_window_positions;
281 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
282 options->save_window_workspace = c_options->save_window_workspace;
283 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
284 options->show_window_ids = c_options->show_window_ids;
285 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
286 options->image.zoom_2pass = c_options->image.zoom_2pass;
287 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
288 options->image.limit_window_size = c_options->image.limit_window_size;
289 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
290 options->image.max_window_size = c_options->image.max_window_size;
291 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
292 options->image.max_autofit_size = c_options->image.max_autofit_size;
293 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
294 options->image.tile_size = c_options->image.tile_size;
295 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
296 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
298 if (options->thumbnails.max_width != c_options->thumbnails.max_width
299 || options->thumbnails.max_height != c_options->thumbnails.max_height
300 || options->thumbnails.quality != c_options->thumbnails.quality)
302 thumb_format_changed = TRUE;
304 options->thumbnails.max_width = c_options->thumbnails.max_width;
305 options->thumbnails.max_height = c_options->thumbnails.max_height;
306 options->thumbnails.quality = c_options->thumbnails.quality;
308 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
309 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
310 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
311 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
312 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
313 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
314 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
315 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
316 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
317 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
318 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
319 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
321 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
322 options->file_filter.disable = c_options->file_filter.disable;
324 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
325 sidecar_ext_parse(options->sidecar.ext);
327 options->slideshow.random = c_options->slideshow.random;
328 options->slideshow.repeat = c_options->slideshow.repeat;
329 options->slideshow.delay = c_options->slideshow.delay;
331 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
332 options->image_lm_click_nav = c_options->image_lm_click_nav;
333 options->image_l_click_archive = c_options->image_l_click_archive;
334 options->image_l_click_video = c_options->image_l_click_video;
335 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
337 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
339 options->image.tile_cache_max = c_options->image.tile_cache_max;
340 options->image.image_cache_max = c_options->image.image_cache_max;
342 options->image.zoom_quality = c_options->image.zoom_quality;
344 options->image.zoom_increment = c_options->image.zoom_increment;
346 options->image.zoom_style = c_options->image.zoom_style;
348 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
350 options->appimage_notifications = c_options->appimage_notifications;
353 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
354 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
355 || !gdk_rgba_equal(&options->image.border_color, &c_options->image.border_color))
357 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
358 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
359 options->image.border_color = c_options->image.border_color;
360 layout_colors_update();
361 view_window_colors_update();
364 options->image.alpha_color_1 = c_options->image.alpha_color_1;
365 options->image.alpha_color_2 = c_options->image.alpha_color_2;
367 options->fullscreen.screen = c_options->fullscreen.screen;
368 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
369 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
370 options->fullscreen.above = c_options->fullscreen.above;
371 if (c_options->image_overlay.template_string)
372 set_image_overlay_template_string(&options->image_overlay.template_string,
373 c_options->image_overlay.template_string);
374 if (c_options->image_overlay.font)
375 set_image_overlay_font_string(&options->image_overlay.font,
376 c_options->image_overlay.font);
377 options->image_overlay.text_red = c_options->image_overlay.text_red;
378 options->image_overlay.text_green = c_options->image_overlay.text_green;
379 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
380 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
381 options->image_overlay.background_red = c_options->image_overlay.background_red;
382 options->image_overlay.background_green = c_options->image_overlay.background_green;
383 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
384 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
385 options->image_overlay.font = c_options->image_overlay.font;
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->selectable_bars.menu_bar = c_options->selectable_bars.menu_bar;
447 options->selectable_bars.tool_bar = c_options->selectable_bars.tool_bar;
448 options->selectable_bars.status_bar = c_options->selectable_bars.status_bar;
450 options->marks_save = c_options->marks_save;
451 options->with_rename = c_options->with_rename;
452 options->collections_duplicates = c_options->collections_duplicates;
453 options->collections_on_top = c_options->collections_on_top;
454 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
455 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
457 options->external_preview.enable = c_options->external_preview.enable;
458 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
459 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
461 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
463 options->star_rating.star = c_options->star_rating.star;
464 options->star_rating.rejected = c_options->star_rating.rejected;
466 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
468 options->alternate_similarity_algorithm.enabled = c_options->alternate_similarity_algorithm.enabled;
469 options->alternate_similarity_algorithm.grayscale = c_options->alternate_similarity_algorithm.grayscale;
472 set_debug_level(debug_c);
476 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
478 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
479 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
481 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
482 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
483 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
485 options->color_profile.render_intent = c_options->color_profile.render_intent;
490 options->mouse_button_8 = c_options->mouse_button_8;
491 options->mouse_button_9 = c_options->mouse_button_9;
493 options->override_disable_gpu = c_options->override_disable_gpu;
495 config_tab_keywords_save();
497 image_options_sync();
502 layout_refresh(nullptr);
505 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
507 toolbar_apply(TOOLBAR_MAIN);
508 toolbar_apply(TOOLBAR_STATUS);
512 *-----------------------------------------------------------------------------
513 * config window main button callbacks (private)
514 *-----------------------------------------------------------------------------
517 static void config_window_close_cb(GtkWidget *, gpointer)
519 gq_gtk_widget_destroy(configwindow);
520 configwindow = nullptr;
521 filter_store = nullptr;
524 static void config_window_help_cb(GtkWidget *, gpointer data)
526 auto notebook = static_cast<GtkWidget *>(data);
529 static const gchar *html_section[] =
531 "GuideOptionsGeneral.html",
532 "GuideOptionsImage.html",
533 "GuideOptionsOSD.html",
534 "GuideOptionsWindow.html",
535 "GuideOptionsKeyboard.html",
536 "GuideOptionsFiltering.html",
537 "GuideOptionsMetadata.html",
538 "GuideOptionsKeywords.html",
539 "GuideOptionsColor.html",
540 "GuideOptionsStereo.html",
541 "GuideOptionsBehavior.html",
542 "GuideOptionsToolbar.html",
543 "GuideOptionsToolbar.html",
544 "GuideOptionsAdvanced.html"
547 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
548 help_window_show(html_section[i]);
551 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
553 config_window_close_cb(nullptr, nullptr);
557 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
560 auto notebook = static_cast<GtkNotebook *>(data);
568 lw = static_cast<LayoutWindow *>(layout_window_list->data);
570 window = gtk_widget_get_window(widget);
571 gdk_window_get_root_origin(window, &x, &y);
572 w = gdk_window_get_width(window);
573 h = gdk_window_get_height(window);
574 page_number = gtk_notebook_get_current_page(notebook);
576 lw->options.preferences_window.x = x;
577 lw->options.preferences_window.y = y;
578 lw->options.preferences_window.w = w;
579 lw->options.preferences_window.h = h;
580 lw->options.preferences_window.page_number = page_number;
582 config_window_apply();
583 layout_util_sync(lw);
584 save_options(options);
585 config_window_close_cb(nullptr, nullptr);
589 *-----------------------------------------------------------------------------
590 * config window setup (private)
591 *-----------------------------------------------------------------------------
594 static void quality_menu_cb(GtkWidget *combo, gpointer data)
596 auto option = static_cast<gint *>(data);
598 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
602 *option = GDK_INTERP_NEAREST;
605 *option = GDK_INTERP_TILES;
608 *option = GDK_INTERP_BILINEAR;
613 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
615 auto option = static_cast<gint *>(data);
617 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
621 *option = DND_ACTION_ASK;
624 *option = DND_ACTION_COPY;
627 *option = DND_ACTION_MOVE;
631 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
633 auto option = static_cast<gint *>(data);
635 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
638 *option = CLIPBOARD_PRIMARY;
641 *option = CLIPBOARD_CLIPBOARD;
644 *option = CLIPBOARD_BOTH;
647 *option = CLIPBOARD_BOTH;
651 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
652 guint option, guint *option_c)
659 pref_table_label(table, column, row, text, GTK_ALIGN_START);
661 combo = gtk_combo_box_text_new();
663 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
664 if (option == GDK_INTERP_NEAREST) current = 0;
665 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
666 if (option == GDK_INTERP_TILES) current = 1;
667 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
668 if (option == GDK_INTERP_BILINEAR) current = 2;
670 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
672 g_signal_connect(G_OBJECT(combo), "changed",
673 G_CALLBACK(quality_menu_cb), option_c);
675 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
676 gtk_widget_show(combo);
679 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
686 pref_table_label(table, column, row, text, GTK_ALIGN_START);
688 combo = gtk_combo_box_text_new();
690 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
691 if (option == DND_ACTION_ASK) current = 0;
692 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
693 if (option == DND_ACTION_COPY) current = 1;
694 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
695 if (option == DND_ACTION_MOVE) current = 2;
697 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
699 g_signal_connect(G_OBJECT(combo), "changed",
700 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
702 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
703 gtk_widget_show(combo);
706 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
707 gint option, gint *option_c)
714 pref_table_label(table, column, row, text, GTK_ALIGN_START);
716 combo = gtk_combo_box_text_new();
718 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
719 if (option == CLIPBOARD_PRIMARY) current = 0;
720 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
721 if (option == CLIPBOARD_CLIPBOARD) current = 1;
722 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
723 if (option == CLIPBOARD_BOTH) current = 2;
725 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
727 g_signal_connect(G_OBJECT(combo), "changed",
728 G_CALLBACK(clipboard_selection_menu_cb), option_c);
730 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
731 gtk_widget_show(combo);
734 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
736 auto option = static_cast<gint *>(data);
738 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
741 *option = ZOOM_GEOMETRIC;
744 *option = ZOOM_ARITHMETIC;
747 *option = ZOOM_GEOMETRIC;
751 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
758 pref_table_label(table, column, row, text, GTK_ALIGN_START);
760 combo = gtk_combo_box_text_new();
762 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
763 if (option == ZOOM_GEOMETRIC) current = 0;
764 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
765 if (option == ZOOM_ARITHMETIC) current = 1;
767 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
769 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
771 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
772 gtk_widget_show(combo);
775 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
777 ActionItem *action_item = nullptr;
778 auto option = static_cast<gchar **>(data);
783 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
785 list = get_action_items();
790 action_item = static_cast<ActionItem *>(work->data);
791 if (g_strcmp0(action_item->label, label) == 0)
799 *option = g_strdup(action_item->name);
801 action_items_free(list);
804 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
806 ActionItem *action_item;
815 pref_table_label(table, column, row, text, GTK_ALIGN_START);
817 combo = gtk_combo_box_text_new();
819 list = get_action_items();
823 action_item = static_cast<ActionItem *>(work->data);
824 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
826 if (g_strcmp0(action_item->name, option) == 0)
834 action_items_free(list);
836 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
838 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
840 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
841 gtk_widget_show(combo);
844 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
848 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
851 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
853 c_options->thumbnails.max_width = thumb_size_list[n].w;
854 c_options->thumbnails.max_height = thumb_size_list[n].h;
858 c_options->thumbnails.max_width = options->thumbnails.max_width;
859 c_options->thumbnails.max_height = options->thumbnails.max_height;
863 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
869 c_options->thumbnails.max_width = options->thumbnails.max_width;
870 c_options->thumbnails.max_height = options->thumbnails.max_height;
872 pref_table_label(table, column, row, text, GTK_ALIGN_START);
874 combo = gtk_combo_box_text_new();
877 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
882 w = thumb_size_list[i].w;
883 h = thumb_size_list[i].h;
885 buf = g_strdup_printf("%d x %d", w, h);
886 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
889 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
896 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
897 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
903 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
904 g_signal_connect(G_OBJECT(combo), "changed",
905 G_CALLBACK(thumb_size_menu_cb), NULL);
907 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
908 gtk_widget_show(combo);
911 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
913 auto option = static_cast<gint *>(data);
915 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
919 *option = PR_STEREO_NONE;
922 *option = PR_STEREO_ANAGLYPH_RC;
925 *option = PR_STEREO_ANAGLYPH_GM;
928 *option = PR_STEREO_ANAGLYPH_YB;
931 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
934 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
937 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
940 *option = PR_STEREO_ANAGLYPH_DB_RC;
943 *option = PR_STEREO_ANAGLYPH_DB_GM;
946 *option = PR_STEREO_ANAGLYPH_DB_YB;
949 *option = PR_STEREO_HORIZ;
952 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
955 *option = PR_STEREO_VERT;
958 *option = PR_STEREO_VERT | PR_STEREO_HALF;
961 *option = PR_STEREO_FIXED;
966 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
967 gint option, gint *option_c, gboolean add_fixed)
974 pref_table_label(table, column, row, text, GTK_ALIGN_START);
976 combo = gtk_combo_box_text_new();
978 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
980 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
981 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
982 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
983 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
984 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
985 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
986 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
987 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
988 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
989 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
990 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
991 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
992 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
993 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
994 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
995 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
996 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
997 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
999 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1000 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1001 if (option & PR_STEREO_HORIZ)
1004 if (option & PR_STEREO_HALF) current = 11;
1007 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1008 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1009 if (option & PR_STEREO_VERT)
1012 if (option & PR_STEREO_HALF) current = 13;
1017 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1018 if (option & PR_STEREO_FIXED) current = 14;
1021 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1023 g_signal_connect(G_OBJECT(combo), "changed",
1024 G_CALLBACK(stereo_mode_menu_cb), option_c);
1026 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1027 gtk_widget_show(combo);
1030 static void video_menu_cb(GtkWidget *combo, gpointer data)
1032 auto option = static_cast<gchar **>(data);
1034 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1038 static void video_menu_populate(gpointer data, gpointer user_data)
1040 auto combo = static_cast<GtkWidget *>(user_data);
1041 auto ed = static_cast<EditorDescription *>(data);
1043 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1046 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1047 gchar *option, gchar **option_c)
1051 /* use lists since they are sorted */
1052 GList *eds = editor_list_get();
1056 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1058 combo = gtk_combo_box_text_new();
1059 g_list_foreach(eds,video_menu_populate,combo);
1060 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1062 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1064 g_signal_connect(G_OBJECT(combo), "changed",
1065 G_CALLBACK(video_menu_cb), option_c);
1067 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1068 gtk_widget_show(combo);
1071 static void filter_store_populate()
1075 if (!filter_store) return;
1077 gtk_list_store_clear(filter_store);
1079 work = filter_get_list();
1085 fe = static_cast<FilterEntry *>(work->data);
1088 gtk_list_store_append(filter_store, &iter);
1089 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1093 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1095 auto model = static_cast<GtkWidget *>(data);
1096 auto fe = static_cast<FilterEntry *>(data);
1100 if (!new_text || strlen(new_text) < 1) return;
1102 tpath = gtk_tree_path_new_from_string(path_str);
1103 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1104 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1106 g_free(fe->extensions);
1107 fe->extensions = g_strdup(new_text);
1109 gtk_tree_path_free(tpath);
1113 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1115 auto model = static_cast<GtkWidget *>(data);
1116 auto fe = static_cast<FilterEntry *>(data);
1121 if (!new_text || !new_text[0]) return;
1123 tpath = gtk_tree_path_new_from_string(path_str);
1124 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1125 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1127 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1129 if (strcmp(new_text, _(format_class_list[i])) == 0)
1131 fe->file_class = static_cast<FileFormatClass>(i);
1136 gtk_tree_path_free(tpath);
1140 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1142 auto model = static_cast<GtkWidget *>(data);
1147 if (!new_text || !new_text[0]) return;
1149 tpath = gtk_tree_path_new_from_string(path_str);
1150 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1151 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1153 g_free(fe->description);
1154 fe->description = g_strdup(new_text);
1156 gtk_tree_path_free(tpath);
1159 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1161 auto model = static_cast<GtkWidget *>(data);
1166 tpath = gtk_tree_path_new_from_string(path_str);
1167 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1168 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1170 fe->enabled = !fe->enabled;
1172 gtk_tree_path_free(tpath);
1176 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1178 auto model = static_cast<GtkWidget *>(data);
1183 tpath = gtk_tree_path_new_from_string(path_str);
1184 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1185 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1187 fe->writable = !fe->writable;
1188 if (fe->writable) fe->allow_sidecar = FALSE;
1190 gtk_tree_path_free(tpath);
1194 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1196 auto model = static_cast<GtkWidget *>(data);
1201 tpath = gtk_tree_path_new_from_string(path_str);
1202 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1203 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1205 fe->allow_sidecar = !fe->allow_sidecar;
1206 if (fe->allow_sidecar) fe->writable = FALSE;
1208 gtk_tree_path_free(tpath);
1212 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1213 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1217 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1219 switch (GPOINTER_TO_INT(data))
1222 g_object_set(GTK_CELL_RENDERER(cell),
1223 "active", fe->enabled, NULL);
1226 g_object_set(GTK_CELL_RENDERER(cell),
1227 "text", fe->extensions, NULL);
1229 case FE_DESCRIPTION:
1230 g_object_set(GTK_CELL_RENDERER(cell),
1231 "text", fe->description, NULL);
1234 g_object_set(GTK_CELL_RENDERER(cell),
1235 "text", _(format_class_list[fe->file_class]), NULL);
1238 g_object_set(GTK_CELL_RENDERER(cell),
1239 "active", fe->writable, NULL);
1241 case FE_ALLOW_SIDECAR:
1242 g_object_set(GTK_CELL_RENDERER(cell),
1243 "active", fe->allow_sidecar, NULL);
1248 static gboolean filter_add_scroll(gpointer data)
1252 GtkCellRenderer *cell;
1253 GtkTreeViewColumn *column;
1256 GtkTreeModel *store;
1258 FilterEntry *filter;
1260 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1261 path = gtk_tree_path_new_from_indices(rows-1, -1);
1263 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1265 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1266 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1268 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1269 valid = gtk_tree_model_get_iter_first(store, &iter);
1273 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1275 if (g_strcmp0(filter->extensions, ".new") == 0)
1277 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1281 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1284 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1285 path, column, FALSE, 0.0, 0.0 );
1286 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1287 path, column, cell, TRUE);
1289 gtk_tree_path_free(path);
1290 g_list_free(list_cells);
1292 return(G_SOURCE_REMOVE);
1295 static void filter_add_cb(GtkWidget *, gpointer data)
1297 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1298 filter_store_populate();
1300 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1303 static void filter_remove_cb(GtkWidget *, gpointer data)
1305 auto filter_view = static_cast<GtkWidget *>(data);
1306 GtkTreeSelection *selection;
1310 if (!filter_store) return;
1311 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1312 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1313 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1316 filter_remove_entry(fe);
1318 filter_store_populate();
1321 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1325 GtkTreeViewColumn *column;
1327 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1328 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1329 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1331 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1335 gtk_tree_path_free(path);
1337 return(G_SOURCE_REMOVE);
1340 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1343 filter_add_defaults();
1345 filter_store_populate();
1347 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1350 static void dummy_cancel_cb(GenericDialog *, gpointer)
1352 /* no op, only so cancel button appears */
1355 static void filter_default_cb(GtkWidget *widget, gpointer data)
1359 gd = generic_dialog_new(_("Reset filters"),
1360 "reset_filter", widget, TRUE,
1361 dummy_cancel_cb, data);
1362 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1363 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1364 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1365 gtk_widget_show(gd->dialog);
1368 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1370 auto frame = static_cast<GtkWidget *>(data);
1372 gtk_widget_set_sensitive(frame,
1373 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1376 static void safe_delete_view_cb(GtkWidget *, gpointer)
1378 layout_set_path(nullptr, gq_gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1381 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1383 file_util_trash_clear();
1386 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1390 gd = generic_dialog_new(_("Clear trash"),
1391 "clear_trash", widget, TRUE,
1392 dummy_cancel_cb, nullptr);
1393 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1394 _("This will remove the trash contents."), FALSE);
1395 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1396 entry = gtk_entry_new();
1397 gtk_widget_set_can_focus(entry, FALSE);
1398 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1399 if (options->file_ops.safe_delete_path) gq_gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1400 gq_gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1401 gtk_widget_show(entry);
1402 gtk_widget_show(gd->dialog);
1405 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1407 GtkWidget *pTextView;
1408 GtkTextBuffer *pTextBuffer;
1412 pTextView = GTK_WIDGET(data);
1414 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1415 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1416 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1418 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1419 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1422 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1424 auto text_view = static_cast<GtkTextView *>(data);
1425 GtkTextBuffer *buffer;
1427 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1428 if (!configwindow) return;
1430 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1431 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1434 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1438 gd = generic_dialog_new(_("Reset image overlay template string"),
1439 "reset_image_overlay_template_string", widget, TRUE,
1440 dummy_cancel_cb, data);
1441 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1442 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1443 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1444 gtk_widget_show(gd->dialog);
1447 static void image_overlay_help_cb(GtkWidget *, gpointer)
1449 help_window_show("GuideOptionsOSD.html");
1452 static void font_activated_cb(GtkFontChooser *widget, gchar *fontname, gpointer)
1454 g_free(c_options->image_overlay.font);
1455 c_options->image_overlay.font = g_strdup(fontname);
1458 gq_gtk_widget_destroy(GTK_WIDGET(widget));
1461 static void font_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1465 g_free(c_options->image_overlay.font);
1466 c_options->image_overlay.font = g_strdup(options->image_overlay.font);
1468 if (response_id == GTK_RESPONSE_OK)
1470 font = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
1471 g_free(c_options->image_overlay.font);
1472 c_options->image_overlay.font = g_strdup(font);
1476 g_object_unref(dialog);
1479 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1483 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1484 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1485 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1487 g_signal_connect(dialog, "font-activated", G_CALLBACK(font_activated_cb), nullptr);
1488 g_signal_connect(dialog, "response", G_CALLBACK(font_response_cb), nullptr);
1490 gtk_widget_show(dialog);
1493 static void text_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1495 c_options->image_overlay.text_red = color->red * 255;
1496 c_options->image_overlay.text_green = color->green * 255;
1497 c_options->image_overlay.text_blue = color->blue * 255;
1498 c_options->image_overlay.text_alpha = color->alpha * 255;
1500 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1503 static void text_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1507 c_options->image_overlay.text_red = options->image_overlay.text_red;
1508 c_options->image_overlay.text_green = options->image_overlay.text_green;
1509 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
1510 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
1512 if (response_id == GTK_RESPONSE_OK)
1514 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1515 c_options->image_overlay.text_red = color.red * 255;
1516 c_options->image_overlay.text_green = color.green * 255;
1517 c_options->image_overlay.text_blue = color.blue * 255;
1518 c_options->image_overlay.text_alpha = color.alpha * 255;
1521 g_object_unref(dialog);
1524 static void image_overlay_set_text_color_cb(GtkWidget *widget, gpointer)
1529 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Text Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1530 color.red = options->image_overlay.text_red;
1531 color.green = options->image_overlay.text_green;
1532 color.blue = options->image_overlay.text_blue;
1533 color.alpha = options->image_overlay.text_alpha;
1534 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1535 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1537 g_signal_connect(dialog, "color-activated", G_CALLBACK(text_color_activated_cb), nullptr);
1538 g_signal_connect(dialog, "response", G_CALLBACK(text_color_response_cb), nullptr);
1540 gtk_widget_show(dialog);
1543 static void bg_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1545 c_options->image_overlay.background_red = color->red * 255;
1546 c_options->image_overlay.background_green = color->green * 255;
1547 c_options->image_overlay.background_blue = color->blue * 255;
1548 c_options->image_overlay.background_alpha = color->alpha * 255;
1550 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1553 static void bg_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1557 c_options->image_overlay.background_red = options->image_overlay.background_red;
1558 c_options->image_overlay.background_green = options->image_overlay.background_green;
1559 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
1560 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
1562 if (response_id == GTK_RESPONSE_OK)
1564 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1565 c_options->image_overlay.background_red = color.red * 255;
1566 c_options->image_overlay.background_green = color.green * 255;
1567 c_options->image_overlay.background_blue = color.blue * 255;
1568 c_options->image_overlay.background_alpha = color.alpha * 255;
1570 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1573 static void image_overlay_set_background_color_cb(GtkWidget *widget, gpointer)
1578 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Background Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1579 color.red = options->image_overlay.background_red;
1580 color.green = options->image_overlay.background_green;
1581 color.blue = options->image_overlay.background_blue;
1582 color.alpha = options->image_overlay.background_alpha;
1583 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1584 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1586 g_signal_connect(dialog, "color-activated", G_CALLBACK(bg_color_activated_cb), nullptr);
1587 g_signal_connect(dialog, "response", G_CALLBACK(bg_color_response_cb), nullptr);
1589 gtk_widget_show(dialog);
1592 static void accel_store_populate()
1595 GList *groups, *actions;
1597 const gchar *accel_path;
1601 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1603 gtk_tree_store_clear(accel_store);
1604 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 */
1606 g_assert(lw && lw->ui_manager);
1607 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1610 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1613 action = GTK_ACTION(actions->data);
1614 accel_path = gtk_action_get_accel_path(action);
1615 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1617 gchar *label, *label2, *tooltip, *accel;
1618 g_object_get(action,
1619 "tooltip", &tooltip,
1623 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1629 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1633 gtk_tree_store_append(accel_store, &iter, nullptr);
1634 gtk_tree_store_set(accel_store, &iter,
1637 AE_TOOLTIP, tooltip ? tooltip : "",
1638 AE_ACCEL, accel_path,
1646 actions = actions->next;
1649 groups = groups->next;
1653 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1658 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1660 auto accel1 = static_cast<gchar *>(data);
1665 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1667 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1668 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1670 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1672 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1673 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1682 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1684 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1688 GtkAccelKey old_key, key;
1689 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1691 gtk_tree_model_get_iter(model, &iter, path);
1692 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1694 /* test if the accelerator can be stored without conflicts*/
1695 gtk_accel_map_lookup_entry(accel_path, &old_key);
1697 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1698 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1699 gtk_accel_map_lookup_entry(accel_path, &key);
1701 /* restore the original for now, the key will be really changed when the changes are confirmed */
1702 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1704 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1705 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1707 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1708 gtk_tree_path_free(path);
1712 static gboolean accel_default_scroll(GtkTreeView *data)
1716 GtkTreeViewColumn *column;
1718 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1719 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1720 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1722 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1726 gtk_tree_path_free(path);
1728 return(G_SOURCE_REMOVE);
1731 static void accel_default_cb(GtkWidget *, gpointer data)
1733 accel_store_populate();
1735 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1738 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1740 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1743 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1746 gchar *accel_path, *accel;
1748 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1749 gtk_accel_map_lookup_entry(accel_path, &key);
1750 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1752 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1754 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1759 static void accel_clear_cb(GtkWidget *, gpointer data)
1761 GtkTreeSelection *selection;
1763 if (!accel_store) return;
1764 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1765 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1768 static void accel_reset_cb(GtkWidget *, gpointer data)
1770 GtkTreeSelection *selection;
1772 if (!accel_store) return;
1773 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1774 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1779 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1783 GtkWidget *scrolled;
1784 GtkWidget *viewport;
1786 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
1787 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1788 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1789 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1790 label = gtk_label_new(title);
1791 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1792 gtk_widget_show(scrolled);
1794 viewport = gtk_viewport_new(nullptr, nullptr);
1795 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1796 gq_gtk_container_add(GTK_WIDGET(scrolled), viewport);
1797 gtk_widget_show(viewport);
1799 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1800 gq_gtk_container_add(GTK_WIDGET(viewport), vbox);
1801 gtk_widget_show(vbox);
1806 static void cache_standard_cb(GtkWidget *widget, gpointer)
1808 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1810 c_options->thumbnails.spec_standard =TRUE;
1811 c_options->thumbnails.cache_into_dirs = FALSE;
1815 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1817 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1819 c_options->thumbnails.spec_standard =FALSE;
1820 c_options->thumbnails.cache_into_dirs = FALSE;
1824 static void cache_local_cb(GtkWidget *widget, gpointer)
1826 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1828 c_options->thumbnails.cache_into_dirs = TRUE;
1829 c_options->thumbnails.spec_standard =FALSE;
1833 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1835 if (pos == GTK_ENTRY_ICON_PRIMARY)
1837 gq_gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1841 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "");
1845 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1847 gchar *rating_symbol;
1849 if (pos == GTK_ENTRY_ICON_PRIMARY)
1851 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1852 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1853 g_free(rating_symbol);
1857 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1858 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1859 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1863 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1865 gchar *rating_symbol;
1867 if (pos == GTK_ENTRY_ICON_PRIMARY)
1869 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1870 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1871 g_free(rating_symbol);
1875 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1876 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1877 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1881 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1883 auto hbox = static_cast<GtkContainer *>(data);
1884 GString *str = g_string_new(nullptr);
1885 GtkEntry *hex_code_entry;
1886 gchar *hex_code_full;
1889 guint64 hex_value = 0;
1891 list = gtk_container_get_children(hbox);
1893 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1894 hex_code_full = g_strdup(gq_gtk_entry_get_text(hex_code_entry));
1896 hex_code = g_strsplit(hex_code_full, "+", 2);
1897 if (hex_code[0] && hex_code[1])
1899 hex_value = strtoull(hex_code[1], nullptr, 16);
1901 if (!hex_value || hex_value > 0x10FFFF)
1903 hex_value = 0x003F; // Unicode 'Question Mark'
1905 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1906 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1908 g_strfreev(hex_code);
1909 g_string_free(str, TRUE);
1910 g_free(hex_code_full);
1915 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1917 guint64 star_symbol;
1919 star_symbol = star_rating_symbol_test(widget, data);
1920 c_options->star_rating.star = star_symbol;
1923 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1925 guint64 rejected_symbol;
1927 rejected_symbol = star_rating_symbol_test(widget, data);
1928 c_options->star_rating.rejected = rejected_symbol;
1931 /* general options tab */
1932 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1936 GCancellable *cancellable;
1938 GtkWidget *progress;
1940 GFile *timezone_database_gq;
1941 gchar *timezone_database_user;
1944 static void config_tab_general(GtkWidget *notebook)
1949 GtkWidget *group_frame;
1950 GtkWidget *subgroup;
1952 GtkWidget *ct_button;
1955 gint hours, minutes, remainder;
1957 GtkWidget *star_rating_entry;
1959 gchar *rating_symbol;
1962 gchar *download_locn;
1963 GNetworkMonitor *net_mon;
1964 GSocketConnectable *tz_org;
1965 gboolean internet_available = FALSE;
1968 vbox = scrolled_notebook_page(notebook, _("General"));
1970 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1972 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1973 add_thumb_size_menu(table, 0, 0, _("Size:"));
1974 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1976 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1977 pref_label_new(hbox, _("Custom size: "));
1978 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1979 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1981 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1982 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1984 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1985 pref_checkbox_link_sensitivity(ct_button, subgroup);
1987 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1988 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1989 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1990 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1991 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
1992 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1993 G_CALLBACK(cache_geeqie_cb), nullptr);
1995 group_frame = pref_frame_new(subgroup, TRUE,
1996 _("Store thumbnails local to image folder (non-standard)"),
1997 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1998 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
1999 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
2000 G_CALLBACK(cache_local_cb), nullptr);
2002 group_frame = pref_frame_new(subgroup, TRUE,
2003 _("Use standard thumbnail style and cache, shared with other applications"),
2004 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2005 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2006 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2007 G_CALLBACK(cache_standard_cb), nullptr);
2009 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2010 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2012 pref_checkbox_new_int(group, _("Thumbnail color management"),
2013 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2015 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2017 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2018 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2020 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
2021 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2022 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2025 pref_spacer(group, PREF_PAD_GROUP);
2027 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2029 c_options->star_rating.star = options->star_rating.star;
2030 c_options->star_rating.rejected = options->star_rating.rejected;
2032 str = g_string_new(nullptr);
2033 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2034 pref_label_new(hbox, _("Star character: "));
2035 str = g_string_append_unichar(str, options->star_rating.star);
2036 pref_label_new(hbox, g_strdup(str->str));
2037 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2038 star_rating_entry = gtk_entry_new();
2039 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2040 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2041 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2042 gtk_widget_show(star_rating_entry);
2043 button = pref_button_new(nullptr, nullptr, _("Set"),
2044 G_CALLBACK(star_rating_star_test_cb), hbox);
2045 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2046 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2047 gtk_widget_show(button);
2048 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."));
2049 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2050 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2051 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2052 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2053 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2054 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2055 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2056 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2057 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2058 G_CALLBACK(star_rating_star_icon_cb),
2061 g_string_free(str, TRUE);
2062 g_free(rating_symbol);
2064 str = g_string_new(nullptr);
2065 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2066 pref_label_new(hbox, _("Rejected character: "));
2067 str = g_string_append_unichar(str, options->star_rating.rejected);
2068 pref_label_new(hbox, g_strdup(str->str));
2069 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2070 star_rating_entry = gtk_entry_new();
2071 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2072 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2073 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2074 gtk_widget_show(star_rating_entry);
2075 button = pref_button_new(nullptr, nullptr, _("Set"),
2076 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2077 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2078 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2079 gtk_widget_show(button);
2080 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."));
2081 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2082 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2083 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2084 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2085 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2086 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2087 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2088 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2089 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2090 G_CALLBACK(star_rating_rejected_icon_cb),
2093 g_string_free(str, TRUE);
2094 g_free(rating_symbol);
2096 pref_spacer(group, PREF_PAD_GROUP);
2098 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2100 c_options->slideshow.delay = options->slideshow.delay;
2101 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2102 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2103 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2104 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2105 SLIDESHOW_SUBSECOND_PRECISION;
2107 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2109 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2111 options->slideshow.delay ? hours : 0.0,
2112 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2113 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2114 spin = pref_spin_new(hbox, ":" , nullptr,
2116 options->slideshow.delay ? minutes: 0.0,
2117 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2118 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2119 spin = pref_spin_new(hbox, ":", nullptr,
2120 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2121 options->slideshow.delay ? seconds : 10.0,
2122 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2123 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2125 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2126 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2128 pref_spacer(group, PREF_PAD_GROUP);
2130 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2132 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2133 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2134 pref_checkbox_new_int(group, _("Preload next image"),
2135 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2137 pref_checkbox_new_int(group, _("Refresh on file change"),
2138 options->update_on_time_change, &c_options->update_on_time_change);
2141 pref_spacer(group, PREF_PAD_GROUP);
2143 group = pref_group_new(vbox, FALSE, _("Expand menu and toolbar"), GTK_ORIENTATION_VERTICAL);
2145 pref_checkbox_new_int(group, _("Expand menu and toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2146 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2147 gtk_widget_set_tooltip_text(group, _("Expand the menu and toolbar to the full width of the window"));
2149 pref_spacer(group, PREF_PAD_GROUP);
2151 group = pref_group_new(vbox, FALSE, _("Hide Selectable Bars"), GTK_ORIENTATION_VERTICAL);
2153 pref_checkbox_new_int(group, _("Menu bar"),
2154 options->selectable_bars.menu_bar, &c_options->selectable_bars.menu_bar);
2156 pref_checkbox_new_int(group, _("Tool bar"),
2157 options->selectable_bars.tool_bar, &c_options->selectable_bars.tool_bar);
2159 pref_checkbox_new_int(group, _("Status bar"),
2160 options->selectable_bars.status_bar, &c_options->selectable_bars.status_bar);
2161 gtk_widget_set_tooltip_text(group, _("The Hide Selectable Bars menu item (default keystroke is control-backtick) will toggle the display of the bars selected here"));
2163 pref_spacer(group, PREF_PAD_GROUP);
2165 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2167 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2168 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2169 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2170 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"));
2172 pref_spacer(group, PREF_PAD_GROUP);
2176 net_mon = g_network_monitor_get_default();
2177 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2180 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2181 g_object_unref(tz_org);
2184 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2185 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2187 if (!internet_available)
2189 gtk_widget_set_sensitive(group, FALSE);
2192 tz = g_new0(TZData, 1);
2194 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2195 basename = g_path_get_basename(path);
2196 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2200 if (isfile(tz->timezone_database_user))
2202 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2206 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2209 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2210 pref_label_new(GTK_WIDGET(hbox), download_locn);
2211 g_free(download_locn);
2213 if (!internet_available)
2215 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"));
2219 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"));
2221 gtk_widget_show(button);
2223 pref_spacer(group, PREF_PAD_GROUP);
2225 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2227 help_search_engine_entry = gtk_entry_new();
2228 gq_gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2229 gq_gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2230 gtk_widget_show(help_search_engine_entry);
2232 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"));
2234 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2235 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2236 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2237 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2238 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2239 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2240 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2241 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2242 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2243 G_CALLBACK(help_search_engine_entry_icon_cb),
2244 help_search_engine_entry);
2248 static void config_tab_image(GtkWidget *notebook)
2253 GtkWidget *ct_button;
2254 GtkWidget *enlargement_button;
2258 vbox = scrolled_notebook_page(notebook, _("Image"));
2260 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2262 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2263 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2265 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2266 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2268 c_options->image.zoom_increment = options->image.zoom_increment;
2269 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2270 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2271 G_CALLBACK(zoom_increment_cb), nullptr);
2272 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2274 c_options->image.zoom_style = options->image.zoom_style;
2275 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2276 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2278 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2280 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2281 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2282 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2283 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2285 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2286 pref_checkbox_link_sensitivity(enlargement_button, spin);
2287 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."));
2289 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2290 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2291 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2292 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2294 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2295 pref_checkbox_link_sensitivity(ct_button, spin);
2296 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."));
2298 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2300 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2301 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2303 options->image.tile_size, &c_options->image.tile_size);
2304 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."));
2306 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2308 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2309 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2311 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2312 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2314 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2315 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2317 c_options->image.border_color = options->image.border_color;
2319 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2320 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2322 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2323 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2325 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2326 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2331 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2333 LayoutWindow *lw = nullptr;
2334 gchar *default_path;
2337 /* Get current lw */
2340 tmp_id = lw->options.id;
2341 lw->options.id = g_strdup("");
2343 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2344 save_default_layout_options_to_file(default_path, options, lw);
2345 g_free(lw->options.id);
2346 lw->options.id = tmp_id;
2347 g_free(default_path);
2350 static gboolean popover_cb(gpointer data)
2352 auto popover = static_cast<GtkPopover *>(data);
2354 gtk_popover_popdown(popover);
2359 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2361 gtk_popover_popup(popover);
2363 g_timeout_add(2000, popover_cb, popover);
2366 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2370 popover = gtk_popover_new(parent);
2371 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2372 gq_gtk_container_add(GTK_WIDGET(popover), child);
2373 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2374 gtk_widget_show (child);
2379 static void config_tab_windows(GtkWidget *notebook)
2384 GtkWidget *subgroup;
2386 GtkWidget *ct_button;
2389 vbox = scrolled_notebook_page(notebook, _("Windows"));
2391 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2393 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2394 options->save_window_positions, &c_options->save_window_positions);
2396 button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2397 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2398 pref_checkbox_link_sensitivity(ct_button, button);
2400 button = pref_checkbox_new_int(group, _("Remember window workspace"),
2401 options->save_window_workspace, &c_options->save_window_workspace);
2402 pref_checkbox_link_sensitivity(ct_button, button);
2404 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2405 options->tools_restore_state, &c_options->tools_restore_state);
2407 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2408 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2410 pref_checkbox_new_int(group, _("Show window IDs"),
2411 options->show_window_ids, &c_options->show_window_ids);
2413 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2414 pref_label_new(subgroup, _("Use current layout for default: "));
2415 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2419 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2420 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2421 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2423 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2425 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2426 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2428 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2429 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2430 options->image.limit_window_size, &c_options->image.limit_window_size);
2431 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2433 options->image.max_window_size, &c_options->image.max_window_size);
2434 pref_checkbox_link_sensitivity(ct_button, spin);
2436 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2438 c_options->fullscreen.screen = options->fullscreen.screen;
2439 c_options->fullscreen.above = options->fullscreen.above;
2440 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2441 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2442 gtk_widget_show(hbox);
2444 pref_checkbox_new_int(group, _("Smooth image flip"),
2445 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2446 pref_checkbox_new_int(group, _("Disable screen saver"),
2447 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2450 #define PRE_FORMATTED_COLUMNS 5
2451 static void config_tab_osd(GtkWidget *notebook)
2457 GtkWidget *image_overlay_template_view;
2458 GtkWidget *scrolled;
2459 GtkWidget *scrolled_pre_formatted;
2460 GtkTextBuffer *buffer;
2462 GtkWidget *subgroup;
2464 vbox = scrolled_notebook_page(notebook, _("OSD"));
2466 image_overlay_template_view = gtk_text_view_new();
2468 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2470 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2472 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2473 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2474 gq_gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2475 gtk_widget_show(scrolled_pre_formatted);
2476 gtk_widget_show(subgroup);
2478 pref_line(group, PREF_PAD_GAP);
2480 pref_label_new(group, _("Image overlay template"));
2482 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2483 gtk_widget_set_size_request(scrolled, 200, 150);
2484 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2485 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2486 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2487 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2488 gtk_widget_show(scrolled);
2490 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2491 _("Extensive formatting options are shown in the Help file"));
2493 gq_gtk_container_add(GTK_WIDGET(scrolled), image_overlay_template_view);
2494 gtk_widget_show(image_overlay_template_view);
2496 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2498 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2499 G_CALLBACK(image_overlay_set_font_cb), notebook);
2501 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2502 gtk_widget_show(button);
2504 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"), G_CALLBACK(image_overlay_set_text_color_cb), nullptr);
2505 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2506 gtk_widget_show(button);
2508 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"), G_CALLBACK(image_overlay_set_background_color_cb), nullptr);
2509 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2510 gtk_widget_show(button);
2511 image_overlay_set_text_colors();
2513 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2514 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2515 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2516 gtk_widget_show(button);
2518 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2519 G_CALLBACK(image_overlay_help_cb), nullptr);
2520 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2521 gtk_widget_show(button);
2523 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2524 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2525 g_signal_connect(G_OBJECT(buffer), "changed",
2526 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2528 pref_line(group, PREF_PAD_GAP);
2530 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2531 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2532 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2533 gtk_widget_show(hbox);
2534 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2535 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2536 gtk_widget_show(label);
2537 pref_spacer(group,TRUE);
2539 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2540 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2541 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2542 gtk_widget_show(hbox);
2543 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2544 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2545 gtk_widget_show(label);
2546 pref_spacer(group,TRUE);
2548 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2549 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2550 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2551 gtk_widget_show(hbox);
2552 label = gtk_label_new(_("%path:39%"));
2553 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2554 gtk_widget_show(label);
2555 pref_spacer(group,TRUE);
2557 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2558 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2559 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2560 gtk_widget_show(hbox);
2561 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%"));
2562 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2563 gtk_widget_show(label);
2564 pref_spacer(group,TRUE);
2566 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2567 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2568 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2569 gtk_widget_show(hbox);
2570 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2571 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2572 gtk_widget_show(label);
2575 static GtkTreeModel *create_class_model()
2577 GtkListStore *model;
2581 /* create list store */
2582 model = gtk_list_store_new(1, G_TYPE_STRING);
2583 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2585 gtk_list_store_append(model, &iter);
2586 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2588 return GTK_TREE_MODEL (model);
2593 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2595 gint n = GPOINTER_TO_INT(data);
2597 FilterEntry *filter_a;
2598 FilterEntry *filter_b;
2600 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2601 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2605 case FILETYPES_COLUMN_ENABLED:
2607 ret = filter_a->enabled - filter_b->enabled;
2610 case FILETYPES_COLUMN_FILTER:
2612 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2615 case FILETYPES_COLUMN_DESCRIPTION:
2617 ret = g_utf8_collate(filter_a->description, filter_b->description);
2620 case FILETYPES_COLUMN_CLASS:
2622 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2625 case FILETYPES_COLUMN_WRITABLE:
2627 ret = filter_a->writable - filter_b->writable;
2630 case FILETYPES_COLUMN_SIDECAR:
2632 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2636 g_return_val_if_reached(0);
2642 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2645 gboolean ret = TRUE;
2647 gtk_tree_model_get(model, iter, 0, &fe, -1);
2649 if (g_strstr_len(fe->extensions, -1, key))
2657 static void config_tab_files(GtkWidget *notebook)
2664 GtkWidget *ct_button;
2665 GtkWidget *scrolled;
2666 GtkWidget *filter_view;
2667 GtkCellRenderer *renderer;
2668 GtkTreeSelection *selection;
2669 GtkTreeViewColumn *column;
2671 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2673 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2675 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2676 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2677 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2678 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2679 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);
2680 pref_checkbox_new_int(group, _("Disable file extension checks"),
2681 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2683 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2684 options->file_filter.disable, &c_options->file_filter.disable);
2687 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2689 sidecar_ext_entry = gtk_entry_new();
2690 gq_gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2691 gq_gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2692 gtk_widget_show(sidecar_ext_entry);
2694 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2696 frame = pref_group_parent(group);
2697 g_signal_connect(G_OBJECT(ct_button), "toggled",
2698 G_CALLBACK(filter_disable_cb), frame);
2699 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2701 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2702 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2703 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2704 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2705 gtk_widget_show(scrolled);
2707 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2708 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2709 g_object_unref(filter_store);
2710 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2711 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2713 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2715 column = gtk_tree_view_column_new();
2716 gtk_tree_view_column_set_title(column, _("Enabled"));
2717 gtk_tree_view_column_set_resizable(column, TRUE);
2719 renderer = gtk_cell_renderer_toggle_new();
2720 g_signal_connect(G_OBJECT(renderer), "toggled",
2721 G_CALLBACK(filter_store_enable_cb), filter_store);
2722 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2723 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2724 GINT_TO_POINTER(FE_ENABLE), nullptr);
2725 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);
2726 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2727 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2729 column = gtk_tree_view_column_new();
2730 gtk_tree_view_column_set_title(column, _("Filter"));
2731 gtk_tree_view_column_set_resizable(column, TRUE);
2732 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);
2733 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2735 renderer = gtk_cell_renderer_text_new();
2736 g_signal_connect(G_OBJECT(renderer), "edited",
2737 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2738 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2739 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2740 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2741 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2742 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2744 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2745 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2746 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2748 column = gtk_tree_view_column_new();
2749 gtk_tree_view_column_set_title(column, _("Description"));
2750 gtk_tree_view_column_set_resizable(column, TRUE);
2751 gtk_tree_view_column_set_fixed_width(column, 200);
2752 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2754 renderer = gtk_cell_renderer_text_new();
2755 g_signal_connect(G_OBJECT(renderer), "edited",
2756 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2757 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2758 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2759 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2760 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2761 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2762 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);
2763 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2765 column = gtk_tree_view_column_new();
2766 gtk_tree_view_column_set_title(column, _("Class"));
2767 gtk_tree_view_column_set_resizable(column, TRUE);
2768 renderer = gtk_cell_renderer_combo_new();
2769 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2770 "model", create_class_model(),
2775 g_signal_connect(G_OBJECT(renderer), "edited",
2776 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2777 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2778 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2779 GINT_TO_POINTER(FE_CLASS), nullptr);
2780 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2781 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);
2782 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2784 column = gtk_tree_view_column_new();
2785 gtk_tree_view_column_set_title(column, _("Writable"));
2786 gtk_tree_view_column_set_resizable(column, FALSE);
2787 renderer = gtk_cell_renderer_toggle_new();
2788 g_signal_connect(G_OBJECT(renderer), "toggled",
2789 G_CALLBACK(filter_store_writable_cb), filter_store);
2790 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2791 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2792 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2793 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2794 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);
2795 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2797 column = gtk_tree_view_column_new();
2798 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2799 gtk_tree_view_column_set_resizable(column, FALSE);
2800 renderer = gtk_cell_renderer_toggle_new();
2801 g_signal_connect(G_OBJECT(renderer), "toggled",
2802 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2803 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2804 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2805 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2806 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2807 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);
2808 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2810 filter_store_populate();
2811 gq_gtk_container_add(GTK_WIDGET(scrolled), filter_view);
2812 gtk_widget_show(filter_view);
2814 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2816 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2817 G_CALLBACK(filter_default_cb), filter_view);
2818 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2819 gtk_widget_show(button);
2821 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2822 G_CALLBACK(filter_remove_cb), filter_view);
2823 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2824 gtk_widget_show(button);
2826 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2827 G_CALLBACK(filter_add_cb), filter_view);
2828 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2829 gtk_widget_show(button);
2833 static void config_tab_metadata(GtkWidget *notebook)
2838 GtkWidget *ct_button;
2840 GtkWidget *tmp_widget;
2842 GtkWidget *text_label;
2844 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2847 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2849 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2851 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."));
2852 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2853 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2855 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2857 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2858 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2859 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"));
2860 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2862 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)"));
2863 gtk_widget_set_tooltip_markup(ct_button, markup);
2867 gtk_widget_set_sensitive(ct_button, FALSE);
2870 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2871 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2872 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)"));
2873 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2876 label = pref_label_new(group, "");
2877 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(), "/");
2878 gtk_label_set_markup (GTK_LABEL(label), markup);
2881 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2882 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2883 gtk_widget_set_margin_start(label, 22);
2884 pref_spacer(group, PREF_PAD_GROUP);
2886 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2888 gtk_widget_set_sensitive(group, FALSE);
2891 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2892 pref_checkbox_link_sensitivity(ct_button, hbox);
2894 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);
2895 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2897 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);
2899 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2901 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2902 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2903 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2904 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", ")");
2905 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2908 pref_spacer(group, PREF_PAD_GROUP);
2910 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2912 gtk_widget_set_sensitive(group, FALSE);
2915 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);
2917 pref_spacer(group, PREF_PAD_GROUP);
2919 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2920 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);
2921 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2923 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2924 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2926 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2927 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"));
2930 gtk_widget_set_sensitive(ct_button, FALSE);
2933 pref_spacer(group, PREF_PAD_GROUP);
2935 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2937 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2939 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2940 pref_checkbox_link_sensitivity(ct_button, hbox);
2942 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2944 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2946 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2948 pref_spacer(group, PREF_PAD_GROUP);
2951 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2953 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2954 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2957 pref_spacer(group, PREF_PAD_GROUP);
2959 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2961 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2962 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");
2967 struct KeywordFindData
2974 GtkWidget *button_close;
2975 GtkWidget *button_stop;
2976 GtkWidget *button_start;
2977 GtkWidget *progress;
2985 guint idle_id; /* event source id */
2988 #define KEYWORD_DIALOG_WIDTH 400
2990 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
2992 GList *list_d = nullptr;
2993 GList *list_f = nullptr;
2997 filelist_read(dir_fd, &list_f, &list_d);
3001 filelist_read(dir_fd, &list_f, nullptr);
3004 list_f = filelist_filter(list_f, FALSE);
3005 list_d = filelist_filter(list_d, TRUE);
3007 kfd->list = g_list_concat(list_f, kfd->list);
3008 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3011 static void keywords_find_reset(KeywordFindData *kfd)
3013 filelist_free(kfd->list);
3014 kfd->list = nullptr;
3016 filelist_free(kfd->list_dir);
3017 kfd->list_dir = nullptr;
3020 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3022 auto kfd = static_cast<KeywordFindData *>(data);
3024 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3026 keywords_find_reset(kfd);
3027 generic_dialog_close(kfd->gd);
3031 static void keywords_find_finish(KeywordFindData *kfd)
3033 keywords_find_reset(kfd);
3035 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3036 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
3038 gtk_widget_set_sensitive(kfd->group, TRUE);
3039 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3040 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3041 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3044 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3046 auto kfd = static_cast<KeywordFindData *>(data);
3048 g_idle_remove_by_data(kfd);
3050 keywords_find_finish(kfd);
3053 static gboolean keywords_find_file(gpointer data)
3055 auto kfd = static_cast<KeywordFindData *>(data);
3057 GtkTextBuffer *buffer;
3065 fd = static_cast<FileData *>(kfd->list->data);
3066 kfd->list = g_list_remove(kfd->list, fd);
3068 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3069 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3073 gtk_text_buffer_get_end_iter(buffer, &iter);
3074 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3075 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3077 keywords = keywords->next;
3080 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3081 file_data_unref(fd);
3082 g_list_free_full(keywords, g_free);
3084 return (G_SOURCE_CONTINUE);
3086 else if (kfd->list_dir)
3090 fd = static_cast<FileData *>(kfd->list_dir->data);
3091 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3093 keywords_find_folder(kfd, fd);
3095 file_data_unref(fd);
3097 return G_SOURCE_CONTINUE;
3100 keywords_find_finish(kfd);
3102 return G_SOURCE_REMOVE;
3105 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3107 auto kfd = static_cast<KeywordFindData *>(data);
3110 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3112 path = remove_trailing_slash((gq_gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3113 parse_out_relatives(path);
3117 warning_dialog(_("Invalid folder"),
3118 _("The specified folder can not be found."),
3119 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3125 gtk_widget_set_sensitive(kfd->group, FALSE);
3126 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3127 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3128 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3129 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3131 dir_fd = file_data_new_dir(path);
3132 keywords_find_folder(kfd, dir_fd);
3133 file_data_unref(dir_fd);
3134 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3140 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3142 KeywordFindData *kfd;
3146 kfd = g_new0(KeywordFindData, 1);
3148 kfd->gd = generic_dialog_new(_("Search for keywords"),
3149 "search_for_keywords",
3152 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3153 kfd->gd->cancel_cb = keywords_find_close_cb;
3154 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3155 keywords_find_close_cb, FALSE);
3156 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3157 keywords_find_start_cb, FALSE);
3158 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3159 keywords_find_stop_cb, FALSE);
3160 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3162 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3164 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3165 pref_spacer(hbox, PREF_PAD_INDENT);
3166 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3168 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3169 pref_label_new(hbox, _("Folder:"));
3171 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3172 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3173 gq_gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3174 gtk_widget_show(label);
3176 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3178 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3179 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3181 kfd->progress = gtk_entry_new();
3182 gtk_widget_set_can_focus(kfd->progress, FALSE);
3183 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3184 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3185 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3186 gtk_widget_show(kfd->progress);
3188 kfd->spinner = gtk_spinner_new();
3189 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3190 gtk_widget_show(kfd->spinner);
3192 kfd->list = nullptr;
3194 gtk_widget_show(kfd->gd->dialog);
3197 static void keywords_find_cb(GtkWidget *widget, gpointer)
3199 const gchar *path = layout_get_path(nullptr);
3201 if (!path || !*path) path = homedir();
3202 keywords_find_dialog(widget, path);
3205 static void config_tab_keywords_save()
3207 GtkTextIter start, end;
3208 GtkTextBuffer *buffer;
3209 GList *kw_list = nullptr;
3215 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3216 gtk_text_buffer_get_bounds(buffer, &start, &end);
3218 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3220 kw_split = strtok(buffer_text, "\n");
3221 while (kw_split != nullptr)
3227 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3236 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3238 kw_split = strtok(nullptr, "\n");
3241 keyword_list_set(kw_list);
3243 g_list_free_full(kw_list, g_free);
3244 g_free(buffer_text);
3247 static void config_tab_keywords(GtkWidget *notebook)
3253 GtkWidget *scrolled;
3255 GtkTextBuffer *buffer;
3258 GspellTextView *gspell_view;
3261 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3263 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3265 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3267 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3268 G_CALLBACK(keywords_find_cb), keyword_text);
3269 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3272 keyword_text = gtk_text_view_new();
3273 gtk_widget_set_size_request(keyword_text, 20, 20);
3274 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3275 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3276 gtk_widget_show(scrolled);
3279 if (options->metadata.check_spelling)
3281 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3282 gspell_text_view_basic_setup(gspell_view);
3286 gq_gtk_container_add(GTK_WIDGET(scrolled), keyword_text);
3287 gtk_widget_show(keyword_text);
3289 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3291 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3292 gtk_text_buffer_create_tag(buffer, "monospace",
3293 "family", "monospace", NULL);
3295 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3296 gtk_text_buffer_get_start_iter(buffer, &iter);
3297 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3300 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3302 GList *kwl = keyword_list_get();
3303 kwl = g_list_first(kwl);
3306 gtk_text_buffer_get_end_iter (buffer, &iter);
3307 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3308 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3313 gtk_text_buffer_set_modified(buffer, FALSE);
3320 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3322 auto option = static_cast<gint *>(data);
3324 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3328 *option = INTENT_PERCEPTUAL;
3331 *option = INTENT_RELATIVE_COLORIMETRIC;
3334 *option = INTENT_SATURATION;
3337 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3342 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3343 gint option, gint *option_c)
3350 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3352 combo = gtk_combo_box_text_new();
3354 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3355 if (option == INTENT_PERCEPTUAL) current = 0;
3356 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3357 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3358 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3359 if (option == INTENT_SATURATION) current = 2;
3360 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3361 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3363 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3365 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3367 g_signal_connect(G_OBJECT(combo), "changed",
3368 G_CALLBACK(intent_menu_cb), option_c);
3370 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3371 gtk_widget_show(combo);
3375 static void config_tab_color(GtkWidget *notebook)
3384 vbox = scrolled_notebook_page(notebook, _("Color management"));
3386 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3388 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3391 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3392 gtk_grid_set_column_spacing(GTK_GRID(table), PREF_PAD_GAP);
3394 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3395 pref_label_bold(label, TRUE, FALSE);
3397 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3398 pref_label_bold(label, TRUE, FALSE);
3400 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3401 pref_label_bold(label, TRUE, FALSE);
3403 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3408 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3409 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3412 entry = gtk_entry_new();
3413 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3414 if (options->color_profile.input_name[i])
3416 gq_gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3418 gq_gtk_grid_attach(GTK_GRID(table), entry, 1, 2, i + 1, i + 2, static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3419 gtk_widget_show(entry);
3420 color_profile_input_name_entry[i] = entry;
3422 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3423 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3424 gtk_widget_set_size_request(entry, 160, -1);
3425 gq_gtk_grid_attach(GTK_GRID(table), tabcomp, 2, 3, i + 1, i + 2, static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3426 gtk_widget_show(tabcomp);
3427 color_profile_input_file_entry[i] = entry;
3430 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3432 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3434 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3435 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3437 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3439 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3440 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3441 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3442 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3443 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3445 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3447 gq_gtk_grid_attach(GTK_GRID(table), tabcomp, 1, 2, 0, 1, static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3449 gtk_widget_show(tabcomp);
3452 /* advanced entry tab */
3453 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3455 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3457 c_options->file_ops.use_system_trash = FALSE;
3458 c_options->file_ops.no_trash = FALSE;
3462 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3464 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3466 c_options->file_ops.use_system_trash = TRUE;
3467 c_options->file_ops.no_trash = FALSE;
3471 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3473 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3475 c_options->file_ops.no_trash = TRUE;
3479 static void config_tab_behavior(GtkWidget *notebook)
3486 GtkWidget *ct_button;
3490 GtkWidget *with_rename;
3491 GtkWidget *collections_on_top;
3492 GtkWidget *hide_window_in_fullscreen;
3493 GtkWidget *checkbox;
3496 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3498 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3500 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3501 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3502 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3503 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3504 pref_checkbox_new_int(group, _("Enable Delete key"),
3505 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3507 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3508 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3510 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3511 pref_checkbox_link_sensitivity(ct_button, hbox);
3513 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3514 pref_label_new(hbox, _("Folder:"));
3516 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3517 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3518 gq_gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3519 gtk_widget_show(tabcomp);
3521 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3522 pref_checkbox_link_sensitivity(ct_button, hbox);
3524 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3525 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3526 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3527 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3528 button = pref_button_new(nullptr, nullptr, _("View"),
3529 G_CALLBACK(safe_delete_view_cb), nullptr);
3530 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3531 gtk_widget_show(button);
3533 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3534 G_CALLBACK(safe_delete_clear_cb), nullptr);
3535 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3537 c_options->file_ops.no_trash = options->file_ops.no_trash;
3538 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3540 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3541 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3543 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3544 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3546 gtk_widget_show(button);
3548 pref_spacer(group, PREF_PAD_GROUP);
3551 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3553 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3554 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3556 pref_checkbox_new_int(group, _("In place renaming"),
3557 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3559 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3560 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3562 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3563 options->circular_selection_lists, &c_options->circular_selection_lists);
3564 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3566 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3567 options->marks_save, &c_options->marks_save);
3568 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3570 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3571 options->with_rename, &c_options->with_rename);
3572 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3574 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3575 options->collections_duplicates, &c_options->collections_duplicates);
3576 gtk_widget_set_tooltip_text(collections_on_top,"Allow the same image to be in a Collection more than once");
3578 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3579 options->collections_on_top, &c_options->collections_on_top);
3580 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3582 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3583 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3584 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3586 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3587 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3589 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);
3590 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."));
3592 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3593 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3595 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3596 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3598 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3599 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3601 pref_spacer(group, PREF_PAD_GROUP);
3603 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3605 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3606 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3607 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3608 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3609 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3610 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3611 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3612 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3613 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3614 options->image_l_click_archive, &c_options->image_l_click_archive);
3615 pref_checkbox_new_int(group, _("Play video by left click on image"),
3616 options->image_l_click_video, &c_options->image_l_click_video);
3617 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3618 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3620 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3621 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3622 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3623 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3624 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3626 pref_spacer(group, PREF_PAD_GROUP);
3628 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3630 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3631 options->override_disable_gpu, &c_options->override_disable_gpu);
3632 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3635 pref_spacer(group, PREF_PAD_GROUP);
3637 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3639 pref_spin_new_int(group, _("Debug level:"), nullptr,
3640 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3642 pref_checkbox_new_int(group, _("Timer data"),
3643 options->log_window.timer_data, &c_options->log_window.timer_data);
3645 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3646 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3650 /* accelerators tab */
3652 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3654 gboolean ret = TRUE;
3659 gtk_tree_model_get(model, iter, column, &text, -1);
3660 text_nocase = g_utf8_casefold(text, -1);
3661 key_nocase = g_utf8_casefold(key, -1);
3663 if (g_strstr_len(text_nocase, -1, key_nocase))
3670 g_free(text_nocase);
3675 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3680 list = gtk_tree_view_get_columns(tree_view);
3681 col_num = g_list_index(list, column);
3685 gtk_tree_view_set_search_column(tree_view, col_num);
3688 static void config_tab_accelerators(GtkWidget *notebook)
3694 GtkWidget *scrolled;
3695 GtkWidget *accel_view;
3696 GtkCellRenderer *renderer;
3697 GtkTreeSelection *selection;
3698 GtkTreeViewColumn *column;
3700 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3702 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3704 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3705 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3706 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3707 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3708 gtk_widget_show(scrolled);
3710 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3712 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3713 g_object_unref(accel_store);
3714 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3715 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3717 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3719 renderer = gtk_cell_renderer_text_new();
3721 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3726 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3727 gtk_tree_view_column_set_resizable(column, TRUE);
3728 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3731 renderer = gtk_cell_renderer_accel_new();
3732 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3733 G_CALLBACK(accel_store_cleared_cb), accel_store);
3734 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3735 G_CALLBACK(accel_store_edited_cb), accel_store);
3738 g_object_set (renderer,
3740 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3743 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3748 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3749 gtk_tree_view_column_set_resizable(column, TRUE);
3750 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3752 renderer = gtk_cell_renderer_text_new();
3754 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3759 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3760 gtk_tree_view_column_set_resizable(column, TRUE);
3761 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3763 renderer = gtk_cell_renderer_text_new();
3765 column = gtk_tree_view_column_new_with_attributes("Accel",
3770 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3771 gtk_tree_view_column_set_resizable(column, TRUE);
3772 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3774 /* Search on text in column */
3775 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3776 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3777 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3778 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3779 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3781 accel_store_populate();
3782 gq_gtk_container_add(GTK_WIDGET(scrolled), accel_view);
3783 gtk_widget_show(accel_view);
3785 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3787 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3788 G_CALLBACK(accel_default_cb), accel_view);
3789 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3790 gtk_widget_show(button);
3792 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3793 G_CALLBACK(accel_reset_cb), accel_view);
3794 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3795 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3796 gtk_widget_show(button);
3798 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3799 G_CALLBACK(accel_clear_cb), accel_view);
3800 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3801 gtk_widget_show(button);
3804 /* toolbar main tab */
3805 static void config_tab_toolbar_main(GtkWidget *notebook)
3808 GtkWidget *toolbardata;
3811 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3813 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3815 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3816 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3817 gtk_widget_show(vbox);
3820 /* toolbar status tab */
3821 static void config_tab_toolbar_status(GtkWidget *notebook)
3824 GtkWidget *toolbardata;
3827 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3829 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3831 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3832 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3833 gtk_widget_show(vbox);
3837 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3839 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3842 static void config_tab_advanced(GtkWidget *notebook)
3845 GdkPixbufFormat *fm;
3847 GList *extensions_list = nullptr;
3848 GSList *formats_list;
3849 GString *types_string = g_string_new(nullptr);
3850 GtkWidget *alternate_checkbox;
3851 GtkWidget *dupes_threads_spin;
3853 GtkWidget *subgroup;
3855 GtkWidget *threads_string_label;
3856 GtkWidget *types_string_label;
3859 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3860 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3862 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3864 pref_spacer(group, PREF_PAD_GROUP);
3866 formats_list = gdk_pixbuf_get_formats();
3868 while (formats_list)
3870 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3871 extensions = gdk_pixbuf_format_get_extensions(fm);
3874 while (extensions[i])
3876 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3880 g_strfreev(extensions);
3881 formats_list = formats_list->next;
3884 while (extensions_list)
3886 if (types_string->len == 0)
3888 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3892 types_string = g_string_append(types_string, ", ");
3893 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3896 extensions_list = extensions_list->next;
3899 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3900 types_string_label = pref_label_new(group, types_string->str);
3901 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3903 pref_spacer(group, PREF_PAD_GROUP);
3905 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3906 external_preview_select_entry = gtk_entry_new();
3907 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3908 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3909 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3910 gtk_widget_show(tabcomp);
3912 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3913 external_preview_extract_entry = gtk_entry_new();
3914 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3915 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3916 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3917 gtk_widget_show(tabcomp);
3919 gtk_widget_show(vbox);
3921 g_slist_free(formats_list);
3922 g_list_free_full(extensions_list, g_free);
3923 g_string_free(types_string, TRUE);
3925 pref_spacer(group, PREF_PAD_GROUP);
3927 pref_line(vbox, PREF_PAD_SPACE);
3928 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3930 threads_string_label = pref_label_new(group, "This option limits the number of threads (or cpu cores) that Geeqie will use when running duplicate checks.\nThe value 0 means all available cores will be used.");
3931 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3933 pref_spacer(vbox, PREF_PAD_GROUP);
3935 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3936 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3938 pref_spacer(group, PREF_PAD_GROUP);
3940 pref_line(vbox, PREF_PAD_SPACE);
3942 group = pref_group_new(vbox, FALSE, _("Alternate similarity alogorithm"), GTK_ORIENTATION_VERTICAL);
3944 alternate_checkbox = pref_checkbox_new_int(group, _("Enable alternate similarity algorithm"), options->alternate_similarity_algorithm.enabled, &c_options->alternate_similarity_algorithm.enabled);
3946 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3947 pref_checkbox_link_sensitivity(alternate_checkbox, subgroup);
3949 alternate_checkbox = pref_checkbox_new_int(subgroup, _("Use grayscale"), options->alternate_similarity_algorithm.grayscale, &c_options->alternate_similarity_algorithm.grayscale);
3950 gtk_widget_set_tooltip_text(alternate_checkbox, _("Reduce fingerprint to grayscale"));
3954 static void config_tab_stereo(GtkWidget *notebook)
3962 GtkWidget *fs_button;
3963 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3965 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3967 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3968 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3970 table = pref_table_new(group, 2, 2, TRUE, FALSE);
3971 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3972 pref_checkbox_new_int(box, _("Mirror left image"),
3973 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3974 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3975 pref_checkbox_new_int(box, _("Flip left image"),
3976 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3977 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3978 pref_checkbox_new_int(box, _("Mirror right image"),
3979 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3980 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3981 pref_checkbox_new_int(box, _("Flip right image"),
3982 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3983 pref_checkbox_new_int(group, _("Swap left and right images"),
3984 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3985 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3986 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3988 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3989 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3990 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3991 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3992 pref_checkbox_link_sensitivity(fs_button, box2);
3993 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3994 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3995 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3996 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3997 pref_checkbox_new_int(box, _("Mirror left image"),
3998 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3999 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4000 pref_checkbox_new_int(box, _("Flip left image"),
4001 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
4002 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4003 pref_checkbox_new_int(box, _("Mirror right image"),
4004 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
4005 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4006 pref_checkbox_new_int(box, _("Flip right image"),
4007 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
4008 pref_checkbox_new_int(box2, _("Swap left and right images"),
4009 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
4010 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
4011 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
4013 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
4014 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
4015 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
4016 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
4017 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
4018 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
4019 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
4020 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
4021 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
4022 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4023 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
4024 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4025 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
4026 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4030 /* Main preferences window */
4031 static void config_window_create(LayoutWindow *lw)
4033 GtkWidget *win_vbox;
4035 GtkWidget *notebook;
4037 GtkWidget *ct_button;
4039 if (!c_options) c_options = init_options(nullptr);
4041 configwindow = window_new("preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4042 DEBUG_NAME(configwindow);
4043 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4044 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4045 G_CALLBACK(config_window_delete), NULL);
4046 if (options->save_dialog_window_positions)
4048 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4049 gq_gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4053 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4055 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4056 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4058 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4059 gq_gtk_container_add(GTK_WIDGET(configwindow), win_vbox);
4060 gtk_widget_show(win_vbox);
4062 notebook = gtk_notebook_new();
4063 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4064 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4065 gq_gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4067 config_tab_general(notebook);
4068 config_tab_image(notebook);
4069 config_tab_osd(notebook);
4070 config_tab_windows(notebook);
4071 config_tab_accelerators(notebook);
4072 config_tab_files(notebook);
4073 config_tab_metadata(notebook);
4074 config_tab_keywords(notebook);
4075 config_tab_color(notebook);
4076 config_tab_stereo(notebook);
4077 config_tab_behavior(notebook);
4078 config_tab_toolbar_main(notebook);
4079 config_tab_toolbar_status(notebook);
4080 config_tab_advanced(notebook);
4082 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4084 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4085 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4086 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4087 gq_gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4088 gtk_widget_show(hbox);
4090 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4091 G_CALLBACK(config_window_help_cb), notebook);
4092 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4093 gtk_widget_set_can_default(button, TRUE);
4094 gtk_widget_show(button);
4096 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4097 G_CALLBACK(config_window_ok_cb), notebook);
4098 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4099 gtk_widget_set_can_default(button, TRUE);
4100 gtk_widget_grab_default(button);
4101 gtk_widget_show(button);
4105 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4106 G_CALLBACK(config_window_close_cb), nullptr);
4107 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4108 gtk_widget_set_can_default(button, TRUE);
4109 gtk_widget_show(button);
4111 if (!generic_dialog_get_alternative_button_order(configwindow))
4113 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4116 gtk_widget_show(notebook);
4118 gtk_widget_show(configwindow);
4122 *-----------------------------------------------------------------------------
4123 * config window show (public)
4124 *-----------------------------------------------------------------------------
4127 void show_config_window(LayoutWindow *lw)
4131 gtk_window_present(GTK_WINDOW(configwindow));
4135 config_window_create(lw);
4144 void show_about_window(LayoutWindow *lw)
4146 GDataInputStream *data_stream;
4147 GInputStream *in_stream_authors;
4148 GInputStream *in_stream_translators;
4150 GdkPixbuf *pixbuf_icon;
4151 GdkPixbuf *pixbuf_logo;
4155 gchar *authors[1000];
4157 gchar *timezone_path;
4166 copyright = g_string_new("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");
4168 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4169 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4171 cd = ZDOpenDatabase(timezone_path);
4174 copyright = g_string_append(copyright, ZDGetNotice(cd));
4178 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4180 ZDCloseDatabase(cd);
4182 g_free(timezone_path);
4184 copyright = g_string_append(copyright, "\n\nSome icons by https://www.flaticon.com");
4186 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4188 data_stream = g_data_input_stream_new(in_stream_authors);
4190 authors[0] = nullptr;
4191 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4193 authors[i_authors] = g_strdup(author_line);
4195 g_free(author_line);
4197 authors[i_authors] = nullptr;
4199 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4201 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4203 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4205 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4206 translators = static_cast<gchar *>(g_malloc0(size));
4207 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4208 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4210 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);
4212 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4213 artists[1] = nullptr;
4215 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4216 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4217 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4218 "title", _("About Geeqie"),
4220 "program-name", GQ_APPNAME,
4222 "logo", pixbuf_logo,
4223 "icon", pixbuf_icon,
4224 "website", GQ_WEBSITE,
4225 "website-label", "Website",
4226 "comments", comment,
4229 "translator-credits", translators,
4230 "wrap-license", TRUE,
4231 "license", copyright->str,
4234 g_string_free(copyright, TRUE);
4236 while(n < i_authors)
4244 g_free(translators);
4245 g_object_unref(data_stream);
4246 g_object_unref(in_stream_authors);
4247 g_object_unref(in_stream_translators);
4250 static void image_overlay_set_text_colors()
4252 c_options->image_overlay.text_red = options->image_overlay.text_red;
4253 c_options->image_overlay.text_green = options->image_overlay.text_green;
4254 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4255 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4256 c_options->image_overlay.background_red = options->image_overlay.background_red;
4257 c_options->image_overlay.background_green = options->image_overlay.background_green;
4258 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4259 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4263 *-----------------------------------------------------------------------------
4264 * timezone database routines
4265 *-----------------------------------------------------------------------------
4268 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4270 GError *error = nullptr;
4271 auto tz = static_cast<TZData *>(data);
4272 gchar *tmp_filename;
4273 gchar *timezone_bin;
4274 gchar *tmp_dir = nullptr;
4277 if (!g_cancellable_is_cancelled(tz->cancellable))
4279 generic_dialog_close(tz->gd);
4283 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4285 tmp_filename = g_file_get_path(tz->tmp_g_file);
4286 fd = file_data_new_simple(tmp_filename);
4287 tmp_dir = open_archive(fd);
4291 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4292 if (isfile(timezone_bin))
4294 move_file(timezone_bin, tz->timezone_database_user);
4298 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4301 g_free(timezone_bin);
4302 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4306 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4308 g_free(tmp_filename);
4309 file_data_unref(fd);
4313 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4316 g_file_delete(tz->tmp_g_file, nullptr, &error);
4317 g_object_unref(tz->tmp_g_file);
4318 tz->tmp_g_file = nullptr;
4319 g_object_unref(tz->cancellable);
4320 g_object_unref(tz->timezone_database_gq);
4323 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4325 auto tz = static_cast<TZData *>(data);
4327 if (!g_cancellable_is_cancelled(tz->cancellable))
4329 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4333 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4335 auto tz = static_cast<TZData *>(data);
4337 g_cancellable_cancel(tz->cancellable);
4340 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4342 auto tz = static_cast<TZData *>(data);
4343 GError *error = nullptr;
4344 GFileIOStream *io_stream;
4351 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4355 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4356 log_printf("Error: Download timezone database failed:\n%s", error->message);
4357 g_error_free(error);
4358 g_object_unref(tz->tmp_g_file);
4362 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4364 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4366 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4368 tz->progress = gtk_progress_bar_new();
4369 gq_gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4370 gtk_widget_show(tz->progress);
4372 gtk_widget_show(tz->gd->dialog);
4373 tz->cancellable = g_cancellable_new();
4374 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);
4376 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4379 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */