]> www.fi.muni.cz Git - evince.git/commitdiff
Clean up selection to be much smoother!
authorJonathan Blandford <jrb@redhat.com>
Sat, 6 Aug 2005 05:13:20 +0000 (05:13 +0000)
committerJonathan Blandford <jrb@src.gnome.org>
Sat, 6 Aug 2005 05:13:20 +0000 (05:13 +0000)
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!

13 files changed:
ChangeLog
NOTES
backend/ev-selection.c
backend/ev-selection.h
pdf/ev-poppler.cc
shell/ev-jobs.c
shell/ev-jobs.h
shell/ev-pixbuf-cache.c
shell/ev-pixbuf-cache.h
shell/ev-utils.c
shell/ev-utils.h
shell/ev-view.c
shell/ev-window.c

index 858f9afdc8bea4d591e8cd0417ddca49c126d244..17ae0549b1a11cc9c5eea0a9d218adfa6397e140 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,31 @@
+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
diff --git a/NOTES b/NOTES
index d709444f4862494af5677f0ced51da477f739e81..a06699fa6cb4654f99d2722cfcc022352750cee7 100644 (file)
--- 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)
index 074bbbcd2ab9620da17c1c60686773e8d0813d91..f9f73e08649a69b5d289a5af85e69430bc2754f5 100644 (file)
@@ -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 *
index 8c5e224c5f128b32a8dd8cb1c8f2d84f8f3a6114..8ed416bfbaabe599c6e5add72b52a0e4bf02ac6a 100644 (file)
@@ -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,
index 2c5b5265b3a4e2263948602db7ba3cb9bd397034..b4a6799521877a6c30733097665d29d4ec873e89 100644 (file)
@@ -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;
 }
index c250f0ebda4845c9e12d4181c9de74ad92b9ecd1..595f9e839de76dc116259f3341063a10a0d21f85 100644 (file)
@@ -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;
        }
index d7f49c70306e9149a5a5da6ce3d94105c0612c2d..15768371412846dfae665365ad9e227cc705ee41 100644 (file)
@@ -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);
index 36eca68a679c9032be498146981db55eca4402b6..cbb6d18c68e3e2f27bbfbc71c04a253c13245547 100644 (file)
@@ -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;
+}
+
index 443e94d31942379a905921632bb67a627c691154..c956832dff9d5033f50c5631144400628ea1ca6b 100644 (file)
@@ -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
 
index 587dd73664e914392582d1fc5c479beced82f1f0..21563eb97c173bdcb13a2055ef3e241b2405c179 100644 (file)
@@ -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 ("<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>
index 9875a95bd30a05dc933142fa54ef37ad2d07334c..7f4938174ac7b91006164e7b1cfd3a6ea60cf9cc 100644 (file)
 #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,
index 31705ab5143d118b07543d578ca272073af7a4bd..5d7df6b3a5f1b768e50aabe3f36448a82c45d02e 100644 (file)
@@ -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);
 }
 
index fac6047d77ac868369087cbf0c4818979df05085..dc67739915faf9eba3864a8469952a3e363cebac 100644 (file)
@@ -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;