From 00efc5c16ed191f07f9a8a5d00acc41ddb064b46 Mon Sep 17 00:00:00 2001 From: Jonathan Blandford Date: Sat, 06 Aug 2005 05:13:20 +0000 Subject: Clean up selection to be much smoother! Sat Aug 6 01:12:44 2005 Jonathan Blandford * 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! --- diff --git a/ChangeLog b/ChangeLog index 858f9af..17ae054 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,31 @@ +Sat Aug 6 01:12:44 2005 Jonathan Blandford + + * 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 * pdf/ev-poppler.cc (pdf_selection_render_selection): Pass colors diff --git a/NOTES b/NOTES index d709444..a06699f 100644 --- a/NOTES +++ b/NOTES @@ -51,3 +51,12 @@ Thoughts on 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. + +Things that can add to the selection: + * C-a/select all + * click-drag + * dbl click + * triple click + * shift-click + * search + * shift-cursor move (later) diff --git a/backend/ev-selection.c b/backend/ev-selection.c index 074bbbc..f9f73e0 100644 --- a/backend/ev-selection.c +++ b/backend/ev-selection.c @@ -61,13 +61,16 @@ ev_selection_render_selection (EvSelection *selection, 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 * diff --git a/backend/ev-selection.h b/backend/ev-selection.h index 8c5e224..8ed416b 100644 --- a/backend/ev-selection.h +++ b/backend/ev-selection.h @@ -47,7 +47,9 @@ struct _EvSelectionIface 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, @@ -60,7 +62,9 @@ void ev_selection_render_selection (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, diff --git a/pdf/ev-poppler.cc b/pdf/ev-poppler.cc index 2c5b526..b4a6799 100644 --- a/pdf/ev-poppler.cc +++ b/pdf/ev-poppler.cc @@ -103,6 +103,20 @@ G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT, 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) { @@ -330,17 +344,16 @@ pdf_document_render_pixbuf (EvDocument *document, 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); @@ -348,13 +361,12 @@ pdf_document_render_pixbuf (EvDocument *document, 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; } @@ -1169,19 +1181,20 @@ pdf_selection_render_selection (EvSelection *selection, 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); @@ -1191,16 +1204,12 @@ pdf_selection_render_selection (EvSelection *selection, 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); } @@ -1210,16 +1219,14 @@ pdf_selection_get_selection_region (EvSelection *selection, 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; } diff --git a/shell/ev-jobs.c b/shell/ev-jobs.c index c250f0e..595f9e8 100644 --- a/shell/ev-jobs.c +++ b/shell/ev-jobs.c @@ -116,6 +116,16 @@ ev_job_render_dispose (GObject *object) 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); } @@ -226,6 +236,8 @@ ev_job_render_new (EvDocument *document, gint width, gint height, EvRectangle *selection_points, + guint text, + guint base, gboolean include_links, gboolean include_text, gboolean include_selection) @@ -242,6 +254,8 @@ ev_job_render_new (EvDocument *document, 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; @@ -285,12 +299,18 @@ ev_job_render_run (EvJobRender *job) 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; } diff --git a/shell/ev-jobs.h b/shell/ev-jobs.h index d7f49c7..1576837 100644 --- a/shell/ev-jobs.h +++ b/shell/ev-jobs.h @@ -118,7 +118,10 @@ struct _EvJobRender GdkRegion *text_mapping; GdkPixbuf *selection; + GdkRegion *selection_region; EvRectangle selection_points; + guint base; + guint text; gint include_links : 1; gint include_text : 1; @@ -184,6 +187,8 @@ EvJob *ev_job_render_new (EvDocument *document, gint width, gint height, EvRectangle *selection_points, + guint text, + guint base, gboolean include_links, gboolean include_text, gboolean include_selection); diff --git a/shell/ev-pixbuf-cache.c b/shell/ev-pixbuf-cache.c index 36eca68..cbb6d18 100644 --- a/shell/ev-pixbuf-cache.c +++ b/shell/ev-pixbuf-cache.c @@ -10,12 +10,13 @@ typedef struct _CacheJobInfo 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; @@ -23,7 +24,9 @@ struct _EvPixbufCache { 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; @@ -64,10 +67,11 @@ 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); +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 */ @@ -153,6 +157,10 @@ dispose_cache_job_info (CacheJobInfo *job_info, 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; @@ -178,11 +186,14 @@ ev_pixbuf_cache_dispose (GObject *object) 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; @@ -223,10 +234,14 @@ job_finished_cb (EvJob *job, } 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); } @@ -487,6 +502,7 @@ add_job_if_needed (EvPixbufCache *pixbuf_cache, gboolean include_text = FALSE; gboolean include_selection = FALSE; int width, height; + guint text, base; if (job_info->job) return; @@ -503,9 +519,9 @@ add_job_if_needed (EvPixbufCache *pixbuf_cache, 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 */ @@ -517,10 +533,16 @@ add_job_if_needed (EvPixbufCache *pixbuf_cache, 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); @@ -643,6 +665,17 @@ ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache, } /* 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, @@ -683,7 +716,7 @@ clear_selection_if_needed (EvPixbufCache *pixbuf_cache, GdkRegion * ev_pixbuf_cache_get_text_mapping (EvPixbufCache *pixbuf_cache, - gint page) + gint page) { CacheJobInfo *job_info; @@ -718,10 +751,44 @@ ev_pixbuf_cache_clear (EvPixbufCache *pixbuf_cache) } +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; @@ -737,6 +804,10 @@ ev_pixbuf_cache_get_selection_pixbuf (EvPixbufCache *pixbuf_cache, 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 */ @@ -753,33 +824,44 @@ ev_pixbuf_cache_get_selection_pixbuf (EvPixbufCache *pixbuf_cache, * 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) @@ -802,7 +884,8 @@ clear_job_selection (CacheJobInfo *job_info) } /* 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, @@ -865,7 +948,7 @@ 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) { @@ -883,3 +966,73 @@ ev_pixbuf_cache_set_selection_list (EvPixbufCache *pixbuf_cache, 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; +} + diff --git a/shell/ev-pixbuf-cache.h b/shell/ev-pixbuf-cache.h index 443e94d..c956832 100644 --- a/shell/ev-pixbuf-cache.h +++ b/shell/ev-pixbuf-cache.h @@ -42,13 +42,15 @@ G_BEGIN_DECLS 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, @@ -62,13 +64,16 @@ GList *ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache 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 diff --git a/shell/ev-utils.c b/shell/ev-utils.c index 587dd73..21563eb 100644 --- a/shell/ev-utils.c +++ b/shell/ev-utils.c @@ -177,6 +177,35 @@ ev_pixbuf_add_shadow (GdkPixbuf *src, int size, } +/* 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 ("\n"); + return; + } + + g_print ("\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 diff --git a/shell/ev-utils.h b/shell/ev-utils.h index 9875a95..7f49381 100644 --- a/shell/ev-utils.h +++ b/shell/ev-utils.h @@ -22,12 +22,15 @@ #define __EV_UTILS_H__ #include +#include 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, diff --git a/shell/ev-view.c b/shell/ev-view.c index 31705ab..5d7df6b 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-utils.h" #include "ev-selection.h" #include "ev-document-find.h" #include "ev-document-misc.h" @@ -120,6 +121,10 @@ struct _EvView { char *status; char *find_status; + /* Scrolling */ + GtkAdjustment *hadjustment; + GtkAdjustment *vadjustment; + gint scroll_x; gint scroll_y; @@ -127,15 +132,16 @@ struct _EvView { 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; @@ -224,6 +230,8 @@ static void find_page_at_location (EvView 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, @@ -261,6 +269,8 @@ static gboolean ev_view_button_release_event (GtkWidget 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); @@ -357,6 +367,7 @@ static void compute_selections (EvView 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, @@ -986,6 +997,14 @@ find_page_at_location (EvView *view, *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, @@ -1424,6 +1443,19 @@ ev_view_button_press_event (GtkWidget *widget, 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) @@ -1434,12 +1466,16 @@ ev_view_motion_notify_event (GtkWidget *widget, 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) { @@ -1544,6 +1580,17 @@ ev_view_leave_notify_event (GtkWidget *widget, GdkEventCrossing *event) 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 @@ -1669,7 +1716,8 @@ draw_one_page (EvView *view, 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; @@ -1852,6 +1900,7 @@ ev_view_class_init (EvViewClass *class) 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; @@ -2068,7 +2117,7 @@ setup_caches (EvView *view) { 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); } @@ -2744,14 +2793,12 @@ ev_view_can_find_next (EvView *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++; @@ -2942,15 +2989,114 @@ compute_new_selection_text (EvView *view, */ 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 @@ -2972,6 +3118,8 @@ compute_selections (EvView *view, static void selection_free (EvViewSelection *selection) { + if (selection->covered_region) + gdk_region_destroy (selection->covered_region); g_free (selection); } diff --git a/shell/ev-window.c b/shell/ev-window.c index fac6047..dc67739 100644 --- a/shell/ev-window.c +++ b/shell/ev-window.c @@ -1572,6 +1572,9 @@ ev_window_update_fullscreen_popup (EvWindow *window) 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; -- cgit v0.9.1