[evince] libview: Make a preview popover for links
- From: Germán Poo-Caamaño <gpoo src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evince] libview: Make a preview popover for links
- Date: Sun, 28 Jun 2020 16:07:50 +0000 (UTC)
commit 6fa47bc006b5847122e29163f38ee43d315863cd
Author: Mads Chr. Olesen <mads mchro dk>
Date: Wed Nov 2 18:52:33 2016 +0100
libview: Make a preview popover for links
Inspired by Wikipedia showing a preview of the contents of footnotes,
show a preview of the target of internal links in the tooltip (as a
popover).
Partially fixes #662
libview/ev-jobs.c | 2 +-
libview/ev-view-private.h | 17 ++++-
libview/ev-view.c | 190 ++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 202 insertions(+), 7 deletions(-)
---
diff --git a/libview/ev-jobs.c b/libview/ev-jobs.c
index f24808b0..c96e92cb 100644
--- a/libview/ev-jobs.c
+++ b/libview/ev-jobs.c
@@ -894,7 +894,7 @@ ev_job_thumbnail_run (EvJob *job)
}
if ((job_thumb->format == EV_JOB_THUMBNAIL_PIXBUF && pixbuf == NULL) ||
- job_thumb->thumbnail_surface == NULL) {
+ (job_thumb->format != EV_JOB_THUMBNAIL_PIXBUF && job_thumb->thumbnail_surface == NULL)) {
ev_job_failed (job,
EV_DOCUMENT_ERROR,
EV_DOCUMENT_ERROR_INVALID,
diff --git a/libview/ev-view-private.h b/libview/ev-view-private.h
index d090c758..648a4b30 100644
--- a/libview/ev-view-private.h
+++ b/libview/ev-view-private.h
@@ -131,6 +131,15 @@ typedef struct {
EvAnnotation *annot;
} MovingAnnotInfo;
+/* Information for handling link preview thumbnails */
+typedef struct {
+ EvJob *job;
+ gdouble left;
+ gdouble top;
+ GtkWidget *popover;
+ EvLink *link;
+} EvLinkPreview;
+
struct _EvView {
GtkContainer layout;
@@ -261,6 +270,9 @@ struct _EvView {
/* Current zoom center */
gdouble zoom_center_x;
gdouble zoom_center_y;
+
+ /* Link preview */
+ EvLinkPreview link_preview;
};
struct _EvViewClass {
@@ -312,6 +324,10 @@ void _ev_view_transform_doc_point_to_view_point (EvView *view,
int page,
EvPoint *doc_point,
GdkPoint *view_point);
+void _ev_view_transform_doc_point_by_rotation_scale (EvView *view,
+ int page,
+ EvPoint *doc_point,
+ GdkPoint *view_point);
void _ev_view_transform_doc_rect_to_view_rect (EvView *view,
int page,
EvRectangle *doc_rect,
@@ -323,7 +339,6 @@ gint _ev_view_get_caret_cursor_offset_at_doc_point (EvView *view,
gint page,
gdouble doc_x,
gdouble doc_y);
-
void _ev_view_clear_selection (EvView *view);
void _ev_view_set_selection (EvView *view,
GdkPoint *start_point,
diff --git a/libview/ev-view.c b/libview/ev-view.c
index 6b5150f0..a40b853e 100644
--- a/libview/ev-view.c
+++ b/libview/ev-view.c
@@ -145,6 +145,7 @@ static EvLink * ev_view_get_link_at_location (EvView
*view,
gdouble y);
static char* tip_from_link (EvView *view,
EvLink *link);
+static void _ev_view_cleanup_link_preview_popover (EvView *view);
/*** Forms ***/
static EvFormField *ev_view_get_form_field_at_location (EvView *view,
gdouble x,
@@ -1428,7 +1429,7 @@ _ev_view_transform_view_rect_to_doc_rect (EvView *view,
}
void
-_ev_view_transform_doc_point_to_view_point (EvView *view,
+_ev_view_transform_doc_point_by_rotation_scale (EvView *view,
int page,
EvPoint *doc_point,
GdkPoint *view_point)
@@ -1475,8 +1476,25 @@ _ev_view_transform_doc_point_to_view_point (EvView *view,
view_x = CLAMP ((gint)(x * view->scale + 0.5), 0, page_area.width);
view_y = CLAMP ((gint)(y * view->scale + 0.5), 0, page_area.height);
- view_point->x = view_x + page_area.x + border.left;
- view_point->y = view_y + page_area.y + border.top;
+
+ view_point->x = view_x;
+ view_point->y = view_y;
+}
+
+void
+_ev_view_transform_doc_point_to_view_point (EvView *view,
+ int page,
+ EvPoint *doc_point,
+ GdkPoint *view_point)
+{
+ GdkRectangle page_area;
+ GtkBorder border;
+ _ev_view_transform_doc_point_by_rotation_scale (view, page, doc_point, view_point);
+
+ ev_view_get_page_extents (view, page, &page_area, &border);
+
+ view_point->x = view_point->x + page_area.x + border.left;
+ view_point->y = view_point->y + page_area.y + border.top;
}
void
@@ -4400,6 +4418,8 @@ ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event)
guint state;
gboolean fit_width, fit_height;
+ _ev_view_cleanup_link_preview_popover (view);
+
state = event->state & gtk_accelerator_get_default_mod_mask ();
if (state == GDK_CONTROL_MASK) {
@@ -5037,6 +5057,72 @@ get_field_area (EvView *view,
ev_view_get_area_from_mapping (view, page, field_mapping, field, area);
}
+static void
+link_preview_show_thumbnail (GdkPixbuf *pixbuf,
+ EvView *view)
+{
+ GtkWidget *popover = view->link_preview.popover;
+ gdouble left = view->link_preview.left;
+ gdouble top = view->link_preview.top;
+ int width = MIN(gdk_pixbuf_get_width (pixbuf) - left, gtk_widget_get_allocated_width
(GTK_WIDGET (view)));
+ int height = MIN (gdk_pixbuf_get_height (pixbuf) - top, 365);
+ GdkPixbuf *thumbnail_slice;
+ GtkWidget *image_view;
+
+ thumbnail_slice = gdk_pixbuf_new_subpixbuf (pixbuf,
+ (int)left, (int)top,
+ width, height);
+ image_view = gtk_image_new_from_pixbuf (thumbnail_slice);
+
+ gtk_widget_destroy (gtk_bin_get_child (GTK_BIN (popover)));
+ gtk_container_add (GTK_CONTAINER (popover), image_view);
+ gtk_widget_show (image_view);
+
+ g_object_unref (thumbnail_slice);
+}
+
+static void
+link_preview_job_finished_cb (EvJobThumbnail *job,
+ EvView *view)
+{
+ GtkWidget *popover = view->link_preview.popover;
+ GdkPixbuf *pixbuf;
+
+ if (ev_job_is_failed (EV_JOB (job))) {
+ gtk_widget_destroy (popover);
+ view->link_preview.popover = NULL;
+ g_object_unref (job);
+ view->link_preview.job = NULL;
+
+ return;
+ }
+
+ if (ev_document_model_get_inverted_colors (view->model))
+ ev_document_misc_invert_surface (job->thumbnail_surface);
+
+ pixbuf = ev_document_misc_pixbuf_from_surface (job->thumbnail_surface);
+
+ link_preview_show_thumbnail (pixbuf, view);
+
+ g_object_unref (pixbuf);
+ g_object_unref (job);
+ view->link_preview.job = NULL;
+}
+
+static void
+_ev_view_cleanup_link_preview_popover (EvView *view) {
+ if (view->link_preview.job) {
+ ev_job_cancel (view->link_preview.job);
+ g_object_unref (view->link_preview.job);
+ view->link_preview.job = NULL;
+ }
+
+ if (view->link_preview.popover) {
+ gtk_widget_destroy (view->link_preview.popover);
+ view->link_preview.popover = NULL;
+ }
+}
+
static gboolean
ev_view_query_tooltip (GtkWidget *widget,
gint x,
@@ -5078,18 +5164,102 @@ ev_view_query_tooltip (GtkWidget *widget,
}
link = ev_view_get_link_at_location (view, x, y);
- if (!link)
+
+ if (!link) {
+ _ev_view_cleanup_link_preview_popover (view);
+ view->link_preview.link = NULL;
return FALSE;
+ }
text = tip_from_link (view, link);
if (text && g_utf8_validate (text, -1, NULL)) {
- GdkRectangle link_area;
+ GdkRectangle link_area;
+ EvLinkAction *action;
+ EvLinkDest *dest;
+ EvLinkDestType type;
+ EvPoint link_dest_doc;
+ GdkPoint link_dest_view;
+ guint link_dest_page;
+ GtkWidget *popover, *spinner;
+ cairo_surface_t *page_surface = NULL;
get_link_area (view, x, y, link, &link_area);
gtk_tooltip_set_text (tooltip, text);
gtk_tooltip_set_tip_area (tooltip, &link_area);
g_free (text);
+ if (link == view->link_preview.link)
+ return TRUE;
+
+ /* Display thumbnail, if applicable */
+ action = ev_link_get_action (link);
+ if (!action) return TRUE;
+ dest = ev_link_action_get_dest (action);
+ if (!dest) return TRUE;
+ type = ev_link_dest_get_dest_type (dest);
+
+ if (type == EV_LINK_DEST_TYPE_NAMED) {
+ dest = ev_document_links_find_link_dest (EV_DOCUMENT_LINKS (view->document),
+ ev_link_dest_get_named_dest (dest));
+ }
+
+ _ev_view_cleanup_link_preview_popover (view);
+
+ /* Init popover */
+ view->link_preview.popover = popover = gtk_popover_new (GTK_WIDGET (view));
+ gtk_popover_set_pointing_to (GTK_POPOVER (popover), &link_area);
+ gtk_popover_set_modal (GTK_POPOVER (popover), FALSE);
+
+ spinner = gtk_spinner_new ();
+ gtk_spinner_start (GTK_SPINNER (spinner));
+ gtk_container_add (GTK_CONTAINER (popover) , spinner);
+ gtk_widget_show (spinner);
+
+ /* Start thumbnailing job async */
+ link_dest_page = ev_link_dest_get_page (dest);
+ view->link_preview.job = ev_job_thumbnail_new (view->document,
+ link_dest_page,
+ view->rotation,
+ view->scale);
+ ev_job_thumbnail_set_output_format (EV_JOB_THUMBNAIL (view->link_preview.job),
+ EV_JOB_THUMBNAIL_SURFACE);
+
+ link_dest_doc.x = ev_link_dest_get_left (dest, NULL);
+ link_dest_doc.y = ev_link_dest_get_top (dest, NULL);
+ _ev_view_transform_doc_point_by_rotation_scale (view,
+ link_dest_page,
+ &link_dest_doc,
+ &link_dest_view);
+ view->link_preview.left = link_dest_view.x;
+ view->link_preview.top = link_dest_view.y;
+ view->link_preview.link = link;
+
+ page_surface = ev_pixbuf_cache_get_surface (view->pixbuf_cache,
+ link_dest_page);
+
+ if (page_surface) {
+ GdkPixbuf *slice;
+
+ slice = gdk_pixbuf_get_from_surface (page_surface, 0, 0,
+ cairo_image_surface_get_width (page_surface),
+ cairo_image_surface_get_height (page_surface));
+ link_preview_show_thumbnail (slice, view);
+
+ g_object_unref (slice);
+ }
+ else {
+ g_signal_connect (view->link_preview.job, "finished",
+ G_CALLBACK (link_preview_job_finished_cb),
+ view);
+ ev_job_scheduler_push_job (view->link_preview.job,
+ EV_JOB_PRIORITY_LOW);
+ }
+
+ if (type == EV_LINK_DEST_TYPE_NAMED)
+ g_object_unref (dest);
+
+ gtk_widget_show (popover);
+
return TRUE;
}
g_free (text);
@@ -5288,6 +5458,8 @@ ev_view_button_press_event (GtkWidget *widget,
{
EvView *view = EV_VIEW (widget);
+ _ev_view_cleanup_link_preview_popover (view);
+
if (!view->document || ev_document_get_n_pages (view->document) <= 0)
return FALSE;
@@ -6803,6 +6975,8 @@ ev_view_key_press_event (GtkWidget *widget,
EvView *view = EV_VIEW (widget);
gboolean retval;
+ _ev_view_cleanup_link_preview_popover (view);
+
if (!view->document)
return FALSE;
@@ -7392,6 +7566,12 @@ ev_view_dispose (GObject *object)
view->child_focus_idle_id = 0;
}
+ if (view->link_preview.job) {
+ ev_job_cancel (view->link_preview.job);
+ g_object_unref (view->link_preview.job);
+ view->link_preview.job = NULL;
+ }
+
gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (view), NULL);
gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (view), NULL);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]