+Thu Jun 30 01:43:00 2005 Jonathan Blandford <jrb@redhat.com>
+
+ * 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 <nshmyrev@yandex.ru>
* pdf/ev-poppler.cc:
* 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.
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)
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;
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;
}
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));
+}
#include "ev-link.h"
#include "ev-document-info.h"
+#include "ev-render-context.h"
G_BEGIN_DECLS
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;
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);
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
--- /dev/null
+#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;
+}
+
--- /dev/null
+/* this file is part of evince, a gnome document viewer
+ *
+ * Copyright (C) 2005 Jonathan Blandford <jrb@gnome.org>
+ *
+ * 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 <glib-object.h>
+
+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 */
--- /dev/null
+/* -*- 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);
+}
--- /dev/null
+/* -*- 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 <glib-object.h>
+#include <glib.h>
+#include <gdk/gdkpixbuf.h>
+#include <gdk/gdk.h>
+#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
#include "ev-document-fonts.h"
#include "ev-document-security.h"
#include "ev-document-thumbnails.h"
+#include "ev-selection.h"
typedef struct {
PdfDocument *document;
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,
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
static GdkPixbuf *
pdf_document_render_pixbuf (EvDocument *document,
- int page,
- double scale)
+ EvRenderContext *rc)
{
PdfDocument *pdf_document;
PopplerPage *poppler_page;
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,
poppler_page_render_to_pixbuf (poppler_page,
0, 0,
width, height,
- scale,
+ rc->scale,
pixbuf,
0, 0);
}
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;
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;
if (iter) {
do {
GtkTreeIter list_iter;
- PopplerIndexIter *child;
const char *name;
name = poppler_fonts_iter_get_name (iter);
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)
{
}
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);
#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);
job->pixbuf = NULL;
}
+ if (job->rc) {
+ g_object_unref (job->rc);
+ job->rc = NULL;
+ }
+
(* G_OBJECT_CLASS (ev_job_render_parent_class)->dispose) (object);
}
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;
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;
}
#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 {
{
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
/* 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);
#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
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 */
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
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)
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;
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);
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++) {
gfloat scale,
EvJobPriority priority)
{
+ gboolean include_links = FALSE;
+ gboolean include_selection = FALSE;
int width, height;
if (job_info->job)
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);
}
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;
* 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);
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 ++;
+ }
+}
* 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__
#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
#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"
#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,
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;
gboolean fullscreen;
gboolean presentation;
EvSizingMode sizing_mode;
-
+
PendingScroll pending_scroll;
gboolean pending_resize;
};
};
/*** 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,
gpointer data);
static void ev_view_update_primary_selection (EvView *ev_view);
-
+
G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET)
static void
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,
} 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));
}
}
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;
}
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);
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;
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) {
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
{
GtkAdjustment *adjustment;
double value;
-
+
if (view->presentation) {
switch (scroll) {
case GTK_SCROLL_STEP_BACKWARD:
{
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);
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;
}
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);
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);
}
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);
} 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)
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)
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;
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
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,
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;
}
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;
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;
}
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);
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) &&
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],
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);
+ }
}
}
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,
g_param_spec_double ("zoom",
"Zoom factor",
"Zoom factor",
- MIN_SCALE,
+ MIN_SCALE,
MAX_SCALE,
1.0,
G_PARAM_READWRITE));
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;
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));
} 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);
view->scale = scale;
view->pending_resize = TRUE;
-
+
gtk_widget_queue_resize (GTK_WIDGET (view));
g_object_notify (G_OBJECT (view), "zoom");
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
view->sizing_mode = sizing_mode;
gtk_widget_queue_resize (GTK_WIDGET (view));
-
+
g_object_notify (G_OBJECT (view), "sizing-mode");
}
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);
}
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);
/*** 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;
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;
}
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));
}
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;
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),
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
}
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;
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;
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);
gint size,
gboolean border)
{
+ EvRenderContext *rc;
GdkPixbuf *pixbuf;
gdouble w, h;
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)
{