2 * Copyright (C) 2006 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "preferences.h"
25 #include "bar-keywords.h"
27 #include "color-man.h"
30 #include "filefilter.h"
31 #include "fullscreen.h"
33 #include "image-overlay.h"
35 #include "layout-util.h"
39 #include "pixbuf-util.h"
41 #include "slideshow.h"
45 #include "ui-fileops.h"
47 #include "ui-tabcomp.h"
49 #include "zonedetect.h"
60 #include <gspell/gspell.h>
63 #define EDITOR_NAME_MAX_LENGTH 32
64 #define EDITOR_COMMAND_MAX_LENGTH 1024
66 static void image_overlay_set_text_colors();
68 GtkWidget *keyword_text;
69 static void config_tab_keywords_save();
77 static ThumbSize thumb_size_list[] =
112 FILETYPES_COLUMN_ENABLED = 0,
113 FILETYPES_COLUMN_FILTER,
114 FILETYPES_COLUMN_DESCRIPTION,
115 FILETYPES_COLUMN_CLASS,
116 FILETYPES_COLUMN_WRITABLE,
117 FILETYPES_COLUMN_SIDECAR,
118 FILETYPES_COLUMN_COUNT
121 const gchar *format_class_list[] = {
132 /* config memory values */
133 static ConfOptions *c_options = nullptr;
140 static GtkWidget *configwindow = nullptr;
141 static GtkListStore *filter_store = nullptr;
142 static GtkTreeStore *accel_store = nullptr;
144 static GtkWidget *safe_delete_path_entry;
146 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
147 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
148 static GtkWidget *color_profile_screen_file_entry;
149 static GtkWidget *external_preview_select_entry;
150 static GtkWidget *external_preview_extract_entry;
152 static GtkWidget *sidecar_ext_entry;
153 static GtkWidget *help_search_engine_entry;
155 static GtkWidget *log_window_f1_entry;
158 #define CONFIG_WINDOW_DEF_WIDTH 700
159 #define CONFIG_WINDOW_DEF_HEIGHT 600
162 *-----------------------------------------------------------------------------
163 * option widget callbacks (private)
164 *-----------------------------------------------------------------------------
167 static void zoom_increment_cb(GtkWidget *spin, gpointer)
169 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
172 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
174 gint mins_secs_tenths, delay;
176 mins_secs_tenths = c_options->slideshow.delay %
177 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
179 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
180 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
183 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
184 SLIDESHOW_SUBSECOND_PRECISION;
187 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
189 gint hours, secs_tenths, delay;
191 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
192 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
194 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
195 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
196 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
198 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
199 SLIDESHOW_SUBSECOND_PRECISION;
202 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
204 gint hours_mins, delay;
206 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
208 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
209 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
210 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
212 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
213 SLIDESHOW_SUBSECOND_PRECISION;
217 *-----------------------------------------------------------------------------
218 * sync program to config window routine (private)
219 *-----------------------------------------------------------------------------
222 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
228 buf = gq_gtk_entry_get_text(GTK_ENTRY(entry));
229 if (buf && strlen(buf) > 0)
234 *option = g_strdup(buf);
239 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
241 gchar *accel_path, *accel;
243 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
245 if (accel_path && accel_path[0])
248 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
249 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
259 static void config_window_apply()
261 gboolean refresh = FALSE;
266 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
268 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
269 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
270 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
271 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
272 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
273 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
275 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
276 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
277 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
278 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
279 options->file_ops.no_trash = c_options->file_ops.no_trash;
280 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
281 options->tools_restore_state = c_options->tools_restore_state;
282 options->save_window_positions = c_options->save_window_positions;
283 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
284 options->save_window_workspace = c_options->save_window_workspace;
285 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
286 options->show_window_ids = c_options->show_window_ids;
287 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
288 options->image.zoom_2pass = c_options->image.zoom_2pass;
289 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
290 options->image.limit_window_size = c_options->image.limit_window_size;
291 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
292 options->image.max_window_size = c_options->image.max_window_size;
293 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
294 options->image.max_autofit_size = c_options->image.max_autofit_size;
295 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
296 options->image.tile_size = c_options->image.tile_size;
297 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
298 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
300 if (options->thumbnails.max_width != c_options->thumbnails.max_width
301 || options->thumbnails.max_height != c_options->thumbnails.max_height
302 || options->thumbnails.quality != c_options->thumbnails.quality)
304 thumb_format_changed = TRUE;
306 options->thumbnails.max_width = c_options->thumbnails.max_width;
307 options->thumbnails.max_height = c_options->thumbnails.max_height;
308 options->thumbnails.quality = c_options->thumbnails.quality;
310 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
311 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
312 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
313 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
314 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
315 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
316 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
317 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
318 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
319 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
320 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
321 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
323 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
324 options->file_filter.disable = c_options->file_filter.disable;
326 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
327 sidecar_ext_parse(options->sidecar.ext);
329 options->slideshow.random = c_options->slideshow.random;
330 options->slideshow.repeat = c_options->slideshow.repeat;
331 options->slideshow.delay = c_options->slideshow.delay;
333 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
334 options->image_lm_click_nav = c_options->image_lm_click_nav;
335 options->image_l_click_archive = c_options->image_l_click_archive;
336 options->image_l_click_video = c_options->image_l_click_video;
337 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
339 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
341 options->image.tile_cache_max = c_options->image.tile_cache_max;
342 options->image.image_cache_max = c_options->image.image_cache_max;
344 options->image.zoom_quality = c_options->image.zoom_quality;
346 options->image.zoom_increment = c_options->image.zoom_increment;
348 options->image.zoom_style = c_options->image.zoom_style;
350 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
352 options->appimage_notifications = c_options->appimage_notifications;
355 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
356 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
357 || !gdk_rgba_equal(&options->image.border_color, &c_options->image.border_color))
359 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
360 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
361 options->image.border_color = c_options->image.border_color;
362 layout_colors_update();
363 view_window_colors_update();
366 options->image.alpha_color_1 = c_options->image.alpha_color_1;
367 options->image.alpha_color_2 = c_options->image.alpha_color_2;
369 options->fullscreen.screen = c_options->fullscreen.screen;
370 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
371 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
372 options->fullscreen.above = c_options->fullscreen.above;
373 if (c_options->image_overlay.template_string)
374 set_image_overlay_template_string(&options->image_overlay.template_string,
375 c_options->image_overlay.template_string);
376 if (c_options->image_overlay.font)
377 set_image_overlay_font_string(&options->image_overlay.font,
378 c_options->image_overlay.font);
379 options->image_overlay.text_red = c_options->image_overlay.text_red;
380 options->image_overlay.text_green = c_options->image_overlay.text_green;
381 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
382 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
383 options->image_overlay.background_red = c_options->image_overlay.background_red;
384 options->image_overlay.background_green = c_options->image_overlay.background_green;
385 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
386 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
387 options->image_overlay.font = c_options->image_overlay.font;
388 options->update_on_time_change = c_options->update_on_time_change;
390 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
391 options->rot_invariant_sim = c_options->rot_invariant_sim;
393 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
395 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
396 options->circular_selection_lists = c_options->circular_selection_lists;
398 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
399 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
400 options->dnd_icon_size = c_options->dnd_icon_size;
401 options->clipboard_selection = c_options->clipboard_selection;
402 options->dnd_default_action = c_options->dnd_default_action;
404 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
405 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
406 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
407 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
408 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
409 options->metadata.confirm_write = c_options->metadata.confirm_write;
410 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
411 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
412 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
413 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
414 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
415 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
416 options->metadata.write_orientation = c_options->metadata.write_orientation;
417 options->metadata.check_spelling = c_options->metadata.check_spelling;
418 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
419 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
420 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
421 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
422 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
423 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
424 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
425 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
426 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
427 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
428 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
429 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
430 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
431 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
432 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
433 options->stereo.fixed_w = c_options->stereo.fixed_w;
434 options->stereo.fixed_h = c_options->stereo.fixed_h;
435 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
436 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
437 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
438 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
440 options->info_keywords.height = c_options->info_keywords.height;
441 options->info_title.height = c_options->info_title.height;
442 options->info_comment.height = c_options->info_comment.height;
443 options->info_rating.height = c_options->info_rating.height;
445 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
446 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
448 options->selectable_bars.menu_bar = c_options->selectable_bars.menu_bar;
449 options->selectable_bars.tool_bar = c_options->selectable_bars.tool_bar;
450 options->selectable_bars.status_bar = c_options->selectable_bars.status_bar;
452 options->marks_save = c_options->marks_save;
453 options->with_rename = c_options->with_rename;
454 options->collections_duplicates = c_options->collections_duplicates;
455 options->collections_on_top = c_options->collections_on_top;
456 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
457 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
459 options->external_preview.enable = c_options->external_preview.enable;
460 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
461 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
463 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
465 options->star_rating.star = c_options->star_rating.star;
466 options->star_rating.rejected = c_options->star_rating.rejected;
468 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
470 options->alternate_similarity_algorithm.enabled = c_options->alternate_similarity_algorithm.enabled;
471 options->alternate_similarity_algorithm.grayscale = c_options->alternate_similarity_algorithm.grayscale;
474 set_debug_level(debug_c);
475 config_entry_to_option(log_window_f1_entry, &options->log_window.action, nullptr);
479 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
481 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
482 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
484 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
485 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
486 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
488 options->color_profile.render_intent = c_options->color_profile.render_intent;
493 options->mouse_button_8 = c_options->mouse_button_8;
494 options->mouse_button_9 = c_options->mouse_button_9;
496 options->override_disable_gpu = c_options->override_disable_gpu;
498 config_tab_keywords_save();
500 image_options_sync();
505 layout_refresh(nullptr);
508 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
510 toolbar_apply(TOOLBAR_MAIN);
511 toolbar_apply(TOOLBAR_STATUS);
515 *-----------------------------------------------------------------------------
516 * config window main button callbacks (private)
517 *-----------------------------------------------------------------------------
520 static void config_window_close_cb(GtkWidget *, gpointer)
522 gq_gtk_widget_destroy(configwindow);
523 configwindow = nullptr;
524 filter_store = nullptr;
527 static void config_window_help_cb(GtkWidget *, gpointer data)
529 auto notebook = static_cast<GtkWidget *>(data);
532 static const gchar *html_section[] =
534 "GuideOptionsGeneral.html",
535 "GuideOptionsImage.html",
536 "GuideOptionsOSD.html",
537 "GuideOptionsWindow.html",
538 "GuideOptionsKeyboard.html",
539 "GuideOptionsFiltering.html",
540 "GuideOptionsMetadata.html",
541 "GuideOptionsKeywords.html",
542 "GuideOptionsColor.html",
543 "GuideOptionsStereo.html",
544 "GuideOptionsBehavior.html",
545 "GuideOptionsToolbar.html",
546 "GuideOptionsToolbar.html",
547 "GuideOptionsAdvanced.html"
550 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
551 help_window_show(html_section[i]);
554 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
556 config_window_close_cb(nullptr, nullptr);
560 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
563 auto notebook = static_cast<GtkNotebook *>(data);
571 lw = static_cast<LayoutWindow *>(layout_window_list->data);
573 window = gtk_widget_get_window(widget);
574 gdk_window_get_root_origin(window, &x, &y);
575 w = gdk_window_get_width(window);
576 h = gdk_window_get_height(window);
577 page_number = gtk_notebook_get_current_page(notebook);
579 lw->options.preferences_window.x = x;
580 lw->options.preferences_window.y = y;
581 lw->options.preferences_window.w = w;
582 lw->options.preferences_window.h = h;
583 lw->options.preferences_window.page_number = page_number;
585 config_window_apply();
586 layout_util_sync(lw);
587 save_options(options);
588 config_window_close_cb(nullptr, nullptr);
592 *-----------------------------------------------------------------------------
593 * config window setup (private)
594 *-----------------------------------------------------------------------------
597 static void quality_menu_cb(GtkWidget *combo, gpointer data)
599 auto option = static_cast<gint *>(data);
601 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
605 *option = GDK_INTERP_NEAREST;
608 *option = GDK_INTERP_TILES;
611 *option = GDK_INTERP_BILINEAR;
616 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
618 auto option = static_cast<gint *>(data);
620 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
624 *option = DND_ACTION_ASK;
627 *option = DND_ACTION_COPY;
630 *option = DND_ACTION_MOVE;
634 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
636 auto option = static_cast<gint *>(data);
638 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
641 *option = CLIPBOARD_PRIMARY;
644 *option = CLIPBOARD_CLIPBOARD;
647 *option = CLIPBOARD_BOTH;
650 *option = CLIPBOARD_BOTH;
654 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
655 guint option, guint *option_c)
662 pref_table_label(table, column, row, text, GTK_ALIGN_START);
664 combo = gtk_combo_box_text_new();
666 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
667 if (option == GDK_INTERP_NEAREST) current = 0;
668 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
669 if (option == GDK_INTERP_TILES) current = 1;
670 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
671 if (option == GDK_INTERP_BILINEAR) current = 2;
673 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
675 g_signal_connect(G_OBJECT(combo), "changed",
676 G_CALLBACK(quality_menu_cb), option_c);
678 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
679 gtk_widget_show(combo);
682 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
689 pref_table_label(table, column, row, text, GTK_ALIGN_START);
691 combo = gtk_combo_box_text_new();
693 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
694 if (option == DND_ACTION_ASK) current = 0;
695 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
696 if (option == DND_ACTION_COPY) current = 1;
697 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
698 if (option == DND_ACTION_MOVE) current = 2;
700 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
702 g_signal_connect(G_OBJECT(combo), "changed",
703 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
705 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
706 gtk_widget_show(combo);
709 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
710 gint option, gint *option_c)
717 pref_table_label(table, column, row, text, GTK_ALIGN_START);
719 combo = gtk_combo_box_text_new();
721 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
722 if (option == CLIPBOARD_PRIMARY) current = 0;
723 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
724 if (option == CLIPBOARD_CLIPBOARD) current = 1;
725 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
726 if (option == CLIPBOARD_BOTH) current = 2;
728 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
730 g_signal_connect(G_OBJECT(combo), "changed",
731 G_CALLBACK(clipboard_selection_menu_cb), option_c);
733 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
734 gtk_widget_show(combo);
737 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
739 auto option = static_cast<gint *>(data);
741 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
744 *option = ZOOM_GEOMETRIC;
747 *option = ZOOM_ARITHMETIC;
750 *option = ZOOM_GEOMETRIC;
754 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
761 pref_table_label(table, column, row, text, GTK_ALIGN_START);
763 combo = gtk_combo_box_text_new();
765 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
766 if (option == ZOOM_GEOMETRIC) current = 0;
767 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
768 if (option == ZOOM_ARITHMETIC) current = 1;
770 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
772 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
774 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
775 gtk_widget_show(combo);
778 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
780 ActionItem *action_item = nullptr;
781 auto option = static_cast<gchar **>(data);
786 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
788 list = get_action_items();
793 action_item = static_cast<ActionItem *>(work->data);
794 if (g_strcmp0(action_item->label, label) == 0)
802 *option = g_strdup(action_item->name);
804 action_items_free(list);
807 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
809 ActionItem *action_item;
818 pref_table_label(table, column, row, text, GTK_ALIGN_START);
820 combo = gtk_combo_box_text_new();
822 list = get_action_items();
826 action_item = static_cast<ActionItem *>(work->data);
827 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
829 if (g_strcmp0(action_item->name, option) == 0)
837 action_items_free(list);
839 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
841 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
843 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
844 gtk_widget_show(combo);
847 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
851 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
854 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
856 c_options->thumbnails.max_width = thumb_size_list[n].w;
857 c_options->thumbnails.max_height = thumb_size_list[n].h;
861 c_options->thumbnails.max_width = options->thumbnails.max_width;
862 c_options->thumbnails.max_height = options->thumbnails.max_height;
866 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
872 c_options->thumbnails.max_width = options->thumbnails.max_width;
873 c_options->thumbnails.max_height = options->thumbnails.max_height;
875 pref_table_label(table, column, row, text, GTK_ALIGN_START);
877 combo = gtk_combo_box_text_new();
880 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
885 w = thumb_size_list[i].w;
886 h = thumb_size_list[i].h;
888 buf = g_strdup_printf("%d x %d", w, h);
889 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
892 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
899 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
900 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
906 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
907 g_signal_connect(G_OBJECT(combo), "changed",
908 G_CALLBACK(thumb_size_menu_cb), NULL);
910 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
911 gtk_widget_show(combo);
914 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
916 auto option = static_cast<gint *>(data);
918 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
922 *option = PR_STEREO_NONE;
925 *option = PR_STEREO_ANAGLYPH_RC;
928 *option = PR_STEREO_ANAGLYPH_GM;
931 *option = PR_STEREO_ANAGLYPH_YB;
934 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
937 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
940 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
943 *option = PR_STEREO_ANAGLYPH_DB_RC;
946 *option = PR_STEREO_ANAGLYPH_DB_GM;
949 *option = PR_STEREO_ANAGLYPH_DB_YB;
952 *option = PR_STEREO_HORIZ;
955 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
958 *option = PR_STEREO_VERT;
961 *option = PR_STEREO_VERT | PR_STEREO_HALF;
964 *option = PR_STEREO_FIXED;
969 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
970 gint option, gint *option_c, gboolean add_fixed)
977 pref_table_label(table, column, row, text, GTK_ALIGN_START);
979 combo = gtk_combo_box_text_new();
981 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
983 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
984 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
985 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
986 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
987 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
988 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
989 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
990 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
991 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
992 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
993 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
994 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
995 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
996 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
997 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
998 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
999 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1000 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1002 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1003 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1004 if (option & PR_STEREO_HORIZ)
1007 if (option & PR_STEREO_HALF) current = 11;
1010 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1011 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1012 if (option & PR_STEREO_VERT)
1015 if (option & PR_STEREO_HALF) current = 13;
1020 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1021 if (option & PR_STEREO_FIXED) current = 14;
1024 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1026 g_signal_connect(G_OBJECT(combo), "changed",
1027 G_CALLBACK(stereo_mode_menu_cb), option_c);
1029 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1030 gtk_widget_show(combo);
1033 static void video_menu_cb(GtkWidget *combo, gpointer data)
1035 auto option = static_cast<gchar **>(data);
1037 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1041 static void video_menu_populate(gpointer data, gpointer user_data)
1043 auto combo = static_cast<GtkWidget *>(user_data);
1044 auto ed = static_cast<EditorDescription *>(data);
1046 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1049 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1050 gchar *option, gchar **option_c)
1054 /* use lists since they are sorted */
1055 GList *eds = editor_list_get();
1059 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1061 combo = gtk_combo_box_text_new();
1062 g_list_foreach(eds,video_menu_populate,combo);
1063 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1065 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1067 g_signal_connect(G_OBJECT(combo), "changed",
1068 G_CALLBACK(video_menu_cb), option_c);
1070 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1071 gtk_widget_show(combo);
1074 static void filter_store_populate()
1078 if (!filter_store) return;
1080 gtk_list_store_clear(filter_store);
1082 work = filter_get_list();
1088 fe = static_cast<FilterEntry *>(work->data);
1091 gtk_list_store_append(filter_store, &iter);
1092 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1096 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1098 auto model = static_cast<GtkWidget *>(data);
1099 auto fe = static_cast<FilterEntry *>(data);
1103 if (!new_text || strlen(new_text) < 1) return;
1105 tpath = gtk_tree_path_new_from_string(path_str);
1106 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1107 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1109 g_free(fe->extensions);
1110 fe->extensions = g_strdup(new_text);
1112 gtk_tree_path_free(tpath);
1116 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1118 auto model = static_cast<GtkWidget *>(data);
1119 auto fe = static_cast<FilterEntry *>(data);
1124 if (!new_text || !new_text[0]) return;
1126 tpath = gtk_tree_path_new_from_string(path_str);
1127 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1128 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1130 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1132 if (strcmp(new_text, _(format_class_list[i])) == 0)
1134 fe->file_class = static_cast<FileFormatClass>(i);
1139 gtk_tree_path_free(tpath);
1143 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1145 auto model = static_cast<GtkWidget *>(data);
1150 if (!new_text || !new_text[0]) return;
1152 tpath = gtk_tree_path_new_from_string(path_str);
1153 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1154 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1156 g_free(fe->description);
1157 fe->description = g_strdup(new_text);
1159 gtk_tree_path_free(tpath);
1162 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1164 auto model = static_cast<GtkWidget *>(data);
1169 tpath = gtk_tree_path_new_from_string(path_str);
1170 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1171 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1173 fe->enabled = !fe->enabled;
1175 gtk_tree_path_free(tpath);
1179 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1181 auto model = static_cast<GtkWidget *>(data);
1186 tpath = gtk_tree_path_new_from_string(path_str);
1187 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1188 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1190 fe->writable = !fe->writable;
1191 if (fe->writable) fe->allow_sidecar = FALSE;
1193 gtk_tree_path_free(tpath);
1197 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1199 auto model = static_cast<GtkWidget *>(data);
1204 tpath = gtk_tree_path_new_from_string(path_str);
1205 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1206 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1208 fe->allow_sidecar = !fe->allow_sidecar;
1209 if (fe->allow_sidecar) fe->writable = FALSE;
1211 gtk_tree_path_free(tpath);
1215 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1216 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1220 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1222 switch (GPOINTER_TO_INT(data))
1225 g_object_set(GTK_CELL_RENDERER(cell),
1226 "active", fe->enabled, NULL);
1229 g_object_set(GTK_CELL_RENDERER(cell),
1230 "text", fe->extensions, NULL);
1232 case FE_DESCRIPTION:
1233 g_object_set(GTK_CELL_RENDERER(cell),
1234 "text", fe->description, NULL);
1237 g_object_set(GTK_CELL_RENDERER(cell),
1238 "text", _(format_class_list[fe->file_class]), NULL);
1241 g_object_set(GTK_CELL_RENDERER(cell),
1242 "active", fe->writable, NULL);
1244 case FE_ALLOW_SIDECAR:
1245 g_object_set(GTK_CELL_RENDERER(cell),
1246 "active", fe->allow_sidecar, NULL);
1251 static gboolean filter_add_scroll(gpointer data)
1255 GtkCellRenderer *cell;
1256 GtkTreeViewColumn *column;
1259 GtkTreeModel *store;
1261 FilterEntry *filter;
1263 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1264 path = gtk_tree_path_new_from_indices(rows-1, -1);
1266 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1268 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1269 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1271 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1272 valid = gtk_tree_model_get_iter_first(store, &iter);
1276 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1278 if (g_strcmp0(filter->extensions, ".new") == 0)
1280 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1284 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1287 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1288 path, column, FALSE, 0.0, 0.0 );
1289 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1290 path, column, cell, TRUE);
1292 gtk_tree_path_free(path);
1293 g_list_free(list_cells);
1295 return(G_SOURCE_REMOVE);
1298 static void filter_add_cb(GtkWidget *, gpointer data)
1300 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1301 filter_store_populate();
1303 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1306 static void filter_remove_cb(GtkWidget *, gpointer data)
1308 auto filter_view = static_cast<GtkWidget *>(data);
1309 GtkTreeSelection *selection;
1313 if (!filter_store) return;
1314 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1315 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1316 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1319 filter_remove_entry(fe);
1321 filter_store_populate();
1324 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1328 GtkTreeViewColumn *column;
1330 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1331 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1332 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1334 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1338 gtk_tree_path_free(path);
1340 return(G_SOURCE_REMOVE);
1343 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1346 filter_add_defaults();
1348 filter_store_populate();
1350 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1353 static void dummy_cancel_cb(GenericDialog *, gpointer)
1355 /* no op, only so cancel button appears */
1358 static void filter_default_cb(GtkWidget *widget, gpointer data)
1362 gd = generic_dialog_new(_("Reset filters"),
1363 "reset_filter", widget, TRUE,
1364 dummy_cancel_cb, data);
1365 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1366 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1367 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1368 gtk_widget_show(gd->dialog);
1371 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1373 auto frame = static_cast<GtkWidget *>(data);
1375 gtk_widget_set_sensitive(frame,
1376 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1379 static void safe_delete_view_cb(GtkWidget *, gpointer)
1381 layout_set_path(nullptr, gq_gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1384 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1386 file_util_trash_clear();
1389 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1393 gd = generic_dialog_new(_("Clear trash"),
1394 "clear_trash", widget, TRUE,
1395 dummy_cancel_cb, nullptr);
1396 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1397 _("This will remove the trash contents."), FALSE);
1398 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1399 entry = gtk_entry_new();
1400 gtk_widget_set_can_focus(entry, FALSE);
1401 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1402 if (options->file_ops.safe_delete_path) gq_gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1403 gq_gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1404 gtk_widget_show(entry);
1405 gtk_widget_show(gd->dialog);
1408 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1410 GtkWidget *pTextView;
1411 GtkTextBuffer *pTextBuffer;
1415 pTextView = GTK_WIDGET(data);
1417 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1418 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1419 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1421 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1422 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1425 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1427 auto text_view = static_cast<GtkTextView *>(data);
1428 GtkTextBuffer *buffer;
1430 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1431 if (!configwindow) return;
1433 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1434 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1437 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1441 gd = generic_dialog_new(_("Reset image overlay template string"),
1442 "reset_image_overlay_template_string", widget, TRUE,
1443 dummy_cancel_cb, data);
1444 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1445 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1446 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1447 gtk_widget_show(gd->dialog);
1450 static void image_overlay_help_cb(GtkWidget *, gpointer)
1452 help_window_show("GuideOptionsOSD.html");
1455 static void font_activated_cb(GtkFontChooser *widget, gchar *fontname, gpointer)
1457 g_free(c_options->image_overlay.font);
1458 c_options->image_overlay.font = g_strdup(fontname);
1461 gq_gtk_widget_destroy(GTK_WIDGET(widget));
1464 static void font_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1468 g_free(c_options->image_overlay.font);
1469 c_options->image_overlay.font = g_strdup(options->image_overlay.font);
1471 if (response_id == GTK_RESPONSE_OK)
1473 font = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
1474 g_free(c_options->image_overlay.font);
1475 c_options->image_overlay.font = g_strdup(font);
1479 g_object_unref(dialog);
1482 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1486 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1487 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1488 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1490 g_signal_connect(dialog, "font-activated", G_CALLBACK(font_activated_cb), nullptr);
1491 g_signal_connect(dialog, "response", G_CALLBACK(font_response_cb), nullptr);
1493 gtk_widget_show(dialog);
1496 static void text_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1498 c_options->image_overlay.text_red = color->red * 255;
1499 c_options->image_overlay.text_green = color->green * 255;
1500 c_options->image_overlay.text_blue = color->blue * 255;
1501 c_options->image_overlay.text_alpha = color->alpha * 255;
1503 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1506 static void text_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1510 c_options->image_overlay.text_red = options->image_overlay.text_red;
1511 c_options->image_overlay.text_green = options->image_overlay.text_green;
1512 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
1513 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
1515 if (response_id == GTK_RESPONSE_OK)
1517 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1518 c_options->image_overlay.text_red = color.red * 255;
1519 c_options->image_overlay.text_green = color.green * 255;
1520 c_options->image_overlay.text_blue = color.blue * 255;
1521 c_options->image_overlay.text_alpha = color.alpha * 255;
1524 g_object_unref(dialog);
1527 static void image_overlay_set_text_color_cb(GtkWidget *widget, gpointer)
1532 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Text Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1533 color.red = options->image_overlay.text_red;
1534 color.green = options->image_overlay.text_green;
1535 color.blue = options->image_overlay.text_blue;
1536 color.alpha = options->image_overlay.text_alpha;
1537 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1538 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1540 g_signal_connect(dialog, "color-activated", G_CALLBACK(text_color_activated_cb), nullptr);
1541 g_signal_connect(dialog, "response", G_CALLBACK(text_color_response_cb), nullptr);
1543 gtk_widget_show(dialog);
1546 static void bg_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1548 c_options->image_overlay.background_red = color->red * 255;
1549 c_options->image_overlay.background_green = color->green * 255;
1550 c_options->image_overlay.background_blue = color->blue * 255;
1551 c_options->image_overlay.background_alpha = color->alpha * 255;
1553 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1556 static void bg_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1560 c_options->image_overlay.background_red = options->image_overlay.background_red;
1561 c_options->image_overlay.background_green = options->image_overlay.background_green;
1562 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
1563 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
1565 if (response_id == GTK_RESPONSE_OK)
1567 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1568 c_options->image_overlay.background_red = color.red * 255;
1569 c_options->image_overlay.background_green = color.green * 255;
1570 c_options->image_overlay.background_blue = color.blue * 255;
1571 c_options->image_overlay.background_alpha = color.alpha * 255;
1573 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1576 static void image_overlay_set_background_color_cb(GtkWidget *widget, gpointer)
1581 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Background Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1582 color.red = options->image_overlay.background_red;
1583 color.green = options->image_overlay.background_green;
1584 color.blue = options->image_overlay.background_blue;
1585 color.alpha = options->image_overlay.background_alpha;
1586 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1587 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1589 g_signal_connect(dialog, "color-activated", G_CALLBACK(bg_color_activated_cb), nullptr);
1590 g_signal_connect(dialog, "response", G_CALLBACK(bg_color_response_cb), nullptr);
1592 gtk_widget_show(dialog);
1595 static void accel_store_populate()
1598 GList *groups, *actions;
1600 const gchar *accel_path;
1604 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1606 gtk_tree_store_clear(accel_store);
1607 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 */
1609 g_assert(lw && lw->ui_manager);
1610 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1613 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1616 action = GTK_ACTION(actions->data);
1617 accel_path = gtk_action_get_accel_path(action);
1618 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1620 gchar *label, *label2, *tooltip, *accel;
1621 g_object_get(action,
1622 "tooltip", &tooltip,
1626 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1632 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1636 gtk_tree_store_append(accel_store, &iter, nullptr);
1637 gtk_tree_store_set(accel_store, &iter,
1640 AE_TOOLTIP, tooltip ? tooltip : "",
1641 AE_ACCEL, accel_path,
1649 actions = actions->next;
1652 groups = groups->next;
1656 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1661 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1663 auto accel1 = static_cast<gchar *>(data);
1668 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1670 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1671 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1673 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1675 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1676 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1685 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1687 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1691 GtkAccelKey old_key, key;
1692 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1694 gtk_tree_model_get_iter(model, &iter, path);
1695 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1697 /* test if the accelerator can be stored without conflicts*/
1698 gtk_accel_map_lookup_entry(accel_path, &old_key);
1700 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1701 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1702 gtk_accel_map_lookup_entry(accel_path, &key);
1704 /* restore the original for now, the key will be really changed when the changes are confirmed */
1705 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1707 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1708 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1710 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1711 gtk_tree_path_free(path);
1715 static gboolean accel_default_scroll(GtkTreeView *data)
1719 GtkTreeViewColumn *column;
1721 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1722 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1723 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1725 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1729 gtk_tree_path_free(path);
1731 return(G_SOURCE_REMOVE);
1734 static void accel_default_cb(GtkWidget *, gpointer data)
1736 accel_store_populate();
1738 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1741 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1743 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1746 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1749 gchar *accel_path, *accel;
1751 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1752 gtk_accel_map_lookup_entry(accel_path, &key);
1753 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1755 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1757 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1762 static void accel_clear_cb(GtkWidget *, gpointer data)
1764 GtkTreeSelection *selection;
1766 if (!accel_store) return;
1767 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1768 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1771 static void accel_reset_cb(GtkWidget *, gpointer data)
1773 GtkTreeSelection *selection;
1775 if (!accel_store) return;
1776 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1777 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1782 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1786 GtkWidget *scrolled;
1787 GtkWidget *viewport;
1789 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
1790 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1791 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1792 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1793 label = gtk_label_new(title);
1794 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1795 gtk_widget_show(scrolled);
1797 viewport = gtk_viewport_new(nullptr, nullptr);
1798 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1799 gq_gtk_container_add(GTK_WIDGET(scrolled), viewport);
1800 gtk_widget_show(viewport);
1802 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1803 gq_gtk_container_add(GTK_WIDGET(viewport), vbox);
1804 gtk_widget_show(vbox);
1809 static void cache_standard_cb(GtkWidget *widget, gpointer)
1811 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1813 c_options->thumbnails.spec_standard =TRUE;
1814 c_options->thumbnails.cache_into_dirs = FALSE;
1818 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1820 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1822 c_options->thumbnails.spec_standard =FALSE;
1823 c_options->thumbnails.cache_into_dirs = FALSE;
1827 static void cache_local_cb(GtkWidget *widget, gpointer)
1829 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1831 c_options->thumbnails.cache_into_dirs = TRUE;
1832 c_options->thumbnails.spec_standard =FALSE;
1836 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1838 if (pos == GTK_ENTRY_ICON_PRIMARY)
1840 gq_gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1844 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "");
1848 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1850 gchar *rating_symbol;
1852 if (pos == GTK_ENTRY_ICON_PRIMARY)
1854 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1855 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1856 g_free(rating_symbol);
1860 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1861 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1862 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1866 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1868 gchar *rating_symbol;
1870 if (pos == GTK_ENTRY_ICON_PRIMARY)
1872 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1873 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1874 g_free(rating_symbol);
1878 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1879 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1880 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1884 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1886 auto hbox = static_cast<GtkContainer *>(data);
1887 GString *str = g_string_new(nullptr);
1888 GtkEntry *hex_code_entry;
1889 gchar *hex_code_full;
1892 guint64 hex_value = 0;
1894 list = gtk_container_get_children(hbox);
1896 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1897 hex_code_full = g_strdup(gq_gtk_entry_get_text(hex_code_entry));
1899 hex_code = g_strsplit(hex_code_full, "+", 2);
1900 if (hex_code[0] && hex_code[1])
1902 hex_value = strtoull(hex_code[1], nullptr, 16);
1904 if (!hex_value || hex_value > 0x10FFFF)
1906 hex_value = 0x003F; // Unicode 'Question Mark'
1908 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1909 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1911 g_strfreev(hex_code);
1912 g_string_free(str, TRUE);
1913 g_free(hex_code_full);
1918 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1920 guint64 star_symbol;
1922 star_symbol = star_rating_symbol_test(widget, data);
1923 c_options->star_rating.star = star_symbol;
1926 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1928 guint64 rejected_symbol;
1930 rejected_symbol = star_rating_symbol_test(widget, data);
1931 c_options->star_rating.rejected = rejected_symbol;
1934 /* general options tab */
1935 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1939 GCancellable *cancellable;
1941 GtkWidget *progress;
1943 GFile *timezone_database_gq;
1944 gchar *timezone_database_user;
1947 static void config_tab_general(GtkWidget *notebook)
1952 GtkWidget *group_frame;
1953 GtkWidget *subgroup;
1955 GtkWidget *ct_button;
1958 gint hours, minutes, remainder;
1960 GtkWidget *star_rating_entry;
1962 gchar *rating_symbol;
1965 gchar *download_locn;
1966 GNetworkMonitor *net_mon;
1967 GSocketConnectable *tz_org;
1968 gboolean internet_available = FALSE;
1971 vbox = scrolled_notebook_page(notebook, _("General"));
1973 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1975 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1976 add_thumb_size_menu(table, 0, 0, _("Size:"));
1977 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1979 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1980 pref_label_new(hbox, _("Custom size: "));
1981 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1982 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1984 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1985 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1987 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1988 pref_checkbox_link_sensitivity(ct_button, subgroup);
1990 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1991 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1992 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1993 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1994 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
1995 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1996 G_CALLBACK(cache_geeqie_cb), nullptr);
1998 group_frame = pref_frame_new(subgroup, TRUE,
1999 _("Store thumbnails local to image folder (non-standard)"),
2000 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2001 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
2002 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
2003 G_CALLBACK(cache_local_cb), nullptr);
2005 group_frame = pref_frame_new(subgroup, TRUE,
2006 _("Use standard thumbnail style and cache, shared with other applications"),
2007 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2008 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2009 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2010 G_CALLBACK(cache_standard_cb), nullptr);
2012 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2013 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2015 pref_checkbox_new_int(group, _("Thumbnail color management"),
2016 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2018 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2020 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2021 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2023 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
2024 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2025 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2028 pref_spacer(group, PREF_PAD_GROUP);
2030 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2032 c_options->star_rating.star = options->star_rating.star;
2033 c_options->star_rating.rejected = options->star_rating.rejected;
2035 str = g_string_new(nullptr);
2036 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2037 pref_label_new(hbox, _("Star character: "));
2038 str = g_string_append_unichar(str, options->star_rating.star);
2039 pref_label_new(hbox, g_strdup(str->str));
2040 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2041 star_rating_entry = gtk_entry_new();
2042 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2043 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2044 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2045 gtk_widget_show(star_rating_entry);
2046 button = pref_button_new(nullptr, nullptr, _("Set"),
2047 G_CALLBACK(star_rating_star_test_cb), hbox);
2048 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2049 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2050 gtk_widget_show(button);
2051 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."));
2052 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2053 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2054 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2055 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2056 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2057 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2058 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2059 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2060 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2061 G_CALLBACK(star_rating_star_icon_cb),
2064 g_string_free(str, TRUE);
2065 g_free(rating_symbol);
2067 str = g_string_new(nullptr);
2068 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2069 pref_label_new(hbox, _("Rejected character: "));
2070 str = g_string_append_unichar(str, options->star_rating.rejected);
2071 pref_label_new(hbox, g_strdup(str->str));
2072 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2073 star_rating_entry = gtk_entry_new();
2074 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2075 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2076 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2077 gtk_widget_show(star_rating_entry);
2078 button = pref_button_new(nullptr, nullptr, _("Set"),
2079 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2080 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2081 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2082 gtk_widget_show(button);
2083 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."));
2084 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2085 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2086 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2087 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2088 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2089 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2090 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2091 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2092 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2093 G_CALLBACK(star_rating_rejected_icon_cb),
2096 g_string_free(str, TRUE);
2097 g_free(rating_symbol);
2099 pref_spacer(group, PREF_PAD_GROUP);
2101 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2103 c_options->slideshow.delay = options->slideshow.delay;
2104 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2105 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2106 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2107 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2108 SLIDESHOW_SUBSECOND_PRECISION;
2110 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2112 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2114 options->slideshow.delay ? hours : 0.0,
2115 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2116 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2117 spin = pref_spin_new(hbox, ":" , nullptr,
2119 options->slideshow.delay ? minutes: 0.0,
2120 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2121 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2122 spin = pref_spin_new(hbox, ":", nullptr,
2123 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2124 options->slideshow.delay ? seconds : 10.0,
2125 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2126 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2128 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2129 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2131 pref_spacer(group, PREF_PAD_GROUP);
2133 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2135 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2136 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2137 pref_checkbox_new_int(group, _("Preload next image"),
2138 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2140 pref_checkbox_new_int(group, _("Refresh on file change"),
2141 options->update_on_time_change, &c_options->update_on_time_change);
2144 pref_spacer(group, PREF_PAD_GROUP);
2146 group = pref_group_new(vbox, FALSE, _("Expand menu and toolbar"), GTK_ORIENTATION_VERTICAL);
2148 pref_checkbox_new_int(group, _("Expand menu and toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2149 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2150 gtk_widget_set_tooltip_text(group, _("Expand the menu and toolbar to the full width of the window"));
2152 pref_spacer(group, PREF_PAD_GROUP);
2154 group = pref_group_new(vbox, FALSE, _("Hide Selectable Bars"), GTK_ORIENTATION_VERTICAL);
2156 pref_checkbox_new_int(group, _("Menu bar"),
2157 options->selectable_bars.menu_bar, &c_options->selectable_bars.menu_bar);
2159 pref_checkbox_new_int(group, _("Tool bar"),
2160 options->selectable_bars.tool_bar, &c_options->selectable_bars.tool_bar);
2162 pref_checkbox_new_int(group, _("Status bar"),
2163 options->selectable_bars.status_bar, &c_options->selectable_bars.status_bar);
2164 gtk_widget_set_tooltip_text(group, _("The Hide Selectable Bars menu item (default keystroke is control-backtick) will toggle the display of the bars selected here"));
2166 pref_spacer(group, PREF_PAD_GROUP);
2168 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2170 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2171 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2172 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2173 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"));
2175 pref_spacer(group, PREF_PAD_GROUP);
2179 net_mon = g_network_monitor_get_default();
2180 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2183 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2184 g_object_unref(tz_org);
2187 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2188 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2190 if (!internet_available)
2192 gtk_widget_set_sensitive(group, FALSE);
2195 tz = g_new0(TZData, 1);
2197 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2198 basename = g_path_get_basename(path);
2199 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2203 if (isfile(tz->timezone_database_user))
2205 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2209 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2212 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2213 pref_label_new(GTK_WIDGET(hbox), download_locn);
2214 g_free(download_locn);
2216 if (!internet_available)
2218 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"));
2222 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"));
2224 gtk_widget_show(button);
2226 pref_spacer(group, PREF_PAD_GROUP);
2228 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2230 help_search_engine_entry = gtk_entry_new();
2231 gq_gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2232 gq_gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2233 gtk_widget_show(help_search_engine_entry);
2235 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"));
2237 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2238 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2239 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2240 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2241 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2242 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2243 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2244 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2245 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2246 G_CALLBACK(help_search_engine_entry_icon_cb),
2247 help_search_engine_entry);
2251 static void config_tab_image(GtkWidget *notebook)
2256 GtkWidget *ct_button;
2257 GtkWidget *enlargement_button;
2261 vbox = scrolled_notebook_page(notebook, _("Image"));
2263 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2265 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2266 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2268 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2269 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2271 c_options->image.zoom_increment = options->image.zoom_increment;
2272 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2273 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2274 G_CALLBACK(zoom_increment_cb), nullptr);
2275 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2277 c_options->image.zoom_style = options->image.zoom_style;
2278 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2279 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2281 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2283 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2284 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2285 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2286 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2288 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2289 pref_checkbox_link_sensitivity(enlargement_button, spin);
2290 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."));
2292 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2293 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2294 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2295 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2297 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2298 pref_checkbox_link_sensitivity(ct_button, spin);
2299 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."));
2301 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2303 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2304 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2306 options->image.tile_size, &c_options->image.tile_size);
2307 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."));
2309 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2311 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2312 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2314 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2315 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2317 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2318 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2320 c_options->image.border_color = options->image.border_color;
2322 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2323 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2325 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2326 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2328 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2329 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2334 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2336 LayoutWindow *lw = nullptr;
2337 gchar *default_path;
2340 /* Get current lw */
2343 tmp_id = lw->options.id;
2344 lw->options.id = g_strdup("");
2346 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2347 save_default_layout_options_to_file(default_path, options, lw);
2348 g_free(lw->options.id);
2349 lw->options.id = tmp_id;
2350 g_free(default_path);
2353 static gboolean popover_cb(gpointer data)
2355 auto popover = static_cast<GtkPopover *>(data);
2357 gtk_popover_popdown(popover);
2362 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2364 gtk_popover_popup(popover);
2366 g_timeout_add(2000, popover_cb, popover);
2369 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2373 popover = gtk_popover_new(parent);
2374 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2375 gq_gtk_container_add(GTK_WIDGET(popover), child);
2376 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2377 gtk_widget_show (child);
2382 static void config_tab_windows(GtkWidget *notebook)
2387 GtkWidget *subgroup;
2389 GtkWidget *ct_button;
2392 vbox = scrolled_notebook_page(notebook, _("Windows"));
2394 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2396 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2397 options->save_window_positions, &c_options->save_window_positions);
2399 button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2400 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2401 pref_checkbox_link_sensitivity(ct_button, button);
2403 button = pref_checkbox_new_int(group, _("Remember window workspace"),
2404 options->save_window_workspace, &c_options->save_window_workspace);
2405 pref_checkbox_link_sensitivity(ct_button, button);
2407 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2408 options->tools_restore_state, &c_options->tools_restore_state);
2410 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2411 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2413 pref_checkbox_new_int(group, _("Show window IDs"),
2414 options->show_window_ids, &c_options->show_window_ids);
2416 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2417 pref_label_new(subgroup, _("Use current layout for default: "));
2418 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2422 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2423 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2424 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2426 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2428 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2429 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2431 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2432 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2433 options->image.limit_window_size, &c_options->image.limit_window_size);
2434 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2436 options->image.max_window_size, &c_options->image.max_window_size);
2437 pref_checkbox_link_sensitivity(ct_button, spin);
2439 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2441 c_options->fullscreen.screen = options->fullscreen.screen;
2442 c_options->fullscreen.above = options->fullscreen.above;
2443 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2444 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2445 gtk_widget_show(hbox);
2447 pref_checkbox_new_int(group, _("Smooth image flip"),
2448 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2449 pref_checkbox_new_int(group, _("Disable screen saver"),
2450 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2453 #define PRE_FORMATTED_COLUMNS 5
2454 static void config_tab_osd(GtkWidget *notebook)
2460 GtkWidget *image_overlay_template_view;
2461 GtkWidget *scrolled;
2462 GtkWidget *scrolled_pre_formatted;
2463 GtkTextBuffer *buffer;
2465 GtkWidget *subgroup;
2467 vbox = scrolled_notebook_page(notebook, _("OSD"));
2469 image_overlay_template_view = gtk_text_view_new();
2471 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2473 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2475 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2476 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2477 gq_gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2478 gtk_widget_show(scrolled_pre_formatted);
2479 gtk_widget_show(subgroup);
2481 pref_line(group, PREF_PAD_GAP);
2483 pref_label_new(group, _("Image overlay template"));
2485 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2486 gtk_widget_set_size_request(scrolled, 200, 150);
2487 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2488 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2489 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2490 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2491 gtk_widget_show(scrolled);
2493 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2494 _("Extensive formatting options are shown in the Help file"));
2496 gq_gtk_container_add(GTK_WIDGET(scrolled), image_overlay_template_view);
2497 gtk_widget_show(image_overlay_template_view);
2499 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2501 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2502 G_CALLBACK(image_overlay_set_font_cb), notebook);
2504 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2505 gtk_widget_show(button);
2507 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"), G_CALLBACK(image_overlay_set_text_color_cb), nullptr);
2508 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2509 gtk_widget_show(button);
2511 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"), G_CALLBACK(image_overlay_set_background_color_cb), nullptr);
2512 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2513 gtk_widget_show(button);
2514 image_overlay_set_text_colors();
2516 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2517 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2518 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2519 gtk_widget_show(button);
2521 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2522 G_CALLBACK(image_overlay_help_cb), nullptr);
2523 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2524 gtk_widget_show(button);
2526 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2527 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2528 g_signal_connect(G_OBJECT(buffer), "changed",
2529 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2531 pref_line(group, PREF_PAD_GAP);
2533 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2534 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2535 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2536 gtk_widget_show(hbox);
2537 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2538 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2539 gtk_widget_show(label);
2540 pref_spacer(group,TRUE);
2542 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2543 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2544 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2545 gtk_widget_show(hbox);
2546 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2547 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2548 gtk_widget_show(label);
2549 pref_spacer(group,TRUE);
2551 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2552 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2553 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2554 gtk_widget_show(hbox);
2555 label = gtk_label_new(_("%path:39%"));
2556 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2557 gtk_widget_show(label);
2558 pref_spacer(group,TRUE);
2560 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2561 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2562 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2563 gtk_widget_show(hbox);
2564 label = gtk_label_new(_("Text shown only if the field is non-null:\n%formatted.Aperture:F no. * setting%\n %formatted.Aperture:10:F no. * setting%"));
2565 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2566 gtk_widget_show(label);
2567 pref_spacer(group,TRUE);
2569 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2570 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2571 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2572 gtk_widget_show(hbox);
2573 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2574 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2575 gtk_widget_show(label);
2578 static GtkTreeModel *create_class_model()
2580 GtkListStore *model;
2584 /* create list store */
2585 model = gtk_list_store_new(1, G_TYPE_STRING);
2586 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2588 gtk_list_store_append(model, &iter);
2589 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2591 return GTK_TREE_MODEL (model);
2596 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2598 gint n = GPOINTER_TO_INT(data);
2600 FilterEntry *filter_a;
2601 FilterEntry *filter_b;
2603 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2604 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2608 case FILETYPES_COLUMN_ENABLED:
2610 ret = filter_a->enabled - filter_b->enabled;
2613 case FILETYPES_COLUMN_FILTER:
2615 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2618 case FILETYPES_COLUMN_DESCRIPTION:
2620 ret = g_utf8_collate(filter_a->description, filter_b->description);
2623 case FILETYPES_COLUMN_CLASS:
2625 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2628 case FILETYPES_COLUMN_WRITABLE:
2630 ret = filter_a->writable - filter_b->writable;
2633 case FILETYPES_COLUMN_SIDECAR:
2635 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2639 g_return_val_if_reached(0);
2645 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2648 gboolean ret = TRUE;
2650 gtk_tree_model_get(model, iter, 0, &fe, -1);
2652 if (g_strstr_len(fe->extensions, -1, key))
2660 static void config_tab_files(GtkWidget *notebook)
2667 GtkWidget *ct_button;
2668 GtkWidget *scrolled;
2669 GtkWidget *filter_view;
2670 GtkCellRenderer *renderer;
2671 GtkTreeSelection *selection;
2672 GtkTreeViewColumn *column;
2674 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2676 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2678 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2679 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2680 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2681 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2682 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);
2683 pref_checkbox_new_int(group, _("Disable file extension checks"),
2684 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2686 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2687 options->file_filter.disable, &c_options->file_filter.disable);
2690 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2692 sidecar_ext_entry = gtk_entry_new();
2693 gq_gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2694 gq_gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2695 gtk_widget_show(sidecar_ext_entry);
2697 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2699 frame = pref_group_parent(group);
2700 g_signal_connect(G_OBJECT(ct_button), "toggled",
2701 G_CALLBACK(filter_disable_cb), frame);
2702 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2704 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2705 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2706 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2707 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2708 gtk_widget_show(scrolled);
2710 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2711 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2712 g_object_unref(filter_store);
2713 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2714 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2716 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2718 column = gtk_tree_view_column_new();
2719 gtk_tree_view_column_set_title(column, _("Enabled"));
2720 gtk_tree_view_column_set_resizable(column, TRUE);
2722 renderer = gtk_cell_renderer_toggle_new();
2723 g_signal_connect(G_OBJECT(renderer), "toggled",
2724 G_CALLBACK(filter_store_enable_cb), filter_store);
2725 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2726 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2727 GINT_TO_POINTER(FE_ENABLE), nullptr);
2728 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);
2729 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2730 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2732 column = gtk_tree_view_column_new();
2733 gtk_tree_view_column_set_title(column, _("Filter"));
2734 gtk_tree_view_column_set_resizable(column, TRUE);
2735 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);
2736 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2738 renderer = gtk_cell_renderer_text_new();
2739 g_signal_connect(G_OBJECT(renderer), "edited",
2740 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2741 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2742 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2743 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2744 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2745 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2747 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2748 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2749 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2751 column = gtk_tree_view_column_new();
2752 gtk_tree_view_column_set_title(column, _("Description"));
2753 gtk_tree_view_column_set_resizable(column, TRUE);
2754 gtk_tree_view_column_set_fixed_width(column, 200);
2755 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2757 renderer = gtk_cell_renderer_text_new();
2758 g_signal_connect(G_OBJECT(renderer), "edited",
2759 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2760 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2761 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2762 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2763 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2764 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2765 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);
2766 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2768 column = gtk_tree_view_column_new();
2769 gtk_tree_view_column_set_title(column, _("Class"));
2770 gtk_tree_view_column_set_resizable(column, TRUE);
2771 renderer = gtk_cell_renderer_combo_new();
2772 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2773 "model", create_class_model(),
2778 g_signal_connect(G_OBJECT(renderer), "edited",
2779 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2780 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2781 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2782 GINT_TO_POINTER(FE_CLASS), nullptr);
2783 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2784 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);
2785 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2787 column = gtk_tree_view_column_new();
2788 gtk_tree_view_column_set_title(column, _("Writable"));
2789 gtk_tree_view_column_set_resizable(column, FALSE);
2790 renderer = gtk_cell_renderer_toggle_new();
2791 g_signal_connect(G_OBJECT(renderer), "toggled",
2792 G_CALLBACK(filter_store_writable_cb), filter_store);
2793 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2794 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2795 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2796 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2797 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);
2798 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2800 column = gtk_tree_view_column_new();
2801 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2802 gtk_tree_view_column_set_resizable(column, FALSE);
2803 renderer = gtk_cell_renderer_toggle_new();
2804 g_signal_connect(G_OBJECT(renderer), "toggled",
2805 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2806 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2807 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2808 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2809 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2810 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);
2811 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2813 filter_store_populate();
2814 gq_gtk_container_add(GTK_WIDGET(scrolled), filter_view);
2815 gtk_widget_show(filter_view);
2817 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2819 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2820 G_CALLBACK(filter_default_cb), filter_view);
2821 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2822 gtk_widget_show(button);
2824 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2825 G_CALLBACK(filter_remove_cb), filter_view);
2826 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2827 gtk_widget_show(button);
2829 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2830 G_CALLBACK(filter_add_cb), filter_view);
2831 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2832 gtk_widget_show(button);
2836 static void config_tab_metadata(GtkWidget *notebook)
2841 GtkWidget *ct_button;
2843 GtkWidget *tmp_widget;
2845 GtkWidget *text_label;
2847 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2850 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2852 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2854 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."));
2855 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2856 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2858 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2860 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2861 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2862 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"));
2863 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2865 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)"));
2866 gtk_widget_set_tooltip_markup(ct_button, markup);
2870 gtk_widget_set_sensitive(ct_button, FALSE);
2873 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2874 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2875 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)"));
2876 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2879 label = pref_label_new(group, "");
2880 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(), "/");
2881 gtk_label_set_markup (GTK_LABEL(label), markup);
2884 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2885 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2886 gtk_widget_set_margin_start(label, 22);
2887 pref_spacer(group, PREF_PAD_GROUP);
2889 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2891 gtk_widget_set_sensitive(group, FALSE);
2894 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2895 pref_checkbox_link_sensitivity(ct_button, hbox);
2897 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);
2898 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2900 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);
2902 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2904 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2905 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2906 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2907 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", ")");
2908 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2911 pref_spacer(group, PREF_PAD_GROUP);
2913 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2915 gtk_widget_set_sensitive(group, FALSE);
2918 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);
2920 pref_spacer(group, PREF_PAD_GROUP);
2922 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2923 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);
2924 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2926 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2927 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2929 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2930 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"));
2933 gtk_widget_set_sensitive(ct_button, FALSE);
2936 pref_spacer(group, PREF_PAD_GROUP);
2938 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2940 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2942 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2943 pref_checkbox_link_sensitivity(ct_button, hbox);
2945 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2947 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2949 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2951 pref_spacer(group, PREF_PAD_GROUP);
2954 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2956 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2957 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2960 pref_spacer(group, PREF_PAD_GROUP);
2962 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2964 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2965 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");
2970 struct KeywordFindData
2977 GtkWidget *button_close;
2978 GtkWidget *button_stop;
2979 GtkWidget *button_start;
2980 GtkWidget *progress;
2988 guint idle_id; /* event source id */
2991 #define KEYWORD_DIALOG_WIDTH 400
2993 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
2995 GList *list_d = nullptr;
2996 GList *list_f = nullptr;
3000 filelist_read(dir_fd, &list_f, &list_d);
3004 filelist_read(dir_fd, &list_f, nullptr);
3007 list_f = filelist_filter(list_f, FALSE);
3008 list_d = filelist_filter(list_d, TRUE);
3010 kfd->list = g_list_concat(list_f, kfd->list);
3011 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3014 static void keywords_find_reset(KeywordFindData *kfd)
3016 filelist_free(kfd->list);
3017 kfd->list = nullptr;
3019 filelist_free(kfd->list_dir);
3020 kfd->list_dir = nullptr;
3023 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3025 auto kfd = static_cast<KeywordFindData *>(data);
3027 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3029 keywords_find_reset(kfd);
3030 generic_dialog_close(kfd->gd);
3034 static void keywords_find_finish(KeywordFindData *kfd)
3036 keywords_find_reset(kfd);
3038 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3039 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
3041 gtk_widget_set_sensitive(kfd->group, TRUE);
3042 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3043 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3044 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3047 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3049 auto kfd = static_cast<KeywordFindData *>(data);
3051 g_idle_remove_by_data(kfd);
3053 keywords_find_finish(kfd);
3056 static gboolean keywords_find_file(gpointer data)
3058 auto kfd = static_cast<KeywordFindData *>(data);
3060 GtkTextBuffer *buffer;
3068 fd = static_cast<FileData *>(kfd->list->data);
3069 kfd->list = g_list_remove(kfd->list, fd);
3071 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3072 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3076 gtk_text_buffer_get_end_iter(buffer, &iter);
3077 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3078 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3080 keywords = keywords->next;
3083 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3084 file_data_unref(fd);
3085 g_list_free_full(keywords, g_free);
3087 return (G_SOURCE_CONTINUE);
3089 else if (kfd->list_dir)
3093 fd = static_cast<FileData *>(kfd->list_dir->data);
3094 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3096 keywords_find_folder(kfd, fd);
3098 file_data_unref(fd);
3100 return G_SOURCE_CONTINUE;
3103 keywords_find_finish(kfd);
3105 return G_SOURCE_REMOVE;
3108 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3110 auto kfd = static_cast<KeywordFindData *>(data);
3113 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3115 path = remove_trailing_slash((gq_gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3116 parse_out_relatives(path);
3120 warning_dialog(_("Invalid folder"),
3121 _("The specified folder can not be found."),
3122 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3128 gtk_widget_set_sensitive(kfd->group, FALSE);
3129 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3130 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3131 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3132 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3134 dir_fd = file_data_new_dir(path);
3135 keywords_find_folder(kfd, dir_fd);
3136 file_data_unref(dir_fd);
3137 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3143 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3145 KeywordFindData *kfd;
3149 kfd = g_new0(KeywordFindData, 1);
3151 kfd->gd = generic_dialog_new(_("Search for keywords"),
3152 "search_for_keywords",
3155 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3156 kfd->gd->cancel_cb = keywords_find_close_cb;
3157 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3158 keywords_find_close_cb, FALSE);
3159 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3160 keywords_find_start_cb, FALSE);
3161 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3162 keywords_find_stop_cb, FALSE);
3163 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3165 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3167 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3168 pref_spacer(hbox, PREF_PAD_INDENT);
3169 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3171 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3172 pref_label_new(hbox, _("Folder:"));
3174 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3175 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3176 gq_gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3177 gtk_widget_show(label);
3179 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3181 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3182 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3184 kfd->progress = gtk_entry_new();
3185 gtk_widget_set_can_focus(kfd->progress, FALSE);
3186 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3187 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3188 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3189 gtk_widget_show(kfd->progress);
3191 kfd->spinner = gtk_spinner_new();
3192 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3193 gtk_widget_show(kfd->spinner);
3195 kfd->list = nullptr;
3197 gtk_widget_show(kfd->gd->dialog);
3200 static void keywords_find_cb(GtkWidget *widget, gpointer)
3202 const gchar *path = layout_get_path(nullptr);
3204 if (!path || !*path) path = homedir();
3205 keywords_find_dialog(widget, path);
3208 static void config_tab_keywords_save()
3210 GtkTextIter start, end;
3211 GtkTextBuffer *buffer;
3212 GList *kw_list = nullptr;
3218 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3219 gtk_text_buffer_get_bounds(buffer, &start, &end);
3221 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3223 kw_split = strtok(buffer_text, "\n");
3224 while (kw_split != nullptr)
3230 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3239 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3241 kw_split = strtok(nullptr, "\n");
3244 keyword_list_set(kw_list);
3246 g_list_free_full(kw_list, g_free);
3247 g_free(buffer_text);
3250 static void config_tab_keywords(GtkWidget *notebook)
3256 GtkWidget *scrolled;
3258 GtkTextBuffer *buffer;
3261 GspellTextView *gspell_view;
3264 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3266 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3268 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3270 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3271 G_CALLBACK(keywords_find_cb), keyword_text);
3272 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3275 keyword_text = gtk_text_view_new();
3276 gtk_widget_set_size_request(keyword_text, 20, 20);
3277 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3278 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3279 gtk_widget_show(scrolled);
3282 if (options->metadata.check_spelling)
3284 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3285 gspell_text_view_basic_setup(gspell_view);
3289 gq_gtk_container_add(GTK_WIDGET(scrolled), keyword_text);
3290 gtk_widget_show(keyword_text);
3292 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3294 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3295 gtk_text_buffer_create_tag(buffer, "monospace",
3296 "family", "monospace", NULL);
3298 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3299 gtk_text_buffer_get_start_iter(buffer, &iter);
3300 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3303 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3305 GList *kwl = keyword_list_get();
3306 kwl = g_list_first(kwl);
3309 gtk_text_buffer_get_end_iter (buffer, &iter);
3310 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3311 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3316 gtk_text_buffer_set_modified(buffer, FALSE);
3323 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3325 auto option = static_cast<gint *>(data);
3327 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3331 *option = INTENT_PERCEPTUAL;
3334 *option = INTENT_RELATIVE_COLORIMETRIC;
3337 *option = INTENT_SATURATION;
3340 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3345 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3346 gint option, gint *option_c)
3353 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3355 combo = gtk_combo_box_text_new();
3357 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3358 if (option == INTENT_PERCEPTUAL) current = 0;
3359 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3360 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3361 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3362 if (option == INTENT_SATURATION) current = 2;
3363 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3364 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3366 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3368 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3370 g_signal_connect(G_OBJECT(combo), "changed",
3371 G_CALLBACK(intent_menu_cb), option_c);
3373 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3374 gtk_widget_show(combo);
3378 static void config_tab_color(GtkWidget *notebook)
3387 vbox = scrolled_notebook_page(notebook, _("Color management"));
3389 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3391 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3394 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3395 gtk_grid_set_column_spacing(GTK_GRID(table), PREF_PAD_GAP);
3397 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3398 pref_label_bold(label, TRUE, FALSE);
3400 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3401 pref_label_bold(label, TRUE, FALSE);
3403 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3404 pref_label_bold(label, TRUE, FALSE);
3406 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3411 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3412 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3415 entry = gtk_entry_new();
3416 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3417 if (options->color_profile.input_name[i])
3419 gq_gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3421 gq_gtk_grid_attach(GTK_GRID(table), entry, 1, 2, i + 1, i + 2, static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3422 gtk_widget_show(entry);
3423 color_profile_input_name_entry[i] = entry;
3425 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3426 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3427 gtk_widget_set_size_request(entry, 160, -1);
3428 gq_gtk_grid_attach(GTK_GRID(table), tabcomp, 2, 3, i + 1, i + 2, static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3429 gtk_widget_show(tabcomp);
3430 color_profile_input_file_entry[i] = entry;
3433 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3435 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3437 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3438 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3440 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3442 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3443 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3444 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3445 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3446 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3448 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3450 gq_gtk_grid_attach(GTK_GRID(table), tabcomp, 1, 2, 0, 1, static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3452 gtk_widget_show(tabcomp);
3455 /* advanced entry tab */
3456 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3458 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3460 c_options->file_ops.use_system_trash = FALSE;
3461 c_options->file_ops.no_trash = FALSE;
3465 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3467 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3469 c_options->file_ops.use_system_trash = TRUE;
3470 c_options->file_ops.no_trash = FALSE;
3474 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3476 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3478 c_options->file_ops.no_trash = TRUE;
3482 static void config_tab_behavior(GtkWidget *notebook)
3489 GtkWidget *ct_button;
3493 GtkWidget *with_rename;
3494 GtkWidget *collections_on_top;
3495 GtkWidget *hide_window_in_fullscreen;
3496 GtkWidget *checkbox;
3499 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3501 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3503 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3504 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3505 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3506 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3507 pref_checkbox_new_int(group, _("Enable Delete key"),
3508 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3510 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3511 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3513 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3514 pref_checkbox_link_sensitivity(ct_button, hbox);
3516 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3517 pref_label_new(hbox, _("Folder:"));
3519 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3520 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3521 gq_gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3522 gtk_widget_show(tabcomp);
3524 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3525 pref_checkbox_link_sensitivity(ct_button, hbox);
3527 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3528 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3529 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3530 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3531 button = pref_button_new(nullptr, nullptr, _("View"),
3532 G_CALLBACK(safe_delete_view_cb), nullptr);
3533 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3534 gtk_widget_show(button);
3536 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3537 G_CALLBACK(safe_delete_clear_cb), nullptr);
3538 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3540 c_options->file_ops.no_trash = options->file_ops.no_trash;
3541 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3543 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3544 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3546 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3547 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3549 gtk_widget_show(button);
3551 pref_spacer(group, PREF_PAD_GROUP);
3554 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3556 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3557 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3559 pref_checkbox_new_int(group, _("In place renaming"),
3560 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3562 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3563 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3565 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3566 options->circular_selection_lists, &c_options->circular_selection_lists);
3567 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3569 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3570 options->marks_save, &c_options->marks_save);
3571 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3573 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3574 options->with_rename, &c_options->with_rename);
3575 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3577 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3578 options->collections_duplicates, &c_options->collections_duplicates);
3579 gtk_widget_set_tooltip_text(collections_on_top,"Allow the same image to be in a Collection more than once");
3581 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3582 options->collections_on_top, &c_options->collections_on_top);
3583 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3585 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3586 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3587 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3589 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3590 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3592 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);
3593 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."));
3595 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3596 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3598 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3599 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3601 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3602 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3604 pref_spacer(group, PREF_PAD_GROUP);
3606 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3608 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3609 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3610 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3611 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3612 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3613 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3614 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3615 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3616 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3617 options->image_l_click_archive, &c_options->image_l_click_archive);
3618 pref_checkbox_new_int(group, _("Play video by left click on image"),
3619 options->image_l_click_video, &c_options->image_l_click_video);
3620 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3621 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3623 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3624 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3625 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3626 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3627 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3629 pref_spacer(group, PREF_PAD_GROUP);
3631 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3633 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3634 options->override_disable_gpu, &c_options->override_disable_gpu);
3635 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3638 pref_spacer(group, PREF_PAD_GROUP);
3640 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3642 pref_spin_new_int(group, _("Debug level:"), nullptr,
3643 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3645 pref_checkbox_new_int(group, _("Timer data"),
3646 options->log_window.timer_data, &c_options->log_window.timer_data);
3648 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3649 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3651 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3652 pref_label_new(hbox, _("Log Window F1 command: "));
3653 log_window_f1_entry = gtk_entry_new();
3654 gq_gtk_entry_set_text(GTK_ENTRY(log_window_f1_entry), options->log_window.action);
3655 gq_gtk_box_pack_start(GTK_BOX(hbox), log_window_f1_entry, FALSE, FALSE, 0);
3656 gtk_entry_set_width_chars(GTK_ENTRY(log_window_f1_entry), 15);
3657 gtk_widget_show(log_window_f1_entry);
3661 /* accelerators tab */
3663 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3665 gboolean ret = TRUE;
3670 gtk_tree_model_get(model, iter, column, &text, -1);
3671 text_nocase = g_utf8_casefold(text, -1);
3672 key_nocase = g_utf8_casefold(key, -1);
3674 if (g_strstr_len(text_nocase, -1, key_nocase))
3681 g_free(text_nocase);
3686 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3691 list = gtk_tree_view_get_columns(tree_view);
3692 col_num = g_list_index(list, column);
3696 gtk_tree_view_set_search_column(tree_view, col_num);
3699 static void config_tab_accelerators(GtkWidget *notebook)
3705 GtkWidget *scrolled;
3706 GtkWidget *accel_view;
3707 GtkCellRenderer *renderer;
3708 GtkTreeSelection *selection;
3709 GtkTreeViewColumn *column;
3711 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3713 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3715 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3716 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3717 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3718 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3719 gtk_widget_show(scrolled);
3721 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3723 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3724 g_object_unref(accel_store);
3725 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3726 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3728 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3730 renderer = gtk_cell_renderer_text_new();
3732 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3737 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3738 gtk_tree_view_column_set_resizable(column, TRUE);
3739 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3742 renderer = gtk_cell_renderer_accel_new();
3743 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3744 G_CALLBACK(accel_store_cleared_cb), accel_store);
3745 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3746 G_CALLBACK(accel_store_edited_cb), accel_store);
3749 g_object_set (renderer,
3751 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3754 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3759 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3760 gtk_tree_view_column_set_resizable(column, TRUE);
3761 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3763 renderer = gtk_cell_renderer_text_new();
3765 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3770 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3771 gtk_tree_view_column_set_resizable(column, TRUE);
3772 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3774 renderer = gtk_cell_renderer_text_new();
3776 column = gtk_tree_view_column_new_with_attributes("Accel",
3781 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3782 gtk_tree_view_column_set_resizable(column, TRUE);
3783 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3785 /* Search on text in column */
3786 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3787 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3788 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3789 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3790 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3792 accel_store_populate();
3793 gq_gtk_container_add(GTK_WIDGET(scrolled), accel_view);
3794 gtk_widget_show(accel_view);
3796 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3798 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3799 G_CALLBACK(accel_default_cb), accel_view);
3800 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3801 gtk_widget_show(button);
3803 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3804 G_CALLBACK(accel_reset_cb), accel_view);
3805 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3806 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3807 gtk_widget_show(button);
3809 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3810 G_CALLBACK(accel_clear_cb), accel_view);
3811 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3812 gtk_widget_show(button);
3815 /* toolbar main tab */
3816 static void config_tab_toolbar_main(GtkWidget *notebook)
3819 GtkWidget *toolbardata;
3822 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3824 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3826 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3827 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3828 gtk_widget_show(vbox);
3831 /* toolbar status tab */
3832 static void config_tab_toolbar_status(GtkWidget *notebook)
3835 GtkWidget *toolbardata;
3838 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3840 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3842 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3843 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3844 gtk_widget_show(vbox);
3848 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3850 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3853 static void config_tab_advanced(GtkWidget *notebook)
3856 GdkPixbufFormat *fm;
3858 GList *extensions_list = nullptr;
3859 GSList *formats_list;
3860 GString *types_string = g_string_new(nullptr);
3861 GtkWidget *alternate_checkbox;
3862 GtkWidget *dupes_threads_spin;
3864 GtkWidget *subgroup;
3866 GtkWidget *threads_string_label;
3867 GtkWidget *types_string_label;
3870 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3871 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3873 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3875 pref_spacer(group, PREF_PAD_GROUP);
3877 formats_list = gdk_pixbuf_get_formats();
3879 while (formats_list)
3881 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3882 extensions = gdk_pixbuf_format_get_extensions(fm);
3885 while (extensions[i])
3887 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3891 g_strfreev(extensions);
3892 formats_list = formats_list->next;
3895 while (extensions_list)
3897 if (types_string->len == 0)
3899 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3903 types_string = g_string_append(types_string, ", ");
3904 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3907 extensions_list = extensions_list->next;
3910 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3911 types_string_label = pref_label_new(group, types_string->str);
3912 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3914 pref_spacer(group, PREF_PAD_GROUP);
3916 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3917 external_preview_select_entry = gtk_entry_new();
3918 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3919 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3920 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3921 gtk_widget_show(tabcomp);
3923 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3924 external_preview_extract_entry = gtk_entry_new();
3925 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3926 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3927 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3928 gtk_widget_show(tabcomp);
3930 gtk_widget_show(vbox);
3932 g_slist_free(formats_list);
3933 g_list_free_full(extensions_list, g_free);
3934 g_string_free(types_string, TRUE);
3936 pref_spacer(group, PREF_PAD_GROUP);
3938 pref_line(vbox, PREF_PAD_SPACE);
3939 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3941 threads_string_label = pref_label_new(group, "This option limits the number of threads (or cpu cores) that Geeqie will use when running duplicate checks.\nThe value 0 means all available cores will be used.");
3942 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3944 pref_spacer(vbox, PREF_PAD_GROUP);
3946 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3947 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3949 pref_spacer(group, PREF_PAD_GROUP);
3951 pref_line(vbox, PREF_PAD_SPACE);
3953 group = pref_group_new(vbox, FALSE, _("Alternate similarity alogorithm"), GTK_ORIENTATION_VERTICAL);
3955 alternate_checkbox = pref_checkbox_new_int(group, _("Enable alternate similarity algorithm"), options->alternate_similarity_algorithm.enabled, &c_options->alternate_similarity_algorithm.enabled);
3957 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3958 pref_checkbox_link_sensitivity(alternate_checkbox, subgroup);
3960 alternate_checkbox = pref_checkbox_new_int(subgroup, _("Use grayscale"), options->alternate_similarity_algorithm.grayscale, &c_options->alternate_similarity_algorithm.grayscale);
3961 gtk_widget_set_tooltip_text(alternate_checkbox, _("Reduce fingerprint to grayscale"));
3965 static void config_tab_stereo(GtkWidget *notebook)
3973 GtkWidget *fs_button;
3974 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3976 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3978 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3979 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3981 table = pref_table_new(group, 2, 2, TRUE, FALSE);
3982 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3983 pref_checkbox_new_int(box, _("Mirror left image"),
3984 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3985 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3986 pref_checkbox_new_int(box, _("Flip left image"),
3987 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3988 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3989 pref_checkbox_new_int(box, _("Mirror right image"),
3990 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3991 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3992 pref_checkbox_new_int(box, _("Flip right image"),
3993 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3994 pref_checkbox_new_int(group, _("Swap left and right images"),
3995 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3996 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3997 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3999 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
4000 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
4001 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
4002 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4003 pref_checkbox_link_sensitivity(fs_button, box2);
4004 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
4005 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
4006 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
4007 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4008 pref_checkbox_new_int(box, _("Mirror left image"),
4009 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
4010 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4011 pref_checkbox_new_int(box, _("Flip left image"),
4012 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
4013 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4014 pref_checkbox_new_int(box, _("Mirror right image"),
4015 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
4016 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4017 pref_checkbox_new_int(box, _("Flip right image"),
4018 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
4019 pref_checkbox_new_int(box2, _("Swap left and right images"),
4020 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
4021 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
4022 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
4024 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
4025 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
4026 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
4027 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
4028 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
4029 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
4030 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
4031 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
4032 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
4033 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4034 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
4035 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4036 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
4037 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4041 /* Main preferences window */
4042 static void config_window_create(LayoutWindow *lw)
4044 GtkWidget *win_vbox;
4046 GtkWidget *notebook;
4048 GtkWidget *ct_button;
4050 if (!c_options) c_options = init_options(nullptr);
4052 configwindow = window_new("preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4053 DEBUG_NAME(configwindow);
4054 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4055 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4056 G_CALLBACK(config_window_delete), NULL);
4057 if (options->save_dialog_window_positions)
4059 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4060 gq_gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4064 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4066 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4067 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4069 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4070 gq_gtk_container_add(GTK_WIDGET(configwindow), win_vbox);
4071 gtk_widget_show(win_vbox);
4073 notebook = gtk_notebook_new();
4074 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4075 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4076 gq_gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4078 config_tab_general(notebook);
4079 config_tab_image(notebook);
4080 config_tab_osd(notebook);
4081 config_tab_windows(notebook);
4082 config_tab_accelerators(notebook);
4083 config_tab_files(notebook);
4084 config_tab_metadata(notebook);
4085 config_tab_keywords(notebook);
4086 config_tab_color(notebook);
4087 config_tab_stereo(notebook);
4088 config_tab_behavior(notebook);
4089 config_tab_toolbar_main(notebook);
4090 config_tab_toolbar_status(notebook);
4091 config_tab_advanced(notebook);
4093 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4095 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4096 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4097 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4098 gq_gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4099 gtk_widget_show(hbox);
4101 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4102 G_CALLBACK(config_window_help_cb), notebook);
4103 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4104 gtk_widget_set_can_default(button, TRUE);
4105 gtk_widget_show(button);
4107 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4108 G_CALLBACK(config_window_ok_cb), notebook);
4109 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4110 gtk_widget_set_can_default(button, TRUE);
4111 gtk_widget_grab_default(button);
4112 gtk_widget_show(button);
4116 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4117 G_CALLBACK(config_window_close_cb), nullptr);
4118 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4119 gtk_widget_set_can_default(button, TRUE);
4120 gtk_widget_show(button);
4122 if (!generic_dialog_get_alternative_button_order(configwindow))
4124 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4127 gtk_widget_show(notebook);
4129 gtk_widget_show(configwindow);
4133 *-----------------------------------------------------------------------------
4134 * config window show (public)
4135 *-----------------------------------------------------------------------------
4138 void show_config_window(LayoutWindow *lw)
4142 gtk_window_present(GTK_WINDOW(configwindow));
4146 config_window_create(lw);
4155 void show_about_window(LayoutWindow *lw)
4157 GDataInputStream *data_stream;
4158 GInputStream *in_stream_authors;
4159 GInputStream *in_stream_translators;
4161 GdkPixbuf *pixbuf_icon;
4162 GdkPixbuf *pixbuf_logo;
4166 gchar *authors[1000];
4168 gchar *timezone_path;
4177 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");
4179 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4180 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4182 cd = ZDOpenDatabase(timezone_path);
4185 copyright = g_string_append(copyright, ZDGetNotice(cd));
4189 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4191 ZDCloseDatabase(cd);
4193 g_free(timezone_path);
4195 copyright = g_string_append(copyright, "\n\nSome icons by https://www.flaticon.com");
4197 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4199 data_stream = g_data_input_stream_new(in_stream_authors);
4201 authors[0] = nullptr;
4202 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4204 authors[i_authors] = g_strdup(author_line);
4206 g_free(author_line);
4208 authors[i_authors] = nullptr;
4210 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4212 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4214 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4216 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4217 translators = static_cast<gchar *>(g_malloc0(size));
4218 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4219 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4221 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);
4223 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4224 artists[1] = nullptr;
4226 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4227 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4228 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4229 "title", _("About Geeqie"),
4231 "program-name", GQ_APPNAME,
4233 "logo", pixbuf_logo,
4234 "icon", pixbuf_icon,
4235 "website", GQ_WEBSITE,
4236 "website-label", "Website",
4237 "comments", comment,
4240 "translator-credits", translators,
4241 "wrap-license", TRUE,
4242 "license", copyright->str,
4245 g_string_free(copyright, TRUE);
4247 while(n < i_authors)
4255 g_free(translators);
4256 g_object_unref(data_stream);
4257 g_object_unref(in_stream_authors);
4258 g_object_unref(in_stream_translators);
4261 static void image_overlay_set_text_colors()
4263 c_options->image_overlay.text_red = options->image_overlay.text_red;
4264 c_options->image_overlay.text_green = options->image_overlay.text_green;
4265 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4266 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4267 c_options->image_overlay.background_red = options->image_overlay.background_red;
4268 c_options->image_overlay.background_green = options->image_overlay.background_green;
4269 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4270 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4274 *-----------------------------------------------------------------------------
4275 * timezone database routines
4276 *-----------------------------------------------------------------------------
4279 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4281 GError *error = nullptr;
4282 auto tz = static_cast<TZData *>(data);
4283 gchar *tmp_filename;
4284 gchar *timezone_bin;
4285 gchar *tmp_dir = nullptr;
4288 if (!g_cancellable_is_cancelled(tz->cancellable))
4290 generic_dialog_close(tz->gd);
4294 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4296 tmp_filename = g_file_get_path(tz->tmp_g_file);
4297 fd = file_data_new_simple(tmp_filename);
4298 tmp_dir = open_archive(fd);
4302 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4303 if (isfile(timezone_bin))
4305 move_file(timezone_bin, tz->timezone_database_user);
4309 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4312 g_free(timezone_bin);
4313 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4317 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4319 g_free(tmp_filename);
4320 file_data_unref(fd);
4324 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4327 g_file_delete(tz->tmp_g_file, nullptr, &error);
4328 g_object_unref(tz->tmp_g_file);
4329 tz->tmp_g_file = nullptr;
4330 g_object_unref(tz->cancellable);
4331 g_object_unref(tz->timezone_database_gq);
4334 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4336 auto tz = static_cast<TZData *>(data);
4338 if (!g_cancellable_is_cancelled(tz->cancellable))
4340 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4344 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4346 auto tz = static_cast<TZData *>(data);
4348 g_cancellable_cancel(tz->cancellable);
4351 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4353 auto tz = static_cast<TZData *>(data);
4354 GError *error = nullptr;
4355 GFileIOStream *io_stream;
4362 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4366 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4367 log_printf("Error: Download timezone database failed:\n%s", error->message);
4368 g_error_free(error);
4369 g_object_unref(tz->tmp_g_file);
4373 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4375 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4377 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4379 tz->progress = gtk_progress_bar_new();
4380 gq_gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4381 gtk_widget_show(tz->progress);
4383 gtk_widget_show(tz->gd->dialog);
4384 tz->cancellable = g_cancellable_new();
4385 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);
4387 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4390 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */