1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8; -*-
2 *
3 * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
4 * Copyright (C) 2013 Red Hat, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU 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 <evince-document.h>
26 #include <evince-view.h>
27
28 #include <libgd/gd.h>
29
30 #include "gd-places-bookmarks.h"
31 #include "gd-places-page.h"
32
33 struct _GdPlacesBookmarksPrivate {
34 EvDocumentModel *document_model;
35 GdBookmarks *bookmarks;
36 const char *name;
37 GtkWidget *tree_view;
38
39 EvJob *job;
40
41 guint activated_id;
42 };
43
44 enum {
45 PROP_0,
46 PROP_NAME,
47 PROP_DOCUMENT_MODEL,
48 PROP_BOOKMARKS,
49 };
50
51 enum {
52 COLUMN_MARKUP,
53 COLUMN_PAGE_LABEL,
54 COLUMN_BOOKMARK,
55 N_COLUMNS
56 };
57
58 enum {
59 BOOKMARK_ACTIVATED,
60 N_SIGNALS
61 };
62
63 static guint signals[N_SIGNALS];
64
65 static void gd_places_bookmarks_page_iface_init (GdPlacesPageInterface *iface);
66
67 G_DEFINE_TYPE_EXTENDED (GdPlacesBookmarks,
68 gd_places_bookmarks,
69 GTK_TYPE_BOX,
70 0,
71 G_IMPLEMENT_INTERFACE (GD_TYPE_PLACES_PAGE,
72 gd_places_bookmarks_page_iface_init))
73
74 static GdBookmark *
gd_places_bookmarks_get_selected_bookmark(GdPlacesBookmarks * self,GtkTreeSelection * selection)75 gd_places_bookmarks_get_selected_bookmark (GdPlacesBookmarks *self,
76 GtkTreeSelection *selection)
77 {
78 GtkTreeModel *model;
79 GtkTreeIter iter;
80
81 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
82 GdBookmark *bookmark;
83
84 gtk_tree_model_get (model, &iter,
85 COLUMN_BOOKMARK, &bookmark,
86 -1);
87 return bookmark;
88 }
89
90 return NULL;
91 }
92
93 typedef struct {
94 EvDocument *document;
95 guint page_number;
96 char *markup;
97 } LinkModelData;
98
99 static gboolean
link_model_foreach(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)100 link_model_foreach (GtkTreeModel *model,
101 GtkTreePath *path,
102 GtkTreeIter *iter,
103 gpointer user_data)
104 {
105 LinkModelData *data = user_data;
106 EvLink *link = NULL;
107 char *markup = NULL;
108 int link_page;
109 gboolean ret = FALSE;
110
111 gtk_tree_model_get (model, iter,
112 EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
113 EV_DOCUMENT_LINKS_COLUMN_MARKUP, &markup,
114 -1);
115 if (link != NULL) {
116 link_page = ev_document_links_get_link_page (EV_DOCUMENT_LINKS (data->document), link);
117 if (link_page == data->page_number) {
118 GtkTreeIter parent;
119
120 if (gtk_tree_model_iter_parent (model, &parent, iter)) {
121 char *parent_markup = NULL;
122 gtk_tree_model_get (model, &parent,
123 EV_DOCUMENT_LINKS_COLUMN_MARKUP, &parent_markup,
124 -1);
125 if (parent_markup != NULL) {
126 data->markup = g_strdup_printf ("%s ﹥ %s", parent_markup, markup);
127 g_free (parent_markup);
128 }
129 }
130
131 if (data->markup == NULL) {
132 data->markup = g_strdup (markup);
133 }
134
135 ret = TRUE;
136 }
137 }
138
139 g_free (markup);
140 g_clear_object (&link);
141
142 return ret;
143 }
144
145 static char *
get_link_title_for_page(EvDocument * document,GtkTreeModel * links_model,guint page)146 get_link_title_for_page (EvDocument *document,
147 GtkTreeModel *links_model,
148 guint page)
149 {
150 LinkModelData *data;
151 char *ret;
152
153 data = g_new0 (LinkModelData, 1);
154 data->page_number = page;
155 data->document = document;
156 gtk_tree_model_foreach (links_model, link_model_foreach, data);
157 ret = data->markup;
158 g_free (data);
159
160 return ret;
161 }
162
163 static void
enable_selection(GdPlacesBookmarks * self,gboolean enabled)164 enable_selection (GdPlacesBookmarks *self,
165 gboolean enabled)
166 {
167 GtkTreeSelection *selection;
168
169 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->tree_view));
170 gtk_tree_selection_set_mode (selection, enabled ? GTK_SELECTION_SINGLE : GTK_SELECTION_NONE);
171 }
172
173 #define MAX_LEN_LABEL 200
174 #define MIN_LEN_LABEL 20
175
176 static char *
get_pretty_name(const char * text)177 get_pretty_name (const char *text)
178 {
179 char *name = NULL;
180 char *trimmed;
181 char *basename = NULL;
182 int i;
183 int last_word = -1;
184 int last_sentence = -1;
185 int last_nonspace = -1;
186 int num_attrs;
187 PangoLogAttr *attrs;
188 gboolean ellipse = TRUE;
189
190 num_attrs = MIN (g_utf8_strlen (text, -1) + 1, MAX_LEN_LABEL);
191 attrs = g_new (PangoLogAttr, num_attrs);
192 trimmed = g_utf8_substring (text, 0, num_attrs - 1);
193 pango_get_log_attrs (trimmed, -1, -1, pango_language_get_default (), attrs, num_attrs);
194
195 /* since the end of the text will always match a word boundary don't include it */
196 for (i = 0; (i < num_attrs - 1); i++) {
197 if (!attrs[i].is_white) {
198 last_nonspace = i;
199 }
200 if (attrs[i].is_sentence_end) {
201 last_sentence = i;
202 }
203 if (attrs[i].is_word_boundary) {
204 last_word = last_nonspace;
205 }
206 }
207 g_free (attrs);
208
209 if (last_sentence > 0) {
210 i = last_sentence;
211 ellipse = FALSE;
212 } else {
213 i = last_word;
214 }
215
216 if (i == -1) {
217 name = NULL;
218 goto out;
219 }
220
221 basename = g_utf8_substring (trimmed, 0, i);
222 if (ellipse) {
223 name = g_strdup_printf ("“%s…”", basename);
224 } else {
225 name = g_strdup_printf ("“%s”", basename);
226 }
227
228 out:
229 g_free (basename);
230 g_free (trimmed);
231
232 return name;
233 }
234
235 static char *
remove_duplicate_whitespace(const char * old)236 remove_duplicate_whitespace (const char *old)
237 {
238 char *new;
239 GRegex *re;
240 GError *error;
241
242 error = NULL;
243 re = g_regex_new ("[ \t\n\r]+", G_REGEX_MULTILINE, 0, &error);
244 if (re == NULL) {
245 g_warning ("Error building regex: %s", error->message);
246 g_error_free (error);
247 return g_strdup (old);
248 }
249
250 new = g_regex_replace (re, old, -1, 0, " ", 0, &error);
251 g_regex_unref (re);
252 if (new == NULL) {
253 g_warning ("Error replacing string: %s", error->message);
254 g_error_free (error);
255 return g_strdup (old);
256 }
257
258 return new;
259 }
260
261 static void
load_bookmark_model(GdPlacesBookmarks * self,GtkTreeModel * links_model)262 load_bookmark_model (GdPlacesBookmarks *self,
263 GtkTreeModel *links_model)
264 {
265 GtkListStore *model;
266 GList *items;
267 GList *l;
268 EvDocument *document;
269
270 if (self->priv->bookmarks == NULL) {
271 return;
272 }
273
274 model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->tree_view)));
275
276 document = ev_document_model_get_document (self->priv->document_model);
277
278 items = gd_bookmarks_get_bookmarks (self->priv->bookmarks);
279 items = g_list_sort (items, (GCompareFunc)gd_bookmark_compare);
280 for (l = items; l; l = g_list_next (l)) {
281 GdBookmark *bookmark = (GdBookmark *)l->data;
282 GtkTreeIter iter;
283 const char *title;
284 char *label = NULL;
285 char *markup = NULL;
286 guint page;
287
288 title = gd_bookmark_get_title (bookmark);
289 page = gd_bookmark_get_page_number (bookmark);
290
291 if (ev_document_has_text_page_labels (document)) {
292 label = ev_document_get_page_label (document, page);
293 } else {
294 label = g_strdup_printf ("%d", page + 1);
295 }
296
297 if (links_model != NULL) {
298 markup = get_link_title_for_page (document, links_model, page);
299 }
300
301 if (markup == NULL && EV_IS_DOCUMENT_TEXT (document)) {
302 char *text;
303 char *trimmed;
304 char *stripped;
305 EvPage *ev_page;
306
307 ev_page = ev_document_get_page (document, page);
308 text = ev_document_text_get_text (EV_DOCUMENT_TEXT (document), ev_page);
309 trimmed = g_utf8_substring (text, 0, MAX_LEN_LABEL * 2);
310 g_free (text);
311 stripped = remove_duplicate_whitespace (trimmed);
312 g_free (trimmed);
313 markup = get_pretty_name (stripped);
314 g_free (stripped);
315 }
316
317 if (markup == NULL) {
318 /* Translators: %s is the number of the page, already formatted
319 * as a string, for example "Page 5".
320 */
321 markup = g_strdup_printf (_("Page %s"), label);
322 }
323
324 gtk_list_store_append (model, &iter);
325 gtk_list_store_set (model, &iter,
326 COLUMN_MARKUP, markup != NULL ? markup : title,
327 COLUMN_PAGE_LABEL, label,
328 COLUMN_BOOKMARK, bookmark,
329 -1);
330 g_free (label);
331 g_free (markup);
332 }
333
334 enable_selection (self, TRUE);
335
336 g_list_free (items);
337 }
338
339 static void
job_finished_cb(EvJobLinks * job,GdPlacesBookmarks * self)340 job_finished_cb (EvJobLinks *job,
341 GdPlacesBookmarks *self)
342 {
343 GdPlacesBookmarksPrivate *priv = self->priv;
344 GtkListStore *model;
345
346 model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)));
347 gtk_list_store_clear (model);
348 load_bookmark_model (self, job->model);
349
350 g_clear_object (&priv->job);
351 }
352
353 static void
gd_places_bookmarks_update(GdPlacesBookmarks * self)354 gd_places_bookmarks_update (GdPlacesBookmarks *self)
355 {
356 GdPlacesBookmarksPrivate *priv = self->priv;
357 GtkListStore *model;
358 GtkTreeIter iter;
359 guint n_items = 0;
360 EvDocument *document;
361
362 if (priv->document_model == NULL) {
363 /* not loaded yet */
364 return;
365 }
366
367 if (priv->job != NULL) {
368 ev_job_cancel (priv->job);
369 g_clear_object (&priv->job);
370 }
371
372 model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)));
373 gtk_list_store_clear (model);
374 enable_selection (self, FALSE);
375
376 if (priv->bookmarks != NULL) {
377 n_items = gd_bookmarks_get_n_items (priv->bookmarks);
378 }
379
380 document = ev_document_model_get_document (priv->document_model);
381 if (n_items == 0) {
382 gtk_list_store_append (model, &iter);
383 gtk_list_store_set (model, &iter,
384 COLUMN_MARKUP, _("No bookmarks"),
385 COLUMN_PAGE_LABEL, NULL,
386 COLUMN_BOOKMARK, NULL,
387 -1);
388 } else if (EV_IS_DOCUMENT_LINKS (document) &&
389 ev_document_links_has_document_links (EV_DOCUMENT_LINKS (document))) {
390 gtk_list_store_append (model, &iter);
391 gtk_list_store_set (model, &iter,
392 COLUMN_MARKUP, _("Loading…"),
393 COLUMN_PAGE_LABEL, NULL,
394 COLUMN_BOOKMARK, NULL,
395 -1);
396 priv->job = ev_job_links_new (document);
397 g_signal_connect (priv->job,
398 "finished",
399 G_CALLBACK (job_finished_cb),
400 self);
401
402 /* The priority doesn't matter for this job */
403 ev_job_scheduler_push_job (priv->job, EV_JOB_PRIORITY_NONE);
404 } else {
405 load_bookmark_model (self, NULL);
406 }
407 }
408
409 static void
gd_places_bookmarks_changed(GdBookmarks * bookmarks,GdPlacesBookmarks * self)410 gd_places_bookmarks_changed (GdBookmarks *bookmarks,
411 GdPlacesBookmarks *self)
412 {
413 gd_places_bookmarks_update (self);
414 }
415
416 static gboolean
emit_activated(GdPlacesBookmarks * self)417 emit_activated (GdPlacesBookmarks *self)
418 {
419 GtkTreeSelection *selection;
420 GdBookmark *bookmark;
421
422 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->tree_view));
423 bookmark = gd_places_bookmarks_get_selected_bookmark (self, selection);
424
425 if (bookmark != NULL) {
426 g_signal_emit (self, signals[BOOKMARK_ACTIVATED], 0, bookmark);
427
428 g_object_unref (bookmark);
429 }
430
431 self->priv->activated_id = 0;
432
433 return FALSE;
434 }
435
436 static void
schedule_emit_activated(GdPlacesBookmarks * self)437 schedule_emit_activated (GdPlacesBookmarks *self)
438 {
439 /* jump through some hoops to avoid destroying in the middle
440 of a button release handler */
441 if (self->priv->activated_id == 0) {
442 self->priv->activated_id = g_idle_add ((GSourceFunc) emit_activated, self);
443 }
444 }
445
446 static void
gd_places_bookmarks_set_document_model(GdPlacesPage * page,EvDocumentModel * model)447 gd_places_bookmarks_set_document_model (GdPlacesPage *page,
448 EvDocumentModel *model)
449 {
450 GdPlacesBookmarks *self = GD_PLACES_BOOKMARKS (page);
451 GdPlacesBookmarksPrivate *priv = self->priv;
452
453 if (priv->document_model == model)
454 return;
455
456 if (priv->document_model != NULL) {
457 g_signal_handlers_disconnect_by_func (priv->document_model,
458 gd_places_bookmarks_update,
459 page);
460 }
461
462 g_clear_object (&priv->document_model);
463 priv->document_model = model;
464
465 if (priv->document_model != NULL) {
466 g_object_ref (priv->document_model);
467 g_signal_connect_swapped (priv->document_model,
468 "notify::document",
469 G_CALLBACK (gd_places_bookmarks_update),
470 page);
471 }
472
473 gd_places_bookmarks_update (self);
474 }
475
476 void
gd_places_bookmarks_set_bookmarks(GdPlacesBookmarks * self,GdBookmarks * bookmarks)477 gd_places_bookmarks_set_bookmarks (GdPlacesBookmarks *self,
478 GdBookmarks *bookmarks)
479 {
480 GdPlacesBookmarksPrivate *priv = self->priv;
481
482 g_return_if_fail (GD_IS_BOOKMARKS (bookmarks));
483
484 if (priv->bookmarks == bookmarks)
485 return;
486
487 if (priv->bookmarks != NULL) {
488 g_signal_handlers_disconnect_by_func (priv->bookmarks,
489 G_CALLBACK (gd_places_bookmarks_update),
490 self);
491 }
492
493 g_clear_object (&priv->bookmarks);
494 priv->bookmarks = g_object_ref (bookmarks);
495 g_signal_connect_swapped (priv->bookmarks, "changed",
496 G_CALLBACK (gd_places_bookmarks_update),
497 self);
498
499 gd_places_bookmarks_update (self);
500 }
501
502 static void
gd_places_bookmarks_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)503 gd_places_bookmarks_set_property (GObject *object,
504 guint prop_id,
505 const GValue *value,
506 GParamSpec *pspec)
507 {
508
509 GdPlacesBookmarks *self = GD_PLACES_BOOKMARKS (object);
510
511 switch (prop_id) {
512 case PROP_DOCUMENT_MODEL:
513 gd_places_bookmarks_set_document_model (GD_PLACES_PAGE (self), g_value_get_object (value));
514 break;
515 case PROP_BOOKMARKS:
516 gd_places_bookmarks_set_bookmarks (self, g_value_get_object (value));
517 break;
518 default:
519 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
520 break;
521 }
522 }
523
524 static void
gd_places_bookmarks_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)525 gd_places_bookmarks_get_property (GObject *object,
526 guint prop_id,
527 GValue *value,
528 GParamSpec *pspec)
529 {
530 GdPlacesBookmarks *self = GD_PLACES_BOOKMARKS (object);
531
532 switch (prop_id) {
533 case PROP_NAME:
534 g_value_set_string (value, self->priv->name);
535 break;
536 case PROP_DOCUMENT_MODEL:
537 g_value_set_object (value, self->priv->document_model);
538 break;
539 case PROP_BOOKMARKS:
540 g_value_set_object (value, self->priv->bookmarks);
541 break;
542 default:
543 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
544 break;
545 }
546 }
547
548 static void
gd_places_bookmarks_dispose(GObject * object)549 gd_places_bookmarks_dispose (GObject *object)
550 {
551 GdPlacesBookmarks *self = GD_PLACES_BOOKMARKS (object);
552 GdPlacesBookmarksPrivate *priv = self->priv;
553
554 if (priv->bookmarks != NULL) {
555 g_signal_handlers_disconnect_by_func (priv->bookmarks,
556 G_CALLBACK (gd_places_bookmarks_changed),
557 self);
558 }
559
560 if (priv->document_model != NULL) {
561 g_signal_handlers_disconnect_by_func (priv->document_model,
562 gd_places_bookmarks_update,
563 self);
564 }
565
566 if (self->priv->job != NULL) {
567 ev_job_cancel (self->priv->job);
568 g_clear_object (&self->priv->job);
569 }
570
571 if (self->priv->activated_id > 0) {
572 g_source_remove (self->priv->activated_id);
573 self->priv->activated_id = 0;
574 }
575
576 g_clear_object (&priv->document_model);
577 g_clear_object (&priv->bookmarks);
578
579 G_OBJECT_CLASS (gd_places_bookmarks_parent_class)->dispose (object);
580 }
581
582 static void
gd_places_bookmarks_construct(GdPlacesBookmarks * self)583 gd_places_bookmarks_construct (GdPlacesBookmarks *self)
584 {
585 GdPlacesBookmarksPrivate *priv = self->priv;
586 GtkWidget *swindow;
587 GtkWidget *hbox;
588 GtkListStore *model;
589 GtkTreeViewColumn *column;
590 GtkCellRenderer *renderer;
591
592 swindow = gtk_scrolled_window_new (NULL, NULL);
593 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
594 GTK_SHADOW_IN);
595 gtk_box_pack_start (GTK_BOX (self), swindow, TRUE, TRUE, 0);
596 gtk_widget_show (swindow);
597
598 model = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, GD_TYPE_BOOKMARK);
599 priv->tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
600 enable_selection (self, FALSE);
601 gtk_tree_view_set_activate_on_single_click (GTK_TREE_VIEW (priv->tree_view), TRUE);
602 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
603 g_object_unref (model);
604
605 g_signal_connect_swapped (priv->tree_view, "row-activated",
606 G_CALLBACK (schedule_emit_activated),
607 self);
608
609 column = gtk_tree_view_column_new ();
610 gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
611 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), column);
612
613 renderer = gtk_cell_renderer_text_new ();
614 g_object_set (renderer,
615 "wrap-mode", PANGO_WRAP_WORD,
616 "wrap-width", 350,
617 "weight", PANGO_WEIGHT_BOLD,
618 "xpad", 10,
619 NULL);
620 gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN (column), renderer, TRUE);
621 gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
622 "markup", COLUMN_MARKUP,
623 NULL);
624
625 renderer = gd_styled_text_renderer_new ();
626 gd_styled_text_renderer_add_class (GD_STYLED_TEXT_RENDERER (renderer), "dim-label");
627 g_object_set (renderer,
628 "max-width-chars", 12,
629 "scale", PANGO_SCALE_SMALL,
630 "xalign", 1.0,
631 "xpad", 10,
632 NULL);
633 gtk_tree_view_column_pack_end (GTK_TREE_VIEW_COLUMN (column), renderer, FALSE);
634 gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
635 "text", COLUMN_PAGE_LABEL,
636 NULL);
637
638 gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
639 gtk_widget_show (priv->tree_view);
640
641 hbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
642
643 gtk_box_pack_end (GTK_BOX (self), hbox, FALSE, TRUE, 0);
644 gtk_widget_show (hbox);
645 gtk_widget_show (GTK_WIDGET (self));
646 }
647
648 static void
gd_places_bookmarks_init(GdPlacesBookmarks * self)649 gd_places_bookmarks_init (GdPlacesBookmarks *self)
650 {
651
652 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
653 GD_TYPE_PLACES_BOOKMARKS,
654 GdPlacesBookmarksPrivate);
655
656 self->priv->name = _("Bookmarks");
657
658 gd_places_bookmarks_construct (self);
659 }
660
661 static void
gd_places_bookmarks_class_init(GdPlacesBookmarksClass * klass)662 gd_places_bookmarks_class_init (GdPlacesBookmarksClass *klass)
663 {
664 GObjectClass *oclass = G_OBJECT_CLASS (klass);
665
666 oclass->get_property = gd_places_bookmarks_get_property;
667 oclass->set_property = gd_places_bookmarks_set_property;
668 oclass->dispose = gd_places_bookmarks_dispose;
669
670 signals[BOOKMARK_ACTIVATED] = g_signal_new ("bookmark-activated",
671 G_TYPE_FROM_CLASS (oclass),
672 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
673 0,
674 NULL, NULL,
675 g_cclosure_marshal_VOID__OBJECT,
676 G_TYPE_NONE, 1, G_TYPE_OBJECT);
677
678 g_object_class_install_property (oclass,
679 PROP_BOOKMARKS,
680 g_param_spec_object ("bookmarks",
681 "Bookmarks",
682 "Bookmarks",
683 GD_TYPE_BOOKMARKS,
684 G_PARAM_READWRITE |
685 G_PARAM_STATIC_STRINGS));
686
687 g_object_class_override_property (oclass, PROP_NAME, "name");
688 g_object_class_override_property (oclass, PROP_DOCUMENT_MODEL, "document-model");
689
690 g_type_class_add_private (oclass, sizeof (GdPlacesBookmarksPrivate));
691 }
692
693 GtkWidget *
gd_places_bookmarks_new(void)694 gd_places_bookmarks_new (void)
695 {
696 return GTK_WIDGET (g_object_new (GD_TYPE_PLACES_BOOKMARKS, NULL));
697 }
698
699 static gboolean
gd_places_bookmarks_supports_document(GdPlacesPage * page,EvDocument * document)700 gd_places_bookmarks_supports_document (GdPlacesPage *page,
701 EvDocument *document)
702 {
703 return TRUE;
704 }
705
706 static const char *
gd_places_bookmarks_get_name(GdPlacesPage * page)707 gd_places_bookmarks_get_name (GdPlacesPage *page)
708 {
709 return GD_PLACES_BOOKMARKS (page)->priv->name;
710 }
711
712 static void
gd_places_bookmarks_page_iface_init(GdPlacesPageInterface * iface)713 gd_places_bookmarks_page_iface_init (GdPlacesPageInterface *iface)
714 {
715 iface->supports_document = gd_places_bookmarks_supports_document;
716 iface->set_document_model = gd_places_bookmarks_set_document_model;
717 iface->get_name = gd_places_bookmarks_get_name;
718 }
719