]> www.fi.muni.cz Git - evince.git/blobdiff - shell/ev-sidebar-thumbnails.c
A bit different fix for rounding problem but it has no
[evince.git] / shell / ev-sidebar-thumbnails.c
index b439e4bf7f78337cb6bed7c5c27cd4f12c9c2eb3..7f84be12361eb2040f4b5ebc1fccba21e0c7dd70 100644 (file)
@@ -28,7 +28,9 @@
 
 #include <string.h>
 #include <gtk/gtk.h>
+#include <glib/gi18n.h>
 
+#include "ev-sidebar-page.h"
 #include "ev-sidebar-thumbnails.h"
 #include "ev-document-thumbnails.h"
 #include "ev-document-misc.h"
@@ -45,9 +47,13 @@ struct _EvSidebarThumbnailsPrivate {
        GtkWidget *tree_view;
        GtkAdjustment *vadjustment;
        GtkListStore *list_store;
+       GdkPixbuf *loading_icon;
        EvDocument *document;
 
        gint n_pages, pages_done;
+
+       /* Visible pages */
+       gint start_page, end_page;
 };
 
 enum {
@@ -58,11 +64,38 @@ enum {
        NUM_COLUMNS
 };
 
-G_DEFINE_TYPE (EvSidebarThumbnails, ev_sidebar_thumbnails, GTK_TYPE_VBOX);
+static void         ev_sidebar_thumbnails_clear_model      (EvSidebarThumbnails *sidebar);
+static gboolean     ev_sidebar_thumbnails_support_document (EvSidebarPage       *sidebar_page,
+                                                           EvDocument          *document);
+static void         ev_sidebar_thumbnails_page_iface_init  (EvSidebarPageIface  *iface);
+static void         ev_sidebar_thumbnails_set_document     (EvSidebarPage       *sidebar_page,
+                                                           EvDocument          *document);
+static const gchar* ev_sidebar_thumbnails_get_label        (EvSidebarPage       *sidebar_page);
+static void         thumbnail_job_completed_callback       (EvJobThumbnail      *job,
+                                                           EvSidebarThumbnails *sidebar_thumbnails);
+
+G_DEFINE_TYPE_EXTENDED (EvSidebarThumbnails, 
+                        ev_sidebar_thumbnails, 
+                        GTK_TYPE_VBOX,
+                        0, 
+                        G_IMPLEMENT_INTERFACE (EV_TYPE_SIDEBAR_PAGE, 
+                                              ev_sidebar_thumbnails_page_iface_init))
 
 #define EV_SIDEBAR_THUMBNAILS_GET_PRIVATE(object) \
        (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_THUMBNAILS, EvSidebarThumbnailsPrivate));
 
+
+static void
+ev_sidebar_thumbnails_dispose (GObject *object)
+{
+       EvSidebarThumbnails *sidebar_thumbnails = EV_SIDEBAR_THUMBNAILS (object);
+       
+       ev_sidebar_thumbnails_clear_model (sidebar_thumbnails);
+       g_object_unref (sidebar_thumbnails->priv->loading_icon);
+
+       G_OBJECT_CLASS (ev_sidebar_thumbnails_parent_class)->dispose (object);
+}
+
 static void
 ev_sidebar_thumbnails_class_init (EvSidebarThumbnailsClass *ev_sidebar_thumbnails_class)
 {
@@ -72,33 +105,177 @@ 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);
 
+       g_object_class->dispose = ev_sidebar_thumbnails_dispose;
+
        g_type_class_add_private (g_object_class, sizeof (EvSidebarThumbnailsPrivate));
 }
 
+GtkWidget *
+ev_sidebar_thumbnails_new (void)
+{
+       GtkWidget *ev_sidebar_thumbnails;
+
+       ev_sidebar_thumbnails = g_object_new (EV_TYPE_SIDEBAR_THUMBNAILS, NULL);
+
+       return ev_sidebar_thumbnails;
+}
+
 static void
-adjustment_changed_cb (GtkAdjustment       *adjustment,
-                      EvSidebarThumbnails *ev_sidebar_thumbnails)
+clear_range (EvSidebarThumbnails *sidebar_thumbnails,
+            gint                 start_page,
+            gint                 end_page)
 {
        EvSidebarThumbnailsPrivate *priv;
        GtkTreePath *path;
        GtkTreeIter iter;
-       int page;
-       gboolean thumbnail_set;
+       gboolean result;
 
-       priv = ev_sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (ev_sidebar_thumbnails);
+       priv = sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (sidebar_thumbnails);
+
+       g_assert (start_page <= end_page);
+
+       path = gtk_tree_path_new_from_indices (start_page, -1);
+       for (result = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->list_store), &iter, path);
+            result && start_page <= end_page;
+            result = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->list_store), &iter), start_page ++) {
+               EvJobThumbnail *job;
+
+               gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store),
+                                   &iter,
+                                   COLUMN_JOB, &job,
+                                   -1);
+
+               if (job) {
+                       g_signal_handlers_disconnect_by_func (job, thumbnail_job_completed_callback, sidebar_thumbnails);
+                       ev_job_queue_remove_job (EV_JOB (job));
+                       g_object_unref (job);
+               }
+
+               gtk_list_store_set (priv->list_store, &iter,
+                                   COLUMN_JOB, NULL,
+                                   COLUMN_THUMBNAIL_SET, FALSE,
+                                   COLUMN_PIXBUF, priv->loading_icon,
+                                   -1);
+       }
+       gtk_tree_path_free (path);
+}
+
+static void
+add_range (EvSidebarThumbnails *sidebar_thumbnails,
+          gint                 start_page,
+          gint                 end_page)
+{
+       EvSidebarThumbnailsPrivate *priv;
+       GtkTreePath *path;
+       GtkTreeIter iter;
+       gboolean result;
+       gint page = start_page;
+
+       priv = sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (sidebar_thumbnails);
+
+       g_assert (start_page <= end_page);
+
+       path = gtk_tree_path_new_from_indices (start_page, -1);
+       for (result = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->list_store), &iter, path);
+            result && page <= end_page;
+            result = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->list_store), &iter), page ++) {
+               EvJobThumbnail *job;
+               gboolean thumbnail_set;
+
+               gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store), &iter,
+                                   COLUMN_JOB, &job,
+                                   COLUMN_THUMBNAIL_SET, &thumbnail_set,
+                                   -1);
+
+               if (job == NULL && !thumbnail_set) {
+                       job = (EvJobThumbnail *)ev_job_thumbnail_new (priv->document, page, THUMBNAIL_WIDTH);
+                       ev_job_queue_add_job (EV_JOB (job), EV_JOB_PRIORITY_HIGH);
+                       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);
+                       gtk_list_store_set (priv->list_store, &iter,
+                                           COLUMN_JOB, job,
+                                           -1);
+                       /* The queue and the list own a ref to the job now */
+                       g_object_unref (job);
+               } else if (job) {
+                       g_object_unref (job);
+               }
+       }
+       gtk_tree_path_free (path);
+}
+
+/* This modifies start */
+static void
+update_visible_range (EvSidebarThumbnails *sidebar_thumbnails,
+                     gint                 start_page,
+                     gint                 end_page)
+{
+       EvSidebarThumbnailsPrivate *priv;
+       int old_start_page, old_end_page;
+
+       priv = sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (sidebar_thumbnails);
+
+       old_start_page = priv->start_page;
+       old_end_page = priv->end_page;
 
+       if (start_page == old_start_page &&
+           end_page == old_end_page)
+               return;
+
+       /* Clear the areas we no longer display */
+       if (old_start_page < start_page)
+               clear_range (sidebar_thumbnails, old_start_page, MIN (start_page - 1, old_end_page));
+
+       if (old_end_page > end_page)
+               clear_range (sidebar_thumbnails, MAX (end_page + 1, old_start_page), old_end_page);
+
+       add_range (sidebar_thumbnails, start_page, end_page);
+       
+       priv->start_page = start_page;
+       priv->end_page = end_page;
+}
+
+static void
+adjustment_changed_cb (EvSidebarThumbnails *sidebar_thumbnails)
+{
+       EvSidebarThumbnailsPrivate *priv;
+       GtkTreePath *path;
+       GtkTreePath *path2;
+       gint wy1;
+       gint wy2;
+
+       priv = sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (sidebar_thumbnails);
+
+       if (! GTK_WIDGET_REALIZED (priv->tree_view))
+               return;
+
+       gtk_tree_view_tree_to_widget_coords (GTK_TREE_VIEW (priv->tree_view),
+                                            0, (int) priv->vadjustment->value,
+                                            NULL, &wy1);
+       gtk_tree_view_tree_to_widget_coords (GTK_TREE_VIEW (priv->tree_view),
+                                            0, (int) (priv->vadjustment->value + priv->vadjustment->page_size),
+                                            NULL, &wy2);
        gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
-                                      1, 1, &path,
+                                      1, wy1 + 1, &path,
                                       NULL, NULL, NULL);
-       if (!path)
-               return;
+       gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
+                                      1, wy2 -1, &path2,
+                                      NULL, NULL, NULL);
+       if (path == NULL)
+               path = gtk_tree_path_new_first ();
+       if (path2 == NULL)
+               path2 = gtk_tree_path_new_from_indices (priv->n_pages,
+                                                       -1);
+       update_visible_range (sidebar_thumbnails,
+                             gtk_tree_path_get_indices (path)[0],
+                             gtk_tree_path_get_indices (path2)[0]);
 
-       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);
+       gtk_tree_path_free (path);
+       gtk_tree_path_free (path2);
 }
 
 static void
@@ -125,6 +302,12 @@ ev_sidebar_tree_selection_changed (GtkTreeSelection *selection,
        ev_page_cache_set_current_page (page_cache, page);
 }
 
+GtkWidget *
+ev_sidebar_thumbnails_get_treeview (EvSidebarThumbnails *sidebar)
+{
+       return sidebar->priv->tree_view;
+}
+
 static void
 ev_sidebar_thumbnails_init (EvSidebarThumbnails *ev_sidebar_thumbnails)
 {
@@ -162,40 +345,30 @@ ev_sidebar_thumbnails_init (EvSidebarThumbnails *ev_sidebar_thumbnails)
        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
                                             GTK_SHADOW_IN);
        priv->vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (swindow));
-       g_signal_connect (G_OBJECT (priv->vadjustment), "value-changed",
-                         G_CALLBACK (adjustment_changed_cb),
-                         ev_sidebar_thumbnails);
+       g_signal_connect_data (G_OBJECT (priv->vadjustment), "value-changed",
+                              G_CALLBACK (adjustment_changed_cb),
+                              ev_sidebar_thumbnails, NULL,
+                              G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+       g_signal_connect_swapped (G_OBJECT (swindow), "size-allocate",
+                                 G_CALLBACK (adjustment_changed_cb),
+                                 ev_sidebar_thumbnails);
        gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
        gtk_box_pack_start (GTK_BOX (ev_sidebar_thumbnails), swindow, TRUE, TRUE, 0);
 
        gtk_widget_show_all (swindow);
 }
 
-GtkWidget *
-ev_sidebar_thumbnails_new (void)
-{
-       GtkWidget *ev_sidebar_thumbnails;
-
-       ev_sidebar_thumbnails = g_object_new (EV_TYPE_SIDEBAR_THUMBNAILS, NULL);
-
-       return ev_sidebar_thumbnails;
-}
-
 static void
 page_changed_cb (EvPageCache         *page_cache,
                 int                  page,
                 EvSidebarThumbnails *sidebar)
 {
+       GtkTreeView *tree_view = GTK_TREE_VIEW (sidebar->priv->tree_view);
        GtkTreePath *path;
-       GtkTreeSelection *selection;
 
        path = gtk_tree_path_new_from_indices (page, -1);
-       selection = gtk_tree_view_get_selection
-                       (GTK_TREE_VIEW (sidebar->priv->tree_view));
-
-       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_view_set_cursor (tree_view, path, NULL, FALSE);
+       gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
        gtk_tree_path_free (path);
 }
 
@@ -215,11 +388,11 @@ thumbnail_job_completed_callback (EvJobThumbnail      *job,
                            -1);
 }
 
-void
-ev_sidebar_thumbnails_set_document (EvSidebarThumbnails *sidebar_thumbnails,
+static void
+ev_sidebar_thumbnails_set_document (EvSidebarPage      *sidebar_page,
                                    EvDocument          *document)
 {
-       GdkPixbuf *loading_icon;
+       EvSidebarThumbnails *sidebar_thumbnails = EV_SIDEBAR_THUMBNAILS (sidebar_page);
        gint i, n_pages;
        GtkTreeIter iter;
        gint width = THUMBNAIL_WIDTH;
@@ -238,46 +411,85 @@ ev_sidebar_thumbnails_set_document (EvSidebarThumbnails *sidebar_thumbnails,
 
        /* We get the dimensions of the first doc so that we can make a blank
         * icon.  */
-       g_mutex_lock (EV_DOC_MUTEX);
+       ev_document_doc_mutex_lock ();
        ev_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (priv->document),
-                                              1, THUMBNAIL_WIDTH, &width, &height);
-       g_mutex_unlock (EV_DOC_MUTEX);
+                                              0, THUMBNAIL_WIDTH, &width, &height);
+       ev_document_doc_mutex_unlock ();
 
-       loading_icon = ev_document_misc_get_thumbnail_frame (width, height, NULL);
+       if (priv->loading_icon)
+               g_object_unref (priv->loading_icon);
+       priv->loading_icon = ev_document_misc_get_thumbnail_frame (width, height, NULL);
 
-       gtk_list_store_clear (priv->list_store);
+       ev_sidebar_thumbnails_clear_model (sidebar_thumbnails);
        for (i = 0; i < n_pages; i++) {
-               EvJob *job;
                gchar *page_label;
                gchar *page_string;
 
-               job = ev_job_thumbnail_new (priv->document, i, THUMBNAIL_WIDTH);
                page_label = ev_page_cache_get_page_label (page_cache, i);
                page_string = g_markup_printf_escaped ("<i>%s</i>", page_label);
 
                gtk_list_store_append (priv->list_store, &iter);
                gtk_list_store_set (priv->list_store, &iter,
                                    COLUMN_PAGE_STRING, page_string,
-                                   COLUMN_PIXBUF, loading_icon,
+                                   COLUMN_PIXBUF, priv->loading_icon,
                                    COLUMN_THUMBNAIL_SET, FALSE,
-                                   COLUMN_JOB, job,
                                    -1);
                g_free (page_label);
                g_free (page_string);
-               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);
-
        /* 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);
+       adjustment_changed_cb (sidebar_thumbnails);
+}
 
+static gboolean
+ev_sidebar_thumbnails_clear_job (GtkTreeModel *model,                                             
+                                GtkTreePath *path,                                                                                      
+                                GtkTreeIter *iter,                                                                                                                                   
+                                gpointer data)
+{
+    EvJob *job;
+    
+    gtk_tree_model_get (model, iter, COLUMN_JOB, &job, -1);
+    
+    if (job != NULL) {
+        ev_job_queue_remove_job (job);
+       g_signal_handlers_disconnect_by_func (job, thumbnail_job_completed_callback, data);
+       g_object_unref (job);
+    }
+
+    return FALSE;    
+}
+
+static void 
+ev_sidebar_thumbnails_clear_model (EvSidebarThumbnails *sidebar_thumbnails)
+{
+    EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
+    
+    gtk_tree_model_foreach (GTK_TREE_MODEL (priv->list_store), ev_sidebar_thumbnails_clear_job, sidebar_thumbnails);
+    gtk_list_store_clear (priv->list_store);
+}
+
+static gboolean
+ev_sidebar_thumbnails_support_document (EvSidebarPage   *sidebar_page,
+                                       EvDocument *document)
+{
+       return (EV_IS_DOCUMENT_THUMBNAILS (document) &&
+                   (ev_document_get_n_pages (document) > 1));
+}
+
+static const gchar*
+ev_sidebar_thumbnails_get_label (EvSidebarPage *sidebar_page)
+{
+    return _("Thumbnails");
+}
+
+static void
+ev_sidebar_thumbnails_page_iface_init (EvSidebarPageIface *iface)
+{
+       iface->support_document = ev_sidebar_thumbnails_support_document;
+       iface->set_document = ev_sidebar_thumbnails_set_document;
+       iface->get_label = ev_sidebar_thumbnails_get_label;
 }