[evince] recent-view: Cache documents metadata and thumbnail



commit 6c3e1d5f5c297d0158d4bfe027a7e16cec6831bb
Author: Carlos Garcia Campos <carlosgc gnome org>
Date:   Mon Jun 30 17:37:53 2014 +0200

    recent-view: Cache documents metadata and thumbnail
    
    Use the GVFS metadata to save the title and author and the desktop thumbnails
    cache to save the thumbnails if libgnome-desktop is available.

 configure.ac           |   30 +++-
 shell/ev-recent-view.c |  557 +++++++++++++++++++++++++++++++++++++++---------
 shell/ev-window.c      |   22 ++
 3 files changed, 509 insertions(+), 100 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index d5ede71..0171e7f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -299,6 +299,31 @@ fi
 
 AM_CONDITIONAL([ENABLE_DBUS], [test "$enable_dbus" = "yes"])
 
+# *******************************
+# GNOME Desktop (Thumbnail cache)
+# *******************************
+
+AC_ARG_ENABLE([libgnome-desktop],
+        [AS_HELP_STRING([--disable-libgnome-desktop], [Disable GNOME Desktop (Thumbnail cache)])],
+        [enable_gnome_desktop=$enableval],
+        [enable_gnome_desktop=auto])
+
+if test "$enable_gnome_desktop" != "no"; then
+   if test "$enable_gnome_desktop" = "auto"; then
+      PKG_CHECK_MODULES([LIBGNOME_DESKTOP], [gnome-desktop-3.0], has_libgnome_desktop=yes, 
has_libgnome_desktop=no)
+   else
+      PKG_CHECK_MODULES([LIBGNOME_DESKTOP], [gnome-desktop-3.0])
+      has_libgnome_desktop = yes
+   fi
+
+   if test x$has_libgnome_desktop = xyes; then
+      AC_DEFINE([HAVE_LIBGNOME_DESKTOP], [1], [Whether GNOME Desktop (Thumbnail cache) is available])
+      enable_gnome_desktop=yes
+   else
+      enable_gnome_desktop=no
+   fi
+fi
+
 dnl ========= Check for Desktop Schemas
 PKG_CHECK_MODULES([DESKTOP_SCHEMAS], [gsettings-desktop-schemas],
                   has_desktop_schemas=yes, has_desktop_schemas=no)
@@ -340,8 +365,8 @@ BACKEND_LIBS="$BACKEND_LIBS -lm"
 AC_SUBST(BACKEND_CFLAGS)
 AC_SUBST(BACKEND_LIBS)
 
-SHELL_CFLAGS="$SHELL_CORE_CFLAGS $LIBSECRET_CFLAGS -DGDK_MULTIHEAD_SAFE -DGTK_MULTIHEAD_SAFE $DEBUG_FLAGS"
-SHELL_LIBS="$SHELL_CORE_LIBS $LIBSECRET_LIBS -lz -lm"
+SHELL_CFLAGS="$SHELL_CORE_CFLAGS $LIBSECRET_CFLAGS -DGDK_MULTIHEAD_SAFE -DGTK_MULTIHEAD_SAFE $DEBUG_FLAGS 
$LIBGNOME_DESKTOP_CFLAGS"
+SHELL_LIBS="$SHELL_CORE_LIBS $LIBSECRET_LIBS $LIBGNOME_DESKTOP_LIBS -lz -lm"
 AC_SUBST(SHELL_CFLAGS)
 AC_SUBST(SHELL_LIBS)
 
@@ -902,5 +927,6 @@ GObject Introspection ....:  $enable_introspection
 DBUS communication .......:  $enable_dbus
 Keyring integration ......:  $with_keyring
 GTK+ Unix Print ..........:  $with_gtk_unix_print
+Thumbnail cache ..........:  $enable_gnome_desktop
 
 ])
diff --git a/shell/ev-recent-view.c b/shell/ev-recent-view.c
index aff20a5..099bfca 100644
--- a/shell/ev-recent-view.c
+++ b/shell/ev-recent-view.c
@@ -17,6 +17,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
+#include "config.h"
 
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
@@ -32,12 +33,15 @@
 #include "ev-jobs.h"
 #include "ev-job-scheduler.h"
 
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnome-desktop/gnome-desktop-thumbnail.h>
+
 typedef enum {
         EV_RECENT_VIEW_COLUMN_URI,
         EV_RECENT_VIEW_COLUMN_PRIMARY_TEXT,
         EV_RECENT_VIEW_COLUMN_SECONDARY_TEXT,
         EV_RECENT_VIEW_COLUMN_ICON,
-        EV_RECENT_VIEW_COLUMN_JOB,
+        EV_RECENT_VIEW_COLUMN_ASYNC_DATA,
         NUM_COLUMNS
 } EvRecentViewColumns;
 
@@ -47,6 +51,10 @@ struct _EvRecentViewPrivate {
         GtkRecentManager *recent_manager;
         GtkTreePath      *pressed_item_tree_path;
         guint             recent_manager_changed_handler_id;
+
+#ifdef HAVE_LIBGNOME_DESKTOP
+        GnomeDesktopThumbnailFactory *thumbnail_factory;
+#endif
 };
 
 enum {
@@ -61,21 +69,57 @@ G_DEFINE_TYPE (EvRecentView, ev_recent_view, GTK_TYPE_SCROLLED_WINDOW)
 #define ICON_VIEW_SIZE 128
 #define MAX_RECENT_VIEW_ITEMS 20
 
-static gboolean
-ev_recent_view_clear_job (GtkTreeModel *model,
-                          GtkTreePath  *path,
-                          GtkTreeIter  *iter,
-                          EvRecentView *ev_recent_view)
+typedef struct {
+        EvRecentView        *ev_recent_view;
+        char                *uri;
+        time_t               mtime;
+        GtkTreeRowReference *row;
+        GCancellable        *cancellable;
+        EvJob               *job;
+        guint                needs_metadata : 1;
+        guint                needs_thumbnail : 1;
+} GetDocumentInfoAsyncData;
+
+static void
+get_document_info_async_data_free (GetDocumentInfoAsyncData *data)
 {
-        EvJob *job;
+        GtkTreePath *path;
+        GtkTreeIter  iter;
 
-        gtk_tree_model_get (model, iter, EV_RECENT_VIEW_COLUMN_JOB, &job, -1);
+        if (data->job) {
+                g_signal_handlers_disconnect_by_data (data->job, data->ev_recent_view);
+                ev_job_cancel (data->job);
+                g_object_unref (data->job);
+        }
+
+        g_clear_object (&data->cancellable);
+        g_free (data->uri);
 
-        if (job != NULL) {
-                ev_job_cancel (job);
-                g_signal_handlers_disconnect_by_data (job, ev_recent_view);
-                g_object_unref (job);
+        path = gtk_tree_row_reference_get_path (data->row);
+        if (path) {
+                gtk_tree_model_get_iter (GTK_TREE_MODEL (data->ev_recent_view->priv->model), &iter, path);
+                gtk_list_store_set (data->ev_recent_view->priv->model, &iter,
+                                    EV_RECENT_VIEW_COLUMN_ASYNC_DATA, NULL,
+                                    -1);
+                gtk_tree_path_free (path);
         }
+        gtk_tree_row_reference_free (data->row);
+
+        g_slice_free (GetDocumentInfoAsyncData, data);
+}
+
+static gboolean
+ev_recent_view_clear_async_data (GtkTreeModel *model,
+                                 GtkTreePath  *path,
+                                 GtkTreeIter  *iter,
+                                 EvRecentView *ev_recent_view)
+{
+        GetDocumentInfoAsyncData *data;
+
+        gtk_tree_model_get (model, iter, EV_RECENT_VIEW_COLUMN_ASYNC_DATA, &data, -1);
+
+        if (data != NULL)
+                g_cancellable_cancel (data->cancellable);
 
         return FALSE;
 }
@@ -86,8 +130,9 @@ ev_recent_view_clear_model (EvRecentView *ev_recent_view)
         EvRecentViewPrivate *priv = ev_recent_view->priv;
 
         gtk_tree_model_foreach (GTK_TREE_MODEL (priv->model),
-                                (GtkTreeModelForeachFunc)ev_recent_view_clear_job,
+                                (GtkTreeModelForeachFunc)ev_recent_view_clear_async_data,
                                 ev_recent_view);
+
         gtk_list_store_clear (priv->model);
 }
 
@@ -110,6 +155,10 @@ ev_recent_view_dispose (GObject *obj)
         }
         priv->recent_manager = NULL;
 
+#ifdef HAVE_LIBGNOME_DESKTOP
+        g_clear_object (&priv->thumbnail_factory);
+#endif
+
         G_OBJECT_CLASS (ev_recent_view_parent_class)->dispose (obj);
 }
 
@@ -206,64 +255,125 @@ on_icon_view_item_activated (GtkIconView  *iconview,
 }
 
 static void
-thumbnail_job_completed_callback (EvJobThumbnail *job,
-                                  EvRecentView   *ev_recent_view)
+add_thumbnail_to_model (GetDocumentInfoAsyncData *data,
+                        cairo_surface_t          *thumbnail)
 {
-        EvRecentViewPrivate *priv = ev_recent_view->priv;
-        GtkTreeRowReference *row;
+        EvRecentViewPrivate *priv = data->ev_recent_view->priv;
         GtkTreePath         *path;
         GtkTreeIter          iter;
-        cairo_surface_t     *surface;
         GtkBorder            border;
+        cairo_surface_t     *surface;
+
+        data->needs_thumbnail = FALSE;
 
         border.left = 4;
         border.right = 3;
         border.top = 3;
         border.bottom = 6;
 
-        surface = gd_embed_image_in_frame (job->thumbnail_surface,
+        surface = gd_embed_image_in_frame (thumbnail,
                                            "resource:///org/gnome/evince/shell/ui/thumbnail-frame.png",
                                            &border, &border);
 
-        row = (GtkTreeRowReference *) g_object_get_data (G_OBJECT (job), "row-reference");
-        path = gtk_tree_row_reference_get_path (row);
-
+        path = gtk_tree_row_reference_get_path (data->row);
         if (path != NULL) {
                 gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->model), &iter, path);
-                gtk_tree_path_free (path);
-
                 gtk_list_store_set (priv->model, &iter,
                                     EV_RECENT_VIEW_COLUMN_ICON, surface,
-                                    EV_RECENT_VIEW_COLUMN_JOB, NULL,
                                     -1);
+                gtk_tree_path_free (path);
         }
 
         cairo_surface_destroy (surface);
 }
 
+#ifdef HAVE_LIBGNOME_DESKTOP
 static void
-document_load_job_completed_callback (EvJobLoad    *job_load,
-                                      EvRecentView *ev_recent_view)
+ev_rencent_view_ensure_desktop_thumbnail_factory (EvRecentView *ev_recent_view)
 {
-        EvRecentViewPrivate *priv = ev_recent_view->priv;
-        GtkTreeRowReference *row;
-        GtkTreePath         *path;
-        GtkTreeIter          iter;
-        EvDocument          *document;
+        if (ev_recent_view->priv->thumbnail_factory)
+                return;
 
-        document = EV_JOB (job_load)->document;
-        row = (GtkTreeRowReference *) g_object_get_data (G_OBJECT (job_load), "row-reference");
-        path = gtk_tree_row_reference_get_path (row);
-        gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->model), &iter, path);
-        gtk_tree_path_free (path);
+        ev_recent_view->priv->thumbnail_factory = gnome_desktop_thumbnail_factory_new 
(GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE);
+}
 
-        if (document) {
-                EvJob                *job_thumbnail;
-                gdouble               height;
-                gdouble               width;
-                gint                  target_width;
-                gint                  target_height;
-                const EvDocumentInfo *info;
+static void
+save_thumbnail_in_cache_thread (GTask                    *task,
+                                EvRecentView             *ev_recent_view,
+                                GetDocumentInfoAsyncData *data,
+                                GCancellable             *cancellable)
+{
+        GdkPixbuf       *thumbnail;
+        cairo_surface_t *surface;
+
+        surface = EV_JOB_THUMBNAIL (data->job)->thumbnail_surface;
+        thumbnail = gdk_pixbuf_get_from_surface (surface, 0, 0,
+                                                 cairo_image_surface_get_width (surface),
+                                                 cairo_image_surface_get_height (surface));
+
+        gnome_desktop_thumbnail_factory_save_thumbnail (ev_recent_view->priv->thumbnail_factory,
+                                                        thumbnail, data->uri, data->mtime);
+        g_object_unref (thumbnail);
+
+        g_task_return_boolean (task, TRUE);
+}
+
+static void
+save_thumbnail_in_cache_cb (EvRecentView             *ev_recent_view,
+                            GAsyncResult             *result,
+                            GetDocumentInfoAsyncData *data)
+{
+        get_document_info_async_data_free (data);
+}
+#endif /* HAVE_LIBGNOME_DESKTOP */
+
+static void
+save_document_thumbnail_in_cache (GetDocumentInfoAsyncData *data)
+{
+#ifdef HAVE_LIBGNOME_DESKTOP
+        GTask *task;
+
+        ev_rencent_view_ensure_desktop_thumbnail_factory (data->ev_recent_view);
+        task = g_task_new (data->ev_recent_view, data->cancellable,
+                           (GAsyncReadyCallback)save_thumbnail_in_cache_cb, data);
+        g_task_set_task_data (task, data, NULL);
+        g_task_run_in_thread (task, (GTaskThreadFunc)save_thumbnail_in_cache_thread);
+        g_object_unref (task);
+#else
+        get_document_info_async_data_free (data);
+#endif /* HAVE_LIBGNOME_DESKTOP */
+}
+
+static void
+thumbnail_job_completed_callback (EvJobThumbnail           *job,
+                                  GetDocumentInfoAsyncData *data)
+{
+        if (g_cancellable_is_cancelled (data->cancellable)) {
+                get_document_info_async_data_free (data);
+                return;
+        }
+
+        add_thumbnail_to_model (data, job->thumbnail_surface);
+        save_document_thumbnail_in_cache (data);
+}
+
+static void
+document_load_job_completed_callback (EvJobLoad                *job_load,
+                                      GetDocumentInfoAsyncData *data)
+{
+        EvRecentViewPrivate *priv = data->ev_recent_view->priv;
+        EvDocument          *document = EV_JOB (job_load)->document;
+
+        if (g_cancellable_is_cancelled (data->cancellable) || !document) {
+                get_document_info_async_data_free (data);
+                return;
+        }
+
+        g_clear_object (&data->job);
+
+        if (data->needs_thumbnail) {
+                gdouble width, height;
+                gint    target_width, target_height;
 
                 ev_document_get_page_size (document, 0, &width, &height);
                 if (height < width) {
@@ -274,40 +384,306 @@ document_load_job_completed_callback (EvJobLoad    *job_load,
                         target_height = ICON_VIEW_SIZE;
                 }
 
-                job_thumbnail = ev_job_thumbnail_new_with_target_size (document, 0, 0, target_width, 
target_height);
-
-                ev_job_thumbnail_set_has_frame (EV_JOB_THUMBNAIL (job_thumbnail), FALSE);
-                ev_job_thumbnail_set_output_format (EV_JOB_THUMBNAIL (job_thumbnail), 
EV_JOB_THUMBNAIL_SURFACE);
+                data->job = ev_job_thumbnail_new_with_target_size (document, 0, 0, target_width, 
target_height);
+                ev_job_thumbnail_set_has_frame (EV_JOB_THUMBNAIL (data->job), FALSE);
+                ev_job_thumbnail_set_output_format (EV_JOB_THUMBNAIL (data->job), EV_JOB_THUMBNAIL_SURFACE);
+                g_signal_connect (data->job, "finished",
+                                  G_CALLBACK (thumbnail_job_completed_callback),
+                                  data);
+                ev_job_scheduler_push_job (data->job, EV_JOB_PRIORITY_HIGH);
+        }
 
-                g_object_set_data_full (G_OBJECT (job_thumbnail), "row-reference",
-                                        gtk_tree_row_reference_copy (row),
-                                        (GDestroyNotify)gtk_tree_row_reference_free);
+        if (data->needs_metadata) {
+                const EvDocumentInfo *info;
+                GtkTreePath          *path;
+                GtkTreeIter           iter;
+                GFile                *file;
+                GFileInfo            *file_info = g_file_info_new ();
 
-                g_signal_connect (job_thumbnail, "finished",
-                                  G_CALLBACK (thumbnail_job_completed_callback),
-                                  ev_recent_view);
+                path = gtk_tree_row_reference_get_path (data->row);
+                if (path)
+                        gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->model), &iter, path);
 
                 info = ev_document_get_info (document);
-                if (info->fields_mask & EV_DOCUMENT_INFO_TITLE && info->title && info->title[0] != '\0')
-                        gtk_list_store_set (priv->model, &iter,
-                                            EV_RECENT_VIEW_COLUMN_PRIMARY_TEXT, info->title,
-                                            -1);
-                if (info->fields_mask & EV_DOCUMENT_INFO_AUTHOR && info->author && info->author[0] != '\0')
-                        gtk_list_store_set (priv->model, &iter,
-                                            EV_RECENT_VIEW_COLUMN_SECONDARY_TEXT, info->author,
-                                            -1);
+                if (info->fields_mask & EV_DOCUMENT_INFO_TITLE && info->title && info->title[0] != '\0') {
+                        if (path) {
+                                gtk_list_store_set (priv->model, &iter,
+                                                    EV_RECENT_VIEW_COLUMN_PRIMARY_TEXT, info->title,
+                                                    -1);
+                        }
+                        g_file_info_set_attribute_string (file_info, "metadata::evince::title", info->title);
+                } else {
+                        g_file_info_set_attribute_string (file_info, "metadata::evince::title", "");
+                }
+                if (info->fields_mask & EV_DOCUMENT_INFO_AUTHOR && info->author && info->author[0] != '\0') {
+                        if (path) {
+                                gtk_list_store_set (priv->model, &iter,
+                                                    EV_RECENT_VIEW_COLUMN_SECONDARY_TEXT, info->author,
+                                                    -1);
+                        }
+                        g_file_info_set_attribute_string (file_info, "metadata::evince::author", 
info->author);
+                } else {
+                        g_file_info_set_attribute_string (file_info, "metadata::evince::author", "");
+                }
 
-                gtk_list_store_set (priv->model, &iter,
-                                    EV_RECENT_VIEW_COLUMN_JOB, job_thumbnail,
-                                    -1);
+                gtk_tree_path_free (path);
 
-                ev_job_scheduler_push_job (EV_JOB (job_thumbnail), EV_JOB_PRIORITY_HIGH);
-                g_object_unref (job_thumbnail);
-        } else {
-                gtk_list_store_set (priv->model, &iter,
-                                    EV_RECENT_VIEW_COLUMN_JOB, NULL,
-                                    -1);
+                file = g_file_new_for_uri (data->uri);
+                g_file_set_attributes_async (file, file_info, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, 
NULL, NULL, NULL);
+                g_object_unref (file);
+        }
+
+        if (!data->job)
+                get_document_info_async_data_free (data);
+}
+
+static void
+load_document_and_get_document_info (GetDocumentInfoAsyncData *data)
+{
+        data->job = EV_JOB (ev_job_load_new (data->uri));
+        g_signal_connect (data->job, "finished",
+                          G_CALLBACK (document_load_job_completed_callback),
+                          data);
+        ev_job_scheduler_push_job (data->job, EV_JOB_PRIORITY_HIGH);
+}
+
+#ifdef HAVE_LIBGNOME_DESKTOP
+static void
+get_thumbnail_from_cache_thread (GTask                    *task,
+                                 EvRecentView             *ev_recent_view,
+                                 GetDocumentInfoAsyncData *data,
+                                 GCancellable             *cancellable)
+{
+        GFile           *file;
+        GFileInfo       *info;
+        gchar           *path;
+        GdkPixbuf       *thumbnail;
+        cairo_surface_t *surface = NULL;
+
+        if (g_task_return_error_if_cancelled (task))
+                return;
+
+        file = g_file_new_for_uri (data->uri);
+        info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE,
+                                  data->cancellable, NULL);
+        g_object_unref (file);
+
+        if (!info) {
+                g_task_return_pointer (task, NULL, NULL);
+                return;
+        }
+
+        data->mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+        g_object_unref (info);
+
+        path = gnome_desktop_thumbnail_factory_lookup (ev_recent_view->priv->thumbnail_factory,
+                                                       data->uri, data->mtime);
+        if (!path) {
+                g_task_return_pointer (task, NULL, NULL);
+                return;
         }
+
+        thumbnail = gdk_pixbuf_new_from_file (path, NULL);
+        g_free (path);
+
+        if (thumbnail) {
+                gint width, height;
+                gint target_width, target_height;
+
+                width = gdk_pixbuf_get_width (thumbnail);
+                height = gdk_pixbuf_get_height (thumbnail);
+
+                if (height < width) {
+                        target_width = ICON_VIEW_SIZE;
+                        target_height = (int)(ICON_VIEW_SIZE * height / width + 0.5);
+                } else {
+                        target_width = (int)(ICON_VIEW_SIZE * width / height + 0.5);
+                        target_height = ICON_VIEW_SIZE;
+                }
+
+                if (width < target_width || height < target_height) {
+                        GdkPixbuf *scaled;
+
+                        scaled = gdk_pixbuf_scale_simple (thumbnail,
+                                                          target_width,
+                                                          target_height,
+                                                          GDK_INTERP_TILES);
+                        g_object_unref (thumbnail);
+                        thumbnail = scaled;
+                } else if (width != target_width || height != target_height) {
+                        GdkPixbuf *scaled;
+
+                        scaled = gnome_desktop_thumbnail_scale_down_pixbuf (thumbnail,
+                                                                            target_width,
+                                                                            target_height);
+                        g_object_unref (thumbnail);
+                        thumbnail = scaled;
+                }
+
+                surface = ev_document_misc_surface_from_pixbuf (thumbnail);
+                g_object_unref (thumbnail);
+        }
+
+        g_task_return_pointer (task, surface, (GDestroyNotify)cairo_surface_destroy);
+}
+
+static void
+get_thumbnail_from_cache_cb (EvRecentView             *ev_recent_view,
+                             GAsyncResult             *result,
+                             GetDocumentInfoAsyncData *data)
+{
+        GTask           *task = G_TASK (result);
+        cairo_surface_t *thumbnail;
+
+        if (g_cancellable_is_cancelled (data->cancellable)) {
+                get_document_info_async_data_free (data);
+                return;
+        }
+
+        thumbnail = g_task_propagate_pointer (task, NULL);
+        if (thumbnail) {
+                add_thumbnail_to_model (data, thumbnail);
+                cairo_surface_destroy (thumbnail);
+        }
+
+        if (data->needs_metadata || data->needs_thumbnail)
+                load_document_and_get_document_info (data);
+        else
+                get_document_info_async_data_free (data);
+}
+#endif /* HAVE_LIBGNOME_DESKTOP */
+
+static void
+get_document_thumbnail_from_cache (GetDocumentInfoAsyncData *data)
+{
+#ifdef HAVE_LIBGNOME_DESKTOP
+        GTask *task;
+
+        ev_rencent_view_ensure_desktop_thumbnail_factory (data->ev_recent_view);
+        task = g_task_new (data->ev_recent_view, data->cancellable,
+                           (GAsyncReadyCallback)get_thumbnail_from_cache_cb, data);
+        g_task_set_task_data (task, data, NULL);
+        g_task_run_in_thread (task, (GTaskThreadFunc)get_thumbnail_from_cache_thread);
+        g_object_unref (task);
+#else
+        load_document_and_get_document_info (data);
+#endif /* HAVE_LIBGNOME_DESKTOP */
+}
+
+static void
+get_document_info (GetDocumentInfoAsyncData *data)
+{
+        if (data->needs_thumbnail) {
+                get_document_thumbnail_from_cache (data);
+                return;
+        }
+
+        if (data->needs_metadata) {
+                load_document_and_get_document_info (data);
+                return;
+        }
+
+        get_document_info_async_data_free (data);
+}
+
+static void
+document_query_info_cb (GFile                    *file,
+                        GAsyncResult             *result,
+                        GetDocumentInfoAsyncData *data)
+{
+        GFileInfo  *info;
+        const char *title = NULL;
+        const char *author = NULL;
+        char      **attrs;
+        guint       i;
+
+        if (g_cancellable_is_cancelled (data->cancellable)) {
+                get_document_info_async_data_free (data);
+                return;
+        }
+
+        info = g_file_query_info_finish (file, result, NULL);
+        if (!info) {
+                get_document_info (data);
+                return;
+        }
+
+        if (!g_file_info_has_namespace (info, "metadata")) {
+                get_document_info (data);
+                g_object_unref (info);
+
+                return;
+        }
+
+        attrs = g_file_info_list_attributes (info, "metadata");
+        for (i = 0; attrs[i]; i++) {
+                if (g_str_equal (attrs[i], "metadata::evince::title")) {
+                        title = g_file_info_get_attribute_string (info, attrs[i]);
+                } else if (g_str_equal (attrs[i], "metadata::evince::author")) {
+                        author = g_file_info_get_attribute_string (info, attrs[i]);
+                }
+
+                if (title && author)
+                        break;
+        }
+        g_strfreev (attrs);
+
+        if (title || author) {
+                GtkTreePath *path;
+
+                data->needs_metadata = FALSE;
+
+                path = gtk_tree_row_reference_get_path (data->row);
+                if (path) {
+                        GtkTreeIter  iter;
+
+                        gtk_tree_model_get_iter (GTK_TREE_MODEL (data->ev_recent_view->priv->model), &iter, 
path);
+
+                        if (title && title[0] != '\0') {
+                                gtk_list_store_set (data->ev_recent_view->priv->model, &iter,
+                                                    EV_RECENT_VIEW_COLUMN_PRIMARY_TEXT, title,
+                                                    -1);
+                        }
+
+                        if (author && author[0] != '\0') {
+                                gtk_list_store_set (data->ev_recent_view->priv->model, &iter,
+                                                    EV_RECENT_VIEW_COLUMN_SECONDARY_TEXT, author,
+                                                    -1);
+                        }
+
+                        gtk_tree_path_free (path);
+                }
+        }
+
+        g_object_unref (info);
+
+        get_document_info (data);
+}
+
+static GetDocumentInfoAsyncData *
+ev_recent_view_get_document_info (EvRecentView  *ev_recent_view,
+                                  const gchar   *uri,
+                                  GtkTreePath   *path)
+{
+        GFile                    *file;
+        GetDocumentInfoAsyncData *data;
+
+        data = g_slice_new0 (GetDocumentInfoAsyncData);
+        data->ev_recent_view = ev_recent_view;
+        data->uri = g_strdup (uri);
+        data->row = gtk_tree_row_reference_new (GTK_TREE_MODEL (ev_recent_view->priv->model), path);;
+        data->cancellable = g_cancellable_new ();
+        data->needs_metadata = TRUE;
+        data->needs_thumbnail = TRUE;
+
+        file = g_file_new_for_uri (uri);
+        g_file_query_info_async (file, "metadata::*", G_FILE_QUERY_INFO_NONE,
+                                 G_PRIORITY_DEFAULT, data->cancellable,
+                                 (GAsyncReadyCallback)document_query_info_cb,
+                                 data);
+        g_object_unref (file);
+
+        return data;
 }
 
 static void
@@ -324,12 +700,13 @@ ev_recent_view_refresh (EvRecentView *ev_recent_view)
         gtk_list_store_clear (priv->model);
 
         for (l = items; l && l->data; l = g_list_next (l)) {
-                EvJob           *job_load;
-                GtkRecentInfo   *info;
-                const gchar     *uri;
-                GdkPixbuf       *pixbuf;
-                cairo_surface_t *thumbnail = NULL;
-                GtkTreeIter      iter;
+                GetDocumentInfoAsyncData *data;
+                GtkTreePath              *path;
+                GtkRecentInfo            *info;
+                const gchar              *uri;
+                GdkPixbuf                *pixbuf;
+                cairo_surface_t          *thumbnail = NULL;
+                GtkTreeIter               iter;
 
                 info = (GtkRecentInfo *) l->data;
 
@@ -345,36 +722,20 @@ ev_recent_view_refresh (EvRecentView *ev_recent_view)
                         thumbnail = ev_document_misc_surface_from_pixbuf (pixbuf);
                         g_object_unref (pixbuf);
                 }
-                job_load = ev_job_load_new (uri);
-                g_signal_connect (job_load, "finished",
-                                  G_CALLBACK (document_load_job_completed_callback),
-                                  ev_recent_view);
 
                 gtk_list_store_append (priv->model, &iter);
+                path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->model), &iter);
+                data = ev_recent_view_get_document_info (ev_recent_view, uri, path);
+                gtk_tree_path_free (path);
 
                 gtk_list_store_set (priv->model, &iter,
                                     EV_RECENT_VIEW_COLUMN_URI, uri,
                                     EV_RECENT_VIEW_COLUMN_PRIMARY_TEXT, gtk_recent_info_get_display_name 
(info),
                                     EV_RECENT_VIEW_COLUMN_SECONDARY_TEXT, NULL,
                                     EV_RECENT_VIEW_COLUMN_ICON, thumbnail,
-                                    EV_RECENT_VIEW_COLUMN_JOB, job_load,
+                                    EV_RECENT_VIEW_COLUMN_ASYNC_DATA, data,
                                     -1);
 
-                if (job_load) {
-                        GtkTreePath         *path;
-                        GtkTreeRowReference *row;
-
-                        path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->model), &iter);
-                        row = gtk_tree_row_reference_new (GTK_TREE_MODEL (priv->model), path);
-                        gtk_tree_path_free (path);
-
-                        g_object_set_data_full (G_OBJECT (job_load), "row-reference", row,
-                                                (GDestroyNotify)gtk_tree_row_reference_free);
-
-                        ev_job_scheduler_push_job (EV_JOB (job_load), EV_JOB_PRIORITY_HIGH);
-                        g_object_unref (job_load);
-                }
-
                 if (thumbnail != NULL)
                         cairo_surface_destroy (thumbnail);
 
@@ -454,7 +815,7 @@ ev_recent_view_init (EvRecentView *ev_recent_view)
                                           G_TYPE_STRING,
                                           G_TYPE_STRING,
                                           CAIRO_GOBJECT_TYPE_SURFACE,
-                                          EV_TYPE_JOB);
+                                          G_TYPE_POINTER);
 
         gtk_widget_set_hexpand (GTK_WIDGET (ev_recent_view), TRUE);
         gtk_widget_set_vexpand (GTK_WIDGET (ev_recent_view), TRUE);
diff --git a/shell/ev-window.c b/shell/ev-window.c
index f64e291..4ae4829 100644
--- a/shell/ev-window.c
+++ b/shell/ev-window.c
@@ -1493,6 +1493,26 @@ ev_window_setup_document (EvWindow *ev_window)
 }
 
 static void
+ev_window_set_document_metadata (EvWindow *window)
+{
+       const EvDocumentInfo *info;
+
+       if (!window->priv->metadata)
+               return;
+
+       info = ev_document_get_info (window->priv->document);
+       if (info->fields_mask & EV_DOCUMENT_INFO_TITLE && info->title && info->title[0] != '\0')
+               ev_metadata_set_string (window->priv->metadata, "title", info->title);
+       else
+               ev_metadata_set_string (window->priv->metadata, "title", "");
+
+       if (info->fields_mask & EV_DOCUMENT_INFO_AUTHOR && info->author && info->author[0] != '\0')
+               ev_metadata_set_string (window->priv->metadata, "author", info->author);
+       else
+               ev_metadata_set_string (window->priv->metadata, "author", "");
+}
+
+static void
 ev_window_set_document (EvWindow *ev_window, EvDocument *document)
 {
        if (ev_window->priv->document == document)
@@ -1504,6 +1524,8 @@ ev_window_set_document (EvWindow *ev_window, EvDocument *document)
 
        ev_window_set_message_area (ev_window, NULL);
 
+       ev_window_set_document_metadata (ev_window);
+
        if (ev_document_get_n_pages (document) <= 0) {
                ev_window_warning_message (ev_window, "%s",
                                           _("The document contains no pages"));


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]