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.
22 #include "preferences.h"
29 #include <gdk-pixbuf/gdk-pixbuf.h>
32 #include <glib-object.h>
35 #include <gspell/gspell.h>
46 #include <pango/pango.h>
48 #include "bar-keywords.h"
50 #include "color-man.h"
55 #include "filefilter.h"
56 #include "fullscreen.h"
57 #include "image-overlay.h"
61 #include "layout-util.h"
63 #include "main-defines.h"
69 #include "pixbuf-util.h"
71 #include "slideshow.h"
72 #include "third-party/zonedetect.h"
76 #include "ui-fileops.h"
78 #include "ui-tabcomp.h"
79 #include "ui-utildlg.h"
86 EDITOR_NAME_MAX_LENGTH = 32,
87 EDITOR_COMMAND_MAX_LENGTH = 1024
90 static void image_overlay_set_text_colors();
92 GtkWidget *keyword_text;
93 static void config_tab_keywords_save();
101 static ThumbSize thumb_size_list[] =
136 FILETYPES_COLUMN_ENABLED = 0,
137 FILETYPES_COLUMN_FILTER,
138 FILETYPES_COLUMN_DESCRIPTION,
139 FILETYPES_COLUMN_CLASS,
140 FILETYPES_COLUMN_WRITABLE,
141 FILETYPES_COLUMN_SIDECAR,
142 FILETYPES_COLUMN_COUNT
145 const gchar *format_class_list[] = {
156 /* config memory values */
157 static ConfOptions *c_options = nullptr;
164 static GtkWidget *configwindow = nullptr;
165 static GtkListStore *filter_store = nullptr;
166 static GtkTreeStore *accel_store = nullptr;
168 static GtkWidget *safe_delete_path_entry;
170 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
171 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
172 static GtkWidget *color_profile_screen_file_entry;
173 static GtkWidget *external_preview_select_entry;
174 static GtkWidget *external_preview_extract_entry;
176 static GtkWidget *sidecar_ext_entry;
177 static GtkWidget *help_search_engine_entry;
179 static GtkWidget *log_window_f1_entry;
183 CONFIG_WINDOW_DEF_WIDTH = 700,
184 CONFIG_WINDOW_DEF_HEIGHT = 600
188 *-----------------------------------------------------------------------------
189 * option widget callbacks (private)
190 *-----------------------------------------------------------------------------
193 static void zoom_increment_cb(GtkWidget *spin, gpointer)
195 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
198 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
200 gint mins_secs_tenths;
203 mins_secs_tenths = c_options->slideshow.delay %
204 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
206 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
207 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
210 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
211 SLIDESHOW_SUBSECOND_PRECISION;
214 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
220 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
221 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
223 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
224 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
225 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
227 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
228 SLIDESHOW_SUBSECOND_PRECISION;
231 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
236 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
238 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
239 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
240 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
242 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
243 SLIDESHOW_SUBSECOND_PRECISION;
247 *-----------------------------------------------------------------------------
248 * sync program to config window routine (private)
249 *-----------------------------------------------------------------------------
252 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
258 buf = gq_gtk_entry_get_text(GTK_ENTRY(entry));
259 if (buf && strlen(buf) > 0)
264 *option = g_strdup(buf);
269 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
274 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
276 if (accel_path && accel_path[0])
279 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
280 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
290 static void config_window_apply()
292 gboolean refresh = FALSE;
297 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
299 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
300 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
301 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
302 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
303 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
304 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
306 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
307 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
308 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
309 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
310 options->file_ops.no_trash = c_options->file_ops.no_trash;
311 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
312 options->tools_restore_state = c_options->tools_restore_state;
313 options->save_window_positions = c_options->save_window_positions;
314 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
315 options->save_window_workspace = c_options->save_window_workspace;
316 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
317 options->show_window_ids = c_options->show_window_ids;
318 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
319 options->image.zoom_2pass = c_options->image.zoom_2pass;
320 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
321 options->image.limit_window_size = c_options->image.limit_window_size;
322 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
323 options->image.max_window_size = c_options->image.max_window_size;
324 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
325 options->image.max_autofit_size = c_options->image.max_autofit_size;
326 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
327 options->image.tile_size = c_options->image.tile_size;
328 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
329 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
331 if (options->thumbnails.max_width != c_options->thumbnails.max_width
332 || options->thumbnails.max_height != c_options->thumbnails.max_height
333 || options->thumbnails.quality != c_options->thumbnails.quality)
335 thumb_format_changed = TRUE;
337 options->thumbnails.max_width = c_options->thumbnails.max_width;
338 options->thumbnails.max_height = c_options->thumbnails.max_height;
339 options->thumbnails.quality = c_options->thumbnails.quality;
341 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
342 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
343 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
344 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
345 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
346 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
347 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
348 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
349 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
350 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
351 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
352 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
354 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
355 options->file_filter.disable = c_options->file_filter.disable;
357 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
358 sidecar_ext_parse(options->sidecar.ext);
360 options->slideshow.random = c_options->slideshow.random;
361 options->slideshow.repeat = c_options->slideshow.repeat;
362 options->slideshow.delay = c_options->slideshow.delay;
364 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
365 options->image_lm_click_nav = c_options->image_lm_click_nav;
366 options->image_l_click_archive = c_options->image_l_click_archive;
367 options->image_l_click_video = c_options->image_l_click_video;
368 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
370 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
372 options->image.tile_cache_max = c_options->image.tile_cache_max;
373 options->image.image_cache_max = c_options->image.image_cache_max;
375 options->image.zoom_quality = c_options->image.zoom_quality;
377 options->image.zoom_increment = c_options->image.zoom_increment;
379 options->image.zoom_style = c_options->image.zoom_style;
381 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
383 options->appimage_notifications = c_options->appimage_notifications;
386 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
387 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
388 || !gdk_rgba_equal(&options->image.border_color, &c_options->image.border_color))
390 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
391 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
392 options->image.border_color = c_options->image.border_color;
393 layout_colors_update();
394 view_window_colors_update();
397 options->image.alpha_color_1 = c_options->image.alpha_color_1;
398 options->image.alpha_color_2 = c_options->image.alpha_color_2;
400 options->fullscreen.screen = c_options->fullscreen.screen;
401 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
402 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
403 options->fullscreen.above = c_options->fullscreen.above;
404 if (c_options->image_overlay.template_string)
405 set_image_overlay_template_string(&options->image_overlay.template_string,
406 c_options->image_overlay.template_string);
407 if (c_options->image_overlay.font)
408 set_image_overlay_font_string(&options->image_overlay.font,
409 c_options->image_overlay.font);
410 options->image_overlay.text_red = c_options->image_overlay.text_red;
411 options->image_overlay.text_green = c_options->image_overlay.text_green;
412 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
413 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
414 options->image_overlay.background_red = c_options->image_overlay.background_red;
415 options->image_overlay.background_green = c_options->image_overlay.background_green;
416 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
417 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
418 options->image_overlay.font = c_options->image_overlay.font;
419 options->update_on_time_change = c_options->update_on_time_change;
421 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
422 options->rot_invariant_sim = c_options->rot_invariant_sim;
424 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
426 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
427 options->circular_selection_lists = c_options->circular_selection_lists;
429 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
430 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
431 options->dnd_icon_size = c_options->dnd_icon_size;
432 options->clipboard_selection = c_options->clipboard_selection;
433 options->dnd_default_action = c_options->dnd_default_action;
435 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
436 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
437 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
438 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
439 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
440 options->metadata.confirm_write = c_options->metadata.confirm_write;
441 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
442 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
443 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
444 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
445 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
446 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
447 options->metadata.write_orientation = c_options->metadata.write_orientation;
448 options->metadata.check_spelling = c_options->metadata.check_spelling;
449 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
450 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
451 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
452 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
453 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
454 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
455 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
456 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
457 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
458 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
459 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
460 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
461 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
462 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
463 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
464 options->stereo.fixed_w = c_options->stereo.fixed_w;
465 options->stereo.fixed_h = c_options->stereo.fixed_h;
466 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
467 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
468 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
469 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
471 options->info_keywords.height = c_options->info_keywords.height;
472 options->info_title.height = c_options->info_title.height;
473 options->info_comment.height = c_options->info_comment.height;
474 options->info_rating.height = c_options->info_rating.height;
476 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
477 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
478 options->hamburger_menu = c_options->hamburger_menu;
480 options->selectable_bars.menu_bar = c_options->selectable_bars.menu_bar;
481 options->selectable_bars.tool_bar = c_options->selectable_bars.tool_bar;
482 options->selectable_bars.status_bar = c_options->selectable_bars.status_bar;
484 options->marks_save = c_options->marks_save;
485 options->with_rename = c_options->with_rename;
486 options->collections_duplicates = c_options->collections_duplicates;
487 options->collections_on_top = c_options->collections_on_top;
488 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
489 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
491 options->external_preview.enable = c_options->external_preview.enable;
492 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
493 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
495 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
497 options->star_rating.star = c_options->star_rating.star;
498 options->star_rating.rejected = c_options->star_rating.rejected;
500 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
502 options->alternate_similarity_algorithm.enabled = c_options->alternate_similarity_algorithm.enabled;
503 options->alternate_similarity_algorithm.grayscale = c_options->alternate_similarity_algorithm.grayscale;
506 set_debug_level(debug_c);
507 config_entry_to_option(log_window_f1_entry, &options->log_window.action, nullptr);
511 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
513 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
514 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
516 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
517 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
518 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
520 options->color_profile.render_intent = c_options->color_profile.render_intent;
525 options->mouse_button_8 = c_options->mouse_button_8;
526 options->mouse_button_9 = c_options->mouse_button_9;
528 options->override_disable_gpu = c_options->override_disable_gpu;
530 config_tab_keywords_save();
532 image_options_sync();
537 layout_refresh(nullptr);
540 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
542 toolbar_apply(TOOLBAR_MAIN);
543 toolbar_apply(TOOLBAR_STATUS);
547 *-----------------------------------------------------------------------------
548 * config window main button callbacks (private)
549 *-----------------------------------------------------------------------------
552 static void config_window_close_cb(GtkWidget *, gpointer)
554 gq_gtk_widget_destroy(configwindow);
555 configwindow = nullptr;
556 filter_store = nullptr;
559 static void config_window_help_cb(GtkWidget *, gpointer data)
561 auto notebook = static_cast<GtkWidget *>(data);
564 static const gchar *html_section[] =
566 "GuideOptionsGeneral.html",
567 "GuideOptionsImage.html",
568 "GuideOptionsOSD.html",
569 "GuideOptionsWindow.html",
570 "GuideOptionsKeyboard.html",
571 "GuideOptionsFiltering.html",
572 "GuideOptionsMetadata.html",
573 "GuideOptionsKeywords.html",
574 "GuideOptionsColor.html",
575 "GuideOptionsStereo.html",
576 "GuideOptionsBehavior.html",
577 "GuideOptionsToolbar.html",
578 "GuideOptionsToolbar.html",
579 "GuideOptionsAdvanced.html"
582 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
583 help_window_show(html_section[i]);
586 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
588 config_window_close_cb(nullptr, nullptr);
592 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
595 auto notebook = static_cast<GtkNotebook *>(data);
603 lw = static_cast<LayoutWindow *>(layout_window_list->data);
605 window = gtk_widget_get_window(widget);
606 gdk_window_get_root_origin(window, &x, &y);
607 w = gdk_window_get_width(window);
608 h = gdk_window_get_height(window);
609 page_number = gtk_notebook_get_current_page(notebook);
611 lw->options.preferences_window.x = x;
612 lw->options.preferences_window.y = y;
613 lw->options.preferences_window.w = w;
614 lw->options.preferences_window.h = h;
615 lw->options.preferences_window.page_number = page_number;
617 config_window_apply();
618 layout_util_sync(lw);
619 save_options(options);
620 config_window_close_cb(nullptr, nullptr);
624 *-----------------------------------------------------------------------------
625 * config window setup (private)
626 *-----------------------------------------------------------------------------
629 static void quality_menu_cb(GtkWidget *combo, gpointer data)
631 auto option = static_cast<gint *>(data);
633 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
637 *option = GDK_INTERP_NEAREST;
640 *option = GDK_INTERP_TILES;
643 *option = GDK_INTERP_BILINEAR;
648 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
650 auto option = static_cast<gint *>(data);
652 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
656 *option = DND_ACTION_ASK;
659 *option = DND_ACTION_COPY;
662 *option = DND_ACTION_MOVE;
666 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
668 auto option = static_cast<gint *>(data);
670 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
673 *option = CLIPBOARD_PRIMARY;
676 *option = CLIPBOARD_CLIPBOARD;
679 *option = CLIPBOARD_BOTH;
682 *option = CLIPBOARD_BOTH;
686 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
687 guint option, guint *option_c)
694 pref_table_label(table, column, row, text, GTK_ALIGN_START);
696 combo = gtk_combo_box_text_new();
698 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
699 if (option == GDK_INTERP_NEAREST) current = 0;
700 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
701 if (option == GDK_INTERP_TILES) current = 1;
702 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
703 if (option == GDK_INTERP_BILINEAR) current = 2;
705 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
707 g_signal_connect(G_OBJECT(combo), "changed",
708 G_CALLBACK(quality_menu_cb), option_c);
710 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
711 gtk_widget_show(combo);
714 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
721 pref_table_label(table, column, row, text, GTK_ALIGN_START);
723 combo = gtk_combo_box_text_new();
725 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
726 if (option == DND_ACTION_ASK) current = 0;
727 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
728 if (option == DND_ACTION_COPY) current = 1;
729 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
730 if (option == DND_ACTION_MOVE) current = 2;
732 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
734 g_signal_connect(G_OBJECT(combo), "changed",
735 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
737 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
738 gtk_widget_show(combo);
741 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
742 gint option, gint *option_c)
749 pref_table_label(table, column, row, text, GTK_ALIGN_START);
751 combo = gtk_combo_box_text_new();
753 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
754 if (option == CLIPBOARD_PRIMARY) current = 0;
755 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
756 if (option == CLIPBOARD_CLIPBOARD) current = 1;
757 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
758 if (option == CLIPBOARD_BOTH) current = 2;
760 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
762 g_signal_connect(G_OBJECT(combo), "changed",
763 G_CALLBACK(clipboard_selection_menu_cb), option_c);
765 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
766 gtk_widget_show(combo);
769 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
771 auto option = static_cast<gint *>(data);
773 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
776 *option = ZOOM_GEOMETRIC;
779 *option = ZOOM_ARITHMETIC;
782 *option = ZOOM_GEOMETRIC;
786 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
793 pref_table_label(table, column, row, text, GTK_ALIGN_START);
795 combo = gtk_combo_box_text_new();
797 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
798 if (option == ZOOM_GEOMETRIC) current = 0;
799 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
800 if (option == ZOOM_ARITHMETIC) current = 1;
802 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
804 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
806 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
807 gtk_widget_show(combo);
810 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
812 ActionItem *action_item = nullptr;
813 auto option = static_cast<gchar **>(data);
818 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
820 list = get_action_items();
825 action_item = static_cast<ActionItem *>(work->data);
826 if (g_strcmp0(action_item->label, label) == 0)
834 *option = g_strdup(action_item->name);
836 action_items_free(list);
839 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
841 ActionItem *action_item;
850 pref_table_label(table, column, row, text, GTK_ALIGN_START);
852 combo = gtk_combo_box_text_new();
854 list = get_action_items();
858 action_item = static_cast<ActionItem *>(work->data);
859 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
861 if (g_strcmp0(action_item->name, option) == 0)
869 action_items_free(list);
871 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
873 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
875 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
876 gtk_widget_show(combo);
879 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
883 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
886 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
888 c_options->thumbnails.max_width = thumb_size_list[n].w;
889 c_options->thumbnails.max_height = thumb_size_list[n].h;
893 c_options->thumbnails.max_width = options->thumbnails.max_width;
894 c_options->thumbnails.max_height = options->thumbnails.max_height;
898 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
904 c_options->thumbnails.max_width = options->thumbnails.max_width;
905 c_options->thumbnails.max_height = options->thumbnails.max_height;
907 pref_table_label(table, column, row, text, GTK_ALIGN_START);
909 combo = gtk_combo_box_text_new();
912 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
918 w = thumb_size_list[i].w;
919 h = thumb_size_list[i].h;
921 buf = g_strdup_printf("%d x %d", w, h);
922 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
925 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
932 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
933 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
939 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
940 g_signal_connect(G_OBJECT(combo), "changed",
941 G_CALLBACK(thumb_size_menu_cb), NULL);
943 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
944 gtk_widget_show(combo);
947 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
949 auto option = static_cast<gint *>(data);
951 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
955 *option = PR_STEREO_NONE;
958 *option = PR_STEREO_ANAGLYPH_RC;
961 *option = PR_STEREO_ANAGLYPH_GM;
964 *option = PR_STEREO_ANAGLYPH_YB;
967 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
970 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
973 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
976 *option = PR_STEREO_ANAGLYPH_DB_RC;
979 *option = PR_STEREO_ANAGLYPH_DB_GM;
982 *option = PR_STEREO_ANAGLYPH_DB_YB;
985 *option = PR_STEREO_HORIZ;
988 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
991 *option = PR_STEREO_VERT;
994 *option = PR_STEREO_VERT | PR_STEREO_HALF;
997 *option = PR_STEREO_FIXED;
1002 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1003 gint option, gint *option_c, gboolean add_fixed)
1010 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1012 combo = gtk_combo_box_text_new();
1014 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
1016 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
1017 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
1018 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
1019 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
1020 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
1021 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
1022 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
1023 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
1024 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
1025 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
1026 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
1027 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
1028 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
1029 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
1030 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
1031 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
1032 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1033 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1035 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1036 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1037 if (option & PR_STEREO_HORIZ)
1040 if (option & PR_STEREO_HALF) current = 11;
1043 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1044 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1045 if (option & PR_STEREO_VERT)
1048 if (option & PR_STEREO_HALF) current = 13;
1053 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1054 if (option & PR_STEREO_FIXED) current = 14;
1057 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1059 g_signal_connect(G_OBJECT(combo), "changed",
1060 G_CALLBACK(stereo_mode_menu_cb), option_c);
1062 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1063 gtk_widget_show(combo);
1066 static void video_menu_cb(GtkWidget *combo, gpointer data)
1068 auto option = static_cast<gchar **>(data);
1070 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1074 static void video_menu_populate(gpointer data, gpointer user_data)
1076 auto combo = static_cast<GtkWidget *>(user_data);
1077 auto ed = static_cast<EditorDescription *>(data);
1079 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1082 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1083 gchar *option, gchar **option_c)
1087 /* use lists since they are sorted */
1088 GList *eds = editor_list_get();
1092 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1094 combo = gtk_combo_box_text_new();
1095 g_list_foreach(eds,video_menu_populate,combo);
1096 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1098 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1100 g_signal_connect(G_OBJECT(combo), "changed",
1101 G_CALLBACK(video_menu_cb), option_c);
1103 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1104 gtk_widget_show(combo);
1107 static void filter_store_populate()
1111 if (!filter_store) return;
1113 gtk_list_store_clear(filter_store);
1115 work = filter_get_list();
1121 fe = static_cast<FilterEntry *>(work->data);
1124 gtk_list_store_append(filter_store, &iter);
1125 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1129 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1131 auto model = static_cast<GtkWidget *>(data);
1132 auto fe = static_cast<FilterEntry *>(data);
1136 if (!new_text || strlen(new_text) < 1) return;
1138 tpath = gtk_tree_path_new_from_string(path_str);
1139 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1140 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1142 g_free(fe->extensions);
1143 fe->extensions = g_strdup(new_text);
1145 gtk_tree_path_free(tpath);
1149 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1151 auto model = static_cast<GtkWidget *>(data);
1152 auto fe = static_cast<FilterEntry *>(data);
1157 if (!new_text || !new_text[0]) return;
1159 tpath = gtk_tree_path_new_from_string(path_str);
1160 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1161 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1163 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1165 if (strcmp(new_text, _(format_class_list[i])) == 0)
1167 fe->file_class = static_cast<FileFormatClass>(i);
1172 gtk_tree_path_free(tpath);
1176 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1178 auto model = static_cast<GtkWidget *>(data);
1183 if (!new_text || !new_text[0]) return;
1185 tpath = gtk_tree_path_new_from_string(path_str);
1186 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1187 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1189 g_free(fe->description);
1190 fe->description = g_strdup(new_text);
1192 gtk_tree_path_free(tpath);
1195 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1197 auto model = static_cast<GtkWidget *>(data);
1202 tpath = gtk_tree_path_new_from_string(path_str);
1203 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1204 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1206 fe->enabled = !fe->enabled;
1208 gtk_tree_path_free(tpath);
1212 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1214 auto model = static_cast<GtkWidget *>(data);
1219 tpath = gtk_tree_path_new_from_string(path_str);
1220 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1221 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1223 fe->writable = !fe->writable;
1224 if (fe->writable) fe->allow_sidecar = FALSE;
1226 gtk_tree_path_free(tpath);
1230 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1232 auto model = static_cast<GtkWidget *>(data);
1237 tpath = gtk_tree_path_new_from_string(path_str);
1238 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1239 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1241 fe->allow_sidecar = !fe->allow_sidecar;
1242 if (fe->allow_sidecar) fe->writable = FALSE;
1244 gtk_tree_path_free(tpath);
1248 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1249 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1253 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1255 switch (GPOINTER_TO_INT(data))
1258 g_object_set(GTK_CELL_RENDERER(cell),
1259 "active", fe->enabled, NULL);
1262 g_object_set(GTK_CELL_RENDERER(cell),
1263 "text", fe->extensions, NULL);
1265 case FE_DESCRIPTION:
1266 g_object_set(GTK_CELL_RENDERER(cell),
1267 "text", fe->description, NULL);
1270 g_object_set(GTK_CELL_RENDERER(cell),
1271 "text", _(format_class_list[fe->file_class]), NULL);
1274 g_object_set(GTK_CELL_RENDERER(cell),
1275 "active", fe->writable, NULL);
1277 case FE_ALLOW_SIDECAR:
1278 g_object_set(GTK_CELL_RENDERER(cell),
1279 "active", fe->allow_sidecar, NULL);
1284 static gboolean filter_add_scroll(gpointer data)
1288 GtkCellRenderer *cell;
1289 GtkTreeViewColumn *column;
1292 GtkTreeModel *store;
1294 FilterEntry *filter;
1296 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1297 path = gtk_tree_path_new_from_indices(rows-1, -1);
1299 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1301 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1302 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1304 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1305 valid = gtk_tree_model_get_iter_first(store, &iter);
1309 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1311 if (g_strcmp0(filter->extensions, ".new") == 0)
1313 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1317 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1320 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1321 path, column, FALSE, 0.0, 0.0 );
1322 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1323 path, column, cell, TRUE);
1325 gtk_tree_path_free(path);
1326 g_list_free(list_cells);
1328 return(G_SOURCE_REMOVE);
1331 static void filter_add_cb(GtkWidget *, gpointer data)
1333 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1334 filter_store_populate();
1336 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1339 static void filter_remove_cb(GtkWidget *, gpointer data)
1341 auto filter_view = static_cast<GtkWidget *>(data);
1342 GtkTreeSelection *selection;
1346 if (!filter_store) return;
1347 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1348 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1349 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1352 filter_remove_entry(fe);
1354 filter_store_populate();
1357 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1361 GtkTreeViewColumn *column;
1363 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1364 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1365 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1367 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1371 gtk_tree_path_free(path);
1373 return(G_SOURCE_REMOVE);
1376 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1379 filter_add_defaults();
1381 filter_store_populate();
1383 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1386 static void dummy_cancel_cb(GenericDialog *, gpointer)
1388 /* no op, only so cancel button appears */
1391 static void filter_default_cb(GtkWidget *widget, gpointer data)
1395 gd = generic_dialog_new(_("Reset filters"),
1396 "reset_filter", widget, TRUE,
1397 dummy_cancel_cb, data);
1398 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1399 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1400 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1401 gtk_widget_show(gd->dialog);
1404 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1406 auto frame = static_cast<GtkWidget *>(data);
1408 gtk_widget_set_sensitive(frame,
1409 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1412 static void safe_delete_view_cb(GtkWidget *, gpointer)
1414 layout_set_path(nullptr, gq_gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1417 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1419 file_util_trash_clear();
1422 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1426 gd = generic_dialog_new(_("Clear trash"),
1427 "clear_trash", widget, TRUE,
1428 dummy_cancel_cb, nullptr);
1429 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1430 _("This will remove the trash contents."), FALSE);
1431 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1432 entry = gtk_entry_new();
1433 gtk_widget_set_can_focus(entry, FALSE);
1434 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1435 if (options->file_ops.safe_delete_path) gq_gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1436 gq_gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1437 gtk_widget_show(entry);
1438 gtk_widget_show(gd->dialog);
1441 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1443 GtkWidget *pTextView;
1444 GtkTextBuffer *pTextBuffer;
1448 pTextView = GTK_WIDGET(data);
1450 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1451 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1452 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1454 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1455 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1458 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1460 auto text_view = static_cast<GtkTextView *>(data);
1461 GtkTextBuffer *buffer;
1463 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1464 if (!configwindow) return;
1466 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1467 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1470 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1474 gd = generic_dialog_new(_("Reset image overlay template string"),
1475 "reset_image_overlay_template_string", widget, TRUE,
1476 dummy_cancel_cb, data);
1477 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1478 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1479 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1480 gtk_widget_show(gd->dialog);
1483 static void image_overlay_help_cb(GtkWidget *, gpointer)
1485 help_window_show("GuideOptionsOSD.html");
1488 static void font_activated_cb(GtkFontChooser *widget, gchar *fontname, gpointer)
1490 g_free(c_options->image_overlay.font);
1491 c_options->image_overlay.font = g_strdup(fontname);
1494 gq_gtk_widget_destroy(GTK_WIDGET(widget));
1497 static void font_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1501 g_free(c_options->image_overlay.font);
1502 c_options->image_overlay.font = g_strdup(options->image_overlay.font);
1504 if (response_id == GTK_RESPONSE_OK)
1506 font = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
1507 g_free(c_options->image_overlay.font);
1508 c_options->image_overlay.font = g_strdup(font);
1512 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1515 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1519 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1520 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1521 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1523 g_signal_connect(dialog, "font-activated", G_CALLBACK(font_activated_cb), nullptr);
1524 g_signal_connect(dialog, "response", G_CALLBACK(font_response_cb), nullptr);
1526 gtk_widget_show(dialog);
1529 static void text_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1531 c_options->image_overlay.text_red = color->red * 255;
1532 c_options->image_overlay.text_green = color->green * 255;
1533 c_options->image_overlay.text_blue = color->blue * 255;
1534 c_options->image_overlay.text_alpha = color->alpha * 255;
1536 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1539 static void text_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1543 c_options->image_overlay.text_red = options->image_overlay.text_red;
1544 c_options->image_overlay.text_green = options->image_overlay.text_green;
1545 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
1546 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
1548 if (response_id == GTK_RESPONSE_OK)
1550 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1551 c_options->image_overlay.text_red = color.red * 255;
1552 c_options->image_overlay.text_green = color.green * 255;
1553 c_options->image_overlay.text_blue = color.blue * 255;
1554 c_options->image_overlay.text_alpha = color.alpha * 255;
1557 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1560 static void image_overlay_set_text_color_cb(GtkWidget *widget, gpointer)
1565 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Text Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1566 color.red = options->image_overlay.text_red;
1567 color.green = options->image_overlay.text_green;
1568 color.blue = options->image_overlay.text_blue;
1569 color.alpha = options->image_overlay.text_alpha;
1570 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1571 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1573 g_signal_connect(dialog, "color-activated", G_CALLBACK(text_color_activated_cb), nullptr);
1574 g_signal_connect(dialog, "response", G_CALLBACK(text_color_response_cb), nullptr);
1576 gtk_widget_show(dialog);
1579 static void bg_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1581 c_options->image_overlay.background_red = color->red * 255;
1582 c_options->image_overlay.background_green = color->green * 255;
1583 c_options->image_overlay.background_blue = color->blue * 255;
1584 c_options->image_overlay.background_alpha = color->alpha * 255;
1586 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1589 static void bg_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1593 c_options->image_overlay.background_red = options->image_overlay.background_red;
1594 c_options->image_overlay.background_green = options->image_overlay.background_green;
1595 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
1596 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
1598 if (response_id == GTK_RESPONSE_OK)
1600 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1601 c_options->image_overlay.background_red = color.red * 255;
1602 c_options->image_overlay.background_green = color.green * 255;
1603 c_options->image_overlay.background_blue = color.blue * 255;
1604 c_options->image_overlay.background_alpha = color.alpha * 255;
1606 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1609 static void image_overlay_set_background_color_cb(GtkWidget *widget, gpointer)
1614 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Background Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1615 color.red = options->image_overlay.background_red;
1616 color.green = options->image_overlay.background_green;
1617 color.blue = options->image_overlay.background_blue;
1618 color.alpha = options->image_overlay.background_alpha;
1619 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1620 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1622 g_signal_connect(dialog, "color-activated", G_CALLBACK(bg_color_activated_cb), nullptr);
1623 g_signal_connect(dialog, "response", G_CALLBACK(bg_color_response_cb), nullptr);
1625 gtk_widget_show(dialog);
1628 static void accel_store_populate()
1634 const gchar *accel_path;
1638 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1640 gtk_tree_store_clear(accel_store);
1641 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 */
1643 g_assert(lw && lw->ui_manager);
1644 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1647 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1650 action = GTK_ACTION(actions->data);
1651 accel_path = gtk_action_get_accel_path(action);
1652 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1658 g_object_get(action,
1659 "tooltip", &tooltip,
1663 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1669 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1673 gtk_tree_store_append(accel_store, &iter, nullptr);
1674 gtk_tree_store_set(accel_store, &iter,
1677 AE_TOOLTIP, tooltip ? tooltip : "",
1678 AE_ACCEL, accel_path,
1686 actions = actions->next;
1689 groups = groups->next;
1693 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1698 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1700 auto accel1 = static_cast<gchar *>(data);
1705 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1707 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1708 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1710 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1712 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1713 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1722 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1724 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1728 GtkAccelKey old_key;
1730 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1732 gtk_tree_model_get_iter(model, &iter, path);
1733 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1735 /* test if the accelerator can be stored without conflicts*/
1736 gtk_accel_map_lookup_entry(accel_path, &old_key);
1738 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1739 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1740 gtk_accel_map_lookup_entry(accel_path, &key);
1742 /* restore the original for now, the key will be really changed when the changes are confirmed */
1743 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1745 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1746 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1748 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1749 gtk_tree_path_free(path);
1753 static gboolean accel_default_scroll(GtkTreeView *data)
1757 GtkTreeViewColumn *column;
1759 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1760 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1761 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1763 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1767 gtk_tree_path_free(path);
1769 return(G_SOURCE_REMOVE);
1772 static void accel_default_cb(GtkWidget *, gpointer data)
1774 accel_store_populate();
1776 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1779 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1781 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1784 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1790 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1791 gtk_accel_map_lookup_entry(accel_path, &key);
1792 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1794 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1796 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1801 static void accel_clear_cb(GtkWidget *, gpointer data)
1803 GtkTreeSelection *selection;
1805 if (!accel_store) return;
1806 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1807 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1810 static void accel_reset_cb(GtkWidget *, gpointer data)
1812 GtkTreeSelection *selection;
1814 if (!accel_store) return;
1815 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1816 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1821 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1825 GtkWidget *scrolled;
1826 GtkWidget *viewport;
1828 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
1829 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1830 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1831 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1832 label = gtk_label_new(title);
1833 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1834 gtk_widget_show(scrolled);
1836 viewport = gtk_viewport_new(nullptr, nullptr);
1837 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1838 gq_gtk_container_add(GTK_WIDGET(scrolled), viewport);
1839 gtk_widget_show(viewport);
1841 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1842 gq_gtk_container_add(GTK_WIDGET(viewport), vbox);
1843 gtk_widget_show(vbox);
1848 static void cache_standard_cb(GtkWidget *widget, gpointer)
1850 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1852 c_options->thumbnails.spec_standard =TRUE;
1853 c_options->thumbnails.cache_into_dirs = FALSE;
1857 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1859 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1861 c_options->thumbnails.spec_standard =FALSE;
1862 c_options->thumbnails.cache_into_dirs = FALSE;
1866 static void cache_local_cb(GtkWidget *widget, gpointer)
1868 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1870 c_options->thumbnails.cache_into_dirs = TRUE;
1871 c_options->thumbnails.spec_standard =FALSE;
1875 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1877 if (pos == GTK_ENTRY_ICON_PRIMARY)
1879 gq_gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1883 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "");
1887 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1889 gchar *rating_symbol;
1891 if (pos == GTK_ENTRY_ICON_PRIMARY)
1893 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1894 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1895 g_free(rating_symbol);
1899 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1900 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1901 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1905 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1907 gchar *rating_symbol;
1909 if (pos == GTK_ENTRY_ICON_PRIMARY)
1911 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1912 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1913 g_free(rating_symbol);
1917 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1918 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1919 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1923 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1925 auto hbox = static_cast<GtkContainer *>(data);
1926 GString *str = g_string_new(nullptr);
1927 GtkEntry *hex_code_entry;
1928 gchar *hex_code_full;
1931 guint64 hex_value = 0;
1933 list = gtk_container_get_children(hbox);
1935 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1936 hex_code_full = g_strdup(gq_gtk_entry_get_text(hex_code_entry));
1938 hex_code = g_strsplit(hex_code_full, "+", 2);
1939 if (hex_code[0] && hex_code[1])
1941 hex_value = strtoull(hex_code[1], nullptr, 16);
1943 if (!hex_value || hex_value > 0x10FFFF)
1945 hex_value = 0x003F; // Unicode 'Question Mark'
1947 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1948 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1950 g_strfreev(hex_code);
1951 g_string_free(str, TRUE);
1952 g_free(hex_code_full);
1957 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1959 guint64 star_symbol;
1961 star_symbol = star_rating_symbol_test(widget, data);
1962 c_options->star_rating.star = star_symbol;
1965 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1967 guint64 rejected_symbol;
1969 rejected_symbol = star_rating_symbol_test(widget, data);
1970 c_options->star_rating.rejected = rejected_symbol;
1973 /* general options tab */
1974 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1978 GCancellable *cancellable;
1980 GtkWidget *progress;
1982 GFile *timezone_database_gq;
1983 gchar *timezone_database_user;
1986 static void config_tab_general(GtkWidget *notebook)
1991 GtkWidget *group_frame;
1992 GtkWidget *subgroup;
1994 GtkWidget *ct_button;
2001 GtkWidget *star_rating_entry;
2003 gchar *rating_symbol;
2006 gchar *download_locn;
2007 GNetworkMonitor *net_mon;
2008 GSocketConnectable *tz_org;
2009 gboolean internet_available = FALSE;
2012 vbox = scrolled_notebook_page(notebook, _("General"));
2014 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
2016 table = pref_table_new(group, 2, 2, FALSE, FALSE);
2017 add_thumb_size_menu(table, 0, 0, _("Size:"));
2018 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
2020 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2021 pref_label_new(hbox, _("Custom size: "));
2022 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
2023 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
2025 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
2026 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
2028 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2029 pref_checkbox_link_sensitivity(ct_button, subgroup);
2031 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
2032 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
2033 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
2034 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2035 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
2036 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2037 G_CALLBACK(cache_geeqie_cb), nullptr);
2039 group_frame = pref_frame_new(subgroup, TRUE,
2040 _("Store thumbnails local to image folder (non-standard)"),
2041 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2042 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
2043 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
2044 G_CALLBACK(cache_local_cb), nullptr);
2046 group_frame = pref_frame_new(subgroup, TRUE,
2047 _("Use standard thumbnail style and cache, shared with other applications"),
2048 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2049 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2050 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2051 G_CALLBACK(cache_standard_cb), nullptr);
2053 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2054 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2056 pref_checkbox_new_int(group, _("Thumbnail color management"),
2057 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2059 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2061 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2062 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2064 #if HAVE_FFMPEGTHUMBNAILER_METADATA
2065 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2066 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2069 pref_spacer(group, PREF_PAD_GROUP);
2071 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2073 c_options->star_rating.star = options->star_rating.star;
2074 c_options->star_rating.rejected = options->star_rating.rejected;
2076 str = g_string_new(nullptr);
2077 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2078 pref_label_new(hbox, _("Star character: "));
2079 str = g_string_append_unichar(str, options->star_rating.star);
2080 pref_label_new(hbox, g_strdup(str->str));
2081 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2082 star_rating_entry = gtk_entry_new();
2083 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2084 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2085 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2086 gtk_widget_show(star_rating_entry);
2087 button = pref_button_new(nullptr, nullptr, _("Set"),
2088 G_CALLBACK(star_rating_star_test_cb), hbox);
2089 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2090 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2091 gtk_widget_show(button);
2092 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."));
2093 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2094 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2095 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2096 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2097 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2098 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2099 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2100 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2101 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2102 G_CALLBACK(star_rating_star_icon_cb),
2105 g_string_free(str, TRUE);
2106 g_free(rating_symbol);
2108 str = g_string_new(nullptr);
2109 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2110 pref_label_new(hbox, _("Rejected character: "));
2111 str = g_string_append_unichar(str, options->star_rating.rejected);
2112 pref_label_new(hbox, g_strdup(str->str));
2113 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2114 star_rating_entry = gtk_entry_new();
2115 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2116 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2117 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2118 gtk_widget_show(star_rating_entry);
2119 button = pref_button_new(nullptr, nullptr, _("Set"),
2120 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2121 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2122 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2123 gtk_widget_show(button);
2124 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."));
2125 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2126 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2127 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2128 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2129 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2130 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2131 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2132 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2133 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2134 G_CALLBACK(star_rating_rejected_icon_cb),
2137 g_string_free(str, TRUE);
2138 g_free(rating_symbol);
2140 pref_spacer(group, PREF_PAD_GROUP);
2142 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2144 c_options->slideshow.delay = options->slideshow.delay;
2145 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2146 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2147 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2148 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2149 SLIDESHOW_SUBSECOND_PRECISION;
2151 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2153 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2155 options->slideshow.delay ? hours : 0.0,
2156 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2157 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2158 spin = pref_spin_new(hbox, ":" , nullptr,
2160 options->slideshow.delay ? minutes: 0.0,
2161 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2162 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2163 spin = pref_spin_new(hbox, ":", nullptr,
2164 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2165 options->slideshow.delay ? seconds : 10.0,
2166 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2167 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2169 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2170 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2172 pref_spacer(group, PREF_PAD_GROUP);
2174 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2176 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2177 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2178 pref_checkbox_new_int(group, _("Preload next image"),
2179 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2181 pref_checkbox_new_int(group, _("Refresh on file change"),
2182 options->update_on_time_change, &c_options->update_on_time_change);
2185 pref_spacer(group, PREF_PAD_GROUP);
2187 group = pref_group_new(vbox, FALSE, _("Menu style"), GTK_ORIENTATION_VERTICAL);
2189 pref_checkbox_new_int(group, _("☰ style menu button (NOTE! Geeqie must be restarted for change to take effect)"),
2190 options->hamburger_menu, &c_options->hamburger_menu);
2191 gtk_widget_set_tooltip_text(group, _("Use a ☰ style menu button instead of the classic style across the top of the frame"));
2193 pref_spacer(group, PREF_PAD_GROUP);
2195 group = pref_group_new(vbox, FALSE, _("Expand toolbar"), GTK_ORIENTATION_VERTICAL);
2197 pref_checkbox_new_int(group, _("Expand menu/toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2198 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2199 gtk_widget_set_tooltip_text(group, _("Expand the menu/toolbar to the full width of the window"));
2201 pref_spacer(group, PREF_PAD_GROUP);
2203 group = pref_group_new(vbox, FALSE, _("Hide Selectable Bars"), GTK_ORIENTATION_VERTICAL);
2205 pref_checkbox_new_int(group, _("Menu bar"),
2206 options->selectable_bars.menu_bar, &c_options->selectable_bars.menu_bar);
2208 pref_checkbox_new_int(group, _("Tool bar"),
2209 options->selectable_bars.tool_bar, &c_options->selectable_bars.tool_bar);
2211 pref_checkbox_new_int(group, _("Status bar"),
2212 options->selectable_bars.status_bar, &c_options->selectable_bars.status_bar);
2213 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"));
2215 pref_spacer(group, PREF_PAD_GROUP);
2217 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2219 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2220 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2221 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2222 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"));
2224 pref_spacer(group, PREF_PAD_GROUP);
2228 net_mon = g_network_monitor_get_default();
2229 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2232 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2233 g_object_unref(tz_org);
2236 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2237 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2239 if (!internet_available)
2241 gtk_widget_set_sensitive(group, FALSE);
2244 tz = g_new0(TZData, 1);
2246 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2247 basename = g_path_get_basename(path);
2248 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2252 if (isfile(tz->timezone_database_user))
2254 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2258 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2261 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2262 pref_label_new(GTK_WIDGET(hbox), download_locn);
2263 g_free(download_locn);
2265 if (!internet_available)
2267 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"));
2271 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"));
2273 gtk_widget_show(button);
2275 pref_spacer(group, PREF_PAD_GROUP);
2277 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2279 help_search_engine_entry = gtk_entry_new();
2280 gq_gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2281 gq_gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2282 gtk_widget_show(help_search_engine_entry);
2284 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"));
2286 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2287 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2288 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2289 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2290 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2291 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2292 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2293 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2294 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2295 G_CALLBACK(help_search_engine_entry_icon_cb),
2296 help_search_engine_entry);
2300 static void config_tab_image(GtkWidget *notebook)
2305 GtkWidget *ct_button;
2306 GtkWidget *enlargement_button;
2310 vbox = scrolled_notebook_page(notebook, _("Image"));
2312 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2314 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2315 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2317 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2318 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2320 c_options->image.zoom_increment = options->image.zoom_increment;
2321 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2322 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2323 G_CALLBACK(zoom_increment_cb), nullptr);
2324 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2326 c_options->image.zoom_style = options->image.zoom_style;
2327 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2328 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2330 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2332 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2333 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2334 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2335 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2337 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2338 pref_checkbox_link_sensitivity(enlargement_button, spin);
2339 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."));
2341 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2342 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (%% of actual window):"),
2343 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2344 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2346 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2347 pref_checkbox_link_sensitivity(ct_button, spin);
2348 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."));
2350 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2352 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2353 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2355 options->image.tile_size, &c_options->image.tile_size);
2356 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."));
2358 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2360 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2361 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2363 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2364 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2366 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2367 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2369 c_options->image.border_color = options->image.border_color;
2371 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2372 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2374 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2375 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2377 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2378 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2383 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2385 LayoutWindow *lw = nullptr;
2386 gchar *default_path;
2389 /* Get current lw */
2392 tmp_id = lw->options.id;
2393 lw->options.id = g_strdup("");
2395 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2396 save_default_layout_options_to_file(default_path, options, lw);
2397 g_free(lw->options.id);
2398 lw->options.id = tmp_id;
2399 g_free(default_path);
2402 static gboolean popover_cb(gpointer data)
2404 auto popover = static_cast<GtkPopover *>(data);
2406 gtk_popover_popdown(popover);
2411 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2413 gtk_popover_popup(popover);
2415 g_timeout_add(2000, popover_cb, popover);
2418 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2422 popover = gtk_popover_new(parent);
2423 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2424 gq_gtk_container_add(GTK_WIDGET(popover), child);
2425 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2426 gtk_widget_show (child);
2431 static void config_tab_windows(GtkWidget *notebook)
2436 GtkWidget *subgroup;
2438 GtkWidget *checkbox;
2439 GtkWidget *ct_button;
2442 vbox = scrolled_notebook_page(notebook, _("Windows"));
2444 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2446 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2447 options->save_window_positions, &c_options->save_window_positions);
2449 checkbox = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2450 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2451 pref_checkbox_link_sensitivity(ct_button, checkbox);
2453 checkbox = pref_checkbox_new_int(group, _("Remember window workspace"),
2454 options->save_window_workspace, &c_options->save_window_workspace);
2455 pref_checkbox_link_sensitivity(ct_button, checkbox);
2457 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2458 options->tools_restore_state, &c_options->tools_restore_state);
2460 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2461 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2463 pref_checkbox_new_int(group, _("Show window IDs"),
2464 options->show_window_ids, &c_options->show_window_ids);
2466 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2467 pref_label_new(subgroup, _("Use current layout for default: "));
2468 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2472 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2473 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2474 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2476 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2478 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2479 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2481 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2482 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2483 options->image.limit_window_size, &c_options->image.limit_window_size);
2484 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2486 options->image.max_window_size, &c_options->image.max_window_size);
2487 pref_checkbox_link_sensitivity(ct_button, spin);
2489 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2491 c_options->fullscreen.screen = options->fullscreen.screen;
2492 c_options->fullscreen.above = options->fullscreen.above;
2493 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2494 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2495 gtk_widget_show(hbox);
2497 pref_checkbox_new_int(group, _("Smooth image flip"),
2498 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2499 pref_checkbox_new_int(group, _("Disable screen saver"),
2500 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2504 PRE_FORMATTED_COLUMNS = 5
2506 static void config_tab_osd(GtkWidget *notebook)
2512 GtkWidget *image_overlay_template_view;
2513 GtkWidget *scrolled;
2514 GtkWidget *scrolled_pre_formatted;
2515 GtkTextBuffer *buffer;
2517 GtkWidget *subgroup;
2519 vbox = scrolled_notebook_page(notebook, _("OSD"));
2521 image_overlay_template_view = gtk_text_view_new();
2523 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2525 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2527 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2528 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2529 gq_gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2530 gtk_widget_show(scrolled_pre_formatted);
2531 gtk_widget_show(subgroup);
2533 pref_line(group, PREF_PAD_GAP);
2535 pref_label_new(group, _("Image overlay template"));
2537 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2538 gtk_widget_set_size_request(scrolled, 200, 150);
2539 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2540 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2541 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2542 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2543 gtk_widget_show(scrolled);
2545 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2546 _("Extensive formatting options are shown in the Help file"));
2548 gq_gtk_container_add(GTK_WIDGET(scrolled), image_overlay_template_view);
2549 gtk_widget_show(image_overlay_template_view);
2551 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2553 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2554 G_CALLBACK(image_overlay_set_font_cb), notebook);
2556 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2557 gtk_widget_show(button);
2559 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"), G_CALLBACK(image_overlay_set_text_color_cb), nullptr);
2560 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2561 gtk_widget_show(button);
2563 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"), G_CALLBACK(image_overlay_set_background_color_cb), nullptr);
2564 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2565 gtk_widget_show(button);
2566 image_overlay_set_text_colors();
2568 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2569 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2570 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2571 gtk_widget_show(button);
2573 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2574 G_CALLBACK(image_overlay_help_cb), nullptr);
2575 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2576 gtk_widget_show(button);
2578 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2579 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2580 g_signal_connect(G_OBJECT(buffer), "changed",
2581 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2583 pref_line(group, PREF_PAD_GAP);
2585 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2586 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2587 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2588 gtk_widget_show(hbox);
2589 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2590 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2591 gtk_widget_show(label);
2592 pref_spacer(group,TRUE);
2594 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2595 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2596 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2597 gtk_widget_show(hbox);
2598 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2599 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2600 gtk_widget_show(label);
2601 pref_spacer(group,TRUE);
2603 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2604 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2605 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2606 gtk_widget_show(hbox);
2607 label = gtk_label_new(_("%path:39%"));
2608 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2609 gtk_widget_show(label);
2610 pref_spacer(group,TRUE);
2612 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2613 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2614 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2615 gtk_widget_show(hbox);
2616 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%"));
2617 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2618 gtk_widget_show(label);
2619 pref_spacer(group,TRUE);
2621 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2622 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2623 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2624 gtk_widget_show(hbox);
2625 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2626 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2627 gtk_widget_show(label);
2630 static GtkTreeModel *create_class_model()
2632 GtkListStore *model;
2636 /* create list store */
2637 model = gtk_list_store_new(1, G_TYPE_STRING);
2638 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2640 gtk_list_store_append(model, &iter);
2641 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2643 return GTK_TREE_MODEL (model);
2648 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2650 gint n = GPOINTER_TO_INT(data);
2652 FilterEntry *filter_a;
2653 FilterEntry *filter_b;
2655 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2656 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2660 case FILETYPES_COLUMN_ENABLED:
2662 ret = filter_a->enabled - filter_b->enabled;
2665 case FILETYPES_COLUMN_FILTER:
2667 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2670 case FILETYPES_COLUMN_DESCRIPTION:
2672 ret = g_utf8_collate(filter_a->description, filter_b->description);
2675 case FILETYPES_COLUMN_CLASS:
2677 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2680 case FILETYPES_COLUMN_WRITABLE:
2682 ret = filter_a->writable - filter_b->writable;
2685 case FILETYPES_COLUMN_SIDECAR:
2687 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2691 g_return_val_if_reached(0);
2697 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2700 gboolean ret = TRUE;
2702 gtk_tree_model_get(model, iter, 0, &fe, -1);
2704 if (g_strstr_len(fe->extensions, -1, key))
2712 static void config_tab_files(GtkWidget *notebook)
2719 GtkWidget *ct_button;
2720 GtkWidget *scrolled;
2721 GtkWidget *filter_view;
2722 GtkCellRenderer *renderer;
2723 GtkTreeSelection *selection;
2724 GtkTreeViewColumn *column;
2726 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2728 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2730 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2731 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2732 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2733 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2734 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);
2735 pref_checkbox_new_int(group, _("Disable file extension checks"),
2736 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2738 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2739 options->file_filter.disable, &c_options->file_filter.disable);
2742 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2744 sidecar_ext_entry = gtk_entry_new();
2745 gq_gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2746 gq_gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2747 gtk_widget_show(sidecar_ext_entry);
2749 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2751 frame = pref_group_parent(group);
2752 g_signal_connect(G_OBJECT(ct_button), "toggled",
2753 G_CALLBACK(filter_disable_cb), frame);
2754 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2756 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2757 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2758 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2759 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2760 gtk_widget_show(scrolled);
2762 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2763 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2764 g_object_unref(filter_store);
2765 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2766 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2768 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2770 column = gtk_tree_view_column_new();
2771 gtk_tree_view_column_set_title(column, _("Enabled"));
2772 gtk_tree_view_column_set_resizable(column, TRUE);
2774 renderer = gtk_cell_renderer_toggle_new();
2775 g_signal_connect(G_OBJECT(renderer), "toggled",
2776 G_CALLBACK(filter_store_enable_cb), filter_store);
2777 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2778 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2779 GINT_TO_POINTER(FE_ENABLE), nullptr);
2780 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);
2781 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2782 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2784 column = gtk_tree_view_column_new();
2785 gtk_tree_view_column_set_title(column, _("Filter"));
2786 gtk_tree_view_column_set_resizable(column, TRUE);
2787 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);
2788 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2790 renderer = gtk_cell_renderer_text_new();
2791 g_signal_connect(G_OBJECT(renderer), "edited",
2792 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2793 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2794 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2795 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2796 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2797 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2799 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2800 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2801 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2803 column = gtk_tree_view_column_new();
2804 gtk_tree_view_column_set_title(column, _("Description"));
2805 gtk_tree_view_column_set_resizable(column, TRUE);
2806 gtk_tree_view_column_set_fixed_width(column, 200);
2807 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2809 renderer = gtk_cell_renderer_text_new();
2810 g_signal_connect(G_OBJECT(renderer), "edited",
2811 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2812 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2813 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2814 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2815 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2816 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2817 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);
2818 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2820 column = gtk_tree_view_column_new();
2821 gtk_tree_view_column_set_title(column, _("Class"));
2822 gtk_tree_view_column_set_resizable(column, TRUE);
2823 renderer = gtk_cell_renderer_combo_new();
2824 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2825 "model", create_class_model(),
2830 g_signal_connect(G_OBJECT(renderer), "edited",
2831 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2832 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2833 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2834 GINT_TO_POINTER(FE_CLASS), nullptr);
2835 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2836 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);
2837 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2839 column = gtk_tree_view_column_new();
2840 gtk_tree_view_column_set_title(column, _("Writable"));
2841 gtk_tree_view_column_set_resizable(column, FALSE);
2842 renderer = gtk_cell_renderer_toggle_new();
2843 g_signal_connect(G_OBJECT(renderer), "toggled",
2844 G_CALLBACK(filter_store_writable_cb), filter_store);
2845 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2846 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2847 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2848 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2849 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);
2850 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2852 column = gtk_tree_view_column_new();
2853 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2854 gtk_tree_view_column_set_resizable(column, FALSE);
2855 renderer = gtk_cell_renderer_toggle_new();
2856 g_signal_connect(G_OBJECT(renderer), "toggled",
2857 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2858 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2859 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2860 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2861 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2862 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);
2863 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2865 filter_store_populate();
2866 gq_gtk_container_add(GTK_WIDGET(scrolled), filter_view);
2867 gtk_widget_show(filter_view);
2869 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2871 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2872 G_CALLBACK(filter_default_cb), filter_view);
2873 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2874 gtk_widget_show(button);
2876 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2877 G_CALLBACK(filter_remove_cb), filter_view);
2878 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2879 gtk_widget_show(button);
2881 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2882 G_CALLBACK(filter_add_cb), filter_view);
2883 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2884 gtk_widget_show(button);
2888 static void config_tab_metadata(GtkWidget *notebook)
2893 GtkWidget *ct_button;
2895 GtkWidget *tmp_widget;
2897 GtkWidget *text_label;
2899 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2902 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2904 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2906 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."));
2907 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2908 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2910 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2912 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2913 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2914 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"));
2915 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2917 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)"));
2918 gtk_widget_set_tooltip_markup(ct_button, markup);
2922 gtk_widget_set_sensitive(ct_button, FALSE);
2925 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2926 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2927 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)"));
2928 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2931 label = pref_label_new(group, "");
2932 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(), "/");
2933 gtk_label_set_markup (GTK_LABEL(label), markup);
2936 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2937 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2938 gtk_widget_set_margin_start(label, 22);
2939 pref_spacer(group, PREF_PAD_GROUP);
2941 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2943 gtk_widget_set_sensitive(group, FALSE);
2946 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2947 pref_checkbox_link_sensitivity(ct_button, hbox);
2949 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);
2950 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2952 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);
2954 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2956 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2957 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2958 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2959 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", ")");
2960 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2963 pref_spacer(group, PREF_PAD_GROUP);
2965 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2967 gtk_widget_set_sensitive(group, FALSE);
2970 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);
2972 pref_spacer(group, PREF_PAD_GROUP);
2974 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2975 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);
2976 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2978 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2979 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2981 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2982 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"));
2985 gtk_widget_set_sensitive(ct_button, FALSE);
2988 pref_spacer(group, PREF_PAD_GROUP);
2990 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2992 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2994 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2995 pref_checkbox_link_sensitivity(ct_button, hbox);
2997 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2999 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
3001 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
3003 pref_spacer(group, PREF_PAD_GROUP);
3006 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
3008 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
3009 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
3012 pref_spacer(group, PREF_PAD_GROUP);
3014 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
3016 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
3017 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");
3022 struct KeywordFindData
3029 GtkWidget *button_close;
3030 GtkWidget *button_stop;
3031 GtkWidget *button_start;
3032 GtkWidget *progress;
3040 guint idle_id; /* event source id */
3044 KEYWORD_DIALOG_WIDTH = 400
3047 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
3049 GList *list_d = nullptr;
3050 GList *list_f = nullptr;
3054 filelist_read(dir_fd, &list_f, &list_d);
3058 filelist_read(dir_fd, &list_f, nullptr);
3061 list_f = filelist_filter(list_f, FALSE);
3062 list_d = filelist_filter(list_d, TRUE);
3064 kfd->list = g_list_concat(list_f, kfd->list);
3065 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3068 static void keywords_find_reset(KeywordFindData *kfd)
3070 filelist_free(kfd->list);
3071 kfd->list = nullptr;
3073 filelist_free(kfd->list_dir);
3074 kfd->list_dir = nullptr;
3077 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3079 auto kfd = static_cast<KeywordFindData *>(data);
3081 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3083 keywords_find_reset(kfd);
3084 generic_dialog_close(kfd->gd);
3088 static void keywords_find_finish(KeywordFindData *kfd)
3090 keywords_find_reset(kfd);
3092 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3093 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
3095 gtk_widget_set_sensitive(kfd->group, TRUE);
3096 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3097 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3098 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3101 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3103 auto kfd = static_cast<KeywordFindData *>(data);
3105 g_idle_remove_by_data(kfd);
3107 keywords_find_finish(kfd);
3110 static gboolean keywords_find_file(gpointer data)
3112 auto kfd = static_cast<KeywordFindData *>(data);
3114 GtkTextBuffer *buffer;
3122 fd = static_cast<FileData *>(kfd->list->data);
3123 kfd->list = g_list_remove(kfd->list, fd);
3125 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3126 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3130 gtk_text_buffer_get_end_iter(buffer, &iter);
3131 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3132 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3134 keywords = keywords->next;
3137 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3138 file_data_unref(fd);
3139 g_list_free_full(keywords, g_free);
3141 return (G_SOURCE_CONTINUE);
3148 fd = static_cast<FileData *>(kfd->list_dir->data);
3149 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3151 keywords_find_folder(kfd, fd);
3153 file_data_unref(fd);
3155 return G_SOURCE_CONTINUE;
3158 keywords_find_finish(kfd);
3160 return G_SOURCE_REMOVE;
3163 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3165 auto kfd = static_cast<KeywordFindData *>(data);
3168 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3170 path = remove_trailing_slash((gq_gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3171 parse_out_relatives(path);
3175 warning_dialog(_("Invalid folder"),
3176 _("The specified folder can not be found."),
3177 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3183 gtk_widget_set_sensitive(kfd->group, FALSE);
3184 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3185 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3186 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3187 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3189 dir_fd = file_data_new_dir(path);
3190 keywords_find_folder(kfd, dir_fd);
3191 file_data_unref(dir_fd);
3192 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3198 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3200 KeywordFindData *kfd;
3204 kfd = g_new0(KeywordFindData, 1);
3206 kfd->gd = generic_dialog_new(_("Search for keywords"),
3207 "search_for_keywords",
3210 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3211 kfd->gd->cancel_cb = keywords_find_close_cb;
3212 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3213 keywords_find_close_cb, FALSE);
3214 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3215 keywords_find_start_cb, FALSE);
3216 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3217 keywords_find_stop_cb, FALSE);
3218 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3220 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3222 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3223 pref_spacer(hbox, PREF_PAD_INDENT);
3224 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3226 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3227 pref_label_new(hbox, _("Folder:"));
3229 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3230 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3231 gq_gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3232 gtk_widget_show(label);
3234 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3236 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3237 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3239 kfd->progress = gtk_entry_new();
3240 gtk_widget_set_can_focus(kfd->progress, FALSE);
3241 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3242 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3243 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3244 gtk_widget_show(kfd->progress);
3246 kfd->spinner = gtk_spinner_new();
3247 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3248 gtk_widget_show(kfd->spinner);
3250 kfd->list = nullptr;
3252 gtk_widget_show(kfd->gd->dialog);
3255 static void keywords_find_cb(GtkWidget *widget, gpointer)
3257 const gchar *path = layout_get_path(nullptr);
3259 if (!path || !*path) path = homedir();
3260 keywords_find_dialog(widget, path);
3263 static void config_tab_keywords_save()
3267 GtkTextBuffer *buffer;
3268 GList *kw_list = nullptr;
3274 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3275 gtk_text_buffer_get_bounds(buffer, &start, &end);
3277 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3279 kw_split = strtok(buffer_text, "\n");
3280 while (kw_split != nullptr)
3286 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3295 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3297 kw_split = strtok(nullptr, "\n");
3300 keyword_list_set(kw_list);
3302 g_list_free_full(kw_list, g_free);
3303 g_free(buffer_text);
3306 static void config_tab_keywords(GtkWidget *notebook)
3312 GtkWidget *scrolled;
3314 GtkTextBuffer *buffer;
3317 GspellTextView *gspell_view;
3320 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3322 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3324 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3326 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3327 G_CALLBACK(keywords_find_cb), keyword_text);
3328 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3331 keyword_text = gtk_text_view_new();
3332 gtk_widget_set_size_request(keyword_text, 20, 20);
3333 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3334 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3335 gtk_widget_show(scrolled);
3338 if (options->metadata.check_spelling)
3340 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3341 gspell_text_view_basic_setup(gspell_view);
3345 gq_gtk_container_add(GTK_WIDGET(scrolled), keyword_text);
3346 gtk_widget_show(keyword_text);
3348 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3350 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3351 gtk_text_buffer_create_tag(buffer, "monospace",
3352 "family", "monospace", NULL);
3354 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3355 gtk_text_buffer_get_start_iter(buffer, &iter);
3356 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3359 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3361 GList *kwl = keyword_list_get();
3362 kwl = g_list_first(kwl);
3365 gtk_text_buffer_get_end_iter (buffer, &iter);
3366 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3367 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3372 gtk_text_buffer_set_modified(buffer, FALSE);
3379 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3381 auto option = static_cast<gint *>(data);
3383 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3387 *option = INTENT_PERCEPTUAL;
3390 *option = INTENT_RELATIVE_COLORIMETRIC;
3393 *option = INTENT_SATURATION;
3396 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3401 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3402 gint option, gint *option_c)
3409 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3411 combo = gtk_combo_box_text_new();
3413 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3414 if (option == INTENT_PERCEPTUAL) current = 0;
3415 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3416 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3417 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3418 if (option == INTENT_SATURATION) current = 2;
3419 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3420 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3422 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3424 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3426 g_signal_connect(G_OBJECT(combo), "changed",
3427 G_CALLBACK(intent_menu_cb), option_c);
3429 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3430 gtk_widget_show(combo);
3434 static void config_tab_color(GtkWidget *notebook)
3443 vbox = scrolled_notebook_page(notebook, _("Color management"));
3445 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3447 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3450 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3451 gtk_grid_set_column_spacing(GTK_GRID(table), PREF_PAD_GAP);
3453 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3454 pref_label_bold(label, TRUE, FALSE);
3456 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3457 pref_label_bold(label, TRUE, FALSE);
3459 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3460 pref_label_bold(label, TRUE, FALSE);
3462 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3467 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3468 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3471 entry = gtk_entry_new();
3472 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3473 if (options->color_profile.input_name[i])
3475 gq_gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3477 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);
3478 gtk_widget_show(entry);
3479 color_profile_input_name_entry[i] = entry;
3481 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3482 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3483 gtk_widget_set_size_request(entry, 160, -1);
3484 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);
3485 gtk_widget_show(tabcomp);
3486 color_profile_input_file_entry[i] = entry;
3489 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3491 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3493 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3494 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3496 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3498 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3499 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3500 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3501 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3502 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3504 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3506 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);
3508 gtk_widget_show(tabcomp);
3511 /* advanced entry tab */
3512 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3514 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3516 c_options->file_ops.use_system_trash = FALSE;
3517 c_options->file_ops.no_trash = FALSE;
3521 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3523 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3525 c_options->file_ops.use_system_trash = TRUE;
3526 c_options->file_ops.no_trash = FALSE;
3530 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3532 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3534 c_options->file_ops.no_trash = TRUE;
3538 static void config_tab_behavior(GtkWidget *notebook)
3545 GtkWidget *ct_button;
3549 GtkWidget *with_rename;
3550 GtkWidget *collections_on_top;
3551 GtkWidget *hide_window_in_fullscreen;
3552 GtkWidget *checkbox;
3555 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3557 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3559 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3560 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3561 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3562 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3563 pref_checkbox_new_int(group, _("Enable Delete key"),
3564 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3566 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3567 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3569 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3570 pref_checkbox_link_sensitivity(ct_button, hbox);
3572 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3573 pref_label_new(hbox, _("Folder:"));
3575 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3576 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3577 gq_gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3578 gtk_widget_show(tabcomp);
3580 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3581 pref_checkbox_link_sensitivity(ct_button, hbox);
3583 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3584 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3585 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3586 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3587 button = pref_button_new(nullptr, nullptr, _("View"),
3588 G_CALLBACK(safe_delete_view_cb), nullptr);
3589 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3590 gtk_widget_show(button);
3592 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3593 G_CALLBACK(safe_delete_clear_cb), nullptr);
3594 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3596 c_options->file_ops.no_trash = options->file_ops.no_trash;
3597 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3599 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3600 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3602 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3603 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3605 gtk_widget_show(button);
3607 pref_spacer(group, PREF_PAD_GROUP);
3610 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3612 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3613 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3615 pref_checkbox_new_int(group, _("In place renaming"),
3616 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3618 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3619 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3621 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3622 options->circular_selection_lists, &c_options->circular_selection_lists);
3623 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3625 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3626 options->marks_save, &c_options->marks_save);
3627 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3629 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3630 options->with_rename, &c_options->with_rename);
3631 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3633 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3634 options->collections_duplicates, &c_options->collections_duplicates);
3635 gtk_widget_set_tooltip_text(collections_on_top,"Allow the same image to be in a Collection more than once");
3637 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3638 options->collections_on_top, &c_options->collections_on_top);
3639 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3641 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3642 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3643 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3645 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3646 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3648 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);
3649 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."));
3651 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3652 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3654 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3655 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3657 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3658 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3660 pref_spacer(group, PREF_PAD_GROUP);
3662 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3664 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3665 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3666 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3667 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3668 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3669 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3670 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3671 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3672 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3673 options->image_l_click_archive, &c_options->image_l_click_archive);
3674 pref_checkbox_new_int(group, _("Play video by left click on image"),
3675 options->image_l_click_video, &c_options->image_l_click_video);
3676 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3677 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3679 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3680 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3681 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3682 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3683 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3685 pref_spacer(group, PREF_PAD_GROUP);
3687 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3689 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3690 options->override_disable_gpu, &c_options->override_disable_gpu);
3691 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3694 pref_spacer(group, PREF_PAD_GROUP);
3696 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3698 pref_spin_new_int(group, _("Debug level:"), nullptr,
3699 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3701 pref_checkbox_new_int(group, _("Timer data"),
3702 options->log_window.timer_data, &c_options->log_window.timer_data);
3704 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3705 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3707 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3708 pref_label_new(hbox, _("Log Window F1 command: "));
3709 log_window_f1_entry = gtk_entry_new();
3710 gq_gtk_entry_set_text(GTK_ENTRY(log_window_f1_entry), options->log_window.action);
3711 gq_gtk_box_pack_start(GTK_BOX(hbox), log_window_f1_entry, FALSE, FALSE, 0);
3712 gtk_entry_set_width_chars(GTK_ENTRY(log_window_f1_entry), 15);
3713 gtk_widget_show(log_window_f1_entry);
3717 /* accelerators tab */
3719 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3721 gboolean ret = TRUE;
3726 gtk_tree_model_get(model, iter, column, &text, -1);
3727 text_nocase = g_utf8_casefold(text, -1);
3728 key_nocase = g_utf8_casefold(key, -1);
3730 if (g_strstr_len(text_nocase, -1, key_nocase))
3737 g_free(text_nocase);
3742 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3747 list = gtk_tree_view_get_columns(tree_view);
3748 col_num = g_list_index(list, column);
3752 gtk_tree_view_set_search_column(tree_view, col_num);
3755 static void config_tab_accelerators(GtkWidget *notebook)
3761 GtkWidget *scrolled;
3762 GtkWidget *accel_view;
3763 GtkCellRenderer *renderer;
3764 GtkTreeSelection *selection;
3765 GtkTreeViewColumn *column;
3767 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3769 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3771 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3772 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3773 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3774 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3775 gtk_widget_show(scrolled);
3777 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3779 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3780 g_object_unref(accel_store);
3781 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3782 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3784 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3786 renderer = gtk_cell_renderer_text_new();
3788 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3793 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3794 gtk_tree_view_column_set_resizable(column, TRUE);
3795 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3798 renderer = gtk_cell_renderer_accel_new();
3799 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3800 G_CALLBACK(accel_store_cleared_cb), accel_store);
3801 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3802 G_CALLBACK(accel_store_edited_cb), accel_store);
3805 g_object_set (renderer,
3807 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3810 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3815 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3816 gtk_tree_view_column_set_resizable(column, TRUE);
3817 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3819 renderer = gtk_cell_renderer_text_new();
3821 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3826 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3827 gtk_tree_view_column_set_resizable(column, TRUE);
3828 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3830 renderer = gtk_cell_renderer_text_new();
3832 column = gtk_tree_view_column_new_with_attributes("Accel",
3837 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3838 gtk_tree_view_column_set_resizable(column, TRUE);
3839 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3841 /* Search on text in column */
3842 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3843 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3844 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3845 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3846 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3848 accel_store_populate();
3849 gq_gtk_container_add(GTK_WIDGET(scrolled), accel_view);
3850 gtk_widget_show(accel_view);
3852 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3854 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3855 G_CALLBACK(accel_default_cb), accel_view);
3856 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3857 gtk_widget_show(button);
3859 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3860 G_CALLBACK(accel_reset_cb), accel_view);
3861 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3862 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3863 gtk_widget_show(button);
3865 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3866 G_CALLBACK(accel_clear_cb), accel_view);
3867 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3868 gtk_widget_show(button);
3871 /* toolbar main tab */
3872 static void config_tab_toolbar_main(GtkWidget *notebook)
3875 GtkWidget *toolbardata;
3878 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3880 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3882 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3883 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3884 gtk_widget_show(vbox);
3887 /* toolbar status tab */
3888 static void config_tab_toolbar_status(GtkWidget *notebook)
3891 GtkWidget *toolbardata;
3894 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3896 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3898 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3899 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3900 gtk_widget_show(vbox);
3904 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3906 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3909 static void config_tab_advanced(GtkWidget *notebook)
3912 GdkPixbufFormat *fm;
3914 GList *extensions_list = nullptr;
3915 GSList *formats_list;
3916 GString *types_string = g_string_new(nullptr);
3917 GtkWidget *alternate_checkbox;
3918 GtkWidget *dupes_threads_spin;
3920 GtkWidget *subgroup;
3922 GtkWidget *threads_string_label;
3923 GtkWidget *types_string_label;
3926 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3927 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3929 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3931 pref_spacer(group, PREF_PAD_GROUP);
3933 formats_list = gdk_pixbuf_get_formats();
3935 while (formats_list)
3937 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3938 extensions = gdk_pixbuf_format_get_extensions(fm);
3941 while (extensions[i])
3943 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3947 g_strfreev(extensions);
3948 formats_list = formats_list->next;
3951 while (extensions_list)
3953 if (types_string->len == 0)
3955 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3959 types_string = g_string_append(types_string, ", ");
3960 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3963 extensions_list = extensions_list->next;
3966 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3967 types_string_label = pref_label_new(group, types_string->str);
3968 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3970 pref_spacer(group, PREF_PAD_GROUP);
3972 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3973 external_preview_select_entry = gtk_entry_new();
3974 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3975 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3976 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3977 gtk_widget_show(tabcomp);
3979 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3980 external_preview_extract_entry = gtk_entry_new();
3981 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3982 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3983 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3984 gtk_widget_show(tabcomp);
3986 gtk_widget_show(vbox);
3988 g_slist_free(formats_list);
3989 g_list_free_full(extensions_list, g_free);
3990 g_string_free(types_string, TRUE);
3992 pref_spacer(group, PREF_PAD_GROUP);
3994 pref_line(vbox, PREF_PAD_SPACE);
3995 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3997 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.");
3998 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
4000 pref_spacer(vbox, PREF_PAD_GROUP);
4002 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
4003 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
4005 pref_spacer(group, PREF_PAD_GROUP);
4007 pref_line(vbox, PREF_PAD_SPACE);
4009 group = pref_group_new(vbox, FALSE, _("Alternate similarity alogorithm"), GTK_ORIENTATION_VERTICAL);
4011 alternate_checkbox = pref_checkbox_new_int(group, _("Enable alternate similarity algorithm"), options->alternate_similarity_algorithm.enabled, &c_options->alternate_similarity_algorithm.enabled);
4013 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4014 pref_checkbox_link_sensitivity(alternate_checkbox, subgroup);
4016 alternate_checkbox = pref_checkbox_new_int(subgroup, _("Use grayscale"), options->alternate_similarity_algorithm.grayscale, &c_options->alternate_similarity_algorithm.grayscale);
4017 gtk_widget_set_tooltip_text(alternate_checkbox, _("Reduce fingerprint to grayscale"));
4021 static void config_tab_stereo(GtkWidget *notebook)
4029 GtkWidget *fs_button;
4030 vbox = scrolled_notebook_page(notebook, _("Stereo"));
4032 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
4034 table = pref_table_new(group, 2, 1, FALSE, FALSE);
4035 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
4037 table = pref_table_new(group, 2, 2, TRUE, FALSE);
4038 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4039 pref_checkbox_new_int(box, _("Mirror left image"),
4040 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
4041 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4042 pref_checkbox_new_int(box, _("Flip left image"),
4043 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
4044 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4045 pref_checkbox_new_int(box, _("Mirror right image"),
4046 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
4047 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4048 pref_checkbox_new_int(box, _("Flip right image"),
4049 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
4050 pref_checkbox_new_int(group, _("Swap left and right images"),
4051 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
4052 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
4053 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
4055 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
4056 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
4057 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
4058 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4059 pref_checkbox_link_sensitivity(fs_button, box2);
4060 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
4061 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
4062 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
4063 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4064 pref_checkbox_new_int(box, _("Mirror left image"),
4065 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
4066 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4067 pref_checkbox_new_int(box, _("Flip left image"),
4068 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
4069 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4070 pref_checkbox_new_int(box, _("Mirror right image"),
4071 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
4072 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4073 pref_checkbox_new_int(box, _("Flip right image"),
4074 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
4075 pref_checkbox_new_int(box2, _("Swap left and right images"),
4076 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
4077 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
4078 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
4080 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
4081 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
4082 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
4083 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
4084 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
4085 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
4086 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
4087 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
4088 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
4089 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4090 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
4091 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4092 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
4093 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4097 /* Main preferences window */
4098 static void config_window_create(LayoutWindow *lw)
4100 GtkWidget *win_vbox;
4102 GtkWidget *notebook;
4104 GtkWidget *ct_button;
4106 if (!c_options) c_options = init_options(nullptr);
4108 configwindow = window_new("preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4109 DEBUG_NAME(configwindow);
4110 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4111 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4112 G_CALLBACK(config_window_delete), NULL);
4113 if (options->save_dialog_window_positions)
4115 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4116 gq_gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4120 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4122 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4123 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4125 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4126 gq_gtk_container_add(GTK_WIDGET(configwindow), win_vbox);
4127 gtk_widget_show(win_vbox);
4129 notebook = gtk_notebook_new();
4130 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4131 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4132 gq_gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4134 config_tab_general(notebook);
4135 config_tab_image(notebook);
4136 config_tab_osd(notebook);
4137 config_tab_windows(notebook);
4138 config_tab_accelerators(notebook);
4139 config_tab_files(notebook);
4140 config_tab_metadata(notebook);
4141 config_tab_keywords(notebook);
4142 config_tab_color(notebook);
4143 config_tab_stereo(notebook);
4144 config_tab_behavior(notebook);
4145 config_tab_toolbar_main(notebook);
4146 config_tab_toolbar_status(notebook);
4147 config_tab_advanced(notebook);
4149 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4151 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4152 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4153 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4154 gq_gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4155 gtk_widget_show(hbox);
4157 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4158 G_CALLBACK(config_window_help_cb), notebook);
4159 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4160 gtk_widget_set_can_default(button, TRUE);
4161 gtk_widget_show(button);
4163 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4164 G_CALLBACK(config_window_ok_cb), notebook);
4165 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4166 gtk_widget_set_can_default(button, TRUE);
4167 gtk_widget_grab_default(button);
4168 gtk_widget_show(button);
4172 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4173 G_CALLBACK(config_window_close_cb), nullptr);
4174 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4175 gtk_widget_set_can_default(button, TRUE);
4176 gtk_widget_show(button);
4178 if (!generic_dialog_get_alternative_button_order(configwindow))
4180 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4183 gtk_widget_show(notebook);
4185 gtk_widget_show(configwindow);
4189 *-----------------------------------------------------------------------------
4190 * config window show (public)
4191 *-----------------------------------------------------------------------------
4194 void show_config_window(LayoutWindow *lw)
4198 gtk_window_present(GTK_WINDOW(configwindow));
4202 config_window_create(lw);
4211 void show_about_window(LayoutWindow *lw)
4213 GDataInputStream *data_stream;
4214 GInputStream *in_stream_authors;
4215 GInputStream *in_stream_translators;
4217 GdkPixbuf *pixbuf_icon;
4218 GdkPixbuf *pixbuf_logo;
4222 gchar *authors[1000];
4224 gchar *timezone_path;
4233 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");
4235 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4236 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4238 cd = ZDOpenDatabase(timezone_path);
4241 copyright = g_string_append(copyright, ZDGetNotice(cd));
4245 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4247 ZDCloseDatabase(cd);
4249 g_free(timezone_path);
4251 copyright = g_string_append(copyright, "\n\nSome icons by https://www.flaticon.com");
4253 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4255 data_stream = g_data_input_stream_new(in_stream_authors);
4257 authors[0] = nullptr;
4258 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4260 authors[i_authors] = g_strdup(author_line);
4262 g_free(author_line);
4264 authors[i_authors] = nullptr;
4266 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4268 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4270 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4272 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4273 translators = static_cast<gchar *>(g_malloc0(size));
4274 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4275 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4277 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);
4279 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4280 artists[1] = nullptr;
4282 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4283 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4284 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4285 "title", _("About Geeqie"),
4287 "program-name", GQ_APPNAME,
4289 "logo", pixbuf_logo,
4290 "icon", pixbuf_icon,
4291 "website", GQ_WEBSITE,
4292 "website-label", "Website",
4293 "comments", comment,
4296 "translator-credits", translators,
4297 "wrap-license", TRUE,
4298 "license", copyright->str,
4301 g_string_free(copyright, TRUE);
4303 while(n < i_authors)
4311 g_free(translators);
4312 g_object_unref(data_stream);
4313 g_object_unref(in_stream_authors);
4314 g_object_unref(in_stream_translators);
4317 static void image_overlay_set_text_colors()
4319 c_options->image_overlay.text_red = options->image_overlay.text_red;
4320 c_options->image_overlay.text_green = options->image_overlay.text_green;
4321 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4322 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4323 c_options->image_overlay.background_red = options->image_overlay.background_red;
4324 c_options->image_overlay.background_green = options->image_overlay.background_green;
4325 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4326 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4330 *-----------------------------------------------------------------------------
4331 * timezone database routines
4332 *-----------------------------------------------------------------------------
4335 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4337 GError *error = nullptr;
4338 auto tz = static_cast<TZData *>(data);
4339 gchar *tmp_filename;
4340 gchar *timezone_bin;
4341 gchar *tmp_dir = nullptr;
4344 if (!g_cancellable_is_cancelled(tz->cancellable))
4346 generic_dialog_close(tz->gd);
4350 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4352 tmp_filename = g_file_get_path(tz->tmp_g_file);
4353 fd = file_data_new_simple(tmp_filename);
4354 tmp_dir = open_archive(fd);
4358 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4359 if (isfile(timezone_bin))
4361 move_file(timezone_bin, tz->timezone_database_user);
4365 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4368 g_free(timezone_bin);
4369 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4373 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4375 g_free(tmp_filename);
4376 file_data_unref(fd);
4380 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4383 g_file_delete(tz->tmp_g_file, nullptr, &error);
4384 g_object_unref(tz->tmp_g_file);
4385 tz->tmp_g_file = nullptr;
4386 g_object_unref(tz->cancellable);
4387 g_object_unref(tz->timezone_database_gq);
4390 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4392 auto tz = static_cast<TZData *>(data);
4394 if (!g_cancellable_is_cancelled(tz->cancellable))
4396 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4400 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4402 auto tz = static_cast<TZData *>(data);
4404 g_cancellable_cancel(tz->cancellable);
4407 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4409 auto tz = static_cast<TZData *>(data);
4410 GError *error = nullptr;
4411 GFileIOStream *io_stream;
4418 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4422 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4423 log_printf("Error: Download timezone database failed:\n%s", error->message);
4424 g_error_free(error);
4425 g_object_unref(tz->tmp_g_file);
4429 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4431 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4433 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4435 tz->progress = gtk_progress_bar_new();
4436 gq_gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4437 gtk_widget_show(tz->progress);
4439 gtk_widget_show(tz->gd->dialog);
4440 tz->cancellable = g_cancellable_new();
4441 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);
4443 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4446 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */