]> www.fi.muni.cz Git - evince.git/blob - shell/ev-sidebar-thumbnails.c
New file with some random thoughts.
[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                                     -1);
232
233                 g_object_unref (pixbuf);
234                 priv->pages_done ++;
235         }
236
237         priv->current_page++;
238
239         if (priv->current_page == priv->n_pages) {
240                 priv->current_page = 0;
241                 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store),
242                                                &(priv->current_page_iter));
243         } else {
244                 gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->list_store),
245                                           &(priv->current_page_iter));
246         }
247
248         if (priv->pages_done == priv->n_pages)
249                 return FALSE;
250         else
251                 return TRUE;
252 }
253
254 static gboolean
255 populate_thumbnails_idle (gpointer data)
256 {
257         GTimer *timer;
258         int i;
259         gdouble time_elapsed = 0;
260
261         EvSidebarThumbnails *ev_sidebar_thumbnails = EV_SIDEBAR_THUMBNAILS (data);
262         EvSidebarThumbnailsPrivate *priv = ev_sidebar_thumbnails->priv;
263
264 #if PROFILE_THUMB == 1
265         static GTimer *total_timer;
266         static gboolean first_time = TRUE;
267
268         if (first_time) {
269                 total_timer = g_timer_new ();
270                 first_time = FALSE;
271                 g_timer_start (total_timer);
272         }
273 #endif
274
275         /* undo the thumbnailing idle and handler */
276         if (priv->pages_done == priv->n_pages) {
277                 priv->idle_id = 0;
278                 g_signal_handlers_disconnect_by_func (priv->vadjustment,
279                                                       adjustment_changed_cb,
280                                                       ev_sidebar_thumbnails);
281 #if PROFILE_THUMB == 1
282                 time_elapsed = g_timer_elapsed (total_timer, NULL);
283                 g_timer_destroy (total_timer);
284                 g_print ("%d rows done in %f seconds\n",
285                          gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->list_store), NULL),
286                          time_elapsed);
287 #endif
288                 return FALSE;
289         }
290
291         timer = g_timer_new ();
292         i = 0;
293         g_timer_start (timer);
294         while (do_one_iteration (ev_sidebar_thumbnails)) {
295                 i++;
296                 time_elapsed = g_timer_elapsed (timer, NULL);
297                 if (time_elapsed > IDLE_WORK_LENGTH/1000000)
298                         break;
299         }
300         g_timer_destroy (timer);
301 #if PROFILE_THUMB == 2
302         g_print ("%d rows done this idle in %f seconds\n", i, time_elapsed);
303 #endif
304
305         return TRUE;
306 }
307
308 void
309 ev_sidebar_thumbnails_select_page (EvSidebarThumbnails *sidebar,
310                                    int                  page)
311 {
312         GtkTreePath *path;
313         GtkTreeSelection *selection;
314
315         path = gtk_tree_path_new_from_indices (page - 1, -1);
316         selection = gtk_tree_view_get_selection
317                         (GTK_TREE_VIEW (sidebar->priv->tree_view));
318
319         if (path) {
320                 gtk_tree_selection_select_path (selection, path);
321                 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (sidebar->priv->tree_view),
322                                               path, NULL, FALSE, 0.0, 0.0);
323                 gtk_tree_path_free (path);      
324         }
325 }
326
327
328 void
329 ev_sidebar_thumbnails_set_document (EvSidebarThumbnails *sidebar_thumbnails,
330                                     EvDocument          *document)
331 {
332         GdkPixbuf *loading_icon;
333         gint i, n_pages;
334         GtkTreeIter iter;
335         gchar *page;
336         gint width = THUMBNAIL_WIDTH;
337         gint height = THUMBNAIL_WIDTH;
338
339         EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
340
341         g_return_if_fail (EV_IS_DOCUMENT_THUMBNAILS (document));
342
343         if (priv->idle_id != 0) {
344                 g_source_remove (priv->idle_id);
345         }
346         n_pages = ev_document_get_n_pages (document);
347
348         priv->document = document;
349         priv->idle_id = g_idle_add (populate_thumbnails_idle, sidebar_thumbnails);
350         priv->n_pages = n_pages;
351
352         /* We get the dimensions of the first doc so that we can make a blank
353          * icon.  */
354         ev_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (priv->document),
355                                                1, THUMBNAIL_WIDTH, &width, &height);
356         loading_icon = ev_document_misc_get_thumbnail_frame (width, height, NULL);
357
358         for (i = 1; i <= n_pages; i++) {
359                 page = g_strdup_printf ("<i>%d</i>", i);
360                 gtk_list_store_append (priv->list_store, &iter);
361                 gtk_list_store_set (priv->list_store, &iter,
362                                     COLUMN_PAGE_STRING, page,
363                                     COLUMN_PIXBUF, loading_icon,
364                                     COLUMN_THUMBNAIL_SET, FALSE,
365                                     -1);
366                 g_free (page);
367         }
368
369         g_object_unref (loading_icon);
370         gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store),
371                                        &(priv->current_page_iter));
372         priv->current_page = 0;
373         priv->pages_done = 0;
374 }
375