]> www.fi.muni.cz Git - evince.git/blob - shell/ev-sidebar-thumbnails.c
Fix the mess we did with thumbnails and 1-basedness
[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, 2005 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-window.h"
36 #include "ev-utils.h"
37
38 #define THUMBNAIL_WIDTH 100
39
40 /* Amount of time we devote to each iteration of the idle, in microseconds */
41 #define IDLE_WORK_LENGTH 5000
42
43 struct _EvSidebarThumbnailsPrivate {
44         GtkWidget *tree_view;
45         GtkAdjustment *vadjustment;
46         GtkListStore *list_store;
47         EvDocument *document;
48
49         guint idle_id;
50         gint current_page, n_pages, pages_done;
51         GtkTreeIter current_page_iter;
52 };
53
54 enum {
55         COLUMN_PAGE_STRING,
56         COLUMN_PIXBUF,
57         COLUMN_THUMBNAIL_SET,
58         NUM_COLUMNS
59 };
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 (ev_sidebar_thumbnails_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         gtk_object_class->destroy = ev_sidebar_thumbnails_destroy;
91
92         g_type_class_add_private (g_object_class, sizeof (EvSidebarThumbnailsPrivate));
93
94 }
95
96 static void
97 adjustment_changed_cb (GtkAdjustment       *adjustment,
98                        EvSidebarThumbnails *ev_sidebar_thumbnails)
99 {
100         EvSidebarThumbnailsPrivate *priv;
101         GtkTreePath *path;
102         GtkTreeIter iter;
103         int page;
104         gboolean thumbnail_set;
105
106         priv = ev_sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (ev_sidebar_thumbnails);
107
108         gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
109                                        1, 1, &path,
110                                        NULL, NULL, NULL);
111         if (!path)
112                 return;
113
114         page = gtk_tree_path_get_indices (path)[0] + 1;
115         if (page == priv->current_page)
116                 return;
117         gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->list_store),
118                                  &iter, path);
119         gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store), &iter,
120                             COLUMN_THUMBNAIL_SET, &thumbnail_set,
121                             -1);
122         if (!thumbnail_set) {
123                 priv->current_page = page;
124                 priv->current_page_iter = iter;
125                 
126         }
127 }
128
129 static void
130 ev_sidebar_tree_selection_changed (GtkTreeSelection *selection,
131                                    EvSidebarThumbnails *ev_sidebar_thumbnails)
132 {
133         EvSidebarThumbnailsPrivate *priv;
134         GtkWidget *window;
135         GtkTreePath *path;
136         GtkTreeIter iter;
137         int page;
138
139         priv = ev_sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (ev_sidebar_thumbnails);
140   
141         if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
142                 return;
143         
144         path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->list_store),
145                                         &iter);
146
147         page = gtk_tree_path_get_indices (path)[0] + 1;
148
149         gtk_tree_path_free (path);
150
151         window = gtk_widget_get_ancestor (GTK_WIDGET (ev_sidebar_thumbnails),
152                                           EV_TYPE_WINDOW);
153         if (window && ev_document_get_page (priv->document) != page) {
154                 ev_window_open_page (EV_WINDOW (window), page);
155         }
156 }
157
158 static void
159 ev_sidebar_thumbnails_init (EvSidebarThumbnails *ev_sidebar_thumbnails)
160 {
161         GtkWidget *swindow;
162         EvSidebarThumbnailsPrivate *priv;
163         GtkCellRenderer *renderer;
164         GtkTreeSelection *selection;
165
166         priv = ev_sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (ev_sidebar_thumbnails);
167         
168         priv->list_store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN);
169         priv->tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->list_store));
170         
171         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
172         g_signal_connect (selection, "changed",
173                           G_CALLBACK (ev_sidebar_tree_selection_changed), ev_sidebar_thumbnails);
174         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
175         renderer = g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF,
176                                  "xpad", 2,
177                                  "ypad", 2,
178                                  NULL);
179         gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->tree_view), -1,
180                                                      NULL, renderer,
181                                                      "pixbuf", 1,
182                                                      NULL);
183         gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->tree_view), -1,
184                                                      NULL, gtk_cell_renderer_text_new (),
185                                                      "markup", 0, NULL);
186
187         g_object_unref (priv->list_store);
188
189         swindow = gtk_scrolled_window_new (NULL, NULL);
190         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
191                                         GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
192         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
193                                              GTK_SHADOW_IN);
194         priv->vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (swindow));
195         g_signal_connect (G_OBJECT (priv->vadjustment), "value-changed",
196                           G_CALLBACK (adjustment_changed_cb),
197                           ev_sidebar_thumbnails);
198         gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
199         gtk_box_pack_start (GTK_BOX (ev_sidebar_thumbnails), swindow, TRUE, TRUE, 0);
200
201         gtk_widget_show_all (swindow);
202 }
203
204 GtkWidget *
205 ev_sidebar_thumbnails_new (void)
206 {
207         GtkWidget *ev_sidebar_thumbnails;
208
209         ev_sidebar_thumbnails = g_object_new (EV_TYPE_SIDEBAR_THUMBNAILS, NULL);
210
211         return ev_sidebar_thumbnails;
212 }
213
214 static gboolean
215 do_one_iteration (EvSidebarThumbnails *ev_sidebar_thumbnails)
216 {
217         EvSidebarThumbnailsPrivate *priv = ev_sidebar_thumbnails->priv;
218         GdkPixbuf *pixbuf;
219         gboolean thumbnail_set;
220
221         gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store),
222                             &(priv->current_page_iter),
223                             COLUMN_THUMBNAIL_SET, &thumbnail_set,
224                             -1);
225         if (!thumbnail_set) {
226                 pixbuf = ev_document_thumbnails_get_thumbnail
227                                 (EV_DOCUMENT_THUMBNAILS (priv->document),
228                                  priv->current_page, THUMBNAIL_WIDTH, TRUE);
229
230                 gtk_list_store_set (priv->list_store,
231                                     &(priv->current_page_iter),
232                                     COLUMN_PIXBUF, pixbuf,
233                                     COLUMN_THUMBNAIL_SET, TRUE,
234                                     -1);
235
236                 g_object_unref (pixbuf);
237                 priv->pages_done ++;
238         }
239
240         priv->current_page++;
241
242         if (priv->current_page > priv->n_pages) {
243                 priv->current_page = 1;
244                 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store),
245                                                &(priv->current_page_iter));
246         } else {
247                 gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->list_store),
248                                           &(priv->current_page_iter));
249         }
250
251         if (priv->pages_done == priv->n_pages)
252                 return FALSE;
253         else
254                 return TRUE;
255 }
256
257 static gboolean
258 populate_thumbnails_idle (gpointer data)
259 {
260         GTimer *timer;
261         int i;
262         gdouble time_elapsed = 0;
263
264         EvSidebarThumbnails *ev_sidebar_thumbnails = EV_SIDEBAR_THUMBNAILS (data);
265         EvSidebarThumbnailsPrivate *priv = ev_sidebar_thumbnails->priv;
266
267 #if PROFILE_THUMB == 1
268         static GTimer *total_timer;
269         static gboolean first_time = TRUE;
270
271         if (first_time) {
272                 total_timer = g_timer_new ();
273                 first_time = FALSE;
274                 g_timer_start (total_timer);
275         }
276 #endif
277
278         /* undo the thumbnailing idle and handler */
279         if (priv->pages_done == priv->n_pages) {
280                 priv->idle_id = 0;
281                 g_signal_handlers_disconnect_by_func (priv->vadjustment,
282                                                       adjustment_changed_cb,
283                                                       ev_sidebar_thumbnails);
284 #if PROFILE_THUMB == 1
285                 time_elapsed = g_timer_elapsed (total_timer, NULL);
286                 g_timer_destroy (total_timer);
287                 g_print ("%d rows done in %f seconds\n",
288                          gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->list_store), NULL),
289                          time_elapsed);
290 #endif
291                 return FALSE;
292         }
293
294         timer = g_timer_new ();
295         i = 0;
296         g_timer_start (timer);
297         while (do_one_iteration (ev_sidebar_thumbnails)) {
298                 i++;
299                 time_elapsed = g_timer_elapsed (timer, NULL);
300                 if (time_elapsed > IDLE_WORK_LENGTH/1000000)
301                         break;
302         }
303         g_timer_destroy (timer);
304 #if PROFILE_THUMB == 2
305         g_print ("%d rows done this idle in %f seconds\n", i, time_elapsed);
306 #endif
307
308         return TRUE;
309 }
310
311 void
312 ev_sidebar_thumbnails_select_page (EvSidebarThumbnails *sidebar,
313                                    int                  page)
314 {
315         GtkTreePath *path;
316         GtkTreeSelection *selection;
317
318         /* if the EvSidebar's document can't provide thumbnails */
319         if (sidebar->priv->document == NULL) 
320                 return;
321
322         path = gtk_tree_path_new_from_indices (page - 1, -1);
323         selection = gtk_tree_view_get_selection
324                         (GTK_TREE_VIEW (sidebar->priv->tree_view));
325
326         if (path) {
327                 gtk_tree_selection_select_path (selection, path);
328                 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (sidebar->priv->tree_view),
329                                               path, NULL, FALSE, 0.0, 0.0);
330                 gtk_tree_path_free (path);      
331         }
332 }
333
334
335 void
336 ev_sidebar_thumbnails_set_document (EvSidebarThumbnails *sidebar_thumbnails,
337                                     EvDocument          *document)
338 {
339         GdkPixbuf *loading_icon;
340         gint i, n_pages;
341         GtkTreeIter iter;
342         gchar *page;
343         gint width = THUMBNAIL_WIDTH;
344         gint height = THUMBNAIL_WIDTH;
345
346         EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
347
348         g_return_if_fail (EV_IS_DOCUMENT_THUMBNAILS (document));
349
350         if (priv->idle_id != 0) {
351                 g_source_remove (priv->idle_id);
352         }
353         n_pages = ev_document_get_n_pages (document);
354
355         priv->document = document;
356         priv->idle_id = g_idle_add (populate_thumbnails_idle, sidebar_thumbnails);
357         priv->n_pages = n_pages;
358
359         /* We get the dimensions of the first doc so that we can make a blank
360          * icon.  */
361         ev_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (priv->document),
362                                                1, THUMBNAIL_WIDTH, &width, &height);
363         loading_icon = ev_document_misc_get_thumbnail_frame (width, height, NULL);
364
365         gtk_list_store_clear (priv->list_store);
366
367         for (i = 1; i <= n_pages; i++) {
368                 page = g_strdup_printf ("<i>%d</i>", i);
369                 gtk_list_store_append (priv->list_store, &iter);
370                 gtk_list_store_set (priv->list_store, &iter,
371                                     COLUMN_PAGE_STRING, page,
372                                     COLUMN_PIXBUF, loading_icon,
373                                     COLUMN_THUMBNAIL_SET, FALSE,
374                                     -1);
375                 g_free (page);
376         }
377
378         g_object_unref (loading_icon);
379         gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store),
380                                        &(priv->current_page_iter));
381         priv->current_page = 1;
382         priv->pages_done = 0;
383 }
384