From b7985bf6b2bc26e5063acb961e232b4fafa0c5f3 Mon Sep 17 00:00:00 2001 From: Colin Clark Date: Mon, 18 Jun 2018 18:53:46 +0100 Subject: [PATCH] Read metadata in the idle loop Selecting sort-by DateOriginal, DateDigitized or Rating caused Geeqie to freeze while the metadata was read for the whole folder contents. This commit reads those data in the idle loop and thus prevents the freeze. There is an option in Preferences/Metadata to autmatically read the metadata in the idle loop whenever a new folder is selected. --- doc/docbook/GuideMainWindowStatusBar.xml | 10 ++- doc/docbook/GuideOptionsMetadata.xml | 25 +++++++ src/filedata.c | 36 +++++---- src/filedata.h | 1 + src/layout.c | 30 +++++++- src/layout_image.c | 1 + src/main.h | 2 + src/options.c | 1 + src/options.h | 2 + src/preferences.c | 7 ++ src/rcfile.c | 4 + src/typedefs.h | 3 + src/view_file.h | 2 +- src/view_file/view_file.c | 94 ++++++++++++++++++++++++ src/view_file/view_file_icon.c | 13 ++++ src/view_file/view_file_icon.h | 1 + src/view_file/view_file_list.c | 18 +++++ src/view_file/view_file_list.h | 1 + web/help/GuideMainWindowStatusBar.html | 10 ++- web/help/GuideOptionsMetadata.html | 26 +++++++ web/help/GuideReferenceXmpExif.html | 17 ++++- web/help/GuideSidebarsInfo.html | 8 +- 22 files changed, 286 insertions(+), 26 deletions(-) diff --git a/doc/docbook/GuideMainWindowStatusBar.xml b/doc/docbook/GuideMainWindowStatusBar.xml index dbbb36f9..343caae0 100644 --- a/doc/docbook/GuideMainWindowStatusBar.xml +++ b/doc/docbook/GuideMainWindowStatusBar.xml @@ -6,8 +6,14 @@
Progress Bar - The Progress bar updates to display the current state of thumbnail generation. When this section contains no text, thumbnail generation is idle. When “Loading thumbs...” is displayed, thumbnails are currently being generated when Geeqie is idle; the progress bar will update to display the percentage of thumbnails that are completed. - + + The Progress bar updates to display the current state of thumbnail generation, or the reading of metadata in the current folder. + + When “Loading thumbs...” is displayed, thumbnails are currently being generated when Geeqie is idle; the progress bar will update to display the percentage of thumbnails that are completed. + + When “Loading meta...” is displayed, certain metadata is being loaded when Geeqie is idle; the progress bar will update to display the percentage of files that have been read. Refer to + Preferences metadata. +
Sort method diff --git a/doc/docbook/GuideOptionsMetadata.xml b/doc/docbook/GuideOptionsMetadata.xml index 5844eaa0..b48ffdc5 100644 --- a/doc/docbook/GuideOptionsMetadata.xml +++ b/doc/docbook/GuideOptionsMetadata.xml @@ -158,4 +158,29 @@
+
+ Pre-load metadata + + + + Read metadata in background + + Using the folder sorting options: + + Exif date original + + Exif date digitized + + Rating + + requires metadata to be read from all files in a folder before the sort action can be made. If a folder contains a large number of file, this can take a noticeable period of time. + + If this option is checked, Geeqie will automatically read the required metatada in the background as soon as a folder is opened. This will reduce the amount of time you have to wait until the sort is completed. + + If you do not use these sort otions, leave this option unchecked. + + + + +
diff --git a/src/filedata.c b/src/filedata.c index 356b7c55..876cb80f 100644 --- a/src/filedata.c +++ b/src/filedata.c @@ -429,7 +429,7 @@ static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean fd->ref = 1; fd->magick = FD_MAGICK; fd->exifdate = 0; - fd->rating = 0; + fd->rating = STAR_RATING_NOT_READ; fd->format_class = filter_file_get_class(path_utf8); if (disable_sidecars) fd->disable_grouping = TRUE; @@ -477,7 +477,10 @@ void read_exif_time_data(FileData *file) return; } - file->exif = exif_read_fd(file); + if (!file->exif) + { + exif_read_fd(file); + } if (file->exif) { @@ -512,7 +515,10 @@ void read_exif_time_digitized_data(FileData *file) return; } - file->exif = exif_read_fd(file); + if (!file->exif) + { + exif_read_fd(file); + } if (file->exif) { @@ -539,6 +545,18 @@ void read_exif_time_digitized_data(FileData *file) } } +void read_rating_data(FileData *file) +{ + gchar *rating_str; + + rating_str = metadata_read_string(file, RATING_KEY, METADATA_PLAIN); + if (rating_str) + { + file->rating = atoi(rating_str); + g_free(rating_str); + } +} + void set_exif_time_data(GList *files) { DEBUG_1("%s set_exif_time_data: ...", get_exec_time()); @@ -1161,18 +1179,6 @@ GList *filelist_insert_sort_full(GList *list, gpointer data, SortType method, gb GList *filelist_sort(GList *list, SortType method, gboolean ascend) { - if (method == SORT_EXIFTIME) - { - set_exif_time_data(list); - } - if (method == SORT_EXIFTIMEDIGITIZED) - { - set_exif_time_digitized_data(list); - } - if (method == SORT_RATING) - { - set_rating_data(list); - } return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb); } diff --git a/src/filedata.h b/src/filedata.h index 6aa159fa..1b4e0975 100644 --- a/src/filedata.h +++ b/src/filedata.h @@ -167,5 +167,6 @@ 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(); +void read_rating_data(FileData *file); #endif /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff --git a/src/layout.c b/src/layout.c index dab1e023..4c14cdfc 100644 --- a/src/layout.c +++ b/src/layout.c @@ -363,6 +363,10 @@ static void layout_sort_menu_cb(GtkWidget *widget, gpointer data) type = (SortType)GPOINTER_TO_INT(data); + if (type == SORT_EXIFTIME || type == SORT_EXIFTIMEDIGITIZED || type == SORT_RATING) + { + vf_read_metadata_in_idle(lw->vf); + } layout_sort_set(lw, type, lw->sort_ascend); } @@ -547,11 +551,30 @@ static GtkWidget *layout_zoom_button(LayoutWindow *lw, GtkWidget *box, gint size void layout_status_update_progress(LayoutWindow *lw, gdouble val, const gchar *text) { + static gdouble thumb = 0; + static gdouble meta = 0; + if (!layout_valid(&lw)) return; if (!lw->info_progress_bar) return; + /* Give priority to the loading meta data message + */ + if(!g_strcmp0(text, "Loading thumbs...")) + { + thumb = val; + if (meta) + { + return; + } + } + else + { + meta = val; + } + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(lw->info_progress_bar), val); - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(lw->info_progress_bar), (text) ? text : " "); + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(lw->info_progress_bar), + val ? ((text) ? text : " ") : " "); } void layout_status_update_info(LayoutWindow *lw, const gchar *text) @@ -1096,6 +1119,11 @@ gboolean layout_set_fd(LayoutWindow *lw, FileData *fd) if (options->metadata.confirm_on_dir_change && dir_changed) metadata_write_queue_confirm(FALSE, NULL, NULL); + if (lw->vf && (options->read_metadata_in_idle || (lw->sort_method == SORT_EXIFTIME || lw->sort_method == SORT_EXIFTIMEDIGITIZED || lw->sort_method == SORT_RATING))) + { + vf_read_metadata_in_idle(lw->vf); + } + return TRUE; } diff --git a/src/layout_image.c b/src/layout_image.c index 39a58009..250df6b9 100644 --- a/src/layout_image.c +++ b/src/layout_image.c @@ -1125,6 +1125,7 @@ void layout_image_alter_orientation(LayoutWindow *lw, AlterType type) static void image_alter_rating(FileData *fd_n, const gchar *rating) { metadata_write_string(fd_n, RATING_KEY, rating); + read_rating_data(fd_n); } void layout_image_rating(LayoutWindow *lw, const gchar *rating) diff --git a/src/main.h b/src/main.h index 2c63afea..6bf9f992 100644 --- a/src/main.h +++ b/src/main.h @@ -128,6 +128,8 @@ #define TIMEZONE_DATABASE "timezone21.bin" #define HELP_SEARCH_ENGINE "https://duckduckgo.com/?q=site:geeqie.org/help " + +#define STAR_RATING_NOT_READ -12345 /* *---------------------------------------------------------------------------- * main.c diff --git a/src/options.c b/src/options.c index a3bcdb8e..a4e63196 100644 --- a/src/options.c +++ b/src/options.c @@ -187,6 +187,7 @@ ConfOptions *init_options(ConfOptions *options) options->log_window.paused = FALSE; options->log_window.timer_data = FALSE; + options->read_metadata_in_idle = FALSE; return options; } diff --git a/src/options.h b/src/options.h index 403e5963..a351ff5d 100644 --- a/src/options.h +++ b/src/options.h @@ -288,6 +288,8 @@ struct _ConfOptions gboolean line_wrap; gboolean timer_data; } log_window; + + gboolean read_metadata_in_idle; }; ConfOptions *options; diff --git a/src/preferences.c b/src/preferences.c index 86915d43..4eb6ad62 100644 --- a/src/preferences.c +++ b/src/preferences.c @@ -412,6 +412,7 @@ static void config_window_apply(void) options->with_rename = c_options->with_rename; config_entry_to_option(help_search_engine_entry, &options->help_search_engine, NULL); + options->read_metadata_in_idle = c_options->read_metadata_in_idle; #ifdef DEBUG set_debug_level(debug_c); #endif @@ -2220,6 +2221,12 @@ static void config_tab_metadata(GtkWidget *notebook) pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change); + + group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL); + + ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), + options->read_metadata_in_idle, &c_options->read_metadata_in_idle); + 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"); } /* metadata tab */ diff --git a/src/rcfile.c b/src/rcfile.c index d02c7642..f2931178 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -479,6 +479,8 @@ static void write_global_attributes(GString *outstr, gint indent) WRITE_NL(); WRITE_INT(*options, stereo.fixed_x2); WRITE_NL(); WRITE_INT(*options, stereo.fixed_y2); + WRITE_NL(); WRITE_BOOL(*options, read_metadata_in_idle); + /* copy move rename */ WRITE_NL(); WRITE_INT(*options, cp_mv_rn.auto_start); WRITE_NL(); WRITE_INT(*options, cp_mv_rn.auto_padding); @@ -802,6 +804,8 @@ static gboolean load_global_params(const gchar **attribute_names, const gchar ** if (READ_INT(*options, stereo.fixed_x2)) continue; if (READ_INT(*options, stereo.fixed_y2)) continue; + if (READ_BOOL(*options, read_metadata_in_idle)) continue; + /* copy move rename */ if (READ_INT(*options, cp_mv_rn.auto_start)) continue; if (READ_INT(*options, cp_mv_rn.auto_padding)) continue; diff --git a/src/typedefs.h b/src/typedefs.h index 33686415..86c2805b 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -595,6 +595,7 @@ struct _FileData { GHashTable *modified_xmp; // hash table which contains unwritten xmp metadata in format: key->list of string values GList *cached_metadata; gint rating; + gboolean metadata_in_idle_loaded; SelectionType selected; // Used by view_file_icon. }; @@ -887,6 +888,8 @@ struct _ViewFile /* file list for edit menu */ GList *editmenu_fd_list; + + guint read_metadata_in_idle_id; }; struct _ViewFileInfoList diff --git a/src/view_file.h b/src/view_file.h index 454f70b7..d02d76c1 100644 --- a/src/view_file.h +++ b/src/view_file.h @@ -72,6 +72,6 @@ void vf_notify_cb(FileData *fd, NotifyType type, gpointer data); void vf_thumb_update(ViewFile *vf); void vf_thumb_cleanup(ViewFile *vf); void vf_thumb_stop(ViewFile *vf); - +void vf_read_metadata_in_idle(ViewFile *vf); #endif /* VIEW_FILE_H */ /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff --git a/src/view_file/view_file.c b/src/view_file/view_file.c index fb80daca..0bac1ffd 100644 --- a/src/view_file/view_file.c +++ b/src/view_file/view_file.c @@ -404,6 +404,11 @@ static void vf_pop_menu_sort_cb(GtkWidget *widget, gpointer data) type = (SortType)GPOINTER_TO_INT(data); + if (type == SORT_EXIFTIME || type == SORT_EXIFTIMEDIGITIZED || type == SORT_RATING) + { + vf_read_metadata_in_idle(vf); + } + if (vf->layout) { layout_sort_set(vf->layout, type, vf->sort_ascend); @@ -697,6 +702,10 @@ static void vf_destroy_cb(GtkWidget *widget, gpointer data) gtk_widget_destroy(vf->popup); } + if (vf->read_metadata_in_idle_id) + { + g_idle_remove_by_data(vf); + } file_data_unref(vf->dir_fd); g_free(vf->info); g_free(vf); @@ -848,6 +857,7 @@ ViewFile *vf_new(FileViewType type, FileData *dir_fd) vf->type = type; vf->sort_method = SORT_NAME; vf->sort_ascend = TRUE; + vf->read_metadata_in_idle_id = 0; vf->scrolled = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(vf->scrolled), GTK_SHADOW_IN); @@ -926,6 +936,20 @@ static gdouble vf_thumb_progress(ViewFile *vf) return (gdouble)done / count; } +static gdouble vf_read_metadata_in_idle_progress(ViewFile *vf) +{ + gint count = 0; + gint done = 0; + + switch (vf->type) + { + case FILEVIEW_LIST: vflist_read_metadata_progress_count(vf->list, &count, &done); break; + case FILEVIEW_ICON: vficon_read_metadata_progress_count(vf->list, &count, &done); break; + } + + return (gdouble)done / count; +} + static void vf_set_thumb_fd(ViewFile *vf, FileData *fd) { switch (vf->type) @@ -1197,4 +1221,74 @@ void vf_notify_cb(FileData *fd, NotifyType type, gpointer data) } } +static gboolean vf_read_metadata_in_idle_cb(gpointer data) +{ + FileData *fd; + ViewFile *vf = data; + GList *list_entry; + GList *work; + + vf_thumb_status(vf, vf_read_metadata_in_idle_progress(vf), _("Loading meta...")); + + work = vf->list; + + while (work) + { + fd = work->data; + + if (fd && !fd->metadata_in_idle_loaded) + { + if (!fd->exifdate) + { + read_exif_time_data(fd); + } + if (!fd->exifdate_digitized) + { + read_exif_time_digitized_data(fd); + } + if (fd->rating == STAR_RATING_NOT_READ) + { + read_rating_data(fd); + } + fd->metadata_in_idle_loaded = TRUE; + return TRUE; + } + work = work->next; + } + + vf_thumb_status(vf, 0.0, NULL); + vf->read_metadata_in_idle_id = 0; + vf_refresh(vf); + return FALSE; +} + +static void vf_read_metadata_in_idle_finished_cb(gpointer data) +{ + ViewFile *vf = data; + + vf_thumb_status(vf, 0.0, "Loading meta..."); + vf->read_metadata_in_idle_id = 0; +} + +void vf_read_metadata_in_idle(ViewFile *vf) +{ + GList *work; + FileData *fd; + + if (!vf) return; + + if (vf->read_metadata_in_idle_id) + { + g_idle_remove_by_data(vf); + } + vf->read_metadata_in_idle_id = 0; + + if (vf->list) + { + vf->read_metadata_in_idle_id = g_idle_add_full(G_PRIORITY_LOW, vf_read_metadata_in_idle_cb, vf, vf_read_metadata_in_idle_finished_cb); + } + + return; +} + /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff --git a/src/view_file/view_file_icon.c b/src/view_file/view_file_icon.c index eea06b2f..75085e4c 100644 --- a/src/view_file/view_file_icon.c +++ b/src/view_file/view_file_icon.c @@ -1690,6 +1690,19 @@ void vficon_thumb_progress_count(GList *list, gint *count, gint *done) } } +void vficon_read_metadata_progress_count(GList *list, gint *count, gint *done) +{ + GList *work = list; + while (work) + { + FileData *fd = work->data; + work = work->next; + + if (fd->metadata_in_idle_loaded) (*done)++; + (*count)++; + } +} + void vficon_set_thumb_fd(ViewFile *vf, FileData *fd) { GtkTreeModel *store; diff --git a/src/view_file/view_file_icon.h b/src/view_file/view_file_icon.h index 0ca75b5f..9e9bd0be 100644 --- a/src/view_file/view_file_icon.h +++ b/src/view_file/view_file_icon.h @@ -68,6 +68,7 @@ void vficon_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode) void vficon_thumb_progress_count(GList *list, gint *count, gint *done); +void vficon_read_metadata_progress_count(GList *list, gint *count, gint *done); void vficon_set_thumb_fd(ViewFile *vf, FileData *fd); FileData *vficon_thumb_next_fd(ViewFile *vf); void vficon_thumb_reset_all(ViewFile *vf); diff --git a/src/view_file/view_file_list.c b/src/view_file/view_file_list.c index c89e6cbd..cde019c5 100644 --- a/src/view_file/view_file_list.c +++ b/src/view_file/view_file_list.c @@ -1079,6 +1079,24 @@ void vflist_thumb_progress_count(GList *list, gint *count, gint *done) } } +void vflist_read_metadata_progress_count(GList *list, gint *count, gint *done) +{ + GList *work = list; + while (work) + { + FileData *fd = work->data; + work = work->next; + + if (fd->metadata_in_idle_loaded) (*done)++; + + if (fd->sidecar_files) + { + vflist_read_metadata_progress_count(fd->sidecar_files, count, done); + } + (*count)++; + } +} + void vflist_set_thumb_fd(ViewFile *vf, FileData *fd) { GtkTreeStore *store; diff --git a/src/view_file/view_file_list.h b/src/view_file/view_file_list.h index 38bc8292..958e0d3d 100644 --- a/src/view_file/view_file_list.h +++ b/src/view_file/view_file_list.h @@ -69,6 +69,7 @@ void vflist_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode) void vflist_color_set(ViewFile *vf, FileData *fd, gboolean color_set); void vflist_thumb_progress_count(GList *list, gint *count, gint *done); +void vflist_read_metadata_progress_count(GList *list, gint *count, gint *done); void vflist_set_thumb_fd(ViewFile *vf, FileData *fd); FileData *vflist_thumb_next_fd(ViewFile *vf); void vflist_thumb_reset_all(ViewFile *vf); diff --git a/web/help/GuideMainWindowStatusBar.html b/web/help/GuideMainWindowStatusBar.html index 77ebd847..56708552 100644 --- a/web/help/GuideMainWindowStatusBar.html +++ b/web/help/GuideMainWindowStatusBar.html @@ -475,8 +475,14 @@ dd.answer div.label { float: left; }

2.6.1. Progress Bar

-

The Progress bar updates to display the current state of thumbnail generation. When this section contains no text, thumbnail generation is idle. When “Loading thumbs...” is displayed, thumbnails are currently being generated when Geeqie is idle; the progress bar will update to display the percentage of thumbnails that are completed.

-

+

+ The Progress bar updates to display the current state of thumbnail generation, or the reading of metadata in the current folder. +

+ When “Loading thumbs...” is displayed, thumbnails are currently being generated when Geeqie is idle; the progress bar will update to display the percentage of thumbnails that are completed. +

+ When “Loading meta...” is displayed, certain metadata is being loaded when Geeqie is idle; the progress bar will update to display the percentage of files that have been read. Refer to + Preferences metadata. +

2.6.2. Sort method

diff --git a/web/help/GuideOptionsMetadata.html b/web/help/GuideOptionsMetadata.html index f758c56a..66c83a12 100644 --- a/web/help/GuideOptionsMetadata.html +++ b/web/help/GuideOptionsMetadata.html @@ -484,6 +484,9 @@ dd.answer div.label { float: left; }
  • 11.6.5. Auto-save options
  • +
  • +11.6.6. Pre-load metadata +
  • 11.6.1. Metadata writing process

    @@ -628,6 +631,29 @@ dd.answer div.label { float: left; }

    +
    +

    11.6.6. Pre-load metadata

    +
    • + + Read metadata in background +

      + Using the folder sorting options: +

      + Exif date original +

      + Exif date digitized +

      + Rating +

      + requires metadata to be read from all files in a folder before the sort action can be made. If a folder contains a large number of file, this can take a noticeable period of time. +

      + If this option is checked, Geeqie will automatically read the required metatada in the background as soon as a folder is opened. This will reduce the amount of time you have to wait until the sort is completed. +

      + If you do not use these sort otions, leave this option unchecked. +
      +
    +

    +
    diff --git a/web/help/GuideSidebarsInfo.html b/web/help/GuideSidebarsInfo.html index cd78e8c0..b8d709eb 100644 --- a/web/help/GuideSidebarsInfo.html +++ b/web/help/GuideSidebarsInfo.html @@ -730,8 +730,12 @@ dd.answer div.label { float: left; } file date and time in human readable form -file.mode -file mode flags +file.mode +file mode flags + + +file.ctime +refer to operating system documentation for the meaning of ctime -- 2.20.1