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>
64 EDITOR_NAME_MAX_LENGTH = 32,
65 EDITOR_COMMAND_MAX_LENGTH = 1024
68 static void image_overlay_set_text_colors();
70 GtkWidget *keyword_text;
71 static void config_tab_keywords_save();
79 static ThumbSize thumb_size_list[] =
114 FILETYPES_COLUMN_ENABLED = 0,
115 FILETYPES_COLUMN_FILTER,
116 FILETYPES_COLUMN_DESCRIPTION,
117 FILETYPES_COLUMN_CLASS,
118 FILETYPES_COLUMN_WRITABLE,
119 FILETYPES_COLUMN_SIDECAR,
120 FILETYPES_COLUMN_COUNT
123 const gchar *format_class_list[] = {
134 /* config memory values */
135 static ConfOptions *c_options = nullptr;
142 static GtkWidget *configwindow = nullptr;
143 static GtkListStore *filter_store = nullptr;
144 static GtkTreeStore *accel_store = nullptr;
146 static GtkWidget *safe_delete_path_entry;
148 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
149 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
150 static GtkWidget *color_profile_screen_file_entry;
151 static GtkWidget *external_preview_select_entry;
152 static GtkWidget *external_preview_extract_entry;
154 static GtkWidget *sidecar_ext_entry;
155 static GtkWidget *help_search_engine_entry;
157 static GtkWidget *log_window_f1_entry;
161 CONFIG_WINDOW_DEF_WIDTH = 700,
162 CONFIG_WINDOW_DEF_HEIGHT = 600
166 *-----------------------------------------------------------------------------
167 * option widget callbacks (private)
168 *-----------------------------------------------------------------------------
171 static void zoom_increment_cb(GtkWidget *spin, gpointer)
173 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
176 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
178 gint mins_secs_tenths;
181 mins_secs_tenths = c_options->slideshow.delay %
182 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
184 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
185 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
188 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
189 SLIDESHOW_SUBSECOND_PRECISION;
192 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
198 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
199 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
201 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
202 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
203 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
205 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
206 SLIDESHOW_SUBSECOND_PRECISION;
209 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
214 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
216 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
217 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
218 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
220 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
221 SLIDESHOW_SUBSECOND_PRECISION;
225 *-----------------------------------------------------------------------------
226 * sync program to config window routine (private)
227 *-----------------------------------------------------------------------------
230 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
236 buf = gq_gtk_entry_get_text(GTK_ENTRY(entry));
237 if (buf && strlen(buf) > 0)
242 *option = g_strdup(buf);
247 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
252 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
254 if (accel_path && accel_path[0])
257 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
258 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
268 static void config_window_apply()
270 gboolean refresh = FALSE;
275 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
277 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
278 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
279 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
280 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
281 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
282 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
284 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
285 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
286 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
287 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
288 options->file_ops.no_trash = c_options->file_ops.no_trash;
289 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
290 options->tools_restore_state = c_options->tools_restore_state;
291 options->save_window_positions = c_options->save_window_positions;
292 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
293 options->save_window_workspace = c_options->save_window_workspace;
294 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
295 options->show_window_ids = c_options->show_window_ids;
296 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
297 options->image.zoom_2pass = c_options->image.zoom_2pass;
298 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
299 options->image.limit_window_size = c_options->image.limit_window_size;
300 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
301 options->image.max_window_size = c_options->image.max_window_size;
302 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
303 options->image.max_autofit_size = c_options->image.max_autofit_size;
304 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
305 options->image.tile_size = c_options->image.tile_size;
306 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
307 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
309 if (options->thumbnails.max_width != c_options->thumbnails.max_width
310 || options->thumbnails.max_height != c_options->thumbnails.max_height
311 || options->thumbnails.quality != c_options->thumbnails.quality)
313 thumb_format_changed = TRUE;
315 options->thumbnails.max_width = c_options->thumbnails.max_width;
316 options->thumbnails.max_height = c_options->thumbnails.max_height;
317 options->thumbnails.quality = c_options->thumbnails.quality;
319 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
320 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
321 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
322 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
323 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
324 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
325 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
326 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
327 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
328 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
329 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
330 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
332 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
333 options->file_filter.disable = c_options->file_filter.disable;
335 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
336 sidecar_ext_parse(options->sidecar.ext);
338 options->slideshow.random = c_options->slideshow.random;
339 options->slideshow.repeat = c_options->slideshow.repeat;
340 options->slideshow.delay = c_options->slideshow.delay;
342 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
343 options->image_lm_click_nav = c_options->image_lm_click_nav;
344 options->image_l_click_archive = c_options->image_l_click_archive;
345 options->image_l_click_video = c_options->image_l_click_video;
346 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
348 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
350 options->image.tile_cache_max = c_options->image.tile_cache_max;
351 options->image.image_cache_max = c_options->image.image_cache_max;
353 options->image.zoom_quality = c_options->image.zoom_quality;
355 options->image.zoom_increment = c_options->image.zoom_increment;
357 options->image.zoom_style = c_options->image.zoom_style;
359 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
361 options->appimage_notifications = c_options->appimage_notifications;
364 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
365 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
366 || !gdk_rgba_equal(&options->image.border_color, &c_options->image.border_color))
368 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
369 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
370 options->image.border_color = c_options->image.border_color;
371 layout_colors_update();
372 view_window_colors_update();
375 options->image.alpha_color_1 = c_options->image.alpha_color_1;
376 options->image.alpha_color_2 = c_options->image.alpha_color_2;
378 options->fullscreen.screen = c_options->fullscreen.screen;
379 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
380 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
381 options->fullscreen.above = c_options->fullscreen.above;
382 if (c_options->image_overlay.template_string)
383 set_image_overlay_template_string(&options->image_overlay.template_string,
384 c_options->image_overlay.template_string);
385 if (c_options->image_overlay.font)
386 set_image_overlay_font_string(&options->image_overlay.font,
387 c_options->image_overlay.font);
388 options->image_overlay.text_red = c_options->image_overlay.text_red;
389 options->image_overlay.text_green = c_options->image_overlay.text_green;
390 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
391 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
392 options->image_overlay.background_red = c_options->image_overlay.background_red;
393 options->image_overlay.background_green = c_options->image_overlay.background_green;
394 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
395 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
396 options->image_overlay.font = c_options->image_overlay.font;
397 options->update_on_time_change = c_options->update_on_time_change;
399 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
400 options->rot_invariant_sim = c_options->rot_invariant_sim;
402 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
404 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
405 options->circular_selection_lists = c_options->circular_selection_lists;
407 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
408 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
409 options->dnd_icon_size = c_options->dnd_icon_size;
410 options->clipboard_selection = c_options->clipboard_selection;
411 options->dnd_default_action = c_options->dnd_default_action;
413 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
414 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
415 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
416 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
417 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
418 options->metadata.confirm_write = c_options->metadata.confirm_write;
419 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
420 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
421 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
422 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
423 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
424 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
425 options->metadata.write_orientation = c_options->metadata.write_orientation;
426 options->metadata.check_spelling = c_options->metadata.check_spelling;
427 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
428 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
429 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
430 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
431 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
432 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
433 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
434 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
435 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
436 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
437 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
438 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
439 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
440 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
441 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
442 options->stereo.fixed_w = c_options->stereo.fixed_w;
443 options->stereo.fixed_h = c_options->stereo.fixed_h;
444 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
445 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
446 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
447 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
449 options->info_keywords.height = c_options->info_keywords.height;
450 options->info_title.height = c_options->info_title.height;
451 options->info_comment.height = c_options->info_comment.height;
452 options->info_rating.height = c_options->info_rating.height;
454 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
455 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
456 options->hamburger_menu = c_options->hamburger_menu;
458 options->selectable_bars.menu_bar = c_options->selectable_bars.menu_bar;
459 options->selectable_bars.tool_bar = c_options->selectable_bars.tool_bar;
460 options->selectable_bars.status_bar = c_options->selectable_bars.status_bar;
462 options->marks_save = c_options->marks_save;
463 options->with_rename = c_options->with_rename;
464 options->collections_duplicates = c_options->collections_duplicates;
465 options->collections_on_top = c_options->collections_on_top;
466 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
467 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
469 options->external_preview.enable = c_options->external_preview.enable;
470 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
471 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
473 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
475 options->star_rating.star = c_options->star_rating.star;
476 options->star_rating.rejected = c_options->star_rating.rejected;
478 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
480 options->alternate_similarity_algorithm.enabled = c_options->alternate_similarity_algorithm.enabled;
481 options->alternate_similarity_algorithm.grayscale = c_options->alternate_similarity_algorithm.grayscale;
484 set_debug_level(debug_c);
485 config_entry_to_option(log_window_f1_entry, &options->log_window.action, nullptr);
489 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
491 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
492 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
494 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
495 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
496 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
498 options->color_profile.render_intent = c_options->color_profile.render_intent;
503 options->mouse_button_8 = c_options->mouse_button_8;
504 options->mouse_button_9 = c_options->mouse_button_9;
506 options->override_disable_gpu = c_options->override_disable_gpu;
508 config_tab_keywords_save();
510 image_options_sync();
515 layout_refresh(nullptr);
518 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
520 toolbar_apply(TOOLBAR_MAIN);
521 toolbar_apply(TOOLBAR_STATUS);
525 *-----------------------------------------------------------------------------
526 * config window main button callbacks (private)
527 *-----------------------------------------------------------------------------
530 static void config_window_close_cb(GtkWidget *, gpointer)
532 gq_gtk_widget_destroy(configwindow);
533 configwindow = nullptr;
534 filter_store = nullptr;
537 static void config_window_help_cb(GtkWidget *, gpointer data)
539 auto notebook = static_cast<GtkWidget *>(data);
542 static const gchar *html_section[] =
544 "GuideOptionsGeneral.html",
545 "GuideOptionsImage.html",
546 "GuideOptionsOSD.html",
547 "GuideOptionsWindow.html",
548 "GuideOptionsKeyboard.html",
549 "GuideOptionsFiltering.html",
550 "GuideOptionsMetadata.html",
551 "GuideOptionsKeywords.html",
552 "GuideOptionsColor.html",
553 "GuideOptionsStereo.html",
554 "GuideOptionsBehavior.html",
555 "GuideOptionsToolbar.html",
556 "GuideOptionsToolbar.html",
557 "GuideOptionsAdvanced.html"
560 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
561 help_window_show(html_section[i]);
564 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
566 config_window_close_cb(nullptr, nullptr);
570 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
573 auto notebook = static_cast<GtkNotebook *>(data);
581 lw = static_cast<LayoutWindow *>(layout_window_list->data);
583 window = gtk_widget_get_window(widget);
584 gdk_window_get_root_origin(window, &x, &y);
585 w = gdk_window_get_width(window);
586 h = gdk_window_get_height(window);
587 page_number = gtk_notebook_get_current_page(notebook);
589 lw->options.preferences_window.x = x;
590 lw->options.preferences_window.y = y;
591 lw->options.preferences_window.w = w;
592 lw->options.preferences_window.h = h;
593 lw->options.preferences_window.page_number = page_number;
595 config_window_apply();
596 layout_util_sync(lw);
597 save_options(options);
598 config_window_close_cb(nullptr, nullptr);
602 *-----------------------------------------------------------------------------
603 * config window setup (private)
604 *-----------------------------------------------------------------------------
607 static void quality_menu_cb(GtkWidget *combo, gpointer data)
609 auto option = static_cast<gint *>(data);
611 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
615 *option = GDK_INTERP_NEAREST;
618 *option = GDK_INTERP_TILES;
621 *option = GDK_INTERP_BILINEAR;
626 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
628 auto option = static_cast<gint *>(data);
630 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
634 *option = DND_ACTION_ASK;
637 *option = DND_ACTION_COPY;
640 *option = DND_ACTION_MOVE;
644 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
646 auto option = static_cast<gint *>(data);
648 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
651 *option = CLIPBOARD_PRIMARY;
654 *option = CLIPBOARD_CLIPBOARD;
657 *option = CLIPBOARD_BOTH;
660 *option = CLIPBOARD_BOTH;
664 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
665 guint option, guint *option_c)
672 pref_table_label(table, column, row, text, GTK_ALIGN_START);
674 combo = gtk_combo_box_text_new();
676 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
677 if (option == GDK_INTERP_NEAREST) current = 0;
678 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
679 if (option == GDK_INTERP_TILES) current = 1;
680 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
681 if (option == GDK_INTERP_BILINEAR) current = 2;
683 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
685 g_signal_connect(G_OBJECT(combo), "changed",
686 G_CALLBACK(quality_menu_cb), option_c);
688 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
689 gtk_widget_show(combo);
692 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
699 pref_table_label(table, column, row, text, GTK_ALIGN_START);
701 combo = gtk_combo_box_text_new();
703 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
704 if (option == DND_ACTION_ASK) current = 0;
705 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
706 if (option == DND_ACTION_COPY) current = 1;
707 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
708 if (option == DND_ACTION_MOVE) current = 2;
710 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
712 g_signal_connect(G_OBJECT(combo), "changed",
713 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
715 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
716 gtk_widget_show(combo);
719 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
720 gint option, gint *option_c)
727 pref_table_label(table, column, row, text, GTK_ALIGN_START);
729 combo = gtk_combo_box_text_new();
731 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
732 if (option == CLIPBOARD_PRIMARY) current = 0;
733 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
734 if (option == CLIPBOARD_CLIPBOARD) current = 1;
735 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
736 if (option == CLIPBOARD_BOTH) current = 2;
738 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
740 g_signal_connect(G_OBJECT(combo), "changed",
741 G_CALLBACK(clipboard_selection_menu_cb), option_c);
743 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
744 gtk_widget_show(combo);
747 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
749 auto option = static_cast<gint *>(data);
751 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
754 *option = ZOOM_GEOMETRIC;
757 *option = ZOOM_ARITHMETIC;
760 *option = ZOOM_GEOMETRIC;
764 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
771 pref_table_label(table, column, row, text, GTK_ALIGN_START);
773 combo = gtk_combo_box_text_new();
775 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
776 if (option == ZOOM_GEOMETRIC) current = 0;
777 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
778 if (option == ZOOM_ARITHMETIC) current = 1;
780 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
782 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
784 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
785 gtk_widget_show(combo);
788 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
790 ActionItem *action_item = nullptr;
791 auto option = static_cast<gchar **>(data);
796 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
798 list = get_action_items();
803 action_item = static_cast<ActionItem *>(work->data);
804 if (g_strcmp0(action_item->label, label) == 0)
812 *option = g_strdup(action_item->name);
814 action_items_free(list);
817 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
819 ActionItem *action_item;
828 pref_table_label(table, column, row, text, GTK_ALIGN_START);
830 combo = gtk_combo_box_text_new();
832 list = get_action_items();
836 action_item = static_cast<ActionItem *>(work->data);
837 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
839 if (g_strcmp0(action_item->name, option) == 0)
847 action_items_free(list);
849 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
851 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
853 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
854 gtk_widget_show(combo);
857 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
861 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
864 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
866 c_options->thumbnails.max_width = thumb_size_list[n].w;
867 c_options->thumbnails.max_height = thumb_size_list[n].h;
871 c_options->thumbnails.max_width = options->thumbnails.max_width;
872 c_options->thumbnails.max_height = options->thumbnails.max_height;
876 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
882 c_options->thumbnails.max_width = options->thumbnails.max_width;
883 c_options->thumbnails.max_height = options->thumbnails.max_height;
885 pref_table_label(table, column, row, text, GTK_ALIGN_START);
887 combo = gtk_combo_box_text_new();
890 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
896 w = thumb_size_list[i].w;
897 h = thumb_size_list[i].h;
899 buf = g_strdup_printf("%d x %d", w, h);
900 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
903 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
910 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
911 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
917 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
918 g_signal_connect(G_OBJECT(combo), "changed",
919 G_CALLBACK(thumb_size_menu_cb), NULL);
921 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
922 gtk_widget_show(combo);
925 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
927 auto option = static_cast<gint *>(data);
929 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
933 *option = PR_STEREO_NONE;
936 *option = PR_STEREO_ANAGLYPH_RC;
939 *option = PR_STEREO_ANAGLYPH_GM;
942 *option = PR_STEREO_ANAGLYPH_YB;
945 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
948 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
951 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
954 *option = PR_STEREO_ANAGLYPH_DB_RC;
957 *option = PR_STEREO_ANAGLYPH_DB_GM;
960 *option = PR_STEREO_ANAGLYPH_DB_YB;
963 *option = PR_STEREO_HORIZ;
966 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
969 *option = PR_STEREO_VERT;
972 *option = PR_STEREO_VERT | PR_STEREO_HALF;
975 *option = PR_STEREO_FIXED;
980 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
981 gint option, gint *option_c, gboolean add_fixed)
988 pref_table_label(table, column, row, text, GTK_ALIGN_START);
990 combo = gtk_combo_box_text_new();
992 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
994 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
995 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
996 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
997 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
998 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
999 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
1000 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
1001 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
1002 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
1003 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
1004 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
1005 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
1006 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
1007 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
1008 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
1009 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
1010 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1011 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1013 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1014 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1015 if (option & PR_STEREO_HORIZ)
1018 if (option & PR_STEREO_HALF) current = 11;
1021 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1022 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1023 if (option & PR_STEREO_VERT)
1026 if (option & PR_STEREO_HALF) current = 13;
1031 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1032 if (option & PR_STEREO_FIXED) current = 14;
1035 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1037 g_signal_connect(G_OBJECT(combo), "changed",
1038 G_CALLBACK(stereo_mode_menu_cb), option_c);
1040 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1041 gtk_widget_show(combo);
1044 static void video_menu_cb(GtkWidget *combo, gpointer data)
1046 auto option = static_cast<gchar **>(data);
1048 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1052 static void video_menu_populate(gpointer data, gpointer user_data)
1054 auto combo = static_cast<GtkWidget *>(user_data);
1055 auto ed = static_cast<EditorDescription *>(data);
1057 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1060 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1061 gchar *option, gchar **option_c)
1065 /* use lists since they are sorted */
1066 GList *eds = editor_list_get();
1070 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1072 combo = gtk_combo_box_text_new();
1073 g_list_foreach(eds,video_menu_populate,combo);
1074 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1076 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1078 g_signal_connect(G_OBJECT(combo), "changed",
1079 G_CALLBACK(video_menu_cb), option_c);
1081 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1082 gtk_widget_show(combo);
1085 static void filter_store_populate()
1089 if (!filter_store) return;
1091 gtk_list_store_clear(filter_store);
1093 work = filter_get_list();
1099 fe = static_cast<FilterEntry *>(work->data);
1102 gtk_list_store_append(filter_store, &iter);
1103 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1107 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1109 auto model = static_cast<GtkWidget *>(data);
1110 auto fe = static_cast<FilterEntry *>(data);
1114 if (!new_text || strlen(new_text) < 1) return;
1116 tpath = gtk_tree_path_new_from_string(path_str);
1117 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1118 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1120 g_free(fe->extensions);
1121 fe->extensions = g_strdup(new_text);
1123 gtk_tree_path_free(tpath);
1127 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1129 auto model = static_cast<GtkWidget *>(data);
1130 auto fe = static_cast<FilterEntry *>(data);
1135 if (!new_text || !new_text[0]) return;
1137 tpath = gtk_tree_path_new_from_string(path_str);
1138 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1139 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1141 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1143 if (strcmp(new_text, _(format_class_list[i])) == 0)
1145 fe->file_class = static_cast<FileFormatClass>(i);
1150 gtk_tree_path_free(tpath);
1154 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1156 auto model = static_cast<GtkWidget *>(data);
1161 if (!new_text || !new_text[0]) return;
1163 tpath = gtk_tree_path_new_from_string(path_str);
1164 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1165 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1167 g_free(fe->description);
1168 fe->description = g_strdup(new_text);
1170 gtk_tree_path_free(tpath);
1173 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1175 auto model = static_cast<GtkWidget *>(data);
1180 tpath = gtk_tree_path_new_from_string(path_str);
1181 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1182 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1184 fe->enabled = !fe->enabled;
1186 gtk_tree_path_free(tpath);
1190 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1192 auto model = static_cast<GtkWidget *>(data);
1197 tpath = gtk_tree_path_new_from_string(path_str);
1198 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1199 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1201 fe->writable = !fe->writable;
1202 if (fe->writable) fe->allow_sidecar = FALSE;
1204 gtk_tree_path_free(tpath);
1208 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1210 auto model = static_cast<GtkWidget *>(data);
1215 tpath = gtk_tree_path_new_from_string(path_str);
1216 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1217 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1219 fe->allow_sidecar = !fe->allow_sidecar;
1220 if (fe->allow_sidecar) fe->writable = FALSE;
1222 gtk_tree_path_free(tpath);
1226 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1227 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1231 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1233 switch (GPOINTER_TO_INT(data))
1236 g_object_set(GTK_CELL_RENDERER(cell),
1237 "active", fe->enabled, NULL);
1240 g_object_set(GTK_CELL_RENDERER(cell),
1241 "text", fe->extensions, NULL);
1243 case FE_DESCRIPTION:
1244 g_object_set(GTK_CELL_RENDERER(cell),
1245 "text", fe->description, NULL);
1248 g_object_set(GTK_CELL_RENDERER(cell),
1249 "text", _(format_class_list[fe->file_class]), NULL);
1252 g_object_set(GTK_CELL_RENDERER(cell),
1253 "active", fe->writable, NULL);
1255 case FE_ALLOW_SIDECAR:
1256 g_object_set(GTK_CELL_RENDERER(cell),
1257 "active", fe->allow_sidecar, NULL);
1262 static gboolean filter_add_scroll(gpointer data)
1266 GtkCellRenderer *cell;
1267 GtkTreeViewColumn *column;
1270 GtkTreeModel *store;
1272 FilterEntry *filter;
1274 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1275 path = gtk_tree_path_new_from_indices(rows-1, -1);
1277 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1279 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1280 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1282 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1283 valid = gtk_tree_model_get_iter_first(store, &iter);
1287 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1289 if (g_strcmp0(filter->extensions, ".new") == 0)
1291 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1295 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1298 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1299 path, column, FALSE, 0.0, 0.0 );
1300 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1301 path, column, cell, TRUE);
1303 gtk_tree_path_free(path);
1304 g_list_free(list_cells);
1306 return(G_SOURCE_REMOVE);
1309 static void filter_add_cb(GtkWidget *, gpointer data)
1311 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1312 filter_store_populate();
1314 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1317 static void filter_remove_cb(GtkWidget *, gpointer data)
1319 auto filter_view = static_cast<GtkWidget *>(data);
1320 GtkTreeSelection *selection;
1324 if (!filter_store) return;
1325 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1326 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1327 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1330 filter_remove_entry(fe);
1332 filter_store_populate();
1335 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1339 GtkTreeViewColumn *column;
1341 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1342 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1343 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1345 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1349 gtk_tree_path_free(path);
1351 return(G_SOURCE_REMOVE);
1354 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1357 filter_add_defaults();
1359 filter_store_populate();
1361 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1364 static void dummy_cancel_cb(GenericDialog *, gpointer)
1366 /* no op, only so cancel button appears */
1369 static void filter_default_cb(GtkWidget *widget, gpointer data)
1373 gd = generic_dialog_new(_("Reset filters"),
1374 "reset_filter", widget, TRUE,
1375 dummy_cancel_cb, data);
1376 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1377 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1378 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1379 gtk_widget_show(gd->dialog);
1382 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1384 auto frame = static_cast<GtkWidget *>(data);
1386 gtk_widget_set_sensitive(frame,
1387 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1390 static void safe_delete_view_cb(GtkWidget *, gpointer)
1392 layout_set_path(nullptr, gq_gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1395 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1397 file_util_trash_clear();
1400 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1404 gd = generic_dialog_new(_("Clear trash"),
1405 "clear_trash", widget, TRUE,
1406 dummy_cancel_cb, nullptr);
1407 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1408 _("This will remove the trash contents."), FALSE);
1409 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1410 entry = gtk_entry_new();
1411 gtk_widget_set_can_focus(entry, FALSE);
1412 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1413 if (options->file_ops.safe_delete_path) gq_gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1414 gq_gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1415 gtk_widget_show(entry);
1416 gtk_widget_show(gd->dialog);
1419 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1421 GtkWidget *pTextView;
1422 GtkTextBuffer *pTextBuffer;
1426 pTextView = GTK_WIDGET(data);
1428 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1429 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1430 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1432 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1433 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1436 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1438 auto text_view = static_cast<GtkTextView *>(data);
1439 GtkTextBuffer *buffer;
1441 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1442 if (!configwindow) return;
1444 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1445 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1448 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1452 gd = generic_dialog_new(_("Reset image overlay template string"),
1453 "reset_image_overlay_template_string", widget, TRUE,
1454 dummy_cancel_cb, data);
1455 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1456 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1457 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1458 gtk_widget_show(gd->dialog);
1461 static void image_overlay_help_cb(GtkWidget *, gpointer)
1463 help_window_show("GuideOptionsOSD.html");
1466 static void font_activated_cb(GtkFontChooser *widget, gchar *fontname, gpointer)
1468 g_free(c_options->image_overlay.font);
1469 c_options->image_overlay.font = g_strdup(fontname);
1472 gq_gtk_widget_destroy(GTK_WIDGET(widget));
1475 static void font_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1479 g_free(c_options->image_overlay.font);
1480 c_options->image_overlay.font = g_strdup(options->image_overlay.font);
1482 if (response_id == GTK_RESPONSE_OK)
1484 font = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
1485 g_free(c_options->image_overlay.font);
1486 c_options->image_overlay.font = g_strdup(font);
1490 g_object_unref(dialog);
1493 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1497 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1498 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1499 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1501 g_signal_connect(dialog, "font-activated", G_CALLBACK(font_activated_cb), nullptr);
1502 g_signal_connect(dialog, "response", G_CALLBACK(font_response_cb), nullptr);
1504 gtk_widget_show(dialog);
1507 static void text_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1509 c_options->image_overlay.text_red = color->red * 255;
1510 c_options->image_overlay.text_green = color->green * 255;
1511 c_options->image_overlay.text_blue = color->blue * 255;
1512 c_options->image_overlay.text_alpha = color->alpha * 255;
1514 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1517 static void text_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1521 c_options->image_overlay.text_red = options->image_overlay.text_red;
1522 c_options->image_overlay.text_green = options->image_overlay.text_green;
1523 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
1524 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
1526 if (response_id == GTK_RESPONSE_OK)
1528 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1529 c_options->image_overlay.text_red = color.red * 255;
1530 c_options->image_overlay.text_green = color.green * 255;
1531 c_options->image_overlay.text_blue = color.blue * 255;
1532 c_options->image_overlay.text_alpha = color.alpha * 255;
1535 g_object_unref(dialog);
1538 static void image_overlay_set_text_color_cb(GtkWidget *widget, gpointer)
1543 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Text Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1544 color.red = options->image_overlay.text_red;
1545 color.green = options->image_overlay.text_green;
1546 color.blue = options->image_overlay.text_blue;
1547 color.alpha = options->image_overlay.text_alpha;
1548 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1549 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1551 g_signal_connect(dialog, "color-activated", G_CALLBACK(text_color_activated_cb), nullptr);
1552 g_signal_connect(dialog, "response", G_CALLBACK(text_color_response_cb), nullptr);
1554 gtk_widget_show(dialog);
1557 static void bg_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1559 c_options->image_overlay.background_red = color->red * 255;
1560 c_options->image_overlay.background_green = color->green * 255;
1561 c_options->image_overlay.background_blue = color->blue * 255;
1562 c_options->image_overlay.background_alpha = color->alpha * 255;
1564 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1567 static void bg_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1571 c_options->image_overlay.background_red = options->image_overlay.background_red;
1572 c_options->image_overlay.background_green = options->image_overlay.background_green;
1573 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
1574 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
1576 if (response_id == GTK_RESPONSE_OK)
1578 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1579 c_options->image_overlay.background_red = color.red * 255;
1580 c_options->image_overlay.background_green = color.green * 255;
1581 c_options->image_overlay.background_blue = color.blue * 255;
1582 c_options->image_overlay.background_alpha = color.alpha * 255;
1584 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1587 static void image_overlay_set_background_color_cb(GtkWidget *widget, gpointer)
1592 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Background Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1593 color.red = options->image_overlay.background_red;
1594 color.green = options->image_overlay.background_green;
1595 color.blue = options->image_overlay.background_blue;
1596 color.alpha = options->image_overlay.background_alpha;
1597 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1598 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1600 g_signal_connect(dialog, "color-activated", G_CALLBACK(bg_color_activated_cb), nullptr);
1601 g_signal_connect(dialog, "response", G_CALLBACK(bg_color_response_cb), nullptr);
1603 gtk_widget_show(dialog);
1606 static void accel_store_populate()
1612 const gchar *accel_path;
1616 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1618 gtk_tree_store_clear(accel_store);
1619 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 */
1621 g_assert(lw && lw->ui_manager);
1622 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1625 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1628 action = GTK_ACTION(actions->data);
1629 accel_path = gtk_action_get_accel_path(action);
1630 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1636 g_object_get(action,
1637 "tooltip", &tooltip,
1641 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1647 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1651 gtk_tree_store_append(accel_store, &iter, nullptr);
1652 gtk_tree_store_set(accel_store, &iter,
1655 AE_TOOLTIP, tooltip ? tooltip : "",
1656 AE_ACCEL, accel_path,
1664 actions = actions->next;
1667 groups = groups->next;
1671 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1676 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1678 auto accel1 = static_cast<gchar *>(data);
1683 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1685 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1686 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1688 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1690 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1691 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1700 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1702 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1706 GtkAccelKey old_key;
1708 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1710 gtk_tree_model_get_iter(model, &iter, path);
1711 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1713 /* test if the accelerator can be stored without conflicts*/
1714 gtk_accel_map_lookup_entry(accel_path, &old_key);
1716 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1717 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1718 gtk_accel_map_lookup_entry(accel_path, &key);
1720 /* restore the original for now, the key will be really changed when the changes are confirmed */
1721 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1723 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1724 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1726 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1727 gtk_tree_path_free(path);
1731 static gboolean accel_default_scroll(GtkTreeView *data)
1735 GtkTreeViewColumn *column;
1737 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1738 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1739 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1741 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1745 gtk_tree_path_free(path);
1747 return(G_SOURCE_REMOVE);
1750 static void accel_default_cb(GtkWidget *, gpointer data)
1752 accel_store_populate();
1754 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1757 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1759 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1762 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1768 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1769 gtk_accel_map_lookup_entry(accel_path, &key);
1770 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1772 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1774 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1779 static void accel_clear_cb(GtkWidget *, gpointer data)
1781 GtkTreeSelection *selection;
1783 if (!accel_store) return;
1784 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1785 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1788 static void accel_reset_cb(GtkWidget *, gpointer data)
1790 GtkTreeSelection *selection;
1792 if (!accel_store) return;
1793 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1794 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1799 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1803 GtkWidget *scrolled;
1804 GtkWidget *viewport;
1806 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
1807 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1808 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1809 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1810 label = gtk_label_new(title);
1811 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1812 gtk_widget_show(scrolled);
1814 viewport = gtk_viewport_new(nullptr, nullptr);
1815 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1816 gq_gtk_container_add(GTK_WIDGET(scrolled), viewport);
1817 gtk_widget_show(viewport);
1819 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1820 gq_gtk_container_add(GTK_WIDGET(viewport), vbox);
1821 gtk_widget_show(vbox);
1826 static void cache_standard_cb(GtkWidget *widget, gpointer)
1828 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1830 c_options->thumbnails.spec_standard =TRUE;
1831 c_options->thumbnails.cache_into_dirs = FALSE;
1835 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1837 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1839 c_options->thumbnails.spec_standard =FALSE;
1840 c_options->thumbnails.cache_into_dirs = FALSE;
1844 static void cache_local_cb(GtkWidget *widget, gpointer)
1846 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1848 c_options->thumbnails.cache_into_dirs = TRUE;
1849 c_options->thumbnails.spec_standard =FALSE;
1853 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1855 if (pos == GTK_ENTRY_ICON_PRIMARY)
1857 gq_gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1861 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "");
1865 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1867 gchar *rating_symbol;
1869 if (pos == GTK_ENTRY_ICON_PRIMARY)
1871 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1872 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1873 g_free(rating_symbol);
1877 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1878 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1879 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1883 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1885 gchar *rating_symbol;
1887 if (pos == GTK_ENTRY_ICON_PRIMARY)
1889 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1890 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1891 g_free(rating_symbol);
1895 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1896 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1897 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1901 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1903 auto hbox = static_cast<GtkContainer *>(data);
1904 GString *str = g_string_new(nullptr);
1905 GtkEntry *hex_code_entry;
1906 gchar *hex_code_full;
1909 guint64 hex_value = 0;
1911 list = gtk_container_get_children(hbox);
1913 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1914 hex_code_full = g_strdup(gq_gtk_entry_get_text(hex_code_entry));
1916 hex_code = g_strsplit(hex_code_full, "+", 2);
1917 if (hex_code[0] && hex_code[1])
1919 hex_value = strtoull(hex_code[1], nullptr, 16);
1921 if (!hex_value || hex_value > 0x10FFFF)
1923 hex_value = 0x003F; // Unicode 'Question Mark'
1925 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1926 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1928 g_strfreev(hex_code);
1929 g_string_free(str, TRUE);
1930 g_free(hex_code_full);
1935 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1937 guint64 star_symbol;
1939 star_symbol = star_rating_symbol_test(widget, data);
1940 c_options->star_rating.star = star_symbol;
1943 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1945 guint64 rejected_symbol;
1947 rejected_symbol = star_rating_symbol_test(widget, data);
1948 c_options->star_rating.rejected = rejected_symbol;
1951 /* general options tab */
1952 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1956 GCancellable *cancellable;
1958 GtkWidget *progress;
1960 GFile *timezone_database_gq;
1961 gchar *timezone_database_user;
1964 static void config_tab_general(GtkWidget *notebook)
1969 GtkWidget *group_frame;
1970 GtkWidget *subgroup;
1972 GtkWidget *ct_button;
1979 GtkWidget *star_rating_entry;
1981 gchar *rating_symbol;
1984 gchar *download_locn;
1985 GNetworkMonitor *net_mon;
1986 GSocketConnectable *tz_org;
1987 gboolean internet_available = FALSE;
1990 vbox = scrolled_notebook_page(notebook, _("General"));
1992 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1994 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1995 add_thumb_size_menu(table, 0, 0, _("Size:"));
1996 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1998 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1999 pref_label_new(hbox, _("Custom size: "));
2000 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
2001 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
2003 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
2004 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
2006 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2007 pref_checkbox_link_sensitivity(ct_button, subgroup);
2009 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
2010 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
2011 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
2012 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2013 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
2014 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2015 G_CALLBACK(cache_geeqie_cb), nullptr);
2017 group_frame = pref_frame_new(subgroup, TRUE,
2018 _("Store thumbnails local to image folder (non-standard)"),
2019 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2020 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
2021 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
2022 G_CALLBACK(cache_local_cb), nullptr);
2024 group_frame = pref_frame_new(subgroup, TRUE,
2025 _("Use standard thumbnail style and cache, shared with other applications"),
2026 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2027 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2028 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2029 G_CALLBACK(cache_standard_cb), nullptr);
2031 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2032 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2034 pref_checkbox_new_int(group, _("Thumbnail color management"),
2035 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2037 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2039 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2040 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2042 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
2043 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2044 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2047 pref_spacer(group, PREF_PAD_GROUP);
2049 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2051 c_options->star_rating.star = options->star_rating.star;
2052 c_options->star_rating.rejected = options->star_rating.rejected;
2054 str = g_string_new(nullptr);
2055 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2056 pref_label_new(hbox, _("Star character: "));
2057 str = g_string_append_unichar(str, options->star_rating.star);
2058 pref_label_new(hbox, g_strdup(str->str));
2059 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2060 star_rating_entry = gtk_entry_new();
2061 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2062 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2063 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2064 gtk_widget_show(star_rating_entry);
2065 button = pref_button_new(nullptr, nullptr, _("Set"),
2066 G_CALLBACK(star_rating_star_test_cb), hbox);
2067 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2068 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2069 gtk_widget_show(button);
2070 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."));
2071 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2072 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2073 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2074 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2075 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2076 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2077 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2078 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2079 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2080 G_CALLBACK(star_rating_star_icon_cb),
2083 g_string_free(str, TRUE);
2084 g_free(rating_symbol);
2086 str = g_string_new(nullptr);
2087 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2088 pref_label_new(hbox, _("Rejected character: "));
2089 str = g_string_append_unichar(str, options->star_rating.rejected);
2090 pref_label_new(hbox, g_strdup(str->str));
2091 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2092 star_rating_entry = gtk_entry_new();
2093 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2094 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2095 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2096 gtk_widget_show(star_rating_entry);
2097 button = pref_button_new(nullptr, nullptr, _("Set"),
2098 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2099 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2100 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2101 gtk_widget_show(button);
2102 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."));
2103 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2104 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2105 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2106 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2107 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2108 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2109 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2110 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2111 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2112 G_CALLBACK(star_rating_rejected_icon_cb),
2115 g_string_free(str, TRUE);
2116 g_free(rating_symbol);
2118 pref_spacer(group, PREF_PAD_GROUP);
2120 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2122 c_options->slideshow.delay = options->slideshow.delay;
2123 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2124 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2125 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2126 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2127 SLIDESHOW_SUBSECOND_PRECISION;
2129 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2131 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2133 options->slideshow.delay ? hours : 0.0,
2134 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2135 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2136 spin = pref_spin_new(hbox, ":" , nullptr,
2138 options->slideshow.delay ? minutes: 0.0,
2139 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2140 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2141 spin = pref_spin_new(hbox, ":", nullptr,
2142 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2143 options->slideshow.delay ? seconds : 10.0,
2144 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2145 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2147 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2148 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2150 pref_spacer(group, PREF_PAD_GROUP);
2152 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2154 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2155 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2156 pref_checkbox_new_int(group, _("Preload next image"),
2157 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2159 pref_checkbox_new_int(group, _("Refresh on file change"),
2160 options->update_on_time_change, &c_options->update_on_time_change);
2163 pref_spacer(group, PREF_PAD_GROUP);
2165 group = pref_group_new(vbox, FALSE, _("Menu style"), GTK_ORIENTATION_VERTICAL);
2167 pref_checkbox_new_int(group, _("☰ style menu button (NOTE! Geeqie must be restarted for change to take effect)"),
2168 options->hamburger_menu, &c_options->hamburger_menu);
2169 gtk_widget_set_tooltip_text(group, _("Use a ☰ style menu button instead of the classic style across the top of the frame"));
2171 pref_spacer(group, PREF_PAD_GROUP);
2173 group = pref_group_new(vbox, FALSE, _("Expand toolbar"), GTK_ORIENTATION_VERTICAL);
2175 pref_checkbox_new_int(group, _("Expand menu/toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2176 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2177 gtk_widget_set_tooltip_text(group, _("Expand the menu/toolbar to the full width of the window"));
2179 pref_spacer(group, PREF_PAD_GROUP);
2181 group = pref_group_new(vbox, FALSE, _("Hide Selectable Bars"), GTK_ORIENTATION_VERTICAL);
2183 pref_checkbox_new_int(group, _("Menu bar"),
2184 options->selectable_bars.menu_bar, &c_options->selectable_bars.menu_bar);
2186 pref_checkbox_new_int(group, _("Tool bar"),
2187 options->selectable_bars.tool_bar, &c_options->selectable_bars.tool_bar);
2189 pref_checkbox_new_int(group, _("Status bar"),
2190 options->selectable_bars.status_bar, &c_options->selectable_bars.status_bar);
2191 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"));
2193 pref_spacer(group, PREF_PAD_GROUP);
2195 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2197 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2198 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2199 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2200 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"));
2202 pref_spacer(group, PREF_PAD_GROUP);
2206 net_mon = g_network_monitor_get_default();
2207 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2210 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2211 g_object_unref(tz_org);
2214 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2215 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2217 if (!internet_available)
2219 gtk_widget_set_sensitive(group, FALSE);
2222 tz = g_new0(TZData, 1);
2224 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2225 basename = g_path_get_basename(path);
2226 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2230 if (isfile(tz->timezone_database_user))
2232 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2236 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2239 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2240 pref_label_new(GTK_WIDGET(hbox), download_locn);
2241 g_free(download_locn);
2243 if (!internet_available)
2245 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"));
2249 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"));
2251 gtk_widget_show(button);
2253 pref_spacer(group, PREF_PAD_GROUP);
2255 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2257 help_search_engine_entry = gtk_entry_new();
2258 gq_gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2259 gq_gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2260 gtk_widget_show(help_search_engine_entry);
2262 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"));
2264 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2265 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2266 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2267 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2268 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2269 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2270 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2271 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2272 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2273 G_CALLBACK(help_search_engine_entry_icon_cb),
2274 help_search_engine_entry);
2278 static void config_tab_image(GtkWidget *notebook)
2283 GtkWidget *ct_button;
2284 GtkWidget *enlargement_button;
2288 vbox = scrolled_notebook_page(notebook, _("Image"));
2290 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2292 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2293 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2295 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2296 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2298 c_options->image.zoom_increment = options->image.zoom_increment;
2299 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2300 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2301 G_CALLBACK(zoom_increment_cb), nullptr);
2302 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2304 c_options->image.zoom_style = options->image.zoom_style;
2305 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2306 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2308 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2310 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2311 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2312 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2313 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2315 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2316 pref_checkbox_link_sensitivity(enlargement_button, spin);
2317 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."));
2319 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2320 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2321 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2322 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2324 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2325 pref_checkbox_link_sensitivity(ct_button, spin);
2326 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."));
2328 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2330 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2331 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2333 options->image.tile_size, &c_options->image.tile_size);
2334 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."));
2336 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2338 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2339 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2341 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2342 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2344 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2345 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2347 c_options->image.border_color = options->image.border_color;
2349 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2350 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2352 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2353 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2355 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2356 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2361 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2363 LayoutWindow *lw = nullptr;
2364 gchar *default_path;
2367 /* Get current lw */
2370 tmp_id = lw->options.id;
2371 lw->options.id = g_strdup("");
2373 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2374 save_default_layout_options_to_file(default_path, options, lw);
2375 g_free(lw->options.id);
2376 lw->options.id = tmp_id;
2377 g_free(default_path);
2380 static gboolean popover_cb(gpointer data)
2382 auto popover = static_cast<GtkPopover *>(data);
2384 gtk_popover_popdown(popover);
2389 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2391 gtk_popover_popup(popover);
2393 g_timeout_add(2000, popover_cb, popover);
2396 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2400 popover = gtk_popover_new(parent);
2401 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2402 gq_gtk_container_add(GTK_WIDGET(popover), child);
2403 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2404 gtk_widget_show (child);
2409 static void config_tab_windows(GtkWidget *notebook)
2414 GtkWidget *subgroup;
2416 GtkWidget *checkbox;
2417 GtkWidget *ct_button;
2420 vbox = scrolled_notebook_page(notebook, _("Windows"));
2422 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2424 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2425 options->save_window_positions, &c_options->save_window_positions);
2427 checkbox = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2428 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2429 pref_checkbox_link_sensitivity(ct_button, checkbox);
2431 checkbox = pref_checkbox_new_int(group, _("Remember window workspace"),
2432 options->save_window_workspace, &c_options->save_window_workspace);
2433 pref_checkbox_link_sensitivity(ct_button, checkbox);
2435 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2436 options->tools_restore_state, &c_options->tools_restore_state);
2438 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2439 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2441 pref_checkbox_new_int(group, _("Show window IDs"),
2442 options->show_window_ids, &c_options->show_window_ids);
2444 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2445 pref_label_new(subgroup, _("Use current layout for default: "));
2446 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2450 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2451 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2452 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2454 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2456 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2457 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2459 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2460 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2461 options->image.limit_window_size, &c_options->image.limit_window_size);
2462 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2464 options->image.max_window_size, &c_options->image.max_window_size);
2465 pref_checkbox_link_sensitivity(ct_button, spin);
2467 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2469 c_options->fullscreen.screen = options->fullscreen.screen;
2470 c_options->fullscreen.above = options->fullscreen.above;
2471 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2472 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2473 gtk_widget_show(hbox);
2475 pref_checkbox_new_int(group, _("Smooth image flip"),
2476 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2477 pref_checkbox_new_int(group, _("Disable screen saver"),
2478 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2482 PRE_FORMATTED_COLUMNS = 5
2484 static void config_tab_osd(GtkWidget *notebook)
2490 GtkWidget *image_overlay_template_view;
2491 GtkWidget *scrolled;
2492 GtkWidget *scrolled_pre_formatted;
2493 GtkTextBuffer *buffer;
2495 GtkWidget *subgroup;
2497 vbox = scrolled_notebook_page(notebook, _("OSD"));
2499 image_overlay_template_view = gtk_text_view_new();
2501 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2503 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2505 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2506 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2507 gq_gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2508 gtk_widget_show(scrolled_pre_formatted);
2509 gtk_widget_show(subgroup);
2511 pref_line(group, PREF_PAD_GAP);
2513 pref_label_new(group, _("Image overlay template"));
2515 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2516 gtk_widget_set_size_request(scrolled, 200, 150);
2517 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2518 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2519 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2520 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2521 gtk_widget_show(scrolled);
2523 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2524 _("Extensive formatting options are shown in the Help file"));
2526 gq_gtk_container_add(GTK_WIDGET(scrolled), image_overlay_template_view);
2527 gtk_widget_show(image_overlay_template_view);
2529 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2531 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2532 G_CALLBACK(image_overlay_set_font_cb), notebook);
2534 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2535 gtk_widget_show(button);
2537 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"), G_CALLBACK(image_overlay_set_text_color_cb), nullptr);
2538 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2539 gtk_widget_show(button);
2541 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"), G_CALLBACK(image_overlay_set_background_color_cb), nullptr);
2542 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2543 gtk_widget_show(button);
2544 image_overlay_set_text_colors();
2546 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2547 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2548 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2549 gtk_widget_show(button);
2551 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2552 G_CALLBACK(image_overlay_help_cb), nullptr);
2553 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2554 gtk_widget_show(button);
2556 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2557 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2558 g_signal_connect(G_OBJECT(buffer), "changed",
2559 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2561 pref_line(group, PREF_PAD_GAP);
2563 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2564 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2565 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2566 gtk_widget_show(hbox);
2567 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2568 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2569 gtk_widget_show(label);
2570 pref_spacer(group,TRUE);
2572 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2573 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2574 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2575 gtk_widget_show(hbox);
2576 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2577 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2578 gtk_widget_show(label);
2579 pref_spacer(group,TRUE);
2581 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2582 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2583 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2584 gtk_widget_show(hbox);
2585 label = gtk_label_new(_("%path:39%"));
2586 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2587 gtk_widget_show(label);
2588 pref_spacer(group,TRUE);
2590 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2591 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2592 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2593 gtk_widget_show(hbox);
2594 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%"));
2595 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2596 gtk_widget_show(label);
2597 pref_spacer(group,TRUE);
2599 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2600 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2601 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2602 gtk_widget_show(hbox);
2603 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2604 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2605 gtk_widget_show(label);
2608 static GtkTreeModel *create_class_model()
2610 GtkListStore *model;
2614 /* create list store */
2615 model = gtk_list_store_new(1, G_TYPE_STRING);
2616 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2618 gtk_list_store_append(model, &iter);
2619 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2621 return GTK_TREE_MODEL (model);
2626 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2628 gint n = GPOINTER_TO_INT(data);
2630 FilterEntry *filter_a;
2631 FilterEntry *filter_b;
2633 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2634 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2638 case FILETYPES_COLUMN_ENABLED:
2640 ret = filter_a->enabled - filter_b->enabled;
2643 case FILETYPES_COLUMN_FILTER:
2645 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2648 case FILETYPES_COLUMN_DESCRIPTION:
2650 ret = g_utf8_collate(filter_a->description, filter_b->description);
2653 case FILETYPES_COLUMN_CLASS:
2655 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2658 case FILETYPES_COLUMN_WRITABLE:
2660 ret = filter_a->writable - filter_b->writable;
2663 case FILETYPES_COLUMN_SIDECAR:
2665 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2669 g_return_val_if_reached(0);
2675 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2678 gboolean ret = TRUE;
2680 gtk_tree_model_get(model, iter, 0, &fe, -1);
2682 if (g_strstr_len(fe->extensions, -1, key))
2690 static void config_tab_files(GtkWidget *notebook)
2697 GtkWidget *ct_button;
2698 GtkWidget *scrolled;
2699 GtkWidget *filter_view;
2700 GtkCellRenderer *renderer;
2701 GtkTreeSelection *selection;
2702 GtkTreeViewColumn *column;
2704 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2706 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2708 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2709 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2710 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2711 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2712 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);
2713 pref_checkbox_new_int(group, _("Disable file extension checks"),
2714 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2716 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2717 options->file_filter.disable, &c_options->file_filter.disable);
2720 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2722 sidecar_ext_entry = gtk_entry_new();
2723 gq_gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2724 gq_gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2725 gtk_widget_show(sidecar_ext_entry);
2727 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2729 frame = pref_group_parent(group);
2730 g_signal_connect(G_OBJECT(ct_button), "toggled",
2731 G_CALLBACK(filter_disable_cb), frame);
2732 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2734 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2735 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2736 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2737 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2738 gtk_widget_show(scrolled);
2740 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2741 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2742 g_object_unref(filter_store);
2743 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2744 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2746 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2748 column = gtk_tree_view_column_new();
2749 gtk_tree_view_column_set_title(column, _("Enabled"));
2750 gtk_tree_view_column_set_resizable(column, TRUE);
2752 renderer = gtk_cell_renderer_toggle_new();
2753 g_signal_connect(G_OBJECT(renderer), "toggled",
2754 G_CALLBACK(filter_store_enable_cb), filter_store);
2755 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2756 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2757 GINT_TO_POINTER(FE_ENABLE), nullptr);
2758 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);
2759 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2760 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2762 column = gtk_tree_view_column_new();
2763 gtk_tree_view_column_set_title(column, _("Filter"));
2764 gtk_tree_view_column_set_resizable(column, TRUE);
2765 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);
2766 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2768 renderer = gtk_cell_renderer_text_new();
2769 g_signal_connect(G_OBJECT(renderer), "edited",
2770 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2771 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2772 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2773 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2774 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2775 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2777 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2778 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2779 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2781 column = gtk_tree_view_column_new();
2782 gtk_tree_view_column_set_title(column, _("Description"));
2783 gtk_tree_view_column_set_resizable(column, TRUE);
2784 gtk_tree_view_column_set_fixed_width(column, 200);
2785 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2787 renderer = gtk_cell_renderer_text_new();
2788 g_signal_connect(G_OBJECT(renderer), "edited",
2789 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2790 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2791 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2792 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2793 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2794 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2795 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);
2796 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2798 column = gtk_tree_view_column_new();
2799 gtk_tree_view_column_set_title(column, _("Class"));
2800 gtk_tree_view_column_set_resizable(column, TRUE);
2801 renderer = gtk_cell_renderer_combo_new();
2802 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2803 "model", create_class_model(),
2808 g_signal_connect(G_OBJECT(renderer), "edited",
2809 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2810 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2811 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2812 GINT_TO_POINTER(FE_CLASS), nullptr);
2813 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2814 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);
2815 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2817 column = gtk_tree_view_column_new();
2818 gtk_tree_view_column_set_title(column, _("Writable"));
2819 gtk_tree_view_column_set_resizable(column, FALSE);
2820 renderer = gtk_cell_renderer_toggle_new();
2821 g_signal_connect(G_OBJECT(renderer), "toggled",
2822 G_CALLBACK(filter_store_writable_cb), filter_store);
2823 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2824 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2825 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2826 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2827 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);
2828 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2830 column = gtk_tree_view_column_new();
2831 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2832 gtk_tree_view_column_set_resizable(column, FALSE);
2833 renderer = gtk_cell_renderer_toggle_new();
2834 g_signal_connect(G_OBJECT(renderer), "toggled",
2835 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2836 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2837 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2838 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2839 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2840 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);
2841 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2843 filter_store_populate();
2844 gq_gtk_container_add(GTK_WIDGET(scrolled), filter_view);
2845 gtk_widget_show(filter_view);
2847 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2849 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2850 G_CALLBACK(filter_default_cb), filter_view);
2851 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2852 gtk_widget_show(button);
2854 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2855 G_CALLBACK(filter_remove_cb), filter_view);
2856 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2857 gtk_widget_show(button);
2859 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2860 G_CALLBACK(filter_add_cb), filter_view);
2861 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2862 gtk_widget_show(button);
2866 static void config_tab_metadata(GtkWidget *notebook)
2871 GtkWidget *ct_button;
2873 GtkWidget *tmp_widget;
2875 GtkWidget *text_label;
2877 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2880 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2882 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2884 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."));
2885 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2886 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2888 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2890 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2891 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2892 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"));
2893 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2895 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)"));
2896 gtk_widget_set_tooltip_markup(ct_button, markup);
2900 gtk_widget_set_sensitive(ct_button, FALSE);
2903 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2904 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2905 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)"));
2906 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2909 label = pref_label_new(group, "");
2910 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(), "/");
2911 gtk_label_set_markup (GTK_LABEL(label), markup);
2914 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2915 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2916 gtk_widget_set_margin_start(label, 22);
2917 pref_spacer(group, PREF_PAD_GROUP);
2919 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2921 gtk_widget_set_sensitive(group, FALSE);
2924 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2925 pref_checkbox_link_sensitivity(ct_button, hbox);
2927 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);
2928 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2930 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);
2932 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2934 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2935 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2936 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2937 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", ")");
2938 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2941 pref_spacer(group, PREF_PAD_GROUP);
2943 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2945 gtk_widget_set_sensitive(group, FALSE);
2948 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);
2950 pref_spacer(group, PREF_PAD_GROUP);
2952 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2953 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);
2954 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2956 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2957 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2959 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2960 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"));
2963 gtk_widget_set_sensitive(ct_button, FALSE);
2966 pref_spacer(group, PREF_PAD_GROUP);
2968 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2970 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2972 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2973 pref_checkbox_link_sensitivity(ct_button, hbox);
2975 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2977 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2979 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2981 pref_spacer(group, PREF_PAD_GROUP);
2984 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2986 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2987 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2990 pref_spacer(group, PREF_PAD_GROUP);
2992 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2994 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2995 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");
3000 struct KeywordFindData
3007 GtkWidget *button_close;
3008 GtkWidget *button_stop;
3009 GtkWidget *button_start;
3010 GtkWidget *progress;
3018 guint idle_id; /* event source id */
3022 KEYWORD_DIALOG_WIDTH = 400
3025 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
3027 GList *list_d = nullptr;
3028 GList *list_f = nullptr;
3032 filelist_read(dir_fd, &list_f, &list_d);
3036 filelist_read(dir_fd, &list_f, nullptr);
3039 list_f = filelist_filter(list_f, FALSE);
3040 list_d = filelist_filter(list_d, TRUE);
3042 kfd->list = g_list_concat(list_f, kfd->list);
3043 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3046 static void keywords_find_reset(KeywordFindData *kfd)
3048 filelist_free(kfd->list);
3049 kfd->list = nullptr;
3051 filelist_free(kfd->list_dir);
3052 kfd->list_dir = nullptr;
3055 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3057 auto kfd = static_cast<KeywordFindData *>(data);
3059 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3061 keywords_find_reset(kfd);
3062 generic_dialog_close(kfd->gd);
3066 static void keywords_find_finish(KeywordFindData *kfd)
3068 keywords_find_reset(kfd);
3070 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3071 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
3073 gtk_widget_set_sensitive(kfd->group, TRUE);
3074 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3075 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3076 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3079 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3081 auto kfd = static_cast<KeywordFindData *>(data);
3083 g_idle_remove_by_data(kfd);
3085 keywords_find_finish(kfd);
3088 static gboolean keywords_find_file(gpointer data)
3090 auto kfd = static_cast<KeywordFindData *>(data);
3092 GtkTextBuffer *buffer;
3100 fd = static_cast<FileData *>(kfd->list->data);
3101 kfd->list = g_list_remove(kfd->list, fd);
3103 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3104 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3108 gtk_text_buffer_get_end_iter(buffer, &iter);
3109 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3110 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3112 keywords = keywords->next;
3115 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3116 file_data_unref(fd);
3117 g_list_free_full(keywords, g_free);
3119 return (G_SOURCE_CONTINUE);
3126 fd = static_cast<FileData *>(kfd->list_dir->data);
3127 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3129 keywords_find_folder(kfd, fd);
3131 file_data_unref(fd);
3133 return G_SOURCE_CONTINUE;
3136 keywords_find_finish(kfd);
3138 return G_SOURCE_REMOVE;
3141 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3143 auto kfd = static_cast<KeywordFindData *>(data);
3146 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3148 path = remove_trailing_slash((gq_gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3149 parse_out_relatives(path);
3153 warning_dialog(_("Invalid folder"),
3154 _("The specified folder can not be found."),
3155 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3161 gtk_widget_set_sensitive(kfd->group, FALSE);
3162 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3163 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3164 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3165 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3167 dir_fd = file_data_new_dir(path);
3168 keywords_find_folder(kfd, dir_fd);
3169 file_data_unref(dir_fd);
3170 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3176 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3178 KeywordFindData *kfd;
3182 kfd = g_new0(KeywordFindData, 1);
3184 kfd->gd = generic_dialog_new(_("Search for keywords"),
3185 "search_for_keywords",
3188 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3189 kfd->gd->cancel_cb = keywords_find_close_cb;
3190 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3191 keywords_find_close_cb, FALSE);
3192 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3193 keywords_find_start_cb, FALSE);
3194 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3195 keywords_find_stop_cb, FALSE);
3196 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3198 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3200 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3201 pref_spacer(hbox, PREF_PAD_INDENT);
3202 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3204 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3205 pref_label_new(hbox, _("Folder:"));
3207 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3208 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3209 gq_gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3210 gtk_widget_show(label);
3212 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3214 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3215 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3217 kfd->progress = gtk_entry_new();
3218 gtk_widget_set_can_focus(kfd->progress, FALSE);
3219 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3220 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3221 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3222 gtk_widget_show(kfd->progress);
3224 kfd->spinner = gtk_spinner_new();
3225 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3226 gtk_widget_show(kfd->spinner);
3228 kfd->list = nullptr;
3230 gtk_widget_show(kfd->gd->dialog);
3233 static void keywords_find_cb(GtkWidget *widget, gpointer)
3235 const gchar *path = layout_get_path(nullptr);
3237 if (!path || !*path) path = homedir();
3238 keywords_find_dialog(widget, path);
3241 static void config_tab_keywords_save()
3245 GtkTextBuffer *buffer;
3246 GList *kw_list = nullptr;
3252 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3253 gtk_text_buffer_get_bounds(buffer, &start, &end);
3255 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3257 kw_split = strtok(buffer_text, "\n");
3258 while (kw_split != nullptr)
3264 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3273 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3275 kw_split = strtok(nullptr, "\n");
3278 keyword_list_set(kw_list);
3280 g_list_free_full(kw_list, g_free);
3281 g_free(buffer_text);
3284 static void config_tab_keywords(GtkWidget *notebook)
3290 GtkWidget *scrolled;
3292 GtkTextBuffer *buffer;
3295 GspellTextView *gspell_view;
3298 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3300 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3302 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3304 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3305 G_CALLBACK(keywords_find_cb), keyword_text);
3306 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3309 keyword_text = gtk_text_view_new();
3310 gtk_widget_set_size_request(keyword_text, 20, 20);
3311 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3312 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3313 gtk_widget_show(scrolled);
3316 if (options->metadata.check_spelling)
3318 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3319 gspell_text_view_basic_setup(gspell_view);
3323 gq_gtk_container_add(GTK_WIDGET(scrolled), keyword_text);
3324 gtk_widget_show(keyword_text);
3326 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3328 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3329 gtk_text_buffer_create_tag(buffer, "monospace",
3330 "family", "monospace", NULL);
3332 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3333 gtk_text_buffer_get_start_iter(buffer, &iter);
3334 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3337 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3339 GList *kwl = keyword_list_get();
3340 kwl = g_list_first(kwl);
3343 gtk_text_buffer_get_end_iter (buffer, &iter);
3344 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3345 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3350 gtk_text_buffer_set_modified(buffer, FALSE);
3357 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3359 auto option = static_cast<gint *>(data);
3361 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3365 *option = INTENT_PERCEPTUAL;
3368 *option = INTENT_RELATIVE_COLORIMETRIC;
3371 *option = INTENT_SATURATION;
3374 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3379 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3380 gint option, gint *option_c)
3387 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3389 combo = gtk_combo_box_text_new();
3391 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3392 if (option == INTENT_PERCEPTUAL) current = 0;
3393 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3394 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3395 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3396 if (option == INTENT_SATURATION) current = 2;
3397 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3398 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3400 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3402 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3404 g_signal_connect(G_OBJECT(combo), "changed",
3405 G_CALLBACK(intent_menu_cb), option_c);
3407 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3408 gtk_widget_show(combo);
3412 static void config_tab_color(GtkWidget *notebook)
3421 vbox = scrolled_notebook_page(notebook, _("Color management"));
3423 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3425 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3428 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3429 gtk_grid_set_column_spacing(GTK_GRID(table), PREF_PAD_GAP);
3431 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3432 pref_label_bold(label, TRUE, FALSE);
3434 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3435 pref_label_bold(label, TRUE, FALSE);
3437 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3438 pref_label_bold(label, TRUE, FALSE);
3440 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3445 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3446 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3449 entry = gtk_entry_new();
3450 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3451 if (options->color_profile.input_name[i])
3453 gq_gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3455 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);
3456 gtk_widget_show(entry);
3457 color_profile_input_name_entry[i] = entry;
3459 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3460 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3461 gtk_widget_set_size_request(entry, 160, -1);
3462 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);
3463 gtk_widget_show(tabcomp);
3464 color_profile_input_file_entry[i] = entry;
3467 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3469 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3471 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3472 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3474 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3476 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3477 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3478 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3479 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3480 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3482 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3484 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);
3486 gtk_widget_show(tabcomp);
3489 /* advanced entry tab */
3490 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3492 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3494 c_options->file_ops.use_system_trash = FALSE;
3495 c_options->file_ops.no_trash = FALSE;
3499 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3501 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3503 c_options->file_ops.use_system_trash = TRUE;
3504 c_options->file_ops.no_trash = FALSE;
3508 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3510 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3512 c_options->file_ops.no_trash = TRUE;
3516 static void config_tab_behavior(GtkWidget *notebook)
3523 GtkWidget *ct_button;
3527 GtkWidget *with_rename;
3528 GtkWidget *collections_on_top;
3529 GtkWidget *hide_window_in_fullscreen;
3530 GtkWidget *checkbox;
3533 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3535 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3537 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3538 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3539 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3540 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3541 pref_checkbox_new_int(group, _("Enable Delete key"),
3542 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3544 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3545 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3547 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3548 pref_checkbox_link_sensitivity(ct_button, hbox);
3550 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3551 pref_label_new(hbox, _("Folder:"));
3553 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3554 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3555 gq_gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3556 gtk_widget_show(tabcomp);
3558 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3559 pref_checkbox_link_sensitivity(ct_button, hbox);
3561 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3562 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3563 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3564 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3565 button = pref_button_new(nullptr, nullptr, _("View"),
3566 G_CALLBACK(safe_delete_view_cb), nullptr);
3567 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3568 gtk_widget_show(button);
3570 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3571 G_CALLBACK(safe_delete_clear_cb), nullptr);
3572 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3574 c_options->file_ops.no_trash = options->file_ops.no_trash;
3575 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3577 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3578 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3580 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3581 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3583 gtk_widget_show(button);
3585 pref_spacer(group, PREF_PAD_GROUP);
3588 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3590 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3591 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3593 pref_checkbox_new_int(group, _("In place renaming"),
3594 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3596 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3597 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3599 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3600 options->circular_selection_lists, &c_options->circular_selection_lists);
3601 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3603 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3604 options->marks_save, &c_options->marks_save);
3605 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3607 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3608 options->with_rename, &c_options->with_rename);
3609 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3611 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3612 options->collections_duplicates, &c_options->collections_duplicates);
3613 gtk_widget_set_tooltip_text(collections_on_top,"Allow the same image to be in a Collection more than once");
3615 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3616 options->collections_on_top, &c_options->collections_on_top);
3617 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3619 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3620 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3621 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3623 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3624 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3626 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);
3627 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."));
3629 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3630 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3632 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3633 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3635 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3636 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3638 pref_spacer(group, PREF_PAD_GROUP);
3640 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3642 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3643 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3644 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3645 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3646 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3647 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3648 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3649 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3650 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3651 options->image_l_click_archive, &c_options->image_l_click_archive);
3652 pref_checkbox_new_int(group, _("Play video by left click on image"),
3653 options->image_l_click_video, &c_options->image_l_click_video);
3654 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3655 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3657 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3658 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3659 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3660 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3661 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3663 pref_spacer(group, PREF_PAD_GROUP);
3665 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3667 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3668 options->override_disable_gpu, &c_options->override_disable_gpu);
3669 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3672 pref_spacer(group, PREF_PAD_GROUP);
3674 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3676 pref_spin_new_int(group, _("Debug level:"), nullptr,
3677 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3679 pref_checkbox_new_int(group, _("Timer data"),
3680 options->log_window.timer_data, &c_options->log_window.timer_data);
3682 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3683 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3685 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3686 pref_label_new(hbox, _("Log Window F1 command: "));
3687 log_window_f1_entry = gtk_entry_new();
3688 gq_gtk_entry_set_text(GTK_ENTRY(log_window_f1_entry), options->log_window.action);
3689 gq_gtk_box_pack_start(GTK_BOX(hbox), log_window_f1_entry, FALSE, FALSE, 0);
3690 gtk_entry_set_width_chars(GTK_ENTRY(log_window_f1_entry), 15);
3691 gtk_widget_show(log_window_f1_entry);
3695 /* accelerators tab */
3697 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3699 gboolean ret = TRUE;
3704 gtk_tree_model_get(model, iter, column, &text, -1);
3705 text_nocase = g_utf8_casefold(text, -1);
3706 key_nocase = g_utf8_casefold(key, -1);
3708 if (g_strstr_len(text_nocase, -1, key_nocase))
3715 g_free(text_nocase);
3720 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3725 list = gtk_tree_view_get_columns(tree_view);
3726 col_num = g_list_index(list, column);
3730 gtk_tree_view_set_search_column(tree_view, col_num);
3733 static void config_tab_accelerators(GtkWidget *notebook)
3739 GtkWidget *scrolled;
3740 GtkWidget *accel_view;
3741 GtkCellRenderer *renderer;
3742 GtkTreeSelection *selection;
3743 GtkTreeViewColumn *column;
3745 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3747 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3749 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3750 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3751 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3752 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3753 gtk_widget_show(scrolled);
3755 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3757 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3758 g_object_unref(accel_store);
3759 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3760 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3762 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3764 renderer = gtk_cell_renderer_text_new();
3766 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3771 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3772 gtk_tree_view_column_set_resizable(column, TRUE);
3773 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3776 renderer = gtk_cell_renderer_accel_new();
3777 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3778 G_CALLBACK(accel_store_cleared_cb), accel_store);
3779 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3780 G_CALLBACK(accel_store_edited_cb), accel_store);
3783 g_object_set (renderer,
3785 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3788 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3793 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3794 gtk_tree_view_column_set_resizable(column, TRUE);
3795 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3797 renderer = gtk_cell_renderer_text_new();
3799 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3804 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3805 gtk_tree_view_column_set_resizable(column, TRUE);
3806 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3808 renderer = gtk_cell_renderer_text_new();
3810 column = gtk_tree_view_column_new_with_attributes("Accel",
3815 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3816 gtk_tree_view_column_set_resizable(column, TRUE);
3817 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3819 /* Search on text in column */
3820 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3821 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3822 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3823 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3824 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3826 accel_store_populate();
3827 gq_gtk_container_add(GTK_WIDGET(scrolled), accel_view);
3828 gtk_widget_show(accel_view);
3830 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3832 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3833 G_CALLBACK(accel_default_cb), accel_view);
3834 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3835 gtk_widget_show(button);
3837 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3838 G_CALLBACK(accel_reset_cb), accel_view);
3839 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3840 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3841 gtk_widget_show(button);
3843 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3844 G_CALLBACK(accel_clear_cb), accel_view);
3845 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3846 gtk_widget_show(button);
3849 /* toolbar main tab */
3850 static void config_tab_toolbar_main(GtkWidget *notebook)
3853 GtkWidget *toolbardata;
3856 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3858 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3860 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3861 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3862 gtk_widget_show(vbox);
3865 /* toolbar status tab */
3866 static void config_tab_toolbar_status(GtkWidget *notebook)
3869 GtkWidget *toolbardata;
3872 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3874 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3876 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3877 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3878 gtk_widget_show(vbox);
3882 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3884 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3887 static void config_tab_advanced(GtkWidget *notebook)
3890 GdkPixbufFormat *fm;
3892 GList *extensions_list = nullptr;
3893 GSList *formats_list;
3894 GString *types_string = g_string_new(nullptr);
3895 GtkWidget *alternate_checkbox;
3896 GtkWidget *dupes_threads_spin;
3898 GtkWidget *subgroup;
3900 GtkWidget *threads_string_label;
3901 GtkWidget *types_string_label;
3904 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3905 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3907 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3909 pref_spacer(group, PREF_PAD_GROUP);
3911 formats_list = gdk_pixbuf_get_formats();
3913 while (formats_list)
3915 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3916 extensions = gdk_pixbuf_format_get_extensions(fm);
3919 while (extensions[i])
3921 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3925 g_strfreev(extensions);
3926 formats_list = formats_list->next;
3929 while (extensions_list)
3931 if (types_string->len == 0)
3933 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3937 types_string = g_string_append(types_string, ", ");
3938 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3941 extensions_list = extensions_list->next;
3944 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3945 types_string_label = pref_label_new(group, types_string->str);
3946 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3948 pref_spacer(group, PREF_PAD_GROUP);
3950 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3951 external_preview_select_entry = gtk_entry_new();
3952 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3953 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3954 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3955 gtk_widget_show(tabcomp);
3957 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3958 external_preview_extract_entry = gtk_entry_new();
3959 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3960 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3961 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3962 gtk_widget_show(tabcomp);
3964 gtk_widget_show(vbox);
3966 g_slist_free(formats_list);
3967 g_list_free_full(extensions_list, g_free);
3968 g_string_free(types_string, TRUE);
3970 pref_spacer(group, PREF_PAD_GROUP);
3972 pref_line(vbox, PREF_PAD_SPACE);
3973 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3975 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.");
3976 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3978 pref_spacer(vbox, PREF_PAD_GROUP);
3980 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3981 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3983 pref_spacer(group, PREF_PAD_GROUP);
3985 pref_line(vbox, PREF_PAD_SPACE);
3987 group = pref_group_new(vbox, FALSE, _("Alternate similarity alogorithm"), GTK_ORIENTATION_VERTICAL);
3989 alternate_checkbox = pref_checkbox_new_int(group, _("Enable alternate similarity algorithm"), options->alternate_similarity_algorithm.enabled, &c_options->alternate_similarity_algorithm.enabled);
3991 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3992 pref_checkbox_link_sensitivity(alternate_checkbox, subgroup);
3994 alternate_checkbox = pref_checkbox_new_int(subgroup, _("Use grayscale"), options->alternate_similarity_algorithm.grayscale, &c_options->alternate_similarity_algorithm.grayscale);
3995 gtk_widget_set_tooltip_text(alternate_checkbox, _("Reduce fingerprint to grayscale"));
3999 static void config_tab_stereo(GtkWidget *notebook)
4007 GtkWidget *fs_button;
4008 vbox = scrolled_notebook_page(notebook, _("Stereo"));
4010 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
4012 table = pref_table_new(group, 2, 1, FALSE, FALSE);
4013 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
4015 table = pref_table_new(group, 2, 2, TRUE, FALSE);
4016 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4017 pref_checkbox_new_int(box, _("Mirror left image"),
4018 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
4019 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4020 pref_checkbox_new_int(box, _("Flip left image"),
4021 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
4022 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4023 pref_checkbox_new_int(box, _("Mirror right image"),
4024 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
4025 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4026 pref_checkbox_new_int(box, _("Flip right image"),
4027 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
4028 pref_checkbox_new_int(group, _("Swap left and right images"),
4029 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
4030 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
4031 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
4033 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
4034 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
4035 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
4036 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4037 pref_checkbox_link_sensitivity(fs_button, box2);
4038 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
4039 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
4040 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
4041 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4042 pref_checkbox_new_int(box, _("Mirror left image"),
4043 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
4044 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4045 pref_checkbox_new_int(box, _("Flip left image"),
4046 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
4047 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4048 pref_checkbox_new_int(box, _("Mirror right image"),
4049 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
4050 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4051 pref_checkbox_new_int(box, _("Flip right image"),
4052 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
4053 pref_checkbox_new_int(box2, _("Swap left and right images"),
4054 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
4055 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
4056 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
4058 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
4059 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
4060 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
4061 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
4062 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
4063 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
4064 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
4065 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
4066 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
4067 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4068 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
4069 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4070 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
4071 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4075 /* Main preferences window */
4076 static void config_window_create(LayoutWindow *lw)
4078 GtkWidget *win_vbox;
4080 GtkWidget *notebook;
4082 GtkWidget *ct_button;
4084 if (!c_options) c_options = init_options(nullptr);
4086 configwindow = window_new("preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4087 DEBUG_NAME(configwindow);
4088 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4089 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4090 G_CALLBACK(config_window_delete), NULL);
4091 if (options->save_dialog_window_positions)
4093 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4094 gq_gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4098 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4100 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4101 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4103 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4104 gq_gtk_container_add(GTK_WIDGET(configwindow), win_vbox);
4105 gtk_widget_show(win_vbox);
4107 notebook = gtk_notebook_new();
4108 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4109 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4110 gq_gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4112 config_tab_general(notebook);
4113 config_tab_image(notebook);
4114 config_tab_osd(notebook);
4115 config_tab_windows(notebook);
4116 config_tab_accelerators(notebook);
4117 config_tab_files(notebook);
4118 config_tab_metadata(notebook);
4119 config_tab_keywords(notebook);
4120 config_tab_color(notebook);
4121 config_tab_stereo(notebook);
4122 config_tab_behavior(notebook);
4123 config_tab_toolbar_main(notebook);
4124 config_tab_toolbar_status(notebook);
4125 config_tab_advanced(notebook);
4127 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4129 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4130 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4131 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4132 gq_gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4133 gtk_widget_show(hbox);
4135 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4136 G_CALLBACK(config_window_help_cb), notebook);
4137 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4138 gtk_widget_set_can_default(button, TRUE);
4139 gtk_widget_show(button);
4141 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4142 G_CALLBACK(config_window_ok_cb), notebook);
4143 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4144 gtk_widget_set_can_default(button, TRUE);
4145 gtk_widget_grab_default(button);
4146 gtk_widget_show(button);
4150 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4151 G_CALLBACK(config_window_close_cb), nullptr);
4152 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4153 gtk_widget_set_can_default(button, TRUE);
4154 gtk_widget_show(button);
4156 if (!generic_dialog_get_alternative_button_order(configwindow))
4158 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4161 gtk_widget_show(notebook);
4163 gtk_widget_show(configwindow);
4167 *-----------------------------------------------------------------------------
4168 * config window show (public)
4169 *-----------------------------------------------------------------------------
4172 void show_config_window(LayoutWindow *lw)
4176 gtk_window_present(GTK_WINDOW(configwindow));
4180 config_window_create(lw);
4189 void show_about_window(LayoutWindow *lw)
4191 GDataInputStream *data_stream;
4192 GInputStream *in_stream_authors;
4193 GInputStream *in_stream_translators;
4195 GdkPixbuf *pixbuf_icon;
4196 GdkPixbuf *pixbuf_logo;
4200 gchar *authors[1000];
4202 gchar *timezone_path;
4211 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");
4213 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4214 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4216 cd = ZDOpenDatabase(timezone_path);
4219 copyright = g_string_append(copyright, ZDGetNotice(cd));
4223 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4225 ZDCloseDatabase(cd);
4227 g_free(timezone_path);
4229 copyright = g_string_append(copyright, "\n\nSome icons by https://www.flaticon.com");
4231 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4233 data_stream = g_data_input_stream_new(in_stream_authors);
4235 authors[0] = nullptr;
4236 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4238 authors[i_authors] = g_strdup(author_line);
4240 g_free(author_line);
4242 authors[i_authors] = nullptr;
4244 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4246 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4248 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4250 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4251 translators = static_cast<gchar *>(g_malloc0(size));
4252 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4253 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4255 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);
4257 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4258 artists[1] = nullptr;
4260 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4261 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4262 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4263 "title", _("About Geeqie"),
4265 "program-name", GQ_APPNAME,
4267 "logo", pixbuf_logo,
4268 "icon", pixbuf_icon,
4269 "website", GQ_WEBSITE,
4270 "website-label", "Website",
4271 "comments", comment,
4274 "translator-credits", translators,
4275 "wrap-license", TRUE,
4276 "license", copyright->str,
4279 g_string_free(copyright, TRUE);
4281 while(n < i_authors)
4289 g_free(translators);
4290 g_object_unref(data_stream);
4291 g_object_unref(in_stream_authors);
4292 g_object_unref(in_stream_translators);
4295 static void image_overlay_set_text_colors()
4297 c_options->image_overlay.text_red = options->image_overlay.text_red;
4298 c_options->image_overlay.text_green = options->image_overlay.text_green;
4299 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4300 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4301 c_options->image_overlay.background_red = options->image_overlay.background_red;
4302 c_options->image_overlay.background_green = options->image_overlay.background_green;
4303 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4304 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4308 *-----------------------------------------------------------------------------
4309 * timezone database routines
4310 *-----------------------------------------------------------------------------
4313 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4315 GError *error = nullptr;
4316 auto tz = static_cast<TZData *>(data);
4317 gchar *tmp_filename;
4318 gchar *timezone_bin;
4319 gchar *tmp_dir = nullptr;
4322 if (!g_cancellable_is_cancelled(tz->cancellable))
4324 generic_dialog_close(tz->gd);
4328 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4330 tmp_filename = g_file_get_path(tz->tmp_g_file);
4331 fd = file_data_new_simple(tmp_filename);
4332 tmp_dir = open_archive(fd);
4336 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4337 if (isfile(timezone_bin))
4339 move_file(timezone_bin, tz->timezone_database_user);
4343 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4346 g_free(timezone_bin);
4347 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4351 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4353 g_free(tmp_filename);
4354 file_data_unref(fd);
4358 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4361 g_file_delete(tz->tmp_g_file, nullptr, &error);
4362 g_object_unref(tz->tmp_g_file);
4363 tz->tmp_g_file = nullptr;
4364 g_object_unref(tz->cancellable);
4365 g_object_unref(tz->timezone_database_gq);
4368 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4370 auto tz = static_cast<TZData *>(data);
4372 if (!g_cancellable_is_cancelled(tz->cancellable))
4374 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4378 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4380 auto tz = static_cast<TZData *>(data);
4382 g_cancellable_cancel(tz->cancellable);
4385 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4387 auto tz = static_cast<TZData *>(data);
4388 GError *error = nullptr;
4389 GFileIOStream *io_stream;
4396 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4400 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4401 log_printf("Error: Download timezone database failed:\n%s", error->message);
4402 g_error_free(error);
4403 g_object_unref(tz->tmp_g_file);
4407 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4409 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4411 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4413 tz->progress = gtk_progress_bar_new();
4414 gq_gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4415 gtk_widget_show(tz->progress);
4417 gtk_widget_show(tz->gd->dialog);
4418 tz->cancellable = g_cancellable_new();
4419 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);
4421 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4424 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */