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 *checkbox;
2399 GtkWidget *ct_button;
2402 vbox = scrolled_notebook_page(notebook, _("Windows"));
2404 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2406 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2407 options->save_window_positions, &c_options->save_window_positions);
2409 checkbox = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2410 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2411 pref_checkbox_link_sensitivity(ct_button, checkbox);
2413 checkbox = pref_checkbox_new_int(group, _("Remember window workspace"),
2414 options->save_window_workspace, &c_options->save_window_workspace);
2415 pref_checkbox_link_sensitivity(ct_button, checkbox);
2417 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2418 options->tools_restore_state, &c_options->tools_restore_state);
2420 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2421 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2423 pref_checkbox_new_int(group, _("Show window IDs"),
2424 options->show_window_ids, &c_options->show_window_ids);
2426 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2427 pref_label_new(subgroup, _("Use current layout for default: "));
2428 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2432 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2433 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2434 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2436 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2438 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2439 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2441 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2442 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2443 options->image.limit_window_size, &c_options->image.limit_window_size);
2444 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2446 options->image.max_window_size, &c_options->image.max_window_size);
2447 pref_checkbox_link_sensitivity(ct_button, spin);
2449 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2451 c_options->fullscreen.screen = options->fullscreen.screen;
2452 c_options->fullscreen.above = options->fullscreen.above;
2453 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2454 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2455 gtk_widget_show(hbox);
2457 pref_checkbox_new_int(group, _("Smooth image flip"),
2458 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2459 pref_checkbox_new_int(group, _("Disable screen saver"),
2460 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2463 #define PRE_FORMATTED_COLUMNS 5
2464 static void config_tab_osd(GtkWidget *notebook)
2470 GtkWidget *image_overlay_template_view;
2471 GtkWidget *scrolled;
2472 GtkWidget *scrolled_pre_formatted;
2473 GtkTextBuffer *buffer;
2475 GtkWidget *subgroup;
2477 vbox = scrolled_notebook_page(notebook, _("OSD"));
2479 image_overlay_template_view = gtk_text_view_new();
2481 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2483 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2485 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2486 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2487 gq_gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2488 gtk_widget_show(scrolled_pre_formatted);
2489 gtk_widget_show(subgroup);
2491 pref_line(group, PREF_PAD_GAP);
2493 pref_label_new(group, _("Image overlay template"));
2495 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2496 gtk_widget_set_size_request(scrolled, 200, 150);
2497 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2498 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2499 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2500 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2501 gtk_widget_show(scrolled);
2503 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2504 _("Extensive formatting options are shown in the Help file"));
2506 gq_gtk_container_add(GTK_WIDGET(scrolled), image_overlay_template_view);
2507 gtk_widget_show(image_overlay_template_view);
2509 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2511 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2512 G_CALLBACK(image_overlay_set_font_cb), notebook);
2514 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2515 gtk_widget_show(button);
2517 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"), G_CALLBACK(image_overlay_set_text_color_cb), nullptr);
2518 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2519 gtk_widget_show(button);
2521 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"), G_CALLBACK(image_overlay_set_background_color_cb), nullptr);
2522 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2523 gtk_widget_show(button);
2524 image_overlay_set_text_colors();
2526 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2527 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2528 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2529 gtk_widget_show(button);
2531 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2532 G_CALLBACK(image_overlay_help_cb), nullptr);
2533 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2534 gtk_widget_show(button);
2536 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2537 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2538 g_signal_connect(G_OBJECT(buffer), "changed",
2539 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2541 pref_line(group, PREF_PAD_GAP);
2543 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2544 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2545 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2546 gtk_widget_show(hbox);
2547 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2548 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2549 gtk_widget_show(label);
2550 pref_spacer(group,TRUE);
2552 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2553 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2554 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2555 gtk_widget_show(hbox);
2556 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2557 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2558 gtk_widget_show(label);
2559 pref_spacer(group,TRUE);
2561 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2562 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2563 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2564 gtk_widget_show(hbox);
2565 label = gtk_label_new(_("%path:39%"));
2566 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2567 gtk_widget_show(label);
2568 pref_spacer(group,TRUE);
2570 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2571 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2572 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2573 gtk_widget_show(hbox);
2574 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%"));
2575 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2576 gtk_widget_show(label);
2577 pref_spacer(group,TRUE);
2579 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2580 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2581 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2582 gtk_widget_show(hbox);
2583 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2584 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2585 gtk_widget_show(label);
2588 static GtkTreeModel *create_class_model()
2590 GtkListStore *model;
2594 /* create list store */
2595 model = gtk_list_store_new(1, G_TYPE_STRING);
2596 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2598 gtk_list_store_append(model, &iter);
2599 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2601 return GTK_TREE_MODEL (model);
2606 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2608 gint n = GPOINTER_TO_INT(data);
2610 FilterEntry *filter_a;
2611 FilterEntry *filter_b;
2613 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2614 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2618 case FILETYPES_COLUMN_ENABLED:
2620 ret = filter_a->enabled - filter_b->enabled;
2623 case FILETYPES_COLUMN_FILTER:
2625 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2628 case FILETYPES_COLUMN_DESCRIPTION:
2630 ret = g_utf8_collate(filter_a->description, filter_b->description);
2633 case FILETYPES_COLUMN_CLASS:
2635 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2638 case FILETYPES_COLUMN_WRITABLE:
2640 ret = filter_a->writable - filter_b->writable;
2643 case FILETYPES_COLUMN_SIDECAR:
2645 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2649 g_return_val_if_reached(0);
2655 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2658 gboolean ret = TRUE;
2660 gtk_tree_model_get(model, iter, 0, &fe, -1);
2662 if (g_strstr_len(fe->extensions, -1, key))
2670 static void config_tab_files(GtkWidget *notebook)
2677 GtkWidget *ct_button;
2678 GtkWidget *scrolled;
2679 GtkWidget *filter_view;
2680 GtkCellRenderer *renderer;
2681 GtkTreeSelection *selection;
2682 GtkTreeViewColumn *column;
2684 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2686 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2688 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2689 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2690 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2691 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2692 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);
2693 pref_checkbox_new_int(group, _("Disable file extension checks"),
2694 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2696 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2697 options->file_filter.disable, &c_options->file_filter.disable);
2700 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2702 sidecar_ext_entry = gtk_entry_new();
2703 gq_gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2704 gq_gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2705 gtk_widget_show(sidecar_ext_entry);
2707 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2709 frame = pref_group_parent(group);
2710 g_signal_connect(G_OBJECT(ct_button), "toggled",
2711 G_CALLBACK(filter_disable_cb), frame);
2712 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2714 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2715 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2716 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2717 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2718 gtk_widget_show(scrolled);
2720 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2721 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2722 g_object_unref(filter_store);
2723 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2724 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2726 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2728 column = gtk_tree_view_column_new();
2729 gtk_tree_view_column_set_title(column, _("Enabled"));
2730 gtk_tree_view_column_set_resizable(column, TRUE);
2732 renderer = gtk_cell_renderer_toggle_new();
2733 g_signal_connect(G_OBJECT(renderer), "toggled",
2734 G_CALLBACK(filter_store_enable_cb), filter_store);
2735 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2736 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2737 GINT_TO_POINTER(FE_ENABLE), nullptr);
2738 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);
2739 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2740 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2742 column = gtk_tree_view_column_new();
2743 gtk_tree_view_column_set_title(column, _("Filter"));
2744 gtk_tree_view_column_set_resizable(column, TRUE);
2745 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);
2746 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2748 renderer = gtk_cell_renderer_text_new();
2749 g_signal_connect(G_OBJECT(renderer), "edited",
2750 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2751 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2752 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2753 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2754 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2755 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2757 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2758 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2759 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2761 column = gtk_tree_view_column_new();
2762 gtk_tree_view_column_set_title(column, _("Description"));
2763 gtk_tree_view_column_set_resizable(column, TRUE);
2764 gtk_tree_view_column_set_fixed_width(column, 200);
2765 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2767 renderer = gtk_cell_renderer_text_new();
2768 g_signal_connect(G_OBJECT(renderer), "edited",
2769 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2770 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2771 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2772 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2773 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2774 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2775 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);
2776 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2778 column = gtk_tree_view_column_new();
2779 gtk_tree_view_column_set_title(column, _("Class"));
2780 gtk_tree_view_column_set_resizable(column, TRUE);
2781 renderer = gtk_cell_renderer_combo_new();
2782 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2783 "model", create_class_model(),
2788 g_signal_connect(G_OBJECT(renderer), "edited",
2789 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2790 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2791 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2792 GINT_TO_POINTER(FE_CLASS), nullptr);
2793 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2794 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_CLASS, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_CLASS), nullptr);
2795 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2797 column = gtk_tree_view_column_new();
2798 gtk_tree_view_column_set_title(column, _("Writable"));
2799 gtk_tree_view_column_set_resizable(column, FALSE);
2800 renderer = gtk_cell_renderer_toggle_new();
2801 g_signal_connect(G_OBJECT(renderer), "toggled",
2802 G_CALLBACK(filter_store_writable_cb), filter_store);
2803 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2804 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2805 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2806 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2807 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_WRITABLE, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_WRITABLE), nullptr);
2808 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2810 column = gtk_tree_view_column_new();
2811 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2812 gtk_tree_view_column_set_resizable(column, FALSE);
2813 renderer = gtk_cell_renderer_toggle_new();
2814 g_signal_connect(G_OBJECT(renderer), "toggled",
2815 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2816 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2817 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2818 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2819 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2820 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);
2821 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2823 filter_store_populate();
2824 gq_gtk_container_add(GTK_WIDGET(scrolled), filter_view);
2825 gtk_widget_show(filter_view);
2827 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2829 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2830 G_CALLBACK(filter_default_cb), filter_view);
2831 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2832 gtk_widget_show(button);
2834 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2835 G_CALLBACK(filter_remove_cb), filter_view);
2836 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2837 gtk_widget_show(button);
2839 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2840 G_CALLBACK(filter_add_cb), filter_view);
2841 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2842 gtk_widget_show(button);
2846 static void config_tab_metadata(GtkWidget *notebook)
2851 GtkWidget *ct_button;
2853 GtkWidget *tmp_widget;
2855 GtkWidget *text_label;
2857 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2860 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2862 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2864 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."));
2865 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2866 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2868 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2870 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2871 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2872 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"));
2873 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2875 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)"));
2876 gtk_widget_set_tooltip_markup(ct_button, markup);
2880 gtk_widget_set_sensitive(ct_button, FALSE);
2883 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2884 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2885 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)"));
2886 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2889 label = pref_label_new(group, "");
2890 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(), "/");
2891 gtk_label_set_markup (GTK_LABEL(label), markup);
2894 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2895 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2896 gtk_widget_set_margin_start(label, 22);
2897 pref_spacer(group, PREF_PAD_GROUP);
2899 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2901 gtk_widget_set_sensitive(group, FALSE);
2904 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2905 pref_checkbox_link_sensitivity(ct_button, hbox);
2907 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);
2908 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2910 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);
2912 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2914 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2915 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2916 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2917 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", ")");
2918 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2921 pref_spacer(group, PREF_PAD_GROUP);
2923 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2925 gtk_widget_set_sensitive(group, FALSE);
2928 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);
2930 pref_spacer(group, PREF_PAD_GROUP);
2932 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2933 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);
2934 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2936 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2937 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2939 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2940 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"));
2943 gtk_widget_set_sensitive(ct_button, FALSE);
2946 pref_spacer(group, PREF_PAD_GROUP);
2948 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2950 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2952 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2953 pref_checkbox_link_sensitivity(ct_button, hbox);
2955 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2957 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2959 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2961 pref_spacer(group, PREF_PAD_GROUP);
2964 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2966 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2967 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2970 pref_spacer(group, PREF_PAD_GROUP);
2972 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2974 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2975 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");
2980 struct KeywordFindData
2987 GtkWidget *button_close;
2988 GtkWidget *button_stop;
2989 GtkWidget *button_start;
2990 GtkWidget *progress;
2998 guint idle_id; /* event source id */
3001 #define KEYWORD_DIALOG_WIDTH 400
3003 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
3005 GList *list_d = nullptr;
3006 GList *list_f = nullptr;
3010 filelist_read(dir_fd, &list_f, &list_d);
3014 filelist_read(dir_fd, &list_f, nullptr);
3017 list_f = filelist_filter(list_f, FALSE);
3018 list_d = filelist_filter(list_d, TRUE);
3020 kfd->list = g_list_concat(list_f, kfd->list);
3021 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3024 static void keywords_find_reset(KeywordFindData *kfd)
3026 filelist_free(kfd->list);
3027 kfd->list = nullptr;
3029 filelist_free(kfd->list_dir);
3030 kfd->list_dir = nullptr;
3033 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3035 auto kfd = static_cast<KeywordFindData *>(data);
3037 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3039 keywords_find_reset(kfd);
3040 generic_dialog_close(kfd->gd);
3044 static void keywords_find_finish(KeywordFindData *kfd)
3046 keywords_find_reset(kfd);
3048 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3049 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
3051 gtk_widget_set_sensitive(kfd->group, TRUE);
3052 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3053 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3054 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3057 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3059 auto kfd = static_cast<KeywordFindData *>(data);
3061 g_idle_remove_by_data(kfd);
3063 keywords_find_finish(kfd);
3066 static gboolean keywords_find_file(gpointer data)
3068 auto kfd = static_cast<KeywordFindData *>(data);
3070 GtkTextBuffer *buffer;
3078 fd = static_cast<FileData *>(kfd->list->data);
3079 kfd->list = g_list_remove(kfd->list, fd);
3081 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3082 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3086 gtk_text_buffer_get_end_iter(buffer, &iter);
3087 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3088 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3090 keywords = keywords->next;
3093 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3094 file_data_unref(fd);
3095 g_list_free_full(keywords, g_free);
3097 return (G_SOURCE_CONTINUE);
3104 fd = static_cast<FileData *>(kfd->list_dir->data);
3105 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3107 keywords_find_folder(kfd, fd);
3109 file_data_unref(fd);
3111 return G_SOURCE_CONTINUE;
3114 keywords_find_finish(kfd);
3116 return G_SOURCE_REMOVE;
3119 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3121 auto kfd = static_cast<KeywordFindData *>(data);
3124 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3126 path = remove_trailing_slash((gq_gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3127 parse_out_relatives(path);
3131 warning_dialog(_("Invalid folder"),
3132 _("The specified folder can not be found."),
3133 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3139 gtk_widget_set_sensitive(kfd->group, FALSE);
3140 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3141 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3142 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3143 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3145 dir_fd = file_data_new_dir(path);
3146 keywords_find_folder(kfd, dir_fd);
3147 file_data_unref(dir_fd);
3148 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3154 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3156 KeywordFindData *kfd;
3160 kfd = g_new0(KeywordFindData, 1);
3162 kfd->gd = generic_dialog_new(_("Search for keywords"),
3163 "search_for_keywords",
3166 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3167 kfd->gd->cancel_cb = keywords_find_close_cb;
3168 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3169 keywords_find_close_cb, FALSE);
3170 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3171 keywords_find_start_cb, FALSE);
3172 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3173 keywords_find_stop_cb, FALSE);
3174 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3176 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3178 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3179 pref_spacer(hbox, PREF_PAD_INDENT);
3180 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3182 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3183 pref_label_new(hbox, _("Folder:"));
3185 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3186 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3187 gq_gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3188 gtk_widget_show(label);
3190 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3192 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3193 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3195 kfd->progress = gtk_entry_new();
3196 gtk_widget_set_can_focus(kfd->progress, FALSE);
3197 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3198 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3199 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3200 gtk_widget_show(kfd->progress);
3202 kfd->spinner = gtk_spinner_new();
3203 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3204 gtk_widget_show(kfd->spinner);
3206 kfd->list = nullptr;
3208 gtk_widget_show(kfd->gd->dialog);
3211 static void keywords_find_cb(GtkWidget *widget, gpointer)
3213 const gchar *path = layout_get_path(nullptr);
3215 if (!path || !*path) path = homedir();
3216 keywords_find_dialog(widget, path);
3219 static void config_tab_keywords_save()
3221 GtkTextIter start, end;
3222 GtkTextBuffer *buffer;
3223 GList *kw_list = nullptr;
3229 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3230 gtk_text_buffer_get_bounds(buffer, &start, &end);
3232 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3234 kw_split = strtok(buffer_text, "\n");
3235 while (kw_split != nullptr)
3241 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3250 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3252 kw_split = strtok(nullptr, "\n");
3255 keyword_list_set(kw_list);
3257 g_list_free_full(kw_list, g_free);
3258 g_free(buffer_text);
3261 static void config_tab_keywords(GtkWidget *notebook)
3267 GtkWidget *scrolled;
3269 GtkTextBuffer *buffer;
3272 GspellTextView *gspell_view;
3275 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3277 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3279 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3281 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3282 G_CALLBACK(keywords_find_cb), keyword_text);
3283 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3286 keyword_text = gtk_text_view_new();
3287 gtk_widget_set_size_request(keyword_text, 20, 20);
3288 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3289 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3290 gtk_widget_show(scrolled);
3293 if (options->metadata.check_spelling)
3295 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3296 gspell_text_view_basic_setup(gspell_view);
3300 gq_gtk_container_add(GTK_WIDGET(scrolled), keyword_text);
3301 gtk_widget_show(keyword_text);
3303 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3305 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3306 gtk_text_buffer_create_tag(buffer, "monospace",
3307 "family", "monospace", NULL);
3309 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3310 gtk_text_buffer_get_start_iter(buffer, &iter);
3311 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3314 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3316 GList *kwl = keyword_list_get();
3317 kwl = g_list_first(kwl);
3320 gtk_text_buffer_get_end_iter (buffer, &iter);
3321 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3322 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3327 gtk_text_buffer_set_modified(buffer, FALSE);
3334 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3336 auto option = static_cast<gint *>(data);
3338 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3342 *option = INTENT_PERCEPTUAL;
3345 *option = INTENT_RELATIVE_COLORIMETRIC;
3348 *option = INTENT_SATURATION;
3351 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3356 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3357 gint option, gint *option_c)
3364 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3366 combo = gtk_combo_box_text_new();
3368 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3369 if (option == INTENT_PERCEPTUAL) current = 0;
3370 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3371 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3372 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3373 if (option == INTENT_SATURATION) current = 2;
3374 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3375 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3377 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3379 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3381 g_signal_connect(G_OBJECT(combo), "changed",
3382 G_CALLBACK(intent_menu_cb), option_c);
3384 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3385 gtk_widget_show(combo);
3389 static void config_tab_color(GtkWidget *notebook)
3398 vbox = scrolled_notebook_page(notebook, _("Color management"));
3400 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3402 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3405 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3406 gtk_grid_set_column_spacing(GTK_GRID(table), PREF_PAD_GAP);
3408 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3409 pref_label_bold(label, TRUE, FALSE);
3411 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3412 pref_label_bold(label, TRUE, FALSE);
3414 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3415 pref_label_bold(label, TRUE, FALSE);
3417 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3422 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3423 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3426 entry = gtk_entry_new();
3427 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3428 if (options->color_profile.input_name[i])
3430 gq_gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3432 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);
3433 gtk_widget_show(entry);
3434 color_profile_input_name_entry[i] = entry;
3436 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3437 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3438 gtk_widget_set_size_request(entry, 160, -1);
3439 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);
3440 gtk_widget_show(tabcomp);
3441 color_profile_input_file_entry[i] = entry;
3444 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3446 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3448 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3449 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3451 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3453 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3454 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3455 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3456 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3457 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3459 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3461 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);
3463 gtk_widget_show(tabcomp);
3466 /* advanced entry tab */
3467 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3469 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3471 c_options->file_ops.use_system_trash = FALSE;
3472 c_options->file_ops.no_trash = FALSE;
3476 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3478 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3480 c_options->file_ops.use_system_trash = TRUE;
3481 c_options->file_ops.no_trash = FALSE;
3485 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3487 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3489 c_options->file_ops.no_trash = TRUE;
3493 static void config_tab_behavior(GtkWidget *notebook)
3500 GtkWidget *ct_button;
3504 GtkWidget *with_rename;
3505 GtkWidget *collections_on_top;
3506 GtkWidget *hide_window_in_fullscreen;
3507 GtkWidget *checkbox;
3510 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3512 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3514 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3515 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3516 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3517 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3518 pref_checkbox_new_int(group, _("Enable Delete key"),
3519 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3521 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3522 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3524 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3525 pref_checkbox_link_sensitivity(ct_button, hbox);
3527 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3528 pref_label_new(hbox, _("Folder:"));
3530 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3531 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3532 gq_gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3533 gtk_widget_show(tabcomp);
3535 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3536 pref_checkbox_link_sensitivity(ct_button, hbox);
3538 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3539 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3540 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3541 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3542 button = pref_button_new(nullptr, nullptr, _("View"),
3543 G_CALLBACK(safe_delete_view_cb), nullptr);
3544 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3545 gtk_widget_show(button);
3547 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3548 G_CALLBACK(safe_delete_clear_cb), nullptr);
3549 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3551 c_options->file_ops.no_trash = options->file_ops.no_trash;
3552 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3554 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3555 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3557 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3558 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3560 gtk_widget_show(button);
3562 pref_spacer(group, PREF_PAD_GROUP);
3565 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3567 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3568 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3570 pref_checkbox_new_int(group, _("In place renaming"),
3571 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3573 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3574 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3576 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3577 options->circular_selection_lists, &c_options->circular_selection_lists);
3578 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3580 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3581 options->marks_save, &c_options->marks_save);
3582 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3584 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3585 options->with_rename, &c_options->with_rename);
3586 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3588 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3589 options->collections_duplicates, &c_options->collections_duplicates);
3590 gtk_widget_set_tooltip_text(collections_on_top,"Allow the same image to be in a Collection more than once");
3592 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3593 options->collections_on_top, &c_options->collections_on_top);
3594 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3596 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3597 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3598 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3600 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3601 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3603 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);
3604 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."));
3606 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3607 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3609 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3610 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3612 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3613 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3615 pref_spacer(group, PREF_PAD_GROUP);
3617 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3619 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3620 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3621 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3622 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3623 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3624 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3625 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3626 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3627 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3628 options->image_l_click_archive, &c_options->image_l_click_archive);
3629 pref_checkbox_new_int(group, _("Play video by left click on image"),
3630 options->image_l_click_video, &c_options->image_l_click_video);
3631 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3632 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3634 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3635 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3636 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3637 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3638 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3640 pref_spacer(group, PREF_PAD_GROUP);
3642 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3644 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3645 options->override_disable_gpu, &c_options->override_disable_gpu);
3646 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3649 pref_spacer(group, PREF_PAD_GROUP);
3651 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3653 pref_spin_new_int(group, _("Debug level:"), nullptr,
3654 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3656 pref_checkbox_new_int(group, _("Timer data"),
3657 options->log_window.timer_data, &c_options->log_window.timer_data);
3659 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3660 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3662 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3663 pref_label_new(hbox, _("Log Window F1 command: "));
3664 log_window_f1_entry = gtk_entry_new();
3665 gq_gtk_entry_set_text(GTK_ENTRY(log_window_f1_entry), options->log_window.action);
3666 gq_gtk_box_pack_start(GTK_BOX(hbox), log_window_f1_entry, FALSE, FALSE, 0);
3667 gtk_entry_set_width_chars(GTK_ENTRY(log_window_f1_entry), 15);
3668 gtk_widget_show(log_window_f1_entry);
3672 /* accelerators tab */
3674 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3676 gboolean ret = TRUE;
3681 gtk_tree_model_get(model, iter, column, &text, -1);
3682 text_nocase = g_utf8_casefold(text, -1);
3683 key_nocase = g_utf8_casefold(key, -1);
3685 if (g_strstr_len(text_nocase, -1, key_nocase))
3692 g_free(text_nocase);
3697 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3702 list = gtk_tree_view_get_columns(tree_view);
3703 col_num = g_list_index(list, column);
3707 gtk_tree_view_set_search_column(tree_view, col_num);
3710 static void config_tab_accelerators(GtkWidget *notebook)
3716 GtkWidget *scrolled;
3717 GtkWidget *accel_view;
3718 GtkCellRenderer *renderer;
3719 GtkTreeSelection *selection;
3720 GtkTreeViewColumn *column;
3722 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3724 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3726 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3727 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3728 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3729 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3730 gtk_widget_show(scrolled);
3732 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3734 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3735 g_object_unref(accel_store);
3736 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3737 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3739 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3741 renderer = gtk_cell_renderer_text_new();
3743 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3748 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3749 gtk_tree_view_column_set_resizable(column, TRUE);
3750 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3753 renderer = gtk_cell_renderer_accel_new();
3754 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3755 G_CALLBACK(accel_store_cleared_cb), accel_store);
3756 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3757 G_CALLBACK(accel_store_edited_cb), accel_store);
3760 g_object_set (renderer,
3762 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3765 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3770 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3771 gtk_tree_view_column_set_resizable(column, TRUE);
3772 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3774 renderer = gtk_cell_renderer_text_new();
3776 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3781 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3782 gtk_tree_view_column_set_resizable(column, TRUE);
3783 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3785 renderer = gtk_cell_renderer_text_new();
3787 column = gtk_tree_view_column_new_with_attributes("Accel",
3792 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3793 gtk_tree_view_column_set_resizable(column, TRUE);
3794 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3796 /* Search on text in column */
3797 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3798 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3799 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3800 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3801 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3803 accel_store_populate();
3804 gq_gtk_container_add(GTK_WIDGET(scrolled), accel_view);
3805 gtk_widget_show(accel_view);
3807 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3809 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3810 G_CALLBACK(accel_default_cb), accel_view);
3811 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3812 gtk_widget_show(button);
3814 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3815 G_CALLBACK(accel_reset_cb), accel_view);
3816 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3817 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3818 gtk_widget_show(button);
3820 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3821 G_CALLBACK(accel_clear_cb), accel_view);
3822 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3823 gtk_widget_show(button);
3826 /* toolbar main tab */
3827 static void config_tab_toolbar_main(GtkWidget *notebook)
3830 GtkWidget *toolbardata;
3833 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3835 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3837 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3838 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3839 gtk_widget_show(vbox);
3842 /* toolbar status tab */
3843 static void config_tab_toolbar_status(GtkWidget *notebook)
3846 GtkWidget *toolbardata;
3849 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3851 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3853 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3854 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3855 gtk_widget_show(vbox);
3859 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3861 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3864 static void config_tab_advanced(GtkWidget *notebook)
3867 GdkPixbufFormat *fm;
3869 GList *extensions_list = nullptr;
3870 GSList *formats_list;
3871 GString *types_string = g_string_new(nullptr);
3872 GtkWidget *alternate_checkbox;
3873 GtkWidget *dupes_threads_spin;
3875 GtkWidget *subgroup;
3877 GtkWidget *threads_string_label;
3878 GtkWidget *types_string_label;
3881 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3882 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3884 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3886 pref_spacer(group, PREF_PAD_GROUP);
3888 formats_list = gdk_pixbuf_get_formats();
3890 while (formats_list)
3892 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3893 extensions = gdk_pixbuf_format_get_extensions(fm);
3896 while (extensions[i])
3898 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3902 g_strfreev(extensions);
3903 formats_list = formats_list->next;
3906 while (extensions_list)
3908 if (types_string->len == 0)
3910 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3914 types_string = g_string_append(types_string, ", ");
3915 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3918 extensions_list = extensions_list->next;
3921 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3922 types_string_label = pref_label_new(group, types_string->str);
3923 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3925 pref_spacer(group, PREF_PAD_GROUP);
3927 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3928 external_preview_select_entry = gtk_entry_new();
3929 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3930 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3931 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3932 gtk_widget_show(tabcomp);
3934 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3935 external_preview_extract_entry = gtk_entry_new();
3936 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3937 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3938 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3939 gtk_widget_show(tabcomp);
3941 gtk_widget_show(vbox);
3943 g_slist_free(formats_list);
3944 g_list_free_full(extensions_list, g_free);
3945 g_string_free(types_string, TRUE);
3947 pref_spacer(group, PREF_PAD_GROUP);
3949 pref_line(vbox, PREF_PAD_SPACE);
3950 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3952 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.");
3953 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3955 pref_spacer(vbox, PREF_PAD_GROUP);
3957 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3958 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3960 pref_spacer(group, PREF_PAD_GROUP);
3962 pref_line(vbox, PREF_PAD_SPACE);
3964 group = pref_group_new(vbox, FALSE, _("Alternate similarity alogorithm"), GTK_ORIENTATION_VERTICAL);
3966 alternate_checkbox = pref_checkbox_new_int(group, _("Enable alternate similarity algorithm"), options->alternate_similarity_algorithm.enabled, &c_options->alternate_similarity_algorithm.enabled);
3968 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3969 pref_checkbox_link_sensitivity(alternate_checkbox, subgroup);
3971 alternate_checkbox = pref_checkbox_new_int(subgroup, _("Use grayscale"), options->alternate_similarity_algorithm.grayscale, &c_options->alternate_similarity_algorithm.grayscale);
3972 gtk_widget_set_tooltip_text(alternate_checkbox, _("Reduce fingerprint to grayscale"));
3976 static void config_tab_stereo(GtkWidget *notebook)
3984 GtkWidget *fs_button;
3985 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3987 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3989 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3990 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3992 table = pref_table_new(group, 2, 2, TRUE, FALSE);
3993 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3994 pref_checkbox_new_int(box, _("Mirror left image"),
3995 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3996 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3997 pref_checkbox_new_int(box, _("Flip left image"),
3998 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3999 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4000 pref_checkbox_new_int(box, _("Mirror right image"),
4001 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
4002 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4003 pref_checkbox_new_int(box, _("Flip right image"),
4004 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
4005 pref_checkbox_new_int(group, _("Swap left and right images"),
4006 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
4007 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
4008 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
4010 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
4011 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
4012 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
4013 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4014 pref_checkbox_link_sensitivity(fs_button, box2);
4015 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
4016 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
4017 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
4018 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4019 pref_checkbox_new_int(box, _("Mirror left image"),
4020 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
4021 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4022 pref_checkbox_new_int(box, _("Flip left image"),
4023 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
4024 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4025 pref_checkbox_new_int(box, _("Mirror right image"),
4026 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
4027 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4028 pref_checkbox_new_int(box, _("Flip right image"),
4029 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
4030 pref_checkbox_new_int(box2, _("Swap left and right images"),
4031 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
4032 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
4033 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
4035 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
4036 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
4037 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
4038 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
4039 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
4040 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
4041 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
4042 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
4043 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
4044 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4045 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
4046 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4047 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
4048 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4052 /* Main preferences window */
4053 static void config_window_create(LayoutWindow *lw)
4055 GtkWidget *win_vbox;
4057 GtkWidget *notebook;
4059 GtkWidget *ct_button;
4061 if (!c_options) c_options = init_options(nullptr);
4063 configwindow = window_new("preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4064 DEBUG_NAME(configwindow);
4065 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4066 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4067 G_CALLBACK(config_window_delete), NULL);
4068 if (options->save_dialog_window_positions)
4070 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4071 gq_gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4075 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4077 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4078 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4080 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4081 gq_gtk_container_add(GTK_WIDGET(configwindow), win_vbox);
4082 gtk_widget_show(win_vbox);
4084 notebook = gtk_notebook_new();
4085 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4086 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4087 gq_gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4089 config_tab_general(notebook);
4090 config_tab_image(notebook);
4091 config_tab_osd(notebook);
4092 config_tab_windows(notebook);
4093 config_tab_accelerators(notebook);
4094 config_tab_files(notebook);
4095 config_tab_metadata(notebook);
4096 config_tab_keywords(notebook);
4097 config_tab_color(notebook);
4098 config_tab_stereo(notebook);
4099 config_tab_behavior(notebook);
4100 config_tab_toolbar_main(notebook);
4101 config_tab_toolbar_status(notebook);
4102 config_tab_advanced(notebook);
4104 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4106 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4107 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4108 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4109 gq_gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4110 gtk_widget_show(hbox);
4112 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4113 G_CALLBACK(config_window_help_cb), notebook);
4114 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4115 gtk_widget_set_can_default(button, TRUE);
4116 gtk_widget_show(button);
4118 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4119 G_CALLBACK(config_window_ok_cb), notebook);
4120 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4121 gtk_widget_set_can_default(button, TRUE);
4122 gtk_widget_grab_default(button);
4123 gtk_widget_show(button);
4127 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4128 G_CALLBACK(config_window_close_cb), nullptr);
4129 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4130 gtk_widget_set_can_default(button, TRUE);
4131 gtk_widget_show(button);
4133 if (!generic_dialog_get_alternative_button_order(configwindow))
4135 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4138 gtk_widget_show(notebook);
4140 gtk_widget_show(configwindow);
4144 *-----------------------------------------------------------------------------
4145 * config window show (public)
4146 *-----------------------------------------------------------------------------
4149 void show_config_window(LayoutWindow *lw)
4153 gtk_window_present(GTK_WINDOW(configwindow));
4157 config_window_create(lw);
4166 void show_about_window(LayoutWindow *lw)
4168 GDataInputStream *data_stream;
4169 GInputStream *in_stream_authors;
4170 GInputStream *in_stream_translators;
4172 GdkPixbuf *pixbuf_icon;
4173 GdkPixbuf *pixbuf_logo;
4177 gchar *authors[1000];
4179 gchar *timezone_path;
4188 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");
4190 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4191 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4193 cd = ZDOpenDatabase(timezone_path);
4196 copyright = g_string_append(copyright, ZDGetNotice(cd));
4200 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4202 ZDCloseDatabase(cd);
4204 g_free(timezone_path);
4206 copyright = g_string_append(copyright, "\n\nSome icons by https://www.flaticon.com");
4208 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4210 data_stream = g_data_input_stream_new(in_stream_authors);
4212 authors[0] = nullptr;
4213 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4215 authors[i_authors] = g_strdup(author_line);
4217 g_free(author_line);
4219 authors[i_authors] = nullptr;
4221 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4223 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4225 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4227 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4228 translators = static_cast<gchar *>(g_malloc0(size));
4229 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4230 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4232 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);
4234 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4235 artists[1] = nullptr;
4237 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4238 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4239 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4240 "title", _("About Geeqie"),
4242 "program-name", GQ_APPNAME,
4244 "logo", pixbuf_logo,
4245 "icon", pixbuf_icon,
4246 "website", GQ_WEBSITE,
4247 "website-label", "Website",
4248 "comments", comment,
4251 "translator-credits", translators,
4252 "wrap-license", TRUE,
4253 "license", copyright->str,
4256 g_string_free(copyright, TRUE);
4258 while(n < i_authors)
4266 g_free(translators);
4267 g_object_unref(data_stream);
4268 g_object_unref(in_stream_authors);
4269 g_object_unref(in_stream_translators);
4272 static void image_overlay_set_text_colors()
4274 c_options->image_overlay.text_red = options->image_overlay.text_red;
4275 c_options->image_overlay.text_green = options->image_overlay.text_green;
4276 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4277 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4278 c_options->image_overlay.background_red = options->image_overlay.background_red;
4279 c_options->image_overlay.background_green = options->image_overlay.background_green;
4280 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4281 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4285 *-----------------------------------------------------------------------------
4286 * timezone database routines
4287 *-----------------------------------------------------------------------------
4290 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4292 GError *error = nullptr;
4293 auto tz = static_cast<TZData *>(data);
4294 gchar *tmp_filename;
4295 gchar *timezone_bin;
4296 gchar *tmp_dir = nullptr;
4299 if (!g_cancellable_is_cancelled(tz->cancellable))
4301 generic_dialog_close(tz->gd);
4305 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4307 tmp_filename = g_file_get_path(tz->tmp_g_file);
4308 fd = file_data_new_simple(tmp_filename);
4309 tmp_dir = open_archive(fd);
4313 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4314 if (isfile(timezone_bin))
4316 move_file(timezone_bin, tz->timezone_database_user);
4320 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4323 g_free(timezone_bin);
4324 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4328 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4330 g_free(tmp_filename);
4331 file_data_unref(fd);
4335 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4338 g_file_delete(tz->tmp_g_file, nullptr, &error);
4339 g_object_unref(tz->tmp_g_file);
4340 tz->tmp_g_file = nullptr;
4341 g_object_unref(tz->cancellable);
4342 g_object_unref(tz->timezone_database_gq);
4345 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4347 auto tz = static_cast<TZData *>(data);
4349 if (!g_cancellable_is_cancelled(tz->cancellable))
4351 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4355 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4357 auto tz = static_cast<TZData *>(data);
4359 g_cancellable_cancel(tz->cancellable);
4362 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4364 auto tz = static_cast<TZData *>(data);
4365 GError *error = nullptr;
4366 GFileIOStream *io_stream;
4373 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4377 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4378 log_printf("Error: Download timezone database failed:\n%s", error->message);
4379 g_error_free(error);
4380 g_object_unref(tz->tmp_g_file);
4384 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4386 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4388 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4390 tz->progress = gtk_progress_bar_new();
4391 gq_gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4392 gtk_widget_show(tz->progress);
4394 gtk_widget_show(tz->gd->dialog);
4395 tz->cancellable = g_cancellable_new();
4396 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);
4398 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4401 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */