From 1c0d19bd22598eca159c3febdcdaf4168891cb8f Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Wed, 23 Mar 2005 11:07:32 +0000 Subject: merge evince-threads branch --- diff --git a/ChangeLog b/ChangeLog index b09c962..c4eada2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,112 @@ +2005-03-23 Marco Pesenti Gritti + + * TODO: + * backend/Makefile.am: + * backend/ev-document-links.c: + (ev_document_links_has_document_links), + (ev_document_links_get_links_model): + * backend/ev-document-links.h: + * backend/ev-document.c: (ev_document_get_page_cache), + (ev_document_get_doc_mutex), (ev_document_load), + (ev_document_save), (ev_document_get_title), + (ev_document_get_n_pages), (ev_document_set_page), + (ev_document_get_page), (ev_document_set_target), + (ev_document_set_scale), (ev_document_set_page_offset), + (ev_document_get_page_size), (ev_document_get_text), + (ev_document_get_link), (ev_document_render), + (ev_document_render_pixbuf), (ev_document_scale_changed): + * backend/ev-document.h: + * backend/ev-job-queue.c: (remove_object_from_queue), + (notify_finished), (handle_job), (search_for_jobs_unlocked), + (no_jobs_available_unlocked), (ev_render_thread), + (ev_job_queue_init), (find_queue), (ev_job_queue_add_job), + (ev_job_queue_remove_job): + * backend/ev-job-queue.h: + * backend/ev-jobs.c: (ev_job_init), (ev_job_dispose), + (ev_job_class_init), (ev_job_links_init), (ev_job_links_dispose), + (ev_job_links_class_init), (ev_job_render_init), + (ev_job_render_dispose), (ev_job_render_class_init), + (ev_job_thumbnail_init), (ev_job_thumbnail_dispose), + (ev_job_thumbnail_class_init), (ev_job_finished), + (ev_job_links_new), (ev_job_links_run), (ev_job_render_new), + (ev_job_render_run), (ev_job_thumbnail_new), + (ev_job_thumbnail_run): + * backend/ev-jobs.h: + * backend/ev-link.c: + * backend/ev-link.h: + * backend/ev-page-cache.c: (ev_page_cache_init), + (ev_page_cache_class_init), (ev_page_cache_finalize), + (_ev_page_cache_new), (ev_page_cache_get_n_pages), + (ev_page_cache_get_current_page), (ev_page_cache_set_current_page), + (ev_page_cache_set_link), (ev_page_cache_get_title), + (ev_page_cache_get_size), (ev_page_cache_next_page), + (ev_page_cache_prev_page): + * backend/ev-page-cache.h: + * pdf/pdf-document.cc: + * ps/gsdefaults.c: + * ps/gsdefaults.h: + * ps/ps-document.c: (ps_document_init), (ps_document_class_init), + (push_pixbuf), (interpreter_failed), (ps_document_cleanup), + (ps_document_widget_event), (ps_document_finalize), (get_xdpi), + (get_ydpi), (setup_pixmap), (setup_page), (close_pipe), + (is_interpreter_ready), (output), (catchPipe), (input), + (start_interpreter), (check_filecompressed), (compute_dimensions), + (ps_document_enable_interpreter), (document_load), + (ps_document_next_page), (render_page), (ps_document_set_page), + (ps_document_set_scale), (render_pixbuf_idle), + (ps_document_render_pixbuf), (ps_document_document_iface_init): + * ps/ps-document.h: + * shell/Makefile.am: + * shell/ev-pixbuf-cache.c: (ev_pixbuf_cache_init), + (ev_pixbuf_cache_class_init), (ev_pixbuf_cache_finalize), + (dispose_cache_job_info), (ev_pixbuf_cache_dispose), + (ev_pixbuf_cache_new), (job_finished_cb), + (check_job_size_and_unref), (move_one_job), + (ev_pixbuf_cache_update_range), (find_job_cache), + (ev_pixbuf_cache_clear_job_sizes), (add_job_if_needed), + (ev_pixbuf_cache_add_jobs_if_needed), + (ev_pixbuf_cache_set_page_range), (ev_pixbuf_cache_get_pixbuf): + * shell/ev-pixbuf-cache.h: + * shell/ev-print-job.c: (ev_print_job_use_print_dialog_settings), + (idle_print_handler): + * shell/ev-sidebar-links.c: (ev_sidebar_links_destroy), + (selection_changed_cb), (create_loading_model), + (ev_sidebar_links_construct), (links_page_num_func), + (ev_sidebar_links_clear_document), (update_page_callback_foreach), + (update_page_callback), (job_finished_cb), + (ev_sidebar_links_set_document): + * shell/ev-sidebar-thumbnails.c: + (ev_sidebar_thumbnails_class_init), (adjustment_changed_cb), + (ev_sidebar_tree_selection_changed), (ev_sidebar_thumbnails_init), + (page_changed_cb), (thumbnail_job_completed_callback), + (ev_sidebar_thumbnails_set_document): + * shell/ev-sidebar-thumbnails.h: + * shell/ev-sidebar.c: (ev_sidebar_set_document): + * shell/ev-view.c: (ev_view_finalize), (ev_view_destroy), + (ev_view_get_offsets), (ev_view_size_request), (ev_view_realize), + (ev_view_unrealize), (highlight_find_results), (expose_bin_window), + (ev_view_select_all), (ev_view_copy), (ev_view_primary_get_cb), + (ev_view_create_invisible_cursor), (ev_view_motion_notify_event), + (ev_view_button_release_event), (ev_view_scroll_view), + (ev_view_class_init), (ev_view_init), (update_find_status_message), + (jump_to_find_result), (jump_to_find_page), (find_changed_cb), + (ev_view_new), (job_finished_cb), (page_changed_cb), + (ev_view_set_document), (go_to_link), (ev_view_zoom), + (size_to_zoom_factor), (ev_view_set_size), (ev_view_find_next), + (ev_view_find_previous): + * shell/ev-view.h: + * shell/ev-window.c: (update_action_sensitivity), + (ev_window_open_page), (update_window_title), (update_total_pages), + (page_changed_cb), (ev_window_setup_document), + (password_dialog_response), (ev_window_cmd_save_as), + (ev_window_print), (ev_window_cmd_go_previous_page), + (ev_window_cmd_go_next_page), (ev_window_cmd_go_first_page), + (ev_window_cmd_go_last_page), (ev_window_cmd_view_reload), + (find_bar_search_changed_cb), (goto_page_cb), (ev_window_init): + * shell/main.c: (main): + + Merge evince-threads branch + 2005-03-16 Žygimantas Beručka * configure.ac: Added "lt" to ALL_LINGUAS. diff --git a/TODO b/TODO index 73d4b0d..afe2a0f 100644 --- a/TODO +++ b/TODO @@ -12,17 +12,13 @@ Move to three page views: Sidebar improvements for ps/pixbuf, or PDF files without a TOC. bug 164811 -Improve look of combobox Thumbnails/Index bug 166683 - Improve Printing Support: (libgnomeprintui?) - * Print Page Selection + * Print Page Selection bug 169099 * Print Page Layout bug 166564 Document Properties Dialog for document meta-data bug 164843 -Provide Desktop icon Thumbnailer for Documents bug 163789 - Make an object that handles the page count. Move to having three sizing types: @@ -41,3 +37,5 @@ Encrypted PDF support Improve Find system Find Next/Previous don't go to other pages Find Next/Previous doesn't focus the results +Improve look of combobox Thumbnails/Index bug 166683 +Provide Desktop icon Thumbnailer for Documents bug 163789 diff --git a/backend/Makefile.am b/backend/Makefile.am index 9030333..be84458 100644 --- a/backend/Makefile.am +++ b/backend/Makefile.am @@ -23,6 +23,12 @@ libevbackend_la_SOURCES= \ ev-document-security.h \ ev-document-find.c \ ev-document-find.h \ + ev-job-queue.h \ + ev-job-queue.c \ + ev-jobs.h \ + ev-jobs.c \ + ev-page-cache.h \ + ev-page-cache.c \ ev-ps-exporter.c \ ev-ps-exporter.h \ ev-document-misc.h \ diff --git a/backend/ev-document-links.c b/backend/ev-document-links.c index 242efb6..d9baae0 100644 --- a/backend/ev-document-links.c +++ b/backend/ev-document-links.c @@ -24,6 +24,7 @@ #include "config.h" #include "ev-document-links.h" +#include "ev-job-queue.h" GType ev_document_links_get_type (void) @@ -51,51 +52,20 @@ gboolean ev_document_links_has_document_links (EvDocumentLinks *document_links) { EvDocumentLinksIface *iface = EV_DOCUMENT_LINKS_GET_IFACE (document_links); - return iface->has_document_links (document_links); -} - -EvDocumentLinksIter * -ev_document_links_begin_read (EvDocumentLinks *document_links) -{ - EvDocumentLinksIface *iface = EV_DOCUMENT_LINKS_GET_IFACE (document_links); - - return iface->begin_read (document_links); -} + gboolean retval; -EvLink * -ev_document_links_get_link (EvDocumentLinks *document_links, - EvDocumentLinksIter *iter) -{ - EvDocumentLinksIface *iface = EV_DOCUMENT_LINKS_GET_IFACE (document_links); + retval = iface->has_document_links (document_links); - return iface->get_link (document_links, iter); + return retval; } -EvDocumentLinksIter * -ev_document_links_get_child (EvDocumentLinks *document_links, - EvDocumentLinksIter *iter) +GtkTreeModel * +ev_document_links_get_links_model (EvDocumentLinks *document_links) { EvDocumentLinksIface *iface = EV_DOCUMENT_LINKS_GET_IFACE (document_links); + GtkTreeModel *retval; - return iface->get_child (document_links, iter); -} - - -gboolean -ev_document_links_next (EvDocumentLinks *document_links, - EvDocumentLinksIter *iter) -{ - EvDocumentLinksIface *iface = EV_DOCUMENT_LINKS_GET_IFACE (document_links); - - return iface->next (document_links, iter); -} - - -void -ev_document_links_free_iter (EvDocumentLinks *document_links, - EvDocumentLinksIter *iter) -{ - EvDocumentLinksIface *iface = EV_DOCUMENT_LINKS_GET_IFACE (document_links); + retval = iface->get_links_model (document_links); - iface->free_iter (document_links, iter); + return retval; } diff --git a/backend/ev-document-links.h b/backend/ev-document-links.h index 59e638b..6105a70 100644 --- a/backend/ev-document-links.h +++ b/backend/ev-document-links.h @@ -26,7 +26,7 @@ #include #include -#include +#include #include "ev-document.h" #include "ev-link.h" @@ -43,37 +43,25 @@ G_BEGIN_DECLS typedef struct _EvDocumentLinks EvDocumentLinks; typedef struct _EvDocumentLinksIface EvDocumentLinksIface; -typedef struct _EvDocumentLinksIter EvDocumentLinksIter; + +enum { + EV_DOCUMENT_LINKS_COLUMN_MARKUP, + EV_DOCUMENT_LINKS_COLUMN_LINK, + EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS +}; struct _EvDocumentLinksIface { GTypeInterface base_iface; /* Methods */ - gboolean (* has_document_links) (EvDocumentLinks *document_links); - EvDocumentLinksIter *(* begin_read) (EvDocumentLinks *document_links); - EvLink *(* get_link) (EvDocumentLinks *document_links, - EvDocumentLinksIter *iter); - EvDocumentLinksIter *(* get_child) (EvDocumentLinks *document_links, - EvDocumentLinksIter *iter); - gboolean (* next) (EvDocumentLinks *document_links, - EvDocumentLinksIter *iter); - void (* free_iter) (EvDocumentLinks *document_links, - EvDocumentLinksIter *iter); + gboolean (* has_document_links) (EvDocumentLinks *document_links); + GtkTreeModel *(* get_links_model) (EvDocumentLinks *document_links); }; -GType ev_document_links_get_type (void); -gboolean ev_document_links_has_document_links (EvDocumentLinks *document_links); -EvDocumentLinksIter *ev_document_links_begin_read (EvDocumentLinks *document_links); -EvLink *ev_document_links_get_link (EvDocumentLinks *document_links, - EvDocumentLinksIter *iter); -EvDocumentLinksIter *ev_document_links_get_child (EvDocumentLinks *document_links, - EvDocumentLinksIter *iter); -gboolean ev_document_links_next (EvDocumentLinks *document_links, - EvDocumentLinksIter *iter); -void ev_document_links_free_iter (EvDocumentLinks *document_links, - EvDocumentLinksIter *iter); - +GType ev_document_links_get_type (void); +gboolean ev_document_links_has_document_links (EvDocumentLinks *document_links); +GtkTreeModel *ev_document_links_get_links_model (EvDocumentLinks *document_links); G_END_DECLS diff --git a/backend/ev-document.c b/backend/ev-document.c index 5109969..6238ac5 100644 --- a/backend/ev-document.c +++ b/backend/ev-document.c @@ -21,7 +21,9 @@ #include "config.h" #include "ev-document.h" + #include "ev-backend-marshalers.h" +#include "ev-job-queue.h" static void ev_document_class_init (gpointer g_class); @@ -33,7 +35,10 @@ enum }; static guint signals[LAST_SIGNAL] = { 0 }; +GMutex *ev_doc_mutex = NULL; + +#define LOG(x) GType ev_document_get_type (void) { @@ -98,13 +103,46 @@ ev_document_class_init (gpointer g_class) G_PARAM_READABLE)); } +#define PAGE_CACHE_STRING "ev-page-cache" + +EvPageCache * +ev_document_get_page_cache (EvDocument *document) +{ + EvPageCache *page_cache; + + g_return_val_if_fail (EV_IS_DOCUMENT (document), 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); + } + + return page_cache; +} + +GMutex * +ev_document_get_doc_mutex (void) +{ + if (ev_doc_mutex == NULL) { + ev_doc_mutex = g_mutex_new (); + } + return ev_doc_mutex; +} + + gboolean ev_document_load (EvDocument *document, const char *uri, GError **error) { EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); - return iface->load (document, uri, error); + gboolean retval; + LOG ("ev_document_load"); + retval = iface->load (document, uri, error); + /* Call this to make the initial cached copy */ + ev_document_get_page_cache (document); + return retval; } gboolean @@ -113,7 +151,12 @@ ev_document_save (EvDocument *document, GError **error) { EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); - return iface->save (document, uri, error); + gboolean retval; + + LOG ("ev_document_save"); + retval = iface->save (document, uri, error); + + return retval; } char * @@ -121,6 +164,7 @@ ev_document_get_title (EvDocument *document) { char *title; + LOG ("ev_document_get_title"); g_object_get (document, "title", &title, NULL); return title; @@ -130,7 +174,12 @@ int ev_document_get_n_pages (EvDocument *document) { EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); - return iface->get_n_pages (document); + gint retval; + + LOG ("ev_document_get_n_pages"); + retval = iface->get_n_pages (document); + + return retval; } void @@ -138,6 +187,8 @@ ev_document_set_page (EvDocument *document, int page) { EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); + + LOG ("ev_document_set_page"); iface->set_page (document, page); } @@ -145,7 +196,12 @@ int ev_document_get_page (EvDocument *document) { EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); - return iface->get_page (document); + int retval; + + LOG ("ev_document_get_page"); + retval = iface->get_page (document); + + return retval; } void @@ -153,6 +209,8 @@ ev_document_set_target (EvDocument *document, GdkDrawable *target) { EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); + + LOG ("ev_document_set_target"); iface->set_target (document, target); } @@ -161,6 +219,8 @@ ev_document_set_scale (EvDocument *document, double scale) { EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); + + LOG ("ev_document_set_scale"); iface->set_scale (document, scale); } @@ -170,6 +230,8 @@ ev_document_set_page_offset (EvDocument *document, int y) { EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); + + LOG ("ev_document_set_page_offset"); iface->set_page_offset (document, x, y); } @@ -180,6 +242,8 @@ ev_document_get_page_size (EvDocument *document, int *height) { EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); + + LOG ("ev_document_get_page_size"); iface->get_page_size (document, page, width, height); } @@ -188,7 +252,12 @@ ev_document_get_text (EvDocument *document, GdkRectangle *rect) { EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); - return iface->get_text (document, rect); + char *retval; + + LOG ("ev_document_get_text"); + retval = iface->get_text (document, rect); + + return retval; } EvLink * @@ -197,7 +266,12 @@ ev_document_get_link (EvDocument *document, int y) { EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); - return iface->get_link (document, x, y); + EvLink *retval; + + LOG ("ev_document_get_link"); + retval = iface->get_link (document, x, y); + + return retval; } void @@ -208,9 +282,27 @@ ev_document_render (EvDocument *document, int clip_height) { EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); + + LOG ("ev_document_render"); iface->render (document, clip_x, clip_y, clip_width, clip_height); } + +GdkPixbuf * +ev_document_render_pixbuf (EvDocument *document) +{ + EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document); + GdkPixbuf *retval; + + LOG ("ev_document_render_pixbuf"); + g_assert (iface->render_pixbuf); + + retval = iface->render_pixbuf (document); + + return retval; +} + + void ev_document_page_changed (EvDocument *document) { @@ -221,4 +313,4 @@ void ev_document_scale_changed (EvDocument *document) { g_signal_emit (G_OBJECT (document), signals[SCALE_CHANGED], 0); -} +} diff --git a/backend/ev-document.h b/backend/ev-document.h index b54a0b9..be0d1db 100644 --- a/backend/ev-document.h +++ b/backend/ev-document.h @@ -37,10 +37,16 @@ G_BEGIN_DECLS #define EV_IS_DOCUMENT_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_DOCUMENT)) #define EV_DOCUMENT_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_DOCUMENT, EvDocumentIface)) -typedef struct _EvDocument EvDocument; -typedef struct _EvDocumentIface EvDocumentIface; +typedef struct _EvDocument EvDocument; +typedef struct _EvDocumentIface EvDocumentIface; +typedef struct _EvPageCache EvPageCache; +typedef struct _EvPageCacheClass EvPageCacheClass; + +#include "ev-page-cache.h" + #define EV_DOCUMENT_ERROR ev_document_error_quark () +#define EV_DOC_MUTEX (ev_document_get_doc_mutex ()) typedef enum { @@ -88,12 +94,17 @@ struct _EvDocumentIface int clip_y, int clip_width, int clip_height); + GdkPixbuf *(* render_pixbuf) (EvDocument *document); + }; -GType ev_document_get_type (void); -GQuark ev_document_error_quark (void); +GType ev_document_get_type (void); +GQuark ev_document_error_quark (void); +EvPageCache *ev_document_get_page_cache (EvDocument *document); +GMutex *ev_document_get_doc_mutex (void); + gboolean ev_document_load (EvDocument *document, const char *uri, @@ -127,6 +138,8 @@ void ev_document_render (EvDocument *document, int clip_y, int clip_width, int clip_height); +/* Quick hack to test threaded rendering */ +GdkPixbuf *ev_document_render_pixbuf (EvDocument *document); void ev_document_page_changed (EvDocument *document); void ev_document_scale_changed (EvDocument *document); diff --git a/backend/ev-job-queue.c b/backend/ev-job-queue.c new file mode 100644 index 0000000..4bd824f --- /dev/null +++ b/backend/ev-job-queue.c @@ -0,0 +1,211 @@ +#include "ev-job-queue.h" + +/* Like glib calling convention, all functions with _locked in their name assume + * that we've already locked the doc mutex and can freely and safely access + * data. + */ +GCond *render_cond = NULL; +GMutex *ev_queue_mutex = NULL; + +static GQueue *links_queue = NULL; +static GQueue *render_queue_high = NULL; +static GQueue *render_queue_low = NULL; +static GQueue *thumbnail_queue_high = NULL; +static GQueue *thumbnail_queue_low = NULL; + +static gboolean +remove_object_from_queue (GQueue *queue, EvJob *job) +{ + GList *list; + + list = g_queue_find (queue, job); + if (list) { + g_object_unref (G_OBJECT (job)); + g_queue_delete_link (queue, list); + + return TRUE; + } + return FALSE; +} + + +static gboolean +notify_finished (GObject *job) +{ + ev_job_finished (EV_JOB (job)); + + return FALSE; +} + + +static void +handle_job (EvJob *job) +{ + g_object_ref (G_OBJECT (job)); + + if (EV_IS_JOB_THUMBNAIL (job)) + ev_job_thumbnail_run (EV_JOB_THUMBNAIL (job)); + else if (EV_IS_JOB_LINKS (job)) + ev_job_links_run (EV_JOB_LINKS (job)); + else if (EV_IS_JOB_RENDER (job)) + ev_job_render_run (EV_JOB_RENDER (job)); + + /* We let the idle own a ref, as we (the queue) are done with the job. */ + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) notify_finished, + job, + g_object_unref); +} + +static EvJob * +search_for_jobs_unlocked (void) +{ + EvJob *job; + + job = (EvJob *) g_queue_pop_head (render_queue_high); + if (job) + return job; + + job = (EvJob *) g_queue_pop_head (thumbnail_queue_high); + if (job) + return job; + + job = (EvJob *) g_queue_pop_head (render_queue_low); + if (job) + return job; + + job = (EvJob *) g_queue_pop_head (links_queue); + if (job) + return job; + + job = (EvJob *) g_queue_pop_head (thumbnail_queue_low); + if (job) + return job; + + return NULL; +} + +static gboolean +no_jobs_available_unlocked (void) +{ + return g_queue_is_empty (render_queue_high) + && g_queue_is_empty (render_queue_low) + && g_queue_is_empty (links_queue) + && g_queue_is_empty (thumbnail_queue_high) + && g_queue_is_empty (thumbnail_queue_low); +} + +/* the thread mainloop function */ +static gpointer +ev_render_thread (gpointer data) +{ + while (TRUE) { + EvJob *job; + + g_mutex_lock (ev_queue_mutex); + if (no_jobs_available_unlocked ()) { + g_cond_wait (render_cond, ev_queue_mutex); + } + + job = search_for_jobs_unlocked (); + g_mutex_unlock (ev_queue_mutex); + + /* Now that we have our job, we handle it */ + if (job) { + handle_job (job); + g_object_unref (G_OBJECT (job)); + } + } + return NULL; + +} + +/* Public Functions */ +void +ev_job_queue_init (void) +{ + if (!g_thread_supported ()) g_thread_init (NULL); + + render_cond = g_cond_new (); + ev_queue_mutex = g_mutex_new (); + + links_queue = g_queue_new (); + render_queue_high = g_queue_new (); + render_queue_low = g_queue_new (); + thumbnail_queue_high = g_queue_new (); + thumbnail_queue_low = g_queue_new (); + + g_thread_create (ev_render_thread, NULL, FALSE, NULL); + +} + +static GQueue * +find_queue (EvJob *job, + EvJobPriority priority) +{ + if (EV_IS_JOB_RENDER (job)) { + if (priority == EV_JOB_PRIORITY_HIGH) + return render_queue_high; + else + return render_queue_low; + } else if (EV_IS_JOB_THUMBNAIL (job)) { + if (priority == EV_JOB_PRIORITY_HIGH) + return thumbnail_queue_high; + else + return thumbnail_queue_low; + } else if (EV_IS_JOB_LINKS (job)) { + /* the priority doesn't effect links */ + return links_queue; + } + + g_assert_not_reached (); + return NULL; +} + +void +ev_job_queue_add_job (EvJob *job, + EvJobPriority priority) +{ + GQueue *queue; + + g_return_if_fail (EV_IS_JOB (job)); + + queue = find_queue (job, priority); + + g_mutex_lock (ev_queue_mutex); + + g_object_ref (job); + g_queue_push_tail (queue, job); + g_cond_broadcast (render_cond); + + g_mutex_unlock (ev_queue_mutex); + +} + +gboolean +ev_job_queue_remove_job (EvJob *job) +{ + gboolean retval = FALSE; + + g_return_val_if_fail (EV_IS_JOB (job), FALSE); + + g_mutex_lock (ev_queue_mutex); + + if (EV_IS_JOB_THUMBNAIL (job)) { + retval = remove_object_from_queue (thumbnail_queue_high, job); + retval = retval || remove_object_from_queue (thumbnail_queue_low, job); + } else if (EV_IS_JOB_RENDER (job)) { + retval = remove_object_from_queue (render_queue_high, job); + retval = retval || remove_object_from_queue (render_queue_low, job); + } else if (EV_IS_JOB_LINKS (job)) { + retval = remove_object_from_queue (links_queue, job); + } else { + g_assert_not_reached (); + } + + g_mutex_unlock (ev_queue_mutex); + + return retval; +} + + diff --git a/backend/ev-job-queue.h b/backend/ev-job-queue.h new file mode 100644 index 0000000..ec93389 --- /dev/null +++ b/backend/ev-job-queue.h @@ -0,0 +1,37 @@ +/* this file is part of evince, a gnome document viewer + * + * Copyright (C) 2005 Red Hat, Inc + * + * Evince is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Evince is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EV_JOB_QUEUE_H__ +#define __EV_JOB_QUEUE_H__ + +#include +#include "ev-jobs.h" + +G_BEGIN_DECLS + + +void ev_job_queue_init (void); + +void ev_job_queue_add_job (EvJob *job, + EvJobPriority priority); +gboolean ev_job_queue_remove_job (EvJob *job); + +G_END_DECLS + +#endif /* __EV_JOB_QUEUE_H__ */ diff --git a/backend/ev-jobs.c b/backend/ev-jobs.c new file mode 100644 index 0000000..355a103 --- /dev/null +++ b/backend/ev-jobs.c @@ -0,0 +1,246 @@ +#include "ev-jobs.h" +#include "ev-job-queue.h" +#include "ev-document-thumbnails.h" +#include "ev-document-links.h" + +static void ev_job_init (EvJob *job); +static void ev_job_class_init (EvJobClass *class); +static void ev_job_links_init (EvJobLinks *job); +static void ev_job_links_class_init (EvJobLinksClass *class); +static void ev_job_render_init (EvJobRender *job); +static void ev_job_render_class_init (EvJobRenderClass *class); +static void ev_job_thumbnail_init (EvJobThumbnail *job); +static void ev_job_thumbnail_class_init (EvJobThumbnailClass *class); + +enum +{ + FINISHED, + LAST_SIGNAL +}; + +static guint job_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (EvJob, ev_job, G_TYPE_OBJECT) +G_DEFINE_TYPE (EvJobLinks, ev_job_links, EV_TYPE_JOB) +G_DEFINE_TYPE (EvJobRender, ev_job_render, EV_TYPE_JOB) +G_DEFINE_TYPE (EvJobThumbnail, ev_job_thumbnail, EV_TYPE_JOB) + + +static void ev_job_init (EvJob *job) { /* Do Nothing */ } + +static void +ev_job_dispose (GObject *object) +{ + EvJob *job; + + job = EV_JOB (object); + + if (job->document) { + g_object_unref (job->document); + job->document = NULL; + } + + (* G_OBJECT_CLASS (ev_job_parent_class)->dispose) (object); +} + +static void +ev_job_class_init (EvJobClass *class) +{ + GObjectClass *oclass; + + oclass = G_OBJECT_CLASS (class); + + oclass->dispose = ev_job_dispose; + + job_signals [FINISHED] = + g_signal_new ("finished", + EV_TYPE_JOB, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EvJobClass, finished), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + + +static void ev_job_links_init (EvJobLinks *job) { /* Do Nothing */ } + +static void +ev_job_links_dispose (GObject *object) +{ + EvJobLinks *job; + + job = EV_JOB_LINKS (object); + + if (job->model) { + g_object_unref (job->model); + job->model = NULL; + } + + (* G_OBJECT_CLASS (ev_job_links_parent_class)->dispose) (object); +} + +static void +ev_job_links_class_init (EvJobLinksClass *class) +{ + GObjectClass *oclass; + + oclass = G_OBJECT_CLASS (class); + + oclass->dispose = ev_job_links_dispose; +} + + +static void ev_job_render_init (EvJobRender *job) { /* Do Nothing */ } + +static void +ev_job_render_dispose (GObject *object) +{ + EvJobRender *job; + + job = EV_JOB_RENDER (object); + + if (job->pixbuf) { + g_object_unref (job->pixbuf); + job->pixbuf = NULL; + } + + (* G_OBJECT_CLASS (ev_job_render_parent_class)->dispose) (object); +} + +static void +ev_job_render_class_init (EvJobRenderClass *class) +{ + GObjectClass *oclass; + + oclass = G_OBJECT_CLASS (class); + + oclass->dispose = ev_job_render_dispose; +} + +static void ev_job_thumbnail_init (EvJobThumbnail *job) { /* Do Nothing */ } + +static void +ev_job_thumbnail_dispose (GObject *object) +{ + EvJobThumbnail *job; + + job = EV_JOB_THUMBNAIL (object); + + if (job->thumbnail) { + g_object_unref (job->thumbnail); + job->thumbnail = NULL; + } + + (* G_OBJECT_CLASS (ev_job_thumbnail_parent_class)->dispose) (object); +} + +static void +ev_job_thumbnail_class_init (EvJobThumbnailClass *class) +{ + GObjectClass *oclass; + + oclass = G_OBJECT_CLASS (class); + + oclass->dispose = ev_job_thumbnail_dispose; +} + +/* Public functions */ +void +ev_job_finished (EvJob *job) +{ + g_return_if_fail (EV_IS_JOB (job)); + + g_signal_emit (job, job_signals[FINISHED], 0); +} + +EvJob * +ev_job_links_new (EvDocument *document) +{ + EvJob *job; + + job = g_object_new (EV_TYPE_JOB_LINKS, NULL); + job->document = g_object_ref (document); + + return job; +} + +void +ev_job_links_run (EvJobLinks *job) +{ + g_return_if_fail (EV_IS_JOB_LINKS (job)); + + g_mutex_lock (EV_DOC_MUTEX); + job->model = ev_document_links_get_links_model (EV_DOCUMENT_LINKS (EV_JOB (job)->document)); + EV_JOB (job)->finished = TRUE; + g_mutex_unlock (EV_DOC_MUTEX); +} + + +EvJob * +ev_job_render_new (EvDocument *document, + gint page, + double scale, + gint width, + gint height) +{ + EvJobRender *job; + + job = g_object_new (EV_TYPE_JOB_RENDER, NULL); + + EV_JOB (job)->document = g_object_ref (document); + job->page = page; + job->scale = scale; + job->target_width = width; + job->target_height = height; + + return EV_JOB (job); +} + +void +ev_job_render_run (EvJobRender *job) +{ + g_return_if_fail (EV_IS_JOB_RENDER (job)); + + g_mutex_lock (EV_DOC_MUTEX); + + ev_document_set_scale (EV_JOB (job)->document, job->scale); + ev_document_set_page (EV_JOB (job)->document, job->page); + job->pixbuf = ev_document_render_pixbuf (EV_JOB (job)->document); + EV_JOB (job)->finished = TRUE; + + g_mutex_unlock (EV_DOC_MUTEX); +} + +EvJob * +ev_job_thumbnail_new (EvDocument *document, + gint page, + gint requested_width) +{ + EvJobThumbnail *job; + + job = g_object_new (EV_TYPE_JOB_THUMBNAIL, NULL); + + EV_JOB (job)->document = g_object_ref (document); + job->page = page; + job->requested_width = requested_width; + + return EV_JOB (job); +} + +void +ev_job_thumbnail_run (EvJobThumbnail *job) +{ + g_return_if_fail (EV_IS_JOB_THUMBNAIL (job)); + + g_mutex_lock (EV_DOC_MUTEX); + + job->thumbnail = + ev_document_thumbnails_get_thumbnail (EV_DOCUMENT_THUMBNAILS (EV_JOB (job)->document), + job->page, + job->requested_width, + TRUE); + EV_JOB (job)->finished = TRUE; + + g_mutex_unlock (EV_DOC_MUTEX); +} diff --git a/backend/ev-jobs.h b/backend/ev-jobs.h new file mode 100644 index 0000000..a212349 --- /dev/null +++ b/backend/ev-jobs.h @@ -0,0 +1,150 @@ +/* this file is part of evince, a gnome document viewer + * + * Copyright (C) 2005 Red Hat, Inc + * + * Evince is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Evince is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EV_JOBS_H__ +#define __EV_JOBS_H__ + +#include +#include "ev-document.h" + +G_BEGIN_DECLS + +typedef struct _EvJob EvJob; +typedef struct _EvJobClass EvJobClass; + +typedef struct _EvJobRender EvJobRender; +typedef struct _EvJobRenderClass EvJobRenderClass; + +typedef struct _EvJobThumbnail EvJobThumbnail; +typedef struct _EvJobThumbnailClass EvJobThumbnailClass; + +typedef struct _EvJobLinks EvJobLinks; +typedef struct _EvJobLinksClass EvJobLinksClass; + +#define EV_TYPE_JOB (ev_job_get_type()) +#define EV_JOB(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB, EvJob)) +#define EV_JOB_CLASS(klass) (G_TYPE_CHACK_CLASS_CAST((klass), EV_TYPE_JOB, EvJobClass)) +#define EV_IS_JOB(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB)) + +#define EV_TYPE_JOB_LINKS (ev_job_links_get_type()) +#define EV_JOB_LINKS(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB_LINKS, EvJobLinks)) +#define EV_JOB_LINKS_CLASS(klass) (G_TYPE_CHACK_CLASS_CAST((klass), EV_TYPE_JOB_LINKS, EvJobLinksClass)) +#define EV_IS_JOB_LINKS(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB_LINKS)) + +#define EV_TYPE_JOB_RENDER (ev_job_render_get_type()) +#define EV_JOB_RENDER(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB_RENDER, EvJobRender)) +#define EV_JOB_RENDER_CLASS(klass) (G_TYPE_CHACK_CLASS_CAST((klass), EV_TYPE_JOB_RENDER, EvJobRenderClass)) +#define EV_IS_JOB_RENDER(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB_RENDER)) + +#define EV_TYPE_JOB_THUMBNAIL (ev_job_thumbnail_get_type()) +#define EV_JOB_THUMBNAIL(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB_THUMBNAIL, EvJobThumbnail)) +#define EV_JOB_THUMBNAIL_CLASS(klass) (G_TYPE_CHACK_CLASS_CAST((klass), EV_TYPE_JOB_THUMBNAIL, EvJobThumbnailClass)) +#define EV_IS_JOB_THUMBNAIL(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB_THUMBNAIL)) + +typedef enum { + EV_JOB_PRIORITY_LOW, + EV_JOB_PRIORITY_HIGH, +} EvJobPriority; + +struct _EvJob +{ + GObject parent; + EvDocument *document; + gboolean finished; +}; + +struct _EvJobClass +{ + GObjectClass parent_class; + + void (* finished) (EvJob *job); +}; + +struct _EvJobLinks +{ + EvJob parent; + + GtkTreeModel *model; +}; + +struct _EvJobLinksClass +{ + EvJobClass parent_class; +}; + +struct _EvJobRender +{ + EvJob parent; + + gint page; + double scale; + gint target_width; + gint target_height; + GdkPixbuf *pixbuf; +}; + +struct _EvJobRenderClass +{ + EvJobClass parent_class; +}; + +struct _EvJobThumbnail +{ + EvJob parent; + + gint page; + gint requested_width; + GdkPixbuf *thumbnail; +}; + +struct _EvJobThumbnailClass +{ + EvJobClass parent_class; +}; + + +/* Base job class */ +GType ev_job_get_type (void); +void ev_job_finished (EvJob *job); + +/* EvJobLinks */ +GType ev_job_links_get_type (void); +EvJob *ev_job_links_new (EvDocument *document); +void ev_job_links_run (EvJobLinks *thumbnail); + +/* EvJobRender */ +GType ev_job_render_get_type (void); +EvJob *ev_job_render_new (EvDocument *document, + gint page, + double scale, + gint width, + gint height); +void ev_job_render_run (EvJobRender *thumbnail); + +/* EvJobThumbnail */ +GType ev_job_thumbnail_get_type (void); +EvJob *ev_job_thumbnail_new (EvDocument *document, + gint page, + gint requested_width); +void ev_job_thumbnail_run (EvJobThumbnail *thumbnail); + + +G_END_DECLS + +#endif /* __EV_JOBS_H__ */ diff --git a/backend/ev-link.c b/backend/ev-link.c index e01aa19..7e6eb52 100644 --- a/backend/ev-link.c +++ b/backend/ev-link.c @@ -32,6 +32,16 @@ enum { PROP_URI }; + +struct _EvLink { + GObject base_instance; + EvLinkPrivate *priv; +}; + +struct _EvLinkClass { + GObjectClass base_class; +}; + struct _EvLinkPrivate { char *title; char *uri; diff --git a/backend/ev-link.h b/backend/ev-link.h index 00d97c4..d92d428 100644 --- a/backend/ev-link.h +++ b/backend/ev-link.h @@ -41,18 +41,11 @@ typedef enum { EV_LINK_TYPE_TITLE, EV_LINK_TYPE_PAGE, - EV_LINK_TYPE_EXTERNAL_URI + EV_LINK_TYPE_EXTERNAL_URI, + /* We'll probably fill this in more as we support the other types of + * links */ } EvLinkType; -struct _EvLink { - GObject base_instance; - EvLinkPrivate *priv; -}; - -struct _EvLinkClass { - GObjectClass base_class; -}; - GType ev_link_type_get_type (void); GType ev_link_get_type (void); diff --git a/backend/ev-page-cache.c b/backend/ev-page-cache.c new file mode 100644 index 0000000..e2771b6 --- /dev/null +++ b/backend/ev-page-cache.c @@ -0,0 +1,256 @@ +#include "ev-page-cache.h" +#include "ev-job-queue.h" + +typedef struct _EvPageCacheInfo +{ + gint width; + gint height; +} +EvPageCacheInfo; + + +struct _EvPageCache +{ + GObject parent; + + gint current_page; + int n_pages; + char *title; + + gboolean uniform; + gint uniform_width; + gint uniform_height; + + EvPageCacheInfo *size_cache; +}; + +struct _EvPageCacheClass +{ + GObjectClass parent_class; + + void (* page_changed) (EvPageCache *page_cache, gint page); +}; + +enum +{ + PAGE_CHANGED, + N_SIGNALS, +}; + +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); + +G_DEFINE_TYPE (EvPageCache, ev_page_cache, G_TYPE_OBJECT) + +static void +ev_page_cache_init (EvPageCache *page_cache) +{ + page_cache->current_page = 1; +} + +static void +ev_page_cache_class_init (EvPageCacheClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + + object_class->finalize = ev_page_cache_finalize; + + 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); + +} + +static void +ev_page_cache_finalize (GObject *object) +{ + EvPageCache *page_cache; + + page_cache = EV_PAGE_CACHE (object); + + g_free (page_cache->title); + g_free (page_cache->size_cache); +} + +EvPageCache * +_ev_page_cache_new (EvDocument *document) +{ + EvPageCache *page_cache; + EvPageCacheInfo *info; + gint i; + + page_cache = (EvPageCache *) g_object_new (EV_TYPE_PAGE_CACHE, NULL); + + g_mutex_lock (EV_DOC_MUTEX); + + /* We read page information out of the document */ + + /* Assume all pages are the same size until proven otherwise */ + page_cache->uniform = TRUE; + page_cache->n_pages = ev_document_get_n_pages (document); + page_cache->title = ev_document_get_title (document); + + ev_document_set_scale (document, 1.0); + for (i = 1; i <= page_cache->n_pages; i++) { + gint page_width = 0; + gint page_height = 0; + + ev_document_get_page_size (document, i, &page_width, &page_height); + + if (i == 1) { + page_cache->uniform_width = page_width; + page_cache->uniform_height = page_height; + } else if (page_cache->uniform && + (page_cache->uniform_width != page_width || + page_cache->uniform_height != page_height)) { + /* It's a different page size. Backfill the array. */ + int j; + + page_cache->size_cache = g_new0 (EvPageCacheInfo, page_cache->n_pages); + + for (j = 1; j < i; j++) { + info = &(page_cache->size_cache [j - 1]); + info->width = page_width; + info->height = page_height; + } + page_cache->uniform = FALSE; + + } + + if (! page_cache->uniform) { + info = &(page_cache->size_cache [i - 1]); + + info->width = page_width; + info->height = page_height; + } + } + + /* make some sanity check assertions */ + g_assert (page_cache->n_pages > 0); + if (! page_cache->uniform) + g_assert (page_cache->size_cache != NULL); + if (page_cache->uniform) + g_assert (page_cache->uniform_width > 0 && page_cache->uniform_height > 0); + + g_mutex_unlock (EV_DOC_MUTEX); + + return page_cache; +} + +gint +ev_page_cache_get_n_pages (EvPageCache *page_cache) +{ + g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), 0); + + return page_cache->n_pages; +} + +gint +ev_page_cache_get_current_page (EvPageCache *page_cache) +{ + g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), 0); + + return page_cache->current_page; +} + +void +ev_page_cache_set_current_page (EvPageCache *page_cache, + int page) +{ + g_return_if_fail (EV_IS_PAGE_CACHE (page_cache)); + g_return_if_fail (page > 0 || page <= page_cache->n_pages); + + if (page == page_cache->current_page) + return; + + page_cache->current_page = page; + g_signal_emit (page_cache, signals[PAGE_CHANGED], 0, page); +} + +void +ev_page_cache_set_link (EvPageCache *page_cache, + EvLink *link) +{ + g_return_if_fail (EV_IS_PAGE_CACHE (page_cache)); + g_return_if_fail (EV_IS_LINK (link)); + + ev_page_cache_set_current_page (page_cache, ev_link_get_page (link)); +} + +char * +ev_page_cache_get_title (EvPageCache *page_cache) +{ + g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), NULL); + + return page_cache->title; +} + +void +ev_page_cache_get_size (EvPageCache *page_cache, + gint page, + gfloat scale, + gint *width, + gint *height) +{ + g_return_if_fail (EV_IS_PAGE_CACHE (page_cache)); + g_return_if_fail (page > 0 && page <= page_cache->n_pages); + + if (page_cache->uniform) { + if (width) + *width = page_cache->uniform_width; + if (height) + *height = page_cache->uniform_height; + } else { + EvPageCacheInfo *info; + + info = &(page_cache->size_cache [page - 1]); + + if (width) + *width = info->width; + if (height) + *height = info->height; + } + + if (width) + *width = (*width) * scale; + if (width) + *height = (*height) * scale; + +} + +gboolean +ev_page_cache_next_page (EvPageCache *page_cache) +{ + g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), FALSE); + + if (page_cache->current_page >= page_cache->n_pages) + return FALSE; + + ev_page_cache_set_current_page (page_cache, page_cache->current_page + 1); + return TRUE; + +} + +gboolean +ev_page_cache_prev_page (EvPageCache *page_cache) +{ + g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), FALSE); + + if (page_cache->current_page <= 1) + return FALSE; + + ev_page_cache_set_current_page (page_cache, page_cache->current_page - 1); + return TRUE; +} + diff --git a/backend/ev-page-cache.h b/backend/ev-page-cache.h new file mode 100644 index 0000000..9edf67c --- /dev/null +++ b/backend/ev-page-cache.h @@ -0,0 +1,54 @@ +/* this file is part of evince, a gnome document viewer + * + * Copyright (C) 2005 Red Hat, Inc + * + * Evince is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Evince is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EV_PAGE_CACHE_H__ +#define __EV_PAGE_CACHE_H__ + +#include +#include "ev-document.h" + +G_BEGIN_DECLS +#define EV_TYPE_PAGE_CACHE (ev_page_cache_get_type ()) +#define EV_PAGE_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_PAGE_CACHE, EvPageCache)) +#define EV_IS_PAGE_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_PAGE_CACHE)) + +GType ev_page_cache_get_type (void) G_GNUC_CONST; +/* Used by ev-document.c only */ +EvPageCache *_ev_page_cache_new (EvDocument *document); + +gint ev_page_cache_get_n_pages (EvPageCache *page_cache); +char *ev_page_cache_get_title (EvPageCache *page_cache); +void ev_page_cache_get_size (EvPageCache *page_cache, + gint page, + gfloat scale, + gint *width, + gint *height); + +/* Navigation */ +gint ev_page_cache_get_current_page (EvPageCache *page_cache); +void ev_page_cache_set_current_page (EvPageCache *page_cache, + int page); +void ev_page_cache_set_link (EvPageCache *page_cache, + EvLink *link); +gboolean ev_page_cache_next_page (EvPageCache *page_cache); +gboolean ev_page_cache_prev_page (EvPageCache *page_cache); + +G_END_DECLS + +#endif /* __EV_PAGE_CACHE_H__ */ diff --git a/pdf/pdf-document.cc b/pdf/pdf-document.cc index fc2568b..0d0ad89 100644 --- a/pdf/pdf-document.cc +++ b/pdf/pdf-document.cc @@ -92,6 +92,10 @@ struct _PdfDocument PdfDocumentSearch *search; }; +static EvLink *build_link_from_action (PdfDocument *pdf_document, + LinkAction *link_action, + const char *title); + static void pdf_document_document_links_iface_init (EvDocumentLinksIface *iface); static void pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface); static void pdf_document_document_iface_init (EvDocumentIface *iface); @@ -101,6 +105,11 @@ static void pdf_document_security_iface_init (EvDocumentSecurityIface static void pdf_document_search_free (PdfDocumentSearch *search); static void pdf_document_search_page_changed (PdfDocumentSearch *search); +static GdkPixbuf *bitmap_to_pixbuf (SplashBitmap *bitmap, + GdkPixbuf *target, + gint x_offset, + gint y_offset); + G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT, { @@ -259,7 +268,7 @@ pdf_document_set_page (EvDocument *document, { PdfDocument *pdf_document = PDF_DOCUMENT (document); - page = CLAMP (page, 1, ev_document_get_n_pages (document)); + page = CLAMP (page, 1, pdf_document_get_n_pages (document)); if (page != pdf_document->page) { pdf_document->page = page; @@ -362,7 +371,7 @@ pdf_document_get_page_size (EvDocument *document, int page_width = 1, page_height = 1; double scale = pdf_document->scale; - if (page == -1) + if (page == -1) page = pdf_document->page; doc_page = pdf_document->doc->getCatalog ()->getPage (page); @@ -415,6 +424,38 @@ pdf_document_render (EvDocument *document, draw.width, draw.height); } + + +static GdkPixbuf * +pdf_document_render_pixbuf (EvDocument *document) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + SplashOutputDev *output; + GdkPixbuf *pixbuf; + SplashColor color; + + color.rgb8 = splashMakeRGB8 (255, 255, 255); + + output = new SplashOutputDev (splashModeRGB8, gFalse, color); + output->startDoc (pdf_document->doc->getXRef()); + + pdf_document->doc->displayPage (output, + pdf_document->page, + 72*pdf_document->scale, + 72*pdf_document->scale, + 0, gTrue, gFalse); + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + FALSE, 8, + output->getBitmap()->getWidth(), + output->getBitmap()->getHeight()); + + bitmap_to_pixbuf (output->getBitmap(), pixbuf, 0, 0); + delete output; + + return pixbuf; +} + double pdf_document_find_get_progress (EvDocumentFind *document_find) { @@ -427,7 +468,7 @@ pdf_document_find_get_progress (EvDocumentFind *document_find) return 0; } - n_pages = ev_document_get_n_pages (EV_DOCUMENT (document_find)); + n_pages = pdf_document_get_n_pages (EV_DOCUMENT (document_find)); if (search->search_page > search->start_page) { pages_done = search->search_page - search->start_page + 1; } else if (search->search_page == search->start_page) { @@ -544,7 +585,7 @@ pdf_document_search_idle_callback (void *data) * and then when the user switches to the current page, we * will emit "found" again with the real results. */ - n_pages = ev_document_get_n_pages (EV_DOCUMENT (search->document)); + n_pages = pdf_document_get_n_pages (EV_DOCUMENT (search->document)); if (search->output_dev == 0) { /* First time through here... */ @@ -632,7 +673,7 @@ pdf_document_find_begin (EvDocumentFind *document, search->current_page_results = g_array_new (FALSE, FALSE, sizeof (GdkRectangle)); - n_pages = ev_document_get_n_pages (EV_DOCUMENT (document)); + n_pages = pdf_document_get_n_pages (EV_DOCUMENT (document)); search->other_page_flags = g_new0 (int, n_pages + 1); for (i = 0; i <= n_pages; i++) { @@ -716,7 +757,7 @@ pdf_document_ps_export_begin (EvPSExporter *exporter, const char *filename) document->ps_out = new PSOutputDev ((char *)filename, document->doc->getXRef(), document->doc->getCatalog(), 1, - ev_document_get_n_pages (EV_DOCUMENT (document)), + pdf_document_get_n_pages (EV_DOCUMENT (document)), psModePS); } @@ -780,27 +821,65 @@ pdf_document_links_has_document_links (EvDocumentLinks *document_links) return FALSE; } -static EvDocumentLinksIter * -pdf_document_links_begin_read (EvDocumentLinks *document_links) +static void +build_tree (PdfDocument *pdf_document, + GtkTreeModel *model, + GtkTreeIter *parent, + GooList *items) +{ + int i; + + for (i = 0; i < items->getLength (); i++) { + OutlineItem *item; + GtkTreeIter iter; + LinkAction *link_action; + gchar *title; + EvLink *link; + + item = (OutlineItem *)items->get (i); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, parent); + + link_action = item->getAction (); + title = unicode_to_char (item, pdf_document->umap); + link = build_link_from_action (pdf_document, link_action, title); + + gtk_tree_store_set (GTK_TREE_STORE (model), &iter, + EV_DOCUMENT_LINKS_COLUMN_MARKUP, title, + EV_DOCUMENT_LINKS_COLUMN_LINK, link, + -1); + + item->open (); + if (item->hasKids () && item->getKids ()) { + build_tree (pdf_document, + model, + &iter, + item->getKids ()); + } + } +} + +static GtkTreeModel * +pdf_document_links_get_links_model (EvDocumentLinks *document_links) { PdfDocument *pdf_document = PDF_DOCUMENT (document_links); + GtkTreeModel *model = NULL; Outline *outline; - LinksIter *iter; GooList *items; g_return_val_if_fail (PDF_IS_DOCUMENT (document_links), NULL); outline = pdf_document->doc->getOutline(); items = outline->getItems(); - if (! items) - return NULL; - iter = g_new0 (LinksIter, 1); - iter->items = items; - iter->index = 0; - iter->level = 0; + /* Create the model iff we have items*/ + if (items != NULL) { + model = (GtkTreeModel *) gtk_tree_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS, + G_TYPE_STRING, + G_TYPE_POINTER); + build_tree (pdf_document, model, NULL, items); + } - return (EvDocumentLinksIter *) iter; + return model; } static EvLink * @@ -873,6 +952,31 @@ build_link_from_action (PdfDocument *pdf_document, return link; } + +#if 0 +static EvDocumentLinksIter * +pdf_document_links_begin_read (EvDocumentLinks *document_links) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document_links); + Outline *outline; + LinksIter *iter; + GooList *items; + + g_return_val_if_fail (PDF_IS_DOCUMENT (document_links), NULL); + + outline = pdf_document->doc->getOutline(); + items = outline->getItems(); + if (! items) + return NULL; + + iter = g_new0 (LinksIter, 1); + iter->items = items; + iter->index = 0; + iter->level = 0; + + return (EvDocumentLinksIter *) iter; +} + /* FIXME This returns a new object every time, probably we should cache it in the iter */ static EvLink * @@ -946,6 +1050,7 @@ pdf_document_links_free_iter (EvDocumentLinks *document_links, /* FIXME: Should I close all the nodes?? Free them? */ g_free (iter); } +#endif static void pdf_document_finalize (GObject *object) @@ -1086,7 +1191,7 @@ pdf_document_get_link (EvDocument *document, int x, int y) link_y = link_y / pdf_document->scale; action = pdf_document->links->find (link_x, link_y); - + if (action) { return build_link_from_action (pdf_document, action, ""); } else { @@ -1140,6 +1245,7 @@ pdf_document_document_iface_init (EvDocumentIface *iface) iface->set_page_offset = pdf_document_set_page_offset; iface->get_page_size = pdf_document_get_page_size; iface->render = pdf_document_render; + iface->render_pixbuf = pdf_document_render_pixbuf; } static void @@ -1173,11 +1279,7 @@ static void pdf_document_document_links_iface_init (EvDocumentLinksIface *iface) { iface->has_document_links = pdf_document_links_has_document_links; - iface->begin_read = pdf_document_links_begin_read; - iface->get_link = pdf_document_links_get_link; - iface->get_child = pdf_document_links_get_child; - iface->next = pdf_document_links_next; - iface->free_iter = pdf_document_links_free_iter; + iface->get_links_model = pdf_document_links_get_links_model; } /* Thumbnails */ @@ -1217,7 +1319,7 @@ bitmap_to_pixbuf (SplashBitmap *bitmap, guchar *q; p = dataPtr.rgb8 + y * width; - q = target_data + ((y + y_offset) * target_rowstride + + q = target_data + ((y + y_offset) * target_rowstride + x_offset * (target_has_alpha?4:3)); for (x = 0; x < width; x++) { rgb = *p++; @@ -1287,7 +1389,7 @@ pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnail Object the_thumb; Thumb *thumb = NULL; - the_page = pdf_document->doc->getCatalog ()->getPage (page); + the_page = pdf_document->doc->getCatalog ()->getPage (page + 1); the_page->getThumb (&the_thumb); if (!(the_thumb.isNull () || the_thumb.isNone())) { diff --git a/ps/gsdefaults.c b/ps/gsdefaults.c index faa4d06..a0c61d5 100644 --- a/ps/gsdefaults.c +++ b/ps/gsdefaults.c @@ -61,12 +61,6 @@ gtk_gs_defaults_get_interpreter_cmd() } const gchar * -gtk_gs_defaults_get_alpha_parameters (void) -{ - return ALPHA_PARAMS; -} - -const gchar * gtk_gs_defaults_get_ungzip_cmd (void) { return "gzip -cd"; diff --git a/ps/gsdefaults.h b/ps/gsdefaults.h index e8f4779..08431ff 100644 --- a/ps/gsdefaults.h +++ b/ps/gsdefaults.h @@ -32,7 +32,6 @@ G_BEGIN_DECLS GtkGSPaperSize *gtk_gs_defaults_get_paper_sizes(void); const gchar *gtk_gs_defaults_get_interpreter_cmd(void); -const gchar *gtk_gs_defaults_get_alpha_parameters(void); const gchar *gtk_gs_defaults_get_ungzip_cmd(void); const gchar *gtk_gs_defaults_get_unbzip2_cmd(void); diff --git a/ps/ps-document.c b/ps/ps-document.c index 958d768..4dd344b 100644 --- a/ps/ps-document.c +++ b/ps/ps-document.c @@ -22,108 +22,6 @@ * Boston, MA 02111-1307, USA. */ -/* -Ghostview interface to ghostscript - -When the GHOSTVIEW environment variable is set, ghostscript draws on -an existing drawable rather than creating its own window. Ghostscript -can be directed to draw on either a window or a pixmap. - -Drawing on a Window - -The GHOSTVIEW environment variable contains the window id of the target -window. The window id is an integer. Ghostscript will use the attributes -of the window to obtain the width, height, colormap, screen, and visual of -the window. The remainder of the information is gotten from the GHOSTVIEW -property on that window. - - -Drawing on a Pixmap - -The GHOSTVIEW environment variable contains a window id and a pixmap id. -They are integers separated by white space. Ghostscript will use the -attributes of the window to obtain the colormap, screen, and visual to use. -The width and height will be obtained from the pixmap. The remainder of the -information, is gotten from the GHOSTVIEW property on the window. In this -case, the property is deleted when read. - -The GHOSTVIEW environment variable - -parameters: window-id [pixmap-id] - -scanf format: "%d %d" - -explanation of parameters: - - window-id: tells ghostscript where to - - read the GHOSTVIEW property - - send events - If pixmap-id is not present, - ghostscript will draw on this window. - - pixmap-id: If present, tells ghostscript that a pixmap will be used - as the final destination for drawing. The window will - not be touched for drawing purposes. - -The GHOSTVIEW property - -type: STRING - -parameters: - - bpixmap orient llx lly urx ury xdpi ydpi [left bottom top right] - -scanf format: "%d %d %d %d %d %d %f %f %d %d %d %d" - -explanation of parameters: - - bpixmap: pixmap id of the backing pixmap for the window. If no - pixmap is to be used, this parameter should be zero. This - parameter must be zero when drawing on a pixmap. - - orient: orientation of the page. The number represents clockwise - rotation of the paper in degrees. Permitted values are - 0, 90, 180, 270. - - llx, lly, urx, ury: Bounding box of the drawable. The bounding box - is specified in PostScript points in default user coordinates. - - xdpi, ydpi: Resolution of window. (This can be derived from the - other parameters, but not without roundoff error. These - values are included to avoid this error.) - - left, bottom, top, right: (optional) - Margins around the window. The margins extend the imageable - area beyond the boundaries of the window. This is primarily - used for popup zoom windows. I have encountered several - instances of PostScript programs that position themselves - with respect to the imageable area. The margins are specified - in PostScript points. If omitted, the margins are assumed to - be 0. - -Events from ghostscript - -If the final destination is a pixmap, the client will get a property notify -event when ghostscript reads the GHOSTVIEW property causing it to be deleted. - -Ghostscript sends events to the window where it read the GHOSTVIEW property. -These events are of type ClientMessage. The message_type is set to -either PAGE or DONE. The first long data value gives the window to be used -to send replies to ghostscript. The second long data value gives the primary -drawable. If rendering to a pixmap, it is the primary drawable. If rendering -to a window, the backing pixmap is the primary drawable. If no backing pixmap -is employed, then the window is the primary drawable. This field is necessary -to distinguish multiple ghostscripts rendering to separate pixmaps where the -GHOSTVIEW property was placed on the same window. - -The PAGE message indicates that a "page" has completed. Ghostscript will -wait until it receives a ClientMessage whose message_type is NEXT before -continuing. - -The DONE message indicates that ghostscript has finished processing. - -*/ - #include "config.h" #include #include @@ -134,10 +32,6 @@ The DONE message indicates that ghostscript has finished processing. #include #include #include -#ifdef HAVE_XINERAMA -# include -# include -#endif /* HAVE_XINERAMA */ #include #include #include @@ -162,9 +56,6 @@ The DONE message indicates that ghostscript has finished processing. # define O_NONBLOCK O_NDELAY #endif -#define PS_DOCUMENT_WATCH_INTERVAL 1000 -#define PS_DOCUMENT_WATCH_TIMEOUT 2 - #define MAX_BUFSIZE 1024 #define PS_DOCUMENT_IS_COMPRESSED(gs) (PS_DOCUMENT(gs)->gs_filename_unc != NULL) @@ -172,7 +63,9 @@ The DONE message indicates that ghostscript has finished processing. PS_DOCUMENT(gs)->gs_filename_unc : \ PS_DOCUMENT(gs)->gs_filename) -enum { INTERPRETER_MESSAGE, INTERPRETER_ERROR, LAST_SIGNAL }; +GCond* pixbuf_cond = NULL; +GMutex* pixbuf_mutex = NULL; +GdkPixbuf *current_pixbuf = NULL; enum { PROP_0, @@ -191,100 +84,67 @@ struct record_list { static gboolean broken_pipe = FALSE; -static void -catchPipe(int i) -{ - broken_pipe = True; -} - /* Forward declarations */ static void ps_document_init(PSDocument * gs); static void ps_document_class_init(PSDocumentClass * klass); -static void ps_document_emit_error_msg(PSDocument * gs, const gchar * msg); static void ps_document_finalize(GObject * object); static void send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close); -static void set_up_page(PSDocument * gs); static void close_pipe(int p[2]); -static void interpreter_failed(PSDocument * gs); -static float compute_xdpi(void); -static float compute_ydpi(void); -static gboolean compute_size(PSDocument * gs); static void output(gpointer data, gint source, GdkInputCondition condition); static void input(gpointer data, gint source, GdkInputCondition condition); static void stop_interpreter(PSDocument * gs); static gint start_interpreter(PSDocument * gs); -gboolean computeSize(void); -static gboolean ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid); static void ps_document_document_iface_init (EvDocumentIface *iface); -static gboolean ps_document_goto_page(PSDocument * gs, gint page); +static gboolean ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data); static GObjectClass *parent_class = NULL; static PSDocumentClass *gs_class = NULL; static void -ps_document_init(PSDocument * gs) +ps_document_init (PSDocument *gs) { - gs->bpixmap = NULL; + gs->bpixmap = NULL; - gs->current_page = 0; - gs->disable_start = FALSE; - gs->interpreter_pid = -1; + gs->current_page = 0; + gs->interpreter_pid = -1; - gs->width = -1; - gs->height = -1; - gs->busy = FALSE; - gs->changed = FALSE; - gs->gs_scanstyle = 0; - gs->gs_filename = 0; - gs->gs_filename_dsc = 0; - gs->gs_filename_unc = 0; + gs->width = -1; + gs->height = -1; + gs->busy = FALSE; + gs->gs_filename = 0; + gs->gs_filename_unc = 0; - broken_pipe = FALSE; + broken_pipe = FALSE; - gs->structured_doc = FALSE; - gs->reading_from_pipe = FALSE; - gs->send_filename_to_gs = FALSE; + gs->structured_doc = FALSE; + gs->reading_from_pipe = FALSE; + gs->send_filename_to_gs = FALSE; - gs->doc = NULL; - gs->loaded = FALSE; + gs->doc = NULL; + gs->loaded = FALSE; + + gs->interpreter_input = -1; + gs->interpreter_output = -1; + gs->interpreter_err = -1; + gs->interpreter_input_id = 0; + gs->interpreter_output_id = 0; + gs->interpreter_error_id = 0; + + gs->ps_input = NULL; + gs->input_buffer = NULL; + gs->input_buffer_ptr = NULL; + gs->bytes_left = 0; + gs->buffer_bytes_left = 0; - gs->interpreter_input = -1; - gs->interpreter_output = -1; - gs->interpreter_err = -1; - gs->interpreter_input_id = 0; - gs->interpreter_output_id = 0; - gs->interpreter_error_id = 0; - - gs->ps_input = NULL; - gs->input_buffer = NULL; - gs->input_buffer_ptr = NULL; - gs->bytes_left = 0; - gs->buffer_bytes_left = 0; - - gs->llx = 0; - gs->lly = 0; - gs->urx = 0; - gs->ury = 0; - gs->xdpi = compute_xdpi(); - gs->ydpi = compute_ydpi(); - - gs->left_margin = 0; - gs->top_margin = 0; - gs->right_margin = 0; - gs->bottom_margin = 0; - - gs->page_x_offset = 0; - gs->page_y_offset = 0; - - /* Set user defined defaults */ - gs->fallback_orientation = GTK_GS_ORIENTATION_PORTRAIT; - gs->zoom_factor = 1.0; - gs->default_size = 1; - gs->antialiased = TRUE; - gs->respect_eof = TRUE; - - gs->gs_status = _("No document loaded."); + gs->page_x_offset = 0; + gs->page_y_offset = 0; + gs->zoom_factor = 1.0; + + gs->gs_status = _("No document loaded."); + + pixbuf_cond = g_cond_new (); + pixbuf_mutex = g_mutex_new (); } static void @@ -325,63 +185,85 @@ ps_document_get_property (GObject *object, static void ps_document_class_init(PSDocumentClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; - object_class = (GObjectClass *) klass; - parent_class = g_type_class_peek_parent (klass); - gs_class = klass; + object_class = (GObjectClass *) klass; + parent_class = g_type_class_peek_parent (klass); + gs_class = klass; - object_class->finalize = ps_document_finalize; - object_class->get_property = ps_document_get_property; - object_class->set_property = ps_document_set_property; + object_class->finalize = ps_document_finalize; + object_class->get_property = ps_document_get_property; + object_class->set_property = ps_document_set_property; - /* Create atoms */ - klass->gs_atom = gdk_atom_intern("GHOSTVIEW", FALSE); - klass->next_atom = gdk_atom_intern("NEXT", FALSE); - klass->page_atom = gdk_atom_intern("PAGE", FALSE); - klass->string_atom = gdk_atom_intern("STRING", FALSE); + klass->gs_atom = gdk_atom_intern ("GHOSTVIEW", FALSE); + klass->next_atom = gdk_atom_intern ("NEXT", FALSE); + klass->page_atom = gdk_atom_intern ("PAGE", FALSE); + klass->string_atom = gdk_atom_intern ("STRING", FALSE); - g_object_class_override_property (object_class, PROP_TITLE, "title"); + g_object_class_override_property (object_class, PROP_TITLE, "title"); } -/* Clean all memory and temporal files */ static void -ps_document_cleanup(PSDocument * gs) +push_pixbuf (PSDocument *gs) { - g_return_if_fail(gs != NULL); - g_return_if_fail(GTK_IS_GS(gs)); + GdkColormap *cmap; + GdkPixbuf *pixbuf; + + cmap = gdk_window_get_colormap (gs->pstarget); + + pixbuf = gdk_pixbuf_get_from_drawable (NULL, gs->bpixmap, cmap, + 0, 0, 0, 0, + gs->width, gs->height); + g_mutex_lock (pixbuf_mutex); + current_pixbuf = pixbuf; + g_cond_signal (pixbuf_cond); + g_mutex_unlock (pixbuf_mutex); - stop_interpreter(gs); +} - if(gs->gs_psfile) { - fclose(gs->gs_psfile); - gs->gs_psfile = NULL; - } - if(gs->gs_filename) { - g_free(gs->gs_filename); - gs->gs_filename = NULL; - } - if(gs->doc) { - psfree(gs->doc); - gs->doc = NULL; - } - if(gs->gs_filename_dsc) { - unlink(gs->gs_filename_dsc); - g_free(gs->gs_filename_dsc); - gs->gs_filename_dsc = NULL; - } - if(gs->gs_filename_unc) { - unlink(gs->gs_filename_unc); - g_free(gs->gs_filename_unc); - gs->gs_filename_unc = NULL; - } - gs->current_page = 0; - gs->loaded = FALSE; - gs->llx = 0; - gs->lly = 0; - gs->urx = 0; - gs->ury = 0; - set_up_page(gs); +static void +interpreter_failed (PSDocument *gs, char *msg) +{ + LOG ("Interpreter failed %s", msg); + + push_pixbuf (gs); + + stop_interpreter (gs); +} + +static void +ps_document_cleanup (PSDocument *gs) +{ + g_return_if_fail (gs != NULL); + g_return_if_fail (PS_IS_DOCUMENT (gs)); + + LOG ("Cleanup\n"); + + stop_interpreter (gs); + + if (gs->gs_psfile) { + fclose (gs->gs_psfile); + gs->gs_psfile = NULL; + } + + if (gs->gs_filename) { + g_free (gs->gs_filename); + gs->gs_filename = NULL; + } + + if (gs->doc) { + psfree (gs->doc); + gs->doc = NULL; + } + + if (gs->gs_filename_unc) { + unlink(gs->gs_filename_unc); + g_free(gs->gs_filename_unc); + gs->gs_filename_unc = NULL; + } + + gs->current_page = 0; + gs->loaded = FALSE; } static gboolean @@ -398,56 +280,19 @@ ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data) LOG ("GS rendered the document"); gs->busy = FALSE; - if (gs->scaling) { - ev_document_scale_changed (EV_DOCUMENT (gs)); - gs->scaling = FALSE; - } else { - ev_document_page_changed (EV_DOCUMENT (gs)); - } + push_pixbuf (gs); } return TRUE; } static void -ps_document_set_target (EvDocument *document, - GdkDrawable *target) -{ - PSDocument *gs = PS_DOCUMENT (document); - GtkWidget *widget; - gpointer data; - - if (gs->pstarget) { - gdk_window_get_user_data (gs->pstarget, &data); - g_return_if_fail (GTK_IS_WIDGET (data)); - - widget = GTK_WIDGET (data); - g_signal_handlers_disconnect_by_func - (widget, ps_document_widget_event, document); - } - - gs->pstarget = target; - - if (gs->pstarget) { - gdk_window_get_user_data (gs->pstarget, &data); - g_return_if_fail (GTK_IS_WIDGET (data)); - - widget = GTK_WIDGET (data); - g_signal_connect (widget, "event", - G_CALLBACK (ps_document_widget_event), - document); - } - - ps_document_goto_page (gs, gs->current_page); -} - -static void ps_document_finalize (GObject * object) { PSDocument *gs; g_return_if_fail (object != NULL); - g_return_if_fail (GTK_IS_GS (object)); + g_return_if_fail (PS_IS_DOCUMENT (object)); LOG ("Finalize"); @@ -456,14 +301,12 @@ ps_document_finalize (GObject * object) ps_document_cleanup (gs); stop_interpreter (gs); - ps_document_set_target (EV_DOCUMENT (object), NULL); - if(gs->input_buffer) { g_free(gs->input_buffer); gs->input_buffer = NULL; } - (*G_OBJECT_CLASS(parent_class)->finalize) (object); + (*G_OBJECT_CLASS (parent_class)->finalize) (object); } static void @@ -505,143 +348,88 @@ send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close) } } -static gint -ps_document_get_orientation(PSDocument * gs) +static float +get_xdpi (PSDocument *gs) { - g_return_val_if_fail(gs != NULL, -1); - g_return_val_if_fail(GTK_IS_GS(gs), -1); - - if(gs->doc) { - if(gs->structured_doc) { - if(gs->doc->pages[MAX(gs->current_page, 0)].orientation != - GTK_GS_ORIENTATION_NONE) - gs->real_orientation = - gs->doc->pages[MAX(gs->current_page, 0)].orientation; - else - gs->real_orientation = gs->doc->default_page_orientation; - } - - if(gs->real_orientation == GTK_GS_ORIENTATION_NONE) - gs->real_orientation = gs->doc->orientation; - } + return 25.4 * gdk_screen_width() / gdk_screen_width_mm(); +} - if(gs->real_orientation == GTK_GS_ORIENTATION_NONE) - return gs->fallback_orientation; - else - return gs->real_orientation; +static float +get_ydpi (PSDocument *gs) +{ + return 25.4 * gdk_screen_height() / gdk_screen_height_mm(); } static void -set_up_page(PSDocument * gs) +setup_pixmap (PSDocument *gs) { - guint orientation; - char buf[1024]; - //GdkColormap *colormap; - GdkGC *fill; - GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF }; /* pixel, r, g, b */ - GdkColormap *colormap; - gboolean size_changed; - -#ifdef HAVE_LOCALE_H - char *savelocale; -#endif - - LOG ("Setup the page"); - - size_changed = compute_size (gs); - - if (gs->pstarget == NULL) - return; - - /* Do we have to check if the actual geometry changed? */ - - stop_interpreter(gs); - - orientation = ps_document_get_orientation(gs); - - if (size_changed || gs->bpixmap == NULL) { - gdk_flush(); - - /* clear new pixmap (set to white) */ - fill = gdk_gc_new(gs->pstarget); - if(fill) { - colormap = gdk_drawable_get_colormap(gs->pstarget); - gdk_color_alloc (colormap, &white); - gdk_gc_set_foreground(fill, &white); - - if(gs->width > 0 && gs->height > 0) { - if(gs->bpixmap) { - gdk_drawable_unref(gs->bpixmap); - gs->bpixmap = NULL; - } + GdkGC *fill; + GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF }; /* pixel, r, g, b */ + GdkColormap *colormap; LOG ("Create our internal pixmap"); - gs->bpixmap = gdk_pixmap_new(gs->pstarget, gs->width, gs->height, -1); - gdk_draw_rectangle(gs->bpixmap, fill, TRUE, - 0, 0, gs->width, gs->height); - } - else { - gdk_draw_rectangle(gs->pstarget, fill, TRUE, - 0, 0, gs->width, gs->height); - } - gdk_gc_unref(fill); + if(gs->bpixmap) { + gdk_drawable_unref(gs->bpixmap); + } - gdk_flush(); - } - } + fill = gdk_gc_new (gs->pstarget); + colormap = gdk_drawable_get_colormap (gs->pstarget); + gdk_color_alloc (colormap, &white); + gdk_gc_set_foreground (fill, &white); + gs->bpixmap = gdk_pixmap_new (gs->pstarget, gs->width, gs->height, -1); + gdk_draw_rectangle (gs->bpixmap, fill, TRUE, + 0, 0, gs->width, gs->height); +} +static void +setup_page (PSDocument *gs) +{ + char buf[1024]; #ifdef HAVE_LOCALE_H - /* gs needs floating point parameters with '.' as decimal point - * while some (european) locales use ',' instead, so we set the - * locale for this snprintf to "C". - */ - savelocale = setlocale(LC_NUMERIC, "C"); + char *savelocale; #endif - g_snprintf(buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d", - 0L, - orientation * 90, - gs->llx, - gs->lly, - gs->urx, - gs->ury, - gs->xdpi * gs->zoom_factor, - gs->ydpi * gs->zoom_factor, - gs->left_margin, - gs->bottom_margin, gs->right_margin, gs->top_margin); + LOG ("Setup the page"); - LOG ("GS property %s", buf); +#ifdef HAVE_LOCALE_H + /* gs needs floating point parameters with '.' as decimal point + * while some (european) locales use ',' instead, so we set the + * locale for this snprintf to "C". + */ + savelocale = setlocale (LC_NUMERIC, "C"); +#endif + + g_snprintf (buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d", + 0L, gs->orientation * 90, gs->llx, gs->lly, gs->urx, gs->ury, + get_xdpi (gs) * gs->zoom_factor, + get_ydpi (gs) * gs->zoom_factor, + 0, 0, 0, 0); + LOG ("GS property %s", buf); #ifdef HAVE_LOCALE_H - setlocale(LC_NUMERIC, savelocale); + setlocale(LC_NUMERIC, savelocale); #endif - gdk_property_change(gs->pstarget, - gs_class->gs_atom, - gs_class->string_atom, - 8, GDK_PROP_MODE_REPLACE, buf, strlen(buf)); - gdk_flush(); + gdk_property_change (gs->pstarget, gs_class->gs_atom, gs_class->string_atom, + 8, GDK_PROP_MODE_REPLACE, (guchar *)buf, strlen(buf)); + gdk_flush (); } static void -close_pipe(int p[2]) +close_pipe (int p[2]) { - if(p[0] != -1) - close(p[0]); - if(p[1] != -1) - close(p[1]); + if (p[0] != -1) { + close (p[0]); + } + if (p[1] != -1) { + close (p[1]); + } } static gboolean -is_interpreter_ready(PSDocument * gs) +is_interpreter_ready (PSDocument *gs) { - return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL); -} - -static void -interpreter_failed(PSDocument * gs) -{ - stop_interpreter(gs); + return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL); } static void @@ -661,11 +449,11 @@ output(gpointer data, gint source, GdkInputCondition condition) } else if(bytes == -1) { /* trouble... */ - interpreter_failed(gs); + interpreter_failed(gs, NULL); return; } if(gs->interpreter_err == -1) { - stop_interpreter(gs); + interpreter_failed(gs, NULL); } } else if(source == gs->interpreter_err) { @@ -678,21 +466,27 @@ output(gpointer data, gint source, GdkInputCondition condition) } else if(bytes == -1) { /* trouble... */ - interpreter_failed(gs); + interpreter_failed(gs, NULL); return; } if(gs->interpreter_output == -1) { - stop_interpreter(gs); + interpreter_failed(gs, NULL); } } if(bytes > 0) { buf[bytes] = '\0'; msg = g_strdup(buf); - ps_document_emit_error_msg (gs, msg); + interpreter_failed (gs, msg); } } static void +catchPipe(int i) +{ + broken_pipe = True; +} + +static void input(gpointer data, gint source, GdkInputCondition condition) { PSDocument *gs = PS_DOCUMENT(data); @@ -700,6 +494,8 @@ input(gpointer data, gint source, GdkInputCondition condition) void (*oldsig) (int); oldsig = signal(SIGPIPE, catchPipe); + LOG ("Input"); + do { if(gs->buffer_bytes_left == 0) { /* Get a new section if required */ @@ -730,26 +526,24 @@ input(gpointer data, gint source, GdkInputCondition condition) gs->buffer_bytes_left = 0; } if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) { - interpreter_failed(gs); /* Error occurred */ + interpreter_failed (gs, NULL); /* Error occurred */ } gs->input_buffer_ptr = gs->input_buffer; gs->bytes_left -= gs->buffer_bytes_left; } if(gs->buffer_bytes_left > 0) { - /* g_print (" writing: %s\n",gs->input_buffer_ptr); */ - bytes_written = write(gs->interpreter_input, gs->input_buffer_ptr, gs->buffer_bytes_left); if(broken_pipe) { - ps_document_emit_error_msg(gs, g_strdup(_("Broken pipe."))); + interpreter_failed (gs, g_strdup(_("Broken pipe."))); broken_pipe = FALSE; - interpreter_failed(gs); + interpreter_failed (gs, NULL); } else if(bytes_written == -1) { if((errno != EWOULDBLOCK) && (errno != EAGAIN)) { - interpreter_failed(gs); /* Something bad happened */ + interpreter_failed (gs, NULL); /* Something bad happened */ } } else { @@ -771,161 +565,149 @@ input(gpointer data, gint source, GdkInputCondition condition) } static int -start_interpreter(PSDocument * gs) +start_interpreter (PSDocument *gs) { - int std_in[2] = { -1, -1 }; /* pipe to interp stdin */ - int std_out[2]; /* pipe from interp stdout */ - int std_err[2]; /* pipe from interp stderr */ + int std_in[2] = { -1, -1 }; /* pipe to interp stdin */ + int std_out[2]; /* pipe from interp stdout */ + int std_err[2]; /* pipe from interp stderr */ #define NUM_ARGS 100 #define NUM_GS_ARGS (NUM_ARGS - 20) #define NUM_ALPHA_ARGS 10 - char *argv[NUM_ARGS], *dir, *gv_env; - char **gs_args, **alpha_args = NULL; - int argc = 0, i; + char *argv[NUM_ARGS], *dir, *gv_env; + char **gs_args, **alpha_args = NULL; + int argc = 0, i; - LOG ("Start the interpreter"); + LOG ("Start the interpreter"); - if(!gs->gs_filename) - return 0; + if(!gs->gs_filename) + return 0; - stop_interpreter(gs); + stop_interpreter(gs); - if(gs->disable_start == TRUE) - return 0; + /* set up the args... */ + gs_args = g_strsplit (gtk_gs_defaults_get_interpreter_cmd (), " ", NUM_GS_ARGS); + for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++) { + argv[argc] = gs_args[i]; + } - /* set up the args... */ - gs_args = g_strsplit(gtk_gs_defaults_get_interpreter_cmd(), " ", NUM_GS_ARGS); - for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++) - argv[argc] = gs_args[i]; - - if(gs->antialiased) { - if(strlen(gtk_gs_defaults_get_alpha_parameters()) == 0) - alpha_args = g_strsplit(ALPHA_PARAMS, " ", NUM_ALPHA_ARGS); - else - alpha_args = g_strsplit(gtk_gs_defaults_get_alpha_parameters(), - " ", NUM_ALPHA_ARGS); - for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++) - argv[argc] = alpha_args[i]; - } - else - argv[argc++] = "-sDEVICE=x11"; - argv[argc++] = "-dNOPAUSE"; - argv[argc++] = "-dQUIET"; - /* I assume we do _not_ want to change this... (: */ - argv[argc++] = "-dSAFER"; - - /* set up the pipes */ - if(gs->send_filename_to_gs) { - argv[argc++] = PS_DOCUMENT_GET_PS_FILE(gs); - argv[argc++] = "-c"; - argv[argc++] = "quit"; - } - else - argv[argc++] = "-"; + alpha_args = g_strsplit (ALPHA_PARAMS, " ", NUM_ALPHA_ARGS); - argv[argc++] = NULL; + argv[argc++] = "-dNOPAUSE"; + argv[argc++] = "-dQUIET"; + argv[argc++] = "-dSAFER"; - if(!gs->reading_from_pipe && !gs->send_filename_to_gs) { - if(pipe(std_in) == -1) { - g_critical("Unable to open pipe to Ghostscript."); - return -1; - } - } - if(pipe(std_out) == -1) { - close_pipe(std_in); - return -1; - } - if(pipe(std_err) == -1) { - close_pipe(std_in); - close_pipe(std_out); - return -1; - } + /* set up the pipes */ + if (gs->send_filename_to_gs) { + argv[argc++] = PS_DOCUMENT_GET_PS_FILE (gs); + argv[argc++] = "-c"; + argv[argc++] = "quit"; + } else { + argv[argc++] = "-"; + } - gv_env = g_strdup_printf("GHOSTVIEW=%ld %ld", - gdk_x11_drawable_get_xid(gs->pstarget), - gdk_x11_drawable_get_xid(gs->bpixmap)); - - LOG ("Launching ghostview with env %s", gv_env); - - gs->busy = TRUE; - gs->interpreter_pid = fork(); - switch (gs->interpreter_pid) { - case -1: /* error */ - close_pipe(std_in); - close_pipe(std_out); - close_pipe(std_err); - return -2; - break; - case 0: /* child */ - close(std_out[0]); - dup2(std_out[1], 1); - close(std_out[1]); - - close(std_err[0]); - dup2(std_err[1], 2); - close(std_err[1]); - - if(!gs->reading_from_pipe) { - if(gs->send_filename_to_gs) { - int stdinfd; - /* just in case gs tries to read from stdin */ - stdinfd = open("/dev/null", O_RDONLY); - if(stdinfd != 0) { - dup2(stdinfd, 0); - close(stdinfd); - } - } - else { - close(std_in[1]); - dup2(std_in[0], 0); - close(std_in[0]); - } - } + argv[argc++] = NULL; - putenv(gv_env); - - /* change to directory where the input file is. This helps - * with postscript-files which include other files using - * a relative path */ - dir = g_path_get_dirname(gs->gs_filename); - chdir(dir); - g_free(dir); - - execvp(argv[0], argv); - - /* Notify error */ - g_print("Unable to execute [%s]\n", argv[0]); - g_strfreev(gs_args); - g_free(gv_env); - if(alpha_args) - g_strfreev(alpha_args); - _exit(1); - break; - default: /* parent */ - if(!gs->send_filename_to_gs && !gs->reading_from_pipe) { - int result; - close(std_in[0]); - /* use non-blocking IO for pipe to ghostscript */ - result = fcntl(std_in[1], F_GETFL, 0); - fcntl(std_in[1], F_SETFL, result | O_NONBLOCK); - gs->interpreter_input = std_in[1]; - } - else { - gs->interpreter_input = -1; - } - close(std_out[1]); - gs->interpreter_output = std_out[0]; - close(std_err[1]); - gs->interpreter_err = std_err[0]; - gs->interpreter_output_id = - gdk_input_add(std_out[0], GDK_INPUT_READ, output, gs); - gs->interpreter_error_id = - gdk_input_add(std_err[0], GDK_INPUT_READ, output, gs); - break; - } - return TRUE; + if (!gs->reading_from_pipe && !gs->send_filename_to_gs) { + if (pipe (std_in) == -1) { + g_critical ("Unable to open pipe to Ghostscript."); + return -1; + } + } + + if (pipe (std_out) == -1) { + close_pipe (std_in); + return -1; + } + + if (pipe(std_err) == -1) { + close_pipe (std_in); + close_pipe (std_out); + return -1; + } + + gv_env = g_strdup_printf ("GHOSTVIEW=%ld %ld", + gdk_x11_drawable_get_xid (gs->pstarget), + gdk_x11_drawable_get_xid (gs->bpixmap)); + LOG ("Launching ghostview with env %s", gv_env); + + gs->busy = TRUE; + gs->interpreter_pid = fork (); + switch (gs->interpreter_pid) { + case -1: /* error */ + close_pipe (std_in); + close_pipe (std_out); + close_pipe (std_err); + return -2; + break; + case 0: /* child */ + close (std_out[0]); + dup2 (std_out[1], 1); + close (std_out[1]); + + close (std_err[0]); + dup2 (std_err[1], 2); + close (std_err[1]); + + if (!gs->reading_from_pipe) { + if (gs->send_filename_to_gs) { + int stdinfd; + /* just in case gs tries to read from stdin */ + stdinfd = open("/dev/null", O_RDONLY); + if (stdinfd != 0) { + dup2(stdinfd, 0); + close(stdinfd); + } + } else { + close (std_in[1]); + dup2 (std_in[0], 0); + close (std_in[0]); + } + } + + putenv(gv_env); + + /* change to directory where the input file is. This helps + * with postscript-files which include other files using + * a relative path */ + dir = g_path_get_dirname (gs->gs_filename); + chdir (dir); + g_free (dir); + + execvp (argv[0], argv); + + /* Notify error */ + g_critical ("Unable to execute [%s]\n", argv[0]); + g_strfreev (gs_args); + g_free (gv_env); + g_strfreev (alpha_args); + _exit (1); + break; + default: /* parent */ + if (!gs->send_filename_to_gs && !gs->reading_from_pipe) { + int result; + close (std_in[0]); + /* use non-blocking IO for pipe to ghostscript */ + result = fcntl (std_in[1], F_GETFL, 0); + fcntl (std_in[1], F_SETFL, result | O_NONBLOCK); + gs->interpreter_input = std_in[1]; + } else { + gs->interpreter_input = -1; + } + close (std_out[1]); + + gs->interpreter_output = std_out[0]; + close (std_err[1]); + gs->interpreter_err = std_err[0]; + gs->interpreter_output_id = + gdk_input_add (std_out[0], GDK_INPUT_READ, output, gs); + gs->interpreter_error_id = + gdk_input_add (std_err[0], GDK_INPUT_READ, output, gs); + break; + } + + return TRUE; } static void @@ -1063,16 +845,7 @@ check_filecompressed(PSDocument * gs) /* report error */ g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"), gs->gs_filename); - ps_document_emit_error_msg(gs, buf); - if(file_length(filename_err) > 0) { - FILE *err; - if((err = fopen(filename_err, "r"))) { - /* print file to message window */ - while(fgets(buf, 1024, err)) - ps_document_emit_error_msg(gs, buf); - fclose(err); - } - } + interpreter_failed (gs, buf); unlink(filename_unc); g_free(filename_unc); filename_unc = NULL; @@ -1084,112 +857,100 @@ check_filecompressed(PSDocument * gs) return filename_unc; } -#ifdef BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED -/* never mind this patch: a properly working X server should take care of - calculating the proper values. */ -static float -compute_xdpi(void) -{ -# ifndef HAVE_XINERAMA - return 25.4 * gdk_screen_width() / gdk_screen_width_mm(); -# else - Display *dpy; - dpy = (Display *) GDK_DISPLAY(); - if(XineramaIsActive(dpy)) { - int num_heads; - XineramaScreenInfo *head_info; - head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads); - /* fake it with dimensions of the first head for now */ - return 25.4 * head_info[0].width / gdk_screen_width_mm(); - } - else { - return 25.4 * gdk_screen_width() / gdk_screen_width_mm(); - } -# endif - /* HAVE_XINERAMA */ -} - -static float -compute_ydpi(void) -{ -# ifndef HAVE_XINERAMA - return 25.4 * gdk_screen_height() / gdk_screen_height_mm(); -# else - Display *dpy; - dpy = (Display *) GDK_DISPLAY(); - if(XineramaIsActive(dpy)) { - int num_heads; - XineramaScreenInfo *head_info; - head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads); - /* fake it with dimensions of the first head for now */ - return 25.4 * head_info[0].height / gdk_screen_height_mm(); - } - else { - return 25.4 * gdk_screen_height() / gdk_screen_height_mm(); - } -# endif - /* HAVE_XINERAMA */ -} -#else -static float -compute_xdpi(void) -{ - return 25.4 * gdk_screen_width() / gdk_screen_width_mm(); -} - -static float -compute_ydpi(void) +static void +compute_dimensions (PSDocument *gs, int page) { - return 25.4 * gdk_screen_height() / gdk_screen_height_mm(); -} -#endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */ + GtkGSPaperSize *paper_sizes = gtk_gs_defaults_get_paper_sizes (); + int urx, ury, llx, lly; + int width, height; + int orientation; + + g_return_if_fail (PS_IS_DOCUMENT (gs)); + g_return_if_fail (gs->doc != NULL); + g_return_if_fail (page >= 0); + g_return_if_fail (gs->doc->numpages > page); + + orientation = GTK_GS_ORIENTATION_NONE; + if (gs->structured_doc) { + orientation = gs->doc->pages[gs->current_page].orientation; + } + if (orientation == GTK_GS_ORIENTATION_NONE) { + orientation = GTK_GS_ORIENTATION_PORTRAIT; + } -/* Compute new size of window, sets xdpi and ydpi if necessary. - * returns True if new window size is different */ -static gboolean -compute_size(PSDocument * gs) -{ - guint new_width = 1; - guint new_height = 1; - gboolean change = FALSE; - gint orientation; - - /* width and height can be changed, calculate window size according */ - /* to xpdi and ydpi */ - orientation = ps_document_get_orientation(gs); - - switch (orientation) { - case GTK_GS_ORIENTATION_PORTRAIT: - case GTK_GS_ORIENTATION_UPSIDEDOWN: - new_width = (gs->urx - gs->llx) / 72.0 * gs->xdpi + 0.5; - new_height = (gs->ury - gs->lly) / 72.0 * gs->ydpi + 0.5; - break; - case GTK_GS_ORIENTATION_LANDSCAPE: - case GTK_GS_ORIENTATION_SEASCAPE: - new_width = (gs->ury - gs->lly) / 72.0 * gs->xdpi + 0.5; - new_height = (gs->urx - gs->llx) / 72.0 * gs->ydpi + 0.5; - break; - } + if (gs->doc->pages && gs->doc->pages[page].size) { + int page_size; + + page_size = gs->doc->pages[page].size - gs->doc->size; + llx = lly = 0; + urx = gs->doc->size[page_size].width; + ury = gs->doc->size[page_size].height; + } else if (gs->doc->pages && + (gs->doc->pages[page].boundingbox[URX] > + gs->doc->pages[page].boundingbox[LLX]) && + (gs->doc->pages[page].boundingbox[URY] > + gs->doc->pages[page].boundingbox[LLY])) { + llx = gs->doc->pages[page].boundingbox[LLX]; + lly = gs->doc->pages[page].boundingbox[LLY]; + urx = gs->doc->pages[page].boundingbox[URX]; + ury = gs->doc->pages[page].boundingbox[URY]; + } else if ((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) && + (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) { + llx = gs->doc->boundingbox[LLX]; + lly = gs->doc->boundingbox[LLY]; + urx = gs->doc->boundingbox[URX]; + ury = gs->doc->boundingbox[URY]; + } else { + /* Fallback to A4 */ + llx = lly = 0; + urx = paper_sizes[12].width; + ury = paper_sizes[12].height; + } - change = (new_width != gs->width * gs->zoom_factor) - || (new_height != gs->height * gs->zoom_factor); - gs->width = (gint) (new_width * gs->zoom_factor); - gs->height = (gint) (new_height * gs->zoom_factor); + switch (orientation) { + case GTK_GS_ORIENTATION_PORTRAIT: + case GTK_GS_ORIENTATION_UPSIDEDOWN: + width = (urx - llx) / 72.0 * get_xdpi (gs) + 0.5; + height = (ury - lly) / 72.0 * get_ydpi (gs) + 0.5; + break; + case GTK_GS_ORIENTATION_LANDSCAPE: + case GTK_GS_ORIENTATION_SEASCAPE: + width = (ury - lly) / 72.0 * get_xdpi (gs) + 0.5; + height = (urx - llx) / 72.0 * get_ydpi (gs) + 0.5; + break; + default: + width = height = 0; + g_assert_not_reached (); + break; + } - return (change); + width = width * gs->zoom_factor; + height = height * gs->zoom_factor; + + if (llx != gs->llx || lly != gs->lly || + urx != gs->urx || ury != gs->ury || + gs->width != width || gs->height != height || + orientation != gs->orientation) { + gs->llx = llx; + gs->lly = lly; + gs->urx = urx; + gs->ury = ury; + gs->width = width; + gs->height = height; + gs->orientation = orientation; + gs->changed = TRUE; + } } static gint ps_document_enable_interpreter(PSDocument * gs) { g_return_val_if_fail(gs != NULL, FALSE); - g_return_val_if_fail(GTK_IS_GS(gs), FALSE); + g_return_val_if_fail(PS_IS_DOCUMENT(gs), FALSE); if(!gs->gs_filename) return 0; - gs->disable_start = FALSE; - return start_interpreter(gs); } @@ -1231,24 +992,11 @@ ps_document_get_type(void) } -/* - * Show error message -> send signal "interpreter_message" - */ -static void -ps_document_emit_error_msg(PSDocument * gs, const gchar * msg) -{ - gdk_pointer_ungrab(GDK_CURRENT_TIME); - if(strstr(msg, "Error:")) { - gs->gs_status = _("File is not a valid PostScript document."); - ps_document_cleanup(gs); - } -} - static gboolean document_load(PSDocument * gs, const gchar * fname) { g_return_val_if_fail(gs != NULL, FALSE); - g_return_val_if_fail(GTK_IS_GS(gs), FALSE); + g_return_val_if_fail(PS_IS_DOCUMENT(gs), FALSE); LOG ("Load the document"); @@ -1261,8 +1009,6 @@ document_load(PSDocument * gs, const gchar * fname) } /* prepare this document */ - - /* default values: no dsc information available */ gs->structured_doc = FALSE; gs->send_filename_to_gs = TRUE; gs->current_page = 0; @@ -1291,7 +1037,7 @@ document_load(PSDocument * gs, const gchar * fname) if(!file_readable(fname)) { gchar buf[1024]; g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname); - ps_document_emit_error_msg(gs, buf); + interpreter_failed (gs, buf); gs->gs_status = _("File is not readable."); } else { @@ -1299,12 +1045,13 @@ document_load(PSDocument * gs, const gchar * fname) } if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) { + interpreter_failed (gs, NULL); ps_document_cleanup(gs); return FALSE; } /* we grab the vital statistics!!! */ - gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename); + gs->doc = psscan(gs->gs_psfile, TRUE, filename); g_object_notify (G_OBJECT (gs), "title"); @@ -1312,7 +1059,7 @@ document_load(PSDocument * gs, const gchar * fname) /* File does not seem to be a Postscript one */ gchar buf[1024]; g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname); - ps_document_emit_error_msg(gs, buf); + interpreter_failed (gs, buf); ps_document_cleanup(gs); gs->gs_status = _("The file is not a PostScript document."); return FALSE; @@ -1323,12 +1070,9 @@ document_load(PSDocument * gs, const gchar * fname) gs->structured_doc = TRUE; gs->send_filename_to_gs = FALSE; } - - /* We have to set up the orientation of the document */ - gs->real_orientation = gs->doc->orientation; } - ps_document_set_page_size(gs, -1, gs->current_page); gs->loaded = TRUE; + compute_dimensions (gs, gs->current_page); gs->gs_status = _("Document loaded."); @@ -1337,246 +1081,75 @@ document_load(PSDocument * gs, const gchar * fname) static gboolean -ps_document_next_page(PSDocument * gs) +ps_document_next_page (PSDocument *gs) { - XEvent event; - - LOG ("Make ghostscript render next page"); - - g_return_val_if_fail(gs != NULL, FALSE); - g_return_val_if_fail(GTK_IS_GS(gs), FALSE); - - if(gs->interpreter_pid == 0) { /* no interpreter active */ - return FALSE; - } - - if(gs->busy) { /* interpreter is busy */ - return FALSE; - } - - gs->busy = TRUE; - - event.xclient.type = ClientMessage; - event.xclient.display = gdk_display; - event.xclient.window = gs->message_window; - event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom); - event.xclient.format = 32; - - gdk_error_trap_push(); - XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event); - gdk_flush(); - gdk_error_trap_pop(); - - return TRUE; -} - -static gboolean -ps_document_goto_page(PSDocument * gs, gint page) -{ - g_return_val_if_fail(gs != NULL, FALSE); - g_return_val_if_fail(GTK_IS_GS(gs), FALSE); - - LOG ("Go to page %d", page); - - if(!gs->gs_filename) { - return FALSE; - } - - /* range checking... */ - if(page < 0) - page = 0; - - if(gs->structured_doc && gs->doc) { - - LOG ("It's a structured document, let's send one page to gs"); - - if(page >= gs->doc->numpages) - page = gs->doc->numpages - 1; - - if(page == gs->current_page && !gs->changed) - return TRUE; - - gs->current_page = page; - - if(gs->doc->pages[page].orientation != NONE && - gs->doc->pages[page].orientation != gs->real_orientation) { - gs->real_orientation = gs->doc->pages[page].orientation; - gs->changed = TRUE; - } - - ps_document_set_page_size(gs, -1, page); + XEvent event; - gs->changed = FALSE; + LOG ("Make ghostscript render next page"); - if(is_interpreter_ready(gs)) { - ps_document_next_page(gs); - } - else { - ps_document_enable_interpreter(gs); - send_ps(gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE); - send_ps(gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE); - } + g_return_val_if_fail (PS_IS_DOCUMENT(gs), FALSE); + g_return_val_if_fail (gs->interpreter_pid != 0, FALSE); + g_return_val_if_fail (gs->busy != TRUE, FALSE); - send_ps(gs, gs->doc->pages[gs->current_page].begin, - gs->doc->pages[gs->current_page].len, FALSE); - } - else { - /* Unstructured document */ - /* In the case of non structured documents, - GS read the PS from the actual file (via command - line. Hence, ggv only send a signal next page. - If ghostview is not running it is usually because - the last page of the file was displayed. In that - case, ggv restarts GS again and the first page is displayed. - */ + gs->busy = TRUE; - LOG ("It's an unstructured document, gs will just read the file"); + event.xclient.type = ClientMessage; + event.xclient.display = gdk_display; + event.xclient.window = gs->message_window; + event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom); + event.xclient.format = 32; - if(page == gs->current_page && !gs->changed) - return TRUE; + gdk_error_trap_push (); + XSendEvent (gdk_display, gs->message_window, FALSE, 0, &event); + gdk_flush (); + gdk_error_trap_pop (); - ps_document_set_page_size(gs, -1, page); - - if(!is_interpreter_ready(gs)) - ps_document_enable_interpreter(gs); - - gs->current_page = page; - - ps_document_next_page(gs); - } - return TRUE; + return TRUE; } -/* - * set pagesize sets the size from - * if new_pagesize is -1, then it is set to either - * a) the default settings of pageid, if they exist, or if pageid != -1. - * b) the default setting of the document, if it exists. - * c) the default setting of the widget. - * otherwise, the new_pagesize is used as the pagesize - */ static gboolean -ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid) +render_page (PSDocument *gs) { - gint new_llx = 0; - gint new_lly = 0; - gint new_urx = 0; - gint new_ury = 0; - GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes(); - - LOG ("Set the page size"); - - g_return_val_if_fail(gs != NULL, FALSE); - g_return_val_if_fail(GTK_IS_GS(gs), FALSE); - - if(new_pagesize == -1) { - if(gs->default_size > 0) - new_pagesize = gs->default_size; - if(gs->doc) { - /* If we have a document: - We use -- the page size (if specified) - or the doc. size (if specified) - or the page bbox (if specified) - or the bounding box - */ - if((pageid >= 0) && (gs->doc->numpages > pageid) && - (gs->doc->pages) && (gs->doc->pages[pageid].size)) { - new_pagesize = gs->doc->pages[pageid].size - gs->doc->size; - } - else if(gs->doc->default_page_size != NULL) { - new_pagesize = gs->doc->default_page_size - gs->doc->size; - } - else if((pageid >= 0) && - (gs->doc->numpages > pageid) && - (gs->doc->pages) && - (gs->doc->pages[pageid].boundingbox[URX] > - gs->doc->pages[pageid].boundingbox[LLX]) && - (gs->doc->pages[pageid].boundingbox[URY] > - gs->doc->pages[pageid].boundingbox[LLY])) { - new_pagesize = -1; - } - else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) && - (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) { - new_pagesize = -1; - } - } - } + g_return_val_if_fail(gs != NULL, FALSE); + g_return_val_if_fail(PS_IS_DOCUMENT(gs), FALSE); - /* Compute bounding box */ - if(gs->doc && (gs->doc->epsf || new_pagesize == -1)) { /* epsf or bbox */ - if((pageid >= 0) && - (gs->doc->pages) && - (gs->doc->pages[pageid].boundingbox[URX] > - gs->doc->pages[pageid].boundingbox[LLX]) - && (gs->doc->pages[pageid].boundingbox[URY] > - gs->doc->pages[pageid].boundingbox[LLY])) { - /* use page bbox */ - new_llx = gs->doc->pages[pageid].boundingbox[LLX]; - new_lly = gs->doc->pages[pageid].boundingbox[LLY]; - new_urx = gs->doc->pages[pageid].boundingbox[URX]; - new_ury = gs->doc->pages[pageid].boundingbox[URY]; - } - else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) && - (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) { - /* use doc bbox */ - new_llx = gs->doc->boundingbox[LLX]; - new_lly = gs->doc->boundingbox[LLY]; - new_urx = gs->doc->boundingbox[URX]; - new_ury = gs->doc->boundingbox[URY]; - } - } - else { - if(new_pagesize < 0) - new_pagesize = gs->default_size; - new_llx = new_lly = 0; - if(gs->doc && gs->doc->size && - (new_pagesize < gs->doc->numsizes)) { - new_urx = gs->doc->size[new_pagesize].width; - new_ury = gs->doc->size[new_pagesize].height; - } - else { - new_urx = papersizes[new_pagesize].width; - new_ury = papersizes[new_pagesize].height; - } - } - - if(new_urx <= new_llx) - new_urx = papersizes[12].width; - if(new_ury <= new_lly) - new_ury = papersizes[12].height; - - /* If bounding box changed, setup for new size. */ - /* ps_document_disable_interpreter (gs); */ - if((new_llx != gs->llx) || (new_lly != gs->lly) || - (new_urx != gs->urx) || (new_ury != gs->ury)) { - gs->llx = new_llx; - gs->lly = new_lly; - gs->urx = new_urx; - gs->ury = new_ury; - gs->changed = TRUE; - } - - if(gs->changed) { - set_up_page(gs); - return TRUE; - } + if(!gs->gs_filename) { + return FALSE; + } - return FALSE; -} + if (gs->structured_doc && gs->doc) { + LOG ("It's a structured document, let's send one page to gs"); -static void -ps_document_set_zoom (PSDocument * gs, gfloat zoom) -{ - g_return_if_fail (gs != NULL); - - gs->zoom_factor = zoom; + if (is_interpreter_ready (gs)) { + ps_document_next_page (gs); + } else { + ps_document_enable_interpreter (gs); + send_ps (gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE); + send_ps (gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE); + } - if (gs->pstarget != NULL) { - set_up_page(gs); - gs->changed = TRUE; - gs->scaling = TRUE; - ps_document_goto_page (gs, gs->current_page); + send_ps (gs, gs->doc->pages[gs->current_page].begin, + gs->doc->pages[gs->current_page].len, FALSE); + } else { + /* Unstructured document + * + * In the case of non structured documents, + * GS read the PS from the actual file (via command + * line. Hence, ggv only send a signal next page. + * If ghostview is not running it is usually because + * the last page of the file was displayed. In that + * case, ggv restarts GS again and the first page is displayed. + */ + + LOG ("It's an unstructured document, gs will just read the file"); + + if (!is_interpreter_ready (gs)) { + ps_document_enable_interpreter(gs); + } + ps_document_next_page(gs); } + + return TRUE; } static gboolean @@ -1631,7 +1204,12 @@ static void ps_document_set_page (EvDocument *document, int page) { - ps_document_goto_page (PS_DOCUMENT (document), page - 1); + PSDocument *gs = PS_DOCUMENT (document); + + LOG ("Set document page %d\n", page); + + gs->current_page = page - 1; + compute_dimensions (gs, page); } static int @@ -1648,7 +1226,10 @@ static void ps_document_set_scale (EvDocument *document, double scale) { - ps_document_set_zoom (PS_DOCUMENT (document), scale); + PSDocument *gs = PS_DOCUMENT (document); + + gs->zoom_factor = scale; + compute_dimensions (gs, gs->current_page); } static void @@ -1681,48 +1262,6 @@ ps_document_get_page_size (EvDocument *document, } } -static void -ps_document_render (EvDocument *document, - int clip_x, - int clip_y, - int clip_width, - int clip_height) -{ - PSDocument *gs = PS_DOCUMENT (document); - GdkRectangle page; - GdkRectangle draw; - GdkGC *gc; - - if (gs->pstarget == NULL || - gs->bpixmap == NULL) { - return; - } - - page.x = gs->page_x_offset; - page.y = gs->page_y_offset; - page.width = gs->width; - page.height = gs->height; - - draw.x = clip_x; - draw.y = clip_y; - draw.width = clip_width; - draw.height = clip_height; - - gc = gdk_gc_new (gs->pstarget); - - gdk_draw_drawable (gs->pstarget, gc, - gs->bpixmap, - draw.x - page.x, draw.y - page.y, - draw.x, draw.y, - draw.width, draw.height); - - LOG ("Copy the internal pixmap: %d %d %d %d %d %d", - draw.x - page.x, draw.y - page.y, - draw.x, draw.y, draw.width, draw.height); - - g_object_unref (gc); -} - static char * ps_document_get_text (EvDocument *document, GdkRectangle *rect) { @@ -1738,6 +1277,56 @@ ps_document_get_link (EvDocument *document, return NULL; } +static gboolean +render_pixbuf_idle (EvDocument *document) +{ + PSDocument *gs = PS_DOCUMENT (document); + + if (gs->pstarget == NULL) { + GtkWidget *widget; + + widget = gtk_window_new (GTK_WINDOW_POPUP); + gtk_widget_realize (widget); + gs->pstarget = widget->window; + + g_assert (gs->pstarget != NULL); + + g_signal_connect (widget, "event", + G_CALLBACK (ps_document_widget_event), + gs); + } + + if (gs->changed) { + stop_interpreter (gs); + setup_pixmap (gs); + setup_page (gs); + gs->changed = FALSE; + } + + render_page (PS_DOCUMENT (document)); + + return FALSE; +} + +static GdkPixbuf * +ps_document_render_pixbuf (EvDocument *document) +{ + GdkPixbuf *pixbuf; + + g_idle_add ((GSourceFunc)render_pixbuf_idle, document); + + g_mutex_lock (pixbuf_mutex); + while (!current_pixbuf) + g_cond_wait (pixbuf_cond, pixbuf_mutex); + pixbuf = current_pixbuf; + current_pixbuf = NULL; + g_mutex_unlock (pixbuf_mutex); + + LOG ("Pixbuf rendered %p\n", pixbuf); + + return pixbuf; +} + static void ps_document_document_iface_init (EvDocumentIface *iface) { @@ -1749,8 +1338,7 @@ ps_document_document_iface_init (EvDocumentIface *iface) iface->set_page = ps_document_set_page; iface->get_page = ps_document_get_page; iface->set_scale = ps_document_set_scale; - iface->set_target = ps_document_set_target; iface->set_page_offset = ps_document_set_page_offset; iface->get_page_size = ps_document_get_page_size; - iface->render = ps_document_render; + iface->render_pixbuf = ps_document_render_pixbuf; } diff --git a/ps/ps-document.h b/ps/ps-document.h index f7c8e92..1c6761b 100644 --- a/ps/ps-document.h +++ b/ps/ps-document.h @@ -35,23 +35,18 @@ G_BEGIN_DECLS #define PS_TYPE_DOCUMENT (ps_document_get_type()) #define PS_DOCUMENT(obj) GTK_CHECK_CAST (obj, ps_document_get_type (), PSDocument) #define PS_DOCUMENT_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, ps_document_get_type (), PSDocumentClass) -#define GTK_IS_GS(obj) GTK_CHECK_TYPE (obj, ps_document_get_type()) +#define PS_IS_DOCUMENT(obj) GTK_CHECK_TYPE (obj, ps_document_get_type()) typedef struct _PSDocument PSDocument; typedef struct _PSDocumentClass PSDocumentClass; struct _PSDocument { GObject object; - GdkWindow *pstarget; /* the window passed to gv - * it is a child of widget... - */ - GdkGC *psgc; - - GdkPixmap *bpixmap; /* Backing pixmap */ + GdkWindow *pstarget; + GdkPixmap *bpixmap; long message_window; /* Used by ghostview to receive messages from app */ - int disable_start; /* Can the interpreter be started? */ pid_t interpreter_pid; /* PID of interpreter, -1 if none */ int interpreter_input; /* stdin of interpreter */ int interpreter_output; /* stdout of interpreter */ @@ -60,22 +55,19 @@ struct _PSDocument { guint interpreter_output_id; guint interpreter_error_id; + gint width; /* Size of window at last setup() */ gint llx; gint lly; gint urx; gint ury; - gint left_margin; - gint right_margin; - gint top_margin; - gint bottom_margin; - gint width; /* Size of window at last setup() */ gint height; + gint orientation; gboolean busy; /* Is gs busy drawing? */ - gboolean changed; /* Anything changed since setup */ gfloat zoom_factor; gint current_page; gboolean structured_doc; gboolean loaded; + gboolean changed; struct record_list *ps_input; gchar *input_buffer_ptr; @@ -84,30 +76,16 @@ struct _PSDocument { FILE *gs_psfile; /* the currently loaded FILE */ gchar *gs_filename; /* the currently loaded filename */ - gchar *gs_filename_dsc; /* Used to browse PDF to PS */ gchar *gs_filename_unc; /* Uncompressed file */ gchar *input_buffer; - gint gs_scanstyle; gboolean send_filename_to_gs; /* True if gs should read from file directly */ gboolean reading_from_pipe; /* True if ggv is reading input from pipe */ struct document *doc; - /* User selected options... */ - gboolean antialiased; /* Using antialiased display */ - gboolean respect_eof; /* respect EOF comments? */ - gint default_size; - gfloat xdpi, ydpi; - gint fallback_orientation; /* Orientation to use if override */ - gint real_orientation; /* Real orientation from the document */ - const gchar *gs_status; /* PSDocument status */ - guint avail_w, avail_h; - int page_x_offset; int page_y_offset; - - gboolean scaling; }; struct _PSDocumentClass { diff --git a/shell/Makefile.am b/shell/Makefile.am index 0ae0958..a14f2a0 100644 --- a/shell/Makefile.am +++ b/shell/Makefile.am @@ -29,6 +29,8 @@ evince_SOURCES= \ ev-password.c \ ev-password-view.h \ ev-password-view.c \ + ev-pixbuf-cache.c \ + ev-pixbuf-cache.h \ ev-print-job.c \ ev-print-job.h \ ev-utils.c \ diff --git a/shell/ev-pixbuf-cache.c b/shell/ev-pixbuf-cache.c new file mode 100644 index 0000000..7d076db --- /dev/null +++ b/shell/ev-pixbuf-cache.c @@ -0,0 +1,525 @@ +#include "ev-pixbuf-cache.h" +#include "ev-job-queue.h" + + +typedef struct _CacheJobInfo +{ + EvJob *job; + GdkPixbuf *pixbuf; +} CacheJobInfo; + +struct _EvPixbufCache +{ + GObject parent; + + EvDocument *document; + int start_page; + int end_page; + + /* preload_cache_size is the number of pages prior to the current + * visible area that we cache. It's normally 1, but could be 2 in the + * case of twin pages. + */ + int preload_cache_size; + CacheJobInfo *prev_job; + CacheJobInfo *job_list; + CacheJobInfo *next_job; +}; + +struct _EvPixbufCacheClass +{ + GObjectClass parent_class; + + void (* job_finished) (EvPixbufCache *pixbuf_cache); +}; + + +enum +{ + JOB_FINISHED, + N_SIGNALS, +}; + +static guint signals[N_SIGNALS] = {0, }; + +static void ev_pixbuf_cache_init (EvPixbufCache *pixbuf_cache); +static void ev_pixbuf_cache_class_init (EvPixbufCacheClass *pixbuf_cache); +static void ev_pixbuf_cache_finalize (GObject *object); +static void ev_pixbuf_cache_dispose (GObject *object); +static void job_finished_cb (EvJob *job, + EvPixbufCache *pixbuf_cache); +static CacheJobInfo *find_job_cache (EvPixbufCache *pixbuf_cache, + int page); + + + +/* These are used for iterating through the prev and next arrays */ +#define FIRST_VISABLE_PREV(pixbuf_cache) \ + (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page)) +#define VISIBLE_NEXT_LEN(pixbuf_cache, page_cache) \ + (MIN(pixbuf_cache->preload_cache_size, ev_page_cache_get_n_pages (page_cache) - pixbuf_cache->end_page)) +#define PAGE_CACHE_LEN(pixbuf_cache) \ + ((pixbuf_cache->end_page - pixbuf_cache->start_page) + 1) + +G_DEFINE_TYPE (EvPixbufCache, ev_pixbuf_cache, G_TYPE_OBJECT) + +static void +ev_pixbuf_cache_init (EvPixbufCache *pixbuf_cache) +{ + pixbuf_cache->start_page = 1; + pixbuf_cache->end_page = 1; + pixbuf_cache->job_list = g_new0 (CacheJobInfo, PAGE_CACHE_LEN (pixbuf_cache)); + + pixbuf_cache->preload_cache_size = 1; + pixbuf_cache->prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size); + pixbuf_cache->next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size); +} + +static void +ev_pixbuf_cache_class_init (EvPixbufCacheClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + + object_class->finalize = ev_pixbuf_cache_finalize; + object_class->dispose = ev_pixbuf_cache_dispose; + + signals[JOB_FINISHED] = g_signal_new ("job-finished", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EvPixbufCacheClass, job_finished), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +ev_pixbuf_cache_finalize (GObject *object) +{ + EvPixbufCache *pixbuf_cache; + + pixbuf_cache = EV_PIXBUF_CACHE (object); + + g_free (pixbuf_cache->prev_job); + g_free (pixbuf_cache->job_list); + g_free (pixbuf_cache->next_job); +} + +static void +dispose_cache_job_info (CacheJobInfo *job_info, + gpointer data) +{ + if (job_info == NULL) + return; + if (job_info->job) { + g_signal_handlers_disconnect_by_func (job_info->job, + G_CALLBACK (job_finished_cb), + data); + g_object_unref (G_OBJECT (job_info->job)); + job_info->job = NULL; + } + if (job_info->pixbuf) { + g_object_unref (G_OBJECT (job_info->pixbuf)); + job_info->pixbuf = NULL; + } +} + +static void +ev_pixbuf_cache_dispose (GObject *object) +{ + EvPixbufCache *pixbuf_cache; + int i; + + pixbuf_cache = EV_PIXBUF_CACHE (object); + + for (i = 0; i < pixbuf_cache->preload_cache_size; i++) { + dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache); + dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache); + } + + for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) { + dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache); + } +} + + +EvPixbufCache * +ev_pixbuf_cache_new (EvDocument *document) +{ + EvPixbufCache *pixbuf_cache; + + pixbuf_cache = (EvPixbufCache *) g_object_new (EV_TYPE_PIXBUF_CACHE, NULL); + pixbuf_cache->document = document; + + return pixbuf_cache; +} + +static void +job_finished_cb (EvJob *job, + EvPixbufCache *pixbuf_cache) +{ + CacheJobInfo *job_info; + EvJobRender *job_render = EV_JOB_RENDER (job); + GdkPixbuf *pixbuf; + + /* If the job is outside of our interest, we silently discard it */ + if ((job_render->page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size)) || + (job_render->page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))) { + g_object_unref (job); + return; + } + + job_info = find_job_cache (pixbuf_cache, job_render->page); + + pixbuf = g_object_ref (job_render->pixbuf); + if (job_info->pixbuf) + g_object_unref (job_info->pixbuf); + job_info->pixbuf = pixbuf; + + if (job_info->job == job) + job_info->job = NULL; + g_object_unref (job); + + g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0); +} + +/* This checks a job to see if the job would generate the right sized pixbuf + * given a scale. If it won't, it removes the job and clears it to NULL. + */ +static void +check_job_size_and_unref (CacheJobInfo *job_info, + EvPageCache *page_cache, + gfloat scale) +{ + gint width; + gint height; + + g_assert (job_info); + + if (job_info->job == NULL) + return; + + ev_page_cache_get_size (page_cache, + EV_JOB_RENDER (job_info->job)->page, + scale, + &width, &height); + + if (width == EV_JOB_RENDER (job_info->job)->target_width && + height == EV_JOB_RENDER (job_info->job)->target_height) + return; + + /* Try to remove the job. If we can't, then the thread has already + * picked it up and we are going get a signal when it's done. If we + * can, then the job is fully dead and will never rnu.. */ + if (ev_job_queue_remove_job (job_info->job)) + g_object_unref (job_info->job); + + job_info->job = NULL; +} + +/* Do all function that copies a job from an older cache to it's position in the + * new cache. It clears the old job if it doesn't have a place. + */ +static void +move_one_job (CacheJobInfo *job_info, + EvPixbufCache *pixbuf_cache, + int page, + CacheJobInfo *new_job_list, + CacheJobInfo *new_prev_job, + CacheJobInfo *new_next_job, + int start_page, + int end_page, + EvJobPriority priority) +{ + CacheJobInfo *target_page = NULL; + int page_offset; + EvJobPriority new_priority; + + if (page < (start_page - pixbuf_cache->preload_cache_size) || + page > (end_page + pixbuf_cache->preload_cache_size)) { + dispose_cache_job_info (job_info, pixbuf_cache); + return; + } + + /* find the target page to copy it over to. */ + if (page < start_page) { + page_offset = (page - (start_page - pixbuf_cache->preload_cache_size)); + + g_assert (page_offset >= 0 && + page_offset < pixbuf_cache->preload_cache_size); + target_page = new_prev_job + page_offset; + new_priority = EV_JOB_PRIORITY_LOW; + } else if (page > end_page) { + page_offset = (page - (end_page + 1)); + + g_assert (page_offset >= 0 && + page_offset < pixbuf_cache->preload_cache_size); + target_page = new_next_job + page_offset; + new_priority = EV_JOB_PRIORITY_LOW; + } else { + page_offset = page - start_page; + g_assert (page_offset >= 0 && + page_offset <= ((end_page - start_page) + 1)); + new_priority = EV_JOB_PRIORITY_HIGH; + target_page = new_job_list + page_offset; + } + + *target_page = *job_info; + job_info->job = NULL; + job_info->pixbuf = NULL; + + if (new_priority != priority && target_page->job) { + g_print ("FIXME: update priority \n"); + } +} + + + +static void +ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache, + gint start_page, + gint end_page) +{ + CacheJobInfo *new_job_list; + CacheJobInfo *new_prev_job; + CacheJobInfo *new_next_job; + EvPageCache *page_cache; + int i, page; + + if (pixbuf_cache->start_page == start_page && + pixbuf_cache->end_page == end_page) + return; + + page_cache = ev_document_get_page_cache (pixbuf_cache->document); + + new_job_list = g_new0 (CacheJobInfo, (end_page - start_page) + 1); + new_prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size); + new_next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size); + + /* We go through each job in the old cache and either clear it or move + * it to a new location. */ + + /* Start with the prev cache. */ + page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size; + for (i = 0; i < pixbuf_cache->preload_cache_size; i++) { + if (page < 1) { + dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache); + } else { + move_one_job (pixbuf_cache->prev_job + i, + pixbuf_cache, page, + new_job_list, new_prev_job, new_next_job, + start_page, end_page, EV_JOB_PRIORITY_LOW); + } + page ++; + } + + page = pixbuf_cache->start_page; + for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) { + move_one_job (pixbuf_cache->job_list + i, + pixbuf_cache, page, + new_job_list, new_prev_job, new_next_job, + start_page, end_page, EV_JOB_PRIORITY_HIGH); + page++; + } + + for (i = 0; i < pixbuf_cache->preload_cache_size; i++) { + if (page > ev_page_cache_get_n_pages (page_cache)) { + dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache); + } else { + move_one_job (pixbuf_cache->next_job + i, + pixbuf_cache, page, + new_job_list, new_prev_job, new_next_job, + start_page, end_page, EV_JOB_PRIORITY_LOW); + } + page ++; + } + + g_free (pixbuf_cache->job_list); + g_free (pixbuf_cache->prev_job); + g_free (pixbuf_cache->next_job); + + pixbuf_cache->job_list = new_job_list; + pixbuf_cache->prev_job = new_prev_job; + pixbuf_cache->next_job = new_next_job; + + pixbuf_cache->start_page = start_page; + pixbuf_cache->end_page = end_page; +} + +static CacheJobInfo * +find_job_cache (EvPixbufCache *pixbuf_cache, + int page) +{ + int page_offset; + + if (page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size) || + page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size)) + return NULL; + + if (page < pixbuf_cache->start_page) { + page_offset = (page - (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size)); + + g_assert (page_offset >= 0 && + page_offset < pixbuf_cache->preload_cache_size); + return pixbuf_cache->prev_job + page_offset; + } + + if (page > pixbuf_cache->end_page) { + page_offset = (page - (pixbuf_cache->end_page + 1)); + + g_assert (page_offset >= 0 && + page_offset < pixbuf_cache->preload_cache_size); + return pixbuf_cache->next_job + page_offset; + } + + page_offset = page - pixbuf_cache->start_page; + g_assert (page_offset >= 0 && + page_offset <= PAGE_CACHE_LEN(pixbuf_cache)); + return pixbuf_cache->job_list + page_offset; +} + +static void +ev_pixbuf_cache_clear_job_sizes (EvPixbufCache *pixbuf_cache, + gfloat scale) +{ + EvPageCache *page_cache; + int i; + + page_cache = ev_document_get_page_cache (pixbuf_cache->document); + + for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) { + check_job_size_and_unref (pixbuf_cache->job_list + i, page_cache, scale); + } + + for (i = 0; i < pixbuf_cache->preload_cache_size; i++) { + check_job_size_and_unref (pixbuf_cache->prev_job + i, page_cache, scale); + check_job_size_and_unref (pixbuf_cache->next_job + i, page_cache, scale); + } +} + +#define FIRST_VISABLE_PREV(pixbuf_cache) \ + (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page)) + +static void +add_job_if_needed (EvPixbufCache *pixbuf_cache, + CacheJobInfo *job_info, + EvPageCache *page_cache, + gint page, + gfloat scale, + EvJobPriority priority) +{ + int width, height; + + if (job_info->job) + return; + + ev_page_cache_get_size (page_cache, + page, scale, + &width, &height); + + if (job_info->pixbuf && + gdk_pixbuf_get_width (job_info->pixbuf) == width && + gdk_pixbuf_get_height (job_info->pixbuf) == height) + return; + + /* make a new job now */ + job_info->job = ev_job_render_new (pixbuf_cache->document, + page, scale, + width, height); + ev_job_queue_add_job (job_info->job, priority); + g_signal_connect (job_info->job, "finished", G_CALLBACK (job_finished_cb), pixbuf_cache); +} + + +static void +ev_pixbuf_cache_add_jobs_if_needed (EvPixbufCache *pixbuf_cache, + gfloat scale) +{ + EvPageCache *page_cache; + CacheJobInfo *job_info; + int page; + int i; + + page_cache = ev_document_get_page_cache (pixbuf_cache->document); + + for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) { + job_info = (pixbuf_cache->job_list + i); + page = pixbuf_cache->start_page + i; + + add_job_if_needed (pixbuf_cache, job_info, + page_cache, page, scale, + EV_JOB_PRIORITY_HIGH); + } + + for (i = FIRST_VISABLE_PREV(pixbuf_cache); i < pixbuf_cache->preload_cache_size; i++) { + job_info = (pixbuf_cache->prev_job + i); + page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size + i; + + add_job_if_needed (pixbuf_cache, job_info, + page_cache, page, scale, + EV_JOB_PRIORITY_LOW); + } + + for (i = 0; i < VISIBLE_NEXT_LEN(pixbuf_cache, page_cache); i++) { + job_info = (pixbuf_cache->next_job + i); + page = pixbuf_cache->end_page + 1 + i; + + add_job_if_needed (pixbuf_cache, job_info, + page_cache, page, scale, + EV_JOB_PRIORITY_LOW); + } + +} + +void +ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache, + gint start_page, + gint end_page, + gfloat scale) +{ + EvPageCache *page_cache; + + g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache)); + + page_cache = ev_document_get_page_cache (pixbuf_cache->document); + + g_return_if_fail (start_page > 0 && start_page <= ev_page_cache_get_n_pages (page_cache)); + g_return_if_fail (end_page > 0 && end_page <= ev_page_cache_get_n_pages (page_cache)); + g_return_if_fail (end_page >= start_page); + + /* First, resize the page_range as needed. We cull old pages + * mercilessly. */ + ev_pixbuf_cache_update_range (pixbuf_cache, start_page, end_page); + + /* Then, we update the current jobs to see if any of them are the wrong + * size, we remove them if we need to. */ + ev_pixbuf_cache_clear_job_sizes (pixbuf_cache, scale); + + /* Finally, we add the new jobs for all the sizes that don't have a + * pixbuf */ + ev_pixbuf_cache_add_jobs_if_needed (pixbuf_cache, scale); +} + +GdkPixbuf * +ev_pixbuf_cache_get_pixbuf (EvPixbufCache *pixbuf_cache, + gint page) +{ + CacheJobInfo *job_info; + + job_info = find_job_cache (pixbuf_cache, page); + if (job_info == NULL) + return NULL; + + /* We don't need to wait for the idle to handle the callback */ + if (job_info->job && + EV_JOB (job_info->job)->finished) { + GdkPixbuf *pixbuf; + + pixbuf = g_object_ref (EV_JOB_RENDER (job_info->job)->pixbuf); + dispose_cache_job_info (job_info, pixbuf_cache); + job_info->pixbuf = pixbuf; + } + + return job_info->pixbuf; +} diff --git a/shell/ev-pixbuf-cache.h b/shell/ev-pixbuf-cache.h new file mode 100644 index 0000000..e49faa0 --- /dev/null +++ b/shell/ev-pixbuf-cache.h @@ -0,0 +1,51 @@ +/* this file is part of evince, a gnome document viewer + * + * Copyright (C) 2005 Red Hat, Inc + * + * Evince is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Evince is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EV_PIXBUF_CACHE_H__ +#define __EV_PIXBUF_CACHE_H__ + +#include +#include "ev-document.h" +#include "ev-job-queue.h" + +G_BEGIN_DECLS + +#define EV_TYPE_PIXBUF_CACHE (ev_pixbuf_cache_get_type ()) +#define EV_PIXBUF_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_PIXBUF_CACHE, EvPixbufCache)) +#define EV_IS_PIXBUF_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_PIXBUF_CACHE)) + +/* This is basically an extention of ev-view.c, and is out here just to keep + * ev-view.c from exploding. + */ + +typedef struct _EvPixbufCache EvPixbufCache; +typedef struct _EvPixbufCacheClass EvPixbufCacheClass; + +GType ev_pixbuf_cache_get_type (void) G_GNUC_CONST; +EvPixbufCache *ev_pixbuf_cache_new (EvDocument *document); +void ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache, + gint start_page, + gint end_page, + gfloat scale); +GdkPixbuf *ev_pixbuf_cache_get_pixbuf (EvPixbufCache *pixbuf_cache, + gint page); + +G_END_DECLS + +#endif /* __EV_PIXBUF_CACHE_H__ */ diff --git a/shell/ev-print-job.c b/shell/ev-print-job.c index 0c66b81..ba283ff 100644 --- a/shell/ev-print-job.c +++ b/shell/ev-print-job.c @@ -224,31 +224,40 @@ ev_print_job_use_print_dialog_settings (EvPrintJob *job, GnomePrintDialog *dialo gnome_print_config_get_page_size (print_config, &job->width, &job->height); gnome_print_config_get_boolean (print_config, - GNOME_PRINT_KEY_DUPLEX, &job->duplex); + (guchar *)GNOME_PRINT_KEY_DUPLEX, &job->duplex); gnome_print_config_unref (print_config); } static gboolean idle_print_handler (EvPrintJob *job) { + EvPageCache *page_cache; + if (!job->printing) { + g_mutex_lock (EV_DOC_MUTEX); ev_ps_exporter_begin (EV_PS_EXPORTER (job->document), job->temp_file); + g_mutex_unlock (EV_DOC_MUTEX); job->next_page = 1; /* FIXME use 0-based page numbering? */ job->printing = TRUE; return TRUE; } - if (job->next_page <= ev_document_get_n_pages (job->document)) { + page_cache = ev_document_get_page_cache (job->document); + if (job->next_page <= ev_page_cache_get_n_pages (page_cache)) { #if 0 g_printerr ("Printing page %d\n", job->next_page); #endif + g_mutex_lock (EV_DOC_MUTEX); ev_ps_exporter_do_page (EV_PS_EXPORTER (job->document), job->next_page); + g_mutex_unlock (EV_DOC_MUTEX); job->next_page++; return TRUE; } else { /* no more pages */ + g_mutex_lock (EV_DOC_MUTEX); ev_ps_exporter_end (EV_PS_EXPORTER (job->document)); + g_mutex_unlock (EV_DOC_MUTEX); close (job->fd); job->fd = 0; diff --git a/shell/ev-sidebar-links.c b/shell/ev-sidebar-links.c index 18f9e8f..03ea9e9 100644 --- a/shell/ev-sidebar-links.c +++ b/shell/ev-sidebar-links.c @@ -25,41 +25,37 @@ #endif #include +#include #include #include "ev-sidebar-links.h" +#include "ev-job-queue.h" #include "ev-document-links.h" #include "ev-window.h" -/* Amount of time we devote to each iteration of the idle, in microseconds */ -#define IDLE_WORK_LENGTH 5000 - -typedef struct { - EvDocumentLinksIter *links_iter; - GtkTreeIter *tree_iter; -} IdleStackData; - struct _EvSidebarLinksPrivate { GtkWidget *tree_view; + + /* Keep these ids around for blocking */ + guint selection_id; + guint page_changed_id; + + EvJob *job; GtkTreeModel *model; - EvDocument *current_document; - GList *idle_stack; - guint idle_id; + EvDocument *document; + EvPageCache *page_cache; }; -enum { - LINKS_COLUMN_MARKUP, - LINKS_COLUMN_PAGE_NUM, - LINKS_COLUMN_PAGE_VALID, - LINKS_COLUMN_LINK, - LINKS_COLUMN_NUM_COLUMNS -}; -static void links_page_num_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data); +static void links_page_num_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data); +static void update_page_callback (EvPageCache *page_cache, + gint current_page, + EvSidebarLinks *sidebar_links); + G_DEFINE_TYPE (EvSidebarLinks, ev_sidebar_links, GTK_TYPE_VBOX) @@ -72,7 +68,6 @@ ev_sidebar_links_destroy (GtkObject *object) { EvSidebarLinks *ev_sidebar_links = (EvSidebarLinks *) object; - g_print ("ev_sidebar_links_destroy!\n"); ev_sidebar_links_clear_document (ev_sidebar_links); } @@ -100,26 +95,48 @@ selection_changed_cb (GtkTreeSelection *selection, g_return_if_fail (EV_IS_SIDEBAR_LINKS (ev_sidebar_links)); - document = EV_DOCUMENT (ev_sidebar_links->priv->current_document); - g_return_if_fail (ev_sidebar_links->priv->current_document != NULL); + document = EV_DOCUMENT (ev_sidebar_links->priv->document); + g_return_if_fail (ev_sidebar_links->priv->document != NULL); if (gtk_tree_selection_get_selected (selection, &model, &iter)) { EvLink *link; - GtkWidget *window; - GValue value = {0, }; - gtk_tree_model_get_value (model, &iter, - LINKS_COLUMN_LINK, &value); + gtk_tree_model_get (model, &iter, + EV_DOCUMENT_LINKS_COLUMN_LINK, &link, + -1); + + if (link == NULL) + return; + + g_signal_handler_block (ev_sidebar_links->priv->page_cache, + ev_sidebar_links->priv->page_changed_id); + ev_page_cache_set_link (ev_sidebar_links->priv->page_cache, link); + g_signal_handler_unblock (ev_sidebar_links->priv->page_cache, + ev_sidebar_links->priv->page_changed_id); + } +} - link = EV_LINK (g_value_get_object (&value)); - g_return_if_fail (link != NULL); +static GtkTreeModel * +create_loading_model (void) +{ + GtkTreeModel *retval; + GtkTreeIter iter; + gchar *markup; + + /* Creates a fake model to indicate that we're loading */ + retval = (GtkTreeModel *)gtk_list_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS, + G_TYPE_STRING, + G_TYPE_BOOLEAN, + G_TYPE_OBJECT); + + gtk_list_store_append (GTK_LIST_STORE (retval), &iter); + markup = g_strdup_printf ("%s", _("Loading...")); + gtk_list_store_set (GTK_LIST_STORE (retval), &iter, + EV_DOCUMENT_LINKS_COLUMN_MARKUP, markup, + -1); + g_free (markup); - window = gtk_widget_get_ancestor (GTK_WIDGET (ev_sidebar_links), - EV_TYPE_WINDOW); - if (window) { - ev_window_open_link (EV_WINDOW (window), link); - } - } + return retval; } static void @@ -130,13 +147,9 @@ ev_sidebar_links_construct (EvSidebarLinks *ev_sidebar_links) GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkTreeSelection *selection; + GtkTreeModel *loading_model; priv = ev_sidebar_links->priv; - priv->model = (GtkTreeModel *) gtk_tree_store_new (LINKS_COLUMN_NUM_COLUMNS, - G_TYPE_STRING, - G_TYPE_INT, - G_TYPE_BOOLEAN, - G_TYPE_OBJECT); swindow = gtk_scrolled_window_new (NULL, NULL); @@ -146,11 +159,14 @@ ev_sidebar_links_construct (EvSidebarLinks *ev_sidebar_links) GTK_SHADOW_IN); /* Create tree view */ - priv->tree_view = gtk_tree_view_new_with_model (priv->model); - g_object_unref (priv->model); + loading_model = create_loading_model (); + priv->tree_view = gtk_tree_view_new_with_model (loading_model); + g_object_unref (loading_model); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE); gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view); - gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (priv->tree_view), TRUE); gtk_box_pack_start (GTK_BOX (ev_sidebar_links), swindow, TRUE, TRUE, 0); gtk_widget_show_all (GTK_WIDGET (ev_sidebar_links)); @@ -165,20 +181,17 @@ ev_sidebar_links_construct (EvSidebarLinks *ev_sidebar_links) NULL); gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN (column), renderer, TRUE); gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer, - "markup", LINKS_COLUMN_MARKUP, + "markup", EV_DOCUMENT_LINKS_COLUMN_MARKUP, NULL); + renderer = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_end (GTK_TREE_VIEW_COLUMN (column), renderer, FALSE); gtk_tree_view_column_set_cell_data_func (GTK_TREE_VIEW_COLUMN (column), renderer, (GtkTreeCellDataFunc) links_page_num_func, NULL, NULL); - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)); - g_signal_connect (selection, "changed", - G_CALLBACK (selection_changed_cb), - ev_sidebar_links); + } static void @@ -196,16 +209,15 @@ links_page_num_func (GtkTreeViewColumn *tree_column, GtkTreeIter *iter, gpointer data) { - int page_num; - gboolean page_valid; + EvLink *link; gtk_tree_model_get (tree_model, iter, - LINKS_COLUMN_PAGE_NUM, &page_num, - LINKS_COLUMN_PAGE_VALID, &page_valid, + EV_DOCUMENT_LINKS_COLUMN_LINK, &link, -1); - - if (page_valid) { - gchar *markup = g_strdup_printf ("%d", page_num); + + if (link != NULL && + ev_link_get_link_type (link) == EV_LINK_TYPE_PAGE) { + gchar *markup = g_strdup_printf ("%d", ev_link_get_page (link)); g_object_set (cell, "markup", markup, "visible", TRUE, @@ -230,139 +242,117 @@ ev_sidebar_links_new (void) return ev_sidebar_links; } -static void -stack_data_free (IdleStackData *stack_data, - EvDocumentLinks *document_links) +void +ev_sidebar_links_clear_document (EvSidebarLinks *sidebar_links) { - g_assert (stack_data); + EvSidebarLinksPrivate *priv; - if (stack_data->tree_iter) - gtk_tree_iter_free (stack_data->tree_iter); - if (stack_data->links_iter) - ev_document_links_free_iter (document_links, stack_data->links_iter); - g_free (stack_data); + g_return_if_fail (EV_IS_SIDEBAR_LINKS (sidebar_links)); + + priv = sidebar_links->priv; + + if (priv->document) { + g_object_unref (priv->document); + priv->document = NULL; + priv->page_cache = NULL; + } + + gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), NULL); } static gboolean -do_one_iteration (EvSidebarLinks *ev_sidebar_links) +update_page_callback_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) { - EvSidebarLinksPrivate *priv = ev_sidebar_links->priv; + EvSidebarLinks *sidebar_links = (data); EvLink *link; - IdleStackData *stack_data; - GtkTreeIter tree_iter; - EvDocumentLinksIter *child_iter; - EvLinkType link_type; - gint page; - - g_assert (priv->idle_stack); - - stack_data = (IdleStackData *) priv->idle_stack->data; - - link = ev_document_links_get_link - (EV_DOCUMENT_LINKS (priv->current_document), - stack_data->links_iter); - if (link == NULL) { - g_warning ("mismatch in model. No values available at current level.\n"); - return FALSE; - } - page = ev_link_get_page (link); - link_type = ev_link_get_link_type (link); - gtk_tree_store_append (GTK_TREE_STORE (priv->model), &tree_iter, stack_data->tree_iter); - gtk_tree_store_set (GTK_TREE_STORE (priv->model), &tree_iter, - LINKS_COLUMN_MARKUP, ev_link_get_title (link), - LINKS_COLUMN_PAGE_NUM, page, - LINKS_COLUMN_PAGE_VALID, (link_type == EV_LINK_TYPE_PAGE), - LINKS_COLUMN_LINK, link, + gtk_tree_model_get (model, iter, + EV_DOCUMENT_LINKS_COLUMN_LINK, &link, -1); - g_object_unref (link); - - child_iter = ev_document_links_get_child (EV_DOCUMENT_LINKS (priv->current_document), - stack_data->links_iter); - if (child_iter) { - IdleStackData *child_stack_data; - child_stack_data = g_new0 (IdleStackData, 1); - child_stack_data->tree_iter = gtk_tree_iter_copy (&tree_iter); - child_stack_data->links_iter = child_iter; - priv->idle_stack = g_list_prepend (priv->idle_stack, child_stack_data); + if (link && ev_link_get_link_type (link) == EV_LINK_TYPE_PAGE) { + int current_page; - return TRUE; - } + current_page = ev_page_cache_get_current_page (sidebar_links->priv->page_cache); + if (ev_link_get_page (link) == current_page) { + GtkTreeSelection *selection; - /* We don't have children, so we need to walk to the next node */ - while (TRUE) { - if (ev_document_links_next (EV_DOCUMENT_LINKS (priv->current_document), - stack_data->links_iter)) - return TRUE; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sidebar_links->priv->tree_view)); + + gtk_tree_selection_select_path (selection, path); - /* We're done with this level. Pop it off the idle stack and go - * to the next level */ - stack_data_free (stack_data, EV_DOCUMENT_LINKS (priv->current_document)); - priv->idle_stack = g_list_delete_link (priv->idle_stack, priv->idle_stack); - if (priv->idle_stack == NULL) - return FALSE; - stack_data = priv->idle_stack->data; + return TRUE; + } } + + return FALSE; } -static gboolean -populate_links_idle (gpointer data) +static void +update_page_callback (EvPageCache *page_cache, + gint current_page, + EvSidebarLinks *sidebar_links) { - GTimer *timer; - gint i; - gulong microseconds = 0; + GtkTreeSelection *selection; + /* We go through the tree linearly looking for the first page that + * matches. This is pretty inefficient. We can do something neat with + * a GtkTreeModelSort here to make it faster, if it turns out to be + * slow. + */ - EvSidebarLinks *ev_sidebar_links = (EvSidebarLinks *)data; - EvSidebarLinksPrivate *priv = ev_sidebar_links->priv; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sidebar_links->priv->tree_view)); - if (priv->idle_stack == NULL) { - priv->idle_id = 0; - return FALSE; - } + g_signal_handler_block (selection, sidebar_links->priv->selection_id); - /* The amount of time that reading the next bookmark takes is wildly - * inconsistent, so we constrain it to IDLE_WORK_LENGTH microseconds per - * idle iteration. */ - timer = g_timer_new (); - i = 0; - g_timer_start (timer); - while (do_one_iteration (ev_sidebar_links)) { - i++; - g_timer_elapsed (timer, µseconds); - if (microseconds > IDLE_WORK_LENGTH) - break; - } - g_timer_destroy (timer); -#if 0 - g_print ("%d rows done this idle in %d\n", i, (int)microseconds); -#endif - return TRUE; + gtk_tree_selection_unselect_all (selection); + gtk_tree_model_foreach (sidebar_links->priv->model, + update_page_callback_foreach, + sidebar_links); + + g_signal_handler_unblock (selection, sidebar_links->priv->selection_id); } -void -ev_sidebar_links_clear_document (EvSidebarLinks *sidebar_links) +static void +job_finished_cb (EvJobLinks *job, + EvSidebarLinks *sidebar_links) { EvSidebarLinksPrivate *priv; - - g_return_if_fail (EV_IS_SIDEBAR_LINKS (sidebar_links)); + GtkTreeSelection *selection; + GtkTreeIter iter; + GtkTreePath *path; + gboolean result; priv = sidebar_links->priv; - /* Clear the idle */ - if (priv->idle_id != 0) { - g_source_remove (priv->idle_id); - priv->idle_id = 0; + priv->model = g_object_ref (job->model); + gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), job->model); + g_object_unref (job); + + /* Expand one level of the tree */ + path = gtk_tree_path_new_first (); + for (result = gtk_tree_model_get_iter_first (priv->model, &iter); + result; + result = gtk_tree_model_iter_next (priv->model, &iter)) { + gtk_tree_view_expand_row (GTK_TREE_VIEW (priv->tree_view), path, FALSE); + gtk_tree_path_next (path); } - g_list_foreach (priv->idle_stack, (GFunc) stack_data_free, priv->current_document); - g_list_free (priv->idle_stack); - priv->idle_stack = NULL; + gtk_tree_path_free (path); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + priv->selection_id = g_signal_connect (selection, "changed", + G_CALLBACK (selection_changed_cb), + sidebar_links); + priv->page_changed_id = g_signal_connect (priv->page_cache, "page-changed", + G_CALLBACK (update_page_callback), + sidebar_links); + update_page_callback (priv->page_cache, + ev_page_cache_get_current_page (priv->page_cache), + sidebar_links); - if (priv->current_document) { - g_object_unref (priv->current_document); - priv->current_document = NULL; - } - gtk_tree_store_clear (GTK_TREE_STORE (priv->model)); } void @@ -370,7 +360,6 @@ ev_sidebar_links_set_document (EvSidebarLinks *sidebar_links, EvDocument *document) { EvSidebarLinksPrivate *priv; - EvDocumentLinksIter *links_iter; g_return_if_fail (EV_IS_SIDEBAR_LINKS (sidebar_links)); g_return_if_fail (EV_IS_DOCUMENT (document)); @@ -378,19 +367,17 @@ ev_sidebar_links_set_document (EvSidebarLinks *sidebar_links, priv = sidebar_links->priv; g_object_ref (document); - ev_sidebar_links_clear_document (sidebar_links); - priv->current_document = document; - links_iter = ev_document_links_begin_read (EV_DOCUMENT_LINKS (document)); - if (links_iter) { - IdleStackData *stack_data; + priv->document = document; + priv->page_cache = ev_document_get_page_cache (document); - stack_data = g_new0 (IdleStackData, 1); - stack_data->links_iter = links_iter; - stack_data->tree_iter = NULL; + priv->job = ev_job_links_new (document); + g_signal_connect (priv->job, + "finished", + G_CALLBACK (job_finished_cb), + sidebar_links); + /* The priority doesn't matter for this job */ + ev_job_queue_add_job (priv->job, EV_JOB_PRIORITY_LOW); - priv->idle_stack = g_list_prepend (priv->idle_stack, stack_data); - priv->idle_id = g_idle_add (populate_links_idle, sidebar_links); - } } diff --git a/shell/ev-sidebar-thumbnails.c b/shell/ev-sidebar-thumbnails.c index b4622f6..c548e29 100644 --- a/shell/ev-sidebar-thumbnails.c +++ b/shell/ev-sidebar-thumbnails.c @@ -32,6 +32,7 @@ #include "ev-sidebar-thumbnails.h" #include "ev-document-thumbnails.h" #include "ev-document-misc.h" +#include "ev-job-queue.h" #include "ev-window.h" #include "ev-utils.h" @@ -46,15 +47,14 @@ struct _EvSidebarThumbnailsPrivate { GtkListStore *list_store; EvDocument *document; - guint idle_id; - gint current_page, n_pages, pages_done; - GtkTreeIter current_page_iter; + gint n_pages, pages_done; }; enum { COLUMN_PAGE_STRING, COLUMN_PIXBUF, COLUMN_THUMBNAIL_SET, + COLUMN_JOB, NUM_COLUMNS }; @@ -64,21 +64,6 @@ G_DEFINE_TYPE (EvSidebarThumbnails, ev_sidebar_thumbnails, GTK_TYPE_VBOX); (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_THUMBNAILS, EvSidebarThumbnailsPrivate)); static void -ev_sidebar_thumbnails_destroy (GtkObject *object) -{ - EvSidebarThumbnails *ev_sidebar_thumbnails = EV_SIDEBAR_THUMBNAILS (object); - EvSidebarThumbnailsPrivate *priv = ev_sidebar_thumbnails->priv; - - if (priv->idle_id != 0) { - g_source_remove (priv->idle_id); - - priv->idle_id = 0; - } - - GTK_OBJECT_CLASS (ev_sidebar_thumbnails_parent_class)->destroy (object); -} - -static void ev_sidebar_thumbnails_class_init (EvSidebarThumbnailsClass *ev_sidebar_thumbnails_class) { GObjectClass *g_object_class; @@ -87,10 +72,7 @@ ev_sidebar_thumbnails_class_init (EvSidebarThumbnailsClass *ev_sidebar_thumbnail g_object_class = G_OBJECT_CLASS (ev_sidebar_thumbnails_class); gtk_object_class = GTK_OBJECT_CLASS (ev_sidebar_thumbnails_class); - gtk_object_class->destroy = ev_sidebar_thumbnails_destroy; - g_type_class_add_private (g_object_class, sizeof (EvSidebarThumbnailsPrivate)); - } static void @@ -111,19 +93,12 @@ adjustment_changed_cb (GtkAdjustment *adjustment, if (!path) return; - page = gtk_tree_path_get_indices (path)[0] + 1; - if (page == priv->current_page) - return; + page = gtk_tree_path_get_indices (path)[0]; gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->list_store), &iter, path); gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store), &iter, COLUMN_THUMBNAIL_SET, &thumbnail_set, -1); - if (!thumbnail_set) { - priv->current_page = page; - priv->current_page_iter = iter; - - } } static void @@ -131,28 +106,23 @@ ev_sidebar_tree_selection_changed (GtkTreeSelection *selection, EvSidebarThumbnails *ev_sidebar_thumbnails) { EvSidebarThumbnailsPrivate *priv; - GtkWidget *window; + EvPageCache *page_cache; GtkTreePath *path; GtkTreeIter iter; int page; priv = ev_sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (ev_sidebar_thumbnails); - + if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) return; - + path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->list_store), &iter); - page = gtk_tree_path_get_indices (path)[0] + 1; - gtk_tree_path_free (path); - window = gtk_widget_get_ancestor (GTK_WIDGET (ev_sidebar_thumbnails), - EV_TYPE_WINDOW); - if (window && ev_document_get_page (priv->document) != page) { - ev_window_open_page (EV_WINDOW (window), page); - } + page_cache = ev_document_get_page_cache (priv->document); + ev_page_cache_set_current_page (page_cache, page); } static void @@ -164,10 +134,10 @@ ev_sidebar_thumbnails_init (EvSidebarThumbnails *ev_sidebar_thumbnails) GtkTreeSelection *selection; priv = ev_sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (ev_sidebar_thumbnails); - - priv->list_store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN); + + priv->list_store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, EV_TYPE_JOB_THUMBNAIL); priv->tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->list_store)); - + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)); g_signal_connect (selection, "changed", G_CALLBACK (ev_sidebar_tree_selection_changed), ev_sidebar_thumbnails); @@ -211,126 +181,39 @@ ev_sidebar_thumbnails_new (void) return ev_sidebar_thumbnails; } -static gboolean -do_one_iteration (EvSidebarThumbnails *ev_sidebar_thumbnails) -{ - EvSidebarThumbnailsPrivate *priv = ev_sidebar_thumbnails->priv; - GdkPixbuf *pixbuf; - gboolean thumbnail_set; - - gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store), - &(priv->current_page_iter), - COLUMN_THUMBNAIL_SET, &thumbnail_set, - -1); - if (!thumbnail_set) { - pixbuf = ev_document_thumbnails_get_thumbnail - (EV_DOCUMENT_THUMBNAILS (priv->document), - priv->current_page, THUMBNAIL_WIDTH, TRUE); - - gtk_list_store_set (priv->list_store, - &(priv->current_page_iter), - COLUMN_PIXBUF, pixbuf, - COLUMN_THUMBNAIL_SET, TRUE, - -1); - - g_object_unref (pixbuf); - priv->pages_done ++; - } - - priv->current_page++; - - if (priv->current_page > priv->n_pages) { - priv->current_page = 1; - gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store), - &(priv->current_page_iter)); - } else { - gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->list_store), - &(priv->current_page_iter)); - } - - if (priv->pages_done == priv->n_pages) - return FALSE; - else - return TRUE; -} - -static gboolean -populate_thumbnails_idle (gpointer data) -{ - GTimer *timer; - int i; - gdouble time_elapsed = 0; - - EvSidebarThumbnails *ev_sidebar_thumbnails = EV_SIDEBAR_THUMBNAILS (data); - EvSidebarThumbnailsPrivate *priv = ev_sidebar_thumbnails->priv; - -#if PROFILE_THUMB == 1 - static GTimer *total_timer; - static gboolean first_time = TRUE; - - if (first_time) { - total_timer = g_timer_new (); - first_time = FALSE; - g_timer_start (total_timer); - } -#endif - - /* undo the thumbnailing idle and handler */ - if (priv->pages_done == priv->n_pages) { - priv->idle_id = 0; - g_signal_handlers_disconnect_by_func (priv->vadjustment, - adjustment_changed_cb, - ev_sidebar_thumbnails); -#if PROFILE_THUMB == 1 - time_elapsed = g_timer_elapsed (total_timer, NULL); - g_timer_destroy (total_timer); - g_print ("%d rows done in %f seconds\n", - gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->list_store), NULL), - time_elapsed); -#endif - return FALSE; - } - - timer = g_timer_new (); - i = 0; - g_timer_start (timer); - while (do_one_iteration (ev_sidebar_thumbnails)) { - i++; - time_elapsed = g_timer_elapsed (timer, NULL); - if (time_elapsed > IDLE_WORK_LENGTH/1000000) - break; - } - g_timer_destroy (timer); -#if PROFILE_THUMB == 2 - g_print ("%d rows done this idle in %f seconds\n", i, time_elapsed); -#endif - - return TRUE; -} - -void -ev_sidebar_thumbnails_select_page (EvSidebarThumbnails *sidebar, - int page) +static void +page_changed_cb (EvPageCache *page_cache, + int page, + EvSidebarThumbnails *sidebar) { GtkTreePath *path; GtkTreeSelection *selection; - /* if the EvSidebar's document can't provide thumbnails */ - if (sidebar->priv->document == NULL) - return; - path = gtk_tree_path_new_from_indices (page - 1, -1); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sidebar->priv->tree_view)); - if (path) { - gtk_tree_selection_select_path (selection, path); - gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (sidebar->priv->tree_view), - path, NULL, FALSE, 0.0, 0.0); - gtk_tree_path_free (path); - } + gtk_tree_selection_select_path (selection, path); + gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (sidebar->priv->tree_view), + path, NULL, FALSE, 0.0, 0.0); + gtk_tree_path_free (path); } +static void +thumbnail_job_completed_callback (EvJobThumbnail *job, + EvSidebarThumbnails *sidebar_thumbnails) +{ + EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv; + GtkTreeIter *iter; + + iter = (GtkTreeIter *) g_object_get_data (G_OBJECT (job), "tree_iter"); + gtk_list_store_set (priv->list_store, + iter, + COLUMN_PIXBUF, job->thumbnail, + COLUMN_THUMBNAIL_SET, TRUE, + COLUMN_JOB, NULL, + -1); +} void ev_sidebar_thumbnails_set_document (EvSidebarThumbnails *sidebar_thumbnails, @@ -342,43 +225,56 @@ ev_sidebar_thumbnails_set_document (EvSidebarThumbnails *sidebar_thumbnails, gchar *page; gint width = THUMBNAIL_WIDTH; gint height = THUMBNAIL_WIDTH; + EvPageCache *page_cache; EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv; g_return_if_fail (EV_IS_DOCUMENT_THUMBNAILS (document)); - if (priv->idle_id != 0) { - g_source_remove (priv->idle_id); - } - n_pages = ev_document_get_n_pages (document); + page_cache = ev_document_get_page_cache (document); + n_pages = ev_page_cache_get_n_pages (page_cache); priv->document = document; - priv->idle_id = g_idle_add (populate_thumbnails_idle, sidebar_thumbnails); priv->n_pages = n_pages; /* We get the dimensions of the first doc so that we can make a blank * icon. */ + g_mutex_lock (EV_DOC_MUTEX); ev_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (priv->document), 1, THUMBNAIL_WIDTH, &width, &height); + g_mutex_unlock (EV_DOC_MUTEX); + loading_icon = ev_document_misc_get_thumbnail_frame (width, height, NULL); gtk_list_store_clear (priv->list_store); - for (i = 1; i <= n_pages; i++) { + EvJob *job; + + /* FIXME: Bah. This is still -1 for some reason. Need to track it down.. */ + job = ev_job_thumbnail_new (priv->document, i - 1, THUMBNAIL_WIDTH); page = g_strdup_printf ("%d", i); gtk_list_store_append (priv->list_store, &iter); gtk_list_store_set (priv->list_store, &iter, COLUMN_PAGE_STRING, page, COLUMN_PIXBUF, loading_icon, COLUMN_THUMBNAIL_SET, FALSE, + COLUMN_JOB, job, -1); g_free (page); + ev_job_queue_add_job (job, EV_JOB_PRIORITY_LOW); + g_object_set_data_full (G_OBJECT (job), "tree_iter", + gtk_tree_iter_copy (&iter), + (GDestroyNotify) gtk_tree_iter_free); + g_signal_connect (job, "finished", + G_CALLBACK (thumbnail_job_completed_callback), + sidebar_thumbnails); } g_object_unref (loading_icon); - gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store), - &(priv->current_page_iter)); - priv->current_page = 1; - priv->pages_done = 0; + + /* Connect to the signal and trigger a fake callback */ + g_signal_connect (page_cache, "page-changed", G_CALLBACK (page_changed_cb), sidebar_thumbnails); + page_changed_cb (page_cache, ev_page_cache_get_current_page (page_cache), sidebar_thumbnails); + } diff --git a/shell/ev-sidebar-thumbnails.h b/shell/ev-sidebar-thumbnails.h index 0d7a470..45f68fc 100644 --- a/shell/ev-sidebar-thumbnails.h +++ b/shell/ev-sidebar-thumbnails.h @@ -56,8 +56,6 @@ GtkWidget *ev_sidebar_thumbnails_new (void); void ev_sidebar_thumbnails_set_document (EvSidebarThumbnails *sidebar_thumbnails, EvDocument *document); -void ev_sidebar_thumbnails_select_page (EvSidebarThumbnails *sidebar_thumbnails, - int page); G_END_DECLS diff --git a/shell/ev-sidebar.c b/shell/ev-sidebar.c index 202c249..f28b514 100644 --- a/shell/ev-sidebar.c +++ b/shell/ev-sidebar.c @@ -402,8 +402,7 @@ ev_sidebar_set_document (EvSidebar *sidebar, if (EV_IS_SIDEBAR_LINKS (widget) && EV_IS_DOCUMENT_LINKS (document) && ev_document_links_has_document_links (EV_DOCUMENT_LINKS (document))) { - ev_sidebar_links_set_document - (EV_SIDEBAR_LINKS (widget), document); + ev_sidebar_links_set_document (EV_SIDEBAR_LINKS (widget), document); } else if (EV_IS_SIDEBAR_THUMBNAILS (widget) && EV_IS_DOCUMENT_THUMBNAILS (document)) { ev_sidebar_thumbnails_set_document diff --git a/shell/ev-view.c b/shell/ev-view.c index 372d4a3..9921ed4 100644 --- a/shell/ev-view.c +++ b/shell/ev-view.c @@ -31,6 +31,9 @@ #include "ev-document-find.h" #include "ev-document-misc.h" #include "ev-debug.h" +#include "ev-job-queue.h" +#include "ev-page-cache.h" +#include "ev-pixbuf-cache.h" #define EV_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_VIEW, EvViewClass)) #define EV_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_VIEW)) @@ -92,6 +95,12 @@ struct _EvView { GtkAdjustment *hadjustment; GtkAdjustment *vadjustment; + EvPageCache *page_cache; + EvPixbufCache *pixbuf_cache; + + gint current_page; + EvJobRender *current_job; + int find_page; int find_result; int spacing; @@ -111,11 +120,8 @@ struct _EvViewClass { GtkScrollType scroll, gboolean horizontal); - /* Should this be notify::page? */ - void (*page_changed) (EvView *view); }; -static guint page_changed_signal = 0; static void ev_view_set_scroll_adjustments (EvView *view, GtkAdjustment *hadjustment, @@ -196,8 +202,6 @@ ev_view_finalize (GObject *object) LOG ("Finalize"); - if (view->document) - g_object_unref (view->document); ev_view_set_scroll_adjustments (view, NULL, NULL); @@ -209,6 +213,14 @@ ev_view_destroy (GtkObject *object) { EvView *view = EV_VIEW (object); + if (view->document) { + g_object_unref (view->document); + view->document = NULL; + } + if (view->pixbuf_cache) { + g_object_unref (view->pixbuf_cache); + view->pixbuf_cache = NULL; + } ev_view_set_scroll_adjustments (view, NULL, NULL); GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object); @@ -224,7 +236,11 @@ ev_view_get_offsets (EvView *view, int *x_offset, int *y_offset) g_return_if_fail (EV_IS_DOCUMENT (document)); - ev_document_get_page_size (document, -1, &width, &height); + ev_page_cache_get_size (view->page_cache, + view->current_page, + view->scale, + &width, &height); + ev_document_misc_get_page_border_size (width, height, &border); *x_offset = view->spacing; @@ -259,6 +275,9 @@ doc_rect_to_view_rect (EvView *view, GdkRectangle *doc_rect, GdkRectangle *view_ view_rect->height = doc_rect->height * view->scale; } + +/* Called by size_request to make sure we have appropriate jobs running. + */ static void ev_view_size_request (GtkWidget *widget, GtkRequisition *requisition) @@ -276,8 +295,15 @@ ev_view_size_request (GtkWidget *widget, return; } - ev_document_get_page_size (view->document, -1, - &width, &height); + ev_page_cache_get_size (view->page_cache, + view->current_page, + view->scale, + &width, &height); + + ev_pixbuf_cache_set_page_range (view->pixbuf_cache, + view->current_page, + view->current_page, + view->scale); ev_document_misc_get_page_border_size (width, height, &border); if (view->width >= 0) { @@ -366,8 +392,6 @@ ev_view_realize (GtkWidget *widget) gdk_window_set_background (view->bin_window, &widget->style->mid[widget->state]); if (view->document) { - ev_document_set_target (view->document, view->bin_window); - /* We can't get page size without a target, so we have to * queue a size request at realization. Could be fixed * with EvDocument changes to allow setting a GdkScreen @@ -382,9 +406,6 @@ ev_view_unrealize (GtkWidget *widget) { EvView *view = EV_VIEW (widget); - if (view->document) - ev_document_set_target (view->document, NULL); - gdk_window_set_user_data (view->bin_window, NULL); gdk_window_destroy (view->bin_window); view->bin_window = NULL; @@ -442,20 +463,26 @@ static void highlight_find_results (EvView *view) { EvDocumentFind *find; - int i, results; + int i, results = 0; g_return_if_fail (EV_IS_DOCUMENT_FIND (view->document)); find = EV_DOCUMENT_FIND (view->document); +#if 0 + g_mutex_lock (EV_DOC_MUTEX); results = ev_document_find_get_n_results (find); - + g_mutex_unlock (EV_DOC_MUTEX); +#endif + for (i = 0; i < results; i++) { GdkRectangle rectangle; guchar alpha; alpha = (i == view->find_result) ? 0x90 : 0x20; + g_mutex_lock (EV_DOC_MUTEX); ev_document_find_get_result (find, i, &rectangle); + g_mutex_unlock (EV_DOC_MUTEX); draw_rubberband (GTK_WIDGET (view), view->bin_window, &rectangle, alpha); } @@ -471,13 +498,18 @@ expose_bin_window (GtkWidget *widget, gint width, height; GdkRectangle area; int x_offset, y_offset; + GdkPixbuf *scaled_image; + GdkPixbuf *current_pixbuf; if (view->document == NULL) return; ev_view_get_offsets (view, &x_offset, &y_offset); - ev_document_get_page_size (view->document, -1, - &width, &height); + ev_page_cache_get_size (view->page_cache, + view->current_page, + view->scale, + &width, &height); + ev_document_misc_get_page_border_size (width, height, &border); /* Paint the frame */ @@ -488,18 +520,34 @@ expose_bin_window (GtkWidget *widget, ev_document_misc_paint_one_page (view->bin_window, widget, &area, &border); /* Render the document itself */ - ev_document_set_page_offset (view->document, - x_offset + border.left, - y_offset + border.top); - LOG ("Render area %d %d %d %d - Offset %d %d", event->area.x, event->area.y, event->area.width, event->area.height, x_offset, y_offset); - ev_document_render (view->document, - event->area.x, event->area.y, - event->area.width, event->area.height); + current_pixbuf = ev_pixbuf_cache_get_pixbuf (view->pixbuf_cache, view->current_page); + + if (current_pixbuf == NULL) + scaled_image = NULL; + else if (width == gdk_pixbuf_get_width (current_pixbuf) && + height == gdk_pixbuf_get_height (current_pixbuf)) + scaled_image = g_object_ref (current_pixbuf); + else + scaled_image = gdk_pixbuf_scale_simple (current_pixbuf, + width, height, + GDK_INTERP_NEAREST); + if (scaled_image) { + gdk_draw_pixbuf (view->bin_window, + GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL], + scaled_image, + 0, 0, + area.x + border.left, + area.y + border.top, + width, height, + GDK_RGB_DITHER_NORMAL, + 0, 0); + g_object_unref (scaled_image); + } if (EV_IS_DOCUMENT_FIND (view->document)) { highlight_find_results (view); @@ -541,8 +589,12 @@ ev_view_select_all (EvView *ev_view) g_return_if_fail (EV_IS_VIEW (ev_view)); + ev_view_get_offsets (ev_view, &x_offset, &y_offset); - ev_document_get_page_size (ev_view->document, -1, &width, &height); + ev_page_cache_get_size (ev_view->page_cache, + ev_view->current_page, + ev_view->scale, + &width, &height); ev_document_misc_get_page_border_size (width, height, &border); ev_view->has_selection = TRUE; @@ -563,7 +615,10 @@ ev_view_copy (EvView *ev_view) char *text; doc_rect_to_view_rect (ev_view, &ev_view->selection, &selection); + g_mutex_lock (EV_DOC_MUTEX); text = ev_document_get_text (ev_view->document, &selection); + g_mutex_unlock (EV_DOC_MUTEX); + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view), GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text (clipboard, text, -1); @@ -581,7 +636,9 @@ ev_view_primary_get_cb (GtkClipboard *clipboard, char *text; doc_rect_to_view_rect (ev_view, &ev_view->selection, &selection); + g_mutex_lock (EV_DOC_MUTEX); text = ev_document_get_text (ev_view->document, &selection); + g_mutex_unlock (EV_DOC_MUTEX); gtk_selection_data_set_text (selection_data, text, -1); } @@ -697,7 +754,7 @@ ev_view_create_invisible_cursor(void) { GdkBitmap *empty; GdkColor black = { 0, 0, 0, 0 }; - static unsigned char bits[] = { 0x00 }; + static char bits[] = { 0x00 }; empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1); @@ -759,10 +816,13 @@ ev_view_motion_notify_event (GtkWidget *widget, view_rect_to_doc_rect (view, &selection, &view->selection); gtk_widget_queue_draw (widget); - } else if (view->document) { + } else if (FALSE && view->document) { EvLink *link; + g_mutex_lock (EV_DOC_MUTEX); link = ev_document_get_link (view->document, event->x, event->y); + g_mutex_unlock (EV_DOC_MUTEX); + if (link) { char *msg; @@ -796,9 +856,11 @@ ev_view_button_release_event (GtkWidget *widget, } else if (view->document) { EvLink *link; + g_mutex_lock (EV_DOC_MUTEX); link = ev_document_get_link (view->document, event->x, event->y); + g_mutex_unlock (EV_DOC_MUTEX); if (link) { ev_view_go_to_link (view, link); g_object_unref (link); @@ -881,9 +943,9 @@ ev_view_scroll_view (EvView *view, gboolean horizontal) { if (scroll == GTK_SCROLL_PAGE_BACKWARD) { - ev_view_set_page (view, ev_view_get_page (view) - 1); + ev_page_cache_prev_page (view->page_cache); } else if (scroll == GTK_SCROLL_PAGE_FORWARD) { - ev_view_set_page (view, ev_view_get_page (view) + 1); + ev_page_cache_next_page (view->page_cache); } else { GtkAdjustment *adjustment; double value; @@ -982,13 +1044,6 @@ ev_view_class_init (EvViewClass *class) G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); - page_changed_signal = g_signal_new ("page-changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (EvViewClass, page_changed), - NULL, NULL, - ev_marshal_VOID__NONE, - G_TYPE_NONE, 0); g_signal_new ("scroll_view", G_TYPE_FROM_CLASS (object_class), @@ -1034,6 +1089,7 @@ ev_view_init (EvView *view) view->spacing = 10; view->scale = 1.0; + view->current_page = 1; view->pressed_button = -1; view->cursor = EV_VIEW_CURSOR_NORMAL; } @@ -1043,12 +1099,14 @@ update_find_status_message (EvView *view) { char *message; +// g_mutex_lock (EV_DOC_MUTEX); if (ev_document_get_page (view->document) == view->find_page) { int results; +// g_mutex_lock (EV_DOC_MUTEX); results = ev_document_find_get_n_results (EV_DOCUMENT_FIND (view->document)); - +// g_mutex_unlock (EV_DOC_MUTEX); /* TRANS: Sometimes this could be better translated as "%d hit(s) on this page". Therefore this string contains plural cases. */ @@ -1058,10 +1116,11 @@ update_find_status_message (EvView *view) results); } else { double percent; - + + g_mutex_lock (EV_DOC_MUTEX); percent = ev_document_find_get_progress (EV_DOCUMENT_FIND (view->document)); - + g_mutex_unlock (EV_DOC_MUTEX); if (percent >= (1.0 - 1e-10)) { message = g_strdup (_("Not found")); } else { @@ -1070,57 +1129,10 @@ update_find_status_message (EvView *view) } } +// g_mutex_unlock (EV_DOC_MUTEX); ev_view_set_find_status (view, message); - g_free (message); -} - -static void -set_document_page (EvView *view, int new_page) -{ - int page; - int pages; - - pages = ev_document_get_n_pages (view->document); - page = CLAMP (new_page, 1, pages); - - if (view->document) { - int old_page = ev_document_get_page (view->document); - int old_width, old_height; - - ev_document_get_page_size (view->document, - -1, - &old_width, &old_height); - - if (old_page != page) { - if (view->cursor != EV_VIEW_CURSOR_HIDDEN) { - ev_view_set_cursor (view, EV_VIEW_CURSOR_WAIT); - } - ev_document_set_page (view->document, page); - } - - if (old_page != ev_document_get_page (view->document)) { - int width, height; - - g_signal_emit (view, page_changed_signal, 0); - - view->has_selection = FALSE; - ev_document_get_page_size (view->document, - -1, - &width, &height); - if (width != old_width || height != old_height) - gtk_widget_queue_resize (GTK_WIDGET (view)); - - gtk_adjustment_set_value (view->vadjustment, - view->vadjustment->lower); - } - - if (EV_IS_DOCUMENT_FIND (view->document)) { - view->find_page = page; - view->find_result = 0; - update_find_status_message (view); - } - } +// g_free (message); } #define MARGIN 5 @@ -1164,11 +1176,16 @@ jump_to_find_result (EvView *view) GdkRectangle rect; int n_results; + g_mutex_lock (EV_DOC_MUTEX); n_results = ev_document_find_get_n_results (find); + g_mutex_unlock (EV_DOC_MUTEX); if (n_results > view->find_result) { + g_mutex_lock (EV_DOC_MUTEX); ev_document_find_get_result (find, view->find_result, &rect); + g_mutex_unlock (EV_DOC_MUTEX); + ensure_rectangle_is_visible (view, &rect); } } @@ -1178,7 +1195,7 @@ jump_to_find_page (EvView *view) { int n_pages, i; - n_pages = ev_document_get_n_pages (view->document); + n_pages = ev_page_cache_get_n_pages (view->page_cache); for (i = 0; i <= n_pages; i++) { int has_results; @@ -1189,13 +1206,14 @@ jump_to_find_page (EvView *view) page = page - n_pages; } + g_mutex_lock (EV_DOC_MUTEX); has_results = ev_document_find_page_has_results (EV_DOCUMENT_FIND (view->document), page); if (has_results == -1) { view->find_page = page; break; } else if (has_results == 1) { - set_document_page (view, page); + ev_page_cache_set_current_page (view->page_cache, page); jump_to_find_result (view); break; } @@ -1209,39 +1227,75 @@ find_changed_cb (EvDocument *document, int page, EvView *view) jump_to_find_result (view); update_find_status_message (view); +#if 0 + /* FIXME: */ if (ev_document_get_page (document) == page) { gtk_widget_queue_draw (GTK_WIDGET (view)); } +#endif +} +/*** Public API ***/ + +GtkWidget* +ev_view_new (void) +{ + return g_object_new (EV_TYPE_VIEW, NULL); } static void -page_changed_callback (EvDocument *document, - EvView *view) +job_finished_cb (EvPixbufCache *pixbuf_cache, + EvView *view) { - LOG ("Page changed callback"); - gtk_widget_queue_draw (GTK_WIDGET (view)); - - if (view->cursor != EV_VIEW_CURSOR_HIDDEN) { - ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); - } } + static void -scale_changed_callback (EvDocument *document, - EvView *view) +page_changed_cb (EvPageCache *page_cache, + int new_page, + EvView *view) { - LOG ("Scale changed callback"); + int old_page = view->current_page; + int old_width, old_height; + int new_width, new_height; - gtk_widget_queue_resize (GTK_WIDGET (view)); -} + if (old_page == new_page) + return; -/*** Public API ***/ - -GtkWidget* -ev_view_new (void) -{ - return g_object_new (EV_TYPE_VIEW, NULL); + ev_page_cache_get_size (page_cache, + old_page, + view->scale, + &old_width, &old_height); + ev_page_cache_get_size (page_cache, + new_page, + view->scale, + &new_width, &new_height); + + if (view->cursor != EV_VIEW_CURSOR_HIDDEN) { + //ev_view_set_cursor (view, EV_VIEW_CURSOR_WAIT); + } + + view->current_page = new_page; + view->has_selection = FALSE; + + ev_pixbuf_cache_set_page_range (view->pixbuf_cache, + view->current_page, + view->current_page, + view->scale); + + if (new_width != old_width || new_height != old_height) + gtk_widget_queue_resize (GTK_WIDGET (view)); + else + gtk_widget_queue_draw (GTK_WIDGET (view)); + + gtk_adjustment_set_value (view->vadjustment, + view->vadjustment->lower); + + if (EV_IS_DOCUMENT_FIND (view->document)) { + view->find_page = new_page; + view->find_result = 0; + update_find_status_message (view); + } } void @@ -1256,6 +1310,8 @@ ev_view_set_document (EvView *view, find_changed_cb, view); g_object_unref (view->document); + view->page_cache = NULL; + } view->document = document; @@ -1270,22 +1326,13 @@ ev_view_set_document (EvView *view, G_CALLBACK (find_changed_cb), view); } - g_signal_connect (view->document, - "page_changed", - G_CALLBACK (page_changed_callback), - view); - g_signal_connect (view->document, - "scale_changed", - G_CALLBACK (scale_changed_callback), - view); + view->page_cache = ev_document_get_page_cache (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); + g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view); } - - if (GTK_WIDGET_REALIZED (view)) - ev_document_set_target (view->document, view->bin_window); gtk_widget_queue_resize (GTK_WIDGET (view)); - - g_signal_emit (view, page_changed_signal, 0); } } @@ -1303,7 +1350,7 @@ go_to_link (EvView *view, EvLink *link) break; case EV_LINK_TYPE_PAGE: page = ev_link_get_page (link); - set_document_page (view, page); + ev_page_cache_set_current_page (view->page_cache, page); break; case EV_LINK_TYPE_EXTERNAL_URI: uri = ev_link_get_uri (link); @@ -1318,24 +1365,6 @@ ev_view_go_to_link (EvView *view, EvLink *link) go_to_link (view, link); } -void -ev_view_set_page (EvView *view, - int page) -{ - g_return_if_fail (EV_IS_VIEW (view)); - - set_document_page (view, page); -} - -int -ev_view_get_page (EvView *view) -{ - if (view->document) - return ev_document_get_page (view->document); - else - return 1; -} - static void ev_view_zoom (EvView *view, double factor, @@ -1351,8 +1380,7 @@ ev_view_zoom (EvView *view, scale = CLAMP (scale, MIN_SCALE, MAX_SCALE); view->scale = scale; - - ev_document_set_scale (view->document, view->scale); + gtk_widget_queue_resize (GTK_WIDGET (view)); } void @@ -1378,7 +1406,12 @@ size_to_zoom_factor (EvView *view, int width, int height) doc_width = doc_height = 0; scale = scale_w = scale_h = 1.0; - ev_document_get_page_size (view->document, -1, &doc_width, &doc_height); + ev_page_cache_get_size (view->page_cache, + view->current_page, + view->scale, + &doc_width, + &doc_height); + /* FIXME: The border size isn't constant. Ugh. Still, if we have extra * space, we just cut it from the border */ ev_document_misc_get_page_border_size (doc_width, doc_height, &border); @@ -1415,16 +1448,16 @@ ev_view_set_size (EvView *view, { double factor; - if (!view->document) { + if (!view->document) return; - } if (view->width != width || view->height != height) { view->width = width; view->height = height; factor = size_to_zoom_factor (view, width, height); - ev_view_zoom (view, factor, FALSE); + ev_view_zoom (view, factor, FALSE); + gtk_widget_queue_resize (GTK_WIDGET (view)); } } @@ -1447,11 +1480,16 @@ ev_view_get_find_status (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_document_get_page_cache (view->document); + g_mutex_lock (EV_DOC_MUTEX); n_results = ev_document_find_get_n_results (find); - n_pages = ev_document_get_n_pages (view->document); + g_mutex_unlock (EV_DOC_MUTEX); + + n_pages = ev_page_cache_get_n_pages (page_cache); view->find_result++; @@ -1475,9 +1513,15 @@ ev_view_find_previous (EvView *view) { int n_results, n_pages; EvDocumentFind *find = EV_DOCUMENT_FIND (view->document); + EvPageCache *page_cache; + + page_cache = ev_document_get_page_cache (view->document); + g_mutex_lock (EV_DOC_MUTEX); n_results = ev_document_find_get_n_results (find); - n_pages = ev_document_get_n_pages (view->document); + g_mutex_unlock (EV_DOC_MUTEX); + + n_pages = ev_page_cache_get_n_pages (page_cache); view->find_result--; diff --git a/shell/ev-view.h b/shell/ev-view.h index cc90fb8..00b92ea 100644 --- a/shell/ev-view.h +++ b/shell/ev-view.h @@ -44,15 +44,8 @@ void ev_view_copy (EvView *view); void ev_view_select_all (EvView *view); /* Navigation */ -gboolean ev_view_can_go_back (EvView *view); -void ev_view_go_back (EvView *view); -gboolean ev_view_can_go_forward (EvView *view); -void ev_view_go_forward (EvView *view); void ev_view_go_to_link (EvView *view, EvLink *link); -void ev_view_set_page (EvView *view, - int page); -int ev_view_get_page (EvView *view); /* Page size */ void ev_view_zoom_in (EvView *view); diff --git a/shell/ev-window.c b/shell/ev-window.c index f1c41bb..849adaf 100644 --- a/shell/ev-window.c +++ b/shell/ev-window.c @@ -41,6 +41,7 @@ #include "ev-document-links.h" #include "ev-document-find.h" #include "ev-document-security.h" +#include "ev-job-queue.h" #include "eggfindbar.h" #include "pdf-document.h" @@ -106,6 +107,7 @@ struct _EvWindowPrivate { char *uri; EvDocument *document; + EvPageCache *page_cache; EvWindowPageMode page_mode; /* These members are used temporarily when in PAGE_MODE_PASSWORD */ @@ -168,7 +170,6 @@ update_action_sensitivity (EvWindow *ev_window) document = ev_window->priv->document; page_mode = ev_window->priv->page_mode; - view = EV_VIEW (ev_window->priv->view); /* File menu */ @@ -198,9 +199,8 @@ update_action_sensitivity (EvWindow *ev_window) if (document) { int n_pages; int page; - - page = ev_view_get_page (EV_VIEW (ev_window->priv->view)); - n_pages = ev_document_get_n_pages (document); + page = ev_page_cache_get_current_page (ev_window->priv->page_cache); + n_pages = ev_page_cache_get_n_pages (ev_window->priv->page_cache); set_action_sensitive (ev_window, "GoPreviousPage", page > 1); set_action_sensitive (ev_window, "GoNextPage", page < n_pages); @@ -347,7 +347,8 @@ update_sizing_buttons (EvWindow *window) void ev_window_open_page (EvWindow *ev_window, int page) { - ev_view_set_page (EV_VIEW (ev_window->priv->view), page); + if (ev_window->priv->page_cache) + ev_page_cache_set_current_page (ev_window->priv->page_cache, page); } void @@ -427,9 +428,8 @@ update_window_title (EvDocument *document, GParamSpec *pspec, EvWindow *ev_windo gboolean password_needed; password_needed = (ev_window->priv->password_document != NULL); - if (document) { - doc_title = ev_document_get_title (document); + doc_title = ev_page_cache_get_title (ev_window->priv->page_cache); /* Make sure we get a valid title back */ if (doc_title) { @@ -485,9 +485,8 @@ update_total_pages (EvWindow *ev_window) GtkAction *action; int pages; - pages = ev_document_get_n_pages (ev_window->priv->document); - action = gtk_action_group_get_action - (ev_window->priv->action_group, PAGE_SELECTOR_ACTION); + pages = ev_page_cache_get_n_pages (ev_window->priv->page_cache); + action = gtk_action_group_get_action (ev_window->priv->action_group, PAGE_SELECTOR_ACTION); ev_page_action_set_total_pages (EV_PAGE_ACTION (action), pages); } @@ -515,6 +514,21 @@ hide_sidebar_and_actions (EvWindow *ev_window) } static void +page_changed_cb (EvPageCache *page_cache, + gint page, + EvWindow *ev_window) +{ + GtkAction *action; + + action = gtk_action_group_get_action + (ev_window->priv->action_group, PAGE_SELECTOR_ACTION); + + ev_page_action_set_current_page (EV_PAGE_ACTION (action), page); + update_action_sensitivity (ev_window); +} + + +static void ev_window_setup_document (EvWindow *ev_window) { EvDocument *document; @@ -522,6 +536,8 @@ ev_window_setup_document (EvWindow *ev_window) EvSidebar *sidebar = EV_SIDEBAR (ev_window->priv->sidebar); document = ev_window->priv->document; + ev_window->priv->page_cache = ev_document_get_page_cache (ev_window->priv->document); + g_signal_connect (ev_window->priv->page_cache, "page-changed", G_CALLBACK (page_changed_cb), ev_window); g_signal_connect_object (G_OBJECT (document), "notify::title", @@ -534,6 +550,7 @@ ev_window_setup_document (EvWindow *ev_window) ev_sidebar_set_document (sidebar, document); else hide_sidebar_and_actions (ev_window); + ev_view_set_document (view, document); update_window_title (document, NULL, ev_window); @@ -553,9 +570,12 @@ password_dialog_response (GtkWidget *password_dialog, gchar *uri; password = ev_password_dialog_get_password (password_dialog); - if (password) + if (password) { + g_mutex_lock (EV_DOC_MUTEX); ev_document_security_set_password (EV_DOCUMENT_SECURITY (ev_window->priv->password_document), password); + g_mutex_unlock (EV_DOC_MUTEX); + } g_free (password); document = ev_window->priv->password_document; @@ -856,6 +876,7 @@ ev_window_cmd_save_as (GtkAction *action, EvWindow *ev_window) GtkWidget *fc; GtkFileFilter *pdf_filter, *all_filter; gchar *uri = NULL; + gboolean success; fc = gtk_file_chooser_dialog_new ( _("Save a Copy"), @@ -888,8 +909,12 @@ ev_window_cmd_save_as (GtkAction *action, EvWindow *ev_window) !overwrite_existing_file (GTK_WINDOW (fc), uri)) continue; */ + + g_mutex_lock (EV_DOC_MUTEX); + success = ev_document_save (ev_window->priv->document, uri, NULL); + g_mutex_unlock (EV_DOC_MUTEX); - if (ev_document_save (ev_window->priv->document, uri, NULL)) + if (success) break; else save_error_dialog (GTK_WINDOW (fc), uri); @@ -936,7 +961,7 @@ ev_window_print (EvWindow *ev_window) config = gnome_print_config_default (); job = gnome_print_job_new (config); - print_dialog = gnome_print_dialog_new (job, _("Print"), + print_dialog = gnome_print_dialog_new (job, (guchar *) _("Print"), (GNOME_PRINT_DIALOG_RANGE | GNOME_PRINT_DIALOG_COPIES)); gtk_dialog_set_response_sensitive (GTK_DIALOG (print_dialog), @@ -964,7 +989,7 @@ ev_window_print (EvWindow *ev_window) GTK_MESSAGE_DIALOG (dialog), _("You were trying to print to a printer using the \"%s\" driver. This program requires a PostScript printer driver."), gnome_print_config_get ( - config, "Settings.Engine.Backend.Driver")); + config, (guchar *)"Settings.Engine.Backend.Driver")); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); @@ -1465,8 +1490,7 @@ ev_window_cmd_go_previous_page (GtkAction *action, EvWindow *ev_window) { g_return_if_fail (EV_IS_WINDOW (ev_window)); - ev_view_set_page (EV_VIEW (ev_window->priv->view), - ev_view_get_page (EV_VIEW (ev_window->priv->view)) - 1); + ev_page_cache_prev_page (ev_window->priv->page_cache); } static void @@ -1474,8 +1498,7 @@ ev_window_cmd_go_next_page (GtkAction *action, EvWindow *ev_window) { g_return_if_fail (EV_IS_WINDOW (ev_window)); - ev_view_set_page (EV_VIEW (ev_window->priv->view), - ev_view_get_page (EV_VIEW (ev_window->priv->view)) + 1); + ev_page_cache_next_page (ev_window->priv->page_cache); } static void @@ -1483,15 +1506,18 @@ ev_window_cmd_go_first_page (GtkAction *action, EvWindow *ev_window) { g_return_if_fail (EV_IS_WINDOW (ev_window)); - ev_view_set_page (EV_VIEW (ev_window->priv->view), 1); + ev_page_cache_set_current_page (ev_window->priv->page_cache, 1); } static void ev_window_cmd_go_last_page (GtkAction *action, EvWindow *ev_window) { + int n_pages; + g_return_if_fail (EV_IS_WINDOW (ev_window)); - ev_view_set_page (EV_VIEW (ev_window->priv->view), G_MAXINT); + n_pages = ev_page_cache_get_n_pages (ev_window->priv->page_cache); + ev_page_cache_set_current_page (ev_window->priv->page_cache, n_pages); } static void @@ -1502,7 +1528,12 @@ ev_window_cmd_view_reload (GtkAction *action, EvWindow *ev_window) g_return_if_fail (EV_IS_WINDOW (ev_window)); - page = ev_document_get_page (ev_window->priv->document); +#if 0 + /* FIXME: uncomment when this is written.*/ + page = ev_page_cache_get_page (ev_window->priv->page_cache); +#else + page = 1; +#endif uri = g_strdup (ev_window->priv->uri); ev_window_open (ev_window, uri); @@ -1767,32 +1798,6 @@ disconnect_proxy_cb (GtkUIManager *ui_manager, GtkAction *action, } static void -update_current_page (EvWindow *ev_window, - EvView *view) -{ - int page; - GtkAction *action; - EvSidebarThumbnails *thumbs; - - thumbs = EV_SIDEBAR_THUMBNAILS (ev_window->priv->thumbs_sidebar); - ev_sidebar_thumbnails_select_page (thumbs, ev_view_get_page (view)); - - action = gtk_action_group_get_action - (ev_window->priv->action_group, PAGE_SELECTOR_ACTION); - - page = ev_view_get_page (EV_VIEW (ev_window->priv->view)); - ev_page_action_set_current_page (EV_PAGE_ACTION (action), page); -} - -static void -view_page_changed_cb (EvView *view, - EvWindow *ev_window) -{ - update_current_page (ev_window, view); - update_action_sensitivity (ev_window); -} - -static void view_status_changed_cb (EvView *view, GParamSpec *pspec, EvWindow *ev_window) @@ -1885,9 +1890,14 @@ find_bar_search_changed_cb (EggFindBar *find_bar, if (ev_window->priv->document && EV_IS_DOCUMENT_FIND (ev_window->priv->document)) { if (visible && search_string) { + g_mutex_lock (EV_DOC_MUTEX); ev_document_find_begin (EV_DOCUMENT_FIND (ev_window->priv->document), search_string, case_sensitive); + g_mutex_unlock (EV_DOC_MUTEX); } else { + g_mutex_lock (EV_DOC_MUTEX); ev_document_find_cancel (EV_DOCUMENT_FIND (ev_window->priv->document)); + g_mutex_unlock (EV_DOC_MUTEX); + egg_find_bar_set_status_text (EGG_FIND_BAR (ev_window->priv->find_bar), NULL); gtk_widget_queue_draw (GTK_WIDGET (ev_window->priv->view)); @@ -2066,11 +2076,8 @@ static GtkRadioActionEntry page_view_entries[] = { static void goto_page_cb (GtkAction *action, int page_number, EvWindow *ev_window) { - EvView *view = EV_VIEW (ev_window->priv->view); - - if (ev_view_get_page (view) != page_number) { - ev_view_set_page (view, page_number); - } + ev_page_cache_set_current_page (ev_window->priv->page_cache, + page_number); } static void @@ -2353,10 +2360,6 @@ ev_window_init (EvWindow *ev_window) gtk_container_add (GTK_CONTAINER (ev_window->priv->scrolled_window), ev_window->priv->view); g_signal_connect (ev_window->priv->view, - "page-changed", - G_CALLBACK (view_page_changed_cb), - ev_window); - g_signal_connect (ev_window->priv->view, "notify::find-status", G_CALLBACK (view_find_status_changed_cb), ev_window); diff --git a/shell/main.c b/shell/main.c index 86450d4..6cd14e8 100644 --- a/shell/main.c +++ b/shell/main.c @@ -30,6 +30,7 @@ #include "ev-stock-icons.h" #include "ev-debug.h" +#include "ev-job-queue.h" static struct poptOption popt_options[] = { @@ -82,6 +83,7 @@ main (int argc, char *argv[]) GNOME_PARAM_APP_DATADIR, DATADIR, NULL); + ev_job_queue_init (); g_set_application_name (_("Evince Document Viewer")); ev_debug_init (); -- cgit v0.9.1