mirror of
https://gitlab.gnome.org/GNOME/evince
synced 2024-07-04 16:48:55 +00:00
84a2648215
Mostly g_clear_object and g_clear_pointer. But also some small simplifications depending on the context.
834 lines
30 KiB
C
834 lines
30 KiB
C
/* this file is part of evince, a gnome document viewer
|
|
*
|
|
* Copyright (C) 2013 Aakash Goenka
|
|
* Copyright (C) 2014 Carlos Garcia Campos
|
|
*
|
|
* Evince is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Evince is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* 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>
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
#include <cairo-gobject.h>
|
|
|
|
#include "ev-recent-view.h"
|
|
#include "ev-file-helpers.h"
|
|
#include "gd-icon-utils.h"
|
|
#include "gd-two-lines-renderer.h"
|
|
#include "ev-document-misc.h"
|
|
#include "ev-document-model.h"
|
|
#include "ev-jobs.h"
|
|
#include "ev-job-scheduler.h"
|
|
|
|
#ifdef HAVE_LIBGNOME_DESKTOP
|
|
#define GNOME_DESKTOP_USE_UNSTABLE_API
|
|
#include <libgnome-desktop/gnome-desktop-thumbnail.h>
|
|
#endif
|
|
|
|
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_ASYNC_DATA,
|
|
NUM_COLUMNS
|
|
} EvRecentViewColumns;
|
|
|
|
typedef struct {
|
|
GtkWidget *view;
|
|
GtkListStore *model;
|
|
GtkRecentManager *recent_manager;
|
|
GtkTreePath *pressed_item_tree_path;
|
|
guint recent_manager_changed_handler_id;
|
|
|
|
#ifdef HAVE_LIBGNOME_DESKTOP
|
|
GnomeDesktopThumbnailFactory *thumbnail_factory;
|
|
#endif
|
|
} EvRecentViewPrivate;
|
|
|
|
enum {
|
|
ITEM_ACTIVATED,
|
|
NUM_SIGNALS
|
|
};
|
|
|
|
static guint signals[NUM_SIGNALS] = { 0, };
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (EvRecentView, ev_recent_view, GTK_TYPE_SCROLLED_WINDOW)
|
|
|
|
#define ICON_VIEW_SIZE 128
|
|
#define MAX_RECENT_VIEW_ITEMS 64
|
|
#define GET_PRIVATE(o) ev_recent_view_get_instance_private (o);
|
|
|
|
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)
|
|
{
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (data->ev_recent_view);
|
|
|
|
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);
|
|
|
|
path = gtk_tree_row_reference_get_path (data->row);
|
|
if (path) {
|
|
gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->model), &iter, path);
|
|
gtk_list_store_set (priv->model, &iter,
|
|
EV_RECENT_VIEW_COLUMN_ASYNC_DATA, NULL,
|
|
-1);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
gtk_tree_row_reference_free (data->row);
|
|
|
|
g_object_unref (data->ev_recent_view);
|
|
|
|
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;
|
|
}
|
|
|
|
static void
|
|
ev_recent_view_clear_model (EvRecentView *ev_recent_view)
|
|
{
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (ev_recent_view);
|
|
|
|
gtk_tree_model_foreach (GTK_TREE_MODEL (priv->model),
|
|
(GtkTreeModelForeachFunc)ev_recent_view_clear_async_data,
|
|
ev_recent_view);
|
|
|
|
gtk_list_store_clear (priv->model);
|
|
}
|
|
|
|
static void
|
|
ev_recent_view_dispose (GObject *obj)
|
|
{
|
|
EvRecentView *ev_recent_view = EV_RECENT_VIEW (obj);
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (ev_recent_view);
|
|
|
|
if (priv->model) {
|
|
ev_recent_view_clear_model (ev_recent_view);
|
|
g_clear_object (&priv->model);
|
|
}
|
|
|
|
if (priv->recent_manager_changed_handler_id) {
|
|
g_signal_handler_disconnect (priv->recent_manager,
|
|
priv->recent_manager_changed_handler_id);
|
|
priv->recent_manager_changed_handler_id = 0;
|
|
}
|
|
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);
|
|
}
|
|
|
|
static gint
|
|
compare_recent_items (GtkRecentInfo *a,
|
|
GtkRecentInfo *b)
|
|
{
|
|
gboolean has_ev_a, has_ev_b;
|
|
const gchar *evince = g_get_application_name ();
|
|
|
|
has_ev_a = gtk_recent_info_has_application (a, evince);
|
|
has_ev_b = gtk_recent_info_has_application (b, evince);
|
|
|
|
if (has_ev_a && has_ev_b) {
|
|
time_t time_a, time_b;
|
|
|
|
time_a = gtk_recent_info_get_modified (a);
|
|
time_b = gtk_recent_info_get_modified (b);
|
|
|
|
return (time_b - time_a);
|
|
} else if (has_ev_a) {
|
|
return -1;
|
|
} else if (has_ev_b) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static gboolean
|
|
on_query_tooltip_event (GtkWidget *widget,
|
|
gint x,
|
|
gint y,
|
|
gboolean keyboard_tip,
|
|
GtkTooltip *tooltip,
|
|
EvRecentView *ev_recent_view)
|
|
{
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (ev_recent_view);
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
GtkTreePath *path = NULL;
|
|
gchar *uri;
|
|
gchar *uri_unescaped;
|
|
|
|
model = gtk_icon_view_get_model (GTK_ICON_VIEW (priv->view));
|
|
if (!gtk_icon_view_get_tooltip_context (GTK_ICON_VIEW (priv->view),
|
|
&x, &y, keyboard_tip,
|
|
&model, &path, &iter))
|
|
return FALSE;
|
|
|
|
gtk_tree_model_get (GTK_TREE_MODEL (priv->model), &iter,
|
|
EV_RECENT_VIEW_COLUMN_URI, &uri,
|
|
-1);
|
|
|
|
uri_unescaped = g_uri_unescape_string (uri, NULL);
|
|
g_free (uri);
|
|
|
|
gtk_tooltip_set_text (tooltip, uri_unescaped);
|
|
g_free (uri_unescaped);
|
|
|
|
gtk_icon_view_set_tooltip_item (GTK_ICON_VIEW (priv->view),
|
|
tooltip, path);
|
|
gtk_tree_path_free (path);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
on_icon_view_item_activated (GtkIconView *iconview,
|
|
GtkTreePath *path,
|
|
EvRecentView *ev_recent_view)
|
|
{
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (ev_recent_view);
|
|
GtkTreeIter iter;
|
|
gchar *uri;
|
|
|
|
if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->model), &iter, path))
|
|
return;
|
|
|
|
gtk_tree_model_get (GTK_TREE_MODEL (priv->model), &iter,
|
|
EV_RECENT_VIEW_COLUMN_URI, &uri,
|
|
-1);
|
|
g_signal_emit (ev_recent_view, signals[ITEM_ACTIVATED], 0, uri);
|
|
g_free (uri);
|
|
}
|
|
|
|
static void
|
|
add_thumbnail_to_model (GetDocumentInfoAsyncData *data,
|
|
cairo_surface_t *thumbnail)
|
|
{
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (data->ev_recent_view);
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
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_surface_in_frame (thumbnail,
|
|
"resource:///org/gnome/evince/ui/thumbnail-frame.png",
|
|
&border, &border);
|
|
|
|
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_list_store_set (priv->model, &iter,
|
|
EV_RECENT_VIEW_COLUMN_ICON, surface,
|
|
-1);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
cairo_surface_destroy (surface);
|
|
}
|
|
|
|
#ifdef HAVE_LIBGNOME_DESKTOP
|
|
static void
|
|
ev_recent_view_ensure_desktop_thumbnail_factory (EvRecentView *ev_recent_view)
|
|
{
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (ev_recent_view);
|
|
|
|
if (priv->thumbnail_factory)
|
|
return;
|
|
|
|
priv->thumbnail_factory = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE);
|
|
}
|
|
|
|
static void
|
|
save_thumbnail_in_cache_thread (GTask *task,
|
|
EvRecentView *ev_recent_view,
|
|
GetDocumentInfoAsyncData *data,
|
|
GCancellable *cancellable)
|
|
{
|
|
GdkPixbuf *thumbnail;
|
|
cairo_surface_t *surface;
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (ev_recent_view);
|
|
#if defined(GNOME_DESKTOP_PLATFORM_VERSION) && GNOME_DESKTOP_PLATFORM_VERSION >= 43
|
|
GError *error = NULL;
|
|
#endif
|
|
|
|
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));
|
|
|
|
#if defined(GNOME_DESKTOP_PLATFORM_VERSION) && GNOME_DESKTOP_PLATFORM_VERSION >= 43
|
|
gnome_desktop_thumbnail_factory_save_thumbnail (priv->thumbnail_factory,
|
|
thumbnail, data->uri, data->mtime, NULL, &error);
|
|
if (error) {
|
|
g_warning ("Failed to save thumbnail %s: %s", data->uri, error->message);
|
|
g_error_free (error);
|
|
}
|
|
#else
|
|
gnome_desktop_thumbnail_factory_save_thumbnail (priv->thumbnail_factory,
|
|
thumbnail, data->uri, data->mtime);
|
|
#endif
|
|
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_recent_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) ||
|
|
ev_job_is_failed (EV_JOB (job))) {
|
|
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 = GET_PRIVATE (data->ev_recent_view);
|
|
EvDocument *document = EV_JOB (job_load)->document;
|
|
|
|
if (g_cancellable_is_cancelled (data->cancellable) ||
|
|
ev_job_is_failed (EV_JOB (job_load))) {
|
|
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) {
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
if (data->needs_metadata) {
|
|
const EvDocumentInfo *info;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
GFile *file;
|
|
GFileInfo *file_info = g_file_info_new ();
|
|
|
|
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') {
|
|
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_tree_path_free (path);
|
|
|
|
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;
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (ev_recent_view);
|
|
|
|
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 (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 = gdk_pixbuf_scale_simple (thumbnail,
|
|
target_width,
|
|
target_height,
|
|
GDK_INTERP_HYPER);
|
|
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_recent_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;
|
|
char *title = NULL;
|
|
char *author = NULL;
|
|
char **attrs;
|
|
guint i;
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (data->ev_recent_view);
|
|
|
|
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 = (gchar *) g_file_info_get_attribute_string (info, attrs[i]);
|
|
} else if (g_str_equal (attrs[i], "metadata::evince::author")) {
|
|
author = (gchar *) 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 (priv->model), &iter, path);
|
|
|
|
if (title && (g_strstrip (title))[0] != '\0')
|
|
gtk_list_store_set (priv->model, &iter,
|
|
EV_RECENT_VIEW_COLUMN_PRIMARY_TEXT, title,
|
|
-1);
|
|
|
|
if (author && (g_strstrip (author))[0] != '\0')
|
|
gtk_list_store_set (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;
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (ev_recent_view);
|
|
|
|
data = g_slice_new0 (GetDocumentInfoAsyncData);
|
|
data->ev_recent_view = g_object_ref (ev_recent_view);
|
|
data->uri = g_strdup (uri);
|
|
data->row = gtk_tree_row_reference_new (GTK_TREE_MODEL (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
|
|
ev_recent_view_refresh (EvRecentView *ev_recent_view)
|
|
{
|
|
GList *items, *l;
|
|
guint n_items = 0;
|
|
const gchar *evince = g_get_application_name ();
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (ev_recent_view);
|
|
|
|
items = gtk_recent_manager_get_items (priv->recent_manager);
|
|
items = g_list_sort (items, (GCompareFunc) compare_recent_items);
|
|
|
|
gtk_list_store_clear (priv->model);
|
|
|
|
for (l = items; l && l->data; l = g_list_next (l)) {
|
|
GetDocumentInfoAsyncData *data;
|
|
GtkTreePath *path;
|
|
GtkRecentInfo *info;
|
|
const gchar *uri;
|
|
GdkPixbuf *pixbuf;
|
|
cairo_surface_t *thumbnail = NULL;
|
|
GtkTreeIter iter;
|
|
|
|
info = (GtkRecentInfo *) l->data;
|
|
|
|
if (!gtk_recent_info_has_application (info, evince))
|
|
continue;
|
|
|
|
if (gtk_recent_info_is_local (info) && !gtk_recent_info_exists (info))
|
|
continue;
|
|
|
|
uri = gtk_recent_info_get_uri (info);
|
|
pixbuf = gtk_recent_info_get_icon (info, ICON_VIEW_SIZE);
|
|
if (pixbuf) {
|
|
thumbnail = ev_document_misc_surface_from_pixbuf (pixbuf);
|
|
g_object_unref (pixbuf);
|
|
}
|
|
|
|
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_ASYNC_DATA, data,
|
|
-1);
|
|
|
|
if (thumbnail != NULL)
|
|
cairo_surface_destroy (thumbnail);
|
|
|
|
if (++n_items == MAX_RECENT_VIEW_ITEMS)
|
|
break;
|
|
}
|
|
|
|
g_list_free_full (items, (GDestroyNotify)gtk_recent_info_unref);
|
|
}
|
|
|
|
static void
|
|
ev_recent_view_constructed (GObject *object)
|
|
{
|
|
EvRecentView *ev_recent_view = EV_RECENT_VIEW (object);
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (ev_recent_view);
|
|
|
|
G_OBJECT_CLASS (ev_recent_view_parent_class)->constructed (object);
|
|
|
|
g_signal_connect (priv->view, "item-activated",
|
|
G_CALLBACK (on_icon_view_item_activated),
|
|
ev_recent_view);
|
|
g_signal_connect (priv->view, "query-tooltip",
|
|
G_CALLBACK (on_query_tooltip_event),
|
|
ev_recent_view);
|
|
|
|
gtk_icon_view_set_model (GTK_ICON_VIEW (priv->view),
|
|
GTK_TREE_MODEL (priv->model));
|
|
|
|
ev_recent_view_refresh (ev_recent_view);
|
|
}
|
|
|
|
static void
|
|
ev_recent_view_init (EvRecentView *ev_recent_view)
|
|
{
|
|
EvRecentViewPrivate *priv = GET_PRIVATE (ev_recent_view);
|
|
|
|
gtk_widget_init_template (GTK_WIDGET (ev_recent_view));
|
|
|
|
priv->recent_manager = gtk_recent_manager_get_default ();
|
|
priv->model = gtk_list_store_new (NUM_COLUMNS,
|
|
G_TYPE_STRING,
|
|
G_TYPE_STRING,
|
|
G_TYPE_STRING,
|
|
CAIRO_GOBJECT_TYPE_SURFACE,
|
|
G_TYPE_POINTER);
|
|
priv->recent_manager_changed_handler_id =
|
|
g_signal_connect_swapped (priv->recent_manager,
|
|
"changed",
|
|
G_CALLBACK (ev_recent_view_refresh),
|
|
ev_recent_view);
|
|
}
|
|
|
|
static void
|
|
ev_recent_view_class_init (EvRecentViewClass *klass)
|
|
{
|
|
GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
g_object_class->constructed = ev_recent_view_constructed;
|
|
g_object_class->dispose = ev_recent_view_dispose;
|
|
|
|
g_type_ensure (GD_TYPE_TWO_LINES_RENDERER);
|
|
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/evince/ui/recent-view.ui");
|
|
gtk_widget_class_bind_template_child_private (widget_class, EvRecentView, view);
|
|
|
|
signals[ITEM_ACTIVATED] =
|
|
g_signal_new ("item-activated",
|
|
EV_TYPE_RECENT_VIEW,
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_generic,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_STRING);
|
|
}
|
|
|
|
GtkWidget *
|
|
ev_recent_view_new (void)
|
|
{
|
|
return GTK_WIDGET (g_object_new (EV_TYPE_RECENT_VIEW, NULL));
|
|
}
|