# HG changeset patch # User Colin Clark # Date 1525446997 -3600 # Node ID 0eac8ea9b1bef2ac43b66384f7aba6f965bc704e # Parent 48333f45a52653ffffaadb634118ca2500a005fb Fix #220, 269: marks do not persist https://github.com/BestImageViewer/geeqie/issues/220 https://github.com/BestImageViewer/geeqie/issues/269 Marks/image connections can optionally be saved in a text file in the same folder as History etc. The option is in Preferences/Behavior - set to save by default. Also a menu item to clear all marks. diff -r 48333f45a526 -r 0eac8ea9b1be doc/docbook/GuideImageMarks.xml --- a/doc/docbook/GuideImageMarks.xml Thu May 03 16:03:26 2018 +0100 +++ b/doc/docbook/GuideImageMarks.xml Fri May 04 16:16:37 2018 +0100 @@ -11,11 +11,11 @@ Select menu gives access to the marks operations of setting, filtering and intersection. - There are 6 individual marks, any of which can be associated with an image simply by pressing the 1 to 6 keys on the keyboard. + There are 10 individual marks, any of which can be associated with an image simply by pressing the 0 to 9 keys on the keyboard, where key 0 represents mark 10. If the Show Marks - menu has been selected, each image will have a set of 6 check-boxes displayed adjacent to it in the file pane in both icon and list mode. In addition a set of 6 check-boxes will be shown at the top of the files pane. Clicking any of these will filter the displayed list. + menu has been selected, each image will have a set of 10 check-boxes displayed adjacent to it in the file pane in both icon and list mode. In addition a set of 10 check-boxes will be shown at the top of the files pane. Clicking any of these will filter the displayed list. If the @@ -23,11 +23,17 @@ is being displayed, the currently set marks for the image are shown. It is not necessary to include an entry into the overlay template for this to happen. - A keyword can be associated with a single mark by right-clicking on the keyword in the sidebar panel. When a meta-data write operation for a file is triggered either manually or as defined in + A keyword can be associated with a single mark by right-clicking on the keyword in the sidebar panel. When a meta-data write operation for a file is triggered either + manually + or as defined in , the keyword data indicated by the current set of mark-to-keyword links will be written. - Neither marks, nor the associations between keywords and marks, are preserved when Geeqie is shut down. + + The associations between keywords and marks is preserved when Geeqie is shut down. The current setting of marks can also be optionally saved - the setting is in the + Behavior tab of Preferences + . + diff -r 48333f45a526 -r 0eac8ea9b1be doc/docbook/GuideMainWindowMenus.xml --- a/doc/docbook/GuideMainWindowMenus.xml Thu May 03 16:03:26 2018 +0100 +++ b/doc/docbook/GuideMainWindowMenus.xml Fri May 04 16:16:37 2018 +0100 @@ -445,6 +445,19 @@ + Clear marks + + + + Clear all marks for all images + + Marks that are linked to keywords will also be cleared. This may result in a metadata write operation being triggered. + + + + + + Mark n diff -r 48333f45a526 -r 0eac8ea9b1be doc/docbook/GuideOptionsBehavior.xml --- a/doc/docbook/GuideOptionsBehavior.xml Thu May 03 16:03:26 2018 +0100 +++ b/doc/docbook/GuideOptionsBehavior.xml Fri May 04 16:16:37 2018 +0100 @@ -126,6 +126,14 @@ + Save marks on exit + + + Save all marks that have been set. Note that marks that are linked to a keyword will always be saved irrespective of this setting. + + + + Recent folder list maximum size diff -r 48333f45a526 -r 0eac8ea9b1be src/filedata.c --- a/src/filedata.c Thu May 03 16:03:26 2018 +0100 +++ b/src/filedata.c Fri May 04 16:16:37 2018 +0100 @@ -29,6 +29,7 @@ #include "metadata.h" #include "trash.h" #include "histogram.h" +#include "secure_save.h" #include "exif.h" @@ -3159,4 +3160,133 @@ return TRUE; } + +/* + *----------------------------------------------------------------------------- + * Saving marks list, clearing marks + * Uses file_data_pool + *----------------------------------------------------------------------------- + */ + +static void marks_get_files(gpointer key, gpointer value, gpointer userdata) +{ + gchar *file_name = key; + GString *result = userdata; + FileData *fd; + + if (isfile(file_name)) + { + fd = value; + if (fd && fd->marks > 0) + { + g_string_append_printf(result, "%s,%i\n", fd->path, fd->marks); + } + } +} + +gboolean marks_list_load(const gchar *path) +{ + FILE *f; + gchar s_buf[1024]; + gchar *pathl; + gchar *file_path; + gchar *marks_value; + + pathl = path_from_utf8(path); + f = fopen(pathl, "r"); + g_free(pathl); + if (!f) return FALSE; + + /* first line must start with Marks comment */ + if (!fgets(s_buf, sizeof(s_buf), f) || + strncmp(s_buf, "#Marks", 6) != 0) + { + fclose(f); + return FALSE; + } + + while (fgets(s_buf, sizeof(s_buf), f)) + { + if (s_buf[0]=='#') continue; + file_path = strtok(s_buf, ","); + marks_value = strtok(NULL, ","); + if (isfile(file_path)) + { + FileData *fd = file_data_new_group(file_path); + file_data_ref(fd); + gint n = 0; + while (n <= 9) + { + gint mark_no = 1 << n; + if (atoi(marks_value) & mark_no) + { + file_data_set_mark(fd, n , 1); + } + n++; + } + } + } + + fclose(f); + return TRUE; +} + +gboolean marks_list_save(gchar *path, gboolean save) +{ + SecureSaveInfo *ssi; + gchar *pathl; + GString *marks = g_string_new(""); + + pathl = path_from_utf8(path); + ssi = secure_open(pathl); + g_free(pathl); + if (!ssi) + { + log_printf(_("Error: Unable to write marks lists to: %s\n"), path); + return FALSE; + } + + secure_fprintf(ssi, "#Marks lists\n"); + + if (save) + { + g_hash_table_foreach(file_data_pool, marks_get_files, marks); + } + secure_fprintf(ssi, "%s", marks->str); + g_string_free(marks, FALSE); + + secure_fprintf(ssi, "#end\n"); + return (secure_close(ssi) == 0); +} + +static void marks_clear(gpointer key, gpointer value, gpointer userdata) +{ + gchar *file_name = key; + gint mark_no; + gint n; + FileData *fd; + + if (isfile(file_name)) + { + fd = value; + if (fd && fd->marks > 0) + { + n = 0; + while (n <= 9) + { + mark_no = 1 << n; + if (fd->marks & mark_no) + { + file_data_set_mark(fd, n , 0); + } + n++; + } + } + } +} + +void marks_clear_all() +{ + g_hash_table_foreach(file_data_pool, marks_clear, NULL); +} /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff -r 48333f45a526 -r 0eac8ea9b1be src/filedata.h --- a/src/filedata.h Thu May 03 16:03:26 2018 +0100 +++ b/src/filedata.h Fri May 04 16:16:37 2018 +0100 @@ -163,5 +163,9 @@ void read_exif_time_data(FileData *file); void read_exif_time_digitized_data(FileData *file); + +gboolean marks_list_save(gchar *path, gboolean clear); +gboolean marks_list_load(const gchar *path); +void marks_clear_all(); #endif /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff -r 48333f45a526 -r 0eac8ea9b1be src/layout_util.c --- a/src/layout_util.c Thu May 03 16:03:26 2018 +0100 +++ b/src/layout_util.c Fri May 04 16:16:37 2018 +0100 @@ -231,6 +231,39 @@ return nw; } + +static void clear_marks_cancel_cb(GenericDialog *gd, gpointer data) +{ + generic_dialog_close(gd); +} + +static void clear_marks_help_cb(GenericDialog *gd, gpointer data) +{ + help_window_show("GuideMainWindowMenus.html"); +} + +void layout_menu_clear_marks_ok_cb(GenericDialog *gd, gpointer data) +{ + marks_clear_all(); + generic_dialog_close(gd); +} + +static void layout_menu_clear_marks_cb(GtkAction *action, gpointer data) +{ + GenericDialog *gd; + + gd = generic_dialog_new(_("Clear Marks"), + "marks_clear", NULL, FALSE, clear_marks_cancel_cb, NULL); + generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, "Clear all marks?", + "This will clear all marks for all images,\nincluding those linked to keywords", + TRUE); + generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, layout_menu_clear_marks_ok_cb, TRUE); + generic_dialog_add_button(gd, GTK_STOCK_HELP, NULL, + clear_marks_help_cb, FALSE); + + gtk_widget_show(gd->dialog); +} + static void layout_menu_new_window_cb(GtkAction *action, gpointer data) { layout_menu_new_window(action, data); @@ -1867,7 +1900,7 @@ { "SplitDownPane", NULL, N_("_Down Pane"), "Down", N_("Down Pane"), CB(layout_menu_split_pane_updown_cb) }, { "WriteRotation", NULL, N_("_Write orientation to file"), NULL, N_("Write orientation to file"), CB(layout_menu_write_rotate_cb) }, { "WriteRotationKeepDate", NULL, N_("_Write orientation to file (preserve timestamp)"), NULL, N_("Write orientation to file (preserve timestamp)"), CB(layout_menu_write_rotate_keep_date_cb) }, - + { "ClearMarks", NULL, N_("Clear Marks..."), NULL, N_("Clear Marks"), CB(layout_menu_clear_marks_cb) }, }; static GtkToggleActionEntry menu_toggle_entries[] = { @@ -1991,6 +2024,7 @@ " " " " " " +" " " " " " " " diff -r 48333f45a526 -r 0eac8ea9b1be src/main.c --- a/src/main.c Thu May 03 16:03:26 2018 +0100 +++ b/src/main.c Fri May 04 16:16:37 2018 +0100 @@ -510,6 +510,7 @@ */ #define RC_HISTORY_NAME "history" +#define RC_MARKS_NAME "marks" static void setup_env_path(void) { @@ -537,6 +538,24 @@ g_free(path); } +static void marks_load(void) +{ + gchar *path; + + path = g_build_filename(get_rc_dir(), RC_MARKS_NAME, NULL); + marks_list_load(path); + g_free(path); +} + +static void marks_save(gboolean save) +{ + gchar *path; + + path = g_build_filename(get_rc_dir(), RC_MARKS_NAME, NULL); + marks_list_save(path, save); + g_free(path); +} + static void mkdir_if_not_exists(const gchar *path) { if (isdir(path)) return; @@ -753,6 +772,8 @@ if (metadata_write_queue_confirm(FALSE, exit_program_write_metadata_cb, NULL)) return; + options->marks_save ? marks_save(TRUE) : marks_save(FALSE); + if (exit_confirm_dlg()) return; exit_program_final(); @@ -988,6 +1009,8 @@ remote_connection = remote_server_init(buf, cd); g_free(buf); + marks_load(); + DEBUG_1("%s main: gtk_main", get_exec_time()); gtk_main(); #ifdef HAVE_GTHREAD diff -r 48333f45a526 -r 0eac8ea9b1be src/options.c --- a/src/options.c Thu May 03 16:03:26 2018 +0100 +++ b/src/options.c Fri May 04 16:16:37 2018 +0100 @@ -79,6 +79,8 @@ options->fullscreen.disable_saver = TRUE; options->fullscreen.screen = -1; + options->marks_save = TRUE; + memset(&options->image.border_color, 0, sizeof(options->image.border_color)); memset(&options->image.alpha_color_1, 0, sizeof(options->image.alpha_color_1)); memset(&options->image.alpha_color_2, 0, sizeof(options->image.alpha_color_2)); diff -r 48333f45a526 -r 0eac8ea9b1be src/options.h --- a/src/options.h Thu May 03 16:03:26 2018 +0100 +++ b/src/options.h Fri May 04 16:16:37 2018 +0100 @@ -61,6 +61,8 @@ gint log_window_lines; + gboolean marks_save; // save marks on exit + /* info sidebar component heights */ struct { gint height; diff -r 48333f45a526 -r 0eac8ea9b1be src/preferences.c --- a/src/preferences.c Thu May 03 16:03:26 2018 +0100 +++ b/src/preferences.c Fri May 04 16:16:37 2018 +0100 @@ -407,6 +407,8 @@ options->info_comment.height = c_options->info_comment.height; options->info_rating.height = c_options->info_rating.height; + options->marks_save = c_options->marks_save; + #ifdef DEBUG set_debug_level(debug_c); #endif @@ -2333,6 +2335,7 @@ GtkWidget *ct_button; GtkWidget *spin; GtkWidget *table; + GtkWidget *marks; vbox = scrolled_notebook_page(notebook, _("Behavior")); @@ -2386,6 +2389,10 @@ pref_checkbox_new_int(group, _("List directory view uses single click to enter"), options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter); + marks = pref_checkbox_new_int(group, _("Save marks on exit"), + options->marks_save, &c_options->marks_save); + gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting"); + pref_spin_new_int(group, _("Recent folder list maximum size"), NULL, 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize); diff -r 48333f45a526 -r 0eac8ea9b1be src/rcfile.c --- a/src/rcfile.c Thu May 03 16:03:26 2018 +0100 +++ b/src/rcfile.c Fri May 04 16:16:37 2018 +0100 @@ -343,6 +343,8 @@ WRITE_NL(); WRITE_UINT(*options, log_window_lines); WRITE_NL(); WRITE_BOOL(*options, log_window.timer_data); + WRITE_NL(); WRITE_BOOL(*options, marks_save); + /* File operations Options */ WRITE_NL(); WRITE_BOOL(*options, file_ops.enable_in_place_rename); WRITE_NL(); WRITE_BOOL(*options, file_ops.confirm_delete); @@ -647,6 +649,8 @@ if (READ_INT(*options, log_window_lines)) continue; if (READ_BOOL(*options, log_window.timer_data)) continue; + if (READ_BOOL(*options, marks_save)) continue; + /* Properties dialog options */ if (READ_CHAR(*options, properties.tabs_order)) continue;