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_colours();
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;
156 #define CONFIG_WINDOW_DEF_WIDTH 700
157 #define CONFIG_WINDOW_DEF_HEIGHT 600
160 *-----------------------------------------------------------------------------
161 * option widget callbacks (private)
162 *-----------------------------------------------------------------------------
165 static void zoom_increment_cb(GtkWidget *spin, gpointer)
167 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
170 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
172 gint mins_secs_tenths, delay;
174 mins_secs_tenths = c_options->slideshow.delay %
175 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
177 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
178 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
181 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
182 SLIDESHOW_SUBSECOND_PRECISION;
185 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
187 gint hours, secs_tenths, delay;
189 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
190 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
192 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
193 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
194 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
196 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
197 SLIDESHOW_SUBSECOND_PRECISION;
200 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
202 gint hours_mins, delay;
204 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
206 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
207 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
208 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
210 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
211 SLIDESHOW_SUBSECOND_PRECISION;
215 *-----------------------------------------------------------------------------
216 * sync program to config window routine (private)
217 *-----------------------------------------------------------------------------
220 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
226 buf = gtk_entry_get_text(GTK_ENTRY(entry));
227 if (buf && strlen(buf) > 0)
232 *option = g_strdup(buf);
237 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
239 gchar *accel_path, *accel;
241 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
243 if (accel_path && accel_path[0])
246 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
247 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
257 static void config_window_apply()
259 gboolean refresh = FALSE;
264 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
266 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
267 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
268 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
269 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
270 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
271 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
273 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
274 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
275 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
276 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
277 options->file_ops.no_trash = c_options->file_ops.no_trash;
278 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
279 options->tools_restore_state = c_options->tools_restore_state;
280 options->save_window_positions = c_options->save_window_positions;
281 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
282 options->save_window_workspace = c_options->save_window_workspace;
283 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
284 options->show_window_ids = c_options->show_window_ids;
285 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
286 options->image.zoom_2pass = c_options->image.zoom_2pass;
287 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
288 options->image.limit_window_size = c_options->image.limit_window_size;
289 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
290 options->image.max_window_size = c_options->image.max_window_size;
291 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
292 options->image.max_autofit_size = c_options->image.max_autofit_size;
293 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
294 options->image.tile_size = c_options->image.tile_size;
295 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
296 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
298 if (options->thumbnails.max_width != c_options->thumbnails.max_width
299 || options->thumbnails.max_height != c_options->thumbnails.max_height
300 || options->thumbnails.quality != c_options->thumbnails.quality)
302 thumb_format_changed = TRUE;
304 options->thumbnails.max_width = c_options->thumbnails.max_width;
305 options->thumbnails.max_height = c_options->thumbnails.max_height;
306 options->thumbnails.quality = c_options->thumbnails.quality;
308 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
309 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
310 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
311 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
312 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
313 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
314 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
315 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
316 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
317 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
318 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
319 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
321 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
322 options->file_filter.disable = c_options->file_filter.disable;
324 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
325 sidecar_ext_parse(options->sidecar.ext);
327 options->slideshow.random = c_options->slideshow.random;
328 options->slideshow.repeat = c_options->slideshow.repeat;
329 options->slideshow.delay = c_options->slideshow.delay;
331 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
332 options->image_lm_click_nav = c_options->image_lm_click_nav;
333 options->image_l_click_archive = c_options->image_l_click_archive;
334 options->image_l_click_video = c_options->image_l_click_video;
335 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
337 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
339 options->image.tile_cache_max = c_options->image.tile_cache_max;
340 options->image.image_cache_max = c_options->image.image_cache_max;
342 options->image.zoom_quality = c_options->image.zoom_quality;
344 options->image.zoom_increment = c_options->image.zoom_increment;
346 options->image.zoom_style = c_options->image.zoom_style;
348 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
350 options->appimage_notifications = c_options->appimage_notifications;
353 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
354 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
355 || !gdk_color_equal(&options->image.border_color, &c_options->image.border_color))
357 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
358 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
359 options->image.border_color = c_options->image.border_color;
360 layout_colors_update();
361 view_window_colors_update();
364 options->image.alpha_color_1 = c_options->image.alpha_color_1;
365 options->image.alpha_color_2 = c_options->image.alpha_color_2;
367 options->fullscreen.screen = c_options->fullscreen.screen;
368 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
369 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
370 options->fullscreen.above = c_options->fullscreen.above;
371 if (c_options->image_overlay.template_string)
372 set_image_overlay_template_string(&options->image_overlay.template_string,
373 c_options->image_overlay.template_string);
374 if (c_options->image_overlay.font)
375 set_image_overlay_font_string(&options->image_overlay.font,
376 c_options->image_overlay.font);
377 options->image_overlay.text_red = c_options->image_overlay.text_red;
378 options->image_overlay.text_green = c_options->image_overlay.text_green;
379 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
380 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
381 options->image_overlay.background_red = c_options->image_overlay.background_red;
382 options->image_overlay.background_green = c_options->image_overlay.background_green;
383 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
384 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
385 options->update_on_time_change = c_options->update_on_time_change;
387 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
388 options->rot_invariant_sim = c_options->rot_invariant_sim;
390 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
392 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
393 options->circular_selection_lists = c_options->circular_selection_lists;
395 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
396 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
397 options->dnd_icon_size = c_options->dnd_icon_size;
398 options->clipboard_selection = c_options->clipboard_selection;
399 options->dnd_default_action = c_options->dnd_default_action;
401 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
402 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
403 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
404 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
405 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
406 options->metadata.confirm_write = c_options->metadata.confirm_write;
407 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
408 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
409 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
410 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
411 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
412 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
413 options->metadata.write_orientation = c_options->metadata.write_orientation;
414 options->metadata.check_spelling = c_options->metadata.check_spelling;
415 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
416 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
417 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
418 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
419 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
420 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
421 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
422 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
423 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
424 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
425 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
426 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
427 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
428 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
429 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
430 options->stereo.fixed_w = c_options->stereo.fixed_w;
431 options->stereo.fixed_h = c_options->stereo.fixed_h;
432 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
433 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
434 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
435 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
437 options->info_keywords.height = c_options->info_keywords.height;
438 options->info_title.height = c_options->info_title.height;
439 options->info_comment.height = c_options->info_comment.height;
440 options->info_rating.height = c_options->info_rating.height;
442 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
443 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
445 options->marks_save = c_options->marks_save;
446 options->with_rename = c_options->with_rename;
447 options->collections_duplicates = c_options->collections_duplicates;
448 options->collections_on_top = c_options->collections_on_top;
449 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
450 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
452 options->external_preview.enable = c_options->external_preview.enable;
453 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
454 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
456 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
458 options->star_rating.star = c_options->star_rating.star;
459 options->star_rating.rejected = c_options->star_rating.rejected;
461 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
464 set_debug_level(debug_c);
468 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
470 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
471 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
473 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
474 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
475 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
477 options->color_profile.render_intent = c_options->color_profile.render_intent;
482 options->mouse_button_8 = c_options->mouse_button_8;
483 options->mouse_button_9 = c_options->mouse_button_9;
485 options->override_disable_gpu = c_options->override_disable_gpu;
487 config_tab_keywords_save();
489 image_options_sync();
494 layout_refresh(nullptr);
497 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
499 toolbar_apply(TOOLBAR_MAIN);
500 toolbar_apply(TOOLBAR_STATUS);
504 *-----------------------------------------------------------------------------
505 * config window main button callbacks (private)
506 *-----------------------------------------------------------------------------
509 static void config_window_close_cb(GtkWidget *, gpointer)
511 gtk_widget_destroy(configwindow);
512 configwindow = nullptr;
513 filter_store = nullptr;
516 static void config_window_help_cb(GtkWidget *, gpointer data)
518 auto notebook = static_cast<GtkWidget *>(data);
521 static const gchar *html_section[] =
523 "GuideOptionsGeneral.html",
524 "GuideOptionsImage.html",
525 "GuideOptionsOSD.html",
526 "GuideOptionsWindow.html",
527 "GuideOptionsKeyboard.html",
528 "GuideOptionsFiltering.html",
529 "GuideOptionsMetadata.html",
530 "GuideOptionsKeywords.html",
531 "GuideOptionsColor.html",
532 "GuideOptionsStereo.html",
533 "GuideOptionsBehavior.html",
534 "GuideOptionsToolbar.html",
535 "GuideOptionsToolbar.html",
536 "GuideOptionsAdvanced.html"
539 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
540 help_window_show(html_section[i]);
543 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
545 config_window_close_cb(nullptr, nullptr);
549 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
552 auto notebook = static_cast<GtkNotebook *>(data);
560 lw = static_cast<LayoutWindow *>(layout_window_list->data);
562 window = gtk_widget_get_window(widget);
563 gdk_window_get_root_origin(window, &x, &y);
564 w = gdk_window_get_width(window);
565 h = gdk_window_get_height(window);
566 page_number = gtk_notebook_get_current_page(notebook);
568 lw->options.preferences_window.x = x;
569 lw->options.preferences_window.y = y;
570 lw->options.preferences_window.w = w;
571 lw->options.preferences_window.h = h;
572 lw->options.preferences_window.page_number = page_number;
574 config_window_apply();
575 layout_util_sync(lw);
576 save_options(options);
577 config_window_close_cb(nullptr, nullptr);
581 *-----------------------------------------------------------------------------
582 * config window setup (private)
583 *-----------------------------------------------------------------------------
586 static void quality_menu_cb(GtkWidget *combo, gpointer data)
588 auto option = static_cast<gint *>(data);
590 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
594 *option = GDK_INTERP_NEAREST;
597 *option = GDK_INTERP_TILES;
600 *option = GDK_INTERP_BILINEAR;
605 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
607 auto option = static_cast<gint *>(data);
609 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
613 *option = DND_ACTION_ASK;
616 *option = DND_ACTION_COPY;
619 *option = DND_ACTION_MOVE;
623 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
625 auto option = static_cast<gint *>(data);
627 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
630 *option = CLIPBOARD_PRIMARY;
633 *option = CLIPBOARD_CLIPBOARD;
636 *option = CLIPBOARD_BOTH;
639 *option = CLIPBOARD_BOTH;
643 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
644 guint option, guint *option_c)
651 pref_table_label(table, column, row, text, GTK_ALIGN_START);
653 combo = gtk_combo_box_text_new();
655 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
656 if (option == GDK_INTERP_NEAREST) current = 0;
657 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
658 if (option == GDK_INTERP_TILES) current = 1;
659 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
660 if (option == GDK_INTERP_BILINEAR) current = 2;
662 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
664 g_signal_connect(G_OBJECT(combo), "changed",
665 G_CALLBACK(quality_menu_cb), option_c);
667 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
668 gtk_widget_show(combo);
671 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
678 pref_table_label(table, column, row, text, GTK_ALIGN_START);
680 combo = gtk_combo_box_text_new();
682 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
683 if (option == DND_ACTION_ASK) current = 0;
684 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
685 if (option == DND_ACTION_COPY) current = 1;
686 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
687 if (option == DND_ACTION_MOVE) current = 2;
689 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
691 g_signal_connect(G_OBJECT(combo), "changed",
692 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
694 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
695 gtk_widget_show(combo);
698 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
699 gint option, gint *option_c)
706 pref_table_label(table, column, row, text, GTK_ALIGN_START);
708 combo = gtk_combo_box_text_new();
710 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
711 if (option == CLIPBOARD_PRIMARY) current = 0;
712 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
713 if (option == CLIPBOARD_CLIPBOARD) current = 1;
714 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
715 if (option == CLIPBOARD_BOTH) current = 2;
717 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
719 g_signal_connect(G_OBJECT(combo), "changed",
720 G_CALLBACK(clipboard_selection_menu_cb), option_c);
722 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
723 gtk_widget_show(combo);
726 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
728 auto option = static_cast<gint *>(data);
730 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
733 *option = ZOOM_GEOMETRIC;
736 *option = ZOOM_ARITHMETIC;
739 *option = ZOOM_GEOMETRIC;
743 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
750 pref_table_label(table, column, row, text, GTK_ALIGN_START);
752 combo = gtk_combo_box_text_new();
754 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
755 if (option == ZOOM_GEOMETRIC) current = 0;
756 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
757 if (option == ZOOM_ARITHMETIC) current = 1;
759 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
761 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
763 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
764 gtk_widget_show(combo);
767 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
769 ActionItem *action_item = nullptr;
770 auto option = static_cast<gchar **>(data);
775 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
777 list = get_action_items();
782 action_item = static_cast<ActionItem *>(work->data);
783 if (g_strcmp0(action_item->label, label) == 0)
791 *option = g_strdup(action_item->name);
793 action_items_free(list);
796 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
798 ActionItem *action_item;
807 pref_table_label(table, column, row, text, GTK_ALIGN_START);
809 combo = gtk_combo_box_text_new();
811 list = get_action_items();
815 action_item = static_cast<ActionItem *>(work->data);
816 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
818 if (g_strcmp0(action_item->name, option) == 0)
826 action_items_free(list);
828 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
830 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
832 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
833 gtk_widget_show(combo);
836 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
840 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
843 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
845 c_options->thumbnails.max_width = thumb_size_list[n].w;
846 c_options->thumbnails.max_height = thumb_size_list[n].h;
850 c_options->thumbnails.max_width = options->thumbnails.max_width;
851 c_options->thumbnails.max_height = options->thumbnails.max_height;
855 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
861 c_options->thumbnails.max_width = options->thumbnails.max_width;
862 c_options->thumbnails.max_height = options->thumbnails.max_height;
864 pref_table_label(table, column, row, text, GTK_ALIGN_START);
866 combo = gtk_combo_box_text_new();
869 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
874 w = thumb_size_list[i].w;
875 h = thumb_size_list[i].h;
877 buf = g_strdup_printf("%d x %d", w, h);
878 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
881 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
888 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
889 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
895 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
896 g_signal_connect(G_OBJECT(combo), "changed",
897 G_CALLBACK(thumb_size_menu_cb), NULL);
899 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
900 gtk_widget_show(combo);
903 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
905 auto option = static_cast<gint *>(data);
907 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
911 *option = PR_STEREO_NONE;
914 *option = PR_STEREO_ANAGLYPH_RC;
917 *option = PR_STEREO_ANAGLYPH_GM;
920 *option = PR_STEREO_ANAGLYPH_YB;
923 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
926 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
929 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
932 *option = PR_STEREO_ANAGLYPH_DB_RC;
935 *option = PR_STEREO_ANAGLYPH_DB_GM;
938 *option = PR_STEREO_ANAGLYPH_DB_YB;
941 *option = PR_STEREO_HORIZ;
944 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
947 *option = PR_STEREO_VERT;
950 *option = PR_STEREO_VERT | PR_STEREO_HALF;
953 *option = PR_STEREO_FIXED;
958 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
959 gint option, gint *option_c, gboolean add_fixed)
966 pref_table_label(table, column, row, text, GTK_ALIGN_START);
968 combo = gtk_combo_box_text_new();
970 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
972 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
973 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
974 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
975 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
976 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
977 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
978 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
979 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
980 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
981 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
982 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
983 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
984 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
985 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
986 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
987 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
988 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
989 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
991 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
992 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
993 if (option & PR_STEREO_HORIZ)
996 if (option & PR_STEREO_HALF) current = 11;
999 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1000 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1001 if (option & PR_STEREO_VERT)
1004 if (option & PR_STEREO_HALF) current = 13;
1009 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1010 if (option & PR_STEREO_FIXED) current = 14;
1013 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1015 g_signal_connect(G_OBJECT(combo), "changed",
1016 G_CALLBACK(stereo_mode_menu_cb), option_c);
1018 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1019 gtk_widget_show(combo);
1022 static void video_menu_cb(GtkWidget *combo, gpointer data)
1024 auto option = static_cast<gchar **>(data);
1026 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1030 static void video_menu_populate(gpointer data, gpointer user_data)
1032 auto combo = static_cast<GtkWidget *>(user_data);
1033 auto ed = static_cast<EditorDescription *>(data);
1035 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1038 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1039 gchar *option, gchar **option_c)
1043 /* use lists since they are sorted */
1044 GList *eds = editor_list_get();
1048 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1050 combo = gtk_combo_box_text_new();
1051 g_list_foreach(eds,video_menu_populate,combo);
1052 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1054 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1056 g_signal_connect(G_OBJECT(combo), "changed",
1057 G_CALLBACK(video_menu_cb), option_c);
1059 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1060 gtk_widget_show(combo);
1063 static void filter_store_populate()
1067 if (!filter_store) return;
1069 gtk_list_store_clear(filter_store);
1071 work = filter_get_list();
1077 fe = static_cast<FilterEntry *>(work->data);
1080 gtk_list_store_append(filter_store, &iter);
1081 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1085 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1087 auto model = static_cast<GtkWidget *>(data);
1088 auto fe = static_cast<FilterEntry *>(data);
1092 if (!new_text || strlen(new_text) < 1) return;
1094 tpath = gtk_tree_path_new_from_string(path_str);
1095 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1096 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1098 g_free(fe->extensions);
1099 fe->extensions = g_strdup(new_text);
1101 gtk_tree_path_free(tpath);
1105 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1107 auto model = static_cast<GtkWidget *>(data);
1108 auto fe = static_cast<FilterEntry *>(data);
1113 if (!new_text || !new_text[0]) return;
1115 tpath = gtk_tree_path_new_from_string(path_str);
1116 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1117 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1119 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1121 if (strcmp(new_text, _(format_class_list[i])) == 0)
1123 fe->file_class = static_cast<FileFormatClass>(i);
1128 gtk_tree_path_free(tpath);
1132 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1134 auto model = static_cast<GtkWidget *>(data);
1139 if (!new_text || !new_text[0]) return;
1141 tpath = gtk_tree_path_new_from_string(path_str);
1142 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1143 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1145 g_free(fe->description);
1146 fe->description = g_strdup(new_text);
1148 gtk_tree_path_free(tpath);
1151 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1153 auto model = static_cast<GtkWidget *>(data);
1158 tpath = gtk_tree_path_new_from_string(path_str);
1159 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1160 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1162 fe->enabled = !fe->enabled;
1164 gtk_tree_path_free(tpath);
1168 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1170 auto model = static_cast<GtkWidget *>(data);
1175 tpath = gtk_tree_path_new_from_string(path_str);
1176 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1177 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1179 fe->writable = !fe->writable;
1180 if (fe->writable) fe->allow_sidecar = FALSE;
1182 gtk_tree_path_free(tpath);
1186 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1188 auto model = static_cast<GtkWidget *>(data);
1193 tpath = gtk_tree_path_new_from_string(path_str);
1194 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1195 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1197 fe->allow_sidecar = !fe->allow_sidecar;
1198 if (fe->allow_sidecar) fe->writable = FALSE;
1200 gtk_tree_path_free(tpath);
1204 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1205 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1209 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1211 switch (GPOINTER_TO_INT(data))
1214 g_object_set(GTK_CELL_RENDERER(cell),
1215 "active", fe->enabled, NULL);
1218 g_object_set(GTK_CELL_RENDERER(cell),
1219 "text", fe->extensions, NULL);
1221 case FE_DESCRIPTION:
1222 g_object_set(GTK_CELL_RENDERER(cell),
1223 "text", fe->description, NULL);
1226 g_object_set(GTK_CELL_RENDERER(cell),
1227 "text", _(format_class_list[fe->file_class]), NULL);
1230 g_object_set(GTK_CELL_RENDERER(cell),
1231 "active", fe->writable, NULL);
1233 case FE_ALLOW_SIDECAR:
1234 g_object_set(GTK_CELL_RENDERER(cell),
1235 "active", fe->allow_sidecar, NULL);
1240 static gboolean filter_add_scroll(gpointer data)
1244 GtkCellRenderer *cell;
1245 GtkTreeViewColumn *column;
1248 GtkTreeModel *store;
1250 FilterEntry *filter;
1252 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1253 path = gtk_tree_path_new_from_indices(rows-1, -1);
1255 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1257 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1258 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1260 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1261 valid = gtk_tree_model_get_iter_first(store, &iter);
1265 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1267 if (g_strcmp0(filter->extensions, ".new") == 0)
1269 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1273 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1276 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1277 path, column, FALSE, 0.0, 0.0 );
1278 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1279 path, column, cell, TRUE);
1281 gtk_tree_path_free(path);
1282 g_list_free(list_cells);
1284 return(G_SOURCE_REMOVE);
1287 static void filter_add_cb(GtkWidget *, gpointer data)
1289 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1290 filter_store_populate();
1292 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1295 static void filter_remove_cb(GtkWidget *, gpointer data)
1297 auto filter_view = static_cast<GtkWidget *>(data);
1298 GtkTreeSelection *selection;
1302 if (!filter_store) return;
1303 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1304 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1305 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1308 filter_remove_entry(fe);
1310 filter_store_populate();
1313 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1317 GtkTreeViewColumn *column;
1319 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1320 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1321 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1323 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1327 gtk_tree_path_free(path);
1329 return(G_SOURCE_REMOVE);
1332 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1335 filter_add_defaults();
1337 filter_store_populate();
1339 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1342 static void dummy_cancel_cb(GenericDialog *, gpointer)
1344 /* no op, only so cancel button appears */
1347 static void filter_default_cb(GtkWidget *widget, gpointer data)
1351 gd = generic_dialog_new(_("Reset filters"),
1352 "reset_filter", widget, TRUE,
1353 dummy_cancel_cb, data);
1354 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1355 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1356 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1357 gtk_widget_show(gd->dialog);
1360 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1362 auto frame = static_cast<GtkWidget *>(data);
1364 gtk_widget_set_sensitive(frame,
1365 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1368 static void safe_delete_view_cb(GtkWidget *, gpointer)
1370 layout_set_path(nullptr, gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1373 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1375 file_util_trash_clear();
1378 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1382 gd = generic_dialog_new(_("Clear trash"),
1383 "clear_trash", widget, TRUE,
1384 dummy_cancel_cb, nullptr);
1385 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1386 _("This will remove the trash contents."), FALSE);
1387 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1388 entry = gtk_entry_new();
1389 gtk_widget_set_can_focus(entry, FALSE);
1390 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1391 if (options->file_ops.safe_delete_path) gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1392 gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1393 gtk_widget_show(entry);
1394 gtk_widget_show(gd->dialog);
1397 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1399 GtkWidget *pTextView;
1400 GtkTextBuffer *pTextBuffer;
1404 pTextView = GTK_WIDGET(data);
1406 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1407 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1408 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1410 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1411 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1414 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1416 auto text_view = static_cast<GtkTextView *>(data);
1417 GtkTextBuffer *buffer;
1419 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1420 if (!configwindow) return;
1422 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1423 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1426 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1430 gd = generic_dialog_new(_("Reset image overlay template string"),
1431 "reset_image_overlay_template_string", widget, TRUE,
1432 dummy_cancel_cb, data);
1433 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1434 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1435 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1436 gtk_widget_show(gd->dialog);
1439 static void image_overlay_help_cb(GtkWidget *, gpointer)
1441 help_window_show("GuideOptionsOSD.html");
1444 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1448 PangoFontDescription *font_desc;
1450 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1451 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1453 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1455 font_desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog));
1456 font = pango_font_description_to_string(font_desc);
1457 g_free(c_options->image_overlay.font);
1458 c_options->image_overlay.font = g_strdup(font);
1462 gtk_widget_destroy(dialog);
1465 static void image_overlay_set_text_colour_cb(GtkWidget *widget, gpointer)
1470 dialog = gtk_color_chooser_dialog_new("Image Overlay Text Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1471 colour.red = options->image_overlay.text_red;
1472 colour.green = options->image_overlay.text_green;
1473 colour.blue = options->image_overlay.text_blue;
1474 colour.alpha = options->image_overlay.text_alpha;
1475 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1476 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1478 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1480 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1481 c_options->image_overlay.text_red = colour.red*255;
1482 c_options->image_overlay.text_green = colour.green*255;
1483 c_options->image_overlay.text_blue = colour.blue*255;
1484 c_options->image_overlay.text_alpha = colour.alpha*255;
1486 gtk_widget_destroy(dialog);
1490 static void image_overlay_set_background_colour_cb(GtkWidget *widget, gpointer)
1495 dialog = gtk_color_chooser_dialog_new("Image Overlay Background Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1496 colour.red = options->image_overlay.background_red;
1497 colour.green = options->image_overlay.background_green;
1498 colour.blue = options->image_overlay.background_blue;
1499 colour.alpha = options->image_overlay.background_alpha;
1500 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1501 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1503 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1505 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1506 c_options->image_overlay.background_red = colour.red*255;
1507 c_options->image_overlay.background_green = colour.green*255;
1508 c_options->image_overlay.background_blue = colour.blue*255;
1509 c_options->image_overlay.background_alpha = colour.alpha*255;
1511 gtk_widget_destroy(dialog);
1514 static void accel_store_populate()
1517 GList *groups, *actions;
1519 const gchar *accel_path;
1523 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1525 gtk_tree_store_clear(accel_store);
1526 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 */
1528 g_assert(lw && lw->ui_manager);
1529 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1532 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1535 action = GTK_ACTION(actions->data);
1536 accel_path = gtk_action_get_accel_path(action);
1537 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1539 gchar *label, *label2, *tooltip, *accel;
1540 g_object_get(action,
1541 "tooltip", &tooltip,
1545 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1551 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1555 gtk_tree_store_append(accel_store, &iter, nullptr);
1556 gtk_tree_store_set(accel_store, &iter,
1559 AE_TOOLTIP, tooltip ? tooltip : "",
1560 AE_ACCEL, accel_path,
1568 actions = actions->next;
1571 groups = groups->next;
1575 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1580 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1582 auto accel1 = static_cast<gchar *>(data);
1587 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1589 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1590 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1592 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1594 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1595 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1604 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1606 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1610 GtkAccelKey old_key, key;
1611 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1613 gtk_tree_model_get_iter(model, &iter, path);
1614 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1616 /* test if the accelerator can be stored without conflicts*/
1617 gtk_accel_map_lookup_entry(accel_path, &old_key);
1619 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1620 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1621 gtk_accel_map_lookup_entry(accel_path, &key);
1623 /* restore the original for now, the key will be really changed when the changes are confirmed */
1624 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1626 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1627 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1629 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1630 gtk_tree_path_free(path);
1634 static gboolean accel_default_scroll(GtkTreeView *data)
1638 GtkTreeViewColumn *column;
1640 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1641 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1642 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1644 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1648 gtk_tree_path_free(path);
1650 return(G_SOURCE_REMOVE);
1653 static void accel_default_cb(GtkWidget *, gpointer data)
1655 accel_store_populate();
1657 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1660 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1662 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1665 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1668 gchar *accel_path, *accel;
1670 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1671 gtk_accel_map_lookup_entry(accel_path, &key);
1672 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1674 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1676 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1681 static void accel_clear_cb(GtkWidget *, gpointer data)
1683 GtkTreeSelection *selection;
1685 if (!accel_store) return;
1686 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1687 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1690 static void accel_reset_cb(GtkWidget *, gpointer data)
1692 GtkTreeSelection *selection;
1694 if (!accel_store) return;
1695 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1696 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1701 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1705 GtkWidget *scrolled;
1706 GtkWidget *viewport;
1708 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
1709 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1710 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1711 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1712 label = gtk_label_new(title);
1713 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1714 gtk_widget_show(scrolled);
1716 viewport = gtk_viewport_new(nullptr, nullptr);
1717 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1718 gtk_container_add(GTK_CONTAINER(scrolled), viewport);
1719 gtk_widget_show(viewport);
1721 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1722 gtk_container_add(GTK_CONTAINER(viewport), vbox);
1723 gtk_widget_show(vbox);
1728 static void cache_standard_cb(GtkWidget *widget, gpointer)
1730 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1732 c_options->thumbnails.spec_standard =TRUE;
1733 c_options->thumbnails.cache_into_dirs = FALSE;
1737 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1739 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1741 c_options->thumbnails.spec_standard =FALSE;
1742 c_options->thumbnails.cache_into_dirs = FALSE;
1746 static void cache_local_cb(GtkWidget *widget, gpointer)
1748 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1750 c_options->thumbnails.cache_into_dirs = TRUE;
1751 c_options->thumbnails.spec_standard =FALSE;
1755 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1757 if (pos == GTK_ENTRY_ICON_PRIMARY)
1759 gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1763 gtk_entry_set_text(GTK_ENTRY(userdata), "");
1767 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1769 gchar *rating_symbol;
1771 if (pos == GTK_ENTRY_ICON_PRIMARY)
1773 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1774 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1775 g_free(rating_symbol);
1779 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1780 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1781 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1785 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1787 gchar *rating_symbol;
1789 if (pos == GTK_ENTRY_ICON_PRIMARY)
1791 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1792 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1793 g_free(rating_symbol);
1797 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1798 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1799 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1803 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1805 auto hbox = static_cast<GtkContainer *>(data);
1806 GString *str = g_string_new(nullptr);
1807 GtkEntry *hex_code_entry;
1808 gchar *hex_code_full;
1811 guint64 hex_value = 0;
1813 list = gtk_container_get_children(hbox);
1815 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1816 hex_code_full = g_strdup(gtk_entry_get_text(hex_code_entry));
1818 hex_code = g_strsplit(hex_code_full, "+", 2);
1819 if (hex_code[0] && hex_code[1])
1821 hex_value = strtoull(hex_code[1], nullptr, 16);
1823 if (!hex_value || hex_value > 0x10FFFF)
1825 hex_value = 0x003F; // Unicode 'Question Mark'
1827 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1828 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1830 g_strfreev(hex_code);
1831 g_string_free(str, TRUE);
1832 g_free(hex_code_full);
1837 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1839 guint64 star_symbol;
1841 star_symbol = star_rating_symbol_test(widget, data);
1842 c_options->star_rating.star = star_symbol;
1845 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1847 guint64 rejected_symbol;
1849 rejected_symbol = star_rating_symbol_test(widget, data);
1850 c_options->star_rating.rejected = rejected_symbol;
1853 /* general options tab */
1854 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1858 GCancellable *cancellable;
1860 GtkWidget *progress;
1862 GFile *timezone_database_gq;
1863 gchar *timezone_database_user;
1866 static void config_tab_general(GtkWidget *notebook)
1871 GtkWidget *group_frame;
1872 GtkWidget *subgroup;
1874 GtkWidget *ct_button;
1877 gint hours, minutes, remainder;
1879 GtkWidget *star_rating_entry;
1881 gchar *rating_symbol;
1884 gchar *download_locn;
1885 GNetworkMonitor *net_mon;
1886 GSocketConnectable *tz_org;
1887 gboolean internet_available = FALSE;
1890 vbox = scrolled_notebook_page(notebook, _("General"));
1892 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1894 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1895 add_thumb_size_menu(table, 0, 0, _("Size:"));
1896 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1898 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1899 pref_label_new(hbox, _("Custom size: "));
1900 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1901 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1903 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1904 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1906 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1907 pref_checkbox_link_sensitivity(ct_button, subgroup);
1909 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1910 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1911 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1912 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1913 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
1914 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1915 G_CALLBACK(cache_geeqie_cb), nullptr);
1917 group_frame = pref_frame_new(subgroup, TRUE,
1918 _("Store thumbnails local to image folder (non-standard)"),
1919 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1920 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
1921 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
1922 G_CALLBACK(cache_local_cb), nullptr);
1924 group_frame = pref_frame_new(subgroup, TRUE,
1925 _("Use standard thumbnail style and cache, shared with other applications"),
1926 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1927 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
1928 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1929 G_CALLBACK(cache_standard_cb), nullptr);
1931 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
1932 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
1934 pref_checkbox_new_int(group, _("Thumbnail color management"),
1935 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
1937 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
1939 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
1940 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
1942 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
1943 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
1944 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
1947 pref_spacer(group, PREF_PAD_GROUP);
1949 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
1951 c_options->star_rating.star = options->star_rating.star;
1952 c_options->star_rating.rejected = options->star_rating.rejected;
1954 str = g_string_new(nullptr);
1955 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1956 pref_label_new(hbox, _("Star character: "));
1957 str = g_string_append_unichar(str, options->star_rating.star);
1958 pref_label_new(hbox, g_strdup(str->str));
1959 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
1960 star_rating_entry = gtk_entry_new();
1961 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
1962 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
1963 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
1964 gtk_widget_show(star_rating_entry);
1965 button = pref_button_new(nullptr, nullptr, _("Set"),
1966 G_CALLBACK(star_rating_star_test_cb), hbox);
1967 gtk_widget_set_tooltip_text(button, _("Display selected character"));
1968 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1969 gtk_widget_show(button);
1970 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."));
1971 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
1972 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
1973 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1974 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
1975 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
1976 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
1977 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1978 GTK_ENTRY_ICON_PRIMARY, _("Default"));
1979 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
1980 G_CALLBACK(star_rating_star_icon_cb),
1983 g_string_free(str, TRUE);
1984 g_free(rating_symbol);
1986 str = g_string_new(nullptr);
1987 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1988 pref_label_new(hbox, _("Rejected character: "));
1989 str = g_string_append_unichar(str, options->star_rating.rejected);
1990 pref_label_new(hbox, g_strdup(str->str));
1991 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
1992 star_rating_entry = gtk_entry_new();
1993 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
1994 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
1995 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
1996 gtk_widget_show(star_rating_entry);
1997 button = pref_button_new(nullptr, nullptr, _("Set"),
1998 G_CALLBACK(star_rating_rejected_test_cb), hbox);
1999 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2000 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2001 gtk_widget_show(button);
2002 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."));
2003 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2004 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2005 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2006 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2007 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2008 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2009 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2010 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2011 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2012 G_CALLBACK(star_rating_rejected_icon_cb),
2015 g_string_free(str, TRUE);
2016 g_free(rating_symbol);
2018 pref_spacer(group, PREF_PAD_GROUP);
2020 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2022 c_options->slideshow.delay = options->slideshow.delay;
2023 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2024 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2025 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2026 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2027 SLIDESHOW_SUBSECOND_PRECISION;
2029 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2031 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2033 options->slideshow.delay ? hours : 0.0,
2034 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2035 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2036 spin = pref_spin_new(hbox, ":" , nullptr,
2038 options->slideshow.delay ? minutes: 0.0,
2039 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2040 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2041 spin = pref_spin_new(hbox, ":", nullptr,
2042 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2043 options->slideshow.delay ? seconds : 10.0,
2044 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2045 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2047 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2048 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2050 pref_spacer(group, PREF_PAD_GROUP);
2052 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2054 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2055 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2056 pref_checkbox_new_int(group, _("Preload next image"),
2057 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2059 pref_checkbox_new_int(group, _("Refresh on file change"),
2060 options->update_on_time_change, &c_options->update_on_time_change);
2063 pref_spacer(group, PREF_PAD_GROUP);
2065 group = pref_group_new(vbox, FALSE, _("Expand menu and toolbar"), GTK_ORIENTATION_VERTICAL);
2067 pref_checkbox_new_int(group, _("Expand menu and toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2068 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2069 gtk_widget_set_tooltip_text(group, _("Expand the menu and toolbar to the full width of the window"));
2071 pref_spacer(group, PREF_PAD_GROUP);
2073 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2075 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2076 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2077 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2078 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"));
2080 pref_spacer(group, PREF_PAD_GROUP);
2084 net_mon = g_network_monitor_get_default();
2085 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2088 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2089 g_object_unref(tz_org);
2092 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2093 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2095 if (!internet_available)
2097 gtk_widget_set_sensitive(group, FALSE);
2100 tz = g_new0(TZData, 1);
2102 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2103 basename = g_path_get_basename(path);
2104 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2108 if (isfile(tz->timezone_database_user))
2110 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2114 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2117 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2118 pref_label_new(GTK_WIDGET(hbox), download_locn);
2119 g_free(download_locn);
2121 if (!internet_available)
2123 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"));
2127 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"));
2129 gtk_widget_show(button);
2131 pref_spacer(group, PREF_PAD_GROUP);
2133 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2135 help_search_engine_entry = gtk_entry_new();
2136 gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2137 gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2138 gtk_widget_show(help_search_engine_entry);
2140 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"));
2142 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2143 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2144 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2145 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2146 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2147 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2148 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2149 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2150 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2151 G_CALLBACK(help_search_engine_entry_icon_cb),
2152 help_search_engine_entry);
2156 static void config_tab_image(GtkWidget *notebook)
2161 GtkWidget *ct_button;
2162 GtkWidget *enlargement_button;
2166 vbox = scrolled_notebook_page(notebook, _("Image"));
2168 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2170 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2171 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2173 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2174 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2176 c_options->image.zoom_increment = options->image.zoom_increment;
2177 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2178 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2179 G_CALLBACK(zoom_increment_cb), nullptr);
2180 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2182 c_options->image.zoom_style = options->image.zoom_style;
2183 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2184 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2186 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2188 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2189 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2190 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2191 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2193 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2194 pref_checkbox_link_sensitivity(enlargement_button, spin);
2195 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."));
2197 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2198 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2199 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2200 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2202 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2203 pref_checkbox_link_sensitivity(ct_button, spin);
2204 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."));
2206 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2208 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2209 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2211 options->image.tile_size, &c_options->image.tile_size);
2212 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."));
2214 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2216 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2217 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2219 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2220 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2222 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2223 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2225 c_options->image.border_color = options->image.border_color;
2227 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2228 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2230 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2231 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2233 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2234 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2239 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2241 LayoutWindow *lw = nullptr;
2242 gchar *default_path;
2245 /* Get current lw */
2248 tmp_id = lw->options.id;
2249 lw->options.id = g_strdup("lw_default");
2251 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2252 save_default_layout_options_to_file(default_path, options, lw);
2253 g_free(lw->options.id);
2254 lw->options.id = tmp_id;
2255 g_free(default_path);
2258 static gboolean popover_cb(gpointer data)
2260 auto popover = static_cast<GtkPopover *>(data);
2262 gtk_popover_popdown(popover);
2267 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2269 gtk_popover_popup(popover);
2271 g_timeout_add(2000, popover_cb, popover);
2274 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2278 popover = gtk_popover_new(parent);
2279 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2280 gtk_container_add (GTK_CONTAINER(popover), child);
2281 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2282 gtk_widget_show (child);
2287 static void config_tab_windows(GtkWidget *notebook)
2292 GtkWidget *subgroup;
2294 GtkWidget *ct_button;
2297 vbox = scrolled_notebook_page(notebook, _("Windows"));
2299 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2301 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2302 options->save_window_positions, &c_options->save_window_positions);
2304 button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2305 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2306 pref_checkbox_link_sensitivity(ct_button, button);
2308 button = pref_checkbox_new_int(group, _("Remember window workspace"),
2309 options->save_window_workspace, &c_options->save_window_workspace);
2310 pref_checkbox_link_sensitivity(ct_button, button);
2312 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2313 options->tools_restore_state, &c_options->tools_restore_state);
2315 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2316 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2318 pref_checkbox_new_int(group, _("Show window IDs"),
2319 options->show_window_ids, &c_options->show_window_ids);
2321 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2322 pref_label_new(subgroup, _("Use current layout for default: "));
2323 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2327 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2328 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2329 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2331 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2333 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2334 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2336 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2337 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2338 options->image.limit_window_size, &c_options->image.limit_window_size);
2339 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2341 options->image.max_window_size, &c_options->image.max_window_size);
2342 pref_checkbox_link_sensitivity(ct_button, spin);
2344 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2346 c_options->fullscreen.screen = options->fullscreen.screen;
2347 c_options->fullscreen.above = options->fullscreen.above;
2348 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2349 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2350 gtk_widget_show(hbox);
2352 pref_checkbox_new_int(group, _("Smooth image flip"),
2353 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2354 pref_checkbox_new_int(group, _("Disable screen saver"),
2355 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2358 #define PRE_FORMATTED_COLUMNS 5
2359 static void config_tab_osd(GtkWidget *notebook)
2365 GtkWidget *image_overlay_template_view;
2366 GtkWidget *scrolled;
2367 GtkWidget *scrolled_pre_formatted;
2368 GtkTextBuffer *buffer;
2370 GtkWidget *subgroup;
2372 vbox = scrolled_notebook_page(notebook, _("OSD"));
2374 image_overlay_template_view = gtk_text_view_new();
2376 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2378 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2380 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2381 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2382 gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2383 gtk_widget_show(scrolled_pre_formatted);
2384 gtk_widget_show(subgroup);
2386 pref_line(group, PREF_PAD_GAP);
2388 pref_label_new(group, _("Image overlay template"));
2390 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2391 gtk_widget_set_size_request(scrolled, 200, 150);
2392 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2393 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2394 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2395 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2396 gtk_widget_show(scrolled);
2398 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2399 _("Extensive formatting options are shown in the Help file"));
2401 gtk_container_add(GTK_CONTAINER(scrolled), image_overlay_template_view);
2402 gtk_widget_show(image_overlay_template_view);
2404 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2406 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2407 G_CALLBACK(image_overlay_set_font_cb), notebook);
2409 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2410 gtk_widget_show(button);
2412 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"),
2413 G_CALLBACK(image_overlay_set_text_colour_cb), nullptr);
2414 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2415 gtk_widget_show(button);
2417 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"),
2418 G_CALLBACK(image_overlay_set_background_colour_cb), nullptr);
2419 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2420 gtk_widget_show(button);
2421 image_overlay_set_text_colours();
2423 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2424 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2425 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2426 gtk_widget_show(button);
2428 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2429 G_CALLBACK(image_overlay_help_cb), nullptr);
2430 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2431 gtk_widget_show(button);
2433 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2434 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2435 g_signal_connect(G_OBJECT(buffer), "changed",
2436 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2438 pref_line(group, PREF_PAD_GAP);
2440 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2441 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2442 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2443 gtk_widget_show(hbox);
2444 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2445 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2446 gtk_widget_show(label);
2447 pref_spacer(group,TRUE);
2449 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2450 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2451 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2452 gtk_widget_show(hbox);
2453 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2454 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2455 gtk_widget_show(label);
2456 pref_spacer(group,TRUE);
2458 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2459 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2460 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2461 gtk_widget_show(hbox);
2462 label = gtk_label_new(_("%path:39%"));
2463 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2464 gtk_widget_show(label);
2465 pref_spacer(group,TRUE);
2467 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2468 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2469 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2470 gtk_widget_show(hbox);
2471 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%"));
2472 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2473 gtk_widget_show(label);
2474 pref_spacer(group,TRUE);
2476 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2477 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2478 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2479 gtk_widget_show(hbox);
2480 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2481 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2482 gtk_widget_show(label);
2485 static GtkTreeModel *create_class_model()
2487 GtkListStore *model;
2491 /* create list store */
2492 model = gtk_list_store_new(1, G_TYPE_STRING);
2493 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2495 gtk_list_store_append(model, &iter);
2496 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2498 return GTK_TREE_MODEL (model);
2503 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2505 gint n = GPOINTER_TO_INT(data);
2507 FilterEntry *filter_a;
2508 FilterEntry *filter_b;
2510 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2511 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2515 case FILETYPES_COLUMN_ENABLED:
2517 ret = filter_a->enabled - filter_b->enabled;
2520 case FILETYPES_COLUMN_FILTER:
2522 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2525 case FILETYPES_COLUMN_DESCRIPTION:
2527 ret = g_utf8_collate(filter_a->description, filter_b->description);
2530 case FILETYPES_COLUMN_CLASS:
2532 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2535 case FILETYPES_COLUMN_WRITABLE:
2537 ret = filter_a->writable - filter_b->writable;
2540 case FILETYPES_COLUMN_SIDECAR:
2542 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2546 g_return_val_if_reached(0);
2552 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2555 gboolean ret = TRUE;
2557 gtk_tree_model_get(model, iter, 0, &fe, -1);
2559 if (g_strstr_len(fe->extensions, -1, key))
2567 static void config_tab_files(GtkWidget *notebook)
2574 GtkWidget *ct_button;
2575 GtkWidget *scrolled;
2576 GtkWidget *filter_view;
2577 GtkCellRenderer *renderer;
2578 GtkTreeSelection *selection;
2579 GtkTreeViewColumn *column;
2581 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2583 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2585 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2586 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2587 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2588 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2589 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);
2590 pref_checkbox_new_int(group, _("Disable file extension checks"),
2591 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2593 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2594 options->file_filter.disable, &c_options->file_filter.disable);
2597 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2599 sidecar_ext_entry = gtk_entry_new();
2600 gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2601 gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2602 gtk_widget_show(sidecar_ext_entry);
2604 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2606 frame = pref_group_parent(group);
2607 g_signal_connect(G_OBJECT(ct_button), "toggled",
2608 G_CALLBACK(filter_disable_cb), frame);
2609 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2611 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2612 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2613 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2614 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2615 gtk_widget_show(scrolled);
2617 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2618 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2619 g_object_unref(filter_store);
2620 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2621 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2623 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2625 column = gtk_tree_view_column_new();
2626 gtk_tree_view_column_set_title(column, _("Enabled"));
2627 gtk_tree_view_column_set_resizable(column, TRUE);
2629 renderer = gtk_cell_renderer_toggle_new();
2630 g_signal_connect(G_OBJECT(renderer), "toggled",
2631 G_CALLBACK(filter_store_enable_cb), filter_store);
2632 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2633 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2634 GINT_TO_POINTER(FE_ENABLE), nullptr);
2635 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);
2636 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2637 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2639 column = gtk_tree_view_column_new();
2640 gtk_tree_view_column_set_title(column, _("Filter"));
2641 gtk_tree_view_column_set_resizable(column, TRUE);
2642 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);
2643 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2645 renderer = gtk_cell_renderer_text_new();
2646 g_signal_connect(G_OBJECT(renderer), "edited",
2647 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2648 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2649 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2650 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2651 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2652 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2654 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2655 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2656 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2658 column = gtk_tree_view_column_new();
2659 gtk_tree_view_column_set_title(column, _("Description"));
2660 gtk_tree_view_column_set_resizable(column, TRUE);
2661 gtk_tree_view_column_set_fixed_width(column, 200);
2662 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2664 renderer = gtk_cell_renderer_text_new();
2665 g_signal_connect(G_OBJECT(renderer), "edited",
2666 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2667 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2668 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2669 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2670 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2671 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2672 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);
2673 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2675 column = gtk_tree_view_column_new();
2676 gtk_tree_view_column_set_title(column, _("Class"));
2677 gtk_tree_view_column_set_resizable(column, TRUE);
2678 renderer = gtk_cell_renderer_combo_new();
2679 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2680 "model", create_class_model(),
2685 g_signal_connect(G_OBJECT(renderer), "edited",
2686 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2687 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2688 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2689 GINT_TO_POINTER(FE_CLASS), nullptr);
2690 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2691 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);
2692 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2694 column = gtk_tree_view_column_new();
2695 gtk_tree_view_column_set_title(column, _("Writable"));
2696 gtk_tree_view_column_set_resizable(column, FALSE);
2697 renderer = gtk_cell_renderer_toggle_new();
2698 g_signal_connect(G_OBJECT(renderer), "toggled",
2699 G_CALLBACK(filter_store_writable_cb), filter_store);
2700 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2701 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2702 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2703 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2704 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);
2705 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2707 column = gtk_tree_view_column_new();
2708 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2709 gtk_tree_view_column_set_resizable(column, FALSE);
2710 renderer = gtk_cell_renderer_toggle_new();
2711 g_signal_connect(G_OBJECT(renderer), "toggled",
2712 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2713 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2714 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2715 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2716 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2717 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);
2718 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2720 filter_store_populate();
2721 gtk_container_add(GTK_CONTAINER(scrolled), filter_view);
2722 gtk_widget_show(filter_view);
2724 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2726 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2727 G_CALLBACK(filter_default_cb), filter_view);
2728 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2729 gtk_widget_show(button);
2731 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2732 G_CALLBACK(filter_remove_cb), filter_view);
2733 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2734 gtk_widget_show(button);
2736 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2737 G_CALLBACK(filter_add_cb), filter_view);
2738 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2739 gtk_widget_show(button);
2743 static void config_tab_metadata(GtkWidget *notebook)
2748 GtkWidget *ct_button;
2750 GtkWidget *tmp_widget;
2752 GtkWidget *text_label;
2754 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2757 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2759 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2761 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."));
2762 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2763 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2765 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2767 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2768 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2769 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"));
2770 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2772 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)"));
2773 gtk_widget_set_tooltip_markup(ct_button, markup);
2777 gtk_widget_set_sensitive(ct_button, FALSE);
2780 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2781 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2782 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)"));
2783 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2786 label = pref_label_new(group, "");
2787 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(), "/");
2788 gtk_label_set_markup (GTK_LABEL(label), markup);
2791 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2792 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2793 gtk_widget_set_margin_start(label, 22);
2794 pref_spacer(group, PREF_PAD_GROUP);
2796 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2798 gtk_widget_set_sensitive(group, FALSE);
2801 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2802 pref_checkbox_link_sensitivity(ct_button, hbox);
2804 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);
2805 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2807 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);
2809 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2811 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2812 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2813 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2814 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", ")");
2815 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2818 pref_spacer(group, PREF_PAD_GROUP);
2820 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2822 gtk_widget_set_sensitive(group, FALSE);
2825 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);
2827 pref_spacer(group, PREF_PAD_GROUP);
2829 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2830 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);
2831 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2833 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2834 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2836 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2837 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"));
2840 gtk_widget_set_sensitive(ct_button, FALSE);
2843 pref_spacer(group, PREF_PAD_GROUP);
2845 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2847 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2849 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2850 pref_checkbox_link_sensitivity(ct_button, hbox);
2852 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2854 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2856 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2858 pref_spacer(group, PREF_PAD_GROUP);
2861 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2863 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2864 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2867 pref_spacer(group, PREF_PAD_GROUP);
2869 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2871 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2872 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");
2877 struct KeywordFindData
2884 GtkWidget *button_close;
2885 GtkWidget *button_stop;
2886 GtkWidget *button_start;
2887 GtkWidget *progress;
2895 guint idle_id; /* event source id */
2898 #define KEYWORD_DIALOG_WIDTH 400
2900 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
2902 GList *list_d = nullptr;
2903 GList *list_f = nullptr;
2907 filelist_read(dir_fd, &list_f, &list_d);
2911 filelist_read(dir_fd, &list_f, nullptr);
2914 list_f = filelist_filter(list_f, FALSE);
2915 list_d = filelist_filter(list_d, TRUE);
2917 kfd->list = g_list_concat(list_f, kfd->list);
2918 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
2921 static void keywords_find_reset(KeywordFindData *kfd)
2923 filelist_free(kfd->list);
2924 kfd->list = nullptr;
2926 filelist_free(kfd->list_dir);
2927 kfd->list_dir = nullptr;
2930 static void keywords_find_close_cb(GenericDialog *, gpointer data)
2932 auto kfd = static_cast<KeywordFindData *>(data);
2934 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
2936 keywords_find_reset(kfd);
2937 generic_dialog_close(kfd->gd);
2941 static void keywords_find_finish(KeywordFindData *kfd)
2943 keywords_find_reset(kfd);
2945 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
2946 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
2948 gtk_widget_set_sensitive(kfd->group, TRUE);
2949 gtk_widget_set_sensitive(kfd->button_start, TRUE);
2950 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
2951 gtk_widget_set_sensitive(kfd->button_close, TRUE);
2954 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
2956 auto kfd = static_cast<KeywordFindData *>(data);
2958 g_idle_remove_by_data(kfd);
2960 keywords_find_finish(kfd);
2963 static gboolean keywords_find_file(gpointer data)
2965 auto kfd = static_cast<KeywordFindData *>(data);
2967 GtkTextBuffer *buffer;
2975 fd = static_cast<FileData *>(kfd->list->data);
2976 kfd->list = g_list_remove(kfd->list, fd);
2978 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
2979 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
2983 gtk_text_buffer_get_end_iter(buffer, &iter);
2984 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
2985 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
2987 keywords = keywords->next;
2990 gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
2991 file_data_unref(fd);
2992 g_list_free_full(keywords, g_free);
2994 return (G_SOURCE_CONTINUE);
2996 else if (kfd->list_dir)
3000 fd = static_cast<FileData *>(kfd->list_dir->data);
3001 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3003 keywords_find_folder(kfd, fd);
3005 file_data_unref(fd);
3007 return G_SOURCE_CONTINUE;
3010 keywords_find_finish(kfd);
3012 return G_SOURCE_REMOVE;
3015 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3017 auto kfd = static_cast<KeywordFindData *>(data);
3020 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3022 path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3023 parse_out_relatives(path);
3027 warning_dialog(_("Invalid folder"),
3028 _("The specified folder can not be found."),
3029 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3035 gtk_widget_set_sensitive(kfd->group, FALSE);
3036 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3037 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3038 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3039 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3041 dir_fd = file_data_new_dir(path);
3042 keywords_find_folder(kfd, dir_fd);
3043 file_data_unref(dir_fd);
3044 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3050 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3052 KeywordFindData *kfd;
3056 kfd = g_new0(KeywordFindData, 1);
3058 kfd->gd = generic_dialog_new(_("Search for keywords"),
3059 "search_for_keywords",
3062 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3063 kfd->gd->cancel_cb = keywords_find_close_cb;
3064 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3065 keywords_find_close_cb, FALSE);
3066 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3067 keywords_find_start_cb, FALSE);
3068 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3069 keywords_find_stop_cb, FALSE);
3070 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3072 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3074 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3075 pref_spacer(hbox, PREF_PAD_INDENT);
3076 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3078 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3079 pref_label_new(hbox, _("Folder:"));
3081 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3082 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3083 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3084 gtk_widget_show(label);
3086 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3088 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3089 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3091 kfd->progress = gtk_entry_new();
3092 gtk_widget_set_can_focus(kfd->progress, FALSE);
3093 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3094 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3095 gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3096 gtk_widget_show(kfd->progress);
3098 kfd->spinner = gtk_spinner_new();
3099 gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3100 gtk_widget_show(kfd->spinner);
3102 kfd->list = nullptr;
3104 gtk_widget_show(kfd->gd->dialog);
3107 static void keywords_find_cb(GtkWidget *widget, gpointer)
3109 const gchar *path = layout_get_path(nullptr);
3111 if (!path || !*path) path = homedir();
3112 keywords_find_dialog(widget, path);
3115 static void config_tab_keywords_save()
3117 GtkTextIter start, end;
3118 GtkTextBuffer *buffer;
3119 GList *kw_list = nullptr;
3125 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3126 gtk_text_buffer_get_bounds(buffer, &start, &end);
3128 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3130 kw_split = strtok(buffer_text, "\n");
3131 while (kw_split != nullptr)
3137 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3146 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3148 kw_split = strtok(nullptr, "\n");
3151 keyword_list_set(kw_list);
3153 g_list_free_full(kw_list, g_free);
3154 g_free(buffer_text);
3157 static void config_tab_keywords(GtkWidget *notebook)
3163 GtkWidget *scrolled;
3165 GtkTextBuffer *buffer;
3168 GspellTextView *gspell_view;
3171 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3173 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3175 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3177 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3178 G_CALLBACK(keywords_find_cb), keyword_text);
3179 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3182 keyword_text = gtk_text_view_new();
3183 gtk_widget_set_size_request(keyword_text, 20, 20);
3184 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3185 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3186 gtk_widget_show(scrolled);
3189 if (options->metadata.check_spelling)
3191 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3192 gspell_text_view_basic_setup(gspell_view);
3196 gtk_container_add(GTK_CONTAINER(scrolled), keyword_text);
3197 gtk_widget_show(keyword_text);
3199 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3201 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3202 gtk_text_buffer_create_tag(buffer, "monospace",
3203 "family", "monospace", NULL);
3205 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3206 gtk_text_buffer_get_start_iter(buffer, &iter);
3207 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3210 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3212 GList *kwl = keyword_list_get();
3213 kwl = g_list_first(kwl);
3216 gtk_text_buffer_get_end_iter (buffer, &iter);
3217 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3218 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3223 gtk_text_buffer_set_modified(buffer, FALSE);
3230 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3232 auto option = static_cast<gint *>(data);
3234 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3238 *option = INTENT_PERCEPTUAL;
3241 *option = INTENT_RELATIVE_COLORIMETRIC;
3244 *option = INTENT_SATURATION;
3247 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3252 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3253 gint option, gint *option_c)
3260 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3262 combo = gtk_combo_box_text_new();
3264 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3265 if (option == INTENT_PERCEPTUAL) current = 0;
3266 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3267 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3268 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3269 if (option == INTENT_SATURATION) current = 2;
3270 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3271 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3273 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3275 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3277 g_signal_connect(G_OBJECT(combo), "changed",
3278 G_CALLBACK(intent_menu_cb), option_c);
3280 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3281 gtk_widget_show(combo);
3285 static void config_tab_color(GtkWidget *notebook)
3294 vbox = scrolled_notebook_page(notebook, _("Color management"));
3296 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3298 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3301 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3302 gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
3304 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3305 pref_label_bold(label, TRUE, FALSE);
3307 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3308 pref_label_bold(label, TRUE, FALSE);
3310 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3311 pref_label_bold(label, TRUE, FALSE);
3313 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3318 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3319 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3322 entry = gtk_entry_new();
3323 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3324 if (options->color_profile.input_name[i])
3326 gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3328 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
3329 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3330 gtk_widget_show(entry);
3331 color_profile_input_name_entry[i] = entry;
3333 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3334 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3335 gtk_widget_set_size_request(entry, 160, -1);
3336 gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
3337 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3338 gtk_widget_show(tabcomp);
3339 color_profile_input_file_entry[i] = entry;
3342 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3344 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3346 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3347 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3349 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3351 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3352 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3353 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3354 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3355 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3357 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3359 gtk_table_attach(GTK_TABLE(table), tabcomp, 1, 2,
3361 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3363 gtk_widget_show(tabcomp);
3366 /* advanced entry tab */
3367 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3369 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3371 c_options->file_ops.use_system_trash = FALSE;
3372 c_options->file_ops.no_trash = FALSE;
3376 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3378 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3380 c_options->file_ops.use_system_trash = TRUE;
3381 c_options->file_ops.no_trash = FALSE;
3385 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3387 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3389 c_options->file_ops.no_trash = TRUE;
3393 static void config_tab_behavior(GtkWidget *notebook)
3400 GtkWidget *ct_button;
3404 GtkWidget *with_rename;
3405 GtkWidget *collections_on_top;
3406 GtkWidget *hide_window_in_fullscreen;
3407 GtkWidget *checkbox;
3410 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3412 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3414 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3415 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3416 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3417 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3418 pref_checkbox_new_int(group, _("Enable Delete key"),
3419 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3421 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3422 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3424 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3425 pref_checkbox_link_sensitivity(ct_button, hbox);
3427 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3428 pref_label_new(hbox, _("Folder:"));
3430 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3431 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3432 gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3433 gtk_widget_show(tabcomp);
3435 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3436 pref_checkbox_link_sensitivity(ct_button, hbox);
3438 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3439 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3440 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3441 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3442 button = pref_button_new(nullptr, nullptr, _("View"),
3443 G_CALLBACK(safe_delete_view_cb), nullptr);
3444 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3445 gtk_widget_show(button);
3447 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3448 G_CALLBACK(safe_delete_clear_cb), nullptr);
3449 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3451 c_options->file_ops.no_trash = options->file_ops.no_trash;
3452 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3454 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3455 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3457 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3458 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3460 gtk_widget_show(button);
3462 pref_spacer(group, PREF_PAD_GROUP);
3465 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3467 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3468 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3470 pref_checkbox_new_int(group, _("In place renaming"),
3471 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3473 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3474 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3476 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3477 options->circular_selection_lists, &c_options->circular_selection_lists);
3478 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3480 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3481 options->marks_save, &c_options->marks_save);
3482 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3484 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3485 options->with_rename, &c_options->with_rename);
3486 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3488 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3489 options->collections_duplicates, &c_options->collections_duplicates);
3490 gtk_widget_set_tooltip_text(collections_on_top,"Allow the same image to be in a Collection more than once");
3492 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3493 options->collections_on_top, &c_options->collections_on_top);
3494 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3496 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3497 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3498 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3500 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3501 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3503 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);
3504 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."));
3506 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3507 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3509 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3510 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3512 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3513 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3515 pref_spacer(group, PREF_PAD_GROUP);
3517 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3519 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3520 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3521 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3522 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3523 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3524 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3525 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3526 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3527 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3528 options->image_l_click_archive, &c_options->image_l_click_archive);
3529 pref_checkbox_new_int(group, _("Play video by left click on image"),
3530 options->image_l_click_video, &c_options->image_l_click_video);
3531 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3532 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3534 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3535 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3536 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3537 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3538 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3540 pref_spacer(group, PREF_PAD_GROUP);
3542 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3544 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3545 options->override_disable_gpu, &c_options->override_disable_gpu);
3546 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3549 pref_spacer(group, PREF_PAD_GROUP);
3551 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3553 pref_spin_new_int(group, _("Debug level:"), nullptr,
3554 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3556 pref_checkbox_new_int(group, _("Timer data"),
3557 options->log_window.timer_data, &c_options->log_window.timer_data);
3559 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3560 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3564 /* accelerators tab */
3566 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3568 gboolean ret = TRUE;
3573 gtk_tree_model_get(model, iter, column, &text, -1);
3574 text_nocase = g_utf8_casefold(text, -1);
3575 key_nocase = g_utf8_casefold(key, -1);
3577 if (g_strstr_len(text_nocase, -1, key_nocase))
3584 g_free(text_nocase);
3589 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3594 list = gtk_tree_view_get_columns(tree_view);
3595 col_num = g_list_index(list, column);
3599 gtk_tree_view_set_search_column(tree_view, col_num);
3602 static void config_tab_accelerators(GtkWidget *notebook)
3608 GtkWidget *scrolled;
3609 GtkWidget *accel_view;
3610 GtkCellRenderer *renderer;
3611 GtkTreeSelection *selection;
3612 GtkTreeViewColumn *column;
3614 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3616 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3618 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3619 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3620 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3621 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3622 gtk_widget_show(scrolled);
3624 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3626 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3627 g_object_unref(accel_store);
3628 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3629 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3631 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3633 renderer = gtk_cell_renderer_text_new();
3635 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3640 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3641 gtk_tree_view_column_set_resizable(column, TRUE);
3642 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3645 renderer = gtk_cell_renderer_accel_new();
3646 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3647 G_CALLBACK(accel_store_cleared_cb), accel_store);
3648 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3649 G_CALLBACK(accel_store_edited_cb), accel_store);
3652 g_object_set (renderer,
3654 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3657 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3662 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3663 gtk_tree_view_column_set_resizable(column, TRUE);
3664 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3666 renderer = gtk_cell_renderer_text_new();
3668 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3673 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3674 gtk_tree_view_column_set_resizable(column, TRUE);
3675 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3677 renderer = gtk_cell_renderer_text_new();
3679 column = gtk_tree_view_column_new_with_attributes("Accel",
3684 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3685 gtk_tree_view_column_set_resizable(column, TRUE);
3686 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3688 /* Search on text in column */
3689 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3690 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3691 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3692 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3693 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3695 accel_store_populate();
3696 gtk_container_add(GTK_CONTAINER(scrolled), accel_view);
3697 gtk_widget_show(accel_view);
3699 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3701 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3702 G_CALLBACK(accel_default_cb), accel_view);
3703 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3704 gtk_widget_show(button);
3706 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3707 G_CALLBACK(accel_reset_cb), accel_view);
3708 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3709 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3710 gtk_widget_show(button);
3712 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3713 G_CALLBACK(accel_clear_cb), accel_view);
3714 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3715 gtk_widget_show(button);
3718 /* toolbar main tab */
3719 static void config_tab_toolbar_main(GtkWidget *notebook)
3722 GtkWidget *toolbardata;
3725 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3727 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3729 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3730 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3731 gtk_widget_show(vbox);
3734 /* toolbar status tab */
3735 static void config_tab_toolbar_status(GtkWidget *notebook)
3738 GtkWidget *toolbardata;
3741 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3743 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3745 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3746 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3747 gtk_widget_show(vbox);
3751 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3753 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3756 static void config_tab_advanced(GtkWidget *notebook)
3760 GSList *formats_list;
3761 GList *extensions_list = nullptr;
3764 GdkPixbufFormat *fm;
3766 GString *types_string = g_string_new(nullptr);
3767 GtkWidget *types_string_label;
3768 GtkWidget *threads_string_label;
3769 GtkWidget *dupes_threads_spin;
3771 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3772 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3774 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3776 pref_spacer(group, PREF_PAD_GROUP);
3778 formats_list = gdk_pixbuf_get_formats();
3780 while (formats_list)
3782 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3783 extensions = gdk_pixbuf_format_get_extensions(fm);
3786 while (extensions[i])
3788 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3792 g_strfreev(extensions);
3793 formats_list = formats_list->next;
3796 while (extensions_list)
3798 if (types_string->len == 0)
3800 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3804 types_string = g_string_append(types_string, ", ");
3805 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3808 extensions_list = extensions_list->next;
3811 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3812 types_string_label = pref_label_new(group, types_string->str);
3813 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3815 pref_spacer(group, PREF_PAD_GROUP);
3817 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3818 external_preview_select_entry = gtk_entry_new();
3819 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3820 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3821 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3822 gtk_widget_show(tabcomp);
3824 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3825 external_preview_extract_entry = gtk_entry_new();
3826 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3827 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3828 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3829 gtk_widget_show(tabcomp);
3831 gtk_widget_show(vbox);
3833 g_slist_free(formats_list);
3834 g_list_free_full(extensions_list, g_free);
3835 g_string_free(types_string, TRUE);
3837 pref_spacer(group, PREF_PAD_GROUP);
3839 pref_line(vbox, PREF_PAD_SPACE);
3840 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3842 threads_string_label = pref_label_new(group, "This option limits the number of threads (or cpu cores)\nthat Geeqie will use when running duplicate checks. The default value is 0, which means all available cores will be used.");
3843 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3845 pref_spacer(vbox, PREF_PAD_GROUP);
3847 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3848 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3852 static void config_tab_stereo(GtkWidget *notebook)
3860 GtkWidget *fs_button;
3861 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3863 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3865 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3866 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3868 table = pref_table_new(group, 2, 2, TRUE, FALSE);
3869 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3870 pref_checkbox_new_int(box, _("Mirror left image"),
3871 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3872 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3873 pref_checkbox_new_int(box, _("Flip left image"),
3874 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3875 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3876 pref_checkbox_new_int(box, _("Mirror right image"),
3877 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3878 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3879 pref_checkbox_new_int(box, _("Flip right image"),
3880 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3881 pref_checkbox_new_int(group, _("Swap left and right images"),
3882 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3883 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3884 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3886 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3887 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3888 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3889 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3890 pref_checkbox_link_sensitivity(fs_button, box2);
3891 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3892 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3893 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3894 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3895 pref_checkbox_new_int(box, _("Mirror left image"),
3896 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3897 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3898 pref_checkbox_new_int(box, _("Flip left image"),
3899 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
3900 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3901 pref_checkbox_new_int(box, _("Mirror right image"),
3902 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
3903 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3904 pref_checkbox_new_int(box, _("Flip right image"),
3905 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
3906 pref_checkbox_new_int(box2, _("Swap left and right images"),
3907 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
3908 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
3909 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
3911 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
3912 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
3913 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
3914 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
3915 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
3916 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
3917 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
3918 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
3919 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
3920 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
3921 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
3922 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
3923 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
3924 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
3928 /* Main preferences window */
3929 static void config_window_create(LayoutWindow *lw)
3931 GtkWidget *win_vbox;
3933 GtkWidget *notebook;
3935 GtkWidget *ct_button;
3937 if (!c_options) c_options = init_options(nullptr);
3939 configwindow = window_new(GTK_WINDOW_TOPLEVEL, "preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
3940 DEBUG_NAME(configwindow);
3941 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
3942 g_signal_connect(G_OBJECT(configwindow), "delete_event",
3943 G_CALLBACK(config_window_delete), NULL);
3944 if (options->save_dialog_window_positions)
3946 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
3947 gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
3951 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
3953 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
3954 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
3956 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3957 gtk_container_add(GTK_CONTAINER(configwindow), win_vbox);
3958 gtk_widget_show(win_vbox);
3960 notebook = gtk_notebook_new();
3961 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
3962 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
3963 gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
3965 config_tab_general(notebook);
3966 config_tab_image(notebook);
3967 config_tab_osd(notebook);
3968 config_tab_windows(notebook);
3969 config_tab_accelerators(notebook);
3970 config_tab_files(notebook);
3971 config_tab_metadata(notebook);
3972 config_tab_keywords(notebook);
3973 config_tab_color(notebook);
3974 config_tab_stereo(notebook);
3975 config_tab_behavior(notebook);
3976 config_tab_toolbar_main(notebook);
3977 config_tab_toolbar_status(notebook);
3978 config_tab_advanced(notebook);
3980 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
3982 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
3983 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
3984 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
3985 gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
3986 gtk_widget_show(hbox);
3988 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
3989 G_CALLBACK(config_window_help_cb), notebook);
3990 gtk_container_add(GTK_CONTAINER(hbox), button);
3991 gtk_widget_set_can_default(button, TRUE);
3992 gtk_widget_show(button);
3994 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
3995 G_CALLBACK(config_window_ok_cb), notebook);
3996 gtk_container_add(GTK_CONTAINER(hbox), button);
3997 gtk_widget_set_can_default(button, TRUE);
3998 gtk_widget_grab_default(button);
3999 gtk_widget_show(button);
4003 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4004 G_CALLBACK(config_window_close_cb), nullptr);
4005 gtk_container_add(GTK_CONTAINER(hbox), button);
4006 gtk_widget_set_can_default(button, TRUE);
4007 gtk_widget_show(button);
4009 if (!generic_dialog_get_alternative_button_order(configwindow))
4011 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4014 gtk_widget_show(notebook);
4016 gtk_widget_show(configwindow);
4020 *-----------------------------------------------------------------------------
4021 * config window show (public)
4022 *-----------------------------------------------------------------------------
4025 void show_config_window(LayoutWindow *lw)
4029 gtk_window_present(GTK_WINDOW(configwindow));
4033 config_window_create(lw);
4042 void show_about_window(LayoutWindow *lw)
4044 GDataInputStream *data_stream;
4045 GInputStream *in_stream_authors;
4046 GInputStream *in_stream_translators;
4048 GdkPixbuf *pixbuf_icon;
4049 GdkPixbuf *pixbuf_logo;
4053 gchar *authors[1000];
4055 gchar *timezone_path;
4064 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");
4066 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4067 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4069 cd = ZDOpenDatabase(timezone_path);
4072 copyright = g_string_append(copyright, ZDGetNotice(cd));
4076 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4078 ZDCloseDatabase(cd);
4080 g_free(timezone_path);
4082 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4084 data_stream = g_data_input_stream_new(in_stream_authors);
4086 authors[0] = nullptr;
4087 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4089 authors[i_authors] = g_strdup(author_line);
4091 g_free(author_line);
4093 authors[i_authors] = nullptr;
4095 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4097 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4099 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4101 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4102 translators = static_cast<gchar *>(g_malloc0(size));
4103 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4104 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4106 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);
4108 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4109 artists[1] = nullptr;
4111 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4112 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4113 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4114 "title", _("About Geeqie"),
4116 "program-name", GQ_APPNAME,
4118 "logo", pixbuf_logo,
4119 "icon", pixbuf_icon,
4120 "website", GQ_WEBSITE,
4121 "website-label", "Website",
4122 "comments", comment,
4125 "translator-credits", translators,
4126 "wrap-license", TRUE,
4127 "license", copyright->str,
4130 g_string_free(copyright, TRUE);
4132 while(n < i_authors)
4140 g_free(translators);
4141 g_object_unref(data_stream);
4142 g_object_unref(in_stream_authors);
4143 g_object_unref(in_stream_translators);
4146 static void image_overlay_set_text_colours()
4148 c_options->image_overlay.text_red = options->image_overlay.text_red;
4149 c_options->image_overlay.text_green = options->image_overlay.text_green;
4150 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4151 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4152 c_options->image_overlay.background_red = options->image_overlay.background_red;
4153 c_options->image_overlay.background_green = options->image_overlay.background_green;
4154 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4155 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4159 *-----------------------------------------------------------------------------
4160 * timezone database routines
4161 *-----------------------------------------------------------------------------
4164 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4166 GError *error = nullptr;
4167 auto tz = static_cast<TZData *>(data);
4168 gchar *tmp_filename;
4169 gchar *timezone_bin;
4170 gchar *tmp_dir = nullptr;
4173 if (!g_cancellable_is_cancelled(tz->cancellable))
4175 generic_dialog_close(tz->gd);
4179 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4181 tmp_filename = g_file_get_path(tz->tmp_g_file);
4182 fd = file_data_new_simple(tmp_filename);
4183 tmp_dir = open_archive(fd);
4187 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4188 if (isfile(timezone_bin))
4190 move_file(timezone_bin, tz->timezone_database_user);
4194 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4197 g_free(timezone_bin);
4198 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4202 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4204 g_free(tmp_filename);
4205 file_data_unref(fd);
4209 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4212 g_file_delete(tz->tmp_g_file, nullptr, &error);
4213 g_object_unref(tz->tmp_g_file);
4214 tz->tmp_g_file = nullptr;
4215 g_object_unref(tz->cancellable);
4216 g_object_unref(tz->timezone_database_gq);
4219 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4221 auto tz = static_cast<TZData *>(data);
4223 if (!g_cancellable_is_cancelled(tz->cancellable))
4225 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4229 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4231 auto tz = static_cast<TZData *>(data);
4233 g_cancellable_cancel(tz->cancellable);
4236 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4238 auto tz = static_cast<TZData *>(data);
4239 GError *error = nullptr;
4240 GFileIOStream *io_stream;
4247 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4251 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4252 log_printf("Error: Download timezone database failed:\n%s", error->message);
4253 g_error_free(error);
4254 g_object_unref(tz->tmp_g_file);
4258 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4260 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4262 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4264 tz->progress = gtk_progress_bar_new();
4265 gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4266 gtk_widget_show(tz->progress);
4268 gtk_widget_show(tz->gd->dialog);
4269 tz->cancellable = g_cancellable_new();
4270 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);
4272 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4275 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */