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"
29 #include "filefilter.h"
30 #include "fullscreen.h"
32 #include "image-overlay.h"
33 #include "color-man.h"
35 #include "layout-util.h"
39 #include "pixbuf-util.h"
41 #include "slideshow.h"
45 #include "ui-fileops.h"
47 #include "ui-spinner.h"
48 #include "ui-tabcomp.h"
50 #include "zonedetect.h"
61 #include <gspell/gspell.h>
64 #define EDITOR_NAME_MAX_LENGTH 32
65 #define EDITOR_COMMAND_MAX_LENGTH 1024
67 static void image_overlay_set_text_colours();
69 GtkWidget *keyword_text;
70 static void config_tab_keywords_save();
78 static ThumbSize thumb_size_list[] =
113 FILETYPES_COLUMN_ENABLED = 0,
114 FILETYPES_COLUMN_FILTER,
115 FILETYPES_COLUMN_DESCRIPTION,
116 FILETYPES_COLUMN_CLASS,
117 FILETYPES_COLUMN_WRITABLE,
118 FILETYPES_COLUMN_SIDECAR,
119 FILETYPES_COLUMN_COUNT
122 const gchar *format_class_list[] = {
133 /* config memory values */
134 static ConfOptions *c_options = nullptr;
141 static GtkWidget *configwindow = nullptr;
142 static GtkListStore *filter_store = nullptr;
143 static GtkTreeStore *accel_store = nullptr;
145 static GtkWidget *safe_delete_path_entry;
147 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
148 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
149 static GtkWidget *color_profile_screen_file_entry;
150 static GtkWidget *external_preview_select_entry;
151 static GtkWidget *external_preview_extract_entry;
153 static GtkWidget *sidecar_ext_entry;
154 static GtkWidget *help_search_engine_entry;
157 #define CONFIG_WINDOW_DEF_WIDTH 700
158 #define CONFIG_WINDOW_DEF_HEIGHT 600
161 *-----------------------------------------------------------------------------
162 * option widget callbacks (private)
163 *-----------------------------------------------------------------------------
166 static void zoom_increment_cb(GtkWidget *spin, gpointer)
168 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
171 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
173 gint mins_secs_tenths, delay;
175 mins_secs_tenths = c_options->slideshow.delay %
176 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
178 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
179 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
182 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
183 SLIDESHOW_SUBSECOND_PRECISION;
186 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
188 gint hours, secs_tenths, delay;
190 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
191 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
193 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
194 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
195 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
197 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
198 SLIDESHOW_SUBSECOND_PRECISION;
201 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
203 gint hours_mins, delay;
205 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
207 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
208 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
209 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
211 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
212 SLIDESHOW_SUBSECOND_PRECISION;
216 *-----------------------------------------------------------------------------
217 * sync program to config window routine (private)
218 *-----------------------------------------------------------------------------
221 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
227 buf = gtk_entry_get_text(GTK_ENTRY(entry));
228 if (buf && strlen(buf) > 0)
233 *option = g_strdup(buf);
238 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
240 gchar *accel_path, *accel;
242 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
244 if (accel_path && accel_path[0])
247 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
248 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
258 static void config_window_apply()
260 gboolean refresh = FALSE;
265 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
267 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
268 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
269 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
270 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
271 if (options->file_sort.natural != c_options->file_sort.natural) 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_sort.natural = c_options->file_sort.natural;
325 options->file_filter.disable = c_options->file_filter.disable;
327 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
328 sidecar_ext_parse(options->sidecar.ext);
330 options->slideshow.random = c_options->slideshow.random;
331 options->slideshow.repeat = c_options->slideshow.repeat;
332 options->slideshow.delay = c_options->slideshow.delay;
334 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
335 options->image_lm_click_nav = c_options->image_lm_click_nav;
336 options->image_l_click_archive = c_options->image_l_click_archive;
337 options->image_l_click_video = c_options->image_l_click_video;
338 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
340 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
342 options->image.tile_cache_max = c_options->image.tile_cache_max;
343 options->image.image_cache_max = c_options->image.image_cache_max;
345 options->image.zoom_quality = c_options->image.zoom_quality;
347 options->image.zoom_increment = c_options->image.zoom_increment;
349 options->image.zoom_style = c_options->image.zoom_style;
351 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
353 options->appimage_notifications = c_options->appimage_notifications;
356 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
357 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
358 || !gdk_color_equal(&options->image.border_color, &c_options->image.border_color))
360 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
361 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
362 options->image.border_color = c_options->image.border_color;
363 layout_colors_update();
364 view_window_colors_update();
367 options->image.alpha_color_1 = c_options->image.alpha_color_1;
368 options->image.alpha_color_2 = c_options->image.alpha_color_2;
370 options->fullscreen.screen = c_options->fullscreen.screen;
371 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
372 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
373 options->fullscreen.above = c_options->fullscreen.above;
374 if (c_options->image_overlay.template_string)
375 set_image_overlay_template_string(&options->image_overlay.template_string,
376 c_options->image_overlay.template_string);
377 if (c_options->image_overlay.font)
378 set_image_overlay_font_string(&options->image_overlay.font,
379 c_options->image_overlay.font);
380 options->image_overlay.text_red = c_options->image_overlay.text_red;
381 options->image_overlay.text_green = c_options->image_overlay.text_green;
382 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
383 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
384 options->image_overlay.background_red = c_options->image_overlay.background_red;
385 options->image_overlay.background_green = c_options->image_overlay.background_green;
386 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
387 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
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->marks_save = c_options->marks_save;
449 options->with_rename = c_options->with_rename;
450 options->collections_on_top = c_options->collections_on_top;
451 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
452 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
454 options->external_preview.enable = c_options->external_preview.enable;
455 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
456 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
458 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
460 options->star_rating.star = c_options->star_rating.star;
461 options->star_rating.rejected = c_options->star_rating.rejected;
463 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
466 set_debug_level(debug_c);
470 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
472 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
473 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
475 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
476 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
477 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
479 options->color_profile.render_intent = c_options->color_profile.render_intent;
484 options->mouse_button_8 = c_options->mouse_button_8;
485 options->mouse_button_9 = c_options->mouse_button_9;
487 options->override_disable_gpu = c_options->override_disable_gpu;
489 config_tab_keywords_save();
491 image_options_sync();
496 layout_refresh(nullptr);
499 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
501 toolbar_apply(TOOLBAR_MAIN);
502 toolbar_apply(TOOLBAR_STATUS);
506 *-----------------------------------------------------------------------------
507 * config window main button callbacks (private)
508 *-----------------------------------------------------------------------------
511 static void config_window_close_cb(GtkWidget *, gpointer)
513 gtk_widget_destroy(configwindow);
514 configwindow = nullptr;
515 filter_store = nullptr;
518 static void config_window_help_cb(GtkWidget *, gpointer data)
520 auto notebook = static_cast<GtkWidget *>(data);
523 static const gchar *html_section[] =
525 "GuideOptionsGeneral.html",
526 "GuideOptionsImage.html",
527 "GuideOptionsOSD.html",
528 "GuideOptionsWindow.html",
529 "GuideOptionsKeyboard.html",
530 "GuideOptionsFiltering.html",
531 "GuideOptionsMetadata.html",
532 "GuideOptionsKeywords.html",
533 "GuideOptionsColor.html",
534 "GuideOptionsStereo.html",
535 "GuideOptionsBehavior.html",
536 "GuideOptionsToolbar.html",
537 "GuideOptionsToolbar.html",
538 "GuideOptionsAdvanced.html"
541 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
542 help_window_show(html_section[i]);
545 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
547 config_window_close_cb(nullptr, nullptr);
551 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
554 auto notebook = static_cast<GtkNotebook *>(data);
562 lw = static_cast<LayoutWindow *>(layout_window_list->data);
564 window = gtk_widget_get_window(widget);
565 gdk_window_get_root_origin(window, &x, &y);
566 w = gdk_window_get_width(window);
567 h = gdk_window_get_height(window);
568 page_number = gtk_notebook_get_current_page(notebook);
570 lw->options.preferences_window.x = x;
571 lw->options.preferences_window.y = y;
572 lw->options.preferences_window.w = w;
573 lw->options.preferences_window.h = h;
574 lw->options.preferences_window.page_number = page_number;
576 config_window_apply();
577 layout_util_sync(lw);
578 save_options(options);
579 config_window_close_cb(nullptr, nullptr);
583 *-----------------------------------------------------------------------------
584 * config window setup (private)
585 *-----------------------------------------------------------------------------
588 static void quality_menu_cb(GtkWidget *combo, gpointer data)
590 auto option = static_cast<gint *>(data);
592 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
596 *option = GDK_INTERP_NEAREST;
599 *option = GDK_INTERP_TILES;
602 *option = GDK_INTERP_BILINEAR;
607 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
609 auto option = static_cast<gint *>(data);
611 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
615 *option = DND_ACTION_ASK;
618 *option = DND_ACTION_COPY;
621 *option = DND_ACTION_MOVE;
625 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
627 auto option = static_cast<gint *>(data);
629 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
632 *option = CLIPBOARD_PRIMARY;
635 *option = CLIPBOARD_CLIPBOARD;
638 *option = CLIPBOARD_BOTH;
641 *option = CLIPBOARD_BOTH;
645 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
646 guint option, guint *option_c)
653 pref_table_label(table, column, row, text, 0.0);
655 combo = gtk_combo_box_text_new();
657 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
658 if (option == GDK_INTERP_NEAREST) current = 0;
659 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
660 if (option == GDK_INTERP_TILES) current = 1;
661 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
662 if (option == GDK_INTERP_BILINEAR) current = 2;
664 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
666 g_signal_connect(G_OBJECT(combo), "changed",
667 G_CALLBACK(quality_menu_cb), option_c);
669 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
670 gtk_widget_show(combo);
673 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
680 pref_table_label(table, column, row, text, 0.0);
682 combo = gtk_combo_box_text_new();
684 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
685 if (option == DND_ACTION_ASK) current = 0;
686 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
687 if (option == DND_ACTION_COPY) current = 1;
688 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
689 if (option == DND_ACTION_MOVE) current = 2;
691 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
693 g_signal_connect(G_OBJECT(combo), "changed",
694 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
696 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
697 gtk_widget_show(combo);
700 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
701 gint option, gint *option_c)
708 pref_table_label(table, column, row, text, 0.0);
710 combo = gtk_combo_box_text_new();
712 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
713 if (option == CLIPBOARD_PRIMARY) current = 0;
714 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
715 if (option == CLIPBOARD_CLIPBOARD) current = 1;
716 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
717 if (option == CLIPBOARD_BOTH) current = 2;
719 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
721 g_signal_connect(G_OBJECT(combo), "changed",
722 G_CALLBACK(clipboard_selection_menu_cb), option_c);
724 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
725 gtk_widget_show(combo);
728 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
730 auto option = static_cast<gint *>(data);
732 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
735 *option = ZOOM_GEOMETRIC;
738 *option = ZOOM_ARITHMETIC;
741 *option = ZOOM_GEOMETRIC;
745 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
752 pref_table_label(table, column, row, text, 0.0);
754 combo = gtk_combo_box_text_new();
756 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
757 if (option == ZOOM_GEOMETRIC) current = 0;
758 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
759 if (option == ZOOM_ARITHMETIC) current = 1;
761 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
763 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
765 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
766 gtk_widget_show(combo);
769 struct UseableMouseItems
771 const gchar *name; /* GtkActionEntry terminology */
773 const gchar *icon_name;
776 static const UseableMouseItems useable_mouse_items[] = {
778 {"FirstImage", N_("First Image"), GQ_ICON_GO_TOP},
779 {"PrevImage", N_("Previous Image"), GQ_ICON_GO_UP},
780 {"NextImage", N_("Next Image"), GQ_ICON_GO_DOWN},
781 {"LastImage", N_("Last Image"), GQ_ICON_GO_BOTTOM},
782 {"Back", N_("Back"), GQ_ICON_GO_PREV},
783 {"Forward", N_("Forward"), GQ_ICON_GO_NEXT},
784 {"Home", N_("Home"), GQ_ICON_HOME},
785 {"Up", N_("Up"), GQ_ICON_GO_UP},
786 {"FirstPage", N_("First page"), GQ_ICON_PREV_PAGE},
787 {"LastPage", N_("Last Page"), GQ_ICON_NEXT_PAGE},
788 {"NextPage", N_("Next page"), GQ_ICON_FORWARD_PAGE},
789 {"PrevPage", N_("Previous Page"), GQ_ICON_BACK_PAGE},
790 {"NewWindow", N_("New _window"), GQ_ICON_NEW},
791 {"NewCollection", N_("New collection"), GQ_ICON_COLLECTION},
792 {"OpenCollection", N_("Open collection"), GQ_ICON_OPEN},
793 {"Search", N_("Search"), GQ_ICON_FIND},
794 {"FindDupes", N_("Find duplicates"), GQ_ICON_FIND},
795 {"NewFolder", N_("New folder"),GQ_ICON_DIRECTORY},
796 {"Copy", N_("Copy"), GQ_ICON_COPY},
797 {"Move", N_("Move"), PIXBUF_INLINE_ICON_MOVE},
798 {"Rename", N_("Rename"), PIXBUF_INLINE_ICON_RENAME},
799 {"Delete", N_("Delete"), GQ_ICON_DELETE},
800 {"CloseWindow", N_("Close Window"), GQ_ICON_CLOSE},
801 {"PanView", N_("Pan view"), PIXBUF_INLINE_ICON_PANORAMA},
802 {"SelectAll", N_("Select all"), PIXBUF_INLINE_ICON_SELECT_ALL},
803 {"SelectNone", N_("Select none"), PIXBUF_INLINE_ICON_SELECT_NONE},
804 {"SelectInvert", N_("Select invert"), PIXBUF_INLINE_ICON_SELECT_INVERT},
805 {"ShowFileFilter", N_("Show file filter"), PIXBUF_INLINE_ICON_FILE_FILTER},
806 {"RectangularSelection", N_("Select rectangle"), PIXBUF_INLINE_ICON_SELECT_RECTANGLE},
807 {"Print", N_("Print"), GQ_ICON_PRINT},
808 {"Preferences", N_("Preferences"), GQ_ICON_PREFERENCES},
809 {"LayoutConfig", N_("Configure this window"), GQ_ICON_PREFERENCES},
810 {"Maintenance", N_("Cache maintenance"), PIXBUF_INLINE_ICON_MAINTENANCE},
811 {"RotateCW", N_("Rotate clockwise 90°"), PIXBUF_INLINE_ICON_CW},
812 {"RotateCCW", N_("Rotate counterclockwise 90°"), PIXBUF_INLINE_ICON_CCW},
813 {"Rotate180", N_("Rotate 180°"), PIXBUF_INLINE_ICON_180},
814 {"Mirror", N_("Mirror"), PIXBUF_INLINE_ICON_MIRROR},
815 {"Flip", N_("Flip"), PIXBUF_INLINE_ICON_FLIP},
816 {"AlterNone", N_("Original state"), PIXBUF_INLINE_ICON_ORIGINAL},
817 {"ZoomIn", N_("Zoom in"), GQ_ICON_ZOOM_IN},
818 {"ZoomOut", N_("Zoom out"), GQ_ICON_ZOOM_OUT},
819 {"Zoom100", N_("Zoom 1:1"), GQ_ICON_ZOOM_100},
820 {"ZoomFit", N_("Zoom to fit"), GQ_ICON_ZOOM_FIT},
821 {"ZoomFillHor", N_("Fit Horizontaly"), PIXBUF_INLINE_ICON_ZOOMFILLHOR},
822 {"ZoomFillVert", N_("Fit vertically"), PIXBUF_INLINE_ICON_ZOOMFILLVERT},
823 {"Zoom200", N_("Zoom 2:1"), GQ_ICON_GENERIC},
824 {"Zoom300", N_("Zoom 3:1"), GQ_ICON_GENERIC},
825 {"Zoom400", N_("Zoom 4:1"), GQ_ICON_GENERIC},
826 {"Zoom50", N_("Zoom 1:2"), GQ_ICON_GENERIC},
827 {"Zoom33", N_("Zoom 1:3"), GQ_ICON_GENERIC},
828 {"Zoom25", N_("Zoom 1:4"), GQ_ICON_GENERIC},
829 {"ConnectZoomIn", N_("Connected Zoom in"), GQ_ICON_ZOOM_IN},
830 {"SplitPaneSync", N_("Split Pane Sync"), PIXBUF_INLINE_SPLIT_PANE_SYNC},
831 {"Grayscale", N_("Grayscale"), PIXBUF_INLINE_ICON_GRAYSCALE},
832 {"OverUnderExposed", N_("Over Under Exposed"), PIXBUF_INLINE_ICON_EXPOSURE},
833 {"HideTools", N_("Hide file list"), PIXBUF_INLINE_ICON_HIDETOOLS},
834 {"SlideShowPause", N_("Pause slideshow"), GQ_ICON_PAUSE},
835 {"SlideShowFaster", N_("Slideshow Faster"), GQ_ICON_GENERIC},
836 {"SlideShowSlower", N_("Slideshow Slower"), GQ_ICON_GENERIC},
837 {"Refresh", N_("Refresh"), GQ_ICON_REFRESH},
838 {"HelpContents", N_("Help"), GQ_ICON_HELP},
839 {"ExifWin", N_("Exif window"), PIXBUF_INLINE_ICON_EXIF},
840 {"Thumbnails", N_("Show thumbnails"), PIXBUF_INLINE_ICON_THUMB},
841 {"ShowMarks", N_("Show marks"), PIXBUF_INLINE_ICON_MARKS},
842 {"DrawRectangle", N_("Draw Rectangle"), PIXBUF_INLINE_ICON_DRAW_RECTANGLE},
843 {"FloatTools", N_("Float file list"), PIXBUF_INLINE_ICON_FLOAT},
844 {"SBar", N_("Info sidebar"), PIXBUF_INLINE_ICON_INFO},
845 {"SBarSort", N_("Sort manager"), PIXBUF_INLINE_ICON_SORT},
846 {"Quit", N_("Quit"), GQ_ICON_QUIT},
847 {nullptr, nullptr, nullptr}
850 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
852 auto option = static_cast<gchar **>(data);
855 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
857 const UseableMouseItems *list = useable_mouse_items;
861 if (g_strcmp0(list->label, label) == 0)
869 *option = g_strdup(list->name);
873 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
874 gchar *option, gchar **option_c)
882 pref_table_label(table, column, row, text, 0.0);
884 combo = gtk_combo_box_text_new();
886 const UseableMouseItems *list = useable_mouse_items;
890 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), list->label);
891 if (g_strcmp0(list->name, option) == 0)
899 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
901 g_signal_connect(G_OBJECT(combo), "changed",
902 G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
904 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
905 gtk_widget_show(combo);
908 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
912 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
915 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
917 c_options->thumbnails.max_width = thumb_size_list[n].w;
918 c_options->thumbnails.max_height = thumb_size_list[n].h;
922 c_options->thumbnails.max_width = options->thumbnails.max_width;
923 c_options->thumbnails.max_height = options->thumbnails.max_height;
927 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
933 c_options->thumbnails.max_width = options->thumbnails.max_width;
934 c_options->thumbnails.max_height = options->thumbnails.max_height;
936 pref_table_label(table, column, row, text, 0.0);
938 combo = gtk_combo_box_text_new();
941 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
946 w = thumb_size_list[i].w;
947 h = thumb_size_list[i].h;
949 buf = g_strdup_printf("%d x %d", w, h);
950 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
953 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
960 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
961 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
967 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
968 g_signal_connect(G_OBJECT(combo), "changed",
969 G_CALLBACK(thumb_size_menu_cb), NULL);
971 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
972 gtk_widget_show(combo);
975 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
977 auto option = static_cast<gint *>(data);
979 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
983 *option = PR_STEREO_NONE;
986 *option = PR_STEREO_ANAGLYPH_RC;
989 *option = PR_STEREO_ANAGLYPH_GM;
992 *option = PR_STEREO_ANAGLYPH_YB;
995 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
998 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
1001 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
1004 *option = PR_STEREO_ANAGLYPH_DB_RC;
1007 *option = PR_STEREO_ANAGLYPH_DB_GM;
1010 *option = PR_STEREO_ANAGLYPH_DB_YB;
1013 *option = PR_STEREO_HORIZ;
1016 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
1019 *option = PR_STEREO_VERT;
1022 *option = PR_STEREO_VERT | PR_STEREO_HALF;
1025 *option = PR_STEREO_FIXED;
1030 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1031 gint option, gint *option_c, gboolean add_fixed)
1038 pref_table_label(table, column, row, text, 0.0);
1040 combo = gtk_combo_box_text_new();
1042 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
1044 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
1045 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
1046 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
1047 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
1048 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
1049 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
1050 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
1051 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
1052 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
1053 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
1054 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
1055 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
1056 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
1057 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
1058 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
1059 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
1060 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1061 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1063 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1064 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1065 if (option & PR_STEREO_HORIZ)
1068 if (option & PR_STEREO_HALF) current = 11;
1071 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1072 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1073 if (option & PR_STEREO_VERT)
1076 if (option & PR_STEREO_HALF) current = 13;
1081 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1082 if (option & PR_STEREO_FIXED) current = 14;
1085 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1087 g_signal_connect(G_OBJECT(combo), "changed",
1088 G_CALLBACK(stereo_mode_menu_cb), option_c);
1090 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1091 gtk_widget_show(combo);
1094 static void video_menu_cb(GtkWidget *combo, gpointer data)
1096 auto option = static_cast<gchar **>(data);
1098 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1102 static void video_menu_populate(gpointer data, gpointer user_data)
1104 auto combo = static_cast<GtkWidget *>(user_data);
1105 auto ed = static_cast<EditorDescription *>(data);
1107 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1110 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1111 gchar *option, gchar **option_c)
1115 /* use lists since they are sorted */
1116 GList *eds = editor_list_get();
1120 pref_table_label(table, column, row, text, 0.0);
1122 combo = gtk_combo_box_text_new();
1123 g_list_foreach(eds,video_menu_populate,combo);
1124 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1126 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1128 g_signal_connect(G_OBJECT(combo), "changed",
1129 G_CALLBACK(video_menu_cb), option_c);
1131 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1132 gtk_widget_show(combo);
1135 static void filter_store_populate()
1139 if (!filter_store) return;
1141 gtk_list_store_clear(filter_store);
1143 work = filter_get_list();
1149 fe = static_cast<FilterEntry *>(work->data);
1152 gtk_list_store_append(filter_store, &iter);
1153 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1157 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1159 auto model = static_cast<GtkWidget *>(data);
1160 auto fe = static_cast<FilterEntry *>(data);
1164 if (!new_text || strlen(new_text) < 1) return;
1166 tpath = gtk_tree_path_new_from_string(path_str);
1167 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1168 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1170 g_free(fe->extensions);
1171 fe->extensions = g_strdup(new_text);
1173 gtk_tree_path_free(tpath);
1177 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1179 auto model = static_cast<GtkWidget *>(data);
1180 auto fe = static_cast<FilterEntry *>(data);
1185 if (!new_text || !new_text[0]) return;
1187 tpath = gtk_tree_path_new_from_string(path_str);
1188 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1189 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1191 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1193 if (strcmp(new_text, _(format_class_list[i])) == 0)
1195 fe->file_class = static_cast<FileFormatClass>(i);
1200 gtk_tree_path_free(tpath);
1204 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1206 auto model = static_cast<GtkWidget *>(data);
1211 if (!new_text || !new_text[0]) return;
1213 tpath = gtk_tree_path_new_from_string(path_str);
1214 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1215 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1217 g_free(fe->description);
1218 fe->description = g_strdup(new_text);
1220 gtk_tree_path_free(tpath);
1223 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1225 auto model = static_cast<GtkWidget *>(data);
1230 tpath = gtk_tree_path_new_from_string(path_str);
1231 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1232 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1234 fe->enabled = !fe->enabled;
1236 gtk_tree_path_free(tpath);
1240 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1242 auto model = static_cast<GtkWidget *>(data);
1247 tpath = gtk_tree_path_new_from_string(path_str);
1248 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1249 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1251 fe->writable = !fe->writable;
1252 if (fe->writable) fe->allow_sidecar = FALSE;
1254 gtk_tree_path_free(tpath);
1258 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1260 auto model = static_cast<GtkWidget *>(data);
1265 tpath = gtk_tree_path_new_from_string(path_str);
1266 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1267 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1269 fe->allow_sidecar = !fe->allow_sidecar;
1270 if (fe->allow_sidecar) fe->writable = FALSE;
1272 gtk_tree_path_free(tpath);
1276 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1277 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1281 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1283 switch (GPOINTER_TO_INT(data))
1286 g_object_set(GTK_CELL_RENDERER(cell),
1287 "active", fe->enabled, NULL);
1290 g_object_set(GTK_CELL_RENDERER(cell),
1291 "text", fe->extensions, NULL);
1293 case FE_DESCRIPTION:
1294 g_object_set(GTK_CELL_RENDERER(cell),
1295 "text", fe->description, NULL);
1298 g_object_set(GTK_CELL_RENDERER(cell),
1299 "text", _(format_class_list[fe->file_class]), NULL);
1302 g_object_set(GTK_CELL_RENDERER(cell),
1303 "active", fe->writable, NULL);
1305 case FE_ALLOW_SIDECAR:
1306 g_object_set(GTK_CELL_RENDERER(cell),
1307 "active", fe->allow_sidecar, NULL);
1312 static gboolean filter_add_scroll(gpointer data)
1316 GtkCellRenderer *cell;
1317 GtkTreeViewColumn *column;
1320 GtkTreeModel *store;
1322 FilterEntry *filter;
1324 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1325 path = gtk_tree_path_new_from_indices(rows-1, -1);
1327 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1329 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1330 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1332 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1333 valid = gtk_tree_model_get_iter_first(store, &iter);
1337 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1339 if (g_strcmp0(filter->extensions, ".new") == 0)
1341 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1345 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1348 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1349 path, column, FALSE, 0.0, 0.0 );
1350 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1351 path, column, cell, TRUE);
1353 gtk_tree_path_free(path);
1354 g_list_free(list_cells);
1356 return(G_SOURCE_REMOVE);
1359 static void filter_add_cb(GtkWidget *, gpointer data)
1361 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1362 filter_store_populate();
1364 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1367 static void filter_remove_cb(GtkWidget *, gpointer data)
1369 auto filter_view = static_cast<GtkWidget *>(data);
1370 GtkTreeSelection *selection;
1374 if (!filter_store) return;
1375 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1376 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1377 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1380 filter_remove_entry(fe);
1382 filter_store_populate();
1385 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1389 GtkTreeViewColumn *column;
1391 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1392 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1393 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1395 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1399 gtk_tree_path_free(path);
1401 return(G_SOURCE_REMOVE);
1404 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1407 filter_add_defaults();
1409 filter_store_populate();
1411 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1414 static void dummy_cancel_cb(GenericDialog *, gpointer)
1416 /* no op, only so cancel button appears */
1419 static void filter_default_cb(GtkWidget *widget, gpointer data)
1423 gd = generic_dialog_new(_("Reset filters"),
1424 "reset_filter", widget, TRUE,
1425 dummy_cancel_cb, data);
1426 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Reset filters"),
1427 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1428 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1429 gtk_widget_show(gd->dialog);
1432 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1434 auto frame = static_cast<GtkWidget *>(data);
1436 gtk_widget_set_sensitive(frame,
1437 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1440 static void safe_delete_view_cb(GtkWidget *, gpointer)
1442 layout_set_path(nullptr, gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1445 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1447 file_util_trash_clear();
1450 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1454 gd = generic_dialog_new(_("Clear trash"),
1455 "clear_trash", widget, TRUE,
1456 dummy_cancel_cb, nullptr);
1457 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Clear trash"),
1458 _("This will remove the trash contents."), FALSE);
1459 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1460 entry = gtk_entry_new();
1461 gtk_widget_set_can_focus(entry, FALSE);
1462 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1463 if (options->file_ops.safe_delete_path) gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1464 gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1465 gtk_widget_show(entry);
1466 gtk_widget_show(gd->dialog);
1469 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1471 GtkWidget *pTextView;
1472 GtkTextBuffer *pTextBuffer;
1476 pTextView = GTK_WIDGET(data);
1478 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1479 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1480 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1482 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1483 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1486 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1488 auto text_view = static_cast<GtkTextView *>(data);
1489 GtkTextBuffer *buffer;
1491 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1492 if (!configwindow) return;
1494 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1495 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1498 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1502 gd = generic_dialog_new(_("Reset image overlay template string"),
1503 "reset_image_overlay_template_string", widget, TRUE,
1504 dummy_cancel_cb, data);
1505 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Reset image overlay template string"),
1506 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1507 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1508 gtk_widget_show(gd->dialog);
1511 static void image_overlay_help_cb(GtkWidget *, gpointer)
1513 help_window_show("GuideOptionsOSD.html");
1516 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1520 PangoFontDescription *font_desc;
1522 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1523 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1525 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1527 font_desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog));
1528 font = pango_font_description_to_string(font_desc);
1529 g_free(c_options->image_overlay.font);
1530 c_options->image_overlay.font = g_strdup(font);
1534 gtk_widget_destroy(dialog);
1537 static void image_overlay_set_text_colour_cb(GtkWidget *widget, gpointer)
1542 dialog = gtk_color_chooser_dialog_new("Image Overlay Text Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1543 colour.red = options->image_overlay.text_red;
1544 colour.green = options->image_overlay.text_green;
1545 colour.blue = options->image_overlay.text_blue;
1546 colour.alpha = options->image_overlay.text_alpha;
1547 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1548 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1550 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1552 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1553 c_options->image_overlay.text_red = colour.red*255;
1554 c_options->image_overlay.text_green = colour.green*255;
1555 c_options->image_overlay.text_blue = colour.blue*255;
1556 c_options->image_overlay.text_alpha = colour.alpha*255;
1558 gtk_widget_destroy(dialog);
1562 static void image_overlay_set_background_colour_cb(GtkWidget *widget, gpointer)
1567 dialog = gtk_color_chooser_dialog_new("Image Overlay Background Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1568 colour.red = options->image_overlay.background_red;
1569 colour.green = options->image_overlay.background_green;
1570 colour.blue = options->image_overlay.background_blue;
1571 colour.alpha = options->image_overlay.background_alpha;
1572 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1573 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1575 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1577 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1578 c_options->image_overlay.background_red = colour.red*255;
1579 c_options->image_overlay.background_green = colour.green*255;
1580 c_options->image_overlay.background_blue = colour.blue*255;
1581 c_options->image_overlay.background_alpha = colour.alpha*255;
1583 gtk_widget_destroy(dialog);
1586 static void accel_store_populate()
1589 GList *groups, *actions;
1591 const gchar *accel_path;
1595 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1597 gtk_tree_store_clear(accel_store);
1598 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 */
1600 g_assert(lw && lw->ui_manager);
1601 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1604 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1607 action = GTK_ACTION(actions->data);
1608 accel_path = gtk_action_get_accel_path(action);
1609 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1611 gchar *label, *label2, *tooltip, *accel;
1612 g_object_get(action,
1613 "tooltip", &tooltip,
1617 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1623 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1627 gtk_tree_store_append(accel_store, &iter, nullptr);
1628 gtk_tree_store_set(accel_store, &iter,
1631 AE_TOOLTIP, tooltip ? tooltip : "",
1632 AE_ACCEL, accel_path,
1640 actions = actions->next;
1643 groups = groups->next;
1647 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1652 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1654 auto accel1 = static_cast<gchar *>(data);
1659 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1661 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1662 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1664 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1666 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1667 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1676 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1678 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1682 GtkAccelKey old_key, key;
1683 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1685 gtk_tree_model_get_iter(model, &iter, path);
1686 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1688 /* test if the accelerator can be stored without conflicts*/
1689 gtk_accel_map_lookup_entry(accel_path, &old_key);
1691 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1692 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1693 gtk_accel_map_lookup_entry(accel_path, &key);
1695 /* restore the original for now, the key will be really changed when the changes are confirmed */
1696 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1698 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1699 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1701 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1702 gtk_tree_path_free(path);
1706 static gboolean accel_default_scroll(GtkTreeView *data)
1710 GtkTreeViewColumn *column;
1712 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1713 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1714 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1716 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1720 gtk_tree_path_free(path);
1722 return(G_SOURCE_REMOVE);
1725 static void accel_default_cb(GtkWidget *, gpointer data)
1727 accel_store_populate();
1729 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1732 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1734 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1737 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1740 gchar *accel_path, *accel;
1742 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1743 gtk_accel_map_lookup_entry(accel_path, &key);
1744 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1746 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1748 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1753 static void accel_clear_cb(GtkWidget *, gpointer data)
1755 GtkTreeSelection *selection;
1757 if (!accel_store) return;
1758 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1759 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1762 static void accel_reset_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_reset_selection, nullptr);
1773 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1777 GtkWidget *scrolled;
1778 GtkWidget *viewport;
1780 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
1781 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1782 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1783 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1784 label = gtk_label_new(title);
1785 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1786 gtk_widget_show(scrolled);
1788 viewport = gtk_viewport_new(nullptr, nullptr);
1789 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1790 gtk_container_add(GTK_CONTAINER(scrolled), viewport);
1791 gtk_widget_show(viewport);
1793 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1794 gtk_container_add(GTK_CONTAINER(viewport), vbox);
1795 gtk_widget_show(vbox);
1800 static void cache_standard_cb(GtkWidget *widget, gpointer)
1802 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1804 c_options->thumbnails.spec_standard =TRUE;
1805 c_options->thumbnails.cache_into_dirs = FALSE;
1809 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1811 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1813 c_options->thumbnails.spec_standard =FALSE;
1814 c_options->thumbnails.cache_into_dirs = FALSE;
1818 static void cache_local_cb(GtkWidget *widget, gpointer)
1820 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1822 c_options->thumbnails.cache_into_dirs = TRUE;
1823 c_options->thumbnails.spec_standard =FALSE;
1827 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1829 if (pos == GTK_ENTRY_ICON_PRIMARY)
1831 gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1835 gtk_entry_set_text(GTK_ENTRY(userdata), "");
1839 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1841 gchar *rating_symbol;
1843 if (pos == GTK_ENTRY_ICON_PRIMARY)
1845 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1846 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1847 g_free(rating_symbol);
1851 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1852 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1853 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1857 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1859 gchar *rating_symbol;
1861 if (pos == GTK_ENTRY_ICON_PRIMARY)
1863 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1864 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1865 g_free(rating_symbol);
1869 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1870 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1871 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1875 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1877 auto hbox = static_cast<GtkContainer *>(data);
1878 GString *str = g_string_new(nullptr);
1879 GtkEntry *hex_code_entry;
1880 gchar *hex_code_full;
1883 guint64 hex_value = 0;
1885 list = gtk_container_get_children(hbox);
1887 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1888 hex_code_full = g_strdup(gtk_entry_get_text(hex_code_entry));
1890 hex_code = g_strsplit(hex_code_full, "+", 2);
1891 if (hex_code[0] && hex_code[1])
1893 hex_value = strtoull(hex_code[1], nullptr, 16);
1895 if (!hex_value || hex_value > 0x10FFFF)
1897 hex_value = 0x003F; // Unicode 'Question Mark'
1899 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1900 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1902 g_strfreev(hex_code);
1903 g_string_free(str, TRUE);
1904 g_free(hex_code_full);
1909 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1911 guint64 star_symbol;
1913 star_symbol = star_rating_symbol_test(widget, data);
1914 c_options->star_rating.star = star_symbol;
1917 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1919 guint64 rejected_symbol;
1921 rejected_symbol = star_rating_symbol_test(widget, data);
1922 c_options->star_rating.rejected = rejected_symbol;
1925 /* general options tab */
1926 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1930 GCancellable *cancellable;
1932 GtkWidget *progress;
1934 GFile *timezone_database_gq;
1935 gchar *timezone_database_user;
1938 static void config_tab_general(GtkWidget *notebook)
1943 GtkWidget *group_frame;
1944 GtkWidget *subgroup;
1946 GtkWidget *ct_button;
1949 gint hours, minutes, remainder;
1951 GtkWidget *star_rating_entry;
1953 gchar *rating_symbol;
1956 gchar *download_locn;
1957 GNetworkMonitor *net_mon;
1958 GSocketConnectable *tz_org;
1959 gboolean internet_available = FALSE;
1962 vbox = scrolled_notebook_page(notebook, _("General"));
1964 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1966 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1967 add_thumb_size_menu(table, 0, 0, _("Size:"));
1968 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1970 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1971 pref_label_new(hbox, _("Custom size: "));
1972 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1973 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1975 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1976 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1978 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1979 pref_checkbox_link_sensitivity(ct_button, subgroup);
1981 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1982 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1983 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1984 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1985 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
1986 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1987 G_CALLBACK(cache_geeqie_cb), nullptr);
1989 group_frame = pref_frame_new(subgroup, TRUE,
1990 _("Store thumbnails local to image folder (non-standard)"),
1991 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1992 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
1993 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
1994 G_CALLBACK(cache_local_cb), nullptr);
1996 group_frame = pref_frame_new(subgroup, TRUE,
1997 _("Use standard thumbnail style and cache, shared with other applications"),
1998 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1999 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2000 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2001 G_CALLBACK(cache_standard_cb), nullptr);
2003 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2004 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2006 pref_checkbox_new_int(group, _("Thumbnail color management"),
2007 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2009 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2011 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2012 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2014 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
2015 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2016 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2019 pref_spacer(group, PREF_PAD_GROUP);
2021 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2023 c_options->star_rating.star = options->star_rating.star;
2024 c_options->star_rating.rejected = options->star_rating.rejected;
2026 str = g_string_new(nullptr);
2027 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2028 pref_label_new(hbox, _("Star character: "));
2029 str = g_string_append_unichar(str, options->star_rating.star);
2030 pref_label_new(hbox, g_strdup(str->str));
2031 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2032 star_rating_entry = gtk_entry_new();
2033 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2034 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2035 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2036 gtk_widget_show(star_rating_entry);
2037 button = pref_button_new(nullptr, nullptr, _("Set"),
2038 G_CALLBACK(star_rating_star_test_cb), hbox);
2039 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2040 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2041 gtk_widget_show(button);
2042 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."));
2043 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2044 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2045 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2046 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2047 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2048 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2049 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2050 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2051 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2052 G_CALLBACK(star_rating_star_icon_cb),
2055 g_string_free(str, TRUE);
2056 g_free(rating_symbol);
2058 str = g_string_new(nullptr);
2059 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2060 pref_label_new(hbox, _("Rejected character: "));
2061 str = g_string_append_unichar(str, options->star_rating.rejected);
2062 pref_label_new(hbox, g_strdup(str->str));
2063 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2064 star_rating_entry = gtk_entry_new();
2065 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2066 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2067 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2068 gtk_widget_show(star_rating_entry);
2069 button = pref_button_new(nullptr, nullptr, _("Set"),
2070 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2071 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2072 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2073 gtk_widget_show(button);
2074 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."));
2075 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2076 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2077 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2078 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2079 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2080 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2081 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2082 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2083 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2084 G_CALLBACK(star_rating_rejected_icon_cb),
2087 g_string_free(str, TRUE);
2088 g_free(rating_symbol);
2090 pref_spacer(group, PREF_PAD_GROUP);
2092 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2094 c_options->slideshow.delay = options->slideshow.delay;
2095 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2096 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2097 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2098 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2099 SLIDESHOW_SUBSECOND_PRECISION;
2101 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2103 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2105 options->slideshow.delay ? hours : 0.0,
2106 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2107 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2108 spin = pref_spin_new(hbox, ":" , nullptr,
2110 options->slideshow.delay ? minutes: 0.0,
2111 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2112 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2113 spin = pref_spin_new(hbox, ":", nullptr,
2114 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2115 options->slideshow.delay ? seconds : 10.0,
2116 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2117 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2119 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2120 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2122 pref_spacer(group, PREF_PAD_GROUP);
2124 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2126 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2127 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2128 pref_checkbox_new_int(group, _("Preload next image"),
2129 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2131 pref_checkbox_new_int(group, _("Refresh on file change"),
2132 options->update_on_time_change, &c_options->update_on_time_change);
2135 pref_spacer(group, PREF_PAD_GROUP);
2137 group = pref_group_new(vbox, FALSE, _("Expand menu and toolbar"), GTK_ORIENTATION_VERTICAL);
2139 pref_checkbox_new_int(group, _("Expand menu and toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2140 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2141 gtk_widget_set_tooltip_text(group, _("Expand the menu and toolbar to the full width of the window"));
2143 pref_spacer(group, PREF_PAD_GROUP);
2145 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2147 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2148 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2149 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2150 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"));
2152 pref_spacer(group, PREF_PAD_GROUP);
2156 net_mon = g_network_monitor_get_default();
2157 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2160 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2161 g_object_unref(tz_org);
2164 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2165 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2167 if (!internet_available)
2169 gtk_widget_set_sensitive(group, FALSE);
2172 tz = g_new0(TZData, 1);
2174 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2175 basename = g_path_get_basename(path);
2176 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2180 if (isfile(tz->timezone_database_user))
2182 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2186 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2189 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2190 pref_label_new(GTK_WIDGET(hbox), download_locn);
2191 g_free(download_locn);
2193 if (!internet_available)
2195 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"));
2199 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"));
2201 gtk_widget_show(button);
2203 pref_spacer(group, PREF_PAD_GROUP);
2205 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2207 help_search_engine_entry = gtk_entry_new();
2208 gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2209 gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2210 gtk_widget_show(help_search_engine_entry);
2212 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"));
2214 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2215 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2216 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2217 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2218 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2219 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2220 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2221 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2222 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2223 G_CALLBACK(help_search_engine_entry_icon_cb),
2224 help_search_engine_entry);
2228 static void config_tab_image(GtkWidget *notebook)
2233 GtkWidget *ct_button;
2234 GtkWidget *enlargement_button;
2238 vbox = scrolled_notebook_page(notebook, _("Image"));
2240 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2242 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2243 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2245 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2246 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2248 c_options->image.zoom_increment = options->image.zoom_increment;
2249 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2250 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2251 G_CALLBACK(zoom_increment_cb), nullptr);
2252 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2254 c_options->image.zoom_style = options->image.zoom_style;
2255 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2256 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2258 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2260 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2261 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2262 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2263 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2265 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2266 pref_checkbox_link_sensitivity(enlargement_button, spin);
2267 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."));
2269 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2270 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2271 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2272 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2274 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2275 pref_checkbox_link_sensitivity(ct_button, spin);
2276 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."));
2278 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2280 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2281 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2283 options->image.tile_size, &c_options->image.tile_size);
2284 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."));
2286 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2288 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2289 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2291 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2292 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2294 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2295 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2297 c_options->image.border_color = options->image.border_color;
2299 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2300 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2302 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2303 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2305 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2306 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2311 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2313 LayoutWindow *lw = nullptr;
2314 gchar *default_path;
2317 /* Get current lw */
2320 tmp_id = lw->options.id;
2321 lw->options.id = g_strdup("lw_default");
2323 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2324 save_default_layout_options_to_file(default_path, options, lw);
2325 g_free(lw->options.id);
2326 lw->options.id = tmp_id;
2327 g_free(default_path);
2330 static gboolean popover_cb(gpointer data)
2332 auto popover = static_cast<GtkPopover *>(data);
2334 gtk_popover_popdown(popover);
2339 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2341 gtk_popover_popup(popover);
2343 g_timeout_add(2000, popover_cb, popover);
2346 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2350 popover = gtk_popover_new(parent);
2351 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2352 gtk_container_add (GTK_CONTAINER(popover), child);
2353 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2354 gtk_widget_show (child);
2359 static void config_tab_windows(GtkWidget *notebook)
2364 GtkWidget *subgroup;
2366 GtkWidget *ct_button;
2369 vbox = scrolled_notebook_page(notebook, _("Windows"));
2371 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2373 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2374 options->save_window_positions, &c_options->save_window_positions);
2376 button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2377 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2378 pref_checkbox_link_sensitivity(ct_button, button);
2380 button = pref_checkbox_new_int(group, _("Remember window workspace"),
2381 options->save_window_workspace, &c_options->save_window_workspace);
2382 pref_checkbox_link_sensitivity(ct_button, button);
2384 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2385 options->tools_restore_state, &c_options->tools_restore_state);
2387 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2388 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2390 pref_checkbox_new_int(group, _("Show window IDs"),
2391 options->show_window_ids, &c_options->show_window_ids);
2393 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2394 pref_label_new(subgroup, _("Use current layout for default: "));
2395 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2399 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2400 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2401 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2403 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2405 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2406 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2408 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2409 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2410 options->image.limit_window_size, &c_options->image.limit_window_size);
2411 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2413 options->image.max_window_size, &c_options->image.max_window_size);
2414 pref_checkbox_link_sensitivity(ct_button, spin);
2416 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2418 c_options->fullscreen.screen = options->fullscreen.screen;
2419 c_options->fullscreen.above = options->fullscreen.above;
2420 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2421 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2422 gtk_widget_show(hbox);
2424 pref_checkbox_new_int(group, _("Smooth image flip"),
2425 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2426 pref_checkbox_new_int(group, _("Disable screen saver"),
2427 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2430 #define PRE_FORMATTED_COLUMNS 5
2431 static void config_tab_osd(GtkWidget *notebook)
2437 GtkWidget *image_overlay_template_view;
2438 GtkWidget *scrolled;
2439 GtkWidget *scrolled_pre_formatted;
2440 GtkTextBuffer *buffer;
2442 GtkWidget *subgroup;
2444 vbox = scrolled_notebook_page(notebook, _("OSD"));
2446 image_overlay_template_view = gtk_text_view_new();
2448 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2450 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2452 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2453 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2454 gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2455 gtk_widget_show(scrolled_pre_formatted);
2456 gtk_widget_show(subgroup);
2458 pref_line(group, PREF_PAD_GAP);
2460 pref_label_new(group, _("Image overlay template"));
2462 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2463 gtk_widget_set_size_request(scrolled, 200, 150);
2464 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2465 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2466 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2467 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2468 gtk_widget_show(scrolled);
2470 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2471 _("Extensive formatting options are shown in the Help file"));
2473 gtk_container_add(GTK_CONTAINER(scrolled), image_overlay_template_view);
2474 gtk_widget_show(image_overlay_template_view);
2476 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2478 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2479 G_CALLBACK(image_overlay_set_font_cb), notebook);
2481 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2482 gtk_widget_show(button);
2484 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"),
2485 G_CALLBACK(image_overlay_set_text_colour_cb), nullptr);
2486 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2487 gtk_widget_show(button);
2489 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"),
2490 G_CALLBACK(image_overlay_set_background_colour_cb), nullptr);
2491 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2492 gtk_widget_show(button);
2493 image_overlay_set_text_colours();
2495 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2496 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2497 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2498 gtk_widget_show(button);
2500 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2501 G_CALLBACK(image_overlay_help_cb), nullptr);
2502 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2503 gtk_widget_show(button);
2505 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2506 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2507 g_signal_connect(G_OBJECT(buffer), "changed",
2508 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2510 pref_line(group, PREF_PAD_GAP);
2512 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2513 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2514 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2515 gtk_widget_show(hbox);
2516 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2517 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2518 gtk_widget_show(label);
2519 pref_spacer(group,TRUE);
2521 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2522 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2523 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2524 gtk_widget_show(hbox);
2525 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2526 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2527 gtk_widget_show(label);
2528 pref_spacer(group,TRUE);
2530 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2531 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2532 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2533 gtk_widget_show(hbox);
2534 label = gtk_label_new(_("%path:39%"));
2535 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2536 gtk_widget_show(label);
2537 pref_spacer(group,TRUE);
2539 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2540 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2541 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2542 gtk_widget_show(hbox);
2543 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%"));
2544 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2545 gtk_widget_show(label);
2546 pref_spacer(group,TRUE);
2548 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2549 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2550 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2551 gtk_widget_show(hbox);
2552 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2553 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2554 gtk_widget_show(label);
2557 static GtkTreeModel *create_class_model()
2559 GtkListStore *model;
2563 /* create list store */
2564 model = gtk_list_store_new(1, G_TYPE_STRING);
2565 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2567 gtk_list_store_append(model, &iter);
2568 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2570 return GTK_TREE_MODEL (model);
2575 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2577 gint n = GPOINTER_TO_INT(data);
2579 FilterEntry *filter_a;
2580 FilterEntry *filter_b;
2582 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2583 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2587 case FILETYPES_COLUMN_ENABLED:
2589 ret = filter_a->enabled - filter_b->enabled;
2592 case FILETYPES_COLUMN_FILTER:
2594 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2597 case FILETYPES_COLUMN_DESCRIPTION:
2599 ret = g_utf8_collate(filter_a->description, filter_b->description);
2602 case FILETYPES_COLUMN_CLASS:
2604 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2607 case FILETYPES_COLUMN_WRITABLE:
2609 ret = filter_a->writable - filter_b->writable;
2612 case FILETYPES_COLUMN_SIDECAR:
2614 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2618 g_return_val_if_reached(0);
2624 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2627 gboolean ret = TRUE;
2629 gtk_tree_model_get(model, iter, 0, &fe, -1);
2631 if (g_strstr_len(fe->extensions, -1, key))
2639 static void config_tab_files(GtkWidget *notebook)
2646 GtkWidget *ct_button;
2647 GtkWidget *scrolled;
2648 GtkWidget *filter_view;
2649 GtkCellRenderer *renderer;
2650 GtkTreeSelection *selection;
2651 GtkTreeViewColumn *column;
2653 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2655 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2657 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2658 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2659 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2660 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2661 pref_checkbox_new_int(group, _("Case sensitive sort"),
2662 options->file_sort.case_sensitive, &c_options->file_sort.case_sensitive);
2663 pref_checkbox_new_int(group, _("Natural sort order - Requires restart"),
2664 options->file_sort.natural, &c_options->file_sort.natural);
2665 pref_checkbox_new_int(group, _("Disable file extension checks"),
2666 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2668 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2669 options->file_filter.disable, &c_options->file_filter.disable);
2672 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2674 sidecar_ext_entry = gtk_entry_new();
2675 gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2676 gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2677 gtk_widget_show(sidecar_ext_entry);
2679 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2681 frame = pref_group_parent(group);
2682 g_signal_connect(G_OBJECT(ct_button), "toggled",
2683 G_CALLBACK(filter_disable_cb), frame);
2684 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2686 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2687 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2688 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2689 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2690 gtk_widget_show(scrolled);
2692 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2693 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2694 g_object_unref(filter_store);
2695 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2696 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2698 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2700 column = gtk_tree_view_column_new();
2701 gtk_tree_view_column_set_title(column, _("Enabled"));
2702 gtk_tree_view_column_set_resizable(column, TRUE);
2704 renderer = gtk_cell_renderer_toggle_new();
2705 g_signal_connect(G_OBJECT(renderer), "toggled",
2706 G_CALLBACK(filter_store_enable_cb), filter_store);
2707 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2708 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2709 GINT_TO_POINTER(FE_ENABLE), nullptr);
2710 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);
2711 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2712 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2714 column = gtk_tree_view_column_new();
2715 gtk_tree_view_column_set_title(column, _("Filter"));
2716 gtk_tree_view_column_set_resizable(column, TRUE);
2717 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);
2718 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2720 renderer = gtk_cell_renderer_text_new();
2721 g_signal_connect(G_OBJECT(renderer), "edited",
2722 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2723 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2724 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2725 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2726 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2727 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2729 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2730 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2731 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2733 column = gtk_tree_view_column_new();
2734 gtk_tree_view_column_set_title(column, _("Description"));
2735 gtk_tree_view_column_set_resizable(column, TRUE);
2736 gtk_tree_view_column_set_fixed_width(column, 200);
2737 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2739 renderer = gtk_cell_renderer_text_new();
2740 g_signal_connect(G_OBJECT(renderer), "edited",
2741 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2742 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2743 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2744 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2745 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2746 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2747 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);
2748 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2750 column = gtk_tree_view_column_new();
2751 gtk_tree_view_column_set_title(column, _("Class"));
2752 gtk_tree_view_column_set_resizable(column, TRUE);
2753 renderer = gtk_cell_renderer_combo_new();
2754 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2755 "model", create_class_model(),
2760 g_signal_connect(G_OBJECT(renderer), "edited",
2761 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2762 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2763 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2764 GINT_TO_POINTER(FE_CLASS), nullptr);
2765 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2766 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);
2767 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2769 column = gtk_tree_view_column_new();
2770 gtk_tree_view_column_set_title(column, _("Writable"));
2771 gtk_tree_view_column_set_resizable(column, FALSE);
2772 renderer = gtk_cell_renderer_toggle_new();
2773 g_signal_connect(G_OBJECT(renderer), "toggled",
2774 G_CALLBACK(filter_store_writable_cb), filter_store);
2775 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2776 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2777 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2778 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2779 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);
2780 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2782 column = gtk_tree_view_column_new();
2783 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2784 gtk_tree_view_column_set_resizable(column, FALSE);
2785 renderer = gtk_cell_renderer_toggle_new();
2786 g_signal_connect(G_OBJECT(renderer), "toggled",
2787 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2788 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2789 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2790 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2791 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2792 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);
2793 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2795 filter_store_populate();
2796 gtk_container_add(GTK_CONTAINER(scrolled), filter_view);
2797 gtk_widget_show(filter_view);
2799 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2801 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2802 G_CALLBACK(filter_default_cb), filter_view);
2803 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2804 gtk_widget_show(button);
2806 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2807 G_CALLBACK(filter_remove_cb), filter_view);
2808 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2809 gtk_widget_show(button);
2811 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2812 G_CALLBACK(filter_add_cb), filter_view);
2813 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2814 gtk_widget_show(button);
2818 static void config_tab_metadata(GtkWidget *notebook)
2823 GtkWidget *ct_button;
2825 GtkWidget *tmp_widget;
2827 GtkWidget *text_label;
2829 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2832 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2834 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2836 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."));
2837 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2838 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2840 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2842 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2843 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2844 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"));
2845 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2847 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)"));
2848 gtk_widget_set_tooltip_markup(ct_button, markup);
2852 gtk_widget_set_sensitive(ct_button, FALSE);
2855 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2856 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2857 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)"));
2858 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2861 label = pref_label_new(group, "");
2862 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(), "/");
2863 gtk_label_set_markup (GTK_LABEL(label), markup);
2866 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2867 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2868 gtk_misc_set_padding(GTK_MISC(label), 22, 0);
2869 pref_spacer(group, PREF_PAD_GROUP);
2871 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2873 gtk_widget_set_sensitive(group, FALSE);
2876 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2877 pref_checkbox_link_sensitivity(ct_button, hbox);
2879 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);
2880 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2882 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);
2884 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2886 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2887 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2888 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2889 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", ")");
2890 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2893 pref_spacer(group, PREF_PAD_GROUP);
2895 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2897 gtk_widget_set_sensitive(group, FALSE);
2900 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);
2902 pref_spacer(group, PREF_PAD_GROUP);
2904 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2905 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);
2906 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2908 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2909 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2911 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2912 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"));
2915 gtk_widget_set_sensitive(ct_button, FALSE);
2918 pref_spacer(group, PREF_PAD_GROUP);
2920 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2922 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2924 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2925 pref_checkbox_link_sensitivity(ct_button, hbox);
2927 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2929 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2931 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2933 pref_spacer(group, PREF_PAD_GROUP);
2936 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2938 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2939 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2942 pref_spacer(group, PREF_PAD_GROUP);
2944 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2946 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2947 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");
2952 struct KeywordFindData
2959 GtkWidget *button_close;
2960 GtkWidget *button_stop;
2961 GtkWidget *button_start;
2962 GtkWidget *progress;
2970 guint idle_id; /* event source id */
2973 #define KEYWORD_DIALOG_WIDTH 400
2975 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
2977 GList *list_d = nullptr;
2978 GList *list_f = nullptr;
2982 filelist_read(dir_fd, &list_f, &list_d);
2986 filelist_read(dir_fd, &list_f, nullptr);
2989 list_f = filelist_filter(list_f, FALSE);
2990 list_d = filelist_filter(list_d, TRUE);
2992 kfd->list = g_list_concat(list_f, kfd->list);
2993 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
2996 static void keywords_find_reset(KeywordFindData *kfd)
2998 filelist_free(kfd->list);
2999 kfd->list = nullptr;
3001 filelist_free(kfd->list_dir);
3002 kfd->list_dir = nullptr;
3005 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3007 auto kfd = static_cast<KeywordFindData *>(data);
3009 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3011 keywords_find_reset(kfd);
3012 generic_dialog_close(kfd->gd);
3016 static void keywords_find_finish(KeywordFindData *kfd)
3018 keywords_find_reset(kfd);
3020 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3021 spinner_set_interval(kfd->spinner, -1);
3023 gtk_widget_set_sensitive(kfd->group, TRUE);
3024 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3025 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3026 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3029 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3031 auto kfd = static_cast<KeywordFindData *>(data);
3033 g_idle_remove_by_data(kfd);
3035 keywords_find_finish(kfd);
3038 static gboolean keywords_find_file(gpointer data)
3040 auto kfd = static_cast<KeywordFindData *>(data);
3042 GtkTextBuffer *buffer;
3050 fd = static_cast<FileData *>(kfd->list->data);
3051 kfd->list = g_list_remove(kfd->list, fd);
3053 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3054 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3058 gtk_text_buffer_get_end_iter(buffer, &iter);
3059 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3060 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3062 keywords = keywords->next;
3065 gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3066 file_data_unref(fd);
3067 g_list_free_full(keywords, g_free);
3069 return (G_SOURCE_CONTINUE);
3071 else if (kfd->list_dir)
3075 fd = static_cast<FileData *>(kfd->list_dir->data);
3076 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3078 keywords_find_folder(kfd, fd);
3080 file_data_unref(fd);
3082 return G_SOURCE_CONTINUE;
3085 keywords_find_finish(kfd);
3087 return G_SOURCE_REMOVE;
3090 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3092 auto kfd = static_cast<KeywordFindData *>(data);
3095 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3097 path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3098 parse_out_relatives(path);
3102 warning_dialog(_("Invalid folder"),
3103 _("The specified folder can not be found."),
3104 GTK_STOCK_DIALOG_WARNING, kfd->gd->dialog);
3110 gtk_widget_set_sensitive(kfd->group, FALSE);
3111 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3112 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3113 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3114 spinner_set_interval(kfd->spinner, SPINNER_SPEED);
3116 dir_fd = file_data_new_dir(path);
3117 keywords_find_folder(kfd, dir_fd);
3118 file_data_unref(dir_fd);
3119 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3125 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3127 KeywordFindData *kfd;
3131 kfd = g_new0(KeywordFindData, 1);
3133 kfd->gd = generic_dialog_new(_("Search for keywords"),
3134 "search_for_keywords",
3137 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3138 kfd->gd->cancel_cb = keywords_find_close_cb;
3139 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3140 keywords_find_close_cb, FALSE);
3141 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3142 keywords_find_start_cb, FALSE);
3143 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3144 keywords_find_stop_cb, FALSE);
3145 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3147 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3149 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3150 pref_spacer(hbox, PREF_PAD_INDENT);
3151 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3153 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3154 pref_label_new(hbox, _("Folder:"));
3156 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3157 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3158 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3159 gtk_widget_show(label);
3161 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3163 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3164 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3166 kfd->progress = gtk_entry_new();
3167 gtk_widget_set_can_focus(kfd->progress, FALSE);
3168 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3169 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3170 gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3171 gtk_widget_show(kfd->progress);
3173 kfd->spinner = spinner_new(nullptr, -1);
3174 gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3175 gtk_widget_show(kfd->spinner);
3177 kfd->list = nullptr;
3179 gtk_widget_show(kfd->gd->dialog);
3182 static void keywords_find_cb(GtkWidget *widget, gpointer)
3184 const gchar *path = layout_get_path(nullptr);
3186 if (!path || !*path) path = homedir();
3187 keywords_find_dialog(widget, path);
3190 static void config_tab_keywords_save()
3192 GtkTextIter start, end;
3193 GtkTextBuffer *buffer;
3194 GList *kw_list = nullptr;
3200 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3201 gtk_text_buffer_get_bounds(buffer, &start, &end);
3203 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3205 kw_split = strtok(buffer_text, "\n");
3206 while (kw_split != nullptr)
3212 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3221 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3223 kw_split = strtok(nullptr, "\n");
3226 keyword_list_set(kw_list);
3228 g_list_free_full(kw_list, g_free);
3229 g_free(buffer_text);
3232 static void config_tab_keywords(GtkWidget *notebook)
3238 GtkWidget *scrolled;
3240 GtkTextBuffer *buffer;
3243 GspellTextView *gspell_view;
3246 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3248 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3250 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3252 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3253 G_CALLBACK(keywords_find_cb), keyword_text);
3254 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3257 keyword_text = gtk_text_view_new();
3258 gtk_widget_set_size_request(keyword_text, 20, 20);
3259 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3260 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3261 gtk_widget_show(scrolled);
3264 if (options->metadata.check_spelling)
3266 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3267 gspell_text_view_basic_setup(gspell_view);
3271 gtk_container_add(GTK_CONTAINER(scrolled), keyword_text);
3272 gtk_widget_show(keyword_text);
3274 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3276 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3277 gtk_text_buffer_create_tag(buffer, "monospace",
3278 "family", "monospace", NULL);
3280 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3281 gtk_text_buffer_get_start_iter(buffer, &iter);
3282 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3285 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3287 GList *kwl = keyword_list_get();
3288 kwl = g_list_first(kwl);
3291 gtk_text_buffer_get_end_iter (buffer, &iter);
3292 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3293 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3298 gtk_text_buffer_set_modified(buffer, FALSE);
3305 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3307 auto option = static_cast<gint *>(data);
3309 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3313 *option = INTENT_PERCEPTUAL;
3316 *option = INTENT_RELATIVE_COLORIMETRIC;
3319 *option = INTENT_SATURATION;
3322 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3327 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3328 gint option, gint *option_c)
3335 pref_table_label(table, column, row, text, 0.0);
3337 combo = gtk_combo_box_text_new();
3339 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3340 if (option == INTENT_PERCEPTUAL) current = 0;
3341 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3342 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3343 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3344 if (option == INTENT_SATURATION) current = 2;
3345 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3346 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3348 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3350 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3352 g_signal_connect(G_OBJECT(combo), "changed",
3353 G_CALLBACK(intent_menu_cb), option_c);
3355 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3356 gtk_widget_show(combo);
3360 static void config_tab_color(GtkWidget *notebook)
3369 vbox = scrolled_notebook_page(notebook, _("Color management"));
3371 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3373 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3376 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3377 gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
3379 label = pref_table_label(table, 0, 0, _("Type"), 0.0);
3380 pref_label_bold(label, TRUE, FALSE);
3382 label = pref_table_label(table, 1, 0, _("Menu name"), 0.0);
3383 pref_label_bold(label, TRUE, FALSE);
3385 label = pref_table_label(table, 2, 0, _("File"), 0.0);
3386 pref_label_bold(label, TRUE, FALSE);
3388 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3393 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3394 pref_table_label(table, 0, i + 1, buf, 1.0);
3397 entry = gtk_entry_new();
3398 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3399 if (options->color_profile.input_name[i])
3401 gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3403 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
3404 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3405 gtk_widget_show(entry);
3406 color_profile_input_name_entry[i] = entry;
3408 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3409 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3410 gtk_widget_set_size_request(entry, 160, -1);
3411 gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
3412 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3413 gtk_widget_show(tabcomp);
3414 color_profile_input_file_entry[i] = entry;
3417 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3419 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3421 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3422 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3424 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3426 pref_table_label(table, 0, 0, _("Screen:"), 1.0);
3427 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3428 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3429 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3430 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3432 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3434 gtk_table_attach(GTK_TABLE(table), tabcomp, 1, 2,
3436 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3438 gtk_widget_show(tabcomp);
3441 /* advanced entry tab */
3442 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3444 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3446 c_options->file_ops.use_system_trash = FALSE;
3447 c_options->file_ops.no_trash = FALSE;
3451 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3453 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3455 c_options->file_ops.use_system_trash = TRUE;
3456 c_options->file_ops.no_trash = FALSE;
3460 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3462 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3464 c_options->file_ops.no_trash = TRUE;
3468 static void config_tab_behavior(GtkWidget *notebook)
3475 GtkWidget *ct_button;
3479 GtkWidget *with_rename;
3480 GtkWidget *collections_on_top;
3481 GtkWidget *hide_window_in_fullscreen;
3482 GtkWidget *checkbox;
3485 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3487 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3489 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3490 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3491 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3492 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3493 pref_checkbox_new_int(group, _("Enable Delete key"),
3494 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3496 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3497 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3499 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3500 pref_checkbox_link_sensitivity(ct_button, hbox);
3502 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3503 pref_label_new(hbox, _("Folder:"));
3505 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3506 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3507 gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3508 gtk_widget_show(tabcomp);
3510 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3511 pref_checkbox_link_sensitivity(ct_button, hbox);
3513 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3514 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3515 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3516 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3517 button = pref_button_new(nullptr, nullptr, _("View"),
3518 G_CALLBACK(safe_delete_view_cb), nullptr);
3519 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3520 gtk_widget_show(button);
3522 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3523 G_CALLBACK(safe_delete_clear_cb), nullptr);
3524 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3526 c_options->file_ops.no_trash = options->file_ops.no_trash;
3527 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3529 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3530 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3532 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3533 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3535 gtk_widget_show(button);
3537 pref_spacer(group, PREF_PAD_GROUP);
3540 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3542 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3543 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3545 pref_checkbox_new_int(group, _("In place renaming"),
3546 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3548 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3549 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3551 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3552 options->circular_selection_lists, &c_options->circular_selection_lists);
3553 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3555 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3556 options->marks_save, &c_options->marks_save);
3557 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3559 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3560 options->with_rename, &c_options->with_rename);
3561 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3563 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3564 options->collections_on_top, &c_options->collections_on_top);
3565 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3567 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3568 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3569 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3571 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3572 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3574 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);
3575 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."));
3577 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3578 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3580 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3581 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3583 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3584 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3586 pref_spacer(group, PREF_PAD_GROUP);
3588 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3590 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3591 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3592 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3593 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3594 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3595 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3596 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3597 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3598 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3599 options->image_l_click_archive, &c_options->image_l_click_archive);
3600 pref_checkbox_new_int(group, _("Play video by left click on image"),
3601 options->image_l_click_video, &c_options->image_l_click_video);
3602 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3603 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3605 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3606 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3607 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3608 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3609 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3611 pref_spacer(group, PREF_PAD_GROUP);
3613 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3615 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3616 options->override_disable_gpu, &c_options->override_disable_gpu);
3617 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3620 pref_spacer(group, PREF_PAD_GROUP);
3622 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3624 pref_spin_new_int(group, _("Debug level:"), nullptr,
3625 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3627 pref_checkbox_new_int(group, _("Timer data"),
3628 options->log_window.timer_data, &c_options->log_window.timer_data);
3630 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3631 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3635 /* accelerators tab */
3637 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3639 gboolean ret = TRUE;
3644 gtk_tree_model_get(model, iter, column, &text, -1);
3645 text_nocase = g_utf8_casefold(text, -1);
3646 key_nocase = g_utf8_casefold(key, -1);
3648 if (g_strstr_len(text_nocase, -1, key_nocase))
3655 g_free(text_nocase);
3660 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3665 list = gtk_tree_view_get_columns(tree_view);
3666 col_num = g_list_index(list, column);
3670 gtk_tree_view_set_search_column(tree_view, col_num);
3673 static void config_tab_accelerators(GtkWidget *notebook)
3679 GtkWidget *scrolled;
3680 GtkWidget *accel_view;
3681 GtkCellRenderer *renderer;
3682 GtkTreeSelection *selection;
3683 GtkTreeViewColumn *column;
3685 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3687 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3689 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3690 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3691 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3692 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3693 gtk_widget_show(scrolled);
3695 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3697 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3698 g_object_unref(accel_store);
3699 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3700 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3702 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3704 renderer = gtk_cell_renderer_text_new();
3706 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3711 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3712 gtk_tree_view_column_set_resizable(column, TRUE);
3713 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3716 renderer = gtk_cell_renderer_accel_new();
3717 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3718 G_CALLBACK(accel_store_cleared_cb), accel_store);
3719 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3720 G_CALLBACK(accel_store_edited_cb), accel_store);
3723 g_object_set (renderer,
3725 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3728 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3733 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3734 gtk_tree_view_column_set_resizable(column, TRUE);
3735 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3737 renderer = gtk_cell_renderer_text_new();
3739 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3744 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3745 gtk_tree_view_column_set_resizable(column, TRUE);
3746 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3748 renderer = gtk_cell_renderer_text_new();
3750 column = gtk_tree_view_column_new_with_attributes("Accel",
3755 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3756 gtk_tree_view_column_set_resizable(column, TRUE);
3757 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3759 /* Search on text in column */
3760 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3761 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3762 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3763 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3764 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3766 accel_store_populate();
3767 gtk_container_add(GTK_CONTAINER(scrolled), accel_view);
3768 gtk_widget_show(accel_view);
3770 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3772 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3773 G_CALLBACK(accel_default_cb), accel_view);
3774 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3775 gtk_widget_show(button);
3777 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3778 G_CALLBACK(accel_reset_cb), accel_view);
3779 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3780 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3781 gtk_widget_show(button);
3783 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3784 G_CALLBACK(accel_clear_cb), accel_view);
3785 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3786 gtk_widget_show(button);
3789 /* toolbar main tab */
3790 static void config_tab_toolbar_main(GtkWidget *notebook)
3793 GtkWidget *toolbardata;
3796 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3798 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3800 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3801 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3802 gtk_widget_show(vbox);
3805 /* toolbar status tab */
3806 static void config_tab_toolbar_status(GtkWidget *notebook)
3809 GtkWidget *toolbardata;
3812 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3814 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3816 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3817 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3818 gtk_widget_show(vbox);
3822 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3824 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3827 static void config_tab_advanced(GtkWidget *notebook)
3831 GSList *formats_list;
3832 GList *extensions_list = nullptr;
3835 GdkPixbufFormat *fm;
3837 GString *types_string = g_string_new(nullptr);
3838 GtkWidget *types_string_label;
3839 GtkWidget *threads_string_label;
3840 GtkWidget *dupes_threads_spin;
3842 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3843 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3845 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3847 pref_spacer(group, PREF_PAD_GROUP);
3849 formats_list = gdk_pixbuf_get_formats();
3851 while (formats_list)
3853 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3854 extensions = gdk_pixbuf_format_get_extensions(fm);
3857 while (extensions[i])
3859 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3863 g_strfreev(extensions);
3864 formats_list = formats_list->next;
3867 while (extensions_list)
3869 if (types_string->len == 0)
3871 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3875 types_string = g_string_append(types_string, ", ");
3876 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3879 extensions_list = extensions_list->next;
3882 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3883 types_string_label = pref_label_new(group, types_string->str);
3884 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3886 pref_spacer(group, PREF_PAD_GROUP);
3888 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3889 external_preview_select_entry = gtk_entry_new();
3890 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3891 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3892 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3893 gtk_widget_show(tabcomp);
3895 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3896 external_preview_extract_entry = gtk_entry_new();
3897 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3898 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3899 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3900 gtk_widget_show(tabcomp);
3902 gtk_widget_show(vbox);
3904 g_slist_free(formats_list);
3905 g_list_free_full(extensions_list, g_free);
3906 g_string_free(types_string, TRUE);
3908 pref_spacer(group, PREF_PAD_GROUP);
3910 pref_line(vbox, PREF_PAD_SPACE);
3911 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3913 threads_string_label = pref_label_new(group, "This option limits the number of threads (or cpu cores)\nthat Geeqie will use when running duplicate checks. The default value is 0, which means all available cores will be used.");
3914 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3916 pref_spacer(vbox, PREF_PAD_GROUP);
3918 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3919 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3923 static void config_tab_stereo(GtkWidget *notebook)
3931 GtkWidget *fs_button;
3932 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3934 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3936 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3937 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3939 table = pref_table_new(group, 2, 2, TRUE, FALSE);
3940 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3941 pref_checkbox_new_int(box, _("Mirror left image"),
3942 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3943 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3944 pref_checkbox_new_int(box, _("Flip left image"),
3945 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3946 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3947 pref_checkbox_new_int(box, _("Mirror right image"),
3948 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3949 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3950 pref_checkbox_new_int(box, _("Flip right image"),
3951 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3952 pref_checkbox_new_int(group, _("Swap left and right images"),
3953 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3954 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3955 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3957 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3958 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3959 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3960 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3961 pref_checkbox_link_sensitivity(fs_button, box2);
3962 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3963 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3964 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3965 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3966 pref_checkbox_new_int(box, _("Mirror left image"),
3967 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3968 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3969 pref_checkbox_new_int(box, _("Flip left image"),
3970 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
3971 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3972 pref_checkbox_new_int(box, _("Mirror right image"),
3973 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
3974 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3975 pref_checkbox_new_int(box, _("Flip right image"),
3976 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
3977 pref_checkbox_new_int(box2, _("Swap left and right images"),
3978 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
3979 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
3980 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
3982 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
3983 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
3984 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
3985 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
3986 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
3987 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
3988 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
3989 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
3990 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
3991 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
3992 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
3993 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
3994 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
3995 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
3999 /* Main preferences window */
4000 static void config_window_create(LayoutWindow *lw)
4002 GtkWidget *win_vbox;
4004 GtkWidget *notebook;
4006 GtkWidget *ct_button;
4008 if (!c_options) c_options = init_options(nullptr);
4010 configwindow = window_new(GTK_WINDOW_TOPLEVEL, "preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4011 DEBUG_NAME(configwindow);
4012 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4013 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4014 G_CALLBACK(config_window_delete), NULL);
4015 if (options->save_dialog_window_positions)
4017 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4018 gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4022 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4024 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4025 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4027 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4028 gtk_container_add(GTK_CONTAINER(configwindow), win_vbox);
4029 gtk_widget_show(win_vbox);
4031 notebook = gtk_notebook_new();
4032 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4033 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4034 gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4036 config_tab_general(notebook);
4037 config_tab_image(notebook);
4038 config_tab_osd(notebook);
4039 config_tab_windows(notebook);
4040 config_tab_accelerators(notebook);
4041 config_tab_files(notebook);
4042 config_tab_metadata(notebook);
4043 config_tab_keywords(notebook);
4044 config_tab_color(notebook);
4045 config_tab_stereo(notebook);
4046 config_tab_behavior(notebook);
4047 config_tab_toolbar_main(notebook);
4048 config_tab_toolbar_status(notebook);
4049 config_tab_advanced(notebook);
4051 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4053 hbox = gtk_hbutton_box_new();
4054 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4055 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4056 gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4057 gtk_widget_show(hbox);
4059 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4060 G_CALLBACK(config_window_help_cb), notebook);
4061 gtk_container_add(GTK_CONTAINER(hbox), button);
4062 gtk_widget_set_can_default(button, TRUE);
4063 gtk_widget_show(button);
4065 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4066 G_CALLBACK(config_window_ok_cb), notebook);
4067 gtk_container_add(GTK_CONTAINER(hbox), button);
4068 gtk_widget_set_can_default(button, TRUE);
4069 gtk_widget_grab_default(button);
4070 gtk_widget_show(button);
4074 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4075 G_CALLBACK(config_window_close_cb), nullptr);
4076 gtk_container_add(GTK_CONTAINER(hbox), button);
4077 gtk_widget_set_can_default(button, TRUE);
4078 gtk_widget_show(button);
4080 if (!generic_dialog_get_alternative_button_order(configwindow))
4082 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4085 gtk_widget_show(notebook);
4087 gtk_widget_show(configwindow);
4091 *-----------------------------------------------------------------------------
4092 * config window show (public)
4093 *-----------------------------------------------------------------------------
4096 void show_config_window(LayoutWindow *lw)
4100 gtk_window_present(GTK_WINDOW(configwindow));
4104 config_window_create(lw);
4113 void show_about_window(LayoutWindow *lw)
4115 GDataInputStream *data_stream;
4116 GInputStream *in_stream_authors;
4117 GInputStream *in_stream_translators;
4119 GdkPixbuf *pixbuf_icon;
4120 GdkPixbuf *pixbuf_logo;
4124 gchar *authors[1000];
4125 gchar *authors_path;
4127 gchar *timezone_path;
4129 gchar *translators_path;
4137 copyright = g_string_new(nullptr);
4138 copyright = g_string_append(copyright, "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");
4140 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4141 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4143 cd = ZDOpenDatabase(timezone_path);
4146 copyright = g_string_append(copyright, ZDGetNotice(cd));
4150 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4152 ZDCloseDatabase(cd);
4154 g_free(timezone_path);
4156 authors_path = g_build_filename(GQ_RESOURCE_PATH_CREDITS, "authors", NULL);
4158 in_stream_authors = g_resources_open_stream(authors_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4160 data_stream = g_data_input_stream_new(in_stream_authors);
4162 authors[0] = nullptr;
4163 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4165 authors[i_authors] = g_strdup(author_line);
4167 g_free(author_line);
4169 authors[i_authors] = nullptr;
4171 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4173 translators_path = g_build_filename(GQ_RESOURCE_PATH_CREDITS, "translators", NULL);
4175 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4177 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4178 translators = static_cast<gchar *>(g_malloc0(size));
4179 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4180 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4182 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);
4184 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4185 artists[1] = nullptr;
4187 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4188 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4189 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4190 "title", _("About Geeqie"),
4192 "program-name", GQ_APPNAME,
4194 "logo", pixbuf_logo,
4195 "icon", pixbuf_icon,
4196 "website", GQ_WEBSITE,
4197 "website-label", "Website",
4198 "comments", comment,
4201 "translator-credits", translators,
4202 "wrap-license", TRUE,
4203 "license", copyright->str,
4206 g_string_free(copyright, TRUE);
4208 while(n < i_authors)
4215 g_free(authors_path);
4217 g_free(translators);
4218 g_free(translators_path);
4219 g_object_unref(data_stream);
4220 g_object_unref(in_stream_authors);
4221 g_object_unref(in_stream_translators);
4224 static void image_overlay_set_text_colours()
4226 c_options->image_overlay.text_red = options->image_overlay.text_red;
4227 c_options->image_overlay.text_green = options->image_overlay.text_green;
4228 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4229 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4230 c_options->image_overlay.background_red = options->image_overlay.background_red;
4231 c_options->image_overlay.background_green = options->image_overlay.background_green;
4232 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4233 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4237 *-----------------------------------------------------------------------------
4238 * timezone database routines
4239 *-----------------------------------------------------------------------------
4242 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4244 GError *error = nullptr;
4245 auto tz = static_cast<TZData *>(data);
4246 gchar *tmp_filename;
4247 gchar *timezone_bin;
4248 gchar *tmp_dir = nullptr;
4251 if (!g_cancellable_is_cancelled(tz->cancellable))
4253 generic_dialog_close(tz->gd);
4257 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4259 tmp_filename = g_file_get_path(tz->tmp_g_file);
4260 fd = file_data_new_simple(tmp_filename);
4261 tmp_dir = open_archive(fd);
4265 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4266 if (isfile(timezone_bin))
4268 move_file(timezone_bin, tz->timezone_database_user);
4272 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GTK_STOCK_DIALOG_WARNING, nullptr);
4275 g_free(timezone_bin);
4276 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4280 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GTK_STOCK_DIALOG_WARNING, nullptr);
4282 g_free(tmp_filename);
4283 file_data_unref(fd);
4287 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, nullptr);
4290 g_file_delete(tz->tmp_g_file, nullptr, &error);
4291 g_object_unref(tz->tmp_g_file);
4292 tz->tmp_g_file = nullptr;
4293 g_object_unref(tz->cancellable);
4294 g_object_unref(tz->timezone_database_gq);
4297 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4299 auto tz = static_cast<TZData *>(data);
4301 if (!g_cancellable_is_cancelled(tz->cancellable))
4303 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4307 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4309 auto tz = static_cast<TZData *>(data);
4311 g_cancellable_cancel(tz->cancellable);
4314 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4316 auto tz = static_cast<TZData *>(data);
4317 GError *error = nullptr;
4318 GFileIOStream *io_stream;
4325 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4329 file_util_warning_dialog(_("Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, nullptr);
4330 log_printf("Error: Download timezone database failed:\n%s", error->message);
4331 g_error_free(error);
4332 g_object_unref(tz->tmp_g_file);
4336 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4338 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4340 generic_dialog_add_message(tz->gd, GTK_STOCK_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4342 tz->progress = gtk_progress_bar_new();
4343 gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4344 gtk_widget_show(tz->progress);
4346 gtk_widget_show(tz->gd->dialog);
4347 tz->cancellable = g_cancellable_new();
4348 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);
4350 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4353 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */