]> www.fi.muni.cz Git - evince.git/blobdiff - libview/ev-page-cache.c
[dualscreen] fix crash on ctrl+w and fix control window closing
[evince.git] / libview / ev-page-cache.c
index 1bf689eea0b5838803f8b4a838a6c55801e5b9c0..c3c570417285e9303a294872c6f6ce4b25274770 100644 (file)
+/* this file is part of evince, a gnome document viewer
+ *
+ *  Copyright (C) 2009 Carlos Garcia Campos
+ *
+ * Evince is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Evince is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
 #include <config.h>
+
+#include <glib.h>
+#include "ev-jobs.h"
+#include "ev-job-scheduler.h"
+#include "ev-mapping-list.h"
+#include "ev-selection.h"
+#include "ev-document-links.h"
+#include "ev-document-forms.h"
+#include "ev-document-images.h"
+#include "ev-document-annotations.h"
+#include "ev-document-text.h"
 #include "ev-page-cache.h"
-#include "ev-document-thumbnails.h"
-#include "ev-page.h"
-#include <stdlib.h>
-#include <string.h>
 
-struct _EvPageCache
-{
+typedef struct _EvPageCacheData {
+       EvJob             *job;
+       gboolean           done : 1;
+       gboolean           dirty : 1;
+       EvJobPageDataFlags flags;
+
+       EvMappingList     *link_mapping;
+       EvMappingList     *image_mapping;
+       EvMappingList     *form_field_mapping;
+       EvMappingList     *annot_mapping;
+       cairo_region_t    *text_mapping;
+       EvRectangle       *text_layout;
+       guint              text_layout_length;
+       gchar             *text;
+} EvPageCacheData;
+
+struct _EvPageCache {
        GObject parent;
 
-       EvDocument *document;
-
-       gint current_page;
+       EvDocument        *document;
+       EvPageCacheData   *page_list;
+       gint               n_pages;
 
-       gboolean dual_even_left;
+       /* Current range */
+       gint               start_page;
+       gint               end_page;
 
-       int rotation;
+       EvJobPageDataFlags flags;
 };
 
-struct _EvPageCacheClass
-{
+struct _EvPageCacheClass {
        GObjectClass parent_class;
-
-       void (* page_changed) (EvPageCache *page_cache, gint page);
-       void (* history_changed) (EvPageCache *page_cache, gint page);
 };
 
-enum
-{
-       PAGE_CHANGED,
-       HISTORY_CHANGED,
-       N_SIGNALS,
-};
+#define EV_PAGE_DATA_FLAGS_DEFAULT (        \
+       EV_PAGE_DATA_INCLUDE_LINKS        | \
+       EV_PAGE_DATA_INCLUDE_TEXT_MAPPING | \
+       EV_PAGE_DATA_INCLUDE_IMAGES       | \
+       EV_PAGE_DATA_INCLUDE_FORMS        | \
+       EV_PAGE_DATA_INCLUDE_ANNOTS)
 
-static guint signals[N_SIGNALS] = {0, };
 
-static void ev_page_cache_init       (EvPageCache      *page_cache);
-static void ev_page_cache_class_init (EvPageCacheClass *page_cache);
-static void ev_page_cache_finalize   (GObject *object);
+static void job_page_data_finished_cb (EvJob       *job,
+                                      EvPageCache *cache);
+static void job_page_data_cancelled_cb (EvJob       *job,
+                                       EvPageCacheData *data);
 
 G_DEFINE_TYPE (EvPageCache, ev_page_cache, G_TYPE_OBJECT)
 
 static void
-ev_page_cache_init (EvPageCache *page_cache)
+ev_page_cache_data_free (EvPageCacheData *data)
 {
-       page_cache->current_page = -1;
-}
+       if (data->job) {
+               g_object_unref (data->job);
+               data->job = NULL;
+       }
 
-static void
-ev_page_cache_class_init (EvPageCacheClass *class)
-{
-       GObjectClass *object_class;
+       if (data->link_mapping) {
+               ev_mapping_list_unref (data->link_mapping);
+               data->link_mapping = NULL;
+       }
+
+       if (data->image_mapping) {
+               ev_mapping_list_unref (data->image_mapping);
+               data->image_mapping = NULL;
+       }
 
-       object_class = G_OBJECT_CLASS (class);
+       if (data->form_field_mapping) {
+               ev_mapping_list_unref (data->form_field_mapping);
+               data->form_field_mapping = NULL;
+       }
 
-       object_class->finalize = ev_page_cache_finalize;
+       if (data->annot_mapping) {
+               ev_mapping_list_unref (data->annot_mapping);
+               data->annot_mapping = NULL;
+       }
 
-       signals [PAGE_CHANGED] =
-               g_signal_new ("page-changed",
-                             EV_TYPE_PAGE_CACHE,
-                             G_SIGNAL_RUN_LAST,
-                             G_STRUCT_OFFSET (EvPageCacheClass, page_changed),
-                             NULL, NULL,
-                             g_cclosure_marshal_VOID__INT,
-                             G_TYPE_NONE, 1,
-                             G_TYPE_INT);
+       if (data->text_mapping) {
+               cairo_region_destroy (data->text_mapping);
+               data->text_mapping = NULL;
+       }
 
-       signals [HISTORY_CHANGED] =
-               g_signal_new ("history-changed",
-                             EV_TYPE_PAGE_CACHE,
-                             G_SIGNAL_RUN_LAST,
-                             G_STRUCT_OFFSET (EvPageCacheClass, history_changed),
-                             NULL, NULL,
-                             g_cclosure_marshal_VOID__INT,
-                             G_TYPE_NONE, 1,
-                             G_TYPE_INT);
+       if (data->text_layout) {
+               g_free (data->text_layout);
+               data->text_layout = NULL;
+               data->text_layout_length = 0;
+       }
 
+       if (data->text) {
+               g_free (data->text);
+               data->text = NULL;
+       }
 }
 
 static void
 ev_page_cache_finalize (GObject *object)
 {
-       EvPageCache *page_cache = EV_PAGE_CACHE (object);
+       EvPageCache *cache = EV_PAGE_CACHE (object);
+       gint         i;
+
+       if (cache->page_list) {
+               for (i = 0; i < cache->n_pages; i++) {
+                       EvPageCacheData *data;
+
+                       data = &cache->page_list[i];
+
+                       if (data->job) {
+                               g_signal_handlers_disconnect_by_func (data->job,
+                                                                     G_CALLBACK (job_page_data_finished_cb),
+                                                                     cache);
+                               g_signal_handlers_disconnect_by_func (data->job,
+                                                                     G_CALLBACK (job_page_data_cancelled_cb),
+                                                                     data);
+                       }
+                       ev_page_cache_data_free (data);
+               }
+
+               g_free (cache->page_list);
+               cache->page_list = NULL;
+               cache->n_pages = 0;
+       }
 
-       page_cache->document = NULL;
+       if (cache->document) {
+               g_object_unref (cache->document);
+               cache->document = NULL;
+       }
 
        G_OBJECT_CLASS (ev_page_cache_parent_class)->finalize (object);
 }
 
-static EvPageCache *
+static void
+ev_page_cache_init (EvPageCache *cache)
+{
+}
+
+static void
+ev_page_cache_class_init (EvPageCacheClass *klass)
+{
+       GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
+
+       g_object_class->finalize = ev_page_cache_finalize;
+}
+
+static EvJobPageDataFlags
+ev_page_cache_get_flags_for_data (EvPageCache     *cache,
+                                 EvPageCacheData *data)
+{
+       EvJobPageDataFlags flags = EV_PAGE_DATA_INCLUDE_NONE;
+
+       if (data->flags == cache->flags && !data->dirty)
+               return cache->flags;
+
+       /* Flags changed or data is dirty */
+       if (cache->flags & EV_PAGE_DATA_INCLUDE_LINKS) {
+               flags = (data->link_mapping) ?
+                       flags & ~EV_PAGE_DATA_INCLUDE_LINKS :
+                       flags | EV_PAGE_DATA_INCLUDE_LINKS;
+       }
+
+       if (cache->flags & EV_PAGE_DATA_INCLUDE_IMAGES) {
+               flags = (data->image_mapping) ?
+                       flags & ~EV_PAGE_DATA_INCLUDE_IMAGES :
+                       flags | EV_PAGE_DATA_INCLUDE_IMAGES;
+       }
+
+       if (cache->flags & EV_PAGE_DATA_INCLUDE_FORMS) {
+               flags = (data->form_field_mapping) ?
+                       flags & ~EV_PAGE_DATA_INCLUDE_FORMS :
+                       flags | EV_PAGE_DATA_INCLUDE_FORMS;
+       }
+
+       if (cache->flags & EV_PAGE_DATA_INCLUDE_ANNOTS) {
+               flags = (data->annot_mapping) ?
+                       flags & ~EV_PAGE_DATA_INCLUDE_ANNOTS :
+                       flags | EV_PAGE_DATA_INCLUDE_ANNOTS;
+       }
+
+       if (cache->flags & EV_PAGE_DATA_INCLUDE_TEXT_MAPPING) {
+               flags = (data->text_mapping) ?
+                       flags & ~EV_PAGE_DATA_INCLUDE_TEXT_MAPPING :
+                       flags | EV_PAGE_DATA_INCLUDE_TEXT_MAPPING;
+       }
+
+       if (cache->flags & EV_PAGE_DATA_INCLUDE_TEXT) {
+               flags = (data->text) ?
+                       flags & ~EV_PAGE_DATA_INCLUDE_TEXT :
+                       flags | EV_PAGE_DATA_INCLUDE_TEXT;
+       }
+
+       if (cache->flags & EV_PAGE_DATA_INCLUDE_TEXT_LAYOUT) {
+               flags = (data->text_layout_length > 0) ?
+                       flags & ~EV_PAGE_DATA_INCLUDE_TEXT_LAYOUT :
+                       flags | EV_PAGE_DATA_INCLUDE_TEXT_LAYOUT;
+       }
+
+       return flags;
+}
+
+EvPageCache *
 ev_page_cache_new (EvDocument *document)
 {
-       EvPageCache *page_cache;
+       EvPageCache *cache;
 
-       page_cache = (EvPageCache *) g_object_new (EV_TYPE_PAGE_CACHE, NULL);
-       page_cache->document = document;
+       g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
 
-       if (ev_document_get_n_pages (page_cache->document) > 0)
-               ev_page_cache_set_current_page (page_cache, 0);
+       cache = EV_PAGE_CACHE (g_object_new (EV_TYPE_PAGE_CACHE, NULL));
+       cache->document = g_object_ref (document);
+       cache->n_pages = ev_document_get_n_pages (document);
+       cache->flags = EV_PAGE_DATA_FLAGS_DEFAULT;
+       cache->page_list = g_new0 (EvPageCacheData, cache->n_pages);
 
-       return page_cache;
+       return cache;
 }
 
-gint
-ev_page_cache_get_current_page (EvPageCache *page_cache)
+static void
+job_page_data_finished_cb (EvJob       *job,
+                          EvPageCache *cache)
 {
-       g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), 0);
+       EvJobPageData   *job_data = EV_JOB_PAGE_DATA (job);
+       EvPageCacheData *data;
+
+       data = &cache->page_list[job_data->page];
+
+       if (job_data->flags & EV_PAGE_DATA_INCLUDE_LINKS)
+               data->link_mapping = job_data->link_mapping;
+       if (job_data->flags & EV_PAGE_DATA_INCLUDE_IMAGES)
+               data->image_mapping = job_data->image_mapping;
+       if (job_data->flags & EV_PAGE_DATA_INCLUDE_FORMS)
+               data->form_field_mapping = job_data->form_field_mapping;
+       if (job_data->flags & EV_PAGE_DATA_INCLUDE_ANNOTS)
+               data->annot_mapping = job_data->annot_mapping;
+       if (job_data->flags & EV_PAGE_DATA_INCLUDE_TEXT_MAPPING)
+               data->text_mapping = job_data->text_mapping;
+       if (job_data->flags & EV_PAGE_DATA_INCLUDE_TEXT_LAYOUT) {
+               data->text_layout = job_data->text_layout;
+               data->text_layout_length = job_data->text_layout_length;
+       }
+       if (job_data->flags & EV_PAGE_DATA_INCLUDE_TEXT)
+               data->text = job_data->text;
+       data->done = TRUE;
+       data->dirty = FALSE;
+
+       g_object_unref (data->job);
+       data->job = NULL;
+}
 
-       return page_cache->current_page;
+static void
+job_page_data_cancelled_cb (EvJob           *job,
+                           EvPageCacheData *data)
+{
+       g_object_unref (data->job);
+       data->job = NULL;
 }
 
 void
-ev_page_cache_set_current_page (EvPageCache *page_cache,
-                               int          page)
+ev_page_cache_set_page_range (EvPageCache *cache,
+                             gint         start,
+                             gint         end)
+{
+       gint i;
+
+       if (cache->flags == EV_PAGE_DATA_INCLUDE_NONE)
+               return;
+
+       cache->start_page = start;
+       cache->end_page = end;
+
+       for (i = start; i <= end; i++) {
+               EvPageCacheData   *data = &cache->page_list[i];
+               EvJobPageDataFlags flags;
+
+               if (data->flags == cache->flags && !data->dirty && (data->done || data->job))
+                       continue;
+
+               if (data->job)
+                       ev_job_cancel (data->job);
+
+               flags = ev_page_cache_get_flags_for_data (cache, data);
+
+               data->flags = cache->flags;
+               data->job = ev_job_page_data_new (cache->document, i, flags);
+               g_signal_connect (data->job, "finished",
+                                 G_CALLBACK (job_page_data_finished_cb),
+                                 cache);
+               g_signal_connect (data->job, "cancelled",
+                                 G_CALLBACK (job_page_data_cancelled_cb),
+                                 data);
+               ev_job_scheduler_push_job (data->job, EV_JOB_PRIORITY_NONE);
+       }
+}
+
+EvJobPageDataFlags
+ev_page_cache_get_flags (EvPageCache *cache)
 {
-       g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
+       return cache->flags;
+}
 
-       if (page == page_cache->current_page)
+void
+ev_page_cache_set_flags (EvPageCache       *cache,
+                        EvJobPageDataFlags flags)
+{
+       if (cache->flags == flags)
                return;
 
-       page_cache->current_page = page;
-       g_signal_emit (page_cache, signals[PAGE_CHANGED], 0, page);
+       cache->flags = flags;
+
+       /* Update the current range for new flags */
+       ev_page_cache_set_page_range (cache, cache->start_page, cache->end_page);
 }
 
 void
-ev_page_cache_set_current_page_history (EvPageCache *page_cache,
-                                       int          page)
+ev_page_cache_mark_dirty (EvPageCache *cache,
+                         gint         page)
 {
-       if (abs (page - page_cache->current_page) > 1)
-               g_signal_emit (page_cache, signals [HISTORY_CHANGED], 0, page);
-       
-       ev_page_cache_set_current_page (page_cache, page);
+       EvPageCacheData *data;
+
+       g_return_if_fail (EV_IS_PAGE_CACHE (cache));
+
+       data = &cache->page_list[page];
+       data->dirty = TRUE;
+
+       /* Update the current range */
+       ev_page_cache_set_page_range (cache, cache->start_page, cache->end_page);
 }
 
-gboolean
-ev_page_cache_set_page_label (EvPageCache *page_cache,
-                             const gchar *page_label)
+EvMappingList *
+ev_page_cache_get_link_mapping (EvPageCache *cache,
+                               gint         page)
 {
-       gint page;
+       EvPageCacheData *data;
 
-       g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), FALSE);
+       g_return_val_if_fail (EV_IS_PAGE_CACHE (cache), NULL);
+       g_return_val_if_fail (page >= 0 && page < cache->n_pages, NULL);
 
-       if (ev_document_find_page_by_label (page_cache->document, page_label, &page)) {
-               ev_page_cache_set_current_page (page_cache, page);
-               return TRUE;
-       }
+       if (!(cache->flags & EV_PAGE_DATA_INCLUDE_LINKS))
+               return NULL;
 
-       return FALSE;
+       data = &cache->page_list[page];
+       if (data->done)
+               return data->link_mapping;
+
+       if (data->job)
+               return EV_JOB_PAGE_DATA (data->job)->link_mapping;
+
+       return data->link_mapping;
 }
 
-void
-ev_page_cache_get_size (EvPageCache  *page_cache,
-                       gint          page,
-                       gint          rotation,
-                       gfloat        scale,
-                       gint         *width,
-                       gint         *height)
+EvMappingList *
+ev_page_cache_get_image_mapping (EvPageCache *cache,
+                                gint         page)
 {
-       double w, h;
+       EvPageCacheData *data;
 
-       g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
+       g_return_val_if_fail (EV_IS_PAGE_CACHE (cache), NULL);
+       g_return_val_if_fail (page >= 0 && page < cache->n_pages, NULL);
 
-       ev_document_get_page_size (page_cache->document, page, &w, &h);
+       if (!(cache->flags & EV_PAGE_DATA_INCLUDE_IMAGES))
+               return NULL;
 
-       w = w * scale + 0.5;
-       h = h * scale + 0.5;
+       data = &cache->page_list[page];
+       if (data->done)
+               return data->image_mapping;
 
-       if (rotation == 0 || rotation == 180) {
-               if (width) *width = (int)w;
-               if (height) *height = (int)h;
-       } else {
-               if (width) *width = (int)h;
-               if (height) *height = (int)w;
-       }
+       if (data->job)
+               return EV_JOB_PAGE_DATA (data->job)->image_mapping;
+
+       return data->image_mapping;
 }
 
-#define PAGE_CACHE_STRING "ev-page-cache"
+EvMappingList *
+ev_page_cache_get_form_field_mapping (EvPageCache *cache,
+                                     gint         page)
+{
+       EvPageCacheData *data;
 
-EvPageCache *
-ev_page_cache_get (EvDocument *document)
+       g_return_val_if_fail (EV_IS_PAGE_CACHE (cache), NULL);
+       g_return_val_if_fail (page >= 0 && page < cache->n_pages, NULL);
+
+       if (!(cache->flags & EV_PAGE_DATA_INCLUDE_FORMS))
+               return NULL;
+
+       data = &cache->page_list[page];
+       if (data->done)
+               return data->form_field_mapping;
+
+       if (data->job)
+               return EV_JOB_PAGE_DATA (data->job)->form_field_mapping;
+
+       return data->form_field_mapping;
+}
+
+EvMappingList *
+ev_page_cache_get_annot_mapping (EvPageCache *cache,
+                                gint         page)
 {
-       EvPageCache *page_cache;
+       EvPageCacheData *data;
 
-       g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
+       g_return_val_if_fail (EV_IS_PAGE_CACHE (cache), NULL);
+       g_return_val_if_fail (page >= 0 && page < cache->n_pages, NULL);
+
+       if (!(cache->flags & EV_PAGE_DATA_INCLUDE_ANNOTS))
+               return NULL;
 
-       page_cache = g_object_get_data (G_OBJECT (document), PAGE_CACHE_STRING);
-       if (page_cache == NULL) {
-               page_cache = ev_page_cache_new (document);
-               g_object_set_data_full (G_OBJECT (document), PAGE_CACHE_STRING, page_cache, g_object_unref);
+       data = &cache->page_list[page];
+       if (data->done)
+               return data->annot_mapping;
+
+       if (data->job)
+               return EV_JOB_PAGE_DATA (data->job)->annot_mapping;
+
+       return data->annot_mapping;
+}
+
+cairo_region_t *
+ev_page_cache_get_text_mapping (EvPageCache *cache,
+                               gint         page)
+{
+       EvPageCacheData *data;
+
+       g_return_val_if_fail (EV_IS_PAGE_CACHE (cache), NULL);
+       g_return_val_if_fail (page >= 0 && page < cache->n_pages, NULL);
+
+       if (!(cache->flags & EV_PAGE_DATA_INCLUDE_TEXT_MAPPING))
+               return NULL;
+
+       data = &cache->page_list[page];
+       if (data->done)
+               return data->text_mapping;
+
+       if (data->job)
+               return EV_JOB_PAGE_DATA (data->job)->text_mapping;
+
+       return data->text_mapping;
+}
+
+const gchar *
+ev_page_cache_get_text (EvPageCache *cache,
+                            gint         page)
+{
+       EvPageCacheData *data;
+
+       g_return_val_if_fail (EV_IS_PAGE_CACHE (cache), NULL);
+       g_return_val_if_fail (page >= 0 && page < cache->n_pages, NULL);
+
+       if (!(cache->flags & EV_PAGE_DATA_INCLUDE_TEXT))
+               return NULL;
+
+       data = &cache->page_list[page];
+       if (data->done)
+               return data->text;
+
+       if (data->job)
+               return EV_JOB_PAGE_DATA (data->job)->text;
+
+       return data->text;
+}
+
+gboolean
+ev_page_cache_get_text_layout (EvPageCache  *cache,
+                              gint          page,
+                              EvRectangle **areas,
+                              guint        *n_areas)
+{
+       EvPageCacheData *data;
+
+       g_return_val_if_fail (EV_IS_PAGE_CACHE (cache), FALSE);
+       g_return_val_if_fail (page >= 0 && page < cache->n_pages, FALSE);
+
+       if (!(cache->flags & EV_PAGE_DATA_INCLUDE_TEXT_LAYOUT))
+               return FALSE;
+
+       data = &cache->page_list[page];
+       if (data->done) {
+               *areas = data->text_layout;
+               *n_areas = data->text_layout_length;
+
+               return TRUE;
+       }
+
+       if (data->job) {
+               *areas = EV_JOB_PAGE_DATA (data->job)->text_layout;
+               *n_areas = EV_JOB_PAGE_DATA (data->job)->text_layout_length;
+
+               return TRUE;
        }
 
-       return page_cache;
+       return FALSE;
 }