]> www.fi.muni.cz Git - evince.git/blob - shell/ev-sidebar-annotations.c
b1f084821851b12265fc9b723b00f1d7cedfbf05
[evince.git] / shell / ev-sidebar-annotations.c
1 /* ev-sidebar-annotations.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-document-annotations.h"
26 #include "ev-sidebar-page.h"
27 #include "ev-sidebar-annotations.h"
28 #include "ev-jobs.h"
29 #include "ev-job-scheduler.h"
30 #include "ev-stock-icons.h"
31
32 enum {
33         PROP_0,
34         PROP_WIDGET
35 };
36
37 enum {
38         COLUMN_MARKUP,
39         COLUMN_ICON,
40         COLUMN_ANNOT_MAPPING,
41         N_COLUMNS
42 };
43
44 enum {
45         ANNOT_ACTIVATED,
46         N_SIGNALS
47 };
48
49 struct _EvSidebarAnnotationsPrivate {
50         GtkWidget  *notebook;
51         GtkWidget  *tree_view;
52
53         EvJob      *job;
54         guint       selection_changed_id;
55 };
56
57 static void ev_sidebar_annotations_page_iface_init (EvSidebarPageInterface *iface);
58
59 static guint signals[N_SIGNALS];
60
61 G_DEFINE_TYPE_EXTENDED (EvSidebarAnnotations,
62                         ev_sidebar_annotations,
63                         GTK_TYPE_VBOX,
64                         0,
65                         G_IMPLEMENT_INTERFACE (EV_TYPE_SIDEBAR_PAGE,
66                                                ev_sidebar_annotations_page_iface_init))
67
68 #define EV_SIDEBAR_ANNOTATIONS_GET_PRIVATE(object) \
69         (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_ANNOTATIONS, EvSidebarAnnotationsPrivate))
70
71 static GtkTreeModel *
72 ev_sidebar_annotations_create_simple_model (const gchar *message)
73 {
74         GtkTreeModel *retval;
75         GtkTreeIter iter;
76         gchar *markup;
77
78         /* Creates a fake model to indicate that we're loading */
79         retval = (GtkTreeModel *)gtk_list_store_new (N_COLUMNS,
80                                                      G_TYPE_STRING,
81                                                      GDK_TYPE_PIXBUF,
82                                                      G_TYPE_POINTER);
83
84         gtk_list_store_append (GTK_LIST_STORE (retval), &iter);
85         markup = g_strdup_printf ("<span size=\"larger\" style=\"italic\">%s</span>",
86                                   message);
87         gtk_list_store_set (GTK_LIST_STORE (retval), &iter,
88                             COLUMN_MARKUP, markup,
89                             -1);
90         g_free (markup);
91
92         return retval;
93 }
94
95 static void
96 ev_sidebar_annotations_add_annots_list (EvSidebarAnnotations *ev_annots)
97 {
98         GtkWidget         *swindow;
99         GtkTreeModel      *loading_model;
100         GtkCellRenderer   *renderer;
101         GtkTreeViewColumn *column;
102         GtkTreeSelection  *selection;
103
104         swindow = gtk_scrolled_window_new (NULL, NULL);
105         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
106                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
107         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
108                                              GTK_SHADOW_IN);
109
110         /* Create tree view */
111         loading_model = ev_sidebar_annotations_create_simple_model (_("Loading…"));
112         ev_annots->priv->tree_view = gtk_tree_view_new_with_model (loading_model);
113         g_object_unref (loading_model);
114
115         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ev_annots->priv->tree_view),
116                                            FALSE);
117         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ev_annots->priv->tree_view));
118         gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
119
120         column = gtk_tree_view_column_new ();
121
122         renderer = gtk_cell_renderer_pixbuf_new ();
123         gtk_tree_view_column_pack_start (column, renderer, FALSE);
124         gtk_tree_view_column_set_attributes (column, renderer,
125                                              "pixbuf", COLUMN_ICON,
126                                              NULL);
127
128         renderer = gtk_cell_renderer_text_new ();
129         gtk_tree_view_column_pack_start (column, renderer, TRUE);
130         gtk_tree_view_column_set_attributes (column, renderer,
131                                              "markup", COLUMN_MARKUP,
132                                              NULL);
133         gtk_tree_view_append_column (GTK_TREE_VIEW (ev_annots->priv->tree_view),
134                                      column);
135
136         gtk_container_add (GTK_CONTAINER (swindow), ev_annots->priv->tree_view);
137         gtk_widget_show (ev_annots->priv->tree_view);
138
139         gtk_notebook_append_page (GTK_NOTEBOOK (ev_annots->priv->notebook),
140                                   swindow, NULL);
141         gtk_widget_show (swindow);
142 }
143
144 static void
145 ev_sidebar_annotations_init (EvSidebarAnnotations *ev_annots)
146 {
147         ev_annots->priv = EV_SIDEBAR_ANNOTATIONS_GET_PRIVATE (ev_annots);
148
149         ev_annots->priv->notebook = gtk_notebook_new ();
150         gtk_notebook_set_show_tabs (GTK_NOTEBOOK (ev_annots->priv->notebook), FALSE);
151         gtk_notebook_set_show_border (GTK_NOTEBOOK (ev_annots->priv->notebook), FALSE);
152         ev_sidebar_annotations_add_annots_list (ev_annots);
153         gtk_container_add (GTK_CONTAINER (ev_annots), ev_annots->priv->notebook);
154         gtk_widget_show (ev_annots->priv->notebook);
155 }
156
157 static void
158 ev_sidebar_annotations_get_property (GObject    *object,
159                                      guint       prop_id,
160                                      GValue     *value,
161                                      GParamSpec *pspec)
162 {
163         EvSidebarAnnotations *ev_sidebar_annots;
164
165         ev_sidebar_annots = EV_SIDEBAR_ANNOTATIONS (object);
166
167         switch (prop_id) {
168                 case PROP_WIDGET:
169                         g_value_set_object (value, ev_sidebar_annots->priv->notebook);
170                         break;
171                 default:
172                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
173                         break;
174         }
175 }
176
177 static void
178 ev_sidebar_annotations_class_init (EvSidebarAnnotationsClass *klass)
179 {
180         GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
181
182         g_object_class->get_property = ev_sidebar_annotations_get_property;
183
184         g_type_class_add_private (g_object_class, sizeof (EvSidebarAnnotationsPrivate));
185
186         g_object_class_override_property (g_object_class, PROP_WIDGET, "main-widget");
187
188         signals[ANNOT_ACTIVATED] =
189                 g_signal_new ("annot-activated",
190                               G_TYPE_FROM_CLASS (g_object_class),
191                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
192                               G_STRUCT_OFFSET (EvSidebarAnnotationsClass, annot_activated),
193                               NULL, NULL,
194                               g_cclosure_marshal_VOID__POINTER,
195                               G_TYPE_NONE, 1,
196                               G_TYPE_POINTER);
197 }
198
199 GtkWidget *
200 ev_sidebar_annotations_new (void)
201 {
202         return GTK_WIDGET (g_object_new (EV_TYPE_SIDEBAR_ANNOTATIONS, NULL));
203 }
204
205 static void
206 selection_changed_cb (GtkTreeSelection     *selection,
207                       EvSidebarAnnotations *sidebar_annots)
208 {
209         GtkTreeModel *model;
210         GtkTreeIter   iter;
211
212         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
213                 EvMapping *mapping = NULL;
214
215                 gtk_tree_model_get (model, &iter,
216                                     COLUMN_ANNOT_MAPPING, &mapping,
217                                     -1);
218                 if (mapping)
219                         g_signal_emit (sidebar_annots, signals[ANNOT_ACTIVATED], 0, mapping);
220         }
221 }
222
223 static void
224 job_finished_callback (EvJobAnnots          *job,
225                        EvSidebarAnnotations *sidebar_annots)
226 {
227         EvSidebarAnnotationsPrivate *priv;
228         GtkTreeStore *model;
229         GtkTreeSelection *selection;
230         GList *l;
231         GdkPixbuf *text_icon = NULL;
232         GdkPixbuf *attachment_icon = NULL;
233
234         priv = sidebar_annots->priv;
235
236         if (!job->annots) {
237                 GtkTreeModel *list;
238
239                 list = ev_sidebar_annotations_create_simple_model (_("Document contains no annotations"));
240                 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), list);
241                 g_object_unref (list);
242
243                 g_object_unref (job);
244                 priv->job = NULL;
245
246                 return;
247         }
248
249         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
250         gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
251         if (priv->selection_changed_id == 0) {
252                 priv->selection_changed_id =
253                         g_signal_connect (selection, "changed",
254                                           G_CALLBACK (selection_changed_cb),
255                                           sidebar_annots);
256         }
257
258         model = gtk_tree_store_new (N_COLUMNS,
259                                     G_TYPE_STRING,
260                                     GDK_TYPE_PIXBUF,
261                                     G_TYPE_POINTER);
262
263         for (l = job->annots; l; l = g_list_next (l)) {
264                 EvMappingList *mapping_list;
265                 GList         *ll;
266                 gchar         *page_label;
267                 GtkTreeIter    iter;
268                 gboolean       found = FALSE;
269
270                 mapping_list = (EvMappingList *)l->data;
271                 page_label = g_strdup_printf (_("Page %d"),
272                                               ev_mapping_list_get_page (mapping_list) + 1);
273                 gtk_tree_store_append (model, &iter, NULL);
274                 gtk_tree_store_set (model, &iter,
275                                     COLUMN_MARKUP, page_label,
276                                     -1);
277                 g_free (page_label);
278
279                 for (ll = ev_mapping_list_get_list (mapping_list); ll; ll = g_list_next (ll)) {
280                         EvAnnotation *annot;
281                         gchar        *label;
282                         gchar        *markup;
283                         GtkTreeIter   child_iter;
284                         GdkPixbuf    *pixbuf = NULL;
285
286                         annot = ((EvMapping *)(ll->data))->data;
287                         if (!EV_IS_ANNOTATION_MARKUP (annot))
288                                 continue;
289
290                         label = ev_annotation_markup_get_label (EV_ANNOTATION_MARKUP (annot));
291                         if (annot->modified) {
292                                 markup = g_strdup_printf ("<span weight=\"bold\">%s</span>\n%s",
293                                                           label, annot->modified);
294                         } else {
295                                 markup = g_strdup_printf ("<span weight=\"bold\">%s</span>", label);
296                         }
297                         g_free (label);
298
299                         if (EV_IS_ANNOTATION_TEXT (annot)) {
300                                 if (!text_icon) {
301                                         /* FIXME: use a better icon than EDIT */
302                                         text_icon = gtk_widget_render_icon (priv->tree_view,
303                                                                             GTK_STOCK_EDIT,
304                                                                             GTK_ICON_SIZE_BUTTON,
305                                                                             NULL);
306                                 }
307                                 pixbuf = text_icon;
308                         } else if (EV_IS_ANNOTATION_ATTACHMENT (annot)) {
309                                 if (!attachment_icon) {
310                                         attachment_icon = gtk_widget_render_icon (priv->tree_view,
311                                                                                   EV_STOCK_ATTACHMENT,
312                                                                                   GTK_ICON_SIZE_BUTTON,
313                                                                                   NULL);
314                                 }
315                                 pixbuf = attachment_icon;
316                         }
317
318                         gtk_tree_store_append (model, &child_iter, &iter);
319                         gtk_tree_store_set (model, &child_iter,
320                                             COLUMN_MARKUP, markup,
321                                             COLUMN_ICON, pixbuf,
322                                             COLUMN_ANNOT_MAPPING, ll->data,
323                                             -1);
324                         g_free (markup);
325                         found = TRUE;
326                 }
327
328                 if (!found)
329                         gtk_tree_store_remove (model, &iter);
330         }
331
332         gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view),
333                                  GTK_TREE_MODEL (model));
334         g_object_unref (model);
335
336         if (text_icon)
337                 g_object_unref (text_icon);
338         if (attachment_icon)
339                 g_object_unref (attachment_icon);
340
341         g_object_unref (job);
342         priv->job = NULL;
343 }
344
345
346 static void
347 ev_sidebar_annotations_document_changed_cb (EvDocumentModel      *model,
348                                             GParamSpec           *pspec,
349                                             EvSidebarAnnotations *sidebar_annots)
350 {
351         EvDocument *document = ev_document_model_get_document (model);
352         EvSidebarAnnotationsPrivate *priv = sidebar_annots->priv;
353
354         if (!EV_IS_DOCUMENT_ANNOTATIONS (document))
355                 return;
356
357         if (priv->job) {
358                 g_signal_handlers_disconnect_by_func (priv->job,
359                                                       job_finished_callback,
360                                                       sidebar_annots);
361                 g_object_unref (priv->job);
362         }
363
364         priv->job = ev_job_annots_new (document);
365         g_signal_connect (priv->job, "finished",
366                           G_CALLBACK (job_finished_callback),
367                           sidebar_annots);
368         /* The priority doesn't matter for this job */
369         ev_job_scheduler_push_job (priv->job, EV_JOB_PRIORITY_NONE);
370 }
371
372 /* EvSidebarPageIface */
373 static void
374 ev_sidebar_annotations_set_model (EvSidebarPage   *sidebar_page,
375                                   EvDocumentModel *model)
376 {
377         g_signal_connect (model, "notify::document",
378                           G_CALLBACK (ev_sidebar_annotations_document_changed_cb),
379                           sidebar_page);
380 }
381
382 static gboolean
383 ev_sidebar_annotations_support_document (EvSidebarPage *sidebar_page,
384                                          EvDocument    *document)
385 {
386         return (EV_IS_DOCUMENT_ANNOTATIONS (document));
387 }
388
389 static const gchar *
390 ev_sidebar_annotations_get_label (EvSidebarPage *sidebar_page)
391 {
392         return _("Annotations");
393 }
394
395 static void
396 ev_sidebar_annotations_page_iface_init (EvSidebarPageInterface *iface)
397 {
398         iface->support_document = ev_sidebar_annotations_support_document;
399         iface->set_model = ev_sidebar_annotations_set_model;
400         iface->get_label = ev_sidebar_annotations_get_label;
401 }