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-bookmarks.c
Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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-document-misc.h"
29 #include "ev-sidebar-page.h"
30 #include "ev-utils.h"
31 
32 enum {
35 };
36 
37 enum {
41 };
42 
46 
47  GtkWidget *tree_view;
48  GtkWidget *del_button;
49  GtkWidget *add_button;
50 
51  /* Popup menu */
52  GtkWidget *popup;
53  GtkUIManager *ui_manager;
54  GtkActionGroup *action_group;
55 };
56 
58 
60  ev_sidebar_bookmarks,
61  GTK_TYPE_BOX,
62  0,
63  G_IMPLEMENT_INTERFACE (EV_TYPE_SIDEBAR_PAGE,
65 
66 static const gchar popup_menu_ui[] =
67  "<popup name=\"BookmarksPopup\" action=\"BookmarksPopupAction\">\n"
68  " <menuitem name=\"OpenBookmark\" action=\"OpenBookmark\"/>\n"
69  " <separator/>\n"
70  " <menuitem name=\"RenameBookmark\" action=\"RenameBookmark\"/>\n"
71  " <menuitem name=\"RemoveBookmark\" action=\"RemoveBookmark\"/>\n"
72  "</popup>\n";
73 
74 static gint
75 ev_sidebar_bookmarks_get_selected_page (EvSidebarBookmarks *sidebar_bookmarks,
76  GtkTreeSelection *selection)
77 {
78  GtkTreeModel *model;
79  GtkTreeIter iter;
80 
81  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
82  guint page;
83 
84  gtk_tree_model_get (model, &iter,
85  COLUMN_PAGE, &page,
86  -1);
87  return page;
88  }
89 
90  return -1;
91 }
92 
93 static void
95  EvSidebarBookmarks *sidebar_bookmarks)
96 {
97  EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
98  GtkTreeSelection *selection;
99  gint page;
100 
101  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
102  page = ev_sidebar_bookmarks_get_selected_page (sidebar_bookmarks, selection);
103  ev_document_model_set_page (priv->model, page);
104 }
105 
106 static void
108  EvSidebarBookmarks *sidebar_bookmarks)
109 {
110  EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
111  GtkTreeView *tree_view = GTK_TREE_VIEW (priv->tree_view);
112  GtkTreeSelection *selection;
113  GtkTreeModel *model;
114  GtkTreeIter iter;
115 
116 
117  selection = gtk_tree_view_get_selection (tree_view);
118  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
119  GtkTreePath *path;
120 
121  path = gtk_tree_model_get_path (model, &iter);
122  gtk_tree_view_set_cursor (tree_view, path,
123  gtk_tree_view_get_column (tree_view, 0),
124  TRUE);
125  gtk_tree_path_free (path);
126  }
127 }
128 
129 static void
131  EvSidebarBookmarks *sidebar_bookmarks)
132 {
133  EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
134  GtkTreeSelection *selection;
135  gint page;
136  EvBookmark bm;
137 
138  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
139  page = ev_sidebar_bookmarks_get_selected_page (sidebar_bookmarks, selection);
140  bm.page = page;
141  bm.title = NULL;
142  ev_bookmarks_delete (priv->bookmarks, &bm);
143 }
144 
145 static const GtkActionEntry popup_entries[] = {
146  { "OpenBookmark", GTK_STOCK_OPEN, N_("_Open Bookmark"), NULL,
147  NULL, G_CALLBACK (ev_bookmarks_popup_cmd_open_bookmark) },
148  { "RenameBookmark", NULL, N_("_Rename Bookmark"), NULL,
149  NULL, G_CALLBACK (ev_bookmarks_popup_cmd_rename_bookmark) },
150  { "RemoveBookmark", NULL, N_("_Remove Bookmark"), NULL,
151  NULL, G_CALLBACK (ev_bookmarks_popup_cmd_remove_bookmark) }
152 };
153 
154 static gint
156  EvBookmark *b)
157 {
158  if (a->page < b->page)
159  return -1;
160  if (a->page > b->page)
161  return 1;
162  return 0;
163 }
164 
165 static void
167 {
168  EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
169  GtkListStore *model;
170  GList *items, *l;
171  GtkTreeIter iter;
172 
173  model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)));
174  gtk_list_store_clear (model);
175 
176  if (!priv->bookmarks) {
177  g_object_set (priv->tree_view, "has-tooltip", FALSE, NULL);
178  return;
179  }
180 
181  items = ev_bookmarks_get_bookmarks (priv->bookmarks);
182  items = g_list_sort (items, (GCompareFunc)compare_bookmarks);
183  for (l = items; l; l = g_list_next (l)) {
184  EvBookmark *bm = (EvBookmark *)l->data;
185 
186  gtk_list_store_append (model, &iter);
187  gtk_list_store_set (model, &iter,
188  COLUMN_MARKUP, bm->title,
189  COLUMN_PAGE, bm->page,
190  -1);
191  }
192  g_list_free (items);
193  g_object_set (priv->tree_view, "has-tooltip", TRUE, NULL);
194 }
195 
196 static void
198  EvSidebarBookmarks *sidebar_bookmarks)
199 {
200  ev_sidebar_bookmarks_update (sidebar_bookmarks);
201 }
202 
203 static void
204 ev_sidebar_bookmarks_selection_changed (GtkTreeSelection *selection,
205  EvSidebarBookmarks *sidebar_bookmarks)
206 {
207  EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
208  gint page;
209 
210  page = ev_sidebar_bookmarks_get_selected_page (sidebar_bookmarks, selection);
211  if (page >= 0) {
212  ev_document_model_set_page (priv->model, page);
213  gtk_widget_set_sensitive (priv->del_button, TRUE);
214  } else {
215  gtk_widget_set_sensitive (priv->del_button, FALSE);
216  }
217 }
218 
219 static void
221  EvSidebarBookmarks *sidebar_bookmarks)
222 {
223  EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
224  GtkTreeSelection *selection;
225  gint page;
226  EvBookmark bm;
227 
228  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
229  page = ev_sidebar_bookmarks_get_selected_page (sidebar_bookmarks, selection);
230  if (page < 0)
231  return;
232 
233  bm.page = page;
234  bm.title = NULL;
235  ev_bookmarks_delete (priv->bookmarks, &bm);
236 }
237 
238 static void
239 ev_sidebar_bookmarks_bookmark_renamed (GtkCellRendererText *renderer,
240  const gchar *path_string,
241  const gchar *new_text,
242  EvSidebarBookmarks *sidebar_bookmarks)
243 {
244  EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
245  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
246  GtkTreeModel *model;
247  GtkTreeIter iter;
248  guint page;
249  EvBookmark bm;
250 
251  if (!new_text || new_text[0] == '\0')
252  return;
253 
254  model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view));
255  gtk_tree_model_get_iter (model, &iter, path);
256  gtk_tree_model_get (model, &iter,
257  COLUMN_PAGE, &page,
258  -1);
259  gtk_tree_path_free (path);
260 
261  bm.page = page;
262  bm.title = g_markup_escape_text (new_text, -1);
263  ev_bookmarks_update (priv->bookmarks, &bm);
264 }
265 
266 static gboolean
268  gint x,
269  gint y,
270  gboolean keyboard_tip,
271  GtkTooltip *tooltip,
272  EvSidebarBookmarks *sidebar_bookmarks)
273 {
274  EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
275  GtkTreeModel *model;
276  GtkTreeIter iter;
277  GtkTreePath *path = NULL;
278  EvDocument *document;
279  guint page;
280  gchar *page_label;
281  gchar *text;
282 
283  model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view));
284  if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (priv->tree_view),
285  &x, &y, keyboard_tip,
286  &model, &path, &iter))
287  return FALSE;
288 
289  gtk_tree_model_get (model, &iter,
290  COLUMN_PAGE, &page,
291  -1);
292 
293  document = ev_document_model_get_document (priv->model);
294  page_label = ev_document_get_page_label (document, page);
295  text = g_strdup_printf (_("Page %s"), page_label);
296  gtk_tooltip_set_text (tooltip, text);
297  g_free (text);
298  g_free (page_label);
299 
300  gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (priv->tree_view),
301  tooltip, path);
302  gtk_tree_path_free (path);
303 
304  return TRUE;
305 }
306 
307 static gboolean
309  gint x,
310  gint y,
311  gboolean keyboard_mode)
312 {
313  EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
314  GtkTreeView *tree_view = GTK_TREE_VIEW (sidebar_bookmarks->priv->tree_view);
315  GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
316 
317  if (keyboard_mode) {
318  if (!gtk_tree_selection_get_selected (selection, NULL, NULL))
319  return FALSE;
320  } else {
321  GtkTreePath *path;
322 
323  if (!gtk_tree_view_get_path_at_pos (tree_view, x, y, &path, NULL, NULL, NULL))
324  return FALSE;
325 
326  g_signal_handlers_block_by_func (selection,
328  sidebar_bookmarks);
329  gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
330  g_signal_handlers_unblock_by_func (selection,
332  sidebar_bookmarks);
333  gtk_tree_path_free (path);
334  }
335 
336  if (!priv->popup)
337  priv->popup = gtk_ui_manager_get_widget (priv->ui_manager, "/BookmarksPopup");
338 
339  gtk_menu_popup (GTK_MENU (priv->popup),
340  NULL, NULL,
341  keyboard_mode ? ev_gui_menu_position_tree_selection : NULL,
342  keyboard_mode ? tree_view : NULL,
343  keyboard_mode ? 0 : 3,
344  gtk_get_current_event_time ());
345  return TRUE;
346 }
347 
348 static gboolean
350  GdkEventButton *event,
351  EvSidebarBookmarks *sidebar_bookmarks)
352 {
353  if (event->button != 3)
354  return FALSE;
355 
356  return ev_sidebar_bookmarks_popup_menu_show (sidebar_bookmarks, event->x, event->y, FALSE);
357 }
358 
359 static gboolean
361 {
362  EvSidebarBookmarks *sidebar_bookmarks = EV_SIDEBAR_BOOKMARKS (widget);
363  gint x, y;
364 
365  ev_document_misc_get_pointer_position (widget, &x, &y);
366  return ev_sidebar_bookmarks_popup_menu_show (sidebar_bookmarks, x, y, TRUE);
367 }
368 
369 static void
371 {
372  EvSidebarBookmarks *sidebar_bookmarks = EV_SIDEBAR_BOOKMARKS (object);
373  EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
374 
375  if (priv->model) {
376  g_object_unref (priv->model);
377  priv->model = NULL;
378  }
379 
380  if (priv->bookmarks) {
381  g_object_unref (priv->bookmarks);
382  priv->bookmarks = NULL;
383  }
384 
385  if (priv->action_group) {
386  g_object_unref (priv->action_group);
387  priv->action_group = NULL;
388  }
389 
390  if (priv->ui_manager) {
391  g_object_unref (priv->ui_manager);
392  priv->ui_manager = NULL;
393  }
394 
395  G_OBJECT_CLASS (ev_sidebar_bookmarks_parent_class)->dispose (object);
396 }
397 
398 static void
400 {
402  GtkWidget *swindow;
403  GtkWidget *hbox;
404  GtkListStore *model;
405  GtkCellRenderer *renderer;
406  GtkTreeSelection *selection;
407 
408  sidebar_bookmarks->priv = G_TYPE_INSTANCE_GET_PRIVATE (sidebar_bookmarks,
411  priv = sidebar_bookmarks->priv;
412 
413  swindow = gtk_scrolled_window_new (NULL, NULL);
414  gtk_box_pack_start (GTK_BOX (sidebar_bookmarks), swindow, TRUE, TRUE, 0);
415  gtk_widget_show (swindow);
416 
417  model = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_UINT);
418  priv->tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
419  g_object_unref (model);
420  g_signal_connect (priv->tree_view, "query-tooltip",
422  sidebar_bookmarks);
423  g_signal_connect (priv->tree_view,
424  "button-press-event",
426  sidebar_bookmarks);
427  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
428  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
429  g_signal_connect (selection, "changed",
431  sidebar_bookmarks);
432 
433  renderer = gtk_cell_renderer_text_new ();
434  g_object_set (renderer,
435  "ellipsize", PANGO_ELLIPSIZE_END,
436  "editable", TRUE,
437  NULL);
438  g_signal_connect (renderer, "edited",
440  sidebar_bookmarks);
441  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->tree_view),
442  0, NULL, renderer,
443  "markup", COLUMN_MARKUP,
444  NULL);
445  gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
446  gtk_widget_show (priv->tree_view);
447 
448  hbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
449  gtk_widget_set_margin_top (hbox, 2);
450  gtk_widget_set_margin_bottom (hbox, 2);
451  gtk_widget_set_margin_start (hbox, 2);
452  gtk_widget_set_margin_end (hbox, 2);
453  gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_CENTER);
454  gtk_style_context_add_class (gtk_widget_get_style_context (hbox), GTK_STYLE_CLASS_LINKED);
455 
456  priv->add_button = gtk_button_new_with_label (_("Add"));
457  gtk_actionable_set_action_name (GTK_ACTIONABLE (priv->add_button),
458  "win.add-bookmark");
459  gtk_widget_set_sensitive (priv->add_button, FALSE);
460  gtk_box_pack_start (GTK_BOX (hbox), priv->add_button, FALSE, FALSE, 0);
461  gtk_widget_show (priv->add_button);
462 
463  priv->del_button = gtk_button_new_with_label (_("Remove"));
464  g_signal_connect (priv->del_button, "clicked",
466  sidebar_bookmarks);
467  gtk_widget_set_sensitive (priv->del_button, FALSE);
468  gtk_box_pack_start (GTK_BOX (hbox), priv->del_button, FALSE, FALSE, 0);
469  gtk_widget_show (priv->del_button);
470 
471  gtk_box_pack_end (GTK_BOX (sidebar_bookmarks), hbox, FALSE, TRUE, 0);
472  gtk_widget_show (hbox);
473  gtk_widget_show (GTK_WIDGET (sidebar_bookmarks));
474 
475  /* Popup menu */
476  priv->action_group = gtk_action_group_new ("BookmarsPopupActions");
477  gtk_action_group_set_translation_domain (priv->action_group, NULL);
478  gtk_action_group_add_actions (priv->action_group, popup_entries,
479  G_N_ELEMENTS (popup_entries),
480  sidebar_bookmarks);
481  priv->ui_manager = gtk_ui_manager_new ();
482  gtk_ui_manager_insert_action_group (priv->ui_manager,
483  priv->action_group, 0);
484  gtk_ui_manager_add_ui_from_string (priv->ui_manager, popup_menu_ui, -1, NULL);
485 }
486 
487 static void
489  guint prop_id,
490  GValue *value,
491  GParamSpec *pspec)
492 {
493  EvSidebarBookmarks *sidebar_bookmarks;
494 
495  sidebar_bookmarks = EV_SIDEBAR_BOOKMARKS (object);
496 
497  switch (prop_id) {
498  case PROP_WIDGET:
499  g_value_set_object (value, sidebar_bookmarks->priv->tree_view);
500  break;
501  default:
502  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
503  break;
504  }
505 }
506 
507 static void
509 {
510  GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
511  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
512 
513  g_object_class->get_property = ev_sidebar_bookmarks_get_property;
514  g_object_class->dispose = ev_sidebar_bookmarks_dispose;
515 
516  widget_class->popup_menu = ev_sidebar_bookmarks_popup_menu;
517 
518  g_type_class_add_private (g_object_class, sizeof (EvSidebarBookmarksPrivate));
519 
520  g_object_class_override_property (g_object_class, PROP_WIDGET, "main-widget");
521 }
522 
523 GtkWidget *
525 {
526  return GTK_WIDGET (g_object_new (EV_TYPE_SIDEBAR_BOOKMARKS,
527  "orientation", GTK_ORIENTATION_VERTICAL,
528  NULL));
529 }
530 
531 void
533  EvBookmarks *bookmarks)
534 {
535  EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
536 
537  g_return_if_fail (EV_IS_BOOKMARKS (bookmarks));
538 
539  if (priv->bookmarks == bookmarks)
540  return;
541 
542  if (priv->bookmarks)
543  g_object_unref (priv->bookmarks);
544  priv->bookmarks = g_object_ref (bookmarks);
545  g_signal_connect (priv->bookmarks, "changed",
546  G_CALLBACK (ev_sidebar_bookmarks_changed),
547  sidebar_bookmarks);
548 
549  gtk_widget_set_sensitive (priv->add_button, TRUE);
550  ev_sidebar_bookmarks_update (sidebar_bookmarks);
551 }
552 
553 /* EvSidebarPageIface */
554 static void
556  EvDocumentModel *model)
557 {
558  EvSidebarBookmarks *sidebar_bookmarks = EV_SIDEBAR_BOOKMARKS (sidebar_page);
559  EvSidebarBookmarksPrivate *priv = sidebar_bookmarks->priv;
560 
561  if (priv->model == model)
562  return;
563 
564  if (priv->model)
565  g_object_unref (priv->model);
566  priv->model = g_object_ref (model);
567 }
568 
569 static gboolean
571  EvDocument *document)
572 {
573  return TRUE;
574 }
575 
576 static const gchar *
578 {
579  return _("Bookmarks");
580 }
581 
582 static void
584 {
588 }