+Sat Aug 6 01:12:44 2005 Jonathan Blandford <jrb@redhat.com>
+
+ * NOTES:
+ * backend/ev-selection.c: (ev_selection_render_selection):
+ * backend/ev-selection.h:
+ * pdf/ev-poppler.cc:
+ * shell/ev-jobs.c: (ev_job_render_dispose), (ev_job_render_new),
+ (ev_job_render_run):
+ * shell/ev-jobs.h:
+ * shell/ev-pixbuf-cache.c: (dispose_cache_job_info),
+ (ev_pixbuf_cache_new), (job_finished_cb), (add_job_if_needed),
+ (convert_gdk_color_to_uint), (ev_pixbuf_cache_get_text_mapping),
+ (ev_pixbuf_cache_style_changed),
+ (ev_pixbuf_cache_get_selection_pixbuf),
+ (ev_pixbuf_cache_set_selection_list),
+ (ev_pixbuf_cache_get_selection_list):
+ * shell/ev-pixbuf-cache.h:
+ * shell/ev-utils.c: (ev_print_region_contents):
+ * shell/ev-utils.h:
+ * shell/ev-view.c: (ev_view_queue_draw_page),
+ (selection_update_idle_cb), (ev_view_motion_notify_event),
+ (ev_view_style_set), (draw_one_page), (ev_view_class_init),
+ (setup_caches), (ev_view_find_next), (merge_selection_region),
+ (selection_free):
+ * shell/ev-window.c: (ev_window_update_fullscreen_popup):
+
+ Clean up selection to be much smoother!
+
2005-08-05 Kristian Høgsberg <krh@redhat.com>
* pdf/ev-poppler.cc (pdf_selection_render_selection): Pass colors
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.
+
+Things that can add to the selection:
+ * C-a/select all
+ * click-drag
+ * dbl click
+ * triple click
+ * shift-click
+ * search
+ * shift-cursor move (later)
EvRenderContext *rc,
GdkPixbuf **pixbuf,
EvRectangle *points,
- EvRectangle *old_points)
+ EvRectangle *old_points,
+ guint text,
+ guint base)
{
EvSelectionIface *iface = EV_SELECTION_GET_IFACE (selection);
iface->render_selection (selection, rc,
pixbuf,
- points, old_points);
+ points, old_points,
+ text, base);
}
GdkRegion *
EvRenderContext *rc,
GdkPixbuf **pixbuf,
EvRectangle *points,
- EvRectangle *old_points);
+ EvRectangle *old_points,
+ guint text,
+ guint base);
GdkRegion * (* get_selection_map) (EvSelection *selection,
EvRenderContext *rc);
GdkRegion * (* get_selection_region) (EvSelection *selection,
EvRenderContext *rc,
GdkPixbuf **pixbuf,
EvRectangle *points,
- EvRectangle *old_points);
+ EvRectangle *old_points,
+ guint text,
+ guint base);
GdkRegion *ev_selection_get_selection_map (EvSelection *selection,
EvRenderContext *rc);
GdkRegion *ev_selection_get_selection_region (EvSelection *selection,
pdf_selection_iface_init);
});
+
+static void
+set_rc_data (PdfDocument *pdf_document,
+ EvRenderContext *rc)
+{
+ if (rc->data == NULL) {
+ rc->data = poppler_document_get_page (pdf_document->document,
+ rc->page);
+ rc->destroy = g_object_unref;
+ } else {
+ g_assert (rc->page == poppler_page_get_index (POPPLER_PAGE (rc->data)));
+ }
+}
+
static void
pdf_document_search_free (PdfDocumentSearch *search)
{
EvRenderContext *rc)
{
PdfDocument *pdf_document;
- PopplerPage *poppler_page;
GdkPixbuf *pixbuf;
double width_points, height_points;
gint width, height;
pdf_document = PDF_DOCUMENT (document);
- poppler_page = poppler_document_get_page (pdf_document->document,
- rc->page);
- set_page_orientation (pdf_document, poppler_page, rc->rotation);
- poppler_page_get_size (poppler_page, &width_points, &height_points);
+ set_rc_data (pdf_document, rc);
+ set_page_orientation (pdf_document, POPPLER_PAGE (rc->data), rc->rotation);
+
+ poppler_page_get_size (POPPLER_PAGE (rc->data), &width_points, &height_points);
width = (int) ((width_points * rc->scale) + 0.5);
height = (int) ((height_points * rc->scale) + 0.5);
FALSE, 8,
width, height);
- poppler_page_render_to_pixbuf (poppler_page,
+ poppler_page_render_to_pixbuf (POPPLER_PAGE (rc->data),
0, 0,
width, height,
rc->scale,
pixbuf);
- g_object_unref (poppler_page);
return pixbuf;
}
EvRenderContext *rc,
GdkPixbuf **pixbuf,
EvRectangle *points,
- EvRectangle *old_points)
+ EvRectangle *old_points,
+ guint text,
+ guint base)
{
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, rc->rotation);
+ set_rc_data (pdf_document, rc);
- poppler_page_get_size (poppler_page, &width_points, &height_points);
+ set_page_orientation (pdf_document, POPPLER_PAGE (rc->data), rc->rotation);
+
+ poppler_page_get_size (POPPLER_PAGE (rc->data), &width_points, &height_points);
width = (int) ((width_points * rc->scale) + 0.5);
height = (int) ((height_points * rc->scale) + 0.5);
width, height);
}
- /* FIXME: Hardcoded clearlooks selection color. We should
- * track theme color changes and focus out event and update
- * selection color accordingly. */
- poppler_page_render_selection (poppler_page,
+ poppler_page_render_selection (POPPLER_PAGE (rc->data),
rc->scale, *pixbuf,
(PopplerRectangle *)points,
(PopplerRectangle *)old_points,
- 0x00ffffff, 0x007c99ad);
- g_object_unref (poppler_page);
-
+ text,
+ base);
}
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, rc->rotation);
- retval = poppler_page_get_selection_region (poppler_page, rc->scale, (PopplerRectangle *) points);
- g_object_unref (poppler_page);
+ set_rc_data (pdf_document, rc);
+ set_page_orientation (pdf_document, POPPLER_PAGE (rc->data), rc->rotation);
+
+ retval = poppler_page_get_selection_region ((PopplerPage *)rc->data, rc->scale, (PopplerRectangle *) points);
return retval;
}
job->rc = NULL;
}
+ if (job->selection) {
+ g_object_unref (job->selection);
+ job->selection = NULL;
+ }
+
+ if (job->selection_region) {
+ gdk_region_destroy (job->selection_region);
+ job->selection_region = NULL;
+ }
+
(* G_OBJECT_CLASS (ev_job_render_parent_class)->dispose) (object);
}
gint width,
gint height,
EvRectangle *selection_points,
+ guint text,
+ guint base,
gboolean include_links,
gboolean include_text,
gboolean include_selection)
job->rc = g_object_ref (rc);
job->target_width = width;
job->target_height = height;
+ job->text = text;
+ job->base = base;
job->include_links = include_links;
job->include_text = include_text;
job->include_selection = include_selection;
job->link_mapping = ev_document_get_links (EV_JOB (job)->document, job->rc->page);
if (job->include_text && EV_IS_SELECTION (EV_JOB (job)->document))
job->text_mapping = ev_selection_get_selection_map (EV_SELECTION (EV_JOB (job)->document), job->rc);
- if (job->include_selection && EV_IS_SELECTION (EV_JOB (job)->document))
+ 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);
+ NULL,
+ job->text, job->base);
+ job->selection_region =
+ ev_selection_get_selection_region (EV_SELECTION (EV_JOB (job)->document),
+ job->rc,
+ &(job->selection_points));
+ }
EV_JOB (job)->finished = TRUE;
}
GdkRegion *text_mapping;
GdkPixbuf *selection;
+ GdkRegion *selection_region;
EvRectangle selection_points;
+ guint base;
+ guint text;
gint include_links : 1;
gint include_text : 1;
gint width,
gint height,
EvRectangle *selection_points,
+ guint text,
+ guint base,
gboolean include_links,
gboolean include_text,
gboolean include_selection);
EvRenderContext *rc;
GList *link_mapping;
GdkRegion *text_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;
+ GdkRegion *selection_region;
EvRectangle new_points;
} CacheJobInfo;
{
GObject parent;
- EvDocument *document;
+ /* We keep a link to our containing view just for style information. */
+ GtkWidget *view;
+ EvDocument *document;
int start_page;
int end_page;
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);
+static guint convert_gdk_color_to_uint (GdkColor *color);
+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 */
g_object_unref (G_OBJECT (job_info->selection));
job_info->selection = NULL;
}
+ if (job_info->selection_region) {
+ gdk_region_destroy (job_info->selection_region);
+ job_info->selection_region = NULL;
+ }
job_info->selection_points.x1 = -1;
job_info->new_points.x1 = -1;
EvPixbufCache *
-ev_pixbuf_cache_new (EvDocument *document)
+ev_pixbuf_cache_new (GtkWidget *view,
+ EvDocument *document)
{
EvPixbufCache *pixbuf_cache;
pixbuf_cache = (EvPixbufCache *) g_object_new (EV_TYPE_PIXBUF_CACHE, NULL);
+ /* This is a backlink, so we don't ref this */
+ pixbuf_cache->view = view;
pixbuf_cache->document = document;
return pixbuf_cache;
}
if (job_render->include_selection) {
+ pixbuf = g_object_ref (job_render->selection);
if (job_info->selection)
g_object_unref (job_info->selection);
+ if (job_info->selection_region)
+ gdk_region_destroy (job_info->selection_region);
job_info->selection_points = job_render->selection_points;
- job_info->selection = job_render->selection;
+ job_info->selection_region = gdk_region_copy (job_render->selection_region);
+ job_info->selection = pixbuf;
g_assert (job_info->selection_points.x1 >= 0);
}
gboolean include_text = FALSE;
gboolean include_selection = FALSE;
int width, height;
+ guint text, base;
if (job_info->job)
return;
if (job_info->rc == NULL) {
job_info->rc = ev_render_context_new (rotation, page, scale);
} else {
+ ev_render_context_set_rotation (job_info->rc, rotation);
ev_render_context_set_page (job_info->rc, page);
ev_render_context_set_scale (job_info->rc, scale);
- ev_render_context_set_rotation (job_info->rc, rotation);
}
/* Figure out what else we need for this job */
include_selection = TRUE;
}
+ gtk_widget_ensure_style (pixbuf_cache->view);
+
+ text = convert_gdk_color_to_uint (& (pixbuf_cache->view->style->text [GTK_STATE_SELECTED]));
+ base = convert_gdk_color_to_uint (& (pixbuf_cache->view->style->base [GTK_STATE_SELECTED]));
+
job_info->job = ev_job_render_new (pixbuf_cache->document,
job_info->rc,
width, height,
&(job_info->new_points),
+ text, base,
include_links,
include_text,
include_selection);
}
/* Selection */
+static guint
+convert_gdk_color_to_uint (GdkColor *color)
+{
+ g_assert (color);
+
+ return 0xff << 24 |
+ (color->red & 0xff00) << 8 |
+ (color->green & 0xff00) |
+ (color->blue & 0xff00) >> 8;
+}
+
static gboolean
new_selection_pixbuf_needed (EvPixbufCache *pixbuf_cache,
CacheJobInfo *job_info,
GdkRegion *
ev_pixbuf_cache_get_text_mapping (EvPixbufCache *pixbuf_cache,
- gint page)
+ gint page)
{
CacheJobInfo *job_info;
}
+void
+ev_pixbuf_cache_style_changed (EvPixbufCache *pixbuf_cache)
+{
+ gint i;
+
+ /* FIXME: doesn't update running jobs. */
+ for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
+ CacheJobInfo *job_info;
+
+ job_info = pixbuf_cache->prev_job + i;
+ if (job_info->selection) {
+ g_object_unref (G_OBJECT (job_info->selection));
+ job_info->selection = NULL;
+ }
+
+ job_info = pixbuf_cache->next_job + i;
+ if (job_info->selection) {
+ g_object_unref (G_OBJECT (job_info->selection));
+ job_info->selection = NULL;
+ }
+ }
+
+ for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
+ CacheJobInfo *job_info;
+
+ job_info = pixbuf_cache->job_list + i;
+ if (job_info->selection) {
+ g_object_unref (G_OBJECT (job_info->selection));
+ job_info->selection = NULL;
+ }
+ }
+}
+
GdkPixbuf *
-ev_pixbuf_cache_get_selection_pixbuf (EvPixbufCache *pixbuf_cache,
- gint page,
- gfloat scale)
+ev_pixbuf_cache_get_selection_pixbuf (EvPixbufCache *pixbuf_cache,
+ gint page,
+ gfloat scale,
+ GdkRegion **region)
{
CacheJobInfo *job_info;
if (job_info->new_points.x1 < 0)
return NULL;
+ /* Update the rc */
+ g_assert (job_info->rc);
+ ev_render_context_set_scale (job_info->rc, scale);
+
/* 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 */
* 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 (0, page, scale);
+ EvRectangle *old_points;
+ guint text, base;
/* 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);
+ old_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));
+ old_points = &(job_info->selection_points);
}
+
+ if (job_info->selection_region)
+ gdk_region_destroy (job_info->selection_region);
+ job_info->selection_region =
+ ev_selection_get_selection_region (EV_SELECTION (pixbuf_cache->document),
+ job_info->rc,
+ &(job_info->new_points));
+
+ gtk_widget_ensure_style (pixbuf_cache->view);
+
+ text = convert_gdk_color_to_uint (& (pixbuf_cache->view->style->text [GTK_STATE_SELECTED]));
+ base = convert_gdk_color_to_uint (& (pixbuf_cache->view->style->base [GTK_STATE_SELECTED]));
+
+ ev_selection_render_selection (EV_SELECTION (pixbuf_cache->document),
+ job_info->rc, &(job_info->selection),
+ &(job_info->new_points),
+ old_points,
+ text, base);
job_info->selection_points = job_info->new_points;
ev_document_doc_mutex_unlock ();
-
}
+ if (region)
+ *region = job_info->selection_region;
return job_info->selection;
}
-
static void
update_job_selection (CacheJobInfo *job_info,
EvViewSelection *selection)
}
/* This function will reset the selection on pages that no longer have them, and
- * will update the target_selection on those that need it.
+ * will update the target_selection on those that need it. It will _not_ free
+ * the previous selection_list -- that's up to caller to do.
*/
void
ev_pixbuf_cache_set_selection_list (EvPixbufCache *pixbuf_cache,
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) {
page ++;
}
}
+
+
+/* Returns what the pixbuf cache thinks is */
+
+GList *
+ev_pixbuf_cache_get_selection_list (EvPixbufCache *pixbuf_cache)
+{
+ EvPageCache *page_cache;
+ EvViewSelection *selection;
+ GList *retval = NULL;
+ int page;
+ int i;
+
+ g_return_val_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache), NULL);
+
+ 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;
+ }
+
+ if (pixbuf_cache->prev_job[i].selection_points.x1 != -1) {
+ selection = g_new0 (EvViewSelection, 1);
+ selection->page = page;
+ selection->rect = pixbuf_cache->prev_job[i].selection_points;
+ if (pixbuf_cache->prev_job[i].selection_region)
+ selection->covered_region = gdk_region_copy (pixbuf_cache->prev_job[i].selection_region);
+ retval = g_list_append (retval, selection);
+ }
+
+ page ++;
+ }
+
+ page = pixbuf_cache->start_page;
+ for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
+ if (pixbuf_cache->job_list[i].selection_points.x1 != -1) {
+ selection = g_new0 (EvViewSelection, 1);
+ selection->page = page;
+ selection->rect = pixbuf_cache->job_list[i].selection_points;
+ if (pixbuf_cache->job_list[i].selection_region)
+ selection->covered_region = gdk_region_copy (pixbuf_cache->job_list[i].selection_region);
+ retval = g_list_append (retval, selection);
+ }
+
+ page ++;
+ }
+
+ for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
+ if (page >= ev_page_cache_get_n_pages (page_cache))
+ break;
+
+ if (pixbuf_cache->next_job[i].selection_points.x1 != -1) {
+ selection = g_new0 (EvViewSelection, 1);
+ selection->page = page;
+ selection->rect = pixbuf_cache->next_job[i].selection_points;
+ if (pixbuf_cache->next_job[i].selection_region)
+ selection->covered_region = gdk_region_copy (pixbuf_cache->next_job[i].selection_region);
+ retval = g_list_append (retval, selection);
+ }
+
+ page ++;
+ }
+
+ return retval;
+}
+
typedef struct {
int page;
EvRectangle rect;
+ GdkRegion *covered_region;
} 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);
+EvPixbufCache *ev_pixbuf_cache_new (GtkWidget *view,
+ EvDocument *document);
void ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache,
gint start_page,
gint end_page,
GdkRegion *ev_pixbuf_cache_get_text_mapping (EvPixbufCache *pixbuf_cache,
gint page);
void ev_pixbuf_cache_clear (EvPixbufCache *pixbuf_cache);
+void ev_pixbuf_cache_style_changed (EvPixbufCache *pixbuf_cache);
/* Selection */
GdkPixbuf *ev_pixbuf_cache_get_selection_pixbuf (EvPixbufCache *pixbuf_cache,
gint page,
- gfloat scale);
+ gfloat scale,
+ GdkRegion **region);
void ev_pixbuf_cache_set_selection_list (EvPixbufCache *pixbuf_cache,
GList *selection_list);
+GList *ev_pixbuf_cache_get_selection_list (EvPixbufCache *pixbuf_cache);
G_END_DECLS
}
+/* Simple function to output the contents of a region. Used solely for testing
+ * the region code.
+ */
+void
+ev_print_region_contents (GdkRegion *region)
+{
+ GdkRectangle *rectangles = NULL;
+ gint n_rectangles, i;
+
+ if (region == NULL) {
+ g_print ("<empty region>\n");
+ return;
+ }
+
+ g_print ("<region %p>\n", region);
+ gdk_region_get_rectangles (region, &rectangles, &n_rectangles);
+ for (i = 0; i < n_rectangles; i++) {
+ g_print ("\t(%d %d, %d %d) [%dx%d]\n",
+ rectangles[i].x,
+ rectangles[i].y,
+ rectangles[i].x + rectangles[i].width,
+ rectangles[i].y + rectangles[i].height,
+ rectangles[i].width,
+ rectangles[i].height);
+ }
+ g_free (rectangles);
+}
+
+
#ifndef HAVE_G_FILE_SET_CONTENTS
#include <errno.h>
#define __EV_UTILS_H__
#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
G_BEGIN_DECLS
GdkPixbuf *ev_pixbuf_add_shadow (GdkPixbuf *src, int size,
int x_offset, int y_offset, double opacity);
+void ev_print_region_contents (GdkRegion *region);
+
#ifndef HAVE_G_FILE_SET_CONTENTS
gboolean ev_file_set_contents (const gchar *filename,
#include "ev-marshal.h"
#include "ev-view.h"
+#include "ev-utils.h"
#include "ev-selection.h"
#include "ev-document-find.h"
#include "ev-document-misc.h"
char *status;
char *find_status;
+ /* Scrolling */
+ GtkAdjustment *hadjustment;
+ GtkAdjustment *vadjustment;
+
gint scroll_x;
gint scroll_y;
DragInfo drag_info;
/* Selection */
+ gint motion_x;
+ gint motion_y;
+ guint selection_update_id;
+
EvViewSelectionMode selection_mode;
SelectionInfo selection_info;
- gboolean pressed_button;
+ int pressed_button;
EvViewCursor cursor;
- GtkAdjustment *hadjustment;
- GtkAdjustment *vadjustment;
-
EvPageCache *page_cache;
EvPixbufCache *pixbuf_cache;
gint *page,
gint *x_offset,
gint *y_offset);
+static void ev_view_queue_draw_page (EvView *view,
+ gint page);
/*** Hyperrefs ***/
static EvLink* get_link_at_location (EvView *view,
GdkEventButton *event);
static gboolean ev_view_leave_notify_event (GtkWidget *widget,
GdkEventCrossing *event);
+static void ev_view_style_set (GtkWidget *widget,
+ GtkStyle *old_style);
/*** Drawing ***/
static guint32 ev_gdk_color_to_rgb (const GdkColor *color);
GdkPoint *start,
GdkPoint *stop);
static void clear_selection (EvView *view);
+static void selection_free (EvViewSelection *selection);
static char* get_selected_text (EvView *ev_view);
static void ev_view_primary_get_cb (GtkClipboard *clipboard,
GtkSelectionData *selection_data,
*page = -1;
}
+static void
+ev_view_queue_draw_page (EvView *view,
+ gint page)
+{
+ /* FIXME: write */
+ gtk_widget_queue_draw (GTK_WIDGET (view));
+}
+
static gboolean
location_in_text (EvView *view,
gdouble x,
return FALSE;
}
+
+static gboolean
+selection_update_idle_cb (EvView *view)
+{
+ GdkPoint point;
+ point.x = view->motion_x;
+ point.y = view->motion_y;
+ compute_selections (view, &view->selection_info.start, &point);
+
+ view->selection_update_id = 0;
+ return FALSE;
+}
+
static gboolean
ev_view_motion_notify_event (GtkWidget *widget,
GdkEventMotion *event)
return FALSE;
if (view->pressed_button == 1) {
- GdkPoint point;
-
view->selection_info.in_selection = TRUE;
- point.x = event->x + view->scroll_x;
- point.y = event->y + view->scroll_y;
- compute_selections (view, &view->selection_info.start, &point);
+ view->motion_x = event->x + view->scroll_x;
+ view->motion_y = event->y + view->scroll_y;
+
+ /* Queue an idle to handle the motion. We do this because
+ * handling any selection events in the motion is probably going
+ * to be slower than new motion events reach us. This means that */
+
+ if (! view->selection_update_id)
+ view->selection_update_id = g_idle_add ((GSourceFunc)selection_update_idle_cb, view);
return TRUE;
} else if (view->pressed_button == 2) {
return FALSE;
}
+static void
+ev_view_style_set (GtkWidget *widget,
+ GtkStyle *old_style)
+{
+ if (EV_VIEW (widget)->pixbuf_cache)
+ ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache);
+
+ GTK_WIDGET_CLASS (ev_view_parent_class)->style_set (widget, old_style);
+}
+
+
/*** Drawing ***/
static guint32
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);
+ view->scale,
+ NULL);
if (current_pixbuf == NULL)
scaled_image = NULL;
widget_class->unrealize = ev_view_unrealize;
widget_class->scroll_event = ev_view_scroll_event;
widget_class->leave_notify_event = ev_view_leave_notify_event;
+ widget_class->style_set = ev_view_style_set;
gtk_object_class->destroy = ev_view_destroy;
class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
{
view->page_cache = ev_page_cache_get (view->document);
g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view);
- view->pixbuf_cache = ev_pixbuf_cache_new (view->document);
+ view->pixbuf_cache = ev_pixbuf_cache_new (GTK_WIDGET (view), view->document);
g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
}
void
ev_view_find_next (EvView *view)
{
- EvPageCache *page_cache;
int n_results, n_pages;
EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
- page_cache = ev_page_cache_get (view->document);
n_results = ev_document_find_get_n_results (find, view->current_page);
- n_pages = ev_page_cache_get_n_pages (page_cache);
+ n_pages = ev_page_cache_get_n_pages (view->page_cache);
view->find_result++;
*/
static void
merge_selection_region (EvView *view,
- GList *list)
+ GList *new_list)
{
+ GList *old_list;
+ GList *new_list_ptr, *old_list_ptr;
- /* FIXME: actually write... */
- clear_selection (view);
- gtk_widget_queue_draw (GTK_WIDGET (view));
+ /* Update the selection */
+ old_list = ev_pixbuf_cache_get_selection_list (view->pixbuf_cache);
+ g_list_foreach (view->selection_info.selections, (GFunc)selection_free, NULL);
+ view->selection_info.selections = new_list;
+ ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, new_list);
+
+ new_list_ptr = new_list;
+ old_list_ptr = old_list;
+
+ while (new_list_ptr || old_list_ptr) {
+ EvViewSelection *old_sel, *new_sel;
+ int cur_page;
+ GdkRegion *region = NULL;
+
+ new_sel = (new_list_ptr) ? (new_list_ptr->data) : NULL;
+ old_sel = (old_list_ptr) ? (old_list_ptr->data) : NULL;
+
+ /* Assume that the lists are in order, and we run through them
+ * comparing them, one page at a time. We come out with the
+ * first page we see. */
+ if (new_sel && old_sel) {
+ if (new_sel->page < old_sel->page) {
+ new_list_ptr = new_list_ptr->next;
+ old_sel = NULL;
+ } else if (new_sel->page > old_sel->page) {
+ old_list_ptr = old_list_ptr->next;
+ new_sel = NULL;
+ } else {
+ new_list_ptr = new_list_ptr->next;
+ old_list_ptr = old_list_ptr->next;
+ }
+ } else if (new_sel) {
+ new_list_ptr = new_list_ptr->next;
+ } else if (old_sel) {
+ old_list_ptr = old_list_ptr->next;
+ }
+
+ g_assert (new_sel || old_sel);
+
+ /* is the page we're looking at on the screen?*/
+ cur_page = new_sel ? new_sel->page : old_sel->page;
+ if (cur_page < view->start_page || cur_page > view->end_page)
+ continue;
+
+ /* seed the cache with a new page. We are going to need the new
+ * region too. */
+ if (new_sel) {
+ GdkRegion *tmp_region = NULL;
+ ev_pixbuf_cache_get_selection_pixbuf (view->pixbuf_cache,
+ cur_page,
+ view->scale,
+ &tmp_region);
+ if (tmp_region) {
+ new_sel->covered_region = gdk_region_copy (tmp_region);
+ }
+ }
+
+ /* Now we figure out what needs redrawing */
+ if (old_sel && new_sel) {
+ if (old_sel->covered_region &&
+ new_sel->covered_region) {
+ /* We only want to redraw the areas that have
+ * changed, so we xor the old and new regions
+ * and redraw if it's different */
+ region = gdk_region_copy (old_sel->covered_region);
+ gdk_region_xor (region, new_sel->covered_region);
+ if (gdk_region_empty (region)) {
+ gdk_region_destroy (region);
+ region = NULL;
+ }
+ } else if (old_sel->covered_region) {
+ region = gdk_region_copy (old_sel->covered_region);
+ } else if (new_sel->covered_region) {
+ region = gdk_region_copy (new_sel->covered_region);
+ }
+ } else if (old_sel && !new_sel) {
+ if (old_sel->covered_region && !gdk_region_empty (old_sel->covered_region)) {
+ region = gdk_region_copy (old_sel->covered_region);
+ }
+ } else if (!old_sel && new_sel) {
+ if (new_sel->covered_region && !gdk_region_empty (new_sel->covered_region)) {
+ region = gdk_region_copy (new_sel->covered_region);
+ }
+ } else {
+ g_assert_not_reached ();
+ }
+
+ /* Redraw the damaged region! */
+ if (region) {
+ GdkRectangle page_area;
+ GtkBorder border;
+
+ get_page_extents (view, cur_page, &page_area, &border);
+ gdk_region_offset (region,
+ page_area.x + border.left - view->scroll_x,
+ page_area.y + border.top - view->scroll_y);
+ gdk_window_invalidate_region (GTK_WIDGET (view)->window, region, TRUE);
+ gdk_region_destroy (region);
+ }
+ }
- view->selection_info.selections = list;
- ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, list);
+ /* Free the old list, now that we're done with it. */
+ g_list_foreach (old_list, (GFunc) selection_free, NULL);
}
static void
static void
selection_free (EvViewSelection *selection)
{
+ if (selection->covered_region)
+ gdk_region_destroy (selection->covered_region);
g_free (selection);
}
g_return_if_fail (popup != NULL);
+ if (GTK_WIDGET (window)->window == NULL)
+ return;
+
toolbar = (window->priv->chrome & EV_CHROME_TOOLBAR) != 0 ||
(window->priv->chrome & EV_CHROME_RAISE_TOOLBAR) != 0;
popup_width = popup->requisition.width;