2 * Copyright (C) 2006 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "preferences.h"
25 #include "bar-keywords.h"
27 #include "color-man.h"
30 #include "filefilter.h"
31 #include "fullscreen.h"
33 #include "image-overlay.h"
35 #include "layout-util.h"
39 #include "pixbuf-util.h"
41 #include "slideshow.h"
45 #include "ui-fileops.h"
47 #include "ui-tabcomp.h"
49 #include "zonedetect.h"
60 #include <gspell/gspell.h>
64 EDITOR_NAME_MAX_LENGTH = 32,
65 EDITOR_COMMAND_MAX_LENGTH = 1024
68 static void image_overlay_set_text_colors();
70 GtkWidget *keyword_text;
71 static void config_tab_keywords_save();
79 static ThumbSize thumb_size_list[] =
114 FILETYPES_COLUMN_ENABLED = 0,
115 FILETYPES_COLUMN_FILTER,
116 FILETYPES_COLUMN_DESCRIPTION,
117 FILETYPES_COLUMN_CLASS,
118 FILETYPES_COLUMN_WRITABLE,
119 FILETYPES_COLUMN_SIDECAR,
120 FILETYPES_COLUMN_COUNT
123 const gchar *format_class_list[] = {
134 /* config memory values */
135 static ConfOptions *c_options = nullptr;
142 static GtkWidget *configwindow = nullptr;
143 static GtkListStore *filter_store = nullptr;
144 static GtkTreeStore *accel_store = nullptr;
146 static GtkWidget *safe_delete_path_entry;
148 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
149 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
150 static GtkWidget *color_profile_screen_file_entry;
151 static GtkWidget *external_preview_select_entry;
152 static GtkWidget *external_preview_extract_entry;
154 static GtkWidget *sidecar_ext_entry;
155 static GtkWidget *help_search_engine_entry;
157 static GtkWidget *log_window_f1_entry;
161 CONFIG_WINDOW_DEF_WIDTH = 700,
162 CONFIG_WINDOW_DEF_HEIGHT = 600
166 *-----------------------------------------------------------------------------
167 * option widget callbacks (private)
168 *-----------------------------------------------------------------------------
171 static void zoom_increment_cb(GtkWidget *spin, gpointer)
173 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
176 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
178 gint mins_secs_tenths, delay;
180 mins_secs_tenths = c_options->slideshow.delay %
181 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
183 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
184 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
187 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
188 SLIDESHOW_SUBSECOND_PRECISION;
191 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
193 gint hours, secs_tenths, delay;
195 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
196 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
198 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
199 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
200 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
202 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
203 SLIDESHOW_SUBSECOND_PRECISION;
206 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
208 gint hours_mins, delay;
210 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
212 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
213 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
214 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
216 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
217 SLIDESHOW_SUBSECOND_PRECISION;
221 *-----------------------------------------------------------------------------
222 * sync program to config window routine (private)
223 *-----------------------------------------------------------------------------
226 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
232 buf = gq_gtk_entry_get_text(GTK_ENTRY(entry));
233 if (buf && strlen(buf) > 0)
238 *option = g_strdup(buf);
243 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
245 gchar *accel_path, *accel;
247 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
249 if (accel_path && accel_path[0])
252 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
253 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
263 static void config_window_apply()
265 gboolean refresh = FALSE;
270 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
272 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
273 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
274 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
275 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
276 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
277 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
279 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
280 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
281 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
282 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
283 options->file_ops.no_trash = c_options->file_ops.no_trash;
284 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
285 options->tools_restore_state = c_options->tools_restore_state;
286 options->save_window_positions = c_options->save_window_positions;
287 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
288 options->save_window_workspace = c_options->save_window_workspace;
289 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
290 options->show_window_ids = c_options->show_window_ids;
291 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
292 options->image.zoom_2pass = c_options->image.zoom_2pass;
293 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
294 options->image.limit_window_size = c_options->image.limit_window_size;
295 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
296 options->image.max_window_size = c_options->image.max_window_size;
297 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
298 options->image.max_autofit_size = c_options->image.max_autofit_size;
299 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
300 options->image.tile_size = c_options->image.tile_size;
301 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
302 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
304 if (options->thumbnails.max_width != c_options->thumbnails.max_width
305 || options->thumbnails.max_height != c_options->thumbnails.max_height
306 || options->thumbnails.quality != c_options->thumbnails.quality)
308 thumb_format_changed = TRUE;
310 options->thumbnails.max_width = c_options->thumbnails.max_width;
311 options->thumbnails.max_height = c_options->thumbnails.max_height;
312 options->thumbnails.quality = c_options->thumbnails.quality;
314 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
315 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
316 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
317 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
318 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
319 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
320 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
321 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
322 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
323 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
324 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
325 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
327 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
328 options->file_filter.disable = c_options->file_filter.disable;
330 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
331 sidecar_ext_parse(options->sidecar.ext);
333 options->slideshow.random = c_options->slideshow.random;
334 options->slideshow.repeat = c_options->slideshow.repeat;
335 options->slideshow.delay = c_options->slideshow.delay;
337 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
338 options->image_lm_click_nav = c_options->image_lm_click_nav;
339 options->image_l_click_archive = c_options->image_l_click_archive;
340 options->image_l_click_video = c_options->image_l_click_video;
341 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
343 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
345 options->image.tile_cache_max = c_options->image.tile_cache_max;
346 options->image.image_cache_max = c_options->image.image_cache_max;
348 options->image.zoom_quality = c_options->image.zoom_quality;
350 options->image.zoom_increment = c_options->image.zoom_increment;
352 options->image.zoom_style = c_options->image.zoom_style;
354 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
356 options->appimage_notifications = c_options->appimage_notifications;
359 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
360 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
361 || !gdk_rgba_equal(&options->image.border_color, &c_options->image.border_color))
363 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
364 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
365 options->image.border_color = c_options->image.border_color;
366 layout_colors_update();
367 view_window_colors_update();
370 options->image.alpha_color_1 = c_options->image.alpha_color_1;
371 options->image.alpha_color_2 = c_options->image.alpha_color_2;
373 options->fullscreen.screen = c_options->fullscreen.screen;
374 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
375 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
376 options->fullscreen.above = c_options->fullscreen.above;
377 if (c_options->image_overlay.template_string)
378 set_image_overlay_template_string(&options->image_overlay.template_string,
379 c_options->image_overlay.template_string);
380 if (c_options->image_overlay.font)
381 set_image_overlay_font_string(&options->image_overlay.font,
382 c_options->image_overlay.font);
383 options->image_overlay.text_red = c_options->image_overlay.text_red;
384 options->image_overlay.text_green = c_options->image_overlay.text_green;
385 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
386 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
387 options->image_overlay.background_red = c_options->image_overlay.background_red;
388 options->image_overlay.background_green = c_options->image_overlay.background_green;
389 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
390 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
391 options->image_overlay.font = c_options->image_overlay.font;
392 options->update_on_time_change = c_options->update_on_time_change;
394 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
395 options->rot_invariant_sim = c_options->rot_invariant_sim;
397 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
399 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
400 options->circular_selection_lists = c_options->circular_selection_lists;
402 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
403 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
404 options->dnd_icon_size = c_options->dnd_icon_size;
405 options->clipboard_selection = c_options->clipboard_selection;
406 options->dnd_default_action = c_options->dnd_default_action;
408 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
409 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
410 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
411 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
412 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
413 options->metadata.confirm_write = c_options->metadata.confirm_write;
414 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
415 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
416 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
417 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
418 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
419 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
420 options->metadata.write_orientation = c_options->metadata.write_orientation;
421 options->metadata.check_spelling = c_options->metadata.check_spelling;
422 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
423 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
424 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
425 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
426 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
427 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
428 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
429 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
430 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
431 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
432 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
433 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
434 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
435 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
436 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
437 options->stereo.fixed_w = c_options->stereo.fixed_w;
438 options->stereo.fixed_h = c_options->stereo.fixed_h;
439 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
440 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
441 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
442 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
444 options->info_keywords.height = c_options->info_keywords.height;
445 options->info_title.height = c_options->info_title.height;
446 options->info_comment.height = c_options->info_comment.height;
447 options->info_rating.height = c_options->info_rating.height;
449 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
450 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
451 options->hamburger_menu = c_options->hamburger_menu;
453 options->selectable_bars.menu_bar = c_options->selectable_bars.menu_bar;
454 options->selectable_bars.tool_bar = c_options->selectable_bars.tool_bar;
455 options->selectable_bars.status_bar = c_options->selectable_bars.status_bar;
457 options->marks_save = c_options->marks_save;
458 options->with_rename = c_options->with_rename;
459 options->collections_duplicates = c_options->collections_duplicates;
460 options->collections_on_top = c_options->collections_on_top;
461 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
462 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
464 options->external_preview.enable = c_options->external_preview.enable;
465 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
466 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
468 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
470 options->star_rating.star = c_options->star_rating.star;
471 options->star_rating.rejected = c_options->star_rating.rejected;
473 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
475 options->alternate_similarity_algorithm.enabled = c_options->alternate_similarity_algorithm.enabled;
476 options->alternate_similarity_algorithm.grayscale = c_options->alternate_similarity_algorithm.grayscale;
479 set_debug_level(debug_c);
480 config_entry_to_option(log_window_f1_entry, &options->log_window.action, nullptr);
484 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
486 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
487 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
489 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
490 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
491 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
493 options->color_profile.render_intent = c_options->color_profile.render_intent;
498 options->mouse_button_8 = c_options->mouse_button_8;
499 options->mouse_button_9 = c_options->mouse_button_9;
501 options->override_disable_gpu = c_options->override_disable_gpu;
503 config_tab_keywords_save();
505 image_options_sync();
510 layout_refresh(nullptr);
513 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
515 toolbar_apply(TOOLBAR_MAIN);
516 toolbar_apply(TOOLBAR_STATUS);
520 *-----------------------------------------------------------------------------
521 * config window main button callbacks (private)
522 *-----------------------------------------------------------------------------
525 static void config_window_close_cb(GtkWidget *, gpointer)
527 gq_gtk_widget_destroy(configwindow);
528 configwindow = nullptr;
529 filter_store = nullptr;
532 static void config_window_help_cb(GtkWidget *, gpointer data)
534 auto notebook = static_cast<GtkWidget *>(data);
537 static const gchar *html_section[] =
539 "GuideOptionsGeneral.html",
540 "GuideOptionsImage.html",
541 "GuideOptionsOSD.html",
542 "GuideOptionsWindow.html",
543 "GuideOptionsKeyboard.html",
544 "GuideOptionsFiltering.html",
545 "GuideOptionsMetadata.html",
546 "GuideOptionsKeywords.html",
547 "GuideOptionsColor.html",
548 "GuideOptionsStereo.html",
549 "GuideOptionsBehavior.html",
550 "GuideOptionsToolbar.html",
551 "GuideOptionsToolbar.html",
552 "GuideOptionsAdvanced.html"
555 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
556 help_window_show(html_section[i]);
559 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
561 config_window_close_cb(nullptr, nullptr);
565 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
568 auto notebook = static_cast<GtkNotebook *>(data);
576 lw = static_cast<LayoutWindow *>(layout_window_list->data);
578 window = gtk_widget_get_window(widget);
579 gdk_window_get_root_origin(window, &x, &y);
580 w = gdk_window_get_width(window);
581 h = gdk_window_get_height(window);
582 page_number = gtk_notebook_get_current_page(notebook);
584 lw->options.preferences_window.x = x;
585 lw->options.preferences_window.y = y;
586 lw->options.preferences_window.w = w;
587 lw->options.preferences_window.h = h;
588 lw->options.preferences_window.page_number = page_number;
590 config_window_apply();
591 layout_util_sync(lw);
592 save_options(options);
593 config_window_close_cb(nullptr, nullptr);
597 *-----------------------------------------------------------------------------
598 * config window setup (private)
599 *-----------------------------------------------------------------------------
602 static void quality_menu_cb(GtkWidget *combo, gpointer data)
604 auto option = static_cast<gint *>(data);
606 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
610 *option = GDK_INTERP_NEAREST;
613 *option = GDK_INTERP_TILES;
616 *option = GDK_INTERP_BILINEAR;
621 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
623 auto option = static_cast<gint *>(data);
625 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
629 *option = DND_ACTION_ASK;
632 *option = DND_ACTION_COPY;
635 *option = DND_ACTION_MOVE;
639 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
641 auto option = static_cast<gint *>(data);
643 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
646 *option = CLIPBOARD_PRIMARY;
649 *option = CLIPBOARD_CLIPBOARD;
652 *option = CLIPBOARD_BOTH;
655 *option = CLIPBOARD_BOTH;
659 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
660 guint option, guint *option_c)
667 pref_table_label(table, column, row, text, GTK_ALIGN_START);
669 combo = gtk_combo_box_text_new();
671 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
672 if (option == GDK_INTERP_NEAREST) current = 0;
673 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
674 if (option == GDK_INTERP_TILES) current = 1;
675 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
676 if (option == GDK_INTERP_BILINEAR) current = 2;
678 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
680 g_signal_connect(G_OBJECT(combo), "changed",
681 G_CALLBACK(quality_menu_cb), option_c);
683 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
684 gtk_widget_show(combo);
687 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *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), _("Ask"));
699 if (option == DND_ACTION_ASK) current = 0;
700 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
701 if (option == DND_ACTION_COPY) current = 1;
702 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
703 if (option == DND_ACTION_MOVE) current = 2;
705 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
707 g_signal_connect(G_OBJECT(combo), "changed",
708 G_CALLBACK(dnd_default_action_selection_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_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
715 gint option, gint *option_c)
722 pref_table_label(table, column, row, text, GTK_ALIGN_START);
724 combo = gtk_combo_box_text_new();
726 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
727 if (option == CLIPBOARD_PRIMARY) current = 0;
728 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
729 if (option == CLIPBOARD_CLIPBOARD) current = 1;
730 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
731 if (option == CLIPBOARD_BOTH) current = 2;
733 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
735 g_signal_connect(G_OBJECT(combo), "changed",
736 G_CALLBACK(clipboard_selection_menu_cb), option_c);
738 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
739 gtk_widget_show(combo);
742 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
744 auto option = static_cast<gint *>(data);
746 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
749 *option = ZOOM_GEOMETRIC;
752 *option = ZOOM_ARITHMETIC;
755 *option = ZOOM_GEOMETRIC;
759 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
766 pref_table_label(table, column, row, text, GTK_ALIGN_START);
768 combo = gtk_combo_box_text_new();
770 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
771 if (option == ZOOM_GEOMETRIC) current = 0;
772 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
773 if (option == ZOOM_ARITHMETIC) current = 1;
775 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
777 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
779 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
780 gtk_widget_show(combo);
783 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
785 ActionItem *action_item = nullptr;
786 auto option = static_cast<gchar **>(data);
791 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
793 list = get_action_items();
798 action_item = static_cast<ActionItem *>(work->data);
799 if (g_strcmp0(action_item->label, label) == 0)
807 *option = g_strdup(action_item->name);
809 action_items_free(list);
812 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
814 ActionItem *action_item;
823 pref_table_label(table, column, row, text, GTK_ALIGN_START);
825 combo = gtk_combo_box_text_new();
827 list = get_action_items();
831 action_item = static_cast<ActionItem *>(work->data);
832 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
834 if (g_strcmp0(action_item->name, option) == 0)
842 action_items_free(list);
844 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
846 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
848 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
849 gtk_widget_show(combo);
852 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
856 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
859 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
861 c_options->thumbnails.max_width = thumb_size_list[n].w;
862 c_options->thumbnails.max_height = thumb_size_list[n].h;
866 c_options->thumbnails.max_width = options->thumbnails.max_width;
867 c_options->thumbnails.max_height = options->thumbnails.max_height;
871 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
877 c_options->thumbnails.max_width = options->thumbnails.max_width;
878 c_options->thumbnails.max_height = options->thumbnails.max_height;
880 pref_table_label(table, column, row, text, GTK_ALIGN_START);
882 combo = gtk_combo_box_text_new();
885 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
890 w = thumb_size_list[i].w;
891 h = thumb_size_list[i].h;
893 buf = g_strdup_printf("%d x %d", w, h);
894 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
897 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
904 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
905 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
911 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
912 g_signal_connect(G_OBJECT(combo), "changed",
913 G_CALLBACK(thumb_size_menu_cb), NULL);
915 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
916 gtk_widget_show(combo);
919 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
921 auto option = static_cast<gint *>(data);
923 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
927 *option = PR_STEREO_NONE;
930 *option = PR_STEREO_ANAGLYPH_RC;
933 *option = PR_STEREO_ANAGLYPH_GM;
936 *option = PR_STEREO_ANAGLYPH_YB;
939 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
942 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
945 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
948 *option = PR_STEREO_ANAGLYPH_DB_RC;
951 *option = PR_STEREO_ANAGLYPH_DB_GM;
954 *option = PR_STEREO_ANAGLYPH_DB_YB;
957 *option = PR_STEREO_HORIZ;
960 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
963 *option = PR_STEREO_VERT;
966 *option = PR_STEREO_VERT | PR_STEREO_HALF;
969 *option = PR_STEREO_FIXED;
974 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
975 gint option, gint *option_c, gboolean add_fixed)
982 pref_table_label(table, column, row, text, GTK_ALIGN_START);
984 combo = gtk_combo_box_text_new();
986 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
988 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
989 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
990 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
991 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
992 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
993 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
994 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
995 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
996 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
997 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
998 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
999 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
1000 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
1001 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
1002 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
1003 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
1004 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1005 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1007 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1008 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1009 if (option & PR_STEREO_HORIZ)
1012 if (option & PR_STEREO_HALF) current = 11;
1015 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1016 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1017 if (option & PR_STEREO_VERT)
1020 if (option & PR_STEREO_HALF) current = 13;
1025 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1026 if (option & PR_STEREO_FIXED) current = 14;
1029 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1031 g_signal_connect(G_OBJECT(combo), "changed",
1032 G_CALLBACK(stereo_mode_menu_cb), option_c);
1034 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1035 gtk_widget_show(combo);
1038 static void video_menu_cb(GtkWidget *combo, gpointer data)
1040 auto option = static_cast<gchar **>(data);
1042 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1046 static void video_menu_populate(gpointer data, gpointer user_data)
1048 auto combo = static_cast<GtkWidget *>(user_data);
1049 auto ed = static_cast<EditorDescription *>(data);
1051 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1054 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1055 gchar *option, gchar **option_c)
1059 /* use lists since they are sorted */
1060 GList *eds = editor_list_get();
1064 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1066 combo = gtk_combo_box_text_new();
1067 g_list_foreach(eds,video_menu_populate,combo);
1068 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1070 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1072 g_signal_connect(G_OBJECT(combo), "changed",
1073 G_CALLBACK(video_menu_cb), option_c);
1075 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1076 gtk_widget_show(combo);
1079 static void filter_store_populate()
1083 if (!filter_store) return;
1085 gtk_list_store_clear(filter_store);
1087 work = filter_get_list();
1093 fe = static_cast<FilterEntry *>(work->data);
1096 gtk_list_store_append(filter_store, &iter);
1097 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1101 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1103 auto model = static_cast<GtkWidget *>(data);
1104 auto fe = static_cast<FilterEntry *>(data);
1108 if (!new_text || strlen(new_text) < 1) return;
1110 tpath = gtk_tree_path_new_from_string(path_str);
1111 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1112 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1114 g_free(fe->extensions);
1115 fe->extensions = g_strdup(new_text);
1117 gtk_tree_path_free(tpath);
1121 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1123 auto model = static_cast<GtkWidget *>(data);
1124 auto fe = static_cast<FilterEntry *>(data);
1129 if (!new_text || !new_text[0]) return;
1131 tpath = gtk_tree_path_new_from_string(path_str);
1132 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1133 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1135 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1137 if (strcmp(new_text, _(format_class_list[i])) == 0)
1139 fe->file_class = static_cast<FileFormatClass>(i);
1144 gtk_tree_path_free(tpath);
1148 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1150 auto model = static_cast<GtkWidget *>(data);
1155 if (!new_text || !new_text[0]) return;
1157 tpath = gtk_tree_path_new_from_string(path_str);
1158 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1159 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1161 g_free(fe->description);
1162 fe->description = g_strdup(new_text);
1164 gtk_tree_path_free(tpath);
1167 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1169 auto model = static_cast<GtkWidget *>(data);
1174 tpath = gtk_tree_path_new_from_string(path_str);
1175 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1176 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1178 fe->enabled = !fe->enabled;
1180 gtk_tree_path_free(tpath);
1184 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1186 auto model = static_cast<GtkWidget *>(data);
1191 tpath = gtk_tree_path_new_from_string(path_str);
1192 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1193 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1195 fe->writable = !fe->writable;
1196 if (fe->writable) fe->allow_sidecar = FALSE;
1198 gtk_tree_path_free(tpath);
1202 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1204 auto model = static_cast<GtkWidget *>(data);
1209 tpath = gtk_tree_path_new_from_string(path_str);
1210 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1211 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1213 fe->allow_sidecar = !fe->allow_sidecar;
1214 if (fe->allow_sidecar) fe->writable = FALSE;
1216 gtk_tree_path_free(tpath);
1220 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1221 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1225 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1227 switch (GPOINTER_TO_INT(data))
1230 g_object_set(GTK_CELL_RENDERER(cell),
1231 "active", fe->enabled, NULL);
1234 g_object_set(GTK_CELL_RENDERER(cell),
1235 "text", fe->extensions, NULL);
1237 case FE_DESCRIPTION:
1238 g_object_set(GTK_CELL_RENDERER(cell),
1239 "text", fe->description, NULL);
1242 g_object_set(GTK_CELL_RENDERER(cell),
1243 "text", _(format_class_list[fe->file_class]), NULL);
1246 g_object_set(GTK_CELL_RENDERER(cell),
1247 "active", fe->writable, NULL);
1249 case FE_ALLOW_SIDECAR:
1250 g_object_set(GTK_CELL_RENDERER(cell),
1251 "active", fe->allow_sidecar, NULL);
1256 static gboolean filter_add_scroll(gpointer data)
1260 GtkCellRenderer *cell;
1261 GtkTreeViewColumn *column;
1264 GtkTreeModel *store;
1266 FilterEntry *filter;
1268 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1269 path = gtk_tree_path_new_from_indices(rows-1, -1);
1271 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1273 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1274 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1276 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1277 valid = gtk_tree_model_get_iter_first(store, &iter);
1281 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1283 if (g_strcmp0(filter->extensions, ".new") == 0)
1285 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1289 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1292 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1293 path, column, FALSE, 0.0, 0.0 );
1294 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1295 path, column, cell, TRUE);
1297 gtk_tree_path_free(path);
1298 g_list_free(list_cells);
1300 return(G_SOURCE_REMOVE);
1303 static void filter_add_cb(GtkWidget *, gpointer data)
1305 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1306 filter_store_populate();
1308 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1311 static void filter_remove_cb(GtkWidget *, gpointer data)
1313 auto filter_view = static_cast<GtkWidget *>(data);
1314 GtkTreeSelection *selection;
1318 if (!filter_store) return;
1319 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1320 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1321 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1324 filter_remove_entry(fe);
1326 filter_store_populate();
1329 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1333 GtkTreeViewColumn *column;
1335 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1336 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1337 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1339 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1343 gtk_tree_path_free(path);
1345 return(G_SOURCE_REMOVE);
1348 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1351 filter_add_defaults();
1353 filter_store_populate();
1355 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1358 static void dummy_cancel_cb(GenericDialog *, gpointer)
1360 /* no op, only so cancel button appears */
1363 static void filter_default_cb(GtkWidget *widget, gpointer data)
1367 gd = generic_dialog_new(_("Reset filters"),
1368 "reset_filter", widget, TRUE,
1369 dummy_cancel_cb, data);
1370 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1371 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1372 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1373 gtk_widget_show(gd->dialog);
1376 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1378 auto frame = static_cast<GtkWidget *>(data);
1380 gtk_widget_set_sensitive(frame,
1381 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1384 static void safe_delete_view_cb(GtkWidget *, gpointer)
1386 layout_set_path(nullptr, gq_gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1389 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1391 file_util_trash_clear();
1394 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1398 gd = generic_dialog_new(_("Clear trash"),
1399 "clear_trash", widget, TRUE,
1400 dummy_cancel_cb, nullptr);
1401 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1402 _("This will remove the trash contents."), FALSE);
1403 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1404 entry = gtk_entry_new();
1405 gtk_widget_set_can_focus(entry, FALSE);
1406 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1407 if (options->file_ops.safe_delete_path) gq_gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1408 gq_gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1409 gtk_widget_show(entry);
1410 gtk_widget_show(gd->dialog);
1413 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1415 GtkWidget *pTextView;
1416 GtkTextBuffer *pTextBuffer;
1420 pTextView = GTK_WIDGET(data);
1422 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1423 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1424 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1426 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1427 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1430 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1432 auto text_view = static_cast<GtkTextView *>(data);
1433 GtkTextBuffer *buffer;
1435 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1436 if (!configwindow) return;
1438 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1439 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1442 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1446 gd = generic_dialog_new(_("Reset image overlay template string"),
1447 "reset_image_overlay_template_string", widget, TRUE,
1448 dummy_cancel_cb, data);
1449 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1450 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1451 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1452 gtk_widget_show(gd->dialog);
1455 static void image_overlay_help_cb(GtkWidget *, gpointer)
1457 help_window_show("GuideOptionsOSD.html");
1460 static void font_activated_cb(GtkFontChooser *widget, gchar *fontname, gpointer)
1462 g_free(c_options->image_overlay.font);
1463 c_options->image_overlay.font = g_strdup(fontname);
1466 gq_gtk_widget_destroy(GTK_WIDGET(widget));
1469 static void font_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1473 g_free(c_options->image_overlay.font);
1474 c_options->image_overlay.font = g_strdup(options->image_overlay.font);
1476 if (response_id == GTK_RESPONSE_OK)
1478 font = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
1479 g_free(c_options->image_overlay.font);
1480 c_options->image_overlay.font = g_strdup(font);
1484 g_object_unref(dialog);
1487 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1491 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1492 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1493 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1495 g_signal_connect(dialog, "font-activated", G_CALLBACK(font_activated_cb), nullptr);
1496 g_signal_connect(dialog, "response", G_CALLBACK(font_response_cb), nullptr);
1498 gtk_widget_show(dialog);
1501 static void text_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1503 c_options->image_overlay.text_red = color->red * 255;
1504 c_options->image_overlay.text_green = color->green * 255;
1505 c_options->image_overlay.text_blue = color->blue * 255;
1506 c_options->image_overlay.text_alpha = color->alpha * 255;
1508 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1511 static void text_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1515 c_options->image_overlay.text_red = options->image_overlay.text_red;
1516 c_options->image_overlay.text_green = options->image_overlay.text_green;
1517 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
1518 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
1520 if (response_id == GTK_RESPONSE_OK)
1522 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1523 c_options->image_overlay.text_red = color.red * 255;
1524 c_options->image_overlay.text_green = color.green * 255;
1525 c_options->image_overlay.text_blue = color.blue * 255;
1526 c_options->image_overlay.text_alpha = color.alpha * 255;
1529 g_object_unref(dialog);
1532 static void image_overlay_set_text_color_cb(GtkWidget *widget, gpointer)
1537 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Text Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1538 color.red = options->image_overlay.text_red;
1539 color.green = options->image_overlay.text_green;
1540 color.blue = options->image_overlay.text_blue;
1541 color.alpha = options->image_overlay.text_alpha;
1542 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1543 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1545 g_signal_connect(dialog, "color-activated", G_CALLBACK(text_color_activated_cb), nullptr);
1546 g_signal_connect(dialog, "response", G_CALLBACK(text_color_response_cb), nullptr);
1548 gtk_widget_show(dialog);
1551 static void bg_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1553 c_options->image_overlay.background_red = color->red * 255;
1554 c_options->image_overlay.background_green = color->green * 255;
1555 c_options->image_overlay.background_blue = color->blue * 255;
1556 c_options->image_overlay.background_alpha = color->alpha * 255;
1558 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1561 static void bg_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1565 c_options->image_overlay.background_red = options->image_overlay.background_red;
1566 c_options->image_overlay.background_green = options->image_overlay.background_green;
1567 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
1568 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
1570 if (response_id == GTK_RESPONSE_OK)
1572 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1573 c_options->image_overlay.background_red = color.red * 255;
1574 c_options->image_overlay.background_green = color.green * 255;
1575 c_options->image_overlay.background_blue = color.blue * 255;
1576 c_options->image_overlay.background_alpha = color.alpha * 255;
1578 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1581 static void image_overlay_set_background_color_cb(GtkWidget *widget, gpointer)
1586 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Background Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1587 color.red = options->image_overlay.background_red;
1588 color.green = options->image_overlay.background_green;
1589 color.blue = options->image_overlay.background_blue;
1590 color.alpha = options->image_overlay.background_alpha;
1591 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1592 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1594 g_signal_connect(dialog, "color-activated", G_CALLBACK(bg_color_activated_cb), nullptr);
1595 g_signal_connect(dialog, "response", G_CALLBACK(bg_color_response_cb), nullptr);
1597 gtk_widget_show(dialog);
1600 static void accel_store_populate()
1603 GList *groups, *actions;
1605 const gchar *accel_path;
1609 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1611 gtk_tree_store_clear(accel_store);
1612 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 */
1614 g_assert(lw && lw->ui_manager);
1615 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1618 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1621 action = GTK_ACTION(actions->data);
1622 accel_path = gtk_action_get_accel_path(action);
1623 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1625 gchar *label, *label2, *tooltip, *accel;
1626 g_object_get(action,
1627 "tooltip", &tooltip,
1631 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1637 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1641 gtk_tree_store_append(accel_store, &iter, nullptr);
1642 gtk_tree_store_set(accel_store, &iter,
1645 AE_TOOLTIP, tooltip ? tooltip : "",
1646 AE_ACCEL, accel_path,
1654 actions = actions->next;
1657 groups = groups->next;
1661 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1666 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1668 auto accel1 = static_cast<gchar *>(data);
1673 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1675 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1676 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1678 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1680 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1681 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1690 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1692 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1696 GtkAccelKey old_key, key;
1697 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1699 gtk_tree_model_get_iter(model, &iter, path);
1700 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1702 /* test if the accelerator can be stored without conflicts*/
1703 gtk_accel_map_lookup_entry(accel_path, &old_key);
1705 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1706 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1707 gtk_accel_map_lookup_entry(accel_path, &key);
1709 /* restore the original for now, the key will be really changed when the changes are confirmed */
1710 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1712 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1713 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1715 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1716 gtk_tree_path_free(path);
1720 static gboolean accel_default_scroll(GtkTreeView *data)
1724 GtkTreeViewColumn *column;
1726 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1727 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1728 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1730 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1734 gtk_tree_path_free(path);
1736 return(G_SOURCE_REMOVE);
1739 static void accel_default_cb(GtkWidget *, gpointer data)
1741 accel_store_populate();
1743 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1746 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1748 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1751 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1754 gchar *accel_path, *accel;
1756 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1757 gtk_accel_map_lookup_entry(accel_path, &key);
1758 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1760 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1762 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1767 static void accel_clear_cb(GtkWidget *, gpointer data)
1769 GtkTreeSelection *selection;
1771 if (!accel_store) return;
1772 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1773 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1776 static void accel_reset_cb(GtkWidget *, gpointer data)
1778 GtkTreeSelection *selection;
1780 if (!accel_store) return;
1781 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1782 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1787 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1791 GtkWidget *scrolled;
1792 GtkWidget *viewport;
1794 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
1795 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1796 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1797 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1798 label = gtk_label_new(title);
1799 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1800 gtk_widget_show(scrolled);
1802 viewport = gtk_viewport_new(nullptr, nullptr);
1803 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1804 gq_gtk_container_add(GTK_WIDGET(scrolled), viewport);
1805 gtk_widget_show(viewport);
1807 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1808 gq_gtk_container_add(GTK_WIDGET(viewport), vbox);
1809 gtk_widget_show(vbox);
1814 static void cache_standard_cb(GtkWidget *widget, gpointer)
1816 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1818 c_options->thumbnails.spec_standard =TRUE;
1819 c_options->thumbnails.cache_into_dirs = FALSE;
1823 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1825 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1827 c_options->thumbnails.spec_standard =FALSE;
1828 c_options->thumbnails.cache_into_dirs = FALSE;
1832 static void cache_local_cb(GtkWidget *widget, gpointer)
1834 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1836 c_options->thumbnails.cache_into_dirs = TRUE;
1837 c_options->thumbnails.spec_standard =FALSE;
1841 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1843 if (pos == GTK_ENTRY_ICON_PRIMARY)
1845 gq_gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1849 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "");
1853 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1855 gchar *rating_symbol;
1857 if (pos == GTK_ENTRY_ICON_PRIMARY)
1859 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1860 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1861 g_free(rating_symbol);
1865 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1866 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1867 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1871 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1873 gchar *rating_symbol;
1875 if (pos == GTK_ENTRY_ICON_PRIMARY)
1877 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1878 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1879 g_free(rating_symbol);
1883 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1884 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1885 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1889 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1891 auto hbox = static_cast<GtkContainer *>(data);
1892 GString *str = g_string_new(nullptr);
1893 GtkEntry *hex_code_entry;
1894 gchar *hex_code_full;
1897 guint64 hex_value = 0;
1899 list = gtk_container_get_children(hbox);
1901 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1902 hex_code_full = g_strdup(gq_gtk_entry_get_text(hex_code_entry));
1904 hex_code = g_strsplit(hex_code_full, "+", 2);
1905 if (hex_code[0] && hex_code[1])
1907 hex_value = strtoull(hex_code[1], nullptr, 16);
1909 if (!hex_value || hex_value > 0x10FFFF)
1911 hex_value = 0x003F; // Unicode 'Question Mark'
1913 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1914 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1916 g_strfreev(hex_code);
1917 g_string_free(str, TRUE);
1918 g_free(hex_code_full);
1923 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1925 guint64 star_symbol;
1927 star_symbol = star_rating_symbol_test(widget, data);
1928 c_options->star_rating.star = star_symbol;
1931 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1933 guint64 rejected_symbol;
1935 rejected_symbol = star_rating_symbol_test(widget, data);
1936 c_options->star_rating.rejected = rejected_symbol;
1939 /* general options tab */
1940 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1944 GCancellable *cancellable;
1946 GtkWidget *progress;
1948 GFile *timezone_database_gq;
1949 gchar *timezone_database_user;
1952 static void config_tab_general(GtkWidget *notebook)
1957 GtkWidget *group_frame;
1958 GtkWidget *subgroup;
1960 GtkWidget *ct_button;
1963 gint hours, minutes, remainder;
1965 GtkWidget *star_rating_entry;
1967 gchar *rating_symbol;
1970 gchar *download_locn;
1971 GNetworkMonitor *net_mon;
1972 GSocketConnectable *tz_org;
1973 gboolean internet_available = FALSE;
1976 vbox = scrolled_notebook_page(notebook, _("General"));
1978 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1980 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1981 add_thumb_size_menu(table, 0, 0, _("Size:"));
1982 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1984 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1985 pref_label_new(hbox, _("Custom size: "));
1986 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1987 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1989 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1990 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1992 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1993 pref_checkbox_link_sensitivity(ct_button, subgroup);
1995 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1996 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1997 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1998 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1999 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
2000 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2001 G_CALLBACK(cache_geeqie_cb), nullptr);
2003 group_frame = pref_frame_new(subgroup, TRUE,
2004 _("Store thumbnails local to image folder (non-standard)"),
2005 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2006 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
2007 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
2008 G_CALLBACK(cache_local_cb), nullptr);
2010 group_frame = pref_frame_new(subgroup, TRUE,
2011 _("Use standard thumbnail style and cache, shared with other applications"),
2012 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2013 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2014 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2015 G_CALLBACK(cache_standard_cb), nullptr);
2017 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2018 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2020 pref_checkbox_new_int(group, _("Thumbnail color management"),
2021 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2023 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2025 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2026 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2028 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
2029 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2030 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2033 pref_spacer(group, PREF_PAD_GROUP);
2035 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2037 c_options->star_rating.star = options->star_rating.star;
2038 c_options->star_rating.rejected = options->star_rating.rejected;
2040 str = g_string_new(nullptr);
2041 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2042 pref_label_new(hbox, _("Star character: "));
2043 str = g_string_append_unichar(str, options->star_rating.star);
2044 pref_label_new(hbox, g_strdup(str->str));
2045 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2046 star_rating_entry = gtk_entry_new();
2047 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2048 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2049 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2050 gtk_widget_show(star_rating_entry);
2051 button = pref_button_new(nullptr, nullptr, _("Set"),
2052 G_CALLBACK(star_rating_star_test_cb), hbox);
2053 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2054 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2055 gtk_widget_show(button);
2056 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."));
2057 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2058 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2059 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2060 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2061 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2062 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2063 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2064 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2065 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2066 G_CALLBACK(star_rating_star_icon_cb),
2069 g_string_free(str, TRUE);
2070 g_free(rating_symbol);
2072 str = g_string_new(nullptr);
2073 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2074 pref_label_new(hbox, _("Rejected character: "));
2075 str = g_string_append_unichar(str, options->star_rating.rejected);
2076 pref_label_new(hbox, g_strdup(str->str));
2077 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2078 star_rating_entry = gtk_entry_new();
2079 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2080 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2081 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2082 gtk_widget_show(star_rating_entry);
2083 button = pref_button_new(nullptr, nullptr, _("Set"),
2084 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2085 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2086 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2087 gtk_widget_show(button);
2088 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."));
2089 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2090 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2091 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2092 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2093 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2094 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2095 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2096 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2097 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2098 G_CALLBACK(star_rating_rejected_icon_cb),
2101 g_string_free(str, TRUE);
2102 g_free(rating_symbol);
2104 pref_spacer(group, PREF_PAD_GROUP);
2106 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2108 c_options->slideshow.delay = options->slideshow.delay;
2109 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2110 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2111 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2112 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2113 SLIDESHOW_SUBSECOND_PRECISION;
2115 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2117 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2119 options->slideshow.delay ? hours : 0.0,
2120 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2121 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2122 spin = pref_spin_new(hbox, ":" , nullptr,
2124 options->slideshow.delay ? minutes: 0.0,
2125 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2126 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2127 spin = pref_spin_new(hbox, ":", nullptr,
2128 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2129 options->slideshow.delay ? seconds : 10.0,
2130 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2131 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2133 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2134 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2136 pref_spacer(group, PREF_PAD_GROUP);
2138 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2140 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2141 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2142 pref_checkbox_new_int(group, _("Preload next image"),
2143 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2145 pref_checkbox_new_int(group, _("Refresh on file change"),
2146 options->update_on_time_change, &c_options->update_on_time_change);
2149 pref_spacer(group, PREF_PAD_GROUP);
2151 group = pref_group_new(vbox, FALSE, _("Menu style"), GTK_ORIENTATION_VERTICAL);
2153 pref_checkbox_new_int(group, _("☰ style menu button (NOTE! Geeqie must be restarted for change to take effect)"),
2154 options->hamburger_menu, &c_options->hamburger_menu);
2155 gtk_widget_set_tooltip_text(group, _("Use a ☰ style menu button instead of the classic style across the top of the frame"));
2157 pref_spacer(group, PREF_PAD_GROUP);
2159 group = pref_group_new(vbox, FALSE, _("Expand toolbar"), GTK_ORIENTATION_VERTICAL);
2161 pref_checkbox_new_int(group, _("Expand menu/toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2162 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2163 gtk_widget_set_tooltip_text(group, _("Expand the menu/toolbar to the full width of the window"));
2165 pref_spacer(group, PREF_PAD_GROUP);
2167 group = pref_group_new(vbox, FALSE, _("Hide Selectable Bars"), GTK_ORIENTATION_VERTICAL);
2169 pref_checkbox_new_int(group, _("Menu bar"),
2170 options->selectable_bars.menu_bar, &c_options->selectable_bars.menu_bar);
2172 pref_checkbox_new_int(group, _("Tool bar"),
2173 options->selectable_bars.tool_bar, &c_options->selectable_bars.tool_bar);
2175 pref_checkbox_new_int(group, _("Status bar"),
2176 options->selectable_bars.status_bar, &c_options->selectable_bars.status_bar);
2177 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"));
2179 pref_spacer(group, PREF_PAD_GROUP);
2181 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2183 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2184 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2185 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2186 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"));
2188 pref_spacer(group, PREF_PAD_GROUP);
2192 net_mon = g_network_monitor_get_default();
2193 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2196 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2197 g_object_unref(tz_org);
2200 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2201 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2203 if (!internet_available)
2205 gtk_widget_set_sensitive(group, FALSE);
2208 tz = g_new0(TZData, 1);
2210 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2211 basename = g_path_get_basename(path);
2212 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2216 if (isfile(tz->timezone_database_user))
2218 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2222 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2225 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2226 pref_label_new(GTK_WIDGET(hbox), download_locn);
2227 g_free(download_locn);
2229 if (!internet_available)
2231 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"));
2235 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"));
2237 gtk_widget_show(button);
2239 pref_spacer(group, PREF_PAD_GROUP);
2241 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2243 help_search_engine_entry = gtk_entry_new();
2244 gq_gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2245 gq_gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2246 gtk_widget_show(help_search_engine_entry);
2248 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"));
2250 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2251 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2252 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2253 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2254 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2255 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2256 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2257 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2258 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2259 G_CALLBACK(help_search_engine_entry_icon_cb),
2260 help_search_engine_entry);
2264 static void config_tab_image(GtkWidget *notebook)
2269 GtkWidget *ct_button;
2270 GtkWidget *enlargement_button;
2274 vbox = scrolled_notebook_page(notebook, _("Image"));
2276 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2278 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2279 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2281 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2282 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2284 c_options->image.zoom_increment = options->image.zoom_increment;
2285 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2286 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2287 G_CALLBACK(zoom_increment_cb), nullptr);
2288 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2290 c_options->image.zoom_style = options->image.zoom_style;
2291 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2292 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2294 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2296 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2297 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2298 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2299 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2301 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2302 pref_checkbox_link_sensitivity(enlargement_button, spin);
2303 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."));
2305 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2306 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2307 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2308 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2310 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2311 pref_checkbox_link_sensitivity(ct_button, spin);
2312 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."));
2314 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2316 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2317 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2319 options->image.tile_size, &c_options->image.tile_size);
2320 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."));
2322 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2324 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2325 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2327 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2328 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2330 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2331 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2333 c_options->image.border_color = options->image.border_color;
2335 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2336 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2338 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2339 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2341 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2342 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2347 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2349 LayoutWindow *lw = nullptr;
2350 gchar *default_path;
2353 /* Get current lw */
2356 tmp_id = lw->options.id;
2357 lw->options.id = g_strdup("");
2359 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2360 save_default_layout_options_to_file(default_path, options, lw);
2361 g_free(lw->options.id);
2362 lw->options.id = tmp_id;
2363 g_free(default_path);
2366 static gboolean popover_cb(gpointer data)
2368 auto popover = static_cast<GtkPopover *>(data);
2370 gtk_popover_popdown(popover);
2375 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2377 gtk_popover_popup(popover);
2379 g_timeout_add(2000, popover_cb, popover);
2382 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2386 popover = gtk_popover_new(parent);
2387 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2388 gq_gtk_container_add(GTK_WIDGET(popover), child);
2389 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2390 gtk_widget_show (child);
2395 static void config_tab_windows(GtkWidget *notebook)
2400 GtkWidget *subgroup;
2402 GtkWidget *checkbox;
2403 GtkWidget *ct_button;
2406 vbox = scrolled_notebook_page(notebook, _("Windows"));
2408 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2410 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2411 options->save_window_positions, &c_options->save_window_positions);
2413 checkbox = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2414 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2415 pref_checkbox_link_sensitivity(ct_button, checkbox);
2417 checkbox = pref_checkbox_new_int(group, _("Remember window workspace"),
2418 options->save_window_workspace, &c_options->save_window_workspace);
2419 pref_checkbox_link_sensitivity(ct_button, checkbox);
2421 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2422 options->tools_restore_state, &c_options->tools_restore_state);
2424 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2425 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2427 pref_checkbox_new_int(group, _("Show window IDs"),
2428 options->show_window_ids, &c_options->show_window_ids);
2430 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2431 pref_label_new(subgroup, _("Use current layout for default: "));
2432 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2436 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2437 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2438 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2440 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2442 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2443 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2445 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2446 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2447 options->image.limit_window_size, &c_options->image.limit_window_size);
2448 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2450 options->image.max_window_size, &c_options->image.max_window_size);
2451 pref_checkbox_link_sensitivity(ct_button, spin);
2453 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2455 c_options->fullscreen.screen = options->fullscreen.screen;
2456 c_options->fullscreen.above = options->fullscreen.above;
2457 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2458 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2459 gtk_widget_show(hbox);
2461 pref_checkbox_new_int(group, _("Smooth image flip"),
2462 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2463 pref_checkbox_new_int(group, _("Disable screen saver"),
2464 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2468 PRE_FORMATTED_COLUMNS = 5
2470 static void config_tab_osd(GtkWidget *notebook)
2476 GtkWidget *image_overlay_template_view;
2477 GtkWidget *scrolled;
2478 GtkWidget *scrolled_pre_formatted;
2479 GtkTextBuffer *buffer;
2481 GtkWidget *subgroup;
2483 vbox = scrolled_notebook_page(notebook, _("OSD"));
2485 image_overlay_template_view = gtk_text_view_new();
2487 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2489 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2491 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2492 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2493 gq_gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2494 gtk_widget_show(scrolled_pre_formatted);
2495 gtk_widget_show(subgroup);
2497 pref_line(group, PREF_PAD_GAP);
2499 pref_label_new(group, _("Image overlay template"));
2501 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2502 gtk_widget_set_size_request(scrolled, 200, 150);
2503 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2504 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2505 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2506 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2507 gtk_widget_show(scrolled);
2509 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2510 _("Extensive formatting options are shown in the Help file"));
2512 gq_gtk_container_add(GTK_WIDGET(scrolled), image_overlay_template_view);
2513 gtk_widget_show(image_overlay_template_view);
2515 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2517 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2518 G_CALLBACK(image_overlay_set_font_cb), notebook);
2520 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2521 gtk_widget_show(button);
2523 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"), G_CALLBACK(image_overlay_set_text_color_cb), nullptr);
2524 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2525 gtk_widget_show(button);
2527 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"), G_CALLBACK(image_overlay_set_background_color_cb), nullptr);
2528 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2529 gtk_widget_show(button);
2530 image_overlay_set_text_colors();
2532 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2533 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2534 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2535 gtk_widget_show(button);
2537 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2538 G_CALLBACK(image_overlay_help_cb), nullptr);
2539 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2540 gtk_widget_show(button);
2542 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2543 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2544 g_signal_connect(G_OBJECT(buffer), "changed",
2545 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2547 pref_line(group, PREF_PAD_GAP);
2549 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2550 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2551 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2552 gtk_widget_show(hbox);
2553 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2554 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2555 gtk_widget_show(label);
2556 pref_spacer(group,TRUE);
2558 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2559 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2560 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2561 gtk_widget_show(hbox);
2562 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2563 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2564 gtk_widget_show(label);
2565 pref_spacer(group,TRUE);
2567 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2568 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2569 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2570 gtk_widget_show(hbox);
2571 label = gtk_label_new(_("%path:39%"));
2572 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2573 gtk_widget_show(label);
2574 pref_spacer(group,TRUE);
2576 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2577 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2578 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2579 gtk_widget_show(hbox);
2580 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%"));
2581 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2582 gtk_widget_show(label);
2583 pref_spacer(group,TRUE);
2585 group = pref_group_new(vbox, FALSE, _("Pango markup"), 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(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2590 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2591 gtk_widget_show(label);
2594 static GtkTreeModel *create_class_model()
2596 GtkListStore *model;
2600 /* create list store */
2601 model = gtk_list_store_new(1, G_TYPE_STRING);
2602 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2604 gtk_list_store_append(model, &iter);
2605 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2607 return GTK_TREE_MODEL (model);
2612 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2614 gint n = GPOINTER_TO_INT(data);
2616 FilterEntry *filter_a;
2617 FilterEntry *filter_b;
2619 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2620 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2624 case FILETYPES_COLUMN_ENABLED:
2626 ret = filter_a->enabled - filter_b->enabled;
2629 case FILETYPES_COLUMN_FILTER:
2631 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2634 case FILETYPES_COLUMN_DESCRIPTION:
2636 ret = g_utf8_collate(filter_a->description, filter_b->description);
2639 case FILETYPES_COLUMN_CLASS:
2641 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2644 case FILETYPES_COLUMN_WRITABLE:
2646 ret = filter_a->writable - filter_b->writable;
2649 case FILETYPES_COLUMN_SIDECAR:
2651 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2655 g_return_val_if_reached(0);
2661 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2664 gboolean ret = TRUE;
2666 gtk_tree_model_get(model, iter, 0, &fe, -1);
2668 if (g_strstr_len(fe->extensions, -1, key))
2676 static void config_tab_files(GtkWidget *notebook)
2683 GtkWidget *ct_button;
2684 GtkWidget *scrolled;
2685 GtkWidget *filter_view;
2686 GtkCellRenderer *renderer;
2687 GtkTreeSelection *selection;
2688 GtkTreeViewColumn *column;
2690 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2692 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2694 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2695 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2696 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2697 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2698 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);
2699 pref_checkbox_new_int(group, _("Disable file extension checks"),
2700 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2702 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2703 options->file_filter.disable, &c_options->file_filter.disable);
2706 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2708 sidecar_ext_entry = gtk_entry_new();
2709 gq_gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2710 gq_gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2711 gtk_widget_show(sidecar_ext_entry);
2713 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2715 frame = pref_group_parent(group);
2716 g_signal_connect(G_OBJECT(ct_button), "toggled",
2717 G_CALLBACK(filter_disable_cb), frame);
2718 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2720 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2721 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2722 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2723 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2724 gtk_widget_show(scrolled);
2726 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2727 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2728 g_object_unref(filter_store);
2729 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2730 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2732 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2734 column = gtk_tree_view_column_new();
2735 gtk_tree_view_column_set_title(column, _("Enabled"));
2736 gtk_tree_view_column_set_resizable(column, TRUE);
2738 renderer = gtk_cell_renderer_toggle_new();
2739 g_signal_connect(G_OBJECT(renderer), "toggled",
2740 G_CALLBACK(filter_store_enable_cb), filter_store);
2741 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2742 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2743 GINT_TO_POINTER(FE_ENABLE), nullptr);
2744 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);
2745 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2746 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2748 column = gtk_tree_view_column_new();
2749 gtk_tree_view_column_set_title(column, _("Filter"));
2750 gtk_tree_view_column_set_resizable(column, TRUE);
2751 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);
2752 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2754 renderer = gtk_cell_renderer_text_new();
2755 g_signal_connect(G_OBJECT(renderer), "edited",
2756 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2757 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2758 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2759 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2760 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2761 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2763 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2764 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2765 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2767 column = gtk_tree_view_column_new();
2768 gtk_tree_view_column_set_title(column, _("Description"));
2769 gtk_tree_view_column_set_resizable(column, TRUE);
2770 gtk_tree_view_column_set_fixed_width(column, 200);
2771 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2773 renderer = gtk_cell_renderer_text_new();
2774 g_signal_connect(G_OBJECT(renderer), "edited",
2775 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2776 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
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_DESCRIPTION), nullptr);
2780 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2781 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_DESCRIPTION, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_DESCRIPTION), nullptr);
2782 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2784 column = gtk_tree_view_column_new();
2785 gtk_tree_view_column_set_title(column, _("Class"));
2786 gtk_tree_view_column_set_resizable(column, TRUE);
2787 renderer = gtk_cell_renderer_combo_new();
2788 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2789 "model", create_class_model(),
2794 g_signal_connect(G_OBJECT(renderer), "edited",
2795 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2796 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2797 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2798 GINT_TO_POINTER(FE_CLASS), nullptr);
2799 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2800 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);
2801 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2803 column = gtk_tree_view_column_new();
2804 gtk_tree_view_column_set_title(column, _("Writable"));
2805 gtk_tree_view_column_set_resizable(column, FALSE);
2806 renderer = gtk_cell_renderer_toggle_new();
2807 g_signal_connect(G_OBJECT(renderer), "toggled",
2808 G_CALLBACK(filter_store_writable_cb), filter_store);
2809 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2810 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2811 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2812 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2813 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);
2814 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2816 column = gtk_tree_view_column_new();
2817 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2818 gtk_tree_view_column_set_resizable(column, FALSE);
2819 renderer = gtk_cell_renderer_toggle_new();
2820 g_signal_connect(G_OBJECT(renderer), "toggled",
2821 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2822 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2823 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2824 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2825 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2826 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);
2827 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2829 filter_store_populate();
2830 gq_gtk_container_add(GTK_WIDGET(scrolled), filter_view);
2831 gtk_widget_show(filter_view);
2833 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2835 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2836 G_CALLBACK(filter_default_cb), filter_view);
2837 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2838 gtk_widget_show(button);
2840 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2841 G_CALLBACK(filter_remove_cb), filter_view);
2842 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2843 gtk_widget_show(button);
2845 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2846 G_CALLBACK(filter_add_cb), filter_view);
2847 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2848 gtk_widget_show(button);
2852 static void config_tab_metadata(GtkWidget *notebook)
2857 GtkWidget *ct_button;
2859 GtkWidget *tmp_widget;
2861 GtkWidget *text_label;
2863 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2866 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2868 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2870 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."));
2871 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2872 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2874 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2876 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2877 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2878 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"));
2879 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2881 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)"));
2882 gtk_widget_set_tooltip_markup(ct_button, markup);
2886 gtk_widget_set_sensitive(ct_button, FALSE);
2889 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2890 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2891 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)"));
2892 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2895 label = pref_label_new(group, "");
2896 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(), "/");
2897 gtk_label_set_markup (GTK_LABEL(label), markup);
2900 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2901 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2902 gtk_widget_set_margin_start(label, 22);
2903 pref_spacer(group, PREF_PAD_GROUP);
2905 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2907 gtk_widget_set_sensitive(group, FALSE);
2910 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2911 pref_checkbox_link_sensitivity(ct_button, hbox);
2913 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);
2914 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2916 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);
2918 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2920 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2921 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2922 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2923 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", ")");
2924 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2927 pref_spacer(group, PREF_PAD_GROUP);
2929 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2931 gtk_widget_set_sensitive(group, FALSE);
2934 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);
2936 pref_spacer(group, PREF_PAD_GROUP);
2938 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2939 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);
2940 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2942 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2943 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2945 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2946 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"));
2949 gtk_widget_set_sensitive(ct_button, FALSE);
2952 pref_spacer(group, PREF_PAD_GROUP);
2954 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2956 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2958 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2959 pref_checkbox_link_sensitivity(ct_button, hbox);
2961 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2963 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2965 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2967 pref_spacer(group, PREF_PAD_GROUP);
2970 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2972 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2973 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2976 pref_spacer(group, PREF_PAD_GROUP);
2978 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2980 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2981 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");
2986 struct KeywordFindData
2993 GtkWidget *button_close;
2994 GtkWidget *button_stop;
2995 GtkWidget *button_start;
2996 GtkWidget *progress;
3004 guint idle_id; /* event source id */
3008 KEYWORD_DIALOG_WIDTH = 400
3011 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
3013 GList *list_d = nullptr;
3014 GList *list_f = nullptr;
3018 filelist_read(dir_fd, &list_f, &list_d);
3022 filelist_read(dir_fd, &list_f, nullptr);
3025 list_f = filelist_filter(list_f, FALSE);
3026 list_d = filelist_filter(list_d, TRUE);
3028 kfd->list = g_list_concat(list_f, kfd->list);
3029 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3032 static void keywords_find_reset(KeywordFindData *kfd)
3034 filelist_free(kfd->list);
3035 kfd->list = nullptr;
3037 filelist_free(kfd->list_dir);
3038 kfd->list_dir = nullptr;
3041 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3043 auto kfd = static_cast<KeywordFindData *>(data);
3045 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3047 keywords_find_reset(kfd);
3048 generic_dialog_close(kfd->gd);
3052 static void keywords_find_finish(KeywordFindData *kfd)
3054 keywords_find_reset(kfd);
3056 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3057 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
3059 gtk_widget_set_sensitive(kfd->group, TRUE);
3060 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3061 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3062 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3065 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3067 auto kfd = static_cast<KeywordFindData *>(data);
3069 g_idle_remove_by_data(kfd);
3071 keywords_find_finish(kfd);
3074 static gboolean keywords_find_file(gpointer data)
3076 auto kfd = static_cast<KeywordFindData *>(data);
3078 GtkTextBuffer *buffer;
3086 fd = static_cast<FileData *>(kfd->list->data);
3087 kfd->list = g_list_remove(kfd->list, fd);
3089 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3090 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3094 gtk_text_buffer_get_end_iter(buffer, &iter);
3095 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3096 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3098 keywords = keywords->next;
3101 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3102 file_data_unref(fd);
3103 g_list_free_full(keywords, g_free);
3105 return (G_SOURCE_CONTINUE);
3112 fd = static_cast<FileData *>(kfd->list_dir->data);
3113 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3115 keywords_find_folder(kfd, fd);
3117 file_data_unref(fd);
3119 return G_SOURCE_CONTINUE;
3122 keywords_find_finish(kfd);
3124 return G_SOURCE_REMOVE;
3127 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3129 auto kfd = static_cast<KeywordFindData *>(data);
3132 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3134 path = remove_trailing_slash((gq_gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3135 parse_out_relatives(path);
3139 warning_dialog(_("Invalid folder"),
3140 _("The specified folder can not be found."),
3141 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3147 gtk_widget_set_sensitive(kfd->group, FALSE);
3148 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3149 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3150 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3151 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3153 dir_fd = file_data_new_dir(path);
3154 keywords_find_folder(kfd, dir_fd);
3155 file_data_unref(dir_fd);
3156 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3162 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3164 KeywordFindData *kfd;
3168 kfd = g_new0(KeywordFindData, 1);
3170 kfd->gd = generic_dialog_new(_("Search for keywords"),
3171 "search_for_keywords",
3174 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3175 kfd->gd->cancel_cb = keywords_find_close_cb;
3176 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3177 keywords_find_close_cb, FALSE);
3178 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3179 keywords_find_start_cb, FALSE);
3180 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3181 keywords_find_stop_cb, FALSE);
3182 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3184 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3186 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3187 pref_spacer(hbox, PREF_PAD_INDENT);
3188 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3190 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3191 pref_label_new(hbox, _("Folder:"));
3193 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3194 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3195 gq_gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3196 gtk_widget_show(label);
3198 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3200 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3201 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3203 kfd->progress = gtk_entry_new();
3204 gtk_widget_set_can_focus(kfd->progress, FALSE);
3205 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3206 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3207 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3208 gtk_widget_show(kfd->progress);
3210 kfd->spinner = gtk_spinner_new();
3211 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3212 gtk_widget_show(kfd->spinner);
3214 kfd->list = nullptr;
3216 gtk_widget_show(kfd->gd->dialog);
3219 static void keywords_find_cb(GtkWidget *widget, gpointer)
3221 const gchar *path = layout_get_path(nullptr);
3223 if (!path || !*path) path = homedir();
3224 keywords_find_dialog(widget, path);
3227 static void config_tab_keywords_save()
3229 GtkTextIter start, end;
3230 GtkTextBuffer *buffer;
3231 GList *kw_list = nullptr;
3237 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3238 gtk_text_buffer_get_bounds(buffer, &start, &end);
3240 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3242 kw_split = strtok(buffer_text, "\n");
3243 while (kw_split != nullptr)
3249 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3258 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3260 kw_split = strtok(nullptr, "\n");
3263 keyword_list_set(kw_list);
3265 g_list_free_full(kw_list, g_free);
3266 g_free(buffer_text);
3269 static void config_tab_keywords(GtkWidget *notebook)
3275 GtkWidget *scrolled;
3277 GtkTextBuffer *buffer;
3280 GspellTextView *gspell_view;
3283 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3285 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3287 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3289 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3290 G_CALLBACK(keywords_find_cb), keyword_text);
3291 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3294 keyword_text = gtk_text_view_new();
3295 gtk_widget_set_size_request(keyword_text, 20, 20);
3296 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3297 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3298 gtk_widget_show(scrolled);
3301 if (options->metadata.check_spelling)
3303 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3304 gspell_text_view_basic_setup(gspell_view);
3308 gq_gtk_container_add(GTK_WIDGET(scrolled), keyword_text);
3309 gtk_widget_show(keyword_text);
3311 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3313 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3314 gtk_text_buffer_create_tag(buffer, "monospace",
3315 "family", "monospace", NULL);
3317 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3318 gtk_text_buffer_get_start_iter(buffer, &iter);
3319 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3322 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3324 GList *kwl = keyword_list_get();
3325 kwl = g_list_first(kwl);
3328 gtk_text_buffer_get_end_iter (buffer, &iter);
3329 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3330 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3335 gtk_text_buffer_set_modified(buffer, FALSE);
3342 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3344 auto option = static_cast<gint *>(data);
3346 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3350 *option = INTENT_PERCEPTUAL;
3353 *option = INTENT_RELATIVE_COLORIMETRIC;
3356 *option = INTENT_SATURATION;
3359 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3364 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3365 gint option, gint *option_c)
3372 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3374 combo = gtk_combo_box_text_new();
3376 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3377 if (option == INTENT_PERCEPTUAL) current = 0;
3378 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3379 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3380 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3381 if (option == INTENT_SATURATION) current = 2;
3382 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3383 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3385 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3387 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3389 g_signal_connect(G_OBJECT(combo), "changed",
3390 G_CALLBACK(intent_menu_cb), option_c);
3392 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3393 gtk_widget_show(combo);
3397 static void config_tab_color(GtkWidget *notebook)
3406 vbox = scrolled_notebook_page(notebook, _("Color management"));
3408 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3410 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3413 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3414 gtk_grid_set_column_spacing(GTK_GRID(table), PREF_PAD_GAP);
3416 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3417 pref_label_bold(label, TRUE, FALSE);
3419 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3420 pref_label_bold(label, TRUE, FALSE);
3422 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3423 pref_label_bold(label, TRUE, FALSE);
3425 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3430 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3431 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3434 entry = gtk_entry_new();
3435 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3436 if (options->color_profile.input_name[i])
3438 gq_gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3440 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);
3441 gtk_widget_show(entry);
3442 color_profile_input_name_entry[i] = entry;
3444 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3445 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3446 gtk_widget_set_size_request(entry, 160, -1);
3447 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);
3448 gtk_widget_show(tabcomp);
3449 color_profile_input_file_entry[i] = entry;
3452 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3454 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3456 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3457 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3459 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3461 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3462 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3463 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3464 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3465 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3467 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3469 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);
3471 gtk_widget_show(tabcomp);
3474 /* advanced entry tab */
3475 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3477 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3479 c_options->file_ops.use_system_trash = FALSE;
3480 c_options->file_ops.no_trash = FALSE;
3484 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3486 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3488 c_options->file_ops.use_system_trash = TRUE;
3489 c_options->file_ops.no_trash = FALSE;
3493 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3495 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3497 c_options->file_ops.no_trash = TRUE;
3501 static void config_tab_behavior(GtkWidget *notebook)
3508 GtkWidget *ct_button;
3512 GtkWidget *with_rename;
3513 GtkWidget *collections_on_top;
3514 GtkWidget *hide_window_in_fullscreen;
3515 GtkWidget *checkbox;
3518 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3520 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3522 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3523 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3524 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3525 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3526 pref_checkbox_new_int(group, _("Enable Delete key"),
3527 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3529 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3530 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3532 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3533 pref_checkbox_link_sensitivity(ct_button, hbox);
3535 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3536 pref_label_new(hbox, _("Folder:"));
3538 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3539 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3540 gq_gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3541 gtk_widget_show(tabcomp);
3543 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3544 pref_checkbox_link_sensitivity(ct_button, hbox);
3546 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3547 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3548 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3549 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3550 button = pref_button_new(nullptr, nullptr, _("View"),
3551 G_CALLBACK(safe_delete_view_cb), nullptr);
3552 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3553 gtk_widget_show(button);
3555 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3556 G_CALLBACK(safe_delete_clear_cb), nullptr);
3557 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3559 c_options->file_ops.no_trash = options->file_ops.no_trash;
3560 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3562 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3563 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3565 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3566 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3568 gtk_widget_show(button);
3570 pref_spacer(group, PREF_PAD_GROUP);
3573 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3575 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3576 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3578 pref_checkbox_new_int(group, _("In place renaming"),
3579 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3581 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3582 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3584 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3585 options->circular_selection_lists, &c_options->circular_selection_lists);
3586 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3588 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3589 options->marks_save, &c_options->marks_save);
3590 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3592 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3593 options->with_rename, &c_options->with_rename);
3594 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3596 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3597 options->collections_duplicates, &c_options->collections_duplicates);
3598 gtk_widget_set_tooltip_text(collections_on_top,"Allow the same image to be in a Collection more than once");
3600 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3601 options->collections_on_top, &c_options->collections_on_top);
3602 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3604 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3605 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3606 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3608 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3609 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3611 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);
3612 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."));
3614 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3615 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3617 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3618 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3620 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3621 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3623 pref_spacer(group, PREF_PAD_GROUP);
3625 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3627 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3628 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3629 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3630 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3631 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3632 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3633 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3634 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3635 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3636 options->image_l_click_archive, &c_options->image_l_click_archive);
3637 pref_checkbox_new_int(group, _("Play video by left click on image"),
3638 options->image_l_click_video, &c_options->image_l_click_video);
3639 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3640 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3642 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3643 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3644 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3645 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3646 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3648 pref_spacer(group, PREF_PAD_GROUP);
3650 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3652 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3653 options->override_disable_gpu, &c_options->override_disable_gpu);
3654 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3657 pref_spacer(group, PREF_PAD_GROUP);
3659 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3661 pref_spin_new_int(group, _("Debug level:"), nullptr,
3662 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3664 pref_checkbox_new_int(group, _("Timer data"),
3665 options->log_window.timer_data, &c_options->log_window.timer_data);
3667 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3668 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3670 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3671 pref_label_new(hbox, _("Log Window F1 command: "));
3672 log_window_f1_entry = gtk_entry_new();
3673 gq_gtk_entry_set_text(GTK_ENTRY(log_window_f1_entry), options->log_window.action);
3674 gq_gtk_box_pack_start(GTK_BOX(hbox), log_window_f1_entry, FALSE, FALSE, 0);
3675 gtk_entry_set_width_chars(GTK_ENTRY(log_window_f1_entry), 15);
3676 gtk_widget_show(log_window_f1_entry);
3680 /* accelerators tab */
3682 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3684 gboolean ret = TRUE;
3689 gtk_tree_model_get(model, iter, column, &text, -1);
3690 text_nocase = g_utf8_casefold(text, -1);
3691 key_nocase = g_utf8_casefold(key, -1);
3693 if (g_strstr_len(text_nocase, -1, key_nocase))
3700 g_free(text_nocase);
3705 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3710 list = gtk_tree_view_get_columns(tree_view);
3711 col_num = g_list_index(list, column);
3715 gtk_tree_view_set_search_column(tree_view, col_num);
3718 static void config_tab_accelerators(GtkWidget *notebook)
3724 GtkWidget *scrolled;
3725 GtkWidget *accel_view;
3726 GtkCellRenderer *renderer;
3727 GtkTreeSelection *selection;
3728 GtkTreeViewColumn *column;
3730 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3732 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3734 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3735 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3736 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3737 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3738 gtk_widget_show(scrolled);
3740 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3742 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3743 g_object_unref(accel_store);
3744 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3745 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3747 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3749 renderer = gtk_cell_renderer_text_new();
3751 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3756 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3757 gtk_tree_view_column_set_resizable(column, TRUE);
3758 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3761 renderer = gtk_cell_renderer_accel_new();
3762 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3763 G_CALLBACK(accel_store_cleared_cb), accel_store);
3764 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3765 G_CALLBACK(accel_store_edited_cb), accel_store);
3768 g_object_set (renderer,
3770 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3773 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3778 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3779 gtk_tree_view_column_set_resizable(column, TRUE);
3780 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3782 renderer = gtk_cell_renderer_text_new();
3784 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3789 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3790 gtk_tree_view_column_set_resizable(column, TRUE);
3791 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3793 renderer = gtk_cell_renderer_text_new();
3795 column = gtk_tree_view_column_new_with_attributes("Accel",
3800 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3801 gtk_tree_view_column_set_resizable(column, TRUE);
3802 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3804 /* Search on text in column */
3805 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3806 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3807 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3808 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3809 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3811 accel_store_populate();
3812 gq_gtk_container_add(GTK_WIDGET(scrolled), accel_view);
3813 gtk_widget_show(accel_view);
3815 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3817 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3818 G_CALLBACK(accel_default_cb), accel_view);
3819 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3820 gtk_widget_show(button);
3822 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3823 G_CALLBACK(accel_reset_cb), accel_view);
3824 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3825 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3826 gtk_widget_show(button);
3828 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3829 G_CALLBACK(accel_clear_cb), accel_view);
3830 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3831 gtk_widget_show(button);
3834 /* toolbar main tab */
3835 static void config_tab_toolbar_main(GtkWidget *notebook)
3838 GtkWidget *toolbardata;
3841 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3843 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3845 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3846 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3847 gtk_widget_show(vbox);
3850 /* toolbar status tab */
3851 static void config_tab_toolbar_status(GtkWidget *notebook)
3854 GtkWidget *toolbardata;
3857 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3859 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3861 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3862 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3863 gtk_widget_show(vbox);
3867 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3869 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3872 static void config_tab_advanced(GtkWidget *notebook)
3875 GdkPixbufFormat *fm;
3877 GList *extensions_list = nullptr;
3878 GSList *formats_list;
3879 GString *types_string = g_string_new(nullptr);
3880 GtkWidget *alternate_checkbox;
3881 GtkWidget *dupes_threads_spin;
3883 GtkWidget *subgroup;
3885 GtkWidget *threads_string_label;
3886 GtkWidget *types_string_label;
3889 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3890 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3892 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3894 pref_spacer(group, PREF_PAD_GROUP);
3896 formats_list = gdk_pixbuf_get_formats();
3898 while (formats_list)
3900 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3901 extensions = gdk_pixbuf_format_get_extensions(fm);
3904 while (extensions[i])
3906 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3910 g_strfreev(extensions);
3911 formats_list = formats_list->next;
3914 while (extensions_list)
3916 if (types_string->len == 0)
3918 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3922 types_string = g_string_append(types_string, ", ");
3923 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3926 extensions_list = extensions_list->next;
3929 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3930 types_string_label = pref_label_new(group, types_string->str);
3931 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3933 pref_spacer(group, PREF_PAD_GROUP);
3935 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3936 external_preview_select_entry = gtk_entry_new();
3937 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3938 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3939 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3940 gtk_widget_show(tabcomp);
3942 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3943 external_preview_extract_entry = gtk_entry_new();
3944 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3945 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3946 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3947 gtk_widget_show(tabcomp);
3949 gtk_widget_show(vbox);
3951 g_slist_free(formats_list);
3952 g_list_free_full(extensions_list, g_free);
3953 g_string_free(types_string, TRUE);
3955 pref_spacer(group, PREF_PAD_GROUP);
3957 pref_line(vbox, PREF_PAD_SPACE);
3958 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3960 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.");
3961 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3963 pref_spacer(vbox, PREF_PAD_GROUP);
3965 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3966 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3968 pref_spacer(group, PREF_PAD_GROUP);
3970 pref_line(vbox, PREF_PAD_SPACE);
3972 group = pref_group_new(vbox, FALSE, _("Alternate similarity alogorithm"), GTK_ORIENTATION_VERTICAL);
3974 alternate_checkbox = pref_checkbox_new_int(group, _("Enable alternate similarity algorithm"), options->alternate_similarity_algorithm.enabled, &c_options->alternate_similarity_algorithm.enabled);
3976 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3977 pref_checkbox_link_sensitivity(alternate_checkbox, subgroup);
3979 alternate_checkbox = pref_checkbox_new_int(subgroup, _("Use grayscale"), options->alternate_similarity_algorithm.grayscale, &c_options->alternate_similarity_algorithm.grayscale);
3980 gtk_widget_set_tooltip_text(alternate_checkbox, _("Reduce fingerprint to grayscale"));
3984 static void config_tab_stereo(GtkWidget *notebook)
3992 GtkWidget *fs_button;
3993 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3995 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3997 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3998 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
4000 table = pref_table_new(group, 2, 2, TRUE, FALSE);
4001 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4002 pref_checkbox_new_int(box, _("Mirror left image"),
4003 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
4004 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4005 pref_checkbox_new_int(box, _("Flip left image"),
4006 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
4007 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4008 pref_checkbox_new_int(box, _("Mirror right image"),
4009 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
4010 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4011 pref_checkbox_new_int(box, _("Flip right image"),
4012 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
4013 pref_checkbox_new_int(group, _("Swap left and right images"),
4014 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
4015 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
4016 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
4018 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
4019 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
4020 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
4021 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4022 pref_checkbox_link_sensitivity(fs_button, box2);
4023 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
4024 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
4025 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
4026 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4027 pref_checkbox_new_int(box, _("Mirror left image"),
4028 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
4029 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4030 pref_checkbox_new_int(box, _("Flip left image"),
4031 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
4032 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4033 pref_checkbox_new_int(box, _("Mirror right image"),
4034 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
4035 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4036 pref_checkbox_new_int(box, _("Flip right image"),
4037 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
4038 pref_checkbox_new_int(box2, _("Swap left and right images"),
4039 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
4040 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
4041 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
4043 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
4044 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
4045 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
4046 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
4047 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
4048 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
4049 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
4050 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
4051 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
4052 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4053 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
4054 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4055 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
4056 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4060 /* Main preferences window */
4061 static void config_window_create(LayoutWindow *lw)
4063 GtkWidget *win_vbox;
4065 GtkWidget *notebook;
4067 GtkWidget *ct_button;
4069 if (!c_options) c_options = init_options(nullptr);
4071 configwindow = window_new("preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4072 DEBUG_NAME(configwindow);
4073 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4074 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4075 G_CALLBACK(config_window_delete), NULL);
4076 if (options->save_dialog_window_positions)
4078 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4079 gq_gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4083 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4085 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4086 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4088 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4089 gq_gtk_container_add(GTK_WIDGET(configwindow), win_vbox);
4090 gtk_widget_show(win_vbox);
4092 notebook = gtk_notebook_new();
4093 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4094 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4095 gq_gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4097 config_tab_general(notebook);
4098 config_tab_image(notebook);
4099 config_tab_osd(notebook);
4100 config_tab_windows(notebook);
4101 config_tab_accelerators(notebook);
4102 config_tab_files(notebook);
4103 config_tab_metadata(notebook);
4104 config_tab_keywords(notebook);
4105 config_tab_color(notebook);
4106 config_tab_stereo(notebook);
4107 config_tab_behavior(notebook);
4108 config_tab_toolbar_main(notebook);
4109 config_tab_toolbar_status(notebook);
4110 config_tab_advanced(notebook);
4112 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4114 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4115 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4116 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4117 gq_gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4118 gtk_widget_show(hbox);
4120 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4121 G_CALLBACK(config_window_help_cb), notebook);
4122 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4123 gtk_widget_set_can_default(button, TRUE);
4124 gtk_widget_show(button);
4126 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4127 G_CALLBACK(config_window_ok_cb), notebook);
4128 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4129 gtk_widget_set_can_default(button, TRUE);
4130 gtk_widget_grab_default(button);
4131 gtk_widget_show(button);
4135 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4136 G_CALLBACK(config_window_close_cb), nullptr);
4137 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4138 gtk_widget_set_can_default(button, TRUE);
4139 gtk_widget_show(button);
4141 if (!generic_dialog_get_alternative_button_order(configwindow))
4143 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4146 gtk_widget_show(notebook);
4148 gtk_widget_show(configwindow);
4152 *-----------------------------------------------------------------------------
4153 * config window show (public)
4154 *-----------------------------------------------------------------------------
4157 void show_config_window(LayoutWindow *lw)
4161 gtk_window_present(GTK_WINDOW(configwindow));
4165 config_window_create(lw);
4174 void show_about_window(LayoutWindow *lw)
4176 GDataInputStream *data_stream;
4177 GInputStream *in_stream_authors;
4178 GInputStream *in_stream_translators;
4180 GdkPixbuf *pixbuf_icon;
4181 GdkPixbuf *pixbuf_logo;
4185 gchar *authors[1000];
4187 gchar *timezone_path;
4196 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");
4198 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4199 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4201 cd = ZDOpenDatabase(timezone_path);
4204 copyright = g_string_append(copyright, ZDGetNotice(cd));
4208 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4210 ZDCloseDatabase(cd);
4212 g_free(timezone_path);
4214 copyright = g_string_append(copyright, "\n\nSome icons by https://www.flaticon.com");
4216 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4218 data_stream = g_data_input_stream_new(in_stream_authors);
4220 authors[0] = nullptr;
4221 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4223 authors[i_authors] = g_strdup(author_line);
4225 g_free(author_line);
4227 authors[i_authors] = nullptr;
4229 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4231 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4233 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4235 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4236 translators = static_cast<gchar *>(g_malloc0(size));
4237 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4238 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4240 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);
4242 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4243 artists[1] = nullptr;
4245 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4246 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4247 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4248 "title", _("About Geeqie"),
4250 "program-name", GQ_APPNAME,
4252 "logo", pixbuf_logo,
4253 "icon", pixbuf_icon,
4254 "website", GQ_WEBSITE,
4255 "website-label", "Website",
4256 "comments", comment,
4259 "translator-credits", translators,
4260 "wrap-license", TRUE,
4261 "license", copyright->str,
4264 g_string_free(copyright, TRUE);
4266 while(n < i_authors)
4274 g_free(translators);
4275 g_object_unref(data_stream);
4276 g_object_unref(in_stream_authors);
4277 g_object_unref(in_stream_translators);
4280 static void image_overlay_set_text_colors()
4282 c_options->image_overlay.text_red = options->image_overlay.text_red;
4283 c_options->image_overlay.text_green = options->image_overlay.text_green;
4284 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4285 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4286 c_options->image_overlay.background_red = options->image_overlay.background_red;
4287 c_options->image_overlay.background_green = options->image_overlay.background_green;
4288 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4289 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4293 *-----------------------------------------------------------------------------
4294 * timezone database routines
4295 *-----------------------------------------------------------------------------
4298 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4300 GError *error = nullptr;
4301 auto tz = static_cast<TZData *>(data);
4302 gchar *tmp_filename;
4303 gchar *timezone_bin;
4304 gchar *tmp_dir = nullptr;
4307 if (!g_cancellable_is_cancelled(tz->cancellable))
4309 generic_dialog_close(tz->gd);
4313 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4315 tmp_filename = g_file_get_path(tz->tmp_g_file);
4316 fd = file_data_new_simple(tmp_filename);
4317 tmp_dir = open_archive(fd);
4321 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4322 if (isfile(timezone_bin))
4324 move_file(timezone_bin, tz->timezone_database_user);
4328 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4331 g_free(timezone_bin);
4332 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4336 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4338 g_free(tmp_filename);
4339 file_data_unref(fd);
4343 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4346 g_file_delete(tz->tmp_g_file, nullptr, &error);
4347 g_object_unref(tz->tmp_g_file);
4348 tz->tmp_g_file = nullptr;
4349 g_object_unref(tz->cancellable);
4350 g_object_unref(tz->timezone_database_gq);
4353 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4355 auto tz = static_cast<TZData *>(data);
4357 if (!g_cancellable_is_cancelled(tz->cancellable))
4359 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4363 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4365 auto tz = static_cast<TZData *>(data);
4367 g_cancellable_cancel(tz->cancellable);
4370 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4372 auto tz = static_cast<TZData *>(data);
4373 GError *error = nullptr;
4374 GFileIOStream *io_stream;
4381 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4385 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4386 log_printf("Error: Download timezone database failed:\n%s", error->message);
4387 g_error_free(error);
4388 g_object_unref(tz->tmp_g_file);
4392 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4394 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4396 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4398 tz->progress = gtk_progress_bar_new();
4399 gq_gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4400 gtk_widget_show(tz->progress);
4402 gtk_widget_show(tz->gd->dialog);
4403 tz->cancellable = g_cancellable_new();
4404 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);
4406 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4409 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */