]> www.fi.muni.cz Git - evince.git/blob - shell/ev-sidebar-bookmarks.c
shell: Add a new sidebar page for bookmarks
[evince.git] / shell / ev-sidebar-bookmarks.c
1 /* ev-sidebar-bookmarks.c
2  *  this file is part of evince, a gnome document viewer
3  *
4  * Copyright (C) 2010 Carlos Garcia Campos  <carlosgc@gnome.org>
5  *
6  * Evince is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Evince is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include <glib/gi18n.h>
24
25 #include "ev-sidebar-bookmarks.h"
26
27 #include "ev-document.h"
28 #include "ev-sidebar-page.h"
29
30 enum {
31         PROP_0,
32         PROP_WIDGET
33 };
34
35 enum {
36         COLUMN_MARKUP,
37         COLUMN_PAGE,
38         N_COLUMNS
39 };
40
41 enum {
42         ADD_BOOKMARK,
43         N_SIGNALS
44 };
45
46 struct _EvSidebarBookmarksPrivate {
47         EvDocumentModel *model;
48         EvBookmarks     *bookmarks;
49
50         GtkWidget       *tree_view;
51         GtkWidget       *del_button;
52         GtkWidget       *add_button;
53 };
54
55 static void ev_sidebar_bookmarks_page_iface_init (EvSidebarPageInterface *iface);
56
57 G_DEFINE_TYPE_EXTENDED (EvSidebarBookmarks,
58                         ev_sidebar_bookmarks,
59                         GTK_TYPE_VBOX,
60                         0,
61                         G_IMPLEMENT_INTERFACE (EV_TYPE_SIDEBAR_PAGE,
62                                                ev_sidebar_bookmarks_page_iface_init))
63
64 static guint signals[N_SIGNALS];
65
66 static gint
67 compare_bookmarks (EvBookmark *a,
68                    EvBookmark *b)
69 {
70         if (a->page < b->page)
71                 return -1;
72         if (a->page > b->page)
73                 return 1;
74         return 0;
75 }
76
77 static void
78 ev_sidebar_bookmarks_update (EvSidebarBookmarks *sidebar_bookmarks)
79 {
80         EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
81         GtkListStore              *model;
82         GList                     *items, *l;
83         GtkTreeIter                iter;
84
85         model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)));
86         gtk_list_store_clear (model);
87
88         if (!priv->bookmarks)
89                 return;
90
91         items = ev_bookmarks_get_bookmarks (priv->bookmarks);
92         items = g_list_sort (items, (GCompareFunc)compare_bookmarks);
93         for (l = items; l; l = g_list_next (l)) {
94                 EvBookmark *bm = (EvBookmark *)l->data;
95
96                 gtk_list_store_append (model, &iter);
97                 gtk_list_store_set (model, &iter,
98                                     COLUMN_MARKUP, bm->title,
99                                     COLUMN_PAGE, bm->page,
100                                     -1);
101         }
102         g_list_free (items);
103 }
104
105 static void
106 ev_sidebar_bookmarks_changed (EvBookmarks        *bookmarks,
107                               EvSidebarBookmarks *sidebar_bookmarks)
108 {
109         ev_sidebar_bookmarks_update (sidebar_bookmarks);
110 }
111
112 static gint
113 ev_sidebar_bookmarks_get_selected_page (EvSidebarBookmarks *sidebar_bookmarks,
114                                         GtkTreeSelection   *selection)
115 {
116         GtkTreeModel *model;
117         GtkTreeIter   iter;
118
119         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
120                 guint page;
121
122                 gtk_tree_model_get (model, &iter,
123                                     COLUMN_PAGE, &page,
124                                     -1);
125                 return page;
126         }
127
128         return -1;
129 }
130
131 static void
132 ev_sidebar_bookmarks_selection_changed (GtkTreeSelection   *selection,
133                                         EvSidebarBookmarks *sidebar_bookmarks)
134 {
135         EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
136         gint                       page;
137
138         page = ev_sidebar_bookmarks_get_selected_page (sidebar_bookmarks, selection);
139         if (page >= 0) {
140                 ev_document_model_set_page (priv->model, page);
141                 gtk_widget_set_sensitive (priv->del_button, TRUE);
142         } else {
143                 gtk_widget_set_sensitive (priv->del_button, FALSE);
144         }
145 }
146
147 static void
148 ev_sidebar_bookmarks_add_clicked (GtkWidget          *button,
149                                   EvSidebarBookmarks *sidebar_bookmarks)
150 {
151         /* Let the window add the bookmark since
152          * since we don't know the page title
153          */
154         g_signal_emit (sidebar_bookmarks, signals[ADD_BOOKMARK], 0);
155 }
156
157 static void
158 ev_sidebar_bookmarks_del_clicked (GtkWidget          *button,
159                                   EvSidebarBookmarks *sidebar_bookmarks)
160 {
161         EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
162         GtkTreeSelection          *selection;
163         gint                       page;
164         EvBookmark                 bm;
165
166         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
167         page = ev_sidebar_bookmarks_get_selected_page (sidebar_bookmarks, selection);
168         if (page < 0)
169                 return;
170
171         bm.page = page;
172         bm.title = NULL;
173         ev_bookmarks_delete (priv->bookmarks, &bm);
174 }
175
176 static void
177 ev_sidebar_bookmarks_bookmark_renamed (GtkCellRendererText *renderer,
178                                        const gchar         *path_string,
179                                        const gchar         *new_text,
180                                        EvSidebarBookmarks  *sidebar_bookmarks)
181 {
182         EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
183         GtkTreePath               *path = gtk_tree_path_new_from_string (path_string);
184         GtkTreeModel              *model;
185         GtkTreeIter                iter;
186         guint                      page;
187         EvBookmark                 bm;
188
189         if (!new_text || new_text[0] == '\0')
190                 return;
191
192         model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view));
193         gtk_tree_model_get_iter (model, &iter, path);
194         gtk_tree_model_get (model, &iter,
195                             COLUMN_PAGE, &page,
196                             -1);
197         gtk_tree_path_free (path);
198
199         bm.page = page;
200         bm.title = g_strdup (new_text);
201         ev_bookmarks_update (priv->bookmarks, &bm);
202 }
203
204 static void
205 ev_sidebar_bookmarks_dispose (GObject *object)
206 {
207         EvSidebarBookmarks *sidebar_bookmarks = EV_SIDEBAR_BOOKMARKS (object);
208         EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
209
210         if (priv->model) {
211                 g_object_unref (priv->model);
212                 priv->model = NULL;
213         }
214
215         if (priv->bookmarks) {
216                 g_object_unref (priv->bookmarks);
217                 priv->bookmarks = NULL;
218         }
219
220         G_OBJECT_CLASS (ev_sidebar_bookmarks_parent_class)->dispose (object);
221 }
222
223 static void
224 ev_sidebar_bookmarks_init (EvSidebarBookmarks *sidebar_bookmarks)
225 {
226         EvSidebarBookmarksPrivate *priv;
227         GtkWidget                 *swindow;
228         GtkWidget                 *hbox;
229         GtkListStore              *model;
230         GtkCellRenderer           *renderer;
231         GtkTreeSelection          *selection;
232
233         sidebar_bookmarks->priv = G_TYPE_INSTANCE_GET_PRIVATE (sidebar_bookmarks,
234                                                                EV_TYPE_SIDEBAR_BOOKMARKS,
235                                                                EvSidebarBookmarksPrivate);
236         priv = sidebar_bookmarks->priv;
237
238         gtk_box_set_spacing (GTK_BOX (sidebar_bookmarks), 6);
239
240         swindow = gtk_scrolled_window_new (NULL, NULL);
241         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
242                                         GTK_POLICY_AUTOMATIC,
243                                         GTK_POLICY_AUTOMATIC);
244         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
245                                              GTK_SHADOW_IN);
246         gtk_box_pack_start (GTK_BOX (sidebar_bookmarks), swindow, TRUE, TRUE, 0);
247         gtk_widget_show (swindow);
248
249         model = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_UINT);
250         priv->tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
251         g_object_unref (model);
252         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
253         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
254         g_signal_connect (selection, "changed",
255                           G_CALLBACK (ev_sidebar_bookmarks_selection_changed),
256                           sidebar_bookmarks);
257
258         renderer = gtk_cell_renderer_text_new ();
259         g_object_set (renderer,
260                       "ellipsize", PANGO_ELLIPSIZE_END,
261                       "editable", TRUE,
262                       NULL);
263         g_signal_connect (renderer, "edited",
264                           G_CALLBACK (ev_sidebar_bookmarks_bookmark_renamed),
265                           sidebar_bookmarks);
266         gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->tree_view),
267                                                      0, NULL, renderer,
268                                                      "markup", COLUMN_MARKUP,
269                                                      NULL);
270         gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
271         gtk_widget_show (priv->tree_view);
272
273         hbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
274
275         priv->add_button = gtk_button_new_from_stock (GTK_STOCK_ADD);
276         g_signal_connect (priv->add_button, "clicked",
277                           G_CALLBACK (ev_sidebar_bookmarks_add_clicked),
278                           sidebar_bookmarks);
279         gtk_widget_set_sensitive (priv->add_button, FALSE);
280         gtk_box_pack_start (GTK_BOX (hbox), priv->add_button, TRUE, TRUE, 6);
281         gtk_widget_show (priv->add_button);
282
283         priv->del_button = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
284         g_signal_connect (priv->del_button, "clicked",
285                           G_CALLBACK (ev_sidebar_bookmarks_del_clicked),
286                           sidebar_bookmarks);
287         gtk_widget_set_sensitive (priv->del_button, FALSE);
288         gtk_box_pack_start (GTK_BOX (hbox), priv->del_button, TRUE, TRUE, 6);
289         gtk_widget_show (priv->del_button);
290
291         gtk_box_pack_end (GTK_BOX (sidebar_bookmarks), hbox, FALSE, TRUE, 0);
292         gtk_widget_show (hbox);
293         gtk_widget_show (GTK_WIDGET (sidebar_bookmarks));
294 }
295
296 static void
297 ev_sidebar_bookmarks_get_property (GObject    *object,
298                                    guint       prop_id,
299                                    GValue     *value,
300                                    GParamSpec *pspec)
301 {
302         EvSidebarBookmarks *sidebar_bookmarks;
303
304         sidebar_bookmarks = EV_SIDEBAR_BOOKMARKS (object);
305
306         switch (prop_id) {
307         case PROP_WIDGET:
308                 g_value_set_object (value, sidebar_bookmarks->priv->tree_view);
309                 break;
310         default:
311                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
312                 break;
313         }
314 }
315
316 static void
317 ev_sidebar_bookmarks_class_init (EvSidebarBookmarksClass *klass)
318 {
319         GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
320
321         g_object_class->get_property = ev_sidebar_bookmarks_get_property;
322         g_object_class->dispose = ev_sidebar_bookmarks_dispose;
323
324         g_type_class_add_private (g_object_class, sizeof (EvSidebarBookmarksPrivate));
325
326         g_object_class_override_property (g_object_class, PROP_WIDGET, "main-widget");
327
328         signals[ADD_BOOKMARK] =
329                 g_signal_new ("add-bookmark",
330                               G_TYPE_FROM_CLASS (g_object_class),
331                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
332                               G_STRUCT_OFFSET (EvSidebarBookmarksClass, add_bookmark),
333                               NULL, NULL,
334                               g_cclosure_marshal_VOID__VOID,
335                               G_TYPE_NONE, 0,
336                               G_TYPE_NONE);
337 }
338
339 GtkWidget *
340 ev_sidebar_bookmarks_new (void)
341 {
342         return GTK_WIDGET (g_object_new (EV_TYPE_SIDEBAR_BOOKMARKS, NULL));
343 }
344
345 void
346 ev_sidebar_bookmarks_set_bookmarks (EvSidebarBookmarks *sidebar_bookmarks,
347                                     EvBookmarks        *bookmarks)
348 {
349         EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
350
351         g_return_if_fail (EV_IS_BOOKMARKS (bookmarks));
352
353         if (priv->bookmarks == bookmarks)
354                 return;
355
356         if (priv->bookmarks)
357                 g_object_unref (priv->bookmarks);
358         priv->bookmarks = g_object_ref (bookmarks);
359         g_signal_connect (priv->bookmarks, "changed",
360                           G_CALLBACK (ev_sidebar_bookmarks_changed),
361                           sidebar_bookmarks);
362
363         gtk_widget_set_sensitive (priv->add_button, TRUE);
364         ev_sidebar_bookmarks_update (sidebar_bookmarks);
365 }
366
367 /* EvSidebarPageIface */
368 static void
369 ev_sidebar_bookmarks_set_model (EvSidebarPage   *sidebar_page,
370                                 EvDocumentModel *model)
371 {
372         EvSidebarBookmarks *sidebar_bookmarks = EV_SIDEBAR_BOOKMARKS (sidebar_page);
373         EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
374
375         if (priv->model == model)
376                 return;
377
378         if (priv->model)
379                 g_object_unref (priv->model);
380         priv->model = g_object_ref (model);
381 }
382
383 static gboolean
384 ev_sidebar_bookmarks_support_document (EvSidebarPage *sidebar_page,
385                                        EvDocument    *document)
386 {
387         return TRUE;
388 }
389
390 static const gchar *
391 ev_sidebar_bookmarks_get_label (EvSidebarPage *sidebar_page)
392 {
393         return _("Bookmarks");
394 }
395
396 static void
397 ev_sidebar_bookmarks_page_iface_init (EvSidebarPageInterface *iface)
398 {
399         iface->support_document = ev_sidebar_bookmarks_support_document;
400         iface->set_model = ev_sidebar_bookmarks_set_model;
401         iface->get_label = ev_sidebar_bookmarks_get_label;
402 }