]> www.fi.muni.cz Git - evince.git/blob - shell/ev-sidebar-thumbnails.c
Set the spin button limits correctly.
[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-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_tree_selection_changed (GtkTreeSelection *selection,
133                                    EvSidebarThumbnails *ev_sidebar_thumbnails)
134 {
135         EvSidebarThumbnailsPrivate *priv;
136         GtkTreePath *path;
137         GtkTreeIter iter;
138         int page;
139
140         priv = ev_sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (ev_sidebar_thumbnails);
141   
142         if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
143                 return;
144         
145         path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->list_store),
146                                         &iter);
147
148         page = gtk_tree_path_get_indices (path)[0] + 1;
149
150         gtk_tree_path_free (path);
151
152         ev_document_set_page (priv->document, page);
153 }
154
155 static void
156 ev_sidebar_thumbnails_init (EvSidebarThumbnails *ev_sidebar_thumbnails)
157 {
158         GtkWidget *swindow;
159         EvSidebarThumbnailsPrivate *priv;
160         GtkCellRenderer *renderer;
161         GtkTreeSelection *selection;
162
163         priv = ev_sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (ev_sidebar_thumbnails);
164         
165         priv->list_store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN);
166         priv->tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->list_store));
167         
168         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
169         g_signal_connect (selection, "changed",
170                           G_CALLBACK (ev_sidebar_tree_selection_changed), ev_sidebar_thumbnails);
171         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
172         renderer = g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF,
173                                  "xpad", 2,
174                                  "ypad", 2,
175                                  NULL);
176         gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->tree_view), -1,
177                                                      NULL, renderer,
178                                                      "pixbuf", 1,
179                                                      NULL);
180         gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->tree_view), -1,
181                                                      NULL, gtk_cell_renderer_text_new (),
182                                                      "markup", 0, NULL);
183
184         g_object_unref (priv->list_store);
185
186         swindow = gtk_scrolled_window_new (NULL, NULL);
187         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
188                                         GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
189         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
190                                              GTK_SHADOW_IN);
191         priv->vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (swindow));
192         g_signal_connect (G_OBJECT (priv->vadjustment), "value-changed",
193                           G_CALLBACK (adjustment_changed_cb),
194                           ev_sidebar_thumbnails);
195         gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
196         gtk_box_pack_start (GTK_BOX (ev_sidebar_thumbnails), swindow, TRUE, TRUE, 0);
197
198         gtk_widget_show_all (swindow);
199 }
200
201 GtkWidget *
202 ev_sidebar_thumbnails_new (void)
203 {
204         GtkWidget *ev_sidebar_thumbnails;
205
206         ev_sidebar_thumbnails = g_object_new (EV_TYPE_SIDEBAR_THUMBNAILS, NULL);
207
208         return ev_sidebar_thumbnails;
209 }
210
211 static gboolean
212 do_one_iteration (EvSidebarThumbnails *ev_sidebar_thumbnails)
213 {
214         EvSidebarThumbnailsPrivate *priv = ev_sidebar_thumbnails->priv;
215         GdkPixbuf *pixbuf;
216         gboolean thumbnail_set;
217
218         gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store),
219                             &(priv->current_page_iter),
220                             COLUMN_THUMBNAIL_SET, &thumbnail_set,
221                             -1);
222         if (!thumbnail_set) {
223                 pixbuf = ev_document_thumbnails_get_thumbnail (EV_DOCUMENT_THUMBNAILS (priv->document),
224                                                                priv->current_page, THUMBNAIL_WIDTH);
225
226                 gtk_list_store_set (priv->list_store,
227                                     &(priv->current_page_iter),
228                                     COLUMN_PIXBUF, pixbuf,
229                                     -1);
230
231                 g_object_unref (pixbuf);
232                 priv->pages_done ++;
233         }
234
235         priv->current_page++;
236
237         if (priv->current_page == priv->n_pages) {
238                 priv->current_page = 0;
239                 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store),
240                                                &(priv->current_page_iter));
241         } else {
242                 gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->list_store),
243                                           &(priv->current_page_iter));
244         }
245
246         if (priv->pages_done == priv->n_pages)
247                 return FALSE;
248         else
249                 return TRUE;
250 }
251
252 static gboolean
253 populate_thumbnails_idle (gpointer data)
254 {
255         GTimer *timer;
256         int i;
257         gdouble time_elapsed = 0;
258
259         EvSidebarThumbnails *ev_sidebar_thumbnails = EV_SIDEBAR_THUMBNAILS (data);
260         EvSidebarThumbnailsPrivate *priv = ev_sidebar_thumbnails->priv;
261
262 #if PROFILE_THUMB == 1
263         static GTimer *total_timer;
264         static gboolean first_time = TRUE;
265
266         if (first_time) {
267                 total_timer = g_timer_new ();
268                 first_time = FALSE;
269                 g_timer_start (total_timer);
270         }
271 #endif
272
273         /* undo the thumbnailing idle and handler */
274         if (priv->pages_done == priv->n_pages) {
275                 priv->idle_id = 0;
276                 g_signal_handlers_disconnect_by_func (priv->vadjustment,
277                                                       adjustment_changed_cb,
278                                                       ev_sidebar_thumbnails);
279 #if PROFILE_THUMB == 1
280                 time_elapsed = g_timer_elapsed (total_timer, NULL);
281                 g_timer_destroy (total_timer);
282                 g_print ("%d rows done in %f seconds\n",
283                          gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->list_store), NULL),
284                          time_elapsed);
285 #endif
286                 return FALSE;
287         }
288
289         timer = g_timer_new ();
290         i = 0;
291         g_timer_start (timer);
292         while (do_one_iteration (ev_sidebar_thumbnails)) {
293                 i++;
294                 time_elapsed = g_timer_elapsed (timer, NULL);
295                 if (time_elapsed > IDLE_WORK_LENGTH/1000000)
296                         break;
297         }
298         g_timer_destroy (timer);
299 #if PROFILE_THUMB == 2
300         g_print ("%d rows done this idle in %f seconds\n", i, time_elapsed);
301 #endif
302
303         return TRUE;
304 }
305
306 void
307 ev_sidebar_thumbnails_set_document (EvSidebarThumbnails *sidebar_thumbnails,
308                                     EvDocument          *document)
309 {
310         GdkPixbuf *loading_icon;
311         gint i, n_pages;
312         GtkTreeIter iter;
313         gchar *page;
314         gint width = THUMBNAIL_WIDTH;
315         gint height = THUMBNAIL_WIDTH;
316
317         EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
318
319         g_return_if_fail (EV_IS_DOCUMENT_THUMBNAILS (document));
320
321         if (priv->idle_id != 0) {
322                 g_source_remove (priv->idle_id);
323         }
324         n_pages = ev_document_get_n_pages (document);
325
326         priv->document = document;
327         priv->idle_id = g_idle_add (populate_thumbnails_idle, sidebar_thumbnails);
328         priv->n_pages = n_pages;
329
330         /* We get the dimensions of the first doc so that we can make a blank
331          * icon.  */
332         ev_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (priv->document),
333                                                0, THUMBNAIL_WIDTH, &width, &height);
334         loading_icon = ev_document_misc_get_thumbnail_frame (width, height, NULL);
335
336         for (i = 0; i < n_pages; i++) {
337                 page = g_strdup_printf ("<i>%d</i>", i + 1);
338                 gtk_list_store_append (priv->list_store, &iter);
339                 gtk_list_store_set (priv->list_store, &iter,
340                                     COLUMN_PAGE_STRING, page,
341                                     COLUMN_PIXBUF, loading_icon,
342                                     COLUMN_THUMBNAIL_SET, FALSE,
343                                     -1);
344                 g_free (page);
345         }
346
347         g_object_unref (loading_icon);
348         gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store),
349                                        &(priv->current_page_iter));
350         priv->current_page = 0;
351         priv->pages_done = 0;
352 }
353