]> www.fi.muni.cz Git - evince.git/blob - shell/ev-sidebar-thumbnails.c
fill in the thumbnails starting at the visible page, not just linearly.
[evince.git] / shell / ev-sidebar-thumbnails.c
1 /* this file is part of evince, a gnome document viewer
2  *
3  *  Copyright (C) 2004 Red Hat, Inc.
4  *  Copyright (C) 2004 Anders Carlsson <andersca@gnome.org>
5  *
6  *  Authors:
7  *    Jonathan Blandford <jrb@alum.mit.edu>
8  *    Anders Carlsson <andersca@gnome.org>
9  *
10  * Evince is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * Evince is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <string.h>
30 #include <gtk/gtk.h>
31
32 #include "ev-sidebar-thumbnails.h"
33 #include "ev-document-thumbnails.h"
34 #include "ev-document-misc.h"
35 #include "ev-utils.h"
36
37 #define THUMBNAIL_WIDTH 75
38 /* Amount of time we devote to each iteration of the idle, in microseconds */
39 #define IDLE_WORK_LENGTH 5000
40
41 struct _EvSidebarThumbnailsPrivate {
42         GtkWidget *tree_view;
43         GtkAdjustment *vadjustment;
44         GtkListStore *list_store;
45         EvDocument *document;
46
47         guint idle_id;
48         gint current_page, n_pages, pages_done;
49         GtkTreeIter current_page_iter;
50 };
51
52 enum {
53         COLUMN_PAGE_STRING,
54         COLUMN_PIXBUF,
55         COLUMN_THUMBNAIL_SET,
56         NUM_COLUMNS
57 };
58
59 static GtkVBoxClass *parent_class;
60
61 G_DEFINE_TYPE (EvSidebarThumbnails, ev_sidebar_thumbnails, GTK_TYPE_VBOX);
62
63 #define EV_SIDEBAR_THUMBNAILS_GET_PRIVATE(object) \
64         (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_THUMBNAILS, EvSidebarThumbnailsPrivate));
65
66 static void
67 ev_sidebar_thumbnails_destroy (GtkObject *object)
68 {
69         EvSidebarThumbnails *ev_sidebar_thumbnails = EV_SIDEBAR_THUMBNAILS (object);
70         EvSidebarThumbnailsPrivate *priv = ev_sidebar_thumbnails->priv;
71
72         if (priv->idle_id != 0) {
73                 g_source_remove (priv->idle_id);
74
75                 priv->idle_id = 0;
76         }
77
78         GTK_OBJECT_CLASS (parent_class)->destroy (object);
79 }
80
81 static void
82 ev_sidebar_thumbnails_class_init (EvSidebarThumbnailsClass *ev_sidebar_thumbnails_class)
83 {
84         GObjectClass *g_object_class;
85         GtkObjectClass *gtk_object_class;
86
87         g_object_class = G_OBJECT_CLASS (ev_sidebar_thumbnails_class);
88         gtk_object_class = GTK_OBJECT_CLASS (ev_sidebar_thumbnails_class);
89
90         parent_class = g_type_class_peek_parent (ev_sidebar_thumbnails_class);
91
92         gtk_object_class->destroy = ev_sidebar_thumbnails_destroy;
93
94         g_type_class_add_private (g_object_class, sizeof (EvSidebarThumbnailsPrivate));
95
96 }
97
98 static void
99 adjustment_changed_cb (GtkAdjustment       *adjustment,
100                        EvSidebarThumbnails *ev_sidebar_thumbnails)
101 {
102         EvSidebarThumbnailsPrivate *priv;
103         GtkTreePath *path;
104         GtkTreeIter iter;
105         int page;
106         gboolean thumbnail_set;
107
108         priv = ev_sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (ev_sidebar_thumbnails);
109
110         gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
111                                        1, 1, &path,
112                                        NULL, NULL, NULL);
113         if (!path)
114                 return;
115
116         page = gtk_tree_path_get_indices (path)[0];
117         if (page == priv->current_page)
118                 return;
119         gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->list_store),
120                                  &iter, path);
121         gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store), &iter,
122                             COLUMN_THUMBNAIL_SET, &thumbnail_set,
123                             -1);
124         if (! thumbnail_set) {
125                 priv->current_page = page;
126                 priv->current_page_iter = iter;
127                 
128         }
129 }
130
131 static void
132 ev_sidebar_thumbnails_init (EvSidebarThumbnails *ev_sidebar_thumbnails)
133 {
134         GtkWidget *swindow;
135         EvSidebarThumbnailsPrivate *priv;
136         GtkCellRenderer *renderer;
137
138         priv = ev_sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (ev_sidebar_thumbnails);
139
140         priv->list_store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN);
141         priv->tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->list_store));
142
143         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
144         renderer = g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF,
145                                  "xpad", 2,
146                                  "ypad", 2,
147                                  NULL);
148         gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->tree_view), -1,
149                                                      NULL, renderer,
150                                                      "pixbuf", 1,
151                                                      NULL);
152         gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->tree_view), -1,
153                                                      NULL, gtk_cell_renderer_text_new (),
154                                                      "markup", 0, NULL);
155
156         g_object_unref (priv->list_store);
157
158         swindow = gtk_scrolled_window_new (NULL, NULL);
159         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
160                                         GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
161         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
162                                              GTK_SHADOW_IN);
163         priv->vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (swindow));
164         g_signal_connect (G_OBJECT (priv->vadjustment), "value-changed",
165                           G_CALLBACK (adjustment_changed_cb),
166                           ev_sidebar_thumbnails);
167         gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
168         gtk_box_pack_start (GTK_BOX (ev_sidebar_thumbnails), swindow, TRUE, TRUE, 0);
169
170         gtk_widget_show_all (swindow);
171 }
172
173 GtkWidget *
174 ev_sidebar_thumbnails_new (void)
175 {
176         GtkWidget *ev_sidebar_thumbnails;
177
178         ev_sidebar_thumbnails = g_object_new (EV_TYPE_SIDEBAR_THUMBNAILS, NULL);
179
180         return ev_sidebar_thumbnails;
181 }
182
183 static gboolean
184 do_one_iteration (EvSidebarThumbnails *ev_sidebar_thumbnails)
185 {
186         EvSidebarThumbnailsPrivate *priv = ev_sidebar_thumbnails->priv;
187         GdkPixbuf *pixbuf;
188         gboolean thumbnail_set;
189
190         gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store),
191                             &(priv->current_page_iter),
192                             COLUMN_THUMBNAIL_SET, &thumbnail_set,
193                             -1);
194         if (!thumbnail_set) {
195                 pixbuf = ev_document_thumbnails_get_thumbnail (EV_DOCUMENT_THUMBNAILS (priv->document),
196                                                                priv->current_page, THUMBNAIL_WIDTH);
197
198                 gtk_list_store_set (priv->list_store,
199                                     &(priv->current_page_iter),
200                                     COLUMN_PIXBUF, pixbuf,
201                                     -1);
202
203                 g_object_unref (pixbuf);
204                 priv->pages_done ++;
205         }
206
207         priv->current_page++;
208
209         if (priv->current_page == priv->n_pages) {
210                 priv->current_page = 0;
211                 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store),
212                                                &(priv->current_page_iter));
213         } else {
214                 gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->list_store),
215                                           &(priv->current_page_iter));
216         }
217
218         if (priv->pages_done == priv->n_pages)
219                 return FALSE;
220         else
221                 return TRUE;
222 }
223
224 static gboolean
225 populate_thumbnails_idle (gpointer data)
226 {
227         GTimer *timer;
228         int i;
229         gdouble time_elapsed = 0;
230
231         EvSidebarThumbnails *ev_sidebar_thumbnails = EV_SIDEBAR_THUMBNAILS (data);
232         EvSidebarThumbnailsPrivate *priv = ev_sidebar_thumbnails->priv;
233
234 #if PROFILE_THUMB == 1
235         static GTimer *total_timer;
236         static gboolean first_time = TRUE;
237
238         if (first_time) {
239                 total_timer = g_timer_new ();
240                 first_time = FALSE;
241                 g_timer_start (total_timer);
242         }
243 #endif
244
245         /* undo the thumbnailing idle and handler */
246         if (priv->pages_done == priv->n_pages) {
247                 priv->idle_id = 0;
248                 g_signal_handlers_disconnect_by_func (priv->vadjustment,
249                                                       adjustment_changed_cb,
250                                                       ev_sidebar_thumbnails);
251 #if PROFILE_THUMB == 1
252                 time_elapsed = g_timer_elapsed (total_timer, NULL);
253                 g_timer_destroy (total_timer);
254                 g_print ("%d rows done in %f seconds\n",
255                          gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->list_store), NULL),
256                          time_elapsed);
257 #endif
258                 return FALSE;
259         }
260
261         timer = g_timer_new ();
262         i = 0;
263         g_timer_start (timer);
264         while (do_one_iteration (ev_sidebar_thumbnails)) {
265                 i++;
266                 time_elapsed = g_timer_elapsed (timer, NULL);
267                 if (time_elapsed > IDLE_WORK_LENGTH/1000000)
268                         break;
269         }
270         g_timer_destroy (timer);
271 #if PROFILE_THUMB == 2
272         g_print ("%d rows done this idle in %f seconds\n", i, time_elapsed);
273 #endif
274
275         return TRUE;
276 }
277
278 void
279 ev_sidebar_thumbnails_set_document (EvSidebarThumbnails *sidebar_thumbnails,
280                                     EvDocument          *document)
281 {
282         GdkPixbuf *loading_icon;
283         gint i, n_pages;
284         GtkTreeIter iter;
285         gchar *page;
286         gint width = THUMBNAIL_WIDTH;
287         gint height = THUMBNAIL_WIDTH;
288
289         EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
290
291         g_return_if_fail (EV_IS_DOCUMENT_THUMBNAILS (document));
292
293         if (priv->idle_id != 0) {
294                 g_source_remove (priv->idle_id);
295         }
296         n_pages = ev_document_get_n_pages (document);
297
298         priv->document = document;
299         priv->idle_id = g_idle_add (populate_thumbnails_idle, sidebar_thumbnails);
300         priv->n_pages = n_pages;
301
302         /* We get the dimensions of the first doc so that we can make a blank
303          * icon.  */
304         ev_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (priv->document),
305                                                0, THUMBNAIL_WIDTH, &width, &height);
306         loading_icon = ev_document_misc_get_thumbnail_frame (width, height, NULL);
307
308         for (i = 0; i < n_pages; i++) {
309                 page = g_strdup_printf ("<i>%d</i>", i + 1);
310                 gtk_list_store_append (priv->list_store, &iter);
311                 gtk_list_store_set (priv->list_store, &iter,
312                                     COLUMN_PAGE_STRING, page,
313                                     COLUMN_PIXBUF, loading_icon,
314                                     COLUMN_THUMBNAIL_SET, FALSE,
315                                     -1);
316                 g_free (page);
317         }
318
319         gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store),
320                                        &(priv->current_page_iter));
321         priv->current_page = 0;
322         priv->pages_done = 0;
323 }
324