]> www.fi.muni.cz Git - evince.git/blob - shell/ev-sidebar-bookmarks.c
Add a bookmark object to the backend and use it instead of get_values
[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
33 /* Amount of time we devote to each iteration of the idle, in microseconds */
34 #define IDLE_WORK_LENGTH 5000
35
36 typedef struct {
37         EvDocumentBookmarksIter *bookmarks_iter;
38         GtkTreeIter *tree_iter;
39 } IdleStackData;
40
41 struct _EvSidebarBookmarksPrivate {
42         GtkWidget *tree_view;
43         GtkTreeModel *model;
44         EvDocument *current_document;
45         GList *idle_stack;
46         guint idle_id;
47 };
48
49 enum {
50         BOOKMARKS_COLUMN_MARKUP,
51         BOOKMARKS_COLUMN_PAGE_NUM,
52         BOOKMARKS_COLUMN_PAGE_VALID,
53         BOOKMARKS_COLUMN_NUM_COLUMNS
54 };
55
56 static void bookmarks_page_num_func (GtkTreeViewColumn *tree_column,
57                                      GtkCellRenderer   *cell,
58                                      GtkTreeModel      *tree_model,
59                                      GtkTreeIter       *iter,
60                                      gpointer           data);
61
62 G_DEFINE_TYPE (EvSidebarBookmarks, ev_sidebar_bookmarks, GTK_TYPE_VBOX)
63
64 #define EV_SIDEBAR_BOOKMARKS_GET_PRIVATE(object) \
65         (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_BOOKMARKS, EvSidebarBookmarksPrivate))
66
67
68 static void
69 ev_sidebar_bookmarks_destroy (GtkObject *object)
70 {
71         EvSidebarBookmarks *ev_sidebar_bookmarks = (EvSidebarBookmarks *) object;
72
73         g_print ("ev_sidebar_bookmarks_destroy!\n");
74         ev_sidebar_bookmarks_clear_document (ev_sidebar_bookmarks);
75 }
76
77 static void
78 ev_sidebar_bookmarks_class_init (EvSidebarBookmarksClass *ev_sidebar_bookmarks_class)
79 {
80         GObjectClass *g_object_class;
81         GtkObjectClass *gtk_object_class;
82
83         g_object_class = G_OBJECT_CLASS (ev_sidebar_bookmarks_class);
84         gtk_object_class = GTK_OBJECT_CLASS (ev_sidebar_bookmarks_class);
85
86         gtk_object_class->destroy = ev_sidebar_bookmarks_destroy;
87
88         g_type_class_add_private (g_object_class, sizeof (EvSidebarBookmarksPrivate));
89 }
90
91
92 static void
93 ev_sidebar_bookmarks_construct (EvSidebarBookmarks *ev_sidebar_bookmarks)
94 {
95         EvSidebarBookmarksPrivate *priv;
96         GtkWidget *swindow;
97         GtkTreeViewColumn *column;
98         GtkCellRenderer *renderer;
99
100         priv = ev_sidebar_bookmarks->priv;
101         priv->model = (GtkTreeModel *) gtk_tree_store_new (BOOKMARKS_COLUMN_NUM_COLUMNS,
102                                                            G_TYPE_STRING,
103                                                            G_TYPE_INT,
104                                                            G_TYPE_BOOLEAN);
105
106         swindow = gtk_scrolled_window_new (NULL, NULL);
107
108         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
109                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
110         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
111                                              GTK_SHADOW_IN);
112
113         /* Create tree view */
114         priv->tree_view = gtk_tree_view_new_with_model (priv->model);
115         g_object_unref (priv->model);
116         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
117         gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
118         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (priv->tree_view), TRUE);
119
120         gtk_box_pack_start (GTK_BOX (ev_sidebar_bookmarks), swindow, TRUE, TRUE, 0);
121         gtk_widget_show_all (GTK_WIDGET (ev_sidebar_bookmarks));
122
123         column = gtk_tree_view_column_new ();
124         gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
125         gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), column);
126
127         renderer = (GtkCellRenderer*)
128                 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
129                               "ellipsize", PANGO_ELLIPSIZE_END,
130                               NULL);
131         gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN (column), renderer, TRUE);
132         gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
133                                              "markup", BOOKMARKS_COLUMN_MARKUP,
134                                              NULL);
135
136         renderer = gtk_cell_renderer_text_new ();
137         gtk_tree_view_column_pack_end (GTK_TREE_VIEW_COLUMN (column), renderer, FALSE);
138         gtk_tree_view_column_set_cell_data_func (GTK_TREE_VIEW_COLUMN (column), renderer,
139                                                  (GtkTreeCellDataFunc) bookmarks_page_num_func,
140                                                  NULL, NULL);
141
142 }
143
144 static void
145 ev_sidebar_bookmarks_init (EvSidebarBookmarks *ev_sidebar_bookmarks)
146 {
147         ev_sidebar_bookmarks->priv = EV_SIDEBAR_BOOKMARKS_GET_PRIVATE (ev_sidebar_bookmarks);
148
149         ev_sidebar_bookmarks_construct (ev_sidebar_bookmarks);
150 }
151
152 static void
153 bookmarks_page_num_func (GtkTreeViewColumn *tree_column,
154                          GtkCellRenderer   *cell,
155                          GtkTreeModel      *tree_model,
156                          GtkTreeIter       *iter,
157                          gpointer           data)
158 {
159         int page_num;
160         gboolean page_valid;
161
162         gtk_tree_model_get (tree_model, iter,
163                             BOOKMARKS_COLUMN_PAGE_NUM, &page_num,
164                             BOOKMARKS_COLUMN_PAGE_VALID, &page_valid,
165                             -1);
166
167         if (page_valid) {
168                 gchar *markup = g_strdup_printf ("<i>%d</i>", page_num);
169                 g_object_set (cell,
170                               "markup", markup,
171                               "visible", TRUE,
172                               NULL);
173                 g_free (markup);
174         } else {
175                 g_object_set (cell,
176                               "visible", FALSE,
177                               NULL);
178         }
179 }
180
181 /* Public Functions */
182
183 GtkWidget *
184 ev_sidebar_bookmarks_new (void)
185 {
186         GtkWidget *ev_sidebar_bookmarks;
187
188         ev_sidebar_bookmarks = g_object_new (EV_TYPE_SIDEBAR_BOOKMARKS, NULL);
189
190         return ev_sidebar_bookmarks;
191 }
192
193 static void
194 stack_data_free (IdleStackData       *stack_data,
195                  EvDocumentBookmarks *document_bookmarks)
196 {
197         g_assert (stack_data);
198
199         if (stack_data->tree_iter)
200                 gtk_tree_iter_free (stack_data->tree_iter);
201         if (stack_data->bookmarks_iter)
202                 ev_document_bookmarks_free_iter (document_bookmarks, stack_data->bookmarks_iter);
203         g_free (stack_data);
204 }
205
206 static gboolean
207 do_one_iteration (EvSidebarBookmarks *ev_sidebar_bookmarks)
208 {
209         EvSidebarBookmarksPrivate *priv = ev_sidebar_bookmarks->priv;
210         EvBookmark *bookmark;
211         IdleStackData *stack_data;
212         GtkTreeIter tree_iter;
213         EvDocumentBookmarksIter *child_iter;
214         gint page;
215
216         g_assert (priv->idle_stack);
217
218         stack_data = (IdleStackData *) priv->idle_stack->data;
219
220         bookmark = ev_document_bookmarks_get_bookmark
221                 (EV_DOCUMENT_BOOKMARKS (priv->current_document),
222                  stack_data->bookmarks_iter);
223         if (bookmark == NULL) {
224                 g_warning ("mismatch in model.  No values available at current level.\n");
225                 return FALSE;
226         }
227
228         page = ev_bookmark_get_page (bookmark);
229         gtk_tree_store_append (GTK_TREE_STORE (priv->model), &tree_iter, stack_data->tree_iter);
230         gtk_tree_store_set (GTK_TREE_STORE (priv->model), &tree_iter,
231                             BOOKMARKS_COLUMN_MARKUP, ev_bookmark_get_title (bookmark),
232                             BOOKMARKS_COLUMN_PAGE_NUM, page,
233                             /* FIXME: Handle links for real. */
234                             BOOKMARKS_COLUMN_PAGE_VALID, (page >= 0),
235                             -1);
236         g_object_unref (bookmark);
237         
238         child_iter = ev_document_bookmarks_get_child (EV_DOCUMENT_BOOKMARKS (priv->current_document),
239                                                       stack_data->bookmarks_iter);
240         if (child_iter) {
241                 IdleStackData *child_stack_data;
242
243                 child_stack_data = g_new0 (IdleStackData, 1);
244                 child_stack_data->tree_iter = gtk_tree_iter_copy (&tree_iter);
245                 child_stack_data->bookmarks_iter = child_iter;
246                 priv->idle_stack = g_list_prepend (priv->idle_stack, child_stack_data);
247
248                 return TRUE;
249         }
250
251         /* We don't have children, so we need to walk to the next node */
252         while (TRUE) {
253                 if (ev_document_bookmarks_next (EV_DOCUMENT_BOOKMARKS (priv->current_document),
254                                                 stack_data->bookmarks_iter))
255                         return TRUE;
256
257                 /* We're done with this level.  Pop it off the idle stack and go
258                  * to the next level */
259                 stack_data_free (stack_data, EV_DOCUMENT_BOOKMARKS (priv->current_document));
260                 priv->idle_stack = g_list_delete_link (priv->idle_stack, priv->idle_stack);
261                 if (priv->idle_stack == NULL)
262                         return FALSE;
263                 stack_data = priv->idle_stack->data;
264         }
265 }
266
267 static gboolean
268 populate_bookmarks_idle (gpointer data)
269 {
270         GTimer *timer;
271         gint i;
272         gulong microseconds = 0;
273
274         EvSidebarBookmarks *ev_sidebar_bookmarks = (EvSidebarBookmarks *)data;
275         EvSidebarBookmarksPrivate *priv = ev_sidebar_bookmarks->priv;
276
277         if (priv->idle_stack == NULL) {
278                 priv->idle_id = 0;
279                 return FALSE;
280         }
281
282         /* The amount of time that reading the next bookmark takes is wildly
283          * inconsistent, so we constrain it to IDLE_WORK_LENGTH microseconds per
284          * idle iteration. */
285         timer = g_timer_new ();
286         i = 0;
287         g_timer_start (timer);
288         while (do_one_iteration (ev_sidebar_bookmarks)) {
289                 i++;
290                 g_timer_elapsed (timer, &microseconds);
291                 if (microseconds > IDLE_WORK_LENGTH)
292                         break;
293         }
294         g_timer_destroy (timer);
295 #if 0
296         g_print ("%d rows done this idle in %d\n", i, (int)microseconds);
297 #endif
298         return TRUE;
299 }
300
301 void
302 ev_sidebar_bookmarks_clear_document (EvSidebarBookmarks *sidebar_bookmarks)
303 {
304         EvSidebarBookmarksPrivate *priv;
305
306         g_return_if_fail (EV_IS_SIDEBAR_BOOKMARKS (sidebar_bookmarks));
307
308         priv = sidebar_bookmarks->priv;
309         if (priv->current_document) {
310                 g_object_unref (priv->current_document);
311                 priv->current_document = NULL;
312         }
313         gtk_tree_store_clear (GTK_TREE_STORE (priv->model));
314
315         /* Clear the idle */
316         if (priv->idle_id != 0) {
317                 g_source_remove (priv->idle_id);
318                 priv->idle_id = 0;
319         }
320         g_list_foreach (priv->idle_stack, (GFunc) stack_data_free, priv->current_document);
321         g_list_free (priv->idle_stack);
322         priv->idle_stack = NULL;
323
324 }
325
326 void
327 ev_sidebar_bookmarks_set_document (EvSidebarBookmarks *sidebar_bookmarks,
328                                    EvDocument         *document)
329 {
330         EvSidebarBookmarksPrivate *priv;
331         EvDocumentBookmarksIter *bookmarks_iter;
332
333         g_return_if_fail (EV_IS_SIDEBAR_BOOKMARKS (sidebar_bookmarks));
334         g_return_if_fail (EV_IS_DOCUMENT (document));
335
336         priv = sidebar_bookmarks->priv;
337
338         g_object_ref (document);
339         ev_sidebar_bookmarks_clear_document (sidebar_bookmarks);
340
341         priv->current_document = document;
342         bookmarks_iter = ev_document_bookmarks_begin_read (EV_DOCUMENT_BOOKMARKS (document));
343         if (bookmarks_iter) {
344                 IdleStackData *stack_data;
345
346                 stack_data = g_new0 (IdleStackData, 1);
347                 stack_data->bookmarks_iter = bookmarks_iter;
348                 stack_data->tree_iter = NULL;
349
350                 priv->idle_stack = g_list_prepend (priv->idle_stack, stack_data);
351                 priv->idle_id = g_idle_add (populate_bookmarks_idle, sidebar_bookmarks);
352         }
353 }
354