2 * Copyright (C) 2006 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "preferences.h"
25 #include "bar-keywords.h"
27 #include "color-man.h"
30 #include "filefilter.h"
31 #include "fullscreen.h"
33 #include "image-overlay.h"
35 #include "layout-util.h"
39 #include "pixbuf-util.h"
41 #include "slideshow.h"
45 #include "ui-fileops.h"
47 #include "ui-tabcomp.h"
49 #include "zonedetect.h"
60 #include <gspell/gspell.h>
63 #define EDITOR_NAME_MAX_LENGTH 32
64 #define EDITOR_COMMAND_MAX_LENGTH 1024
66 static void image_overlay_set_text_colors();
68 GtkWidget *keyword_text;
69 static void config_tab_keywords_save();
77 static ThumbSize thumb_size_list[] =
112 FILETYPES_COLUMN_ENABLED = 0,
113 FILETYPES_COLUMN_FILTER,
114 FILETYPES_COLUMN_DESCRIPTION,
115 FILETYPES_COLUMN_CLASS,
116 FILETYPES_COLUMN_WRITABLE,
117 FILETYPES_COLUMN_SIDECAR,
118 FILETYPES_COLUMN_COUNT
121 const gchar *format_class_list[] = {
132 /* config memory values */
133 static ConfOptions *c_options = nullptr;
140 static GtkWidget *configwindow = nullptr;
141 static GtkListStore *filter_store = nullptr;
142 static GtkTreeStore *accel_store = nullptr;
144 static GtkWidget *safe_delete_path_entry;
146 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
147 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
148 static GtkWidget *color_profile_screen_file_entry;
149 static GtkWidget *external_preview_select_entry;
150 static GtkWidget *external_preview_extract_entry;
152 static GtkWidget *sidecar_ext_entry;
153 static GtkWidget *help_search_engine_entry;
155 static GtkWidget *log_window_f1_entry;
158 #define CONFIG_WINDOW_DEF_WIDTH 700
159 #define CONFIG_WINDOW_DEF_HEIGHT 600
162 *-----------------------------------------------------------------------------
163 * option widget callbacks (private)
164 *-----------------------------------------------------------------------------
167 static void zoom_increment_cb(GtkWidget *spin, gpointer)
169 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
172 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
174 gint mins_secs_tenths, delay;
176 mins_secs_tenths = c_options->slideshow.delay %
177 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
179 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
180 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
183 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
184 SLIDESHOW_SUBSECOND_PRECISION;
187 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
189 gint hours, secs_tenths, delay;
191 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
192 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
194 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
195 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
196 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
198 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
199 SLIDESHOW_SUBSECOND_PRECISION;
202 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
204 gint hours_mins, delay;
206 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
208 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
209 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
210 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
212 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
213 SLIDESHOW_SUBSECOND_PRECISION;
217 *-----------------------------------------------------------------------------
218 * sync program to config window routine (private)
219 *-----------------------------------------------------------------------------
222 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
228 buf = gq_gtk_entry_get_text(GTK_ENTRY(entry));
229 if (buf && strlen(buf) > 0)
234 *option = g_strdup(buf);
239 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
241 gchar *accel_path, *accel;
243 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
245 if (accel_path && accel_path[0])
248 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
249 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
259 static void config_window_apply()
261 gboolean refresh = FALSE;
266 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
268 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
269 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
270 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
271 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
272 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
273 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
275 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
276 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
277 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
278 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
279 options->file_ops.no_trash = c_options->file_ops.no_trash;
280 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
281 options->tools_restore_state = c_options->tools_restore_state;
282 options->save_window_positions = c_options->save_window_positions;
283 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
284 options->save_window_workspace = c_options->save_window_workspace;
285 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
286 options->show_window_ids = c_options->show_window_ids;
287 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
288 options->image.zoom_2pass = c_options->image.zoom_2pass;
289 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
290 options->image.limit_window_size = c_options->image.limit_window_size;
291 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
292 options->image.max_window_size = c_options->image.max_window_size;
293 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
294 options->image.max_autofit_size = c_options->image.max_autofit_size;
295 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
296 options->image.tile_size = c_options->image.tile_size;
297 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
298 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
300 if (options->thumbnails.max_width != c_options->thumbnails.max_width
301 || options->thumbnails.max_height != c_options->thumbnails.max_height
302 || options->thumbnails.quality != c_options->thumbnails.quality)
304 thumb_format_changed = TRUE;
306 options->thumbnails.max_width = c_options->thumbnails.max_width;
307 options->thumbnails.max_height = c_options->thumbnails.max_height;
308 options->thumbnails.quality = c_options->thumbnails.quality;
310 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
311 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
312 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
313 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
314 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
315 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
316 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
317 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
318 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
319 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
320 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
321 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
323 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
324 options->file_filter.disable = c_options->file_filter.disable;
326 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
327 sidecar_ext_parse(options->sidecar.ext);
329 options->slideshow.random = c_options->slideshow.random;
330 options->slideshow.repeat = c_options->slideshow.repeat;
331 options->slideshow.delay = c_options->slideshow.delay;
333 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
334 options->image_lm_click_nav = c_options->image_lm_click_nav;
335 options->image_l_click_archive = c_options->image_l_click_archive;
336 options->image_l_click_video = c_options->image_l_click_video;
337 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
339 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
341 options->image.tile_cache_max = c_options->image.tile_cache_max;
342 options->image.image_cache_max = c_options->image.image_cache_max;
344 options->image.zoom_quality = c_options->image.zoom_quality;
346 options->image.zoom_increment = c_options->image.zoom_increment;
348 options->image.zoom_style = c_options->image.zoom_style;
350 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
352 options->appimage_notifications = c_options->appimage_notifications;
355 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
356 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
357 || !gdk_rgba_equal(&options->image.border_color, &c_options->image.border_color))
359 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
360 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
361 options->image.border_color = c_options->image.border_color;
362 layout_colors_update();
363 view_window_colors_update();
366 options->image.alpha_color_1 = c_options->image.alpha_color_1;
367 options->image.alpha_color_2 = c_options->image.alpha_color_2;
369 options->fullscreen.screen = c_options->fullscreen.screen;
370 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
371 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
372 options->fullscreen.above = c_options->fullscreen.above;
373 if (c_options->image_overlay.template_string)
374 set_image_overlay_template_string(&options->image_overlay.template_string,
375 c_options->image_overlay.template_string);
376 if (c_options->image_overlay.font)
377 set_image_overlay_font_string(&options->image_overlay.font,
378 c_options->image_overlay.font);
379 options->image_overlay.text_red = c_options->image_overlay.text_red;
380 options->image_overlay.text_green = c_options->image_overlay.text_green;
381 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
382 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
383 options->image_overlay.background_red = c_options->image_overlay.background_red;
384 options->image_overlay.background_green = c_options->image_overlay.background_green;
385 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
386 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
387 options->image_overlay.font = c_options->image_overlay.font;
388 options->update_on_time_change = c_options->update_on_time_change;
390 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
391 options->rot_invariant_sim = c_options->rot_invariant_sim;
393 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
395 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
396 options->circular_selection_lists = c_options->circular_selection_lists;
398 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
399 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
400 options->dnd_icon_size = c_options->dnd_icon_size;
401 options->clipboard_selection = c_options->clipboard_selection;
402 options->dnd_default_action = c_options->dnd_default_action;
404 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
405 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
406 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
407 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
408 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
409 options->metadata.confirm_write = c_options->metadata.confirm_write;
410 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
411 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
412 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
413 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
414 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
415 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
416 options->metadata.write_orientation = c_options->metadata.write_orientation;
417 options->metadata.check_spelling = c_options->metadata.check_spelling;
418 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
419 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
420 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
421 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
422 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
423 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
424 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
425 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
426 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
427 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
428 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
429 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
430 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
431 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
432 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
433 options->stereo.fixed_w = c_options->stereo.fixed_w;
434 options->stereo.fixed_h = c_options->stereo.fixed_h;
435 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
436 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
437 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
438 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
440 options->info_keywords.height = c_options->info_keywords.height;
441 options->info_title.height = c_options->info_title.height;
442 options->info_comment.height = c_options->info_comment.height;
443 options->info_rating.height = c_options->info_rating.height;
445 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
446 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
447 options->hamburger_menu = c_options->hamburger_menu;
449 options->selectable_bars.menu_bar = c_options->selectable_bars.menu_bar;
450 options->selectable_bars.tool_bar = c_options->selectable_bars.tool_bar;
451 options->selectable_bars.status_bar = c_options->selectable_bars.status_bar;
453 options->marks_save = c_options->marks_save;
454 options->with_rename = c_options->with_rename;
455 options->collections_duplicates = c_options->collections_duplicates;
456 options->collections_on_top = c_options->collections_on_top;
457 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
458 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
460 options->external_preview.enable = c_options->external_preview.enable;
461 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
462 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
464 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
466 options->star_rating.star = c_options->star_rating.star;
467 options->star_rating.rejected = c_options->star_rating.rejected;
469 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
471 options->alternate_similarity_algorithm.enabled = c_options->alternate_similarity_algorithm.enabled;
472 options->alternate_similarity_algorithm.grayscale = c_options->alternate_similarity_algorithm.grayscale;
475 set_debug_level(debug_c);
476 config_entry_to_option(log_window_f1_entry, &options->log_window.action, nullptr);
480 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
482 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
483 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
485 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
486 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
487 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
489 options->color_profile.render_intent = c_options->color_profile.render_intent;
494 options->mouse_button_8 = c_options->mouse_button_8;
495 options->mouse_button_9 = c_options->mouse_button_9;
497 options->override_disable_gpu = c_options->override_disable_gpu;
499 config_tab_keywords_save();
501 image_options_sync();
506 layout_refresh(nullptr);
509 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
511 toolbar_apply(TOOLBAR_MAIN);
512 toolbar_apply(TOOLBAR_STATUS);
516 *-----------------------------------------------------------------------------
517 * config window main button callbacks (private)
518 *-----------------------------------------------------------------------------
521 static void config_window_close_cb(GtkWidget *, gpointer)
523 gq_gtk_widget_destroy(configwindow);
524 configwindow = nullptr;
525 filter_store = nullptr;
528 static void config_window_help_cb(GtkWidget *, gpointer data)
530 auto notebook = static_cast<GtkWidget *>(data);
533 static const gchar *html_section[] =
535 "GuideOptionsGeneral.html",
536 "GuideOptionsImage.html",
537 "GuideOptionsOSD.html",
538 "GuideOptionsWindow.html",
539 "GuideOptionsKeyboard.html",
540 "GuideOptionsFiltering.html",
541 "GuideOptionsMetadata.html",
542 "GuideOptionsKeywords.html",
543 "GuideOptionsColor.html",
544 "GuideOptionsStereo.html",
545 "GuideOptionsBehavior.html",
546 "GuideOptionsToolbar.html",
547 "GuideOptionsToolbar.html",
548 "GuideOptionsAdvanced.html"
551 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
552 help_window_show(html_section[i]);
555 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
557 config_window_close_cb(nullptr, nullptr);
561 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
564 auto notebook = static_cast<GtkNotebook *>(data);
572 lw = static_cast<LayoutWindow *>(layout_window_list->data);
574 window = gtk_widget_get_window(widget);
575 gdk_window_get_root_origin(window, &x, &y);
576 w = gdk_window_get_width(window);
577 h = gdk_window_get_height(window);
578 page_number = gtk_notebook_get_current_page(notebook);
580 lw->options.preferences_window.x = x;
581 lw->options.preferences_window.y = y;
582 lw->options.preferences_window.w = w;
583 lw->options.preferences_window.h = h;
584 lw->options.preferences_window.page_number = page_number;
586 config_window_apply();
587 layout_util_sync(lw);
588 save_options(options);
589 config_window_close_cb(nullptr, nullptr);
593 *-----------------------------------------------------------------------------
594 * config window setup (private)
595 *-----------------------------------------------------------------------------
598 static void quality_menu_cb(GtkWidget *combo, gpointer data)
600 auto option = static_cast<gint *>(data);
602 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
606 *option = GDK_INTERP_NEAREST;
609 *option = GDK_INTERP_TILES;
612 *option = GDK_INTERP_BILINEAR;
617 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
619 auto option = static_cast<gint *>(data);
621 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
625 *option = DND_ACTION_ASK;
628 *option = DND_ACTION_COPY;
631 *option = DND_ACTION_MOVE;
635 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
637 auto option = static_cast<gint *>(data);
639 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
642 *option = CLIPBOARD_PRIMARY;
645 *option = CLIPBOARD_CLIPBOARD;
648 *option = CLIPBOARD_BOTH;
651 *option = CLIPBOARD_BOTH;
655 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
656 guint option, guint *option_c)
663 pref_table_label(table, column, row, text, GTK_ALIGN_START);
665 combo = gtk_combo_box_text_new();
667 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
668 if (option == GDK_INTERP_NEAREST) current = 0;
669 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
670 if (option == GDK_INTERP_TILES) current = 1;
671 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
672 if (option == GDK_INTERP_BILINEAR) current = 2;
674 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
676 g_signal_connect(G_OBJECT(combo), "changed",
677 G_CALLBACK(quality_menu_cb), option_c);
679 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
680 gtk_widget_show(combo);
683 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
690 pref_table_label(table, column, row, text, GTK_ALIGN_START);
692 combo = gtk_combo_box_text_new();
694 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
695 if (option == DND_ACTION_ASK) current = 0;
696 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
697 if (option == DND_ACTION_COPY) current = 1;
698 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
699 if (option == DND_ACTION_MOVE) current = 2;
701 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
703 g_signal_connect(G_OBJECT(combo), "changed",
704 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
706 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
707 gtk_widget_show(combo);
710 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
711 gint option, gint *option_c)
718 pref_table_label(table, column, row, text, GTK_ALIGN_START);
720 combo = gtk_combo_box_text_new();
722 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
723 if (option == CLIPBOARD_PRIMARY) current = 0;
724 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
725 if (option == CLIPBOARD_CLIPBOARD) current = 1;
726 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
727 if (option == CLIPBOARD_BOTH) current = 2;
729 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
731 g_signal_connect(G_OBJECT(combo), "changed",
732 G_CALLBACK(clipboard_selection_menu_cb), option_c);
734 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
735 gtk_widget_show(combo);
738 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
740 auto option = static_cast<gint *>(data);
742 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
745 *option = ZOOM_GEOMETRIC;
748 *option = ZOOM_ARITHMETIC;
751 *option = ZOOM_GEOMETRIC;
755 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
762 pref_table_label(table, column, row, text, GTK_ALIGN_START);
764 combo = gtk_combo_box_text_new();
766 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
767 if (option == ZOOM_GEOMETRIC) current = 0;
768 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
769 if (option == ZOOM_ARITHMETIC) current = 1;
771 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
773 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
775 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
776 gtk_widget_show(combo);
779 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
781 ActionItem *action_item = nullptr;
782 auto option = static_cast<gchar **>(data);
787 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
789 list = get_action_items();
794 action_item = static_cast<ActionItem *>(work->data);
795 if (g_strcmp0(action_item->label, label) == 0)
803 *option = g_strdup(action_item->name);
805 action_items_free(list);
808 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
810 ActionItem *action_item;
819 pref_table_label(table, column, row, text, GTK_ALIGN_START);
821 combo = gtk_combo_box_text_new();
823 list = get_action_items();
827 action_item = static_cast<ActionItem *>(work->data);
828 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
830 if (g_strcmp0(action_item->name, option) == 0)
838 action_items_free(list);
840 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
842 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
844 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
845 gtk_widget_show(combo);
848 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
852 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
855 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
857 c_options->thumbnails.max_width = thumb_size_list[n].w;
858 c_options->thumbnails.max_height = thumb_size_list[n].h;
862 c_options->thumbnails.max_width = options->thumbnails.max_width;
863 c_options->thumbnails.max_height = options->thumbnails.max_height;
867 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
873 c_options->thumbnails.max_width = options->thumbnails.max_width;
874 c_options->thumbnails.max_height = options->thumbnails.max_height;
876 pref_table_label(table, column, row, text, GTK_ALIGN_START);
878 combo = gtk_combo_box_text_new();
881 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
886 w = thumb_size_list[i].w;
887 h = thumb_size_list[i].h;
889 buf = g_strdup_printf("%d x %d", w, h);
890 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
893 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
900 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
901 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
907 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
908 g_signal_connect(G_OBJECT(combo), "changed",
909 G_CALLBACK(thumb_size_menu_cb), NULL);
911 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
912 gtk_widget_show(combo);
915 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
917 auto option = static_cast<gint *>(data);
919 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
923 *option = PR_STEREO_NONE;
926 *option = PR_STEREO_ANAGLYPH_RC;
929 *option = PR_STEREO_ANAGLYPH_GM;
932 *option = PR_STEREO_ANAGLYPH_YB;
935 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
938 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
941 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
944 *option = PR_STEREO_ANAGLYPH_DB_RC;
947 *option = PR_STEREO_ANAGLYPH_DB_GM;
950 *option = PR_STEREO_ANAGLYPH_DB_YB;
953 *option = PR_STEREO_HORIZ;
956 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
959 *option = PR_STEREO_VERT;
962 *option = PR_STEREO_VERT | PR_STEREO_HALF;
965 *option = PR_STEREO_FIXED;
970 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
971 gint option, gint *option_c, gboolean add_fixed)
978 pref_table_label(table, column, row, text, GTK_ALIGN_START);
980 combo = gtk_combo_box_text_new();
982 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
984 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
985 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
986 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
987 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
988 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
989 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
990 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
991 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
992 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
993 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
994 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
995 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
996 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
997 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
998 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
999 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
1000 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1001 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1003 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1004 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1005 if (option & PR_STEREO_HORIZ)
1008 if (option & PR_STEREO_HALF) current = 11;
1011 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1012 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1013 if (option & PR_STEREO_VERT)
1016 if (option & PR_STEREO_HALF) current = 13;
1021 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1022 if (option & PR_STEREO_FIXED) current = 14;
1025 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1027 g_signal_connect(G_OBJECT(combo), "changed",
1028 G_CALLBACK(stereo_mode_menu_cb), option_c);
1030 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1031 gtk_widget_show(combo);
1034 static void video_menu_cb(GtkWidget *combo, gpointer data)
1036 auto option = static_cast<gchar **>(data);
1038 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1042 static void video_menu_populate(gpointer data, gpointer user_data)
1044 auto combo = static_cast<GtkWidget *>(user_data);
1045 auto ed = static_cast<EditorDescription *>(data);
1047 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1050 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1051 gchar *option, gchar **option_c)
1055 /* use lists since they are sorted */
1056 GList *eds = editor_list_get();
1060 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1062 combo = gtk_combo_box_text_new();
1063 g_list_foreach(eds,video_menu_populate,combo);
1064 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1066 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1068 g_signal_connect(G_OBJECT(combo), "changed",
1069 G_CALLBACK(video_menu_cb), option_c);
1071 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1072 gtk_widget_show(combo);
1075 static void filter_store_populate()
1079 if (!filter_store) return;
1081 gtk_list_store_clear(filter_store);
1083 work = filter_get_list();
1089 fe = static_cast<FilterEntry *>(work->data);
1092 gtk_list_store_append(filter_store, &iter);
1093 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1097 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1099 auto model = static_cast<GtkWidget *>(data);
1100 auto fe = static_cast<FilterEntry *>(data);
1104 if (!new_text || strlen(new_text) < 1) return;
1106 tpath = gtk_tree_path_new_from_string(path_str);
1107 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1108 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1110 g_free(fe->extensions);
1111 fe->extensions = g_strdup(new_text);
1113 gtk_tree_path_free(tpath);
1117 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1119 auto model = static_cast<GtkWidget *>(data);
1120 auto fe = static_cast<FilterEntry *>(data);
1125 if (!new_text || !new_text[0]) return;
1127 tpath = gtk_tree_path_new_from_string(path_str);
1128 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1129 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1131 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1133 if (strcmp(new_text, _(format_class_list[i])) == 0)
1135 fe->file_class = static_cast<FileFormatClass>(i);
1140 gtk_tree_path_free(tpath);
1144 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1146 auto model = static_cast<GtkWidget *>(data);
1151 if (!new_text || !new_text[0]) return;
1153 tpath = gtk_tree_path_new_from_string(path_str);
1154 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1155 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1157 g_free(fe->description);
1158 fe->description = g_strdup(new_text);
1160 gtk_tree_path_free(tpath);
1163 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1165 auto model = static_cast<GtkWidget *>(data);
1170 tpath = gtk_tree_path_new_from_string(path_str);
1171 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1172 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1174 fe->enabled = !fe->enabled;
1176 gtk_tree_path_free(tpath);
1180 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1182 auto model = static_cast<GtkWidget *>(data);
1187 tpath = gtk_tree_path_new_from_string(path_str);
1188 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1189 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1191 fe->writable = !fe->writable;
1192 if (fe->writable) fe->allow_sidecar = FALSE;
1194 gtk_tree_path_free(tpath);
1198 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1200 auto model = static_cast<GtkWidget *>(data);
1205 tpath = gtk_tree_path_new_from_string(path_str);
1206 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1207 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1209 fe->allow_sidecar = !fe->allow_sidecar;
1210 if (fe->allow_sidecar) fe->writable = FALSE;
1212 gtk_tree_path_free(tpath);
1216 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1217 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1221 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1223 switch (GPOINTER_TO_INT(data))
1226 g_object_set(GTK_CELL_RENDERER(cell),
1227 "active", fe->enabled, NULL);
1230 g_object_set(GTK_CELL_RENDERER(cell),
1231 "text", fe->extensions, NULL);
1233 case FE_DESCRIPTION:
1234 g_object_set(GTK_CELL_RENDERER(cell),
1235 "text", fe->description, NULL);
1238 g_object_set(GTK_CELL_RENDERER(cell),
1239 "text", _(format_class_list[fe->file_class]), NULL);
1242 g_object_set(GTK_CELL_RENDERER(cell),
1243 "active", fe->writable, NULL);
1245 case FE_ALLOW_SIDECAR:
1246 g_object_set(GTK_CELL_RENDERER(cell),
1247 "active", fe->allow_sidecar, NULL);
1252 static gboolean filter_add_scroll(gpointer data)
1256 GtkCellRenderer *cell;
1257 GtkTreeViewColumn *column;
1260 GtkTreeModel *store;
1262 FilterEntry *filter;
1264 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1265 path = gtk_tree_path_new_from_indices(rows-1, -1);
1267 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1269 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1270 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1272 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1273 valid = gtk_tree_model_get_iter_first(store, &iter);
1277 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1279 if (g_strcmp0(filter->extensions, ".new") == 0)
1281 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1285 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1288 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1289 path, column, FALSE, 0.0, 0.0 );
1290 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1291 path, column, cell, TRUE);
1293 gtk_tree_path_free(path);
1294 g_list_free(list_cells);
1296 return(G_SOURCE_REMOVE);
1299 static void filter_add_cb(GtkWidget *, gpointer data)
1301 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1302 filter_store_populate();
1304 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1307 static void filter_remove_cb(GtkWidget *, gpointer data)
1309 auto filter_view = static_cast<GtkWidget *>(data);
1310 GtkTreeSelection *selection;
1314 if (!filter_store) return;
1315 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1316 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1317 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1320 filter_remove_entry(fe);
1322 filter_store_populate();
1325 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1329 GtkTreeViewColumn *column;
1331 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1332 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1333 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1335 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1339 gtk_tree_path_free(path);
1341 return(G_SOURCE_REMOVE);
1344 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1347 filter_add_defaults();
1349 filter_store_populate();
1351 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1354 static void dummy_cancel_cb(GenericDialog *, gpointer)
1356 /* no op, only so cancel button appears */
1359 static void filter_default_cb(GtkWidget *widget, gpointer data)
1363 gd = generic_dialog_new(_("Reset filters"),
1364 "reset_filter", widget, TRUE,
1365 dummy_cancel_cb, data);
1366 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1367 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1368 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1369 gtk_widget_show(gd->dialog);
1372 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1374 auto frame = static_cast<GtkWidget *>(data);
1376 gtk_widget_set_sensitive(frame,
1377 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1380 static void safe_delete_view_cb(GtkWidget *, gpointer)
1382 layout_set_path(nullptr, gq_gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1385 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1387 file_util_trash_clear();
1390 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1394 gd = generic_dialog_new(_("Clear trash"),
1395 "clear_trash", widget, TRUE,
1396 dummy_cancel_cb, nullptr);
1397 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1398 _("This will remove the trash contents."), FALSE);
1399 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1400 entry = gtk_entry_new();
1401 gtk_widget_set_can_focus(entry, FALSE);
1402 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1403 if (options->file_ops.safe_delete_path) gq_gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1404 gq_gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1405 gtk_widget_show(entry);
1406 gtk_widget_show(gd->dialog);
1409 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1411 GtkWidget *pTextView;
1412 GtkTextBuffer *pTextBuffer;
1416 pTextView = GTK_WIDGET(data);
1418 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1419 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1420 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1422 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1423 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1426 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1428 auto text_view = static_cast<GtkTextView *>(data);
1429 GtkTextBuffer *buffer;
1431 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1432 if (!configwindow) return;
1434 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1435 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1438 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1442 gd = generic_dialog_new(_("Reset image overlay template string"),
1443 "reset_image_overlay_template_string", widget, TRUE,
1444 dummy_cancel_cb, data);
1445 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1446 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1447 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1448 gtk_widget_show(gd->dialog);
1451 static void image_overlay_help_cb(GtkWidget *, gpointer)
1453 help_window_show("GuideOptionsOSD.html");
1456 static void font_activated_cb(GtkFontChooser *widget, gchar *fontname, gpointer)
1458 g_free(c_options->image_overlay.font);
1459 c_options->image_overlay.font = g_strdup(fontname);
1462 gq_gtk_widget_destroy(GTK_WIDGET(widget));
1465 static void font_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1469 g_free(c_options->image_overlay.font);
1470 c_options->image_overlay.font = g_strdup(options->image_overlay.font);
1472 if (response_id == GTK_RESPONSE_OK)
1474 font = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
1475 g_free(c_options->image_overlay.font);
1476 c_options->image_overlay.font = g_strdup(font);
1480 g_object_unref(dialog);
1483 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1487 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1488 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1489 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1491 g_signal_connect(dialog, "font-activated", G_CALLBACK(font_activated_cb), nullptr);
1492 g_signal_connect(dialog, "response", G_CALLBACK(font_response_cb), nullptr);
1494 gtk_widget_show(dialog);
1497 static void text_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1499 c_options->image_overlay.text_red = color->red * 255;
1500 c_options->image_overlay.text_green = color->green * 255;
1501 c_options->image_overlay.text_blue = color->blue * 255;
1502 c_options->image_overlay.text_alpha = color->alpha * 255;
1504 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1507 static void text_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1511 c_options->image_overlay.text_red = options->image_overlay.text_red;
1512 c_options->image_overlay.text_green = options->image_overlay.text_green;
1513 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
1514 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
1516 if (response_id == GTK_RESPONSE_OK)
1518 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1519 c_options->image_overlay.text_red = color.red * 255;
1520 c_options->image_overlay.text_green = color.green * 255;
1521 c_options->image_overlay.text_blue = color.blue * 255;
1522 c_options->image_overlay.text_alpha = color.alpha * 255;
1525 g_object_unref(dialog);
1528 static void image_overlay_set_text_color_cb(GtkWidget *widget, gpointer)
1533 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Text Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1534 color.red = options->image_overlay.text_red;
1535 color.green = options->image_overlay.text_green;
1536 color.blue = options->image_overlay.text_blue;
1537 color.alpha = options->image_overlay.text_alpha;
1538 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1539 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1541 g_signal_connect(dialog, "color-activated", G_CALLBACK(text_color_activated_cb), nullptr);
1542 g_signal_connect(dialog, "response", G_CALLBACK(text_color_response_cb), nullptr);
1544 gtk_widget_show(dialog);
1547 static void bg_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1549 c_options->image_overlay.background_red = color->red * 255;
1550 c_options->image_overlay.background_green = color->green * 255;
1551 c_options->image_overlay.background_blue = color->blue * 255;
1552 c_options->image_overlay.background_alpha = color->alpha * 255;
1554 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1557 static void bg_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1561 c_options->image_overlay.background_red = options->image_overlay.background_red;
1562 c_options->image_overlay.background_green = options->image_overlay.background_green;
1563 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
1564 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
1566 if (response_id == GTK_RESPONSE_OK)
1568 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1569 c_options->image_overlay.background_red = color.red * 255;
1570 c_options->image_overlay.background_green = color.green * 255;
1571 c_options->image_overlay.background_blue = color.blue * 255;
1572 c_options->image_overlay.background_alpha = color.alpha * 255;
1574 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1577 static void image_overlay_set_background_color_cb(GtkWidget *widget, gpointer)
1582 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Background Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1583 color.red = options->image_overlay.background_red;
1584 color.green = options->image_overlay.background_green;
1585 color.blue = options->image_overlay.background_blue;
1586 color.alpha = options->image_overlay.background_alpha;
1587 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1588 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1590 g_signal_connect(dialog, "color-activated", G_CALLBACK(bg_color_activated_cb), nullptr);
1591 g_signal_connect(dialog, "response", G_CALLBACK(bg_color_response_cb), nullptr);
1593 gtk_widget_show(dialog);
1596 static void accel_store_populate()
1599 GList *groups, *actions;
1601 const gchar *accel_path;
1605 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1607 gtk_tree_store_clear(accel_store);
1608 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 */
1610 g_assert(lw && lw->ui_manager);
1611 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1614 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1617 action = GTK_ACTION(actions->data);
1618 accel_path = gtk_action_get_accel_path(action);
1619 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1621 gchar *label, *label2, *tooltip, *accel;
1622 g_object_get(action,
1623 "tooltip", &tooltip,
1627 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1633 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1637 gtk_tree_store_append(accel_store, &iter, nullptr);
1638 gtk_tree_store_set(accel_store, &iter,
1641 AE_TOOLTIP, tooltip ? tooltip : "",
1642 AE_ACCEL, accel_path,
1650 actions = actions->next;
1653 groups = groups->next;
1657 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1662 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1664 auto accel1 = static_cast<gchar *>(data);
1669 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1671 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1672 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1674 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1676 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1677 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1686 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1688 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1692 GtkAccelKey old_key, key;
1693 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1695 gtk_tree_model_get_iter(model, &iter, path);
1696 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1698 /* test if the accelerator can be stored without conflicts*/
1699 gtk_accel_map_lookup_entry(accel_path, &old_key);
1701 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1702 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1703 gtk_accel_map_lookup_entry(accel_path, &key);
1705 /* restore the original for now, the key will be really changed when the changes are confirmed */
1706 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1708 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1709 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1711 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1712 gtk_tree_path_free(path);
1716 static gboolean accel_default_scroll(GtkTreeView *data)
1720 GtkTreeViewColumn *column;
1722 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1723 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1724 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1726 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1730 gtk_tree_path_free(path);
1732 return(G_SOURCE_REMOVE);
1735 static void accel_default_cb(GtkWidget *, gpointer data)
1737 accel_store_populate();
1739 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1742 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1744 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1747 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1750 gchar *accel_path, *accel;
1752 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1753 gtk_accel_map_lookup_entry(accel_path, &key);
1754 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1756 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1758 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1763 static void accel_clear_cb(GtkWidget *, gpointer data)
1765 GtkTreeSelection *selection;
1767 if (!accel_store) return;
1768 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1769 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1772 static void accel_reset_cb(GtkWidget *, gpointer data)
1774 GtkTreeSelection *selection;
1776 if (!accel_store) return;
1777 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1778 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1783 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1787 GtkWidget *scrolled;
1788 GtkWidget *viewport;
1790 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
1791 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1792 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1793 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1794 label = gtk_label_new(title);
1795 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1796 gtk_widget_show(scrolled);
1798 viewport = gtk_viewport_new(nullptr, nullptr);
1799 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1800 gq_gtk_container_add(GTK_WIDGET(scrolled), viewport);
1801 gtk_widget_show(viewport);
1803 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1804 gq_gtk_container_add(GTK_WIDGET(viewport), vbox);
1805 gtk_widget_show(vbox);
1810 static void cache_standard_cb(GtkWidget *widget, gpointer)
1812 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1814 c_options->thumbnails.spec_standard =TRUE;
1815 c_options->thumbnails.cache_into_dirs = FALSE;
1819 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1821 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1823 c_options->thumbnails.spec_standard =FALSE;
1824 c_options->thumbnails.cache_into_dirs = FALSE;
1828 static void cache_local_cb(GtkWidget *widget, gpointer)
1830 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1832 c_options->thumbnails.cache_into_dirs = TRUE;
1833 c_options->thumbnails.spec_standard =FALSE;
1837 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1839 if (pos == GTK_ENTRY_ICON_PRIMARY)
1841 gq_gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1845 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "");
1849 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1851 gchar *rating_symbol;
1853 if (pos == GTK_ENTRY_ICON_PRIMARY)
1855 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1856 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1857 g_free(rating_symbol);
1861 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1862 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1863 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1867 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1869 gchar *rating_symbol;
1871 if (pos == GTK_ENTRY_ICON_PRIMARY)
1873 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1874 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1875 g_free(rating_symbol);
1879 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1880 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1881 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1885 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1887 auto hbox = static_cast<GtkContainer *>(data);
1888 GString *str = g_string_new(nullptr);
1889 GtkEntry *hex_code_entry;
1890 gchar *hex_code_full;
1893 guint64 hex_value = 0;
1895 list = gtk_container_get_children(hbox);
1897 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1898 hex_code_full = g_strdup(gq_gtk_entry_get_text(hex_code_entry));
1900 hex_code = g_strsplit(hex_code_full, "+", 2);
1901 if (hex_code[0] && hex_code[1])
1903 hex_value = strtoull(hex_code[1], nullptr, 16);
1905 if (!hex_value || hex_value > 0x10FFFF)
1907 hex_value = 0x003F; // Unicode 'Question Mark'
1909 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1910 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1912 g_strfreev(hex_code);
1913 g_string_free(str, TRUE);
1914 g_free(hex_code_full);
1919 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1921 guint64 star_symbol;
1923 star_symbol = star_rating_symbol_test(widget, data);
1924 c_options->star_rating.star = star_symbol;
1927 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1929 guint64 rejected_symbol;
1931 rejected_symbol = star_rating_symbol_test(widget, data);
1932 c_options->star_rating.rejected = rejected_symbol;
1935 /* general options tab */
1936 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1940 GCancellable *cancellable;
1942 GtkWidget *progress;
1944 GFile *timezone_database_gq;
1945 gchar *timezone_database_user;
1948 static void config_tab_general(GtkWidget *notebook)
1953 GtkWidget *group_frame;
1954 GtkWidget *subgroup;
1956 GtkWidget *ct_button;
1959 gint hours, minutes, remainder;
1961 GtkWidget *star_rating_entry;
1963 gchar *rating_symbol;
1966 gchar *download_locn;
1967 GNetworkMonitor *net_mon;
1968 GSocketConnectable *tz_org;
1969 gboolean internet_available = FALSE;
1972 vbox = scrolled_notebook_page(notebook, _("General"));
1974 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1976 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1977 add_thumb_size_menu(table, 0, 0, _("Size:"));
1978 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1980 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1981 pref_label_new(hbox, _("Custom size: "));
1982 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1983 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1985 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1986 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1988 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1989 pref_checkbox_link_sensitivity(ct_button, subgroup);
1991 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1992 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1993 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1994 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1995 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
1996 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1997 G_CALLBACK(cache_geeqie_cb), nullptr);
1999 group_frame = pref_frame_new(subgroup, TRUE,
2000 _("Store thumbnails local to image folder (non-standard)"),
2001 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2002 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
2003 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
2004 G_CALLBACK(cache_local_cb), nullptr);
2006 group_frame = pref_frame_new(subgroup, TRUE,
2007 _("Use standard thumbnail style and cache, shared with other applications"),
2008 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2009 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2010 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2011 G_CALLBACK(cache_standard_cb), nullptr);
2013 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2014 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2016 pref_checkbox_new_int(group, _("Thumbnail color management"),
2017 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2019 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2021 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2022 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2024 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
2025 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2026 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2029 pref_spacer(group, PREF_PAD_GROUP);
2031 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2033 c_options->star_rating.star = options->star_rating.star;
2034 c_options->star_rating.rejected = options->star_rating.rejected;
2036 str = g_string_new(nullptr);
2037 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2038 pref_label_new(hbox, _("Star character: "));
2039 str = g_string_append_unichar(str, options->star_rating.star);
2040 pref_label_new(hbox, g_strdup(str->str));
2041 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2042 star_rating_entry = gtk_entry_new();
2043 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2044 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2045 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2046 gtk_widget_show(star_rating_entry);
2047 button = pref_button_new(nullptr, nullptr, _("Set"),
2048 G_CALLBACK(star_rating_star_test_cb), hbox);
2049 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2050 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2051 gtk_widget_show(button);
2052 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."));
2053 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2054 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2055 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2056 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2057 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2058 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2059 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2060 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2061 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2062 G_CALLBACK(star_rating_star_icon_cb),
2065 g_string_free(str, TRUE);
2066 g_free(rating_symbol);
2068 str = g_string_new(nullptr);
2069 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2070 pref_label_new(hbox, _("Rejected character: "));
2071 str = g_string_append_unichar(str, options->star_rating.rejected);
2072 pref_label_new(hbox, g_strdup(str->str));
2073 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2074 star_rating_entry = gtk_entry_new();
2075 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2076 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2077 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2078 gtk_widget_show(star_rating_entry);
2079 button = pref_button_new(nullptr, nullptr, _("Set"),
2080 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2081 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2082 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2083 gtk_widget_show(button);
2084 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."));
2085 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2086 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2087 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2088 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2089 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2090 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2091 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2092 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2093 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2094 G_CALLBACK(star_rating_rejected_icon_cb),
2097 g_string_free(str, TRUE);
2098 g_free(rating_symbol);
2100 pref_spacer(group, PREF_PAD_GROUP);
2102 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2104 c_options->slideshow.delay = options->slideshow.delay;
2105 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2106 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2107 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2108 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2109 SLIDESHOW_SUBSECOND_PRECISION;
2111 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2113 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2115 options->slideshow.delay ? hours : 0.0,
2116 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2117 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2118 spin = pref_spin_new(hbox, ":" , nullptr,
2120 options->slideshow.delay ? minutes: 0.0,
2121 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2122 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2123 spin = pref_spin_new(hbox, ":", nullptr,
2124 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2125 options->slideshow.delay ? seconds : 10.0,
2126 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2127 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2129 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2130 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2132 pref_spacer(group, PREF_PAD_GROUP);
2134 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2136 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2137 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2138 pref_checkbox_new_int(group, _("Preload next image"),
2139 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2141 pref_checkbox_new_int(group, _("Refresh on file change"),
2142 options->update_on_time_change, &c_options->update_on_time_change);
2145 pref_spacer(group, PREF_PAD_GROUP);
2147 group = pref_group_new(vbox, FALSE, _("Menu style"), GTK_ORIENTATION_VERTICAL);
2149 pref_checkbox_new_int(group, _("☰ style menu button (NOTE! Geeqie must be restarted for change to take effect)"),
2150 options->hamburger_menu, &c_options->hamburger_menu);
2151 gtk_widget_set_tooltip_text(group, _("Use a ☰ style menu button instead of the classic style across the top of the frame"));
2153 pref_spacer(group, PREF_PAD_GROUP);
2155 group = pref_group_new(vbox, FALSE, _("Expand toolbar"), GTK_ORIENTATION_VERTICAL);
2157 pref_checkbox_new_int(group, _("Expand menu/toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2158 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2159 gtk_widget_set_tooltip_text(group, _("Expand the menu/toolbar to the full width of the window"));
2161 pref_spacer(group, PREF_PAD_GROUP);
2163 group = pref_group_new(vbox, FALSE, _("Hide Selectable Bars"), GTK_ORIENTATION_VERTICAL);
2165 pref_checkbox_new_int(group, _("Menu bar"),
2166 options->selectable_bars.menu_bar, &c_options->selectable_bars.menu_bar);
2168 pref_checkbox_new_int(group, _("Tool bar"),
2169 options->selectable_bars.tool_bar, &c_options->selectable_bars.tool_bar);
2171 pref_checkbox_new_int(group, _("Status bar"),
2172 options->selectable_bars.status_bar, &c_options->selectable_bars.status_bar);
2173 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"));
2175 pref_spacer(group, PREF_PAD_GROUP);
2177 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2179 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2180 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2181 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2182 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"));
2184 pref_spacer(group, PREF_PAD_GROUP);
2188 net_mon = g_network_monitor_get_default();
2189 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2192 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2193 g_object_unref(tz_org);
2196 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2197 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2199 if (!internet_available)
2201 gtk_widget_set_sensitive(group, FALSE);
2204 tz = g_new0(TZData, 1);
2206 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2207 basename = g_path_get_basename(path);
2208 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2212 if (isfile(tz->timezone_database_user))
2214 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2218 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2221 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2222 pref_label_new(GTK_WIDGET(hbox), download_locn);
2223 g_free(download_locn);
2225 if (!internet_available)
2227 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"));
2231 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"));
2233 gtk_widget_show(button);
2235 pref_spacer(group, PREF_PAD_GROUP);
2237 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2239 help_search_engine_entry = gtk_entry_new();
2240 gq_gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2241 gq_gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2242 gtk_widget_show(help_search_engine_entry);
2244 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"));
2246 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2247 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2248 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2249 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2250 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2251 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2252 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2253 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2254 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2255 G_CALLBACK(help_search_engine_entry_icon_cb),
2256 help_search_engine_entry);
2260 static void config_tab_image(GtkWidget *notebook)
2265 GtkWidget *ct_button;
2266 GtkWidget *enlargement_button;
2270 vbox = scrolled_notebook_page(notebook, _("Image"));
2272 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2274 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2275 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2277 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2278 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2280 c_options->image.zoom_increment = options->image.zoom_increment;
2281 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2282 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2283 G_CALLBACK(zoom_increment_cb), nullptr);
2284 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2286 c_options->image.zoom_style = options->image.zoom_style;
2287 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2288 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2290 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2292 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2293 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2294 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2295 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2297 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2298 pref_checkbox_link_sensitivity(enlargement_button, spin);
2299 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."));
2301 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2302 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2303 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2304 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2306 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2307 pref_checkbox_link_sensitivity(ct_button, spin);
2308 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."));
2310 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2312 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2313 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2315 options->image.tile_size, &c_options->image.tile_size);
2316 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."));
2318 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2320 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2321 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2323 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2324 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2326 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2327 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2329 c_options->image.border_color = options->image.border_color;
2331 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2332 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2334 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2335 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2337 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2338 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2343 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2345 LayoutWindow *lw = nullptr;
2346 gchar *default_path;
2349 /* Get current lw */
2352 tmp_id = lw->options.id;
2353 lw->options.id = g_strdup("");
2355 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2356 save_default_layout_options_to_file(default_path, options, lw);
2357 g_free(lw->options.id);
2358 lw->options.id = tmp_id;
2359 g_free(default_path);
2362 static gboolean popover_cb(gpointer data)
2364 auto popover = static_cast<GtkPopover *>(data);
2366 gtk_popover_popdown(popover);
2371 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2373 gtk_popover_popup(popover);
2375 g_timeout_add(2000, popover_cb, popover);
2378 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2382 popover = gtk_popover_new(parent);
2383 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2384 gq_gtk_container_add(GTK_WIDGET(popover), child);
2385 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2386 gtk_widget_show (child);
2391 static void config_tab_windows(GtkWidget *notebook)
2396 GtkWidget *subgroup;
2398 GtkWidget *ct_button;
2401 vbox = scrolled_notebook_page(notebook, _("Windows"));
2403 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2405 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2406 options->save_window_positions, &c_options->save_window_positions);
2408 button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2409 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2410 pref_checkbox_link_sensitivity(ct_button, button);
2412 button = pref_checkbox_new_int(group, _("Remember window workspace"),
2413 options->save_window_workspace, &c_options->save_window_workspace);
2414 pref_checkbox_link_sensitivity(ct_button, button);
2416 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2417 options->tools_restore_state, &c_options->tools_restore_state);
2419 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2420 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2422 pref_checkbox_new_int(group, _("Show window IDs"),
2423 options->show_window_ids, &c_options->show_window_ids);
2425 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2426 pref_label_new(subgroup, _("Use current layout for default: "));
2427 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2431 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2432 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2433 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2435 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2437 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2438 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2440 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2441 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2442 options->image.limit_window_size, &c_options->image.limit_window_size);
2443 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2445 options->image.max_window_size, &c_options->image.max_window_size);
2446 pref_checkbox_link_sensitivity(ct_button, spin);
2448 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2450 c_options->fullscreen.screen = options->fullscreen.screen;
2451 c_options->fullscreen.above = options->fullscreen.above;
2452 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2453 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2454 gtk_widget_show(hbox);
2456 pref_checkbox_new_int(group, _("Smooth image flip"),
2457 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2458 pref_checkbox_new_int(group, _("Disable screen saver"),
2459 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2462 #define PRE_FORMATTED_COLUMNS 5
2463 static void config_tab_osd(GtkWidget *notebook)
2469 GtkWidget *image_overlay_template_view;
2470 GtkWidget *scrolled;
2471 GtkWidget *scrolled_pre_formatted;
2472 GtkTextBuffer *buffer;
2474 GtkWidget *subgroup;
2476 vbox = scrolled_notebook_page(notebook, _("OSD"));
2478 image_overlay_template_view = gtk_text_view_new();
2480 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2482 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2484 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2485 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2486 gq_gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2487 gtk_widget_show(scrolled_pre_formatted);
2488 gtk_widget_show(subgroup);
2490 pref_line(group, PREF_PAD_GAP);
2492 pref_label_new(group, _("Image overlay template"));
2494 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2495 gtk_widget_set_size_request(scrolled, 200, 150);
2496 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2497 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2498 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2499 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2500 gtk_widget_show(scrolled);
2502 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2503 _("Extensive formatting options are shown in the Help file"));
2505 gq_gtk_container_add(GTK_WIDGET(scrolled), image_overlay_template_view);
2506 gtk_widget_show(image_overlay_template_view);
2508 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2510 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2511 G_CALLBACK(image_overlay_set_font_cb), notebook);
2513 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2514 gtk_widget_show(button);
2516 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"), G_CALLBACK(image_overlay_set_text_color_cb), nullptr);
2517 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2518 gtk_widget_show(button);
2520 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"), G_CALLBACK(image_overlay_set_background_color_cb), nullptr);
2521 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2522 gtk_widget_show(button);
2523 image_overlay_set_text_colors();
2525 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2526 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2527 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2528 gtk_widget_show(button);
2530 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2531 G_CALLBACK(image_overlay_help_cb), nullptr);
2532 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2533 gtk_widget_show(button);
2535 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2536 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2537 g_signal_connect(G_OBJECT(buffer), "changed",
2538 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2540 pref_line(group, PREF_PAD_GAP);
2542 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2543 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2544 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2545 gtk_widget_show(hbox);
2546 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2547 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2548 gtk_widget_show(label);
2549 pref_spacer(group,TRUE);
2551 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2552 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2553 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2554 gtk_widget_show(hbox);
2555 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2556 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2557 gtk_widget_show(label);
2558 pref_spacer(group,TRUE);
2560 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2561 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2562 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2563 gtk_widget_show(hbox);
2564 label = gtk_label_new(_("%path:39%"));
2565 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2566 gtk_widget_show(label);
2567 pref_spacer(group,TRUE);
2569 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2570 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2571 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2572 gtk_widget_show(hbox);
2573 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%"));
2574 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2575 gtk_widget_show(label);
2576 pref_spacer(group,TRUE);
2578 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2579 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2580 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2581 gtk_widget_show(hbox);
2582 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2583 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2584 gtk_widget_show(label);
2587 static GtkTreeModel *create_class_model()
2589 GtkListStore *model;
2593 /* create list store */
2594 model = gtk_list_store_new(1, G_TYPE_STRING);
2595 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2597 gtk_list_store_append(model, &iter);
2598 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2600 return GTK_TREE_MODEL (model);
2605 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2607 gint n = GPOINTER_TO_INT(data);
2609 FilterEntry *filter_a;
2610 FilterEntry *filter_b;
2612 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2613 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2617 case FILETYPES_COLUMN_ENABLED:
2619 ret = filter_a->enabled - filter_b->enabled;
2622 case FILETYPES_COLUMN_FILTER:
2624 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2627 case FILETYPES_COLUMN_DESCRIPTION:
2629 ret = g_utf8_collate(filter_a->description, filter_b->description);
2632 case FILETYPES_COLUMN_CLASS:
2634 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2637 case FILETYPES_COLUMN_WRITABLE:
2639 ret = filter_a->writable - filter_b->writable;
2642 case FILETYPES_COLUMN_SIDECAR:
2644 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2648 g_return_val_if_reached(0);
2654 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2657 gboolean ret = TRUE;
2659 gtk_tree_model_get(model, iter, 0, &fe, -1);
2661 if (g_strstr_len(fe->extensions, -1, key))
2669 static void config_tab_files(GtkWidget *notebook)
2676 GtkWidget *ct_button;
2677 GtkWidget *scrolled;
2678 GtkWidget *filter_view;
2679 GtkCellRenderer *renderer;
2680 GtkTreeSelection *selection;
2681 GtkTreeViewColumn *column;
2683 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2685 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2687 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2688 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2689 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2690 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2691 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);
2692 pref_checkbox_new_int(group, _("Disable file extension checks"),
2693 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2695 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2696 options->file_filter.disable, &c_options->file_filter.disable);
2699 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2701 sidecar_ext_entry = gtk_entry_new();
2702 gq_gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2703 gq_gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2704 gtk_widget_show(sidecar_ext_entry);
2706 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2708 frame = pref_group_parent(group);
2709 g_signal_connect(G_OBJECT(ct_button), "toggled",
2710 G_CALLBACK(filter_disable_cb), frame);
2711 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2713 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2714 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2715 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2716 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2717 gtk_widget_show(scrolled);
2719 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2720 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2721 g_object_unref(filter_store);
2722 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2723 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2725 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2727 column = gtk_tree_view_column_new();
2728 gtk_tree_view_column_set_title(column, _("Enabled"));
2729 gtk_tree_view_column_set_resizable(column, TRUE);
2731 renderer = gtk_cell_renderer_toggle_new();
2732 g_signal_connect(G_OBJECT(renderer), "toggled",
2733 G_CALLBACK(filter_store_enable_cb), filter_store);
2734 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2735 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2736 GINT_TO_POINTER(FE_ENABLE), nullptr);
2737 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);
2738 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2739 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2741 column = gtk_tree_view_column_new();
2742 gtk_tree_view_column_set_title(column, _("Filter"));
2743 gtk_tree_view_column_set_resizable(column, TRUE);
2744 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);
2745 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2747 renderer = gtk_cell_renderer_text_new();
2748 g_signal_connect(G_OBJECT(renderer), "edited",
2749 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2750 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2751 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2752 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2753 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2754 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2756 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2757 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2758 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2760 column = gtk_tree_view_column_new();
2761 gtk_tree_view_column_set_title(column, _("Description"));
2762 gtk_tree_view_column_set_resizable(column, TRUE);
2763 gtk_tree_view_column_set_fixed_width(column, 200);
2764 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2766 renderer = gtk_cell_renderer_text_new();
2767 g_signal_connect(G_OBJECT(renderer), "edited",
2768 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2769 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2770 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2771 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2772 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2773 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2774 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_DESCRIPTION, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_DESCRIPTION), nullptr);
2775 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2777 column = gtk_tree_view_column_new();
2778 gtk_tree_view_column_set_title(column, _("Class"));
2779 gtk_tree_view_column_set_resizable(column, TRUE);
2780 renderer = gtk_cell_renderer_combo_new();
2781 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2782 "model", create_class_model(),
2787 g_signal_connect(G_OBJECT(renderer), "edited",
2788 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2789 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2790 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2791 GINT_TO_POINTER(FE_CLASS), nullptr);
2792 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2793 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);
2794 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2796 column = gtk_tree_view_column_new();
2797 gtk_tree_view_column_set_title(column, _("Writable"));
2798 gtk_tree_view_column_set_resizable(column, FALSE);
2799 renderer = gtk_cell_renderer_toggle_new();
2800 g_signal_connect(G_OBJECT(renderer), "toggled",
2801 G_CALLBACK(filter_store_writable_cb), filter_store);
2802 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2803 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2804 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2805 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2806 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);
2807 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2809 column = gtk_tree_view_column_new();
2810 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2811 gtk_tree_view_column_set_resizable(column, FALSE);
2812 renderer = gtk_cell_renderer_toggle_new();
2813 g_signal_connect(G_OBJECT(renderer), "toggled",
2814 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2815 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2816 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2817 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2818 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2819 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);
2820 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2822 filter_store_populate();
2823 gq_gtk_container_add(GTK_WIDGET(scrolled), filter_view);
2824 gtk_widget_show(filter_view);
2826 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2828 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2829 G_CALLBACK(filter_default_cb), filter_view);
2830 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2831 gtk_widget_show(button);
2833 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2834 G_CALLBACK(filter_remove_cb), filter_view);
2835 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2836 gtk_widget_show(button);
2838 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2839 G_CALLBACK(filter_add_cb), filter_view);
2840 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2841 gtk_widget_show(button);
2845 static void config_tab_metadata(GtkWidget *notebook)
2850 GtkWidget *ct_button;
2852 GtkWidget *tmp_widget;
2854 GtkWidget *text_label;
2856 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2859 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2861 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2863 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."));
2864 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2865 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2867 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2869 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2870 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2871 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"));
2872 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2874 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)"));
2875 gtk_widget_set_tooltip_markup(ct_button, markup);
2879 gtk_widget_set_sensitive(ct_button, FALSE);
2882 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2883 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2884 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)"));
2885 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2888 label = pref_label_new(group, "");
2889 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(), "/");
2890 gtk_label_set_markup (GTK_LABEL(label), markup);
2893 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2894 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2895 gtk_widget_set_margin_start(label, 22);
2896 pref_spacer(group, PREF_PAD_GROUP);
2898 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2900 gtk_widget_set_sensitive(group, FALSE);
2903 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2904 pref_checkbox_link_sensitivity(ct_button, hbox);
2906 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);
2907 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2909 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);
2911 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2913 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2914 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2915 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2916 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", ")");
2917 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2920 pref_spacer(group, PREF_PAD_GROUP);
2922 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2924 gtk_widget_set_sensitive(group, FALSE);
2927 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);
2929 pref_spacer(group, PREF_PAD_GROUP);
2931 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2932 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);
2933 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2935 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2936 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2938 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2939 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"));
2942 gtk_widget_set_sensitive(ct_button, FALSE);
2945 pref_spacer(group, PREF_PAD_GROUP);
2947 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2949 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2951 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2952 pref_checkbox_link_sensitivity(ct_button, hbox);
2954 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2956 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2958 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2960 pref_spacer(group, PREF_PAD_GROUP);
2963 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2965 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2966 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2969 pref_spacer(group, PREF_PAD_GROUP);
2971 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2973 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2974 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");
2979 struct KeywordFindData
2986 GtkWidget *button_close;
2987 GtkWidget *button_stop;
2988 GtkWidget *button_start;
2989 GtkWidget *progress;
2997 guint idle_id; /* event source id */
3000 #define KEYWORD_DIALOG_WIDTH 400
3002 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
3004 GList *list_d = nullptr;
3005 GList *list_f = nullptr;
3009 filelist_read(dir_fd, &list_f, &list_d);
3013 filelist_read(dir_fd, &list_f, nullptr);
3016 list_f = filelist_filter(list_f, FALSE);
3017 list_d = filelist_filter(list_d, TRUE);
3019 kfd->list = g_list_concat(list_f, kfd->list);
3020 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3023 static void keywords_find_reset(KeywordFindData *kfd)
3025 filelist_free(kfd->list);
3026 kfd->list = nullptr;
3028 filelist_free(kfd->list_dir);
3029 kfd->list_dir = nullptr;
3032 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3034 auto kfd = static_cast<KeywordFindData *>(data);
3036 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3038 keywords_find_reset(kfd);
3039 generic_dialog_close(kfd->gd);
3043 static void keywords_find_finish(KeywordFindData *kfd)
3045 keywords_find_reset(kfd);
3047 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3048 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
3050 gtk_widget_set_sensitive(kfd->group, TRUE);
3051 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3052 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3053 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3056 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3058 auto kfd = static_cast<KeywordFindData *>(data);
3060 g_idle_remove_by_data(kfd);
3062 keywords_find_finish(kfd);
3065 static gboolean keywords_find_file(gpointer data)
3067 auto kfd = static_cast<KeywordFindData *>(data);
3069 GtkTextBuffer *buffer;
3077 fd = static_cast<FileData *>(kfd->list->data);
3078 kfd->list = g_list_remove(kfd->list, fd);
3080 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3081 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3085 gtk_text_buffer_get_end_iter(buffer, &iter);
3086 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3087 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3089 keywords = keywords->next;
3092 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3093 file_data_unref(fd);
3094 g_list_free_full(keywords, g_free);
3096 return (G_SOURCE_CONTINUE);
3098 else if (kfd->list_dir)
3102 fd = static_cast<FileData *>(kfd->list_dir->data);
3103 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3105 keywords_find_folder(kfd, fd);
3107 file_data_unref(fd);
3109 return G_SOURCE_CONTINUE;
3112 keywords_find_finish(kfd);
3114 return G_SOURCE_REMOVE;
3117 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3119 auto kfd = static_cast<KeywordFindData *>(data);
3122 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3124 path = remove_trailing_slash((gq_gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3125 parse_out_relatives(path);
3129 warning_dialog(_("Invalid folder"),
3130 _("The specified folder can not be found."),
3131 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3137 gtk_widget_set_sensitive(kfd->group, FALSE);
3138 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3139 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3140 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3141 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3143 dir_fd = file_data_new_dir(path);
3144 keywords_find_folder(kfd, dir_fd);
3145 file_data_unref(dir_fd);
3146 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3152 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3154 KeywordFindData *kfd;
3158 kfd = g_new0(KeywordFindData, 1);
3160 kfd->gd = generic_dialog_new(_("Search for keywords"),
3161 "search_for_keywords",
3164 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3165 kfd->gd->cancel_cb = keywords_find_close_cb;
3166 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3167 keywords_find_close_cb, FALSE);
3168 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3169 keywords_find_start_cb, FALSE);
3170 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3171 keywords_find_stop_cb, FALSE);
3172 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3174 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3176 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3177 pref_spacer(hbox, PREF_PAD_INDENT);
3178 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3180 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3181 pref_label_new(hbox, _("Folder:"));
3183 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3184 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3185 gq_gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3186 gtk_widget_show(label);
3188 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3190 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3191 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3193 kfd->progress = gtk_entry_new();
3194 gtk_widget_set_can_focus(kfd->progress, FALSE);
3195 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3196 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3197 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3198 gtk_widget_show(kfd->progress);
3200 kfd->spinner = gtk_spinner_new();
3201 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3202 gtk_widget_show(kfd->spinner);
3204 kfd->list = nullptr;
3206 gtk_widget_show(kfd->gd->dialog);
3209 static void keywords_find_cb(GtkWidget *widget, gpointer)
3211 const gchar *path = layout_get_path(nullptr);
3213 if (!path || !*path) path = homedir();
3214 keywords_find_dialog(widget, path);
3217 static void config_tab_keywords_save()
3219 GtkTextIter start, end;
3220 GtkTextBuffer *buffer;
3221 GList *kw_list = nullptr;
3227 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3228 gtk_text_buffer_get_bounds(buffer, &start, &end);
3230 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3232 kw_split = strtok(buffer_text, "\n");
3233 while (kw_split != nullptr)
3239 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3248 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3250 kw_split = strtok(nullptr, "\n");
3253 keyword_list_set(kw_list);
3255 g_list_free_full(kw_list, g_free);
3256 g_free(buffer_text);
3259 static void config_tab_keywords(GtkWidget *notebook)
3265 GtkWidget *scrolled;
3267 GtkTextBuffer *buffer;
3270 GspellTextView *gspell_view;
3273 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3275 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3277 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3279 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3280 G_CALLBACK(keywords_find_cb), keyword_text);
3281 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3284 keyword_text = gtk_text_view_new();
3285 gtk_widget_set_size_request(keyword_text, 20, 20);
3286 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3287 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3288 gtk_widget_show(scrolled);
3291 if (options->metadata.check_spelling)
3293 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3294 gspell_text_view_basic_setup(gspell_view);
3298 gq_gtk_container_add(GTK_WIDGET(scrolled), keyword_text);
3299 gtk_widget_show(keyword_text);
3301 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3303 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3304 gtk_text_buffer_create_tag(buffer, "monospace",
3305 "family", "monospace", NULL);
3307 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3308 gtk_text_buffer_get_start_iter(buffer, &iter);
3309 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3312 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3314 GList *kwl = keyword_list_get();
3315 kwl = g_list_first(kwl);
3318 gtk_text_buffer_get_end_iter (buffer, &iter);
3319 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3320 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3325 gtk_text_buffer_set_modified(buffer, FALSE);
3332 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3334 auto option = static_cast<gint *>(data);
3336 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3340 *option = INTENT_PERCEPTUAL;
3343 *option = INTENT_RELATIVE_COLORIMETRIC;
3346 *option = INTENT_SATURATION;
3349 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3354 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3355 gint option, gint *option_c)
3362 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3364 combo = gtk_combo_box_text_new();
3366 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3367 if (option == INTENT_PERCEPTUAL) current = 0;
3368 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3369 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3370 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3371 if (option == INTENT_SATURATION) current = 2;
3372 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3373 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3375 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3377 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3379 g_signal_connect(G_OBJECT(combo), "changed",
3380 G_CALLBACK(intent_menu_cb), option_c);
3382 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3383 gtk_widget_show(combo);
3387 static void config_tab_color(GtkWidget *notebook)
3396 vbox = scrolled_notebook_page(notebook, _("Color management"));
3398 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3400 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3403 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3404 gtk_grid_set_column_spacing(GTK_GRID(table), PREF_PAD_GAP);
3406 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3407 pref_label_bold(label, TRUE, FALSE);
3409 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3410 pref_label_bold(label, TRUE, FALSE);
3412 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3413 pref_label_bold(label, TRUE, FALSE);
3415 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3420 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3421 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3424 entry = gtk_entry_new();
3425 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3426 if (options->color_profile.input_name[i])
3428 gq_gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3430 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);
3431 gtk_widget_show(entry);
3432 color_profile_input_name_entry[i] = entry;
3434 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3435 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3436 gtk_widget_set_size_request(entry, 160, -1);
3437 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);
3438 gtk_widget_show(tabcomp);
3439 color_profile_input_file_entry[i] = entry;
3442 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3444 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3446 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3447 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3449 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3451 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3452 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3453 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3454 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3455 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3457 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3459 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);
3461 gtk_widget_show(tabcomp);
3464 /* advanced entry tab */
3465 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3467 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3469 c_options->file_ops.use_system_trash = FALSE;
3470 c_options->file_ops.no_trash = FALSE;
3474 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3476 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3478 c_options->file_ops.use_system_trash = TRUE;
3479 c_options->file_ops.no_trash = FALSE;
3483 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3485 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3487 c_options->file_ops.no_trash = TRUE;
3491 static void config_tab_behavior(GtkWidget *notebook)
3498 GtkWidget *ct_button;
3502 GtkWidget *with_rename;
3503 GtkWidget *collections_on_top;
3504 GtkWidget *hide_window_in_fullscreen;
3505 GtkWidget *checkbox;
3508 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3510 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3512 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3513 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3514 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3515 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3516 pref_checkbox_new_int(group, _("Enable Delete key"),
3517 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3519 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3520 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3522 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3523 pref_checkbox_link_sensitivity(ct_button, hbox);
3525 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3526 pref_label_new(hbox, _("Folder:"));
3528 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3529 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3530 gq_gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3531 gtk_widget_show(tabcomp);
3533 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3534 pref_checkbox_link_sensitivity(ct_button, hbox);
3536 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3537 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3538 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3539 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3540 button = pref_button_new(nullptr, nullptr, _("View"),
3541 G_CALLBACK(safe_delete_view_cb), nullptr);
3542 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3543 gtk_widget_show(button);
3545 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3546 G_CALLBACK(safe_delete_clear_cb), nullptr);
3547 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3549 c_options->file_ops.no_trash = options->file_ops.no_trash;
3550 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3552 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3553 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3555 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3556 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3558 gtk_widget_show(button);
3560 pref_spacer(group, PREF_PAD_GROUP);
3563 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3565 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3566 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3568 pref_checkbox_new_int(group, _("In place renaming"),
3569 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3571 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3572 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3574 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3575 options->circular_selection_lists, &c_options->circular_selection_lists);
3576 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3578 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3579 options->marks_save, &c_options->marks_save);
3580 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3582 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3583 options->with_rename, &c_options->with_rename);
3584 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3586 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3587 options->collections_duplicates, &c_options->collections_duplicates);
3588 gtk_widget_set_tooltip_text(collections_on_top,"Allow the same image to be in a Collection more than once");
3590 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3591 options->collections_on_top, &c_options->collections_on_top);
3592 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3594 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3595 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3596 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3598 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3599 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3601 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);
3602 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."));
3604 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3605 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3607 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3608 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3610 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3611 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3613 pref_spacer(group, PREF_PAD_GROUP);
3615 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3617 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3618 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3619 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3620 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3621 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3622 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3623 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3624 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3625 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3626 options->image_l_click_archive, &c_options->image_l_click_archive);
3627 pref_checkbox_new_int(group, _("Play video by left click on image"),
3628 options->image_l_click_video, &c_options->image_l_click_video);
3629 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3630 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3632 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3633 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3634 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3635 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3636 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3638 pref_spacer(group, PREF_PAD_GROUP);
3640 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3642 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3643 options->override_disable_gpu, &c_options->override_disable_gpu);
3644 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3647 pref_spacer(group, PREF_PAD_GROUP);
3649 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3651 pref_spin_new_int(group, _("Debug level:"), nullptr,
3652 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3654 pref_checkbox_new_int(group, _("Timer data"),
3655 options->log_window.timer_data, &c_options->log_window.timer_data);
3657 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3658 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3660 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3661 pref_label_new(hbox, _("Log Window F1 command: "));
3662 log_window_f1_entry = gtk_entry_new();
3663 gq_gtk_entry_set_text(GTK_ENTRY(log_window_f1_entry), options->log_window.action);
3664 gq_gtk_box_pack_start(GTK_BOX(hbox), log_window_f1_entry, FALSE, FALSE, 0);
3665 gtk_entry_set_width_chars(GTK_ENTRY(log_window_f1_entry), 15);
3666 gtk_widget_show(log_window_f1_entry);
3670 /* accelerators tab */
3672 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3674 gboolean ret = TRUE;
3679 gtk_tree_model_get(model, iter, column, &text, -1);
3680 text_nocase = g_utf8_casefold(text, -1);
3681 key_nocase = g_utf8_casefold(key, -1);
3683 if (g_strstr_len(text_nocase, -1, key_nocase))
3690 g_free(text_nocase);
3695 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3700 list = gtk_tree_view_get_columns(tree_view);
3701 col_num = g_list_index(list, column);
3705 gtk_tree_view_set_search_column(tree_view, col_num);
3708 static void config_tab_accelerators(GtkWidget *notebook)
3714 GtkWidget *scrolled;
3715 GtkWidget *accel_view;
3716 GtkCellRenderer *renderer;
3717 GtkTreeSelection *selection;
3718 GtkTreeViewColumn *column;
3720 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3722 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3724 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3725 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3726 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3727 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3728 gtk_widget_show(scrolled);
3730 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3732 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3733 g_object_unref(accel_store);
3734 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3735 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3737 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3739 renderer = gtk_cell_renderer_text_new();
3741 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3746 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3747 gtk_tree_view_column_set_resizable(column, TRUE);
3748 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3751 renderer = gtk_cell_renderer_accel_new();
3752 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3753 G_CALLBACK(accel_store_cleared_cb), accel_store);
3754 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3755 G_CALLBACK(accel_store_edited_cb), accel_store);
3758 g_object_set (renderer,
3760 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3763 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3768 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3769 gtk_tree_view_column_set_resizable(column, TRUE);
3770 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3772 renderer = gtk_cell_renderer_text_new();
3774 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3779 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3780 gtk_tree_view_column_set_resizable(column, TRUE);
3781 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3783 renderer = gtk_cell_renderer_text_new();
3785 column = gtk_tree_view_column_new_with_attributes("Accel",
3790 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3791 gtk_tree_view_column_set_resizable(column, TRUE);
3792 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3794 /* Search on text in column */
3795 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3796 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3797 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3798 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3799 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3801 accel_store_populate();
3802 gq_gtk_container_add(GTK_WIDGET(scrolled), accel_view);
3803 gtk_widget_show(accel_view);
3805 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3807 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3808 G_CALLBACK(accel_default_cb), accel_view);
3809 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3810 gtk_widget_show(button);
3812 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3813 G_CALLBACK(accel_reset_cb), accel_view);
3814 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3815 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3816 gtk_widget_show(button);
3818 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3819 G_CALLBACK(accel_clear_cb), accel_view);
3820 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3821 gtk_widget_show(button);
3824 /* toolbar main tab */
3825 static void config_tab_toolbar_main(GtkWidget *notebook)
3828 GtkWidget *toolbardata;
3831 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3833 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3835 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3836 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3837 gtk_widget_show(vbox);
3840 /* toolbar status tab */
3841 static void config_tab_toolbar_status(GtkWidget *notebook)
3844 GtkWidget *toolbardata;
3847 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3849 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3851 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3852 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3853 gtk_widget_show(vbox);
3857 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3859 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3862 static void config_tab_advanced(GtkWidget *notebook)
3865 GdkPixbufFormat *fm;
3867 GList *extensions_list = nullptr;
3868 GSList *formats_list;
3869 GString *types_string = g_string_new(nullptr);
3870 GtkWidget *alternate_checkbox;
3871 GtkWidget *dupes_threads_spin;
3873 GtkWidget *subgroup;
3875 GtkWidget *threads_string_label;
3876 GtkWidget *types_string_label;
3879 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3880 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3882 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3884 pref_spacer(group, PREF_PAD_GROUP);
3886 formats_list = gdk_pixbuf_get_formats();
3888 while (formats_list)
3890 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3891 extensions = gdk_pixbuf_format_get_extensions(fm);
3894 while (extensions[i])
3896 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3900 g_strfreev(extensions);
3901 formats_list = formats_list->next;
3904 while (extensions_list)
3906 if (types_string->len == 0)
3908 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3912 types_string = g_string_append(types_string, ", ");
3913 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3916 extensions_list = extensions_list->next;
3919 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3920 types_string_label = pref_label_new(group, types_string->str);
3921 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3923 pref_spacer(group, PREF_PAD_GROUP);
3925 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3926 external_preview_select_entry = gtk_entry_new();
3927 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3928 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3929 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3930 gtk_widget_show(tabcomp);
3932 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3933 external_preview_extract_entry = gtk_entry_new();
3934 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3935 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3936 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3937 gtk_widget_show(tabcomp);
3939 gtk_widget_show(vbox);
3941 g_slist_free(formats_list);
3942 g_list_free_full(extensions_list, g_free);
3943 g_string_free(types_string, TRUE);
3945 pref_spacer(group, PREF_PAD_GROUP);
3947 pref_line(vbox, PREF_PAD_SPACE);
3948 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3950 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.");
3951 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3953 pref_spacer(vbox, PREF_PAD_GROUP);
3955 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3956 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3958 pref_spacer(group, PREF_PAD_GROUP);
3960 pref_line(vbox, PREF_PAD_SPACE);
3962 group = pref_group_new(vbox, FALSE, _("Alternate similarity alogorithm"), GTK_ORIENTATION_VERTICAL);
3964 alternate_checkbox = pref_checkbox_new_int(group, _("Enable alternate similarity algorithm"), options->alternate_similarity_algorithm.enabled, &c_options->alternate_similarity_algorithm.enabled);
3966 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3967 pref_checkbox_link_sensitivity(alternate_checkbox, subgroup);
3969 alternate_checkbox = pref_checkbox_new_int(subgroup, _("Use grayscale"), options->alternate_similarity_algorithm.grayscale, &c_options->alternate_similarity_algorithm.grayscale);
3970 gtk_widget_set_tooltip_text(alternate_checkbox, _("Reduce fingerprint to grayscale"));
3974 static void config_tab_stereo(GtkWidget *notebook)
3982 GtkWidget *fs_button;
3983 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3985 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3987 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3988 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3990 table = pref_table_new(group, 2, 2, TRUE, FALSE);
3991 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3992 pref_checkbox_new_int(box, _("Mirror left image"),
3993 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3994 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3995 pref_checkbox_new_int(box, _("Flip left image"),
3996 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3997 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3998 pref_checkbox_new_int(box, _("Mirror right image"),
3999 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
4000 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4001 pref_checkbox_new_int(box, _("Flip right image"),
4002 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
4003 pref_checkbox_new_int(group, _("Swap left and right images"),
4004 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
4005 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
4006 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
4008 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
4009 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
4010 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
4011 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4012 pref_checkbox_link_sensitivity(fs_button, box2);
4013 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
4014 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
4015 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
4016 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4017 pref_checkbox_new_int(box, _("Mirror left image"),
4018 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
4019 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4020 pref_checkbox_new_int(box, _("Flip left image"),
4021 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
4022 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4023 pref_checkbox_new_int(box, _("Mirror right image"),
4024 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
4025 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4026 pref_checkbox_new_int(box, _("Flip right image"),
4027 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
4028 pref_checkbox_new_int(box2, _("Swap left and right images"),
4029 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
4030 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
4031 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
4033 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
4034 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
4035 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
4036 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
4037 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
4038 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
4039 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
4040 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
4041 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
4042 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4043 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
4044 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4045 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
4046 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4050 /* Main preferences window */
4051 static void config_window_create(LayoutWindow *lw)
4053 GtkWidget *win_vbox;
4055 GtkWidget *notebook;
4057 GtkWidget *ct_button;
4059 if (!c_options) c_options = init_options(nullptr);
4061 configwindow = window_new("preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4062 DEBUG_NAME(configwindow);
4063 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4064 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4065 G_CALLBACK(config_window_delete), NULL);
4066 if (options->save_dialog_window_positions)
4068 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4069 gq_gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4073 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4075 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4076 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4078 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4079 gq_gtk_container_add(GTK_WIDGET(configwindow), win_vbox);
4080 gtk_widget_show(win_vbox);
4082 notebook = gtk_notebook_new();
4083 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4084 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4085 gq_gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4087 config_tab_general(notebook);
4088 config_tab_image(notebook);
4089 config_tab_osd(notebook);
4090 config_tab_windows(notebook);
4091 config_tab_accelerators(notebook);
4092 config_tab_files(notebook);
4093 config_tab_metadata(notebook);
4094 config_tab_keywords(notebook);
4095 config_tab_color(notebook);
4096 config_tab_stereo(notebook);
4097 config_tab_behavior(notebook);
4098 config_tab_toolbar_main(notebook);
4099 config_tab_toolbar_status(notebook);
4100 config_tab_advanced(notebook);
4102 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4104 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4105 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4106 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4107 gq_gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4108 gtk_widget_show(hbox);
4110 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4111 G_CALLBACK(config_window_help_cb), notebook);
4112 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4113 gtk_widget_set_can_default(button, TRUE);
4114 gtk_widget_show(button);
4116 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4117 G_CALLBACK(config_window_ok_cb), notebook);
4118 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4119 gtk_widget_set_can_default(button, TRUE);
4120 gtk_widget_grab_default(button);
4121 gtk_widget_show(button);
4125 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4126 G_CALLBACK(config_window_close_cb), nullptr);
4127 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4128 gtk_widget_set_can_default(button, TRUE);
4129 gtk_widget_show(button);
4131 if (!generic_dialog_get_alternative_button_order(configwindow))
4133 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4136 gtk_widget_show(notebook);
4138 gtk_widget_show(configwindow);
4142 *-----------------------------------------------------------------------------
4143 * config window show (public)
4144 *-----------------------------------------------------------------------------
4147 void show_config_window(LayoutWindow *lw)
4151 gtk_window_present(GTK_WINDOW(configwindow));
4155 config_window_create(lw);
4164 void show_about_window(LayoutWindow *lw)
4166 GDataInputStream *data_stream;
4167 GInputStream *in_stream_authors;
4168 GInputStream *in_stream_translators;
4170 GdkPixbuf *pixbuf_icon;
4171 GdkPixbuf *pixbuf_logo;
4175 gchar *authors[1000];
4177 gchar *timezone_path;
4186 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");
4188 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4189 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4191 cd = ZDOpenDatabase(timezone_path);
4194 copyright = g_string_append(copyright, ZDGetNotice(cd));
4198 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4200 ZDCloseDatabase(cd);
4202 g_free(timezone_path);
4204 copyright = g_string_append(copyright, "\n\nSome icons by https://www.flaticon.com");
4206 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4208 data_stream = g_data_input_stream_new(in_stream_authors);
4210 authors[0] = nullptr;
4211 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4213 authors[i_authors] = g_strdup(author_line);
4215 g_free(author_line);
4217 authors[i_authors] = nullptr;
4219 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4221 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4223 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4225 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4226 translators = static_cast<gchar *>(g_malloc0(size));
4227 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4228 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4230 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);
4232 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4233 artists[1] = nullptr;
4235 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4236 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4237 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4238 "title", _("About Geeqie"),
4240 "program-name", GQ_APPNAME,
4242 "logo", pixbuf_logo,
4243 "icon", pixbuf_icon,
4244 "website", GQ_WEBSITE,
4245 "website-label", "Website",
4246 "comments", comment,
4249 "translator-credits", translators,
4250 "wrap-license", TRUE,
4251 "license", copyright->str,
4254 g_string_free(copyright, TRUE);
4256 while(n < i_authors)
4264 g_free(translators);
4265 g_object_unref(data_stream);
4266 g_object_unref(in_stream_authors);
4267 g_object_unref(in_stream_translators);
4270 static void image_overlay_set_text_colors()
4272 c_options->image_overlay.text_red = options->image_overlay.text_red;
4273 c_options->image_overlay.text_green = options->image_overlay.text_green;
4274 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4275 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4276 c_options->image_overlay.background_red = options->image_overlay.background_red;
4277 c_options->image_overlay.background_green = options->image_overlay.background_green;
4278 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4279 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4283 *-----------------------------------------------------------------------------
4284 * timezone database routines
4285 *-----------------------------------------------------------------------------
4288 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4290 GError *error = nullptr;
4291 auto tz = static_cast<TZData *>(data);
4292 gchar *tmp_filename;
4293 gchar *timezone_bin;
4294 gchar *tmp_dir = nullptr;
4297 if (!g_cancellable_is_cancelled(tz->cancellable))
4299 generic_dialog_close(tz->gd);
4303 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4305 tmp_filename = g_file_get_path(tz->tmp_g_file);
4306 fd = file_data_new_simple(tmp_filename);
4307 tmp_dir = open_archive(fd);
4311 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4312 if (isfile(timezone_bin))
4314 move_file(timezone_bin, tz->timezone_database_user);
4318 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4321 g_free(timezone_bin);
4322 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4326 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4328 g_free(tmp_filename);
4329 file_data_unref(fd);
4333 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4336 g_file_delete(tz->tmp_g_file, nullptr, &error);
4337 g_object_unref(tz->tmp_g_file);
4338 tz->tmp_g_file = nullptr;
4339 g_object_unref(tz->cancellable);
4340 g_object_unref(tz->timezone_database_gq);
4343 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4345 auto tz = static_cast<TZData *>(data);
4347 if (!g_cancellable_is_cancelled(tz->cancellable))
4349 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4353 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4355 auto tz = static_cast<TZData *>(data);
4357 g_cancellable_cancel(tz->cancellable);
4360 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4362 auto tz = static_cast<TZData *>(data);
4363 GError *error = nullptr;
4364 GFileIOStream *io_stream;
4371 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4375 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4376 log_printf("Error: Download timezone database failed:\n%s", error->message);
4377 g_error_free(error);
4378 g_object_unref(tz->tmp_g_file);
4382 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4384 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4386 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4388 tz->progress = gtk_progress_bar_new();
4389 gq_gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4390 gtk_widget_show(tz->progress);
4392 gtk_widget_show(tz->gd->dialog);
4393 tz->cancellable = g_cancellable_new();
4394 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);
4396 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4399 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */