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