From: Jonathan Blandford Date: Thu, 30 Jun 2005 05:44:28 +0000 (+0000) Subject: Massive changes. We now support text selection of pdfs, and not just X-Git-Tag: EVINCE_0_3_3~199 X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=commitdiff_plain;h=e93b3a265bbc02546d230204402e3f41e83d6ab5;p=evince.git Massive changes. We now support text selection of pdfs, and not just Thu Jun 30 01:43:00 2005 Jonathan Blandford * shell/*: * backend/ev-render-context.[ch]: * backend/ev-selection.[ch]: Massive changes. We now support text selection of pdfs, and not just rectangular selection. This is pretty broken still, but I want to get something into CVS. --- diff --git a/ChangeLog b/ChangeLog index f9033524..5ef20ffb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +Thu Jun 30 01:43:00 2005 Jonathan Blandford + + * shell/*: + * backend/ev-render-context.[ch]: + * backend/ev-selection.[ch]: + + Massive changes. We now support text selection of pdfs, and not + just rectangular selection. This is pretty broken still, but I + want to get something into CVS. + 2005-06-28 Nickolay V. Shmyrev * pdf/ev-poppler.cc: diff --git a/NOTES b/NOTES index 19b52b3e..d709444f 100644 --- a/NOTES +++ b/NOTES @@ -25,6 +25,29 @@ Thoughts on threading: * The primary thing we are trying to do is minimize switching pages, as doing so is slow for backends. Additionally, some operations on the - backend are slow, leaving poor interactivity. This + backend are slow, leaving poor interactivity. -- + +Thoughts on selection: + + * On button_press, we record selection_start, and set in_selection. + + * selection_list must be ordered!!! + + * On motion_notify, we update selection_end and call compute_selection, + that keeps an ordered list of the selections. + + * If any of the selection changes, we notify the pixbuf_cache right + away. + + * On button_release, we unset in_selection, and trim all the current + pixbufs. + + * If a resize (somehow) occurs, during a selection, we clear + in_selection and abort. + + * I'd like to support shift-click to handle extending the selection, + but for that to survive resizing, I might need to store the points as + doubles, etc. It should be possible to reconstruct it from the + existing EvViewSelection structs, so maybe I don't need it. diff --git a/backend/Makefile.am b/backend/Makefile.am index b000dc4e..8ce117d5 100644 --- a/backend/Makefile.am +++ b/backend/Makefile.am @@ -30,6 +30,10 @@ libevbackend_la_SOURCES= \ ev-document-info.h \ ev-ps-exporter.c \ ev-ps-exporter.h \ + ev-render-context.h \ + ev-render-context.c \ + ev-selection.h \ + ev-selection.c \ ev-document-misc.h \ ev-document-misc.c \ $(NULL) diff --git a/backend/ev-document.c b/backend/ev-document.c index 9bccc4ca..3395bf7c 100644 --- a/backend/ev-document.c +++ b/backend/ev-document.c @@ -203,9 +203,8 @@ ev_document_get_links (EvDocument *document, GdkPixbuf * -ev_document_render_pixbuf (EvDocument *document, - int page, - double scale) +ev_document_render_pixbuf (EvDocument *document, + EvRenderContext *rc) { EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); GdkPixbuf *retval; @@ -213,7 +212,7 @@ ev_document_render_pixbuf (EvDocument *document, LOG ("ev_document_render_pixbuf"); g_assert (iface->render_pixbuf); - retval = iface->render_pixbuf (document, page, scale); + retval = iface->render_pixbuf (document, rc); return retval; } @@ -250,3 +249,22 @@ ev_document_info_free (EvDocumentInfo *info) g_free (info); } + + +/* Compares two rects. returns 0 if they're equal */ +#define EPSILON 0.0000001 + +gint +ev_rect_cmp (EvRectangle *a, + EvRectangle *b) +{ + if (a == b) + return 0; + if (a == NULL || b == NULL) + return 1; + + return ! ((ABS (a->x1 - b->x1) < EPSILON) && + (ABS (a->y1 - b->y1) < EPSILON) && + (ABS (a->x2 - b->x2) < EPSILON) && + (ABS (a->y2 - b->y2) < EPSILON)); +} diff --git a/backend/ev-document.h b/backend/ev-document.h index 044014f3..e95ce96f 100644 --- a/backend/ev-document.h +++ b/backend/ev-document.h @@ -28,6 +28,7 @@ #include "ev-link.h" #include "ev-document-info.h" +#include "ev-render-context.h" G_BEGIN_DECLS @@ -55,14 +56,6 @@ typedef enum EV_DOCUMENT_ERROR_ENCRYPTED } EvDocumentError; -typedef enum -{ - EV_ORIENTATION_PORTRAIT, - EV_ORIENTATION_LANDSCAPE, - EV_ORIENTATION_UPSIDEDOWN, - EV_ORIENTATION_SEASCAPE -} EvOrientation; - typedef struct { double x1; double y1; @@ -94,9 +87,8 @@ struct _EvDocumentIface EvRectangle *rect); GList * (* get_links) (EvDocument *document, int page); - GdkPixbuf * (* render_pixbuf) (EvDocument *document, - int page, - double scale); + GdkPixbuf * (* render_pixbuf) (EvDocument *document, + EvRenderContext *rc); EvOrientation (* get_orientation) (EvDocument *document); void (* set_orientation) (EvDocument *document, EvOrientation orientation); @@ -130,12 +122,16 @@ char *ev_document_get_text (EvDocument *document, GList *ev_document_get_links (EvDocument *document, int page); GdkPixbuf *ev_document_render_pixbuf (EvDocument *document, - int page, - double scale); + EvRenderContext *rc); EvOrientation ev_document_get_orientation (EvDocument *document); void ev_document_set_orientation (EvDocument *document, EvOrientation orientation); + +gint ev_rect_cmp (EvRectangle *a, + EvRectangle *b); + + G_END_DECLS #endif diff --git a/backend/ev-render-context.c b/backend/ev-render-context.c new file mode 100644 index 00000000..2d09d446 --- /dev/null +++ b/backend/ev-render-context.c @@ -0,0 +1,79 @@ +#include "ev-render-context.h" + +static void ev_render_context_init (EvRenderContext *rc); +static void ev_render_context_class_init (EvRenderContextClass *class); + + +G_DEFINE_TYPE (EvRenderContext, ev_render_context, G_TYPE_OBJECT); + +static void ev_render_context_init (EvRenderContext *rc) { /* Do Nothing */ } + +static void +ev_render_context_dispose (GObject *object) +{ + EvRenderContext *rc; + + rc = (EvRenderContext *) object; + + if (rc->destroy) { + (*rc->destroy) (rc->data); + rc->destroy = NULL; + } + + (* G_OBJECT_CLASS (ev_render_context_parent_class)->dispose) (object); +} + +static void +ev_render_context_class_init (EvRenderContextClass *class) +{ + GObjectClass *oclass; + + oclass = G_OBJECT_CLASS (class); + + oclass->dispose = ev_render_context_dispose; +} + + +EvRenderContext * +ev_render_context_new (EvOrientation orientation, + gint page, + gdouble scale) +{ + EvRenderContext *rc; + + rc = (EvRenderContext *) g_object_new (EV_TYPE_RENDER_CONTEXT, NULL); + + rc->orientation = orientation; + rc->page = page; + rc->scale = scale; + + return rc; +} + +void +ev_render_context_set_page (EvRenderContext *rc, + gint page) +{ + g_return_if_fail (rc != NULL); + + rc->page = page; +} + +void +ev_render_context_set_orientation (EvRenderContext *rc, + EvOrientation orientation) +{ + g_return_if_fail (rc != NULL); + + rc->orientation = orientation; +} + +void +ev_render_context_set_scale (EvRenderContext *rc, + gdouble scale) +{ + g_return_if_fail (rc != NULL); + + rc->scale = scale; +} + diff --git a/backend/ev-render-context.h b/backend/ev-render-context.h new file mode 100644 index 00000000..7bd73d60 --- /dev/null +++ b/backend/ev-render-context.h @@ -0,0 +1,75 @@ +/* this file is part of evince, a gnome document viewer + * + * Copyright (C) 2005 Jonathan Blandford + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef EV_RENDER_CONTEXT_H +#define EV_RENDER_CONTEXT_H + +#include + +G_BEGIN_DECLS + +typedef struct _EvRenderContext EvRenderContext; +typedef struct _EvRenderContextClass EvRenderContextClass; + +#define EV_TYPE_RENDER_CONTEXT (ev_render_context_get_type()) +#define EV_RENDER_CONTEXT(context) ((EvRenderContext *) (context)) +#define EV_RENDER_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_RENDER_CONTEXT, EvRenderContext)) +#define EV_IS_RENDER_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_RENDER_CONTEXT)) + +typedef enum +{ + EV_ORIENTATION_PORTRAIT, + EV_ORIENTATION_LANDSCAPE, + EV_ORIENTATION_UPSIDEDOWN, + EV_ORIENTATION_SEASCAPE +} EvOrientation; + + +struct _EvRenderContextClass +{ + GObjectClass klass; +}; + +struct _EvRenderContext +{ + GObject parent; + EvOrientation orientation; + gint page; + gdouble scale; + + gpointer data; + GDestroyNotify destroy; +}; + + +GType ev_render_context_get_type (void) G_GNUC_CONST; +EvRenderContext *ev_render_context_new (EvOrientation orientation, + gint page, + gdouble scale); +void ev_render_context_set_page (EvRenderContext *rc, + gint page); +void ev_render_context_set_orientation (EvRenderContext *rc, + EvOrientation orientation); +void ev_render_context_set_scale (EvRenderContext *rc, + gdouble scale); + + +G_END_DECLS + +#endif /* !EV_RENDER_CONTEXT */ diff --git a/backend/ev-selection.c b/backend/ev-selection.c new file mode 100644 index 00000000..fc8caa60 --- /dev/null +++ b/backend/ev-selection.c @@ -0,0 +1,80 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ +/* + * Copyright (C) 2005 Red Hat, Inc. + * + * This program 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, or (at your option) + * any later version. + * + * This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include "ev-selection.h" + +static void ev_selection_base_init (gpointer g_class); + +GType +ev_selection_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GTypeInfo our_info = + { + sizeof (EvSelectionIface), + ev_selection_base_init, + NULL, + }; + + type = g_type_register_static (G_TYPE_INTERFACE, + "EvSelection", + &our_info, (GTypeFlags)0); + } + + return type; +} + +static void +ev_selection_base_init (gpointer g_class) +{ + static gboolean initialized = FALSE; + + if (!initialized) { + } +} + + +void +ev_selection_render_selection (EvSelection *selection, + EvRenderContext *rc, + GdkPixbuf **pixbuf, + EvRectangle *points, + EvRectangle *old_points) +{ + EvSelectionIface *iface = EV_SELECTION_GET_IFACE (selection); + + iface->render_selection (selection, rc, + pixbuf, + points, old_points); +} + +GdkRegion *ev_selection_get_selection_region (EvSelection *selection, + EvRenderContext *rc, + EvRectangle *points) +{ + EvSelectionIface *iface = EV_SELECTION_GET_IFACE (selection); + + return iface->get_selection_region (selection, rc, points); +} diff --git a/backend/ev-selection.h b/backend/ev-selection.h new file mode 100644 index 00000000..a07d6393 --- /dev/null +++ b/backend/ev-selection.h @@ -0,0 +1,68 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ +/* + * Copyright (C) 2000-2003 Marco Pesenti Gritti + * + * This program 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, or (at your option) + * any later version. + * + * This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef EV_SELECTION_H +#define EV_SELECTION_H + +#include +#include +#include +#include +#include "ev-document.h" + +G_BEGIN_DECLS + +#define EV_TYPE_SELECTION (ev_selection_get_type ()) +#define EV_SELECTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_SELECTION, EvSelection)) +#define EV_SELECTION_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_SELECTION, EvSelectionIface)) +#define EV_IS_SELECTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_SELECTION)) +#define EV_IS_SELECTION_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_SELECTION)) +#define EV_SELECTION_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_SELECTION, EvSelectionIface)) + +typedef struct _EvSelection EvSelection; +typedef struct _EvSelectionIface EvSelectionIface; + +struct _EvSelectionIface +{ + GTypeInterface base_iface; + + void (* render_selection) (EvSelection *selection, + EvRenderContext *rc, + GdkPixbuf **pixbuf, + EvRectangle *points, + EvRectangle *old_points); + GdkRegion * (* get_selection_region) (EvSelection *selection, + EvRenderContext *rc, + EvRectangle *points); +}; + +GType ev_selection_get_type (void); +void ev_selection_render_selection (EvSelection *selection, + EvRenderContext *rc, + GdkPixbuf **pixbuf, + EvRectangle *points, + EvRectangle *old_points); +GdkRegion *ev_selection_get_selection_region (EvSelection *selection, + EvRenderContext *rc, + EvRectangle *points); + +G_END_DECLS + +#endif diff --git a/pdf/ev-poppler.cc b/pdf/ev-poppler.cc index 9ebc614f..528ecfd4 100644 --- a/pdf/ev-poppler.cc +++ b/pdf/ev-poppler.cc @@ -33,6 +33,7 @@ #include "ev-document-fonts.h" #include "ev-document-security.h" #include "ev-document-thumbnails.h" +#include "ev-selection.h" typedef struct { PdfDocument *document; @@ -73,6 +74,7 @@ static void pdf_document_document_links_iface_init (EvDocumentLinksIface static void pdf_document_document_fonts_iface_init (EvDocumentFontsIface *iface); static void pdf_document_find_iface_init (EvDocumentFindIface *iface); static void pdf_document_ps_exporter_iface_init (EvPSExporterIface *iface); +static void pdf_selection_iface_init (EvSelectionIface *iface); static void pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails, gint page, gint size, @@ -97,6 +99,8 @@ G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT, pdf_document_find_iface_init); G_IMPLEMENT_INTERFACE (EV_TYPE_PS_EXPORTER, pdf_document_ps_exporter_iface_init); + G_IMPLEMENT_INTERFACE (EV_TYPE_SELECTION, + pdf_selection_iface_init); }); static void @@ -308,8 +312,7 @@ pdf_document_get_links (EvDocument *document, static GdkPixbuf * pdf_document_render_pixbuf (EvDocument *document, - int page, - double scale) + EvRenderContext *rc) { PdfDocument *pdf_document; PopplerPage *poppler_page; @@ -319,12 +322,12 @@ pdf_document_render_pixbuf (EvDocument *document, pdf_document = PDF_DOCUMENT (document); poppler_page = poppler_document_get_page (pdf_document->document, - page); + rc->page); set_page_orientation (pdf_document, poppler_page); poppler_page_get_size (poppler_page, &width_points, &height_points); - width = (int) ((width_points * scale) + 0.5); - height = (int) ((height_points * scale) + 0.5); + width = (int) ((width_points * rc->scale) + 0.5); + height = (int) ((height_points * rc->scale) + 0.5); pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, @@ -333,7 +336,7 @@ pdf_document_render_pixbuf (EvDocument *document, poppler_page_render_to_pixbuf (poppler_page, 0, 0, width, height, - scale, + rc->scale, pixbuf, 0, 0); @@ -543,18 +546,20 @@ pdf_document_get_orientation (EvDocument *document) } switch (pdf_document->orientation) { - case POPPLER_ORIENTATION_PORTRAIT: - result = EV_ORIENTATION_PORTRAIT; - break; - case POPPLER_ORIENTATION_LANDSCAPE: - result = EV_ORIENTATION_LANDSCAPE; - break; - case POPPLER_ORIENTATION_UPSIDEDOWN: - result = EV_ORIENTATION_UPSIDEDOWN; - break; - case POPPLER_ORIENTATION_SEASCAPE: - result = EV_ORIENTATION_SEASCAPE; - break; + case POPPLER_ORIENTATION_PORTRAIT: + result = EV_ORIENTATION_PORTRAIT; + break; + case POPPLER_ORIENTATION_LANDSCAPE: + result = EV_ORIENTATION_LANDSCAPE; + break; + case POPPLER_ORIENTATION_UPSIDEDOWN: + result = EV_ORIENTATION_UPSIDEDOWN; + break; + case POPPLER_ORIENTATION_SEASCAPE: + result = EV_ORIENTATION_SEASCAPE; + break; + default: + g_assert_not_reached (); } return result; @@ -567,18 +572,20 @@ pdf_document_set_orientation (EvDocument *document, EvOrientation orientation) PopplerOrientation poppler_orientation; switch (orientation) { - case EV_ORIENTATION_PORTRAIT: - poppler_orientation = POPPLER_ORIENTATION_PORTRAIT; - break; - case EV_ORIENTATION_LANDSCAPE: - poppler_orientation = POPPLER_ORIENTATION_LANDSCAPE; - break; - case EV_ORIENTATION_UPSIDEDOWN: - poppler_orientation = POPPLER_ORIENTATION_UPSIDEDOWN; - break; - case EV_ORIENTATION_SEASCAPE: - poppler_orientation = POPPLER_ORIENTATION_SEASCAPE; - break; + case EV_ORIENTATION_PORTRAIT: + poppler_orientation = POPPLER_ORIENTATION_PORTRAIT; + break; + case EV_ORIENTATION_LANDSCAPE: + poppler_orientation = POPPLER_ORIENTATION_LANDSCAPE; + break; + case EV_ORIENTATION_UPSIDEDOWN: + poppler_orientation = POPPLER_ORIENTATION_UPSIDEDOWN; + break; + case EV_ORIENTATION_SEASCAPE: + poppler_orientation = POPPLER_ORIENTATION_SEASCAPE; + break; + default: + g_assert_not_reached (); } pdf_document->orientation = poppler_orientation; @@ -662,7 +669,6 @@ pdf_document_fonts_fill_model (EvDocumentFonts *document_fonts, if (iter) { do { GtkTreeIter list_iter; - PopplerIndexIter *child; const char *name; name = poppler_fonts_iter_get_name (iter); @@ -1170,6 +1176,70 @@ pdf_document_ps_exporter_iface_init (EvPSExporterIface *iface) iface->end = pdf_document_ps_exporter_end; } + +void +pdf_selection_render_selection (EvSelection *selection, + EvRenderContext *rc, + GdkPixbuf **pixbuf, + EvRectangle *points, + EvRectangle *old_points) +{ + PdfDocument *pdf_document; + PopplerPage *poppler_page; + double width_points, height_points; + gint width, height; + + pdf_document = PDF_DOCUMENT (selection); + poppler_page = poppler_document_get_page (pdf_document->document, + rc->page); + set_page_orientation (pdf_document, poppler_page); + + poppler_page_get_size (poppler_page, &width_points, &height_points); + width = (int) ((width_points * rc->scale) + 0.5); + height = (int) ((height_points * rc->scale) + 0.5); + + if (*pixbuf == NULL) { + * pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + TRUE, 8, + width, height); + } + + poppler_page_render_selection (poppler_page, + rc->scale, *pixbuf, + (PopplerRectangle *)points, + (PopplerRectangle *)old_points); + g_object_unref (poppler_page); + +} + + +GdkRegion * +pdf_selection_get_selection_region (EvSelection *selection, + EvRenderContext *rc, + EvRectangle *points) +{ + PdfDocument *pdf_document; + PopplerPage *poppler_page; + GdkRegion *retval; + + pdf_document = PDF_DOCUMENT (selection); + poppler_page = poppler_document_get_page (pdf_document->document, + rc->page); + set_page_orientation (pdf_document, poppler_page); + + retval = poppler_page_get_selection_region (poppler_page, rc->scale, (PopplerRectangle *) points); + g_object_unref (poppler_page); + + return retval; +} + +static void +pdf_selection_iface_init (EvSelectionIface *iface) +{ + iface->render_selection = pdf_selection_render_selection; + iface->get_selection_region = pdf_selection_get_selection_region; +} + PdfDocument * pdf_document_new (void) { diff --git a/pixbuf/pixbuf-document.c b/pixbuf/pixbuf-document.c index 97f52718..d4a8c9af 100644 --- a/pixbuf/pixbuf-document.c +++ b/pixbuf/pixbuf-document.c @@ -139,14 +139,15 @@ pixbuf_document_get_page_size (EvDocument *document, } static GdkPixbuf* -pixbuf_document_render_pixbuf (EvDocument *document, int page, double scale) +pixbuf_document_render_pixbuf (EvDocument *document, + EvRenderContext *rc) { PixbufDocument *pixbuf_document = PIXBUF_DOCUMENT (document); GdkPixbuf *scaled_pixbuf, *rotated_pixbuf; scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf_document->pixbuf, - gdk_pixbuf_get_width (pixbuf_document->pixbuf) * scale, - gdk_pixbuf_get_height (pixbuf_document->pixbuf) * scale, + gdk_pixbuf_get_width (pixbuf_document->pixbuf) * rc->scale, + gdk_pixbuf_get_height (pixbuf_document->pixbuf) * rc->scale, GDK_INTERP_BILINEAR); rotated_pixbuf = rotate_pixbuf (document, scaled_pixbuf); diff --git a/shell/ev-jobs.c b/shell/ev-jobs.c index 20ebf856..3bc2fa88 100644 --- a/shell/ev-jobs.c +++ b/shell/ev-jobs.c @@ -3,6 +3,7 @@ #include "ev-document-thumbnails.h" #include "ev-document-links.h" #include "ev-document-fonts.h" +#include "ev-selection.h" #include "ev-async-renderer.h" static void ev_job_init (EvJob *job); @@ -110,6 +111,11 @@ ev_job_render_dispose (GObject *object) job->pixbuf = NULL; } + if (job->rc) { + g_object_unref (job->rc); + job->rc = NULL; + } + (* G_OBJECT_CLASS (ev_job_render_parent_class)->dispose) (object); } @@ -215,23 +221,31 @@ ev_job_links_run (EvJobLinks *job) EvJob * -ev_job_render_new (EvDocument *document, - gint page, - double scale, - gint width, - gint height, - gboolean include_links) +ev_job_render_new (EvDocument *document, + EvRenderContext *rc, + gint width, + gint height, + EvRectangle *selection_points, + gboolean include_links, + gboolean include_selection) { EvJobRender *job; + g_return_val_if_fail (EV_IS_RENDER_CONTEXT (rc), NULL); + if (include_selection) + g_return_val_if_fail (selection_points != NULL, NULL); + job = g_object_new (EV_TYPE_JOB_RENDER, NULL); EV_JOB (job)->document = g_object_ref (document); - job->page = page; - job->scale = scale; + job->rc = g_object_ref (rc); job->target_width = width; job->target_height = height; job->include_links = include_links; + job->include_selection = include_selection; + + if (include_selection) + job->selection_points = *selection_points; if (EV_IS_ASYNC_RENDERER (document)) { EV_JOB (job)->async = TRUE; @@ -260,15 +274,19 @@ ev_job_render_run (EvJobRender *job) if (EV_JOB (job)->async) { EvAsyncRenderer *renderer = EV_ASYNC_RENDERER (EV_JOB (job)->document); - ev_async_renderer_render_pixbuf (renderer, job->page, job->scale); + ev_async_renderer_render_pixbuf (renderer, job->rc->page, job->rc->scale); g_signal_connect (EV_JOB (job)->document, "render_finished", G_CALLBACK (render_finished_cb), job); } else { - job->pixbuf = ev_document_render_pixbuf (EV_JOB (job)->document, - job->page, - job->scale); + job->pixbuf = ev_document_render_pixbuf (EV_JOB (job)->document, job->rc); if (job->include_links) - job->link_mapping = ev_document_get_links (EV_JOB (job)->document, job->page); + job->link_mapping = ev_document_get_links (EV_JOB (job)->document, job->rc->page); + if (job->include_selection && EV_IS_SELECTION (EV_JOB (job)->document)) + ev_selection_render_selection (EV_SELECTION (EV_JOB (job)->document), + job->rc, + &(job->selection), + &(job->selection_points), + NULL); EV_JOB (job)->finished = TRUE; } diff --git a/shell/ev-jobs.h b/shell/ev-jobs.h index 900d0405..9e469093 100644 --- a/shell/ev-jobs.h +++ b/shell/ev-jobs.h @@ -45,32 +45,32 @@ typedef struct _EvJobFontsClass EvJobFontsClass; #define EV_TYPE_JOB (ev_job_get_type()) #define EV_JOB(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB, EvJob)) -#define EV_JOB_CLASS(klass) (G_TYPE_CHACK_CLASS_CAST((klass), EV_TYPE_JOB, EvJobClass)) +#define EV_JOB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_JOB, EvJobClass)) #define EV_IS_JOB(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB)) #define EV_TYPE_JOB_LINKS (ev_job_links_get_type()) #define EV_JOB_LINKS(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB_LINKS, EvJobLinks)) -#define EV_JOB_LINKS_CLASS(klass) (G_TYPE_CHACK_CLASS_CAST((klass), EV_TYPE_JOB_LINKS, EvJobLinksClass)) +#define EV_JOB_LINKS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_JOB_LINKS, EvJobLinksClass)) #define EV_IS_JOB_LINKS(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB_LINKS)) #define EV_TYPE_JOB_RENDER (ev_job_render_get_type()) #define EV_JOB_RENDER(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB_RENDER, EvJobRender)) -#define EV_JOB_RENDER_CLASS(klass) (G_TYPE_CHACK_CLASS_CAST((klass), EV_TYPE_JOB_RENDER, EvJobRenderClass)) +#define EV_JOB_RENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_JOB_RENDER, EvJobRenderClass)) #define EV_IS_JOB_RENDER(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB_RENDER)) #define EV_TYPE_JOB_THUMBNAIL (ev_job_thumbnail_get_type()) #define EV_JOB_THUMBNAIL(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB_THUMBNAIL, EvJobThumbnail)) -#define EV_JOB_THUMBNAIL_CLASS(klass) (G_TYPE_CHACK_CLASS_CAST((klass), EV_TYPE_JOB_THUMBNAIL, EvJobThumbnailClass)) +#define EV_JOB_THUMBNAIL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_JOB_THUMBNAIL, EvJobThumbnailClass)) #define EV_IS_JOB_THUMBNAIL(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB_THUMBNAIL)) #define EV_TYPE_JOB_LOAD (ev_job_load_get_type()) #define EV_JOB_LOAD(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB_LOAD, EvJobLoad)) -#define EV_JOB_LOAD_CLASS(klass) (G_TYPE_CHACK_CLASS_CAST((klass), EV_TYPE_JOB_LOAD, EvJobLoadClass)) +#define EV_JOB_LOAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_JOB_LOAD, EvJobLoadClass)) #define EV_IS_JOB_LOAD(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB_LOAD)) #define EV_TYPE_JOB_FONTS (ev_job_fonts_get_type()) #define EV_JOB_FONTS(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB_FONTS, EvJobFonts)) -#define EV_JOB_FONTS_CLASS(klass) (G_TYPE_CHACK_CLASS_CAST((klass), EV_TYPE_JOB_FONTS, EvJobFontsClass)) +#define EV_JOB_FONTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_JOB_FONTS, EvJobFontsClass)) #define EV_IS_JOB_FONTS(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB_FONTS)) typedef enum { @@ -109,13 +109,18 @@ struct _EvJobRender { EvJob parent; - gint page; - double scale; + EvRenderContext *rc; gint target_width; gint target_height; GdkPixbuf *pixbuf; + GList *link_mapping; - gboolean include_links; + + GdkPixbuf *selection; + EvRectangle selection_points; + + gint include_links : 1; + gint include_selection : 1; }; struct _EvJobRenderClass @@ -171,13 +176,14 @@ void ev_job_links_run (EvJobLinks *thumbnail); /* EvJobRender */ GType ev_job_render_get_type (void); -EvJob *ev_job_render_new (EvDocument *document, - gint page, - double scale, - gint width, - gint height, - gboolean include_links); -void ev_job_render_run (EvJobRender *thumbnail); +EvJob *ev_job_render_new (EvDocument *document, + EvRenderContext *rc, + gint width, + gint height, + EvRectangle *selection_points, + gboolean include_links, + gboolean include_selection); +void ev_job_render_run (EvJobRender *thumbnail); /* EvJobThumbnail */ GType ev_job_thumbnail_get_type (void); diff --git a/shell/ev-pixbuf-cache.c b/shell/ev-pixbuf-cache.c index 0601de05..6c5618da 100644 --- a/shell/ev-pixbuf-cache.c +++ b/shell/ev-pixbuf-cache.c @@ -1,13 +1,21 @@ #include "ev-pixbuf-cache.h" #include "ev-job-queue.h" #include "ev-page-cache.h" - +#include "ev-selection.h" typedef struct _CacheJobInfo { EvJob *job; GdkPixbuf *pixbuf; + EvRenderContext *rc; GList *link_mapping; + + /* Selection info. If the *_points structs are unset, we put -1 in x1. + * selection_points are the coordinates encapsulated in selection. + * new_points is the target selection size. */ + EvRectangle selection_points; + GdkPixbuf *selection; + EvRectangle new_points; } CacheJobInfo; struct _EvPixbufCache @@ -55,6 +63,10 @@ static CacheJobInfo *find_job_cache (EvPixbufCache *pixbuf_cach static void copy_job_to_job_info (EvJobRender *job_render, CacheJobInfo *job_info, EvPixbufCache *pixbuf_cache); +static gboolean new_selection_pixbuf_needed(EvPixbufCache *pixbuf_cache, + CacheJobInfo *job_info, + gint page, + gfloat scale); /* These are used for iterating through the prev and next arrays */ @@ -132,6 +144,13 @@ dispose_cache_job_info (CacheJobInfo *job_info, ev_link_mapping_free (job_info->link_mapping); job_info->link_mapping = NULL; } + if (job_info->selection) { + g_object_unref (G_OBJECT (job_info->selection)); + job_info->selection = NULL; + } + + job_info->selection_points.x1 = -1; + job_info->new_points.x1 = -1; } static void @@ -173,13 +192,13 @@ job_finished_cb (EvJob *job, GdkPixbuf *pixbuf; /* If the job is outside of our interest, we silently discard it */ - if ((job_render->page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size)) || - (job_render->page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))) { + if ((job_render->rc->page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size)) || + (job_render->rc->page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))) { g_object_unref (job); return; } - job_info = find_job_cache (pixbuf_cache, job_render->page); + job_info = find_job_cache (pixbuf_cache, job_render->rc->page); pixbuf = g_object_ref (job_render->pixbuf); if (job_info->pixbuf) @@ -191,6 +210,13 @@ job_finished_cb (EvJob *job, ev_link_mapping_free (job_info->link_mapping); job_info->link_mapping = job_render->link_mapping; } + if (job_render->include_selection) { + if (job_info->selection) + g_object_unref (job_info->selection); + job_info->selection_points = job_render->selection_points; + job_info->selection = job_render->selection; + g_assert (job_info->selection_points.x1 >= 0); + } if (job_info->job == job) job_info->job = NULL; @@ -216,7 +242,7 @@ check_job_size_and_unref (CacheJobInfo *job_info, return; ev_page_cache_get_size (page_cache, - EV_JOB_RENDER (job_info->job)->page, + EV_JOB_RENDER (job_info->job)->rc->page, scale, &width, &height); @@ -336,7 +362,7 @@ ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache, pixbuf_cache, page, new_job_list, new_prev_job, new_next_job, start_page, end_page, EV_JOB_PRIORITY_HIGH); - page++; + page ++; } for (i = 0; i < pixbuf_cache->preload_cache_size; i++) { @@ -441,6 +467,8 @@ add_job_if_needed (EvPixbufCache *pixbuf_cache, gfloat scale, EvJobPriority priority) { + gboolean include_links = FALSE; + gboolean include_selection = FALSE; int width, height; if (job_info->job) @@ -456,10 +484,27 @@ add_job_if_needed (EvPixbufCache *pixbuf_cache, return; /* make a new job now */ + if (job_info->rc == NULL) { + job_info->rc = ev_render_context_new (EV_ORIENTATION_PORTRAIT, + page, scale); + } else { + ev_render_context_set_page (job_info->rc, page); + ev_render_context_set_scale (job_info->rc, scale); + } + + /* Figure out what else we need for this job */ + if (job_info->link_mapping == NULL) + include_links = TRUE; + if (new_selection_pixbuf_needed (pixbuf_cache, job_info, page, scale)) { + include_selection = TRUE; + } + job_info->job = ev_job_render_new (pixbuf_cache->document, - page, scale, + job_info->rc, width, height, - (job_info->link_mapping == NULL)?TRUE:FALSE); + &(job_info->new_points), + include_links, + include_selection); ev_job_queue_add_job (job_info->job, priority); g_signal_connect (job_info->job, "finished", G_CALLBACK (job_finished_cb), pixbuf_cache); } @@ -509,7 +554,8 @@ void ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache, gint start_page, gint end_page, - gfloat scale) + gfloat scale, + GList *selection_list) { EvPageCache *page_cache; @@ -529,6 +575,9 @@ ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache, * size, we remove them if we need to. */ ev_pixbuf_cache_clear_job_sizes (pixbuf_cache, scale); + /* Next, we update the target selection for our pages */ + ev_pixbuf_cache_set_selection_list (pixbuf_cache, selection_list); + /* Finally, we add the new jobs for all the sizes that don't have a * pixbuf */ ev_pixbuf_cache_add_jobs_if_needed (pixbuf_cache, scale); @@ -571,3 +620,212 @@ ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache, return job_info->link_mapping; } + +/* Selection */ +static gboolean +new_selection_pixbuf_needed (EvPixbufCache *pixbuf_cache, + CacheJobInfo *job_info, + gint page, + gfloat scale) +{ + EvPageCache *page_cache; + gint width, height; + + if (job_info->selection) { + page_cache = ev_page_cache_get (pixbuf_cache->document); + ev_page_cache_get_size (page_cache, page, scale, + &width, &height); + + if (width != gdk_pixbuf_get_width (job_info->selection) || + height != gdk_pixbuf_get_height (job_info->selection)) + return TRUE; + } else { + if (job_info->new_points.x1 >= 0) + return TRUE; + } + return FALSE; +} + +static void +clear_selection_if_needed (EvPixbufCache *pixbuf_cache, + CacheJobInfo *job_info, + gint page, + gfloat scale) +{ + if (new_selection_pixbuf_needed (pixbuf_cache, job_info, page, scale)) { + g_object_unref (job_info->selection); + job_info->selection = NULL; + job_info->selection_points.x1 = -1; + } +} + +GList * +ev_pixbuf_cach_get_text_mapping (EvPixbufCache *pixbuf_cache, + gint page) +{ + return NULL; +} + +GdkPixbuf * +ev_pixbuf_cache_get_selection_pixbuf (EvPixbufCache *pixbuf_cache, + gint page, + gfloat scale) +{ + CacheJobInfo *job_info; + + job_info = find_job_cache (pixbuf_cache, page); + if (job_info == NULL) + return NULL; + + /* No selection on this page */ + if (job_info->new_points.x1 < 0) + return NULL; + + /* If we have a running job, we just return what we have under the + * assumption that it'll be updated later and we can scale it as need + * be */ + if (job_info->job && EV_JOB_RENDER (job_info->job)->include_selection) + return job_info->selection; + + /* Now, lets see if we need to resize the image. If we do, we clear the + * old one. */ + clear_selection_if_needed (pixbuf_cache, job_info, page, scale); + + /* Finally, we see if the two scales are the same, and get a new pixbuf + * if needed. We do this synchronously for now. At some point, we + * _should_ be able to get rid of the doc_mutex, so the synchronicity + * doesn't kill us. Rendering a few glyphs should really be fast. + */ + if (ev_rect_cmp (&(job_info->new_points), &(job_info->selection_points))) { + EvRenderContext *rc; + + rc = ev_render_context_new (EV_ORIENTATION_PORTRAIT, + page, + scale); + + /* we need to get a new selection pixbuf */ + ev_document_doc_mutex_lock (); + if (job_info->selection_points.x1 < 0) { + g_assert (job_info->selection == NULL); + ev_selection_render_selection (EV_SELECTION (pixbuf_cache->document), + rc, &(job_info->selection), + &(job_info->new_points), + NULL); + } else { + g_assert (job_info->selection != NULL); + ev_selection_render_selection (EV_SELECTION (pixbuf_cache->document), + rc, &(job_info->selection), + &(job_info->new_points), + &(job_info->selection_points)); + } + job_info->selection_points = job_info->new_points; + ev_document_doc_mutex_unlock (); + + } + return job_info->selection; +} + + +static void +update_job_selection (CacheJobInfo *job_info, + EvViewSelection *selection) +{ + if (job_info->selection == NULL) + job_info->selection_points.x1 = -1; + job_info->new_points = selection->rect; +} + +static void +clear_job_selection (CacheJobInfo *job_info) +{ + job_info->selection_points.x1 = -1; + job_info->new_points.x1 = -1; + + if (job_info->selection) { + g_object_unref (job_info->selection); + job_info->selection = NULL; + } +} + +/* This function will reset the selection on pages that no longer have them, and + * will update the target_selection on those that need it. + */ +void +ev_pixbuf_cache_set_selection_list (EvPixbufCache *pixbuf_cache, + GList *selection_list) +{ + EvPageCache *page_cache; + EvViewSelection *selection; + GList *list = selection_list; + int page; + int i; + + g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache)); + + page_cache = ev_page_cache_get (pixbuf_cache->document); + + /* We check each area to see what needs updating, and what needs freeing; */ + page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size; + for (i = 0; i < pixbuf_cache->preload_cache_size; i++) { + if (page < 0) { + page ++; + continue; + } + + selection = NULL; + while (list) { + if (((EvViewSelection *)list->data)->page == page) { + selection = list->data; + break; + } else if (((EvViewSelection *)list->data)->page > page) + break; + list = list->next; + } + + if (selection) + update_job_selection (pixbuf_cache->prev_job + i, selection); + else + clear_job_selection (pixbuf_cache->prev_job + i); + page ++; + } + + page = pixbuf_cache->start_page; + for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) { + selection = NULL; + while (list) { + if (((EvViewSelection *)list->data)->page == page) { + selection = list->data; + break; + } else if (((EvViewSelection *)list->data)->page > page) + break; + list = list->next; + } + + if (selection) + update_job_selection (pixbuf_cache->job_list + i, selection); + else + clear_job_selection (pixbuf_cache->job_list + i); + page ++; + } + + for (i = 0; i < pixbuf_cache->preload_cache_size; i++) { + if (page >= ev_page_cache_get_n_pages (page_cache)) + break; + + selection = NULL; + while (list) { + if (((EvViewSelection *)list->data)->page == page) { + selection = list->data; + break; + } else if (((EvViewSelection *)list->data)->page > page) + break; + list = list->next; + } + + if (selection) + update_job_selection (pixbuf_cache->next_job + i, selection); + else + clear_job_selection (pixbuf_cache->next_job + i); + page ++; + } +} diff --git a/shell/ev-pixbuf-cache.h b/shell/ev-pixbuf-cache.h index 0bd1cbc7..19d0311f 100644 --- a/shell/ev-pixbuf-cache.h +++ b/shell/ev-pixbuf-cache.h @@ -17,6 +17,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ +/* This File is basically an extention of EvView, and is out here just to keep + * ev-view.c from exploding. + */ + #ifndef __EV_PIXBUF_CACHE_H__ #define __EV_PIXBUF_CACHE_H__ @@ -30,24 +34,38 @@ G_BEGIN_DECLS #define EV_PIXBUF_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_PIXBUF_CACHE, EvPixbufCache)) #define EV_IS_PIXBUF_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_PIXBUF_CACHE)) -/* This is basically an extention of ev-view.c, and is out here just to keep - * ev-view.c from exploding. + + +/* The coordinates in the rect here are at scale == 1.0, so that we can ignore + * resizings. There is one per page, maximum. */ +typedef struct { + int page; + EvRectangle rect; +} EvViewSelection; typedef struct _EvPixbufCache EvPixbufCache; typedef struct _EvPixbufCacheClass EvPixbufCacheClass; GType ev_pixbuf_cache_get_type (void) G_GNUC_CONST; -EvPixbufCache *ev_pixbuf_cache_new (EvDocument *document); -void ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache, - gint start_page, - gint end_page, - gfloat scale); -GdkPixbuf *ev_pixbuf_cache_get_pixbuf (EvPixbufCache *pixbuf_cache, - gint page); -GList *ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache, - gint page); - +EvPixbufCache *ev_pixbuf_cache_new (EvDocument *document); +void ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache, + gint start_page, + gint end_page, + gfloat scale, + GList *selection_list); +GdkPixbuf *ev_pixbuf_cache_get_pixbuf (EvPixbufCache *pixbuf_cache, + gint page); +GList *ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache, + gint page); +/* Selection */ +GList *ev_pixbuf_cach_get_text_mapping (EvPixbufCache *pixbuf_cache, + gint page); +GdkPixbuf *ev_pixbuf_cache_get_selection_pixbuf (EvPixbufCache *pixbuf_cache, + gint page, + gfloat scale); +void ev_pixbuf_cache_set_selection_list (EvPixbufCache *pixbuf_cache, + GList *selection_list); G_END_DECLS diff --git a/shell/ev-view.c b/shell/ev-view.c index 9ff59dbb..29c87a29 100644 --- a/shell/ev-view.c +++ b/shell/ev-view.c @@ -29,6 +29,7 @@ #include "ev-marshal.h" #include "ev-view.h" +#include "ev-selection.h" #include "ev-document-find.h" #include "ev-document-misc.h" #include "ev-debug.h" @@ -89,18 +90,21 @@ typedef enum { #define MIN_SCALE 0.05409 #define MAX_SCALE 4.0 +/* Information for middle clicking and moving around the doc */ typedef struct { - EvRectangle rect; - int page; -} EvViewSelection; - -typedef struct { - gboolean dragging; + gboolean in_drag; GdkPoint start; gdouble hadj; gdouble vadj; } DragInfo; +/* Information for handling selection */ +typedef struct { + gboolean in_selection; + GdkPoint start; + GList *selections; +} SelectionInfo; + typedef enum { SCROLL_TO_KEEP_POSITION, SCROLL_TO_CURRENT_PAGE, @@ -118,10 +122,14 @@ struct _EvView { gint scroll_x; gint scroll_y; + /* Information for middle clicking and dragging around. */ DragInfo drag_info; + + /* Selection */ + EvViewSelectionMode selection_mode; + SelectionInfo selection_info; + gboolean pressed_button; - GdkPoint selection_start; - GList *selections; EvViewCursor cursor; GtkAdjustment *hadjustment; @@ -147,7 +155,7 @@ struct _EvView { gboolean fullscreen; gboolean presentation; EvSizingMode sizing_mode; - + PendingScroll pending_scroll; gboolean pending_resize; }; @@ -165,7 +173,7 @@ struct _EvViewClass { }; /*** Scrolling ***/ -static void scroll_to_current_page (EvView *view, +static void scroll_to_current_page (EvView *view, GtkOrientation orientation); static void ev_view_set_scroll_adjustments (EvView *view, GtkAdjustment *hadjustment, @@ -353,7 +361,7 @@ static void ev_view_primary_clear_cb (GtkClipboard gpointer data); static void ev_view_update_primary_selection (EvView *ev_view); - + G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET) static void @@ -364,7 +372,7 @@ scroll_to_current_page (EvView *view, GtkOrientation orientation) get_page_extents (view, view->current_page, &page_area, &border); - if (orientation == GTK_ORIENTATION_VERTICAL) { + if (orientation == GTK_ORIENTATION_VERTICAL) { if (view->continuous) { gtk_adjustment_clamp_page (view->vadjustment, page_area.y - view->spacing, @@ -376,13 +384,13 @@ scroll_to_current_page (EvView *view, GtkOrientation orientation) } else { if (view->dual_page) { gtk_adjustment_clamp_page (view->hadjustment, - page_area.x, + page_area.x, page_area.x + view->hadjustment->page_size); } else { gtk_adjustment_set_value (view->hadjustment, - CLAMP (view->hadjustment->value, + CLAMP (view->hadjustment->value, view->hadjustment->lower, - view->hadjustment->upper - + view->hadjustment->upper - view->hadjustment->page_size)); } } @@ -412,15 +420,15 @@ view_set_adjustment_values (EvView *view, if (!adjustment) return; - + factor = 1.0; switch (view->pending_scroll) { - case SCROLL_TO_KEEP_POSITION: + case SCROLL_TO_KEEP_POSITION: factor = (adjustment->value) / adjustment->upper; break; - case SCROLL_TO_CURRENT_PAGE: + case SCROLL_TO_CURRENT_PAGE: break; - case SCROLL_TO_CENTER: + case SCROLL_TO_CENTER: factor = (adjustment->value + adjustment->page_size * 0.5) / adjustment->upper; break; } @@ -431,18 +439,18 @@ view_set_adjustment_values (EvView *view, adjustment->lower = 0; adjustment->upper = MAX (allocation, requisition); - /* + /* * We add 0.5 to the values before to average out our rounding errors. */ switch (view->pending_scroll) { - case SCROLL_TO_KEEP_POSITION: + case SCROLL_TO_KEEP_POSITION: new_value = CLAMP (adjustment->upper * factor + 0.5, 0, adjustment->upper - adjustment->page_size); gtk_adjustment_set_value (adjustment, (int)new_value); break; - case SCROLL_TO_CURRENT_PAGE: + case SCROLL_TO_CURRENT_PAGE: scroll_to_current_page (view, orientation); break; - case SCROLL_TO_CENTER: + case SCROLL_TO_CENTER: new_value = CLAMP (adjustment->upper * factor - adjustment->page_size * 0.5 + 0.5, 0, adjustment->upper - adjustment->page_size); gtk_adjustment_set_value (adjustment, (int)new_value); @@ -465,10 +473,10 @@ view_update_range_and_current_page (EvView *view) gint current_page; gboolean found = FALSE; int i; - + if (!(view->vadjustment && view->hadjustment)) return; - + current_area.x = view->hadjustment->value; current_area.width = view->hadjustment->upper; current_area.y = view->vadjustment->value; @@ -477,25 +485,25 @@ view_update_range_and_current_page (EvView *view) for (i = 0; i < ev_page_cache_get_n_pages (view->page_cache); i++) { get_page_extents (view, i, &page_area, &border); - + if (gdk_rectangle_intersect (¤t_area, &page_area, &unused)) { if (! found) { view->start_page = i; found = TRUE; - + } view->end_page = i; } else if (found) { break; } } - + current_page = ev_page_cache_get_current_page (view->page_cache); if (current_page < view->start_page || current_page > view->end_page) { view->current_page = view->start_page; ev_page_cache_set_current_page (view->page_cache, view->start_page); - } + } } else { if (view->dual_page) { if (view->current_page % 2 == 0) { @@ -515,7 +523,8 @@ view_update_range_and_current_page (EvView *view) ev_pixbuf_cache_set_page_range (view->pixbuf_cache, view->start_page, view->end_page, - view->scale); + view->scale, + view->selection_info.selections); } static void @@ -639,7 +648,7 @@ ev_view_binding_activated (EvView *view, { GtkAdjustment *adjustment; double value; - + if (view->presentation) { switch (scroll) { case GTK_SCROLL_STEP_BACKWARD: @@ -735,13 +744,13 @@ static void get_page_y_offset (EvView *view, { int max_width, offset; GtkBorder border; - + g_return_if_fail (y_offset != NULL); - + ev_page_cache_get_max_width (view->page_cache, zoom, &max_width); compute_border (view, max_width, max_width, &border); - + if (view->dual_page) { ev_page_cache_get_height_to_page (view->page_cache, page, zoom, NULL, &offset); offset += (page / 2 + 1) * view->spacing + (page / 2) * (border.top + border.bottom); @@ -749,7 +758,7 @@ static void get_page_y_offset (EvView *view, ev_page_cache_get_height_to_page (view->page_cache, page, zoom, &offset, NULL); offset += (page + 1) * view->spacing + page * (border.top + border.bottom); } - + *y_offset = offset; return; } @@ -1150,15 +1159,15 @@ ev_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { EvView *view = EV_VIEW (widget); - + if (view->sizing_mode == EV_SIZING_FIT_WIDTH || view->sizing_mode == EV_SIZING_BEST_FIT) { g_signal_emit (view, signals[SIGNAL_ZOOM_INVALID], 0); - + ev_view_size_request (widget, &widget->requisition); } - + view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL); view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL); @@ -1166,7 +1175,7 @@ ev_view_size_allocate (GtkWidget *widget, view->pending_resize = FALSE; if (view->document) - view_update_range_and_current_page (view); + view_update_range_and_current_page (view); GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation); } @@ -1220,13 +1229,13 @@ ev_view_unrealize (GtkWidget *widget) static gboolean ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event) { - EvView *view = EV_VIEW (widget); + EvView *view = EV_VIEW (widget); - if ((event->state & GDK_CONTROL_MASK) != 0) { + if ((event->state & GDK_CONTROL_MASK) != 0) { - ev_view_set_sizing_mode (view, EV_SIZING_FREE); + ev_view_set_sizing_mode (view, EV_SIZING_FREE); - if (event->direction == GDK_SCROLL_UP || + if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_LEFT) { if (ev_view_can_zoom_in (view)) { ev_view_zoom_in (view); @@ -1234,12 +1243,12 @@ ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event) } else { if (ev_view_can_zoom_out (view)) { ev_view_zoom_out (view); - } + } } return TRUE; } - - if ((event->state & GDK_SHIFT_MASK) != 0) { + + if ((event->state & GDK_SHIFT_MASK) != 0) { if (event->direction == GDK_SCROLL_UP) event->direction = GDK_SCROLL_LEFT; if (event->direction == GDK_SCROLL_DOWN) @@ -1249,13 +1258,29 @@ ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event) return FALSE; } +static EvViewSelection * +find_selection_for_page (EvView *view, + gint page) +{ + GList *list; + + for (list = view->selection_info.selections; list != NULL; list = list->next) { + EvViewSelection *selection; + + selection = (EvViewSelection *) list->data; + + if (selection->page == page) + return selection; + } + + return NULL; +} + static gboolean ev_view_expose_event (GtkWidget *widget, GdkEventExpose *event) { EvView *view = EV_VIEW (widget); - GdkRectangle rubberband; - GList *l; int i; if (view->document == NULL) @@ -1267,26 +1292,14 @@ ev_view_expose_event (GtkWidget *widget, if (!get_page_extents (view, i, &page_area, &border)) continue; - + page_area.x -= view->scroll_x; page_area.y -= view->scroll_y; - + draw_one_page (view, i, &page_area, &border, &(event->area)); - if (EV_IS_DOCUMENT_FIND (view->document)) { + if (EV_IS_DOCUMENT_FIND (view->document)) highlight_find_results (view, i); - } - } - - for (l = view->selections; l != NULL; l = l->next) { - EvViewSelection *selection = (EvViewSelection *)l->data; - - doc_rect_to_view_rect (view, selection->page, - &selection->rect, &rubberband); - if (rubberband.width > 0 && rubberband.height > 0) { - draw_rubberband (GTK_WIDGET (view), GTK_WIDGET(view)->window, - &rubberband, 0x40); - } } return FALSE; @@ -1306,13 +1319,13 @@ ev_view_button_press_event (GtkWidget *widget, switch (event->button) { case 1: - if (view->selections) { + if (view->selection_info.selections) { clear_selection (view); gtk_widget_queue_draw (widget); } - view->selection_start.x = event->x; - view->selection_start.y = event->y; + view->selection_info.start.x = event->x; + view->selection_info.start.y = event->y; return TRUE; case 2: /* use root coordinates as reference point because @@ -1341,19 +1354,18 @@ ev_view_motion_notify_event (GtkWidget *widget, if (view->pressed_button == 1) { GdkRectangle selection; + view->selection_info.in_selection = TRUE; - selection.x = MIN (view->selection_start.x, event->x) + view->scroll_x; - selection.y = MIN (view->selection_start.y, event->y) + view->scroll_y; - selection.width = ABS (view->selection_start.x - event->x) + 1; - selection.height = ABS (view->selection_start.y - event->y) + 1; + selection.x = MIN (view->selection_info.start.x, event->x) + view->scroll_x; + selection.y = MIN (view->selection_info.start.y, event->y) + view->scroll_y; + selection.width = ABS (view->selection_info.start.x - event->x) + 1; + selection.height = ABS (view->selection_info.start.y - event->y) + 1; compute_selections (view, &selection); - gtk_widget_queue_draw (widget); - return TRUE; } else if (view->pressed_button == 2) { - if (!view->drag_info.dragging) { + if (!view->drag_info.in_drag) { gboolean start; start = gtk_drag_check_threshold (widget, @@ -1361,10 +1373,10 @@ ev_view_motion_notify_event (GtkWidget *widget, view->drag_info.start.y, event->x_root, event->y_root); - view->drag_info.dragging = start; + view->drag_info.in_drag = start; } - if (view->drag_info.dragging) { + if (view->drag_info.in_drag) { int dx, dy; gdouble dhadj_value, dvadj_value; @@ -1422,9 +1434,9 @@ ev_view_button_release_event (GtkWidget *widget, } view->pressed_button = -1; - view->drag_info.dragging = FALSE; + view->drag_info.in_drag = FALSE; - if (view->selections) { + if (view->selection_info.selections) { ev_view_update_primary_selection (view); } else if (view->document) { EvLink *link; @@ -1458,7 +1470,7 @@ draw_rubberband (GtkWidget *widget, GdkWindow *window, GdkPixbuf *pixbuf; GdkColor *fill_color_gdk; guint fill_color; - + fill_color_gdk = gdk_color_copy (>K_WIDGET (widget)->style->base[GTK_STATE_SELECTED]); fill_color = ev_gdk_color_to_rgb (fill_color_gdk) << 8 | alpha; @@ -1519,22 +1531,23 @@ highlight_find_results (EvView *view, int page) } static void -draw_one_page (EvView *view, - gint page, - GdkRectangle *page_area, - GtkBorder *border, - GdkRectangle *expose_area) +draw_one_page (EvView *view, + gint page, + GdkRectangle *page_area, + GtkBorder *border, + GdkRectangle *expose_area) { gint width, height; - GdkPixbuf *scaled_image; GdkPixbuf *current_pixbuf; GdkRectangle overlap; GdkRectangle real_page_area; + EvViewSelection *selection; g_assert (view->document); if (! gdk_rectangle_intersect (page_area, expose_area, &overlap)) return; + selection = find_selection_for_page (view, page); ev_page_cache_get_size (view->page_cache, page, view->scale, &width, &height); @@ -1551,8 +1564,18 @@ draw_one_page (EvView *view, page_area, border); if (gdk_rectangle_intersect (&real_page_area, expose_area, &overlap)) { + GdkPixbuf *selection_pixbuf = NULL; + GdkPixbuf *scaled_image; + GdkPixbuf *scaled_selection; + current_pixbuf = ev_pixbuf_cache_get_pixbuf (view->pixbuf_cache, page); + /* Get the selection pixbuf iff we have something to draw */ + if (current_pixbuf && view->selection_mode == EV_VIEW_SELECTION_TEXT && selection) + selection_pixbuf = ev_pixbuf_cache_get_selection_pixbuf (view->pixbuf_cache, + page, + view->scale); + if (current_pixbuf == NULL) scaled_image = NULL; else if (width == gdk_pixbuf_get_width (current_pixbuf) && @@ -1565,6 +1588,18 @@ draw_one_page (EvView *view, width, height, GDK_INTERP_NEAREST); + if (selection_pixbuf == NULL) + scaled_selection = NULL; + else if (width == gdk_pixbuf_get_width (selection_pixbuf) && + height == gdk_pixbuf_get_height (selection_pixbuf)) + scaled_selection = g_object_ref (selection_pixbuf); + else + /* FIXME: We don't want to scale the whole area, just the right + * area of it */ + scaled_selection = gdk_pixbuf_scale_simple (selection_pixbuf, + width, height, + GDK_INTERP_NEAREST); + if (scaled_image) { gdk_draw_pixbuf (GTK_WIDGET(view)->window, GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL], @@ -1577,6 +1612,19 @@ draw_one_page (EvView *view, 0, 0); g_object_unref (scaled_image); } + + if (scaled_selection) { + gdk_draw_pixbuf (GTK_WIDGET(view)->window, + GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL], + scaled_selection, + overlap.x - real_page_area.x, + overlap.y - real_page_area.y, + overlap.x, overlap.y, + overlap.width, overlap.height, + GDK_RGB_DITHER_NORMAL, + 0, 0); + g_object_unref (scaled_selection); + } } } @@ -1713,7 +1761,7 @@ ev_view_class_init (EvViewClass *class) class->set_scroll_adjustments = ev_view_set_scroll_adjustments; class->binding_activated = ev_view_binding_activated; - widget_class->set_scroll_adjustments_signal = + widget_class->set_scroll_adjustments_signal = g_signal_new ("set-scroll-adjustments", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, @@ -1802,7 +1850,7 @@ ev_view_class_init (EvViewClass *class) g_param_spec_double ("zoom", "Zoom factor", "Zoom factor", - MIN_SCALE, + MIN_SCALE, MAX_SCALE, 1.0, G_PARAM_READWRITE)); @@ -1825,8 +1873,10 @@ ev_view_init (EvView *view) view->current_page = 0; view->pressed_button = -1; view->cursor = EV_VIEW_CURSOR_NORMAL; - view->drag_info.dragging = FALSE; + view->drag_info.in_drag = FALSE; + view->selection_info.in_selection = FALSE; + view->selection_mode = EV_VIEW_SELECTION_TEXT; view->continuous = TRUE; view->dual_page = FALSE; view->presentation = FALSE; @@ -1861,7 +1911,7 @@ page_changed_cb (EvPageCache *page_cache, EvView *view) { if (view->current_page != new_page) { - + view->current_page = new_page; view->pending_scroll = SCROLL_TO_CURRENT_PAGE; gtk_widget_queue_resize (GTK_WIDGET (view)); @@ -1895,9 +1945,9 @@ static void on_adjustment_value_changed (GtkAdjustment *adjustment, } else { view->scroll_y = 0; } - - - if (view->pending_resize) + + + if (view->pending_resize) gtk_widget_queue_draw (GTK_WIDGET (view)); else gdk_window_scroll (GTK_WIDGET (view)->window, dx, dy); @@ -1999,7 +2049,7 @@ ev_view_set_zoom (EvView *view, view->scale = scale; view->pending_resize = TRUE; - + gtk_widget_queue_resize (GTK_WIDGET (view)); g_object_notify (G_OBJECT (view), "zoom"); @@ -2038,7 +2088,7 @@ ev_view_set_dual_page (EvView *view, if (view->dual_page == dual_page) return; - + view->pending_scroll = SCROLL_TO_CURRENT_PAGE; view->dual_page = dual_page; /* FIXME: if we're keeping the pixbuf cache around, we should extend the @@ -2118,7 +2168,7 @@ ev_view_set_sizing_mode (EvView *view, view->sizing_mode = sizing_mode; gtk_widget_queue_resize (GTK_WIDGET (view)); - + g_object_notify (G_OBJECT (view), "sizing-mode"); } @@ -2146,7 +2196,7 @@ void ev_view_zoom_in (EvView *view) { g_return_if_fail (view->sizing_mode == EV_SIZING_FREE); - + view->pending_scroll = SCROLL_TO_CENTER; ev_view_set_zoom (view, ZOOM_IN_FACTOR, TRUE); } @@ -2421,7 +2471,7 @@ ev_view_set_zoom_for_size (EvView *view, int height, int vsb_width, int hsb_height) -{ +{ g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH || view->sizing_mode == EV_SIZING_BEST_FIT); g_return_if_fail (width >= 0); @@ -2643,14 +2693,20 @@ ev_view_find_previous (EvView *view) /*** Selections ***/ -static void -compute_selections (EvView *view, GdkRectangle *view_rect) +/* compute_new_selection_rect/text calculates the area currently selected by + * view_rect. each handles a different mode; + */ +static GList * +compute_new_selection_rect (EvView *view, + GdkRectangle *view_rect) { int n_pages, i; + GList *list = NULL; - clear_selection (view); + g_assert (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE); n_pages = ev_page_cache_get_n_pages (view->page_cache); + for (i = 0; i < n_pages; i++) { GdkRectangle page_area; GtkBorder border; @@ -2666,18 +2722,137 @@ compute_selections (EvView *view, GdkRectangle *view_rect) view_rect_to_doc_rect (view, &overlap, &page_area, &(selection->rect)); - view->selections = g_list_append - (view->selections, selection); + list = g_list_append (list, selection); + } + } + } + + return list; +} + +static GList * +compute_new_selection_text (EvView *view, + GdkRectangle *view_rect) +{ + int n_pages, i; + GList *list = NULL; + EvViewSelection *first_selection = NULL; + EvViewSelection *last_selection = NULL; + gint width, height; + + g_assert (view->selection_mode == EV_VIEW_SELECTION_TEXT); + + n_pages = ev_page_cache_get_n_pages (view->page_cache); + + /* We get the two edge pages */ + for (i = 0; i < n_pages; i++) { + GdkRectangle page_area; + GtkBorder border; + + if (get_page_extents (view, i, &page_area, &border)) { + GdkRectangle overlap; + + if (gdk_rectangle_intersect (&page_area, view_rect, &overlap)) { + EvViewSelection *selection; + + if (first_selection == NULL) { + first_selection = g_new0 (EvViewSelection, 1); + selection = first_selection; + } else if (last_selection == NULL) { + last_selection = g_new0 (EvViewSelection, 1); + selection = last_selection; + } else { + selection = last_selection; + } + + selection->page = i; + view_rect_to_doc_rect (view, &overlap, &page_area, + &(selection->rect)); } } } + + /* No overlap */ + if (first_selection == NULL) + return NULL; + + /* only one selection. Return a page of it */ + if (last_selection == NULL) + return g_list_append (NULL, first_selection); + + /*clean up the selections; + */ + ev_page_cache_get_size (view->page_cache, first_selection->page, + 1.0, &width, &height); + first_selection->rect.x2 = width; + first_selection->rect.y2 = height; + list = g_list_append (NULL, first_selection); + + /* Add all the intervening pages */ + for (i = first_selection->page + 1; i < last_selection->page; i++) { + EvViewSelection *selection; + + selection = g_new0 (EvViewSelection, 1); + selection->page = i; + ev_page_cache_get_size (view->page_cache, i, + 1.0, &width, &height); + selection->rect.x1 = selection->rect.y1 = 0; + selection->rect.x2 = width; + selection->rect.y2 = height; + g_list_append (list, selection); + } + + /* Clean up the last page */ + last_selection->rect.x1 = 0; + last_selection->rect.y1 = 0; + list = g_list_append (list, last_selection); + + return list; +} + +/* This function takes the newly calculated list, and figures out which regions + * have changed. It then queues a redraw approporiately. + */ +static void +merge_selection_region (EvView *view, + GList *list) +{ + + /* FIXME: actually write... */ + clear_selection (view); + gtk_widget_queue_draw (GTK_WIDGET (view)); + + view->selection_info.selections = list; + ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, list); +} + +static void +compute_selections (EvView *view, + GdkRectangle *view_rect) +{ + GList *list; + + if (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE) + list = compute_new_selection_rect (view, view_rect); + else + list = compute_new_selection_text (view, view_rect); + merge_selection_region (view, list); +} + +/* Free's the selection. It's up to the caller to queue redraws if needed. + */ +static void +selection_free (EvViewSelection *selection) +{ + g_free (selection); } static void clear_selection (EvView *view) { - g_list_foreach (view->selections, (GFunc)g_free, NULL); - view->selections = NULL; + g_list_foreach (view->selection_info.selections, (GFunc)selection_free, NULL); + view->selection_info.selections = NULL; + view->selection_info.in_selection = FALSE; } @@ -2702,9 +2877,10 @@ ev_view_select_all (EvView *view) selection->rect.x2 = width; selection->rect.y2 = height; - view->selections = g_list_append (view->selections, selection); + view->selection_info.selections = g_list_append (view->selection_info.selections, selection); } + ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, view->selection_info.selections); gtk_widget_queue_draw (GTK_WIDGET (view)); } @@ -2718,7 +2894,7 @@ get_selected_text (EvView *ev_view) ev_document_doc_mutex_lock (); - for (l = ev_view->selections; l != NULL; l = l->next) { + for (l = ev_view->selection_info.selections; l != NULL; l = l->next) { EvViewSelection *selection = (EvViewSelection *)l->data; char *tmp; @@ -2786,7 +2962,7 @@ ev_view_update_primary_selection (EvView *ev_view) clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view), GDK_SELECTION_PRIMARY); - if (ev_view->selections) { + if (ev_view->selection_info.selections) { if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets), diff --git a/shell/ev-view.h b/shell/ev-view.h index 05b25429..0c0ebaf6 100644 --- a/shell/ev-view.h +++ b/shell/ev-view.h @@ -42,6 +42,11 @@ typedef enum { EV_SIZING_FREE, } EvSizingMode; +typedef enum { + EV_VIEW_SELECTION_TEXT, + EV_VIEW_SELECTION_RECTANGLE, +} EvViewSelectionMode; + typedef enum { EV_SCROLL_PAGE_FORWARD, EV_SCROLL_PAGE_BACKWARD diff --git a/tiff/tiff-document.c b/tiff/tiff-document.c index a4c8638a..f2ed4986 100644 --- a/tiff/tiff-document.c +++ b/tiff/tiff-document.c @@ -205,7 +205,8 @@ rotate_pixbuf (EvDocument *document, GdkPixbuf *pixbuf) } static GdkPixbuf * -tiff_document_render_pixbuf (EvDocument *document, int page, double scale) +tiff_document_render_pixbuf (EvDocument *document, + EvRenderContext *rc) { TiffDocument *tiff_document = TIFF_DOCUMENT (document); int width, height; @@ -219,7 +220,7 @@ tiff_document_render_pixbuf (EvDocument *document, int page, double scale) g_return_val_if_fail (tiff_document->tiff != NULL, 0); push_handlers (); - if (TIFFSetDirectory (tiff_document->tiff, page) != 1) + if (TIFFSetDirectory (tiff_document->tiff, rc->page) != 1) { pop_handlers (); return NULL; @@ -266,8 +267,8 @@ tiff_document_render_pixbuf (EvDocument *document, int page, double scale) pop_handlers (); scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf, - width * scale, - height * scale, + width * rc->scale, + height * rc->scale, GDK_INTERP_BILINEAR); g_object_unref (pixbuf); @@ -332,6 +333,7 @@ tiff_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document, gint size, gboolean border) { + EvRenderContext *rc; GdkPixbuf *pixbuf; gdouble w, h; @@ -339,9 +341,9 @@ tiff_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document, page, &w, &h); - pixbuf = tiff_document_render_pixbuf (EV_DOCUMENT (document), - page, - size/w); + rc = ev_render_context_new (EV_ORIENTATION_PORTRAIT, page, size/w); + pixbuf = tiff_document_render_pixbuf (EV_DOCUMENT (document), rc); + g_object_unref (G_OBJECT (rc)); if (border) {