]> www.fi.muni.cz Git - evince.git/blob - shell/ev-sidebar-links.c
18f9e8f35ed6ced7efc6aae92dd5cc30f8517b4d
[evince.git] / shell / ev-sidebar-links.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-links.h"
31 #include "ev-document-links.h"
32 #include "ev-window.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         EvDocumentLinksIter *links_iter;
39         GtkTreeIter *tree_iter;
40 } IdleStackData;
41
42 struct _EvSidebarLinksPrivate {
43         GtkWidget *tree_view;
44         GtkTreeModel *model;
45         EvDocument *current_document;
46         GList *idle_stack;
47         guint idle_id;
48 };
49
50 enum {
51         LINKS_COLUMN_MARKUP,
52         LINKS_COLUMN_PAGE_NUM,
53         LINKS_COLUMN_PAGE_VALID,
54         LINKS_COLUMN_LINK,
55         LINKS_COLUMN_NUM_COLUMNS
56 };
57
58 static void links_page_num_func (GtkTreeViewColumn *tree_column,
59                                  GtkCellRenderer   *cell,
60                                  GtkTreeModel      *tree_model,
61                                  GtkTreeIter       *iter,
62                                  gpointer           data);
63
64 G_DEFINE_TYPE (EvSidebarLinks, ev_sidebar_links, GTK_TYPE_VBOX)
65
66 #define EV_SIDEBAR_LINKS_GET_PRIVATE(object) \
67         (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_LINKS, EvSidebarLinksPrivate))
68
69
70 static void
71 ev_sidebar_links_destroy (GtkObject *object)
72 {
73         EvSidebarLinks *ev_sidebar_links = (EvSidebarLinks *) object;
74
75         g_print ("ev_sidebar_links_destroy!\n");
76         ev_sidebar_links_clear_document (ev_sidebar_links);
77 }
78
79 static void
80 ev_sidebar_links_class_init (EvSidebarLinksClass *ev_sidebar_links_class)
81 {
82         GObjectClass *g_object_class;
83         GtkObjectClass *gtk_object_class;
84
85         g_object_class = G_OBJECT_CLASS (ev_sidebar_links_class);
86         gtk_object_class = GTK_OBJECT_CLASS (ev_sidebar_links_class);
87
88         gtk_object_class->destroy = ev_sidebar_links_destroy;
89
90         g_type_class_add_private (g_object_class, sizeof (EvSidebarLinksPrivate));
91 }
92
93 static void
94 selection_changed_cb (GtkTreeSelection   *selection,
95                       EvSidebarLinks     *ev_sidebar_links)
96 {
97         EvDocument *document;
98         GtkTreeModel *model;
99         GtkTreeIter iter;
100
101         g_return_if_fail (EV_IS_SIDEBAR_LINKS (ev_sidebar_links));
102
103         document = EV_DOCUMENT (ev_sidebar_links->priv->current_document);
104         g_return_if_fail (ev_sidebar_links->priv->current_document != NULL);
105
106         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
107                 EvLink *link;
108                 GtkWidget *window;
109                 GValue value = {0, };
110
111                 gtk_tree_model_get_value (model, &iter,
112                                           LINKS_COLUMN_LINK, &value);
113
114                 link = EV_LINK (g_value_get_object (&value));
115                 g_return_if_fail (link != NULL);
116
117                 window = gtk_widget_get_ancestor (GTK_WIDGET (ev_sidebar_links),
118                                                   EV_TYPE_WINDOW);
119                 if (window) {
120                         ev_window_open_link (EV_WINDOW (window), link);
121                 }
122         }
123 }
124
125 static void
126 ev_sidebar_links_construct (EvSidebarLinks *ev_sidebar_links)
127 {
128         EvSidebarLinksPrivate *priv;
129         GtkWidget *swindow;
130         GtkTreeViewColumn *column;
131         GtkCellRenderer *renderer;
132         GtkTreeSelection *selection;
133
134         priv = ev_sidebar_links->priv;
135         priv->model = (GtkTreeModel *) gtk_tree_store_new (LINKS_COLUMN_NUM_COLUMNS,
136                                                            G_TYPE_STRING,
137                                                            G_TYPE_INT,
138                                                            G_TYPE_BOOLEAN,
139                                                            G_TYPE_OBJECT);
140
141         swindow = gtk_scrolled_window_new (NULL, NULL);
142
143         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
144                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
145         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
146                                              GTK_SHADOW_IN);
147
148         /* Create tree view */
149         priv->tree_view = gtk_tree_view_new_with_model (priv->model);
150         g_object_unref (priv->model);
151         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
152         gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
153         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (priv->tree_view), TRUE);
154
155         gtk_box_pack_start (GTK_BOX (ev_sidebar_links), swindow, TRUE, TRUE, 0);
156         gtk_widget_show_all (GTK_WIDGET (ev_sidebar_links));
157
158         column = gtk_tree_view_column_new ();
159         gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
160         gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), column);
161
162         renderer = (GtkCellRenderer*)
163                 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
164                               "ellipsize", PANGO_ELLIPSIZE_END,
165                               NULL);
166         gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN (column), renderer, TRUE);
167         gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
168                                              "markup", LINKS_COLUMN_MARKUP,
169                                              NULL);
170
171         renderer = gtk_cell_renderer_text_new ();
172         gtk_tree_view_column_pack_end (GTK_TREE_VIEW_COLUMN (column), renderer, FALSE);
173         gtk_tree_view_column_set_cell_data_func (GTK_TREE_VIEW_COLUMN (column), renderer,
174                                                  (GtkTreeCellDataFunc) links_page_num_func,
175                                                  NULL, NULL);
176
177
178         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
179         g_signal_connect (selection, "changed",
180                           G_CALLBACK (selection_changed_cb),
181                           ev_sidebar_links);
182 }
183
184 static void
185 ev_sidebar_links_init (EvSidebarLinks *ev_sidebar_links)
186 {
187         ev_sidebar_links->priv = EV_SIDEBAR_LINKS_GET_PRIVATE (ev_sidebar_links);
188
189         ev_sidebar_links_construct (ev_sidebar_links);
190 }
191
192 static void
193 links_page_num_func (GtkTreeViewColumn *tree_column,
194                      GtkCellRenderer   *cell,
195                      GtkTreeModel      *tree_model,
196                      GtkTreeIter       *iter,
197                      gpointer           data)
198 {
199         int page_num;
200         gboolean page_valid;
201
202         gtk_tree_model_get (tree_model, iter,
203                             LINKS_COLUMN_PAGE_NUM, &page_num,
204                             LINKS_COLUMN_PAGE_VALID, &page_valid,
205                             -1);
206
207         if (page_valid) {
208                 gchar *markup = g_strdup_printf ("<i>%d</i>", page_num);
209                 g_object_set (cell,
210                               "markup", markup,
211                               "visible", TRUE,
212                               NULL);
213                 g_free (markup);
214         } else {
215                 g_object_set (cell,
216                               "visible", FALSE,
217                               NULL);
218         }
219 }
220
221 /* Public Functions */
222
223 GtkWidget *
224 ev_sidebar_links_new (void)
225 {
226         GtkWidget *ev_sidebar_links;
227
228         ev_sidebar_links = g_object_new (EV_TYPE_SIDEBAR_LINKS, NULL);
229
230         return ev_sidebar_links;
231 }
232
233 static void
234 stack_data_free (IdleStackData       *stack_data,
235                  EvDocumentLinks     *document_links)
236 {
237         g_assert (stack_data);
238
239         if (stack_data->tree_iter)
240                 gtk_tree_iter_free (stack_data->tree_iter);
241         if (stack_data->links_iter)
242                 ev_document_links_free_iter (document_links, stack_data->links_iter);
243         g_free (stack_data);
244 }
245
246 static gboolean
247 do_one_iteration (EvSidebarLinks *ev_sidebar_links)
248 {
249         EvSidebarLinksPrivate *priv = ev_sidebar_links->priv;
250         EvLink *link;
251         IdleStackData *stack_data;
252         GtkTreeIter tree_iter;
253         EvDocumentLinksIter *child_iter;
254         EvLinkType link_type;
255         gint page;
256
257         g_assert (priv->idle_stack);
258
259         stack_data = (IdleStackData *) priv->idle_stack->data;
260
261         link = ev_document_links_get_link
262                 (EV_DOCUMENT_LINKS (priv->current_document),
263                  stack_data->links_iter);
264         if (link == NULL) {
265                 g_warning ("mismatch in model.  No values available at current level.\n");
266                 return FALSE;
267         }
268
269         page = ev_link_get_page (link);
270         link_type = ev_link_get_link_type (link);
271         gtk_tree_store_append (GTK_TREE_STORE (priv->model), &tree_iter, stack_data->tree_iter);
272         gtk_tree_store_set (GTK_TREE_STORE (priv->model), &tree_iter,
273                             LINKS_COLUMN_MARKUP, ev_link_get_title (link),
274                             LINKS_COLUMN_PAGE_NUM, page,
275                             LINKS_COLUMN_PAGE_VALID, (link_type == EV_LINK_TYPE_PAGE),
276                             LINKS_COLUMN_LINK, link,
277                             -1);
278         g_object_unref (link);
279         
280         child_iter = ev_document_links_get_child (EV_DOCUMENT_LINKS (priv->current_document),
281                                                       stack_data->links_iter);
282         if (child_iter) {
283                 IdleStackData *child_stack_data;
284
285                 child_stack_data = g_new0 (IdleStackData, 1);
286                 child_stack_data->tree_iter = gtk_tree_iter_copy (&tree_iter);
287                 child_stack_data->links_iter = child_iter;
288                 priv->idle_stack = g_list_prepend (priv->idle_stack, child_stack_data);
289
290                 return TRUE;
291         }
292
293         /* We don't have children, so we need to walk to the next node */
294         while (TRUE) {
295                 if (ev_document_links_next (EV_DOCUMENT_LINKS (priv->current_document),
296                                                 stack_data->links_iter))
297                         return TRUE;
298
299                 /* We're done with this level.  Pop it off the idle stack and go
300                  * to the next level */
301                 stack_data_free (stack_data, EV_DOCUMENT_LINKS (priv->current_document));
302                 priv->idle_stack = g_list_delete_link (priv->idle_stack, priv->idle_stack);
303                 if (priv->idle_stack == NULL)
304                         return FALSE;
305                 stack_data = priv->idle_stack->data;
306         }
307 }
308
309 static gboolean
310 populate_links_idle (gpointer data)
311 {
312         GTimer *timer;
313         gint i;
314         gulong microseconds = 0;
315
316         EvSidebarLinks *ev_sidebar_links = (EvSidebarLinks *)data;
317         EvSidebarLinksPrivate *priv = ev_sidebar_links->priv;
318
319         if (priv->idle_stack == NULL) {
320                 priv->idle_id = 0;
321                 return FALSE;
322         }
323
324         /* The amount of time that reading the next bookmark takes is wildly
325          * inconsistent, so we constrain it to IDLE_WORK_LENGTH microseconds per
326          * idle iteration. */
327         timer = g_timer_new ();
328         i = 0;
329         g_timer_start (timer);
330         while (do_one_iteration (ev_sidebar_links)) {
331                 i++;
332                 g_timer_elapsed (timer, &microseconds);
333                 if (microseconds > IDLE_WORK_LENGTH)
334                         break;
335         }
336         g_timer_destroy (timer);
337 #if 0
338         g_print ("%d rows done this idle in %d\n", i, (int)microseconds);
339 #endif
340         return TRUE;
341 }
342
343 void
344 ev_sidebar_links_clear_document (EvSidebarLinks *sidebar_links)
345 {
346         EvSidebarLinksPrivate *priv;
347
348         g_return_if_fail (EV_IS_SIDEBAR_LINKS (sidebar_links));
349
350         priv = sidebar_links->priv;
351
352         /* Clear the idle */
353         if (priv->idle_id != 0) {
354                 g_source_remove (priv->idle_id);
355                 priv->idle_id = 0;
356         }
357         g_list_foreach (priv->idle_stack, (GFunc) stack_data_free, priv->current_document);
358         g_list_free (priv->idle_stack);
359         priv->idle_stack = NULL;
360
361         if (priv->current_document) {
362                 g_object_unref (priv->current_document);
363                 priv->current_document = NULL;
364         }
365         gtk_tree_store_clear (GTK_TREE_STORE (priv->model));
366 }
367
368 void
369 ev_sidebar_links_set_document (EvSidebarLinks *sidebar_links,
370                                EvDocument     *document)
371 {
372         EvSidebarLinksPrivate *priv;
373         EvDocumentLinksIter *links_iter;
374
375         g_return_if_fail (EV_IS_SIDEBAR_LINKS (sidebar_links));
376         g_return_if_fail (EV_IS_DOCUMENT (document));
377
378         priv = sidebar_links->priv;
379
380         g_object_ref (document);
381         ev_sidebar_links_clear_document (sidebar_links);
382
383         priv->current_document = document;
384         links_iter = ev_document_links_begin_read (EV_DOCUMENT_LINKS (document));
385         if (links_iter) {
386                 IdleStackData *stack_data;
387
388                 stack_data = g_new0 (IdleStackData, 1);
389                 stack_data->links_iter = links_iter;
390                 stack_data->tree_iter = NULL;
391
392                 priv->idle_stack = g_list_prepend (priv->idle_stack, stack_data);
393                 priv->idle_id = g_idle_add (populate_links_idle, sidebar_links);
394         }
395 }
396