Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ev-sidebar-annotations.c
Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include "config.h"
22 
23 #include <glib/gi18n.h>
24 
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 {
35 };
36 
37 enum {
42 };
43 
44 enum {
47 };
48 
51 
52  GtkWidget *swindow;
53  GtkWidget *tree_view;
54 
57 };
58 
60 static void ev_sidebar_annotations_load (EvSidebarAnnotations *sidebar_annots);
61 
62 static guint signals[N_SIGNALS];
63 
65  ev_sidebar_annotations,
66  GTK_TYPE_BOX,
67  0,
68  G_IMPLEMENT_INTERFACE (EV_TYPE_SIDEBAR_PAGE,
70 
71 #define EV_SIDEBAR_ANNOTATIONS_GET_PRIVATE(object) \
72  (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_ANNOTATIONS, EvSidebarAnnotationsPrivate))
73 
74 static void
75 ev_sidebar_annotations_dispose (GObject *object)
76 {
77  EvSidebarAnnotations *sidebar_annots = EV_SIDEBAR_ANNOTATIONS (object);
78  EvSidebarAnnotationsPrivate *priv = sidebar_annots->priv;
79 
80  if (priv->document) {
81  g_object_unref (priv->document);
82  priv->document = NULL;
83  }
84 
85  G_OBJECT_CLASS (ev_sidebar_annotations_parent_class)->dispose (object);
86 }
87 
88 static GtkTreeModel *
90 {
91  GtkTreeModel *retval;
92  GtkTreeIter iter;
93  gchar *markup;
94 
95  /* Creates a fake model to indicate that we're loading */
96  retval = (GtkTreeModel *)gtk_list_store_new (N_COLUMNS,
97  G_TYPE_STRING,
98  GDK_TYPE_PIXBUF,
99  G_TYPE_POINTER);
100 
101  gtk_list_store_append (GTK_LIST_STORE (retval), &iter);
102  markup = g_strdup_printf ("<span size=\"larger\" style=\"italic\">%s</span>",
103  message);
104  gtk_list_store_set (GTK_LIST_STORE (retval), &iter,
105  COLUMN_MARKUP, markup,
106  -1);
107  g_free (markup);
108 
109  return retval;
110 }
111 
112 static void
114 {
115  GtkTreeModel *loading_model;
116  GtkCellRenderer *renderer;
117  GtkTreeViewColumn *column;
118  GtkTreeSelection *selection;
119 
120  ev_annots->priv = EV_SIDEBAR_ANNOTATIONS_GET_PRIVATE (ev_annots);
121 
122  ev_annots->priv->swindow = gtk_scrolled_window_new (NULL, NULL);
123 
124  /* Create tree view */
125  loading_model = ev_sidebar_annotations_create_simple_model (_("Loading…"));
126  ev_annots->priv->tree_view = gtk_tree_view_new_with_model (loading_model);
127  g_object_unref (loading_model);
128 
129  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ev_annots->priv->tree_view),
130  FALSE);
131  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ev_annots->priv->tree_view));
132  gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
133 
134  column = gtk_tree_view_column_new ();
135 
136  renderer = gtk_cell_renderer_pixbuf_new ();
137  gtk_tree_view_column_pack_start (column, renderer, FALSE);
138  gtk_tree_view_column_set_attributes (column, renderer,
139  "pixbuf", COLUMN_ICON,
140  NULL);
141 
142  renderer = gtk_cell_renderer_text_new ();
143  gtk_tree_view_column_pack_start (column, renderer, TRUE);
144  gtk_tree_view_column_set_attributes (column, renderer,
145  "markup", COLUMN_MARKUP,
146  NULL);
147  gtk_tree_view_append_column (GTK_TREE_VIEW (ev_annots->priv->tree_view),
148  column);
149 
150  gtk_container_add (GTK_CONTAINER (ev_annots->priv->swindow), ev_annots->priv->tree_view);
151  gtk_widget_show (ev_annots->priv->tree_view);
152 
153  gtk_box_pack_start (GTK_BOX (ev_annots), ev_annots->priv->swindow, TRUE, TRUE, 0);
154  gtk_widget_show (ev_annots->priv->swindow);
155 }
156 
157 static void
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->swindow);
170  break;
171  default:
172  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
173  break;
174  }
175 }
176 
177 static void
179 {
180  GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
181 
182  g_object_class->get_property = ev_sidebar_annotations_get_property;
183  g_object_class->dispose = ev_sidebar_annotations_dispose;
184 
185  g_type_class_add_private (g_object_class, sizeof (EvSidebarAnnotationsPrivate));
186 
187  g_object_class_override_property (g_object_class, PROP_WIDGET, "main-widget");
188 
190  g_signal_new ("annot-activated",
191  G_TYPE_FROM_CLASS (g_object_class),
192  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
193  G_STRUCT_OFFSET (EvSidebarAnnotationsClass, annot_activated),
194  NULL, NULL,
195  g_cclosure_marshal_VOID__POINTER,
196  G_TYPE_NONE, 1,
197  G_TYPE_POINTER);
198 }
199 
200 GtkWidget *
202 {
203  return GTK_WIDGET (g_object_new (EV_TYPE_SIDEBAR_ANNOTATIONS,
204  "orientation", GTK_ORIENTATION_VERTICAL,
205  NULL));
206 }
207 
208 void
210  EvAnnotation *annot)
211 {
212  ev_sidebar_annotations_load (sidebar_annots);
213 }
214 
215 void
217 {
218  ev_sidebar_annotations_load (sidebar_annots);
219 }
220 
221 static void
222 selection_changed_cb (GtkTreeSelection *selection,
223  EvSidebarAnnotations *sidebar_annots)
224 {
225  GtkTreeModel *model;
226  GtkTreeIter iter;
227 
228  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
229  EvMapping *mapping = NULL;
230 
231  gtk_tree_model_get (model, &iter,
232  COLUMN_ANNOT_MAPPING, &mapping,
233  -1);
234  if (mapping)
235  g_signal_emit (sidebar_annots, signals[ANNOT_ACTIVATED], 0, mapping);
236  }
237 }
238 
239 static void
241  EvSidebarAnnotations *sidebar_annots)
242 {
244  GtkTreeStore *model;
245  GtkTreeSelection *selection;
246  GList *l;
247  GdkPixbuf *text_icon = NULL;
248  GdkPixbuf *attachment_icon = NULL;
249  GdkPixbuf *highlight_icon = NULL;
250  GdkPixbuf *strike_out_icon = NULL;
251  GdkPixbuf *underline_icon = NULL;
252  GdkPixbuf *squiggly_icon = NULL;
253 
254  priv = sidebar_annots->priv;
255 
256  if (!job->annots) {
257  GtkTreeModel *list;
258 
259  list = ev_sidebar_annotations_create_simple_model (_("Document contains no annotations"));
260  gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), list);
261  g_object_unref (list);
262 
263  g_object_unref (job);
264  priv->job = NULL;
265 
266  return;
267  }
268 
269  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
270  gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
271  if (priv->selection_changed_id == 0) {
272  priv->selection_changed_id =
273  g_signal_connect (selection, "changed",
274  G_CALLBACK (selection_changed_cb),
275  sidebar_annots);
276  }
277 
278  model = gtk_tree_store_new (N_COLUMNS,
279  G_TYPE_STRING,
280  GDK_TYPE_PIXBUF,
281  G_TYPE_POINTER);
282 
283  for (l = job->annots; l; l = g_list_next (l)) {
284  EvMappingList *mapping_list;
285  GList *ll;
286  gchar *page_label;
287  GtkTreeIter iter;
288  gboolean found = FALSE;
289 
290  mapping_list = (EvMappingList *)l->data;
291  page_label = g_strdup_printf (_("Page %d"),
292  ev_mapping_list_get_page (mapping_list) + 1);
293  gtk_tree_store_append (model, &iter, NULL);
294  gtk_tree_store_set (model, &iter,
295  COLUMN_MARKUP, page_label,
296  -1);
297  g_free (page_label);
298 
299  for (ll = ev_mapping_list_get_list (mapping_list); ll; ll = g_list_next (ll)) {
300  EvAnnotation *annot;
301  const gchar *label;
302  const gchar *modified;
303  gchar *markup;
304  GtkTreeIter child_iter;
305  GdkPixbuf *pixbuf = NULL;
306 
307  annot = ((EvMapping *)(ll->data))->data;
308  if (!EV_IS_ANNOTATION_MARKUP (annot))
309  continue;
310 
312  modified = ev_annotation_get_modified (annot);
313  if (modified) {
314  markup = g_strdup_printf ("<span weight=\"bold\">%s</span>\n%s",
315  label, modified);
316  } else {
317  markup = g_strdup_printf ("<span weight=\"bold\">%s</span>", label);
318  }
319 
320  if (EV_IS_ANNOTATION_TEXT (annot)) {
321  if (!text_icon) {
322  /* FIXME: use a better icon than EDIT */
323  text_icon = gtk_widget_render_icon_pixbuf (priv->tree_view,
324  GTK_STOCK_EDIT,
325  GTK_ICON_SIZE_BUTTON);
326  }
327  pixbuf = text_icon;
328  } else if (EV_IS_ANNOTATION_ATTACHMENT (annot)) {
329  if (!attachment_icon) {
330  attachment_icon = gtk_widget_render_icon_pixbuf (priv->tree_view,
332  GTK_ICON_SIZE_BUTTON);
333  }
334  pixbuf = attachment_icon;
335  } else if (EV_IS_ANNOTATION_TEXT_MARKUP (annot)) {
338  if (!highlight_icon) {
339  /* FIXME: use better icon than select all */
340  highlight_icon = gtk_widget_render_icon_pixbuf (priv->tree_view,
341  GTK_STOCK_SELECT_ALL,
342  GTK_ICON_SIZE_BUTTON);
343  }
344  pixbuf = highlight_icon;
345 
346  break;
348  if (!strike_out_icon) {
349  strike_out_icon = gtk_widget_render_icon_pixbuf (priv->tree_view,
350  GTK_STOCK_STRIKETHROUGH,
351  GTK_ICON_SIZE_BUTTON);
352  }
353  pixbuf = strike_out_icon;
354  break;
356  if (!underline_icon) {
357  underline_icon = gtk_widget_render_icon_pixbuf (priv->tree_view,
358  GTK_STOCK_UNDERLINE,
359  GTK_ICON_SIZE_BUTTON);
360  }
361  pixbuf = underline_icon;
362  break;
364  if (!squiggly_icon) {
365  squiggly_icon = gtk_widget_render_icon_pixbuf (priv->tree_view,
366  GTK_STOCK_UNDERLINE,
367  GTK_ICON_SIZE_BUTTON);
368  }
369  pixbuf = squiggly_icon;
370  break;
371  }
372  }
373 
374  gtk_tree_store_append (model, &child_iter, &iter);
375  gtk_tree_store_set (model, &child_iter,
376  COLUMN_MARKUP, markup,
377  COLUMN_ICON, pixbuf,
378  COLUMN_ANNOT_MAPPING, ll->data,
379  -1);
380  g_free (markup);
381  found = TRUE;
382  }
383 
384  if (!found)
385  gtk_tree_store_remove (model, &iter);
386  }
387 
388  gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view),
389  GTK_TREE_MODEL (model));
390  g_object_unref (model);
391 
392  if (text_icon)
393  g_object_unref (text_icon);
394  if (attachment_icon)
395  g_object_unref (attachment_icon);
396  if (highlight_icon)
397  g_object_unref (highlight_icon);
398  if (strike_out_icon)
399  g_object_unref (strike_out_icon);
400  if (underline_icon)
401  g_object_unref (underline_icon);
402  if (squiggly_icon)
403  g_object_unref (squiggly_icon);
404 
405  g_object_unref (job);
406  priv->job = NULL;
407 }
408 
409 static void
411 {
412  EvSidebarAnnotationsPrivate *priv = sidebar_annots->priv;
413 
414  if (priv->job) {
415  g_signal_handlers_disconnect_by_func (priv->job,
417  sidebar_annots);
418  g_object_unref (priv->job);
419  }
420 
421  priv->job = ev_job_annots_new (priv->document);
422  g_signal_connect (priv->job, "finished",
423  G_CALLBACK (job_finished_callback),
424  sidebar_annots);
425  /* The priority doesn't matter for this job */
427 }
428 
429 static void
431  GParamSpec *pspec,
432  EvSidebarAnnotations *sidebar_annots)
433 {
434  EvDocument *document = ev_document_model_get_document (model);
435  EvSidebarAnnotationsPrivate *priv = sidebar_annots->priv;
436 
437  if (!EV_IS_DOCUMENT_ANNOTATIONS (document))
438  return;
439 
440  if (priv->document)
441  g_object_unref (priv->document);
442  priv->document = g_object_ref (document);
443 
444  ev_sidebar_annotations_load (sidebar_annots);
445 }
446 
447 /* EvSidebarPageIface */
448 static void
450  EvDocumentModel *model)
451 {
452  g_signal_connect (model, "notify::document",
454  sidebar_page);
455 }
456 
457 static gboolean
459  EvDocument *document)
460 {
461  return (EV_IS_DOCUMENT_ANNOTATIONS (document));
462 }
463 
464 static const gchar *
466 {
467  return _("Annotations");
468 }
469 
470 static void
472 {
476 }