]> www.fi.muni.cz Git - evince.git/blob - shell/ev-sidebar-bookmarks.c
Initial history implementation. Needs work.
[evince.git] / shell / ev-sidebar-bookmarks.c
1 /* this file is part of evince, a gnome document viewer
2  *
3  *  Copyright (C) 2004 Red Hat, Inc.
4  *
5  *  Author:
6  *    Jonathan Blandford <jrb@alum.mit.edu>
7  *
8  * Evince is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * Evince is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <string.h>
28 #include <gtk/gtk.h>
29
30 #include "ev-sidebar-bookmarks.h"
31 #include "ev-document-bookmarks.h"
32 #include "ev-application.h"
33
34 /* Amount of time we devote to each iteration of the idle, in microseconds */
35 #define IDLE_WORK_LENGTH 5000
36
37 typedef struct {
38         EvDocumentBookmarksIter *bookmarks_iter;
39         GtkTreeIter *tree_iter;
40 } IdleStackData;
41
42 struct _EvSidebarBookmarksPrivate {
43         GtkWidget *tree_view;
44         GtkTreeModel *model;
45         EvDocument *current_document;
46         GList *idle_stack;
47         guint idle_id;
48 };
49
50 enum {
51         BOOKMARKS_COLUMN_MARKUP,
52         BOOKMARKS_COLUMN_PAGE_NUM,
53         BOOKMARKS_COLUMN_PAGE_VALID,
54         BOOKMARKS_COLUMN_BOOKMARK,
55         BOOKMARKS_COLUMN_NUM_COLUMNS
56 };
57
58 static void bookmarks_page_num_func (GtkTreeViewColumn *tree_column,
59                                      GtkCellRenderer   *cell,
60                                      GtkTreeModel      *tree_model,
61                                      GtkTreeIter       *iter,
62                                      gpointer           data);
63
64 G_DEFINE_TYPE (EvSidebarBookmarks, ev_sidebar_bookmarks, GTK_TYPE_VBOX)
65
66 #define EV_SIDEBAR_BOOKMARKS_GET_PRIVATE(object) \
67         (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_BOOKMARKS, EvSidebarBookmarksPrivate))
68
69
70 static void
71 ev_sidebar_bookmarks_destroy (GtkObject *object)
72 {
73         EvSidebarBookmarks *ev_sidebar_bookmarks = (EvSidebarBookmarks *) object;
74
75         g_print ("ev_sidebar_bookmarks_destroy!\n");
76         ev_sidebar_bookmarks_clear_document (ev_sidebar_bookmarks);
77 }
78
79 static void
80 ev_sidebar_bookmarks_class_init (EvSidebarBookmarksClass *ev_sidebar_bookmarks_class)
81 {
82         GObjectClass *g_object_class;
83         GtkObjectClass *gtk_object_class;
84
85         g_object_class = G_OBJECT_CLASS (ev_sidebar_bookmarks_class);
86         gtk_object_class = GTK_OBJECT_CLASS (ev_sidebar_bookmarks_class);
87
88         gtk_object_class->destroy = ev_sidebar_bookmarks_destroy;
89
90         g_type_class_add_private (g_object_class, sizeof (EvSidebarBookmarksPrivate));
91 }
92
93 static void
94 selection_changed_cb (GtkTreeSelection   *selection,
95                       EvSidebarBookmarks *ev_sidebar_bookmarks)
96 {
97         EvDocument *document;
98         GtkTreeModel *model;
99         GtkTreeIter iter;
100
101         g_return_if_fail (EV_IS_SIDEBAR_BOOKMARKS (ev_sidebar_bookmarks));
102
103         document = EV_DOCUMENT (ev_sidebar_bookmarks->priv->current_document);
104         g_return_if_fail (ev_sidebar_bookmarks->priv->current_document != NULL);
105
106         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
107                 EvBookmark *bookmark;
108                 EvApplication *app;
109                 GtkWidget *window;
110                 GValue value = {0, };
111
112                 gtk_tree_model_get_value (model, &iter,
113                                           BOOKMARKS_COLUMN_BOOKMARK, &value);
114
115                 bookmark = EV_BOOKMARK (g_value_get_object (&value));
116                 g_return_if_fail (bookmark != NULL);
117
118                 window = gtk_widget_get_ancestor (GTK_WIDGET (ev_sidebar_bookmarks),
119                                                   EV_TYPE_WINDOW);
120                 if (window) {
121                         app = ev_application_get_instance ();
122                         ev_application_open_bookmark (app, EV_WINDOW (window),
123                                                       bookmark, NULL);
124                 }
125         }
126 }
127
128 static void
129 ev_sidebar_bookmarks_construct (EvSidebarBookmarks *ev_sidebar_bookmarks)
130 {
131         EvSidebarBookmarksPrivate *priv;
132         GtkWidget *swindow;
133         GtkTreeViewColumn *column;
134         GtkCellRenderer *renderer;
135         GtkTreeSelection *selection;
136
137         priv = ev_sidebar_bookmarks->priv;
138         priv->model = (GtkTreeModel *) gtk_tree_store_new (BOOKMARKS_COLUMN_NUM_COLUMNS,
139                                                            G_TYPE_STRING,
140                                                            G_TYPE_INT,
141                                                            G_TYPE_BOOLEAN,
142                                                            G_TYPE_OBJECT);
143
144         swindow = gtk_scrolled_window_new (NULL, NULL);
145
146         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
147                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
148         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
149                                              GTK_SHADOW_IN);
150
151         /* Create tree view */
152         priv->tree_view = gtk_tree_view_new_with_model (priv->model);
153         g_object_unref (priv->model);
154         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
155         gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
156         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (priv->tree_view), TRUE);
157
158         gtk_box_pack_start (GTK_BOX (ev_sidebar_bookmarks), swindow, TRUE, TRUE, 0);
159         gtk_widget_show_all (GTK_WIDGET (ev_sidebar_bookmarks));
160
161         column = gtk_tree_view_column_new ();
162         gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
163         gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), column);
164
165         renderer = (GtkCellRenderer*)
166                 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
167                               "ellipsize", PANGO_ELLIPSIZE_END,
168                               NULL);
169         gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN (column), renderer, TRUE);
170         gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
171                                              "markup", BOOKMARKS_COLUMN_MARKUP,
172                                              NULL);
173
174         renderer = gtk_cell_renderer_text_new ();
175         gtk_tree_view_column_pack_end (GTK_TREE_VIEW_COLUMN (column), renderer, FALSE);
176         gtk_tree_view_column_set_cell_data_func (GTK_TREE_VIEW_COLUMN (column), renderer,
177                                                  (GtkTreeCellDataFunc) bookmarks_page_num_func,
178                                                  NULL, NULL);
179
180
181         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
182         g_signal_connect (selection, "changed",
183                           G_CALLBACK (selection_changed_cb),
184                           ev_sidebar_bookmarks);
185 }
186
187 static void
188 ev_sidebar_bookmarks_init (EvSidebarBookmarks *ev_sidebar_bookmarks)
189 {
190         ev_sidebar_bookmarks->priv = EV_SIDEBAR_BOOKMARKS_GET_PRIVATE (ev_sidebar_bookmarks);
191
192         ev_sidebar_bookmarks_construct (ev_sidebar_bookmarks);
193 }
194
195 static void
196 bookmarks_page_num_func (GtkTreeViewColumn *tree_column,
197                          GtkCellRenderer   *cell,
198                          GtkTreeModel      *tree_model,
199                          GtkTreeIter       *iter,
200                          gpointer           data)
201 {
202         int page_num;
203         gboolean page_valid;
204
205         gtk_tree_model_get (tree_model, iter,
206                             BOOKMARKS_COLUMN_PAGE_NUM, &page_num,
207                             BOOKMARKS_COLUMN_PAGE_VALID, &page_valid,
208                             -1);
209
210         if (page_valid) {
211                 gchar *markup = g_strdup_printf ("<i>%d</i>", page_num);
212                 g_object_set (cell,
213                               "markup", markup,
214                               "visible", TRUE,
215                               NULL);
216                 g_free (markup);
217         } else {
218                 g_object_set (cell,
219                               "visible", FALSE,
220                               NULL);
221         }
222 }
223
224 /* Public Functions */
225
226 GtkWidget *
227 ev_sidebar_bookmarks_new (void)
228 {
229         GtkWidget *ev_sidebar_bookmarks;
230
231         ev_sidebar_bookmarks = g_object_new (EV_TYPE_SIDEBAR_BOOKMARKS, NULL);
232
233         return ev_sidebar_bookmarks;
234 }
235
236 static void
237 stack_data_free (IdleStackData       *stack_data,
238                  EvDocumentBookmarks *document_bookmarks)
239 {
240         g_assert (stack_data);
241
242         if (stack_data->tree_iter)
243                 gtk_tree_iter_free (stack_data->tree_iter);
244         if (stack_data->bookmarks_iter)
245                 ev_document_bookmarks_free_iter (document_bookmarks, stack_data->bookmarks_iter);
246         g_free (stack_data);
247 }
248
249 static gboolean
250 do_one_iteration (EvSidebarBookmarks *ev_sidebar_bookmarks)
251 {
252         EvSidebarBookmarksPrivate *priv = ev_sidebar_bookmarks->priv;
253         EvBookmark *bookmark;
254         IdleStackData *stack_data;
255         GtkTreeIter tree_iter;
256         EvDocumentBookmarksIter *child_iter;
257         gint page;
258
259         g_assert (priv->idle_stack);
260
261         stack_data = (IdleStackData *) priv->idle_stack->data;
262
263         bookmark = ev_document_bookmarks_get_bookmark
264                 (EV_DOCUMENT_BOOKMARKS (priv->current_document),
265                  stack_data->bookmarks_iter);
266         if (bookmark == NULL) {
267                 g_warning ("mismatch in model.  No values available at current level.\n");
268                 return FALSE;
269         }
270
271         page = ev_bookmark_get_page (bookmark);
272         gtk_tree_store_append (GTK_TREE_STORE (priv->model), &tree_iter, stack_data->tree_iter);
273         gtk_tree_store_set (GTK_TREE_STORE (priv->model), &tree_iter,
274                             BOOKMARKS_COLUMN_MARKUP, ev_bookmark_get_title (bookmark),
275                             BOOKMARKS_COLUMN_PAGE_NUM, page,
276                             /* FIXME: Handle links for real. */
277                             BOOKMARKS_COLUMN_PAGE_VALID, (page >= 0),
278                             BOOKMARKS_COLUMN_BOOKMARK, bookmark,
279                             -1);
280         g_object_unref (bookmark);
281         
282         child_iter = ev_document_bookmarks_get_child (EV_DOCUMENT_BOOKMARKS (priv->current_document),
283                                                       stack_data->bookmarks_iter);
284         if (child_iter) {
285                 IdleStackData *child_stack_data;
286
287                 child_stack_data = g_new0 (IdleStackData, 1);
288                 child_stack_data->tree_iter = gtk_tree_iter_copy (&tree_iter);
289                 child_stack_data->bookmarks_iter = child_iter;
290                 priv->idle_stack = g_list_prepend (priv->idle_stack, child_stack_data);
291
292                 return TRUE;
293         }
294
295         /* We don't have children, so we need to walk to the next node */
296         while (TRUE) {
297                 if (ev_document_bookmarks_next (EV_DOCUMENT_BOOKMARKS (priv->current_document),
298                                                 stack_data->bookmarks_iter))
299                         return TRUE;
300
301                 /* We're done with this level.  Pop it off the idle stack and go
302                  * to the next level */
303                 stack_data_free (stack_data, EV_DOCUMENT_BOOKMARKS (priv->current_document));
304                 priv->idle_stack = g_list_delete_link (priv->idle_stack, priv->idle_stack);
305                 if (priv->idle_stack == NULL)
306                         return FALSE;
307                 stack_data = priv->idle_stack->data;
308         }
309 }
310
311 static gboolean
312 populate_bookmarks_idle (gpointer data)
313 {
314         GTimer *timer;
315         gint i;
316         gulong microseconds = 0;
317
318         EvSidebarBookmarks *ev_sidebar_bookmarks = (EvSidebarBookmarks *)data;
319         EvSidebarBookmarksPrivate *priv = ev_sidebar_bookmarks->priv;
320
321         if (priv->idle_stack == NULL) {
322                 priv->idle_id = 0;
323                 return FALSE;
324         }
325
326         /* The amount of time that reading the next bookmark takes is wildly
327          * inconsistent, so we constrain it to IDLE_WORK_LENGTH microseconds per
328          * idle iteration. */
329         timer = g_timer_new ();
330         i = 0;
331         g_timer_start (timer);
332         while (do_one_iteration (ev_sidebar_bookmarks)) {
333                 i++;
334                 g_timer_elapsed (timer, &microseconds);
335                 if (microseconds > IDLE_WORK_LENGTH)
336                         break;
337         }
338         g_timer_destroy (timer);
339 #if 0
340         g_print ("%d rows done this idle in %d\n", i, (int)microseconds);
341 #endif
342         return TRUE;
343 }
344
345 void
346 ev_sidebar_bookmarks_clear_document (EvSidebarBookmarks *sidebar_bookmarks)
347 {
348         EvSidebarBookmarksPrivate *priv;
349
350         g_return_if_fail (EV_IS_SIDEBAR_BOOKMARKS (sidebar_bookmarks));
351
352         priv = sidebar_bookmarks->priv;
353         if (priv->current_document) {
354                 g_object_unref (priv->current_document);
355                 priv->current_document = NULL;
356         }
357         gtk_tree_store_clear (GTK_TREE_STORE (priv->model));
358
359         /* Clear the idle */
360         if (priv->idle_id != 0) {
361                 g_source_remove (priv->idle_id);
362                 priv->idle_id = 0;
363         }
364         g_list_foreach (priv->idle_stack, (GFunc) stack_data_free, priv->current_document);
365         g_list_free (priv->idle_stack);
366         priv->idle_stack = NULL;
367
368 }
369
370 void
371 ev_sidebar_bookmarks_set_document (EvSidebarBookmarks *sidebar_bookmarks,
372                                    EvDocument         *document)
373 {
374         EvSidebarBookmarksPrivate *priv;
375         EvDocumentBookmarksIter *bookmarks_iter;
376
377         g_return_if_fail (EV_IS_SIDEBAR_BOOKMARKS (sidebar_bookmarks));
378         g_return_if_fail (EV_IS_DOCUMENT (document));
379
380         priv = sidebar_bookmarks->priv;
381
382         g_object_ref (document);
383         ev_sidebar_bookmarks_clear_document (sidebar_bookmarks);
384
385         priv->current_document = document;
386         bookmarks_iter = ev_document_bookmarks_begin_read (EV_DOCUMENT_BOOKMARKS (document));
387         if (bookmarks_iter) {
388                 IdleStackData *stack_data;
389
390                 stack_data = g_new0 (IdleStackData, 1);
391                 stack_data->bookmarks_iter = bookmarks_iter;
392                 stack_data->tree_iter = NULL;
393
394                 priv->idle_stack = g_list_prepend (priv->idle_stack, stack_data);
395                 priv->idle_id = g_idle_add (populate_bookmarks_idle, sidebar_bookmarks);
396         }
397 }
398