[gnome-documents] Add bookmarks place
- From: William Jon McCann <mccann src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-documents] Add bookmarks place
- Date: Tue, 29 Jan 2013 18:09:45 +0000 (UTC)
commit b0eb19903f9db0802bfcc1b10747071360087e23
Author: William Jon McCann <jmccann redhat com>
Date: Sun Jan 27 14:40:31 2013 -0500
Add bookmarks place
https://bugzilla.gnome.org/show_bug.cgi?id=691254
src/Makefile-lib.am | 11 +-
src/application.js | 7 +-
src/documents.js | 11 +-
src/lib/gd-bookmark.c | 185 +++++++++++
src/lib/gd-bookmark.h | 50 +++
src/lib/gd-bookmarks.c | 342 ++++++++++++++++++++
src/lib/gd-bookmarks.h | 55 ++++
src/lib/gd-nav-bar.c | 2 +-
src/lib/gd-places-bookmarks.c | 708 +++++++++++++++++++++++++++++++++++++++++
src/lib/gd-places-bookmarks.h | 58 ++++
src/lib/gd-places-links.c | 23 +-
src/places.js | 52 +++-
src/preview.js | 60 +++-
13 files changed, 1529 insertions(+), 35 deletions(-)
---
diff --git a/src/Makefile-lib.am b/src/Makefile-lib.am
index da1b13e..51347b5 100644
--- a/src/Makefile-lib.am
+++ b/src/Makefile-lib.am
@@ -1,5 +1,6 @@
gdprivate_cflags = \
-I$(top_srcdir)/src \
+ -I$(top_srcdir)/libgd \
-DPREFIX=\"$(prefix)\" \
-DLIBDIR=\"$(libdir)\" \
-DG_LOG_DOMAIN=\"Gdprivate\" \
@@ -11,7 +12,10 @@ gdprivate_source_h = \
lib/gd-metadata.h \
lib/gd-pdf-loader.h \
lib/gd-nav-bar.h \
+ lib/gd-bookmark.h \
+ lib/gd-bookmarks.h \
lib/gd-places-page.h \
+ lib/gd-places-bookmarks.h \
lib/gd-places-links.h \
$(NULL)
@@ -20,7 +24,10 @@ gdprivate_source_c = \
lib/gd-metadata.c \
lib/gd-pdf-loader.c \
lib/gd-nav-bar.c \
+ lib/gd-bookmark.c \
+ lib/gd-bookmarks.c \
lib/gd-places-page.c \
+ lib/gd-places-bookmarks.c \
lib/gd-places-links.c \
$(NULL)
@@ -28,7 +35,9 @@ pkglib_LTLIBRARIES += libgdprivate-1.0.la
libgdprivate_1_0_la_LIBADD = \
$(DOCUMENTS_LIBS) \
- $(LIBM)
+ $(LIBM) \
+ $(top_builddir)/libgd/libgd.la
+
libgdprivate_1_0_la_LDFLAGS = \
-avoid-version
diff --git a/src/application.js b/src/application.js
index 4129ce2..cba72d9 100644
--- a/src/application.js
+++ b/src/application.js
@@ -404,8 +404,13 @@ const Application = new Lang.Class({
{ name: 'properties',
callback: this._onActionProperties,
window_mode: WindowMode.WindowMode.PREVIEW },
+ { name: 'bookmark-page',
+ callback: this._onActionToggle,
+ state: GLib.Variant.new('b', false),
+ accel: '<Primary>d',
+ window_mode: WindowMode.WindowMode.PREVIEW },
{ name: 'places',
- accel: 'F3',
+ accel: '<Primary>b',
window_mode: WindowMode.WindowMode.PREVIEW }
];
diff --git a/src/documents.js b/src/documents.js
index bb6eee3..bedf24f 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -938,10 +938,11 @@ const DocumentManager = new Lang.Class({
// save loaded model and signal
this._activeDocModel = docModel;
this._activeDocModel.set_continuous(false);
- this.emit('load-finished', doc, docModel);
// load metadata
this._connectMetadata(docModel);
+
+ this.emit('load-finished', doc, docModel);
},
reloadActiveItem: function() {
@@ -1004,6 +1005,7 @@ const DocumentManager = new Lang.Class({
this._activeDocModel.disconnect(id);
}));
+ this.metadata = null;
this._activeDocModel = null;
this._activeDocModelIds = [];
}
@@ -1012,20 +1014,19 @@ const DocumentManager = new Lang.Class({
_connectMetadata: function(docModel) {
let evDoc = docModel.get_document();
let file = Gio.File.new_for_uri(evDoc.get_uri());
-
if (!GdPrivate.is_metadata_supported_for_file(file))
return;
- let metadata = new GdPrivate.Metadata({ file: file });
+ this.metadata = new GdPrivate.Metadata({ file: file });
// save current page in metadata
- let [res, val] = metadata.get_int('page');
+ let [res, val] = this.metadata.get_int('page');
if (res)
docModel.set_page(val);
this._activeDocModelIds.push(
docModel.connect('page-changed', Lang.bind(this,
function(source, oldPage, newPage) {
- metadata.set_int('page', newPage);
+ this.metadata.set_int('page', newPage);
}))
);
}
diff --git a/src/lib/gd-bookmark.c b/src/lib/gd-bookmark.c
new file mode 100644
index 0000000..25345aa
--- /dev/null
+++ b/src/lib/gd-bookmark.c
@@ -0,0 +1,185 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8; -*-
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gd-bookmark.h"
+
+enum {
+ PROP_0,
+ PROP_PAGE_NUMBER,
+ PROP_TITLE,
+};
+
+struct _GdBookmark {
+ GObject base;
+
+ char *title;
+ guint page_num;
+};
+
+struct _GdBookmarkClass {
+ GObjectClass base_class;
+};
+
+G_DEFINE_TYPE (GdBookmark, gd_bookmark, G_TYPE_OBJECT)
+
+int
+gd_bookmark_compare (GdBookmark *a,
+ GdBookmark *b)
+{
+ if (a->page_num < b->page_num) {
+ return -1;
+ }
+ if (a->page_num > b->page_num) {
+ return 1;
+ }
+
+ return 0;
+}
+
+const char *
+gd_bookmark_get_title (GdBookmark *bookmark)
+{
+ return bookmark->title;
+}
+
+void
+gd_bookmark_set_title (GdBookmark *bookmark,
+ const char *title)
+{
+ if (g_strcmp0 (title, bookmark->title) == 0) {
+ return;
+ }
+
+ g_free (bookmark->title);
+ bookmark->title = g_strdup (title);
+ g_object_notify (G_OBJECT (bookmark), "title");
+}
+
+guint
+gd_bookmark_get_page_number (GdBookmark *bookmark)
+{
+ return bookmark->page_num;
+}
+
+void
+gd_bookmark_set_page_number (GdBookmark *bookmark,
+ guint page_num)
+{
+ if (page_num == bookmark->page_num) {
+ return;
+ }
+
+ bookmark->page_num = page_num;
+ g_object_notify (G_OBJECT (bookmark), "page-number");
+}
+
+static void
+gd_bookmark_finalize (GObject *object)
+{
+ GdBookmark *bookmark = GD_BOOKMARK (object);
+
+ g_free (bookmark->title);
+
+ G_OBJECT_CLASS (gd_bookmark_parent_class)->finalize (object);
+}
+
+static void
+gd_bookmark_init (GdBookmark *bookmark)
+{
+}
+
+static void
+gd_bookmark_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdBookmark *self = GD_BOOKMARK (object);
+
+ switch (prop_id) {
+ case PROP_TITLE:
+ g_value_set_string (value, self->title);
+ break;
+ case PROP_PAGE_NUMBER:
+ g_value_set_uint (value, self->page_num);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gd_bookmark_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdBookmark *self = GD_BOOKMARK (object);
+
+ switch (prop_id) {
+ case PROP_TITLE:
+ gd_bookmark_set_title (self, g_value_get_string (value));
+ break;
+ case PROP_PAGE_NUMBER:
+ gd_bookmark_set_page_number (self, g_value_get_uint (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gd_bookmark_class_init (GdBookmarkClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->get_property = gd_bookmark_get_property;
+ gobject_class->set_property = gd_bookmark_set_property;
+ gobject_class->finalize = gd_bookmark_finalize;
+
+ g_object_class_install_property (gobject_class,
+ PROP_PAGE_NUMBER,
+ g_param_spec_uint ("page-number",
+ "Page Number",
+ "Page Number",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class,
+ PROP_TITLE,
+ g_param_spec_string ("title",
+ "Title",
+ "Title",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+GdBookmark *
+gd_bookmark_new (void)
+{
+ return GD_BOOKMARK (g_object_new (GD_TYPE_BOOKMARK, NULL));
+}
diff --git a/src/lib/gd-bookmark.h b/src/lib/gd-bookmark.h
new file mode 100644
index 0000000..fc5937d
--- /dev/null
+++ b/src/lib/gd-bookmark.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8; -*-
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef GD_BOOKMARK_H
+#define GD_BOOKMARK_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_BOOKMARK (gd_bookmark_get_type())
+#define GD_BOOKMARK(object) (G_TYPE_CHECK_INSTANCE_CAST((object), GD_TYPE_BOOKMARK, GdBookmark))
+#define GD_BOOKMARK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GD_TYPE_BOOKMARK, GdBookmarkClass))
+#define GD_IS_BOOKMARK(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), GD_TYPE_BOOKMARK))
+
+typedef struct _GdBookmark GdBookmark;
+typedef struct _GdBookmarkClass GdBookmarkClass;
+
+GType gd_bookmark_get_type (void) G_GNUC_CONST;
+
+GdBookmark *gd_bookmark_new (void);
+
+void gd_bookmark_set_page_number (GdBookmark *bookmark,
+ guint num);
+guint gd_bookmark_get_page_number (GdBookmark *bookmark);
+void gd_bookmark_set_title (GdBookmark *bookmark,
+ const char *title);
+const char * gd_bookmark_get_title (GdBookmark *bookmark);
+
+int gd_bookmark_compare (GdBookmark *a,
+ GdBookmark *b);
+G_END_DECLS
+
+#endif /* GD_BOOKMARK_H */
diff --git a/src/lib/gd-bookmarks.c b/src/lib/gd-bookmarks.c
new file mode 100644
index 0000000..982792c
--- /dev/null
+++ b/src/lib/gd-bookmarks.c
@@ -0,0 +1,342 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8; -*-
+ *
+ * Copyright (C) 2010 Carlos Garcia Campos <carlosgc gnome org>
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gd-bookmarks.h"
+
+enum {
+ PROP_0,
+ PROP_METADATA,
+ PROP_N_ITEMS
+};
+
+enum {
+ CHANGED,
+ N_SIGNALS
+};
+
+struct _GdBookmarks {
+ GObject base;
+
+ GdMetadata *metadata;
+ GList *items;
+};
+
+struct _GdBookmarksClass {
+ GObjectClass base_class;
+
+ void (*changed) (GdBookmarks *bookmarks);
+};
+
+G_DEFINE_TYPE (GdBookmarks, gd_bookmarks, G_TYPE_OBJECT)
+
+static guint signals[N_SIGNALS];
+
+static void
+gd_bookmarks_finalize (GObject *object)
+{
+ GdBookmarks *self = GD_BOOKMARKS (object);
+
+ g_list_free_full (self->items, g_object_unref);
+
+ g_clear_object (&self->metadata);
+
+ G_OBJECT_CLASS (gd_bookmarks_parent_class)->finalize (object);
+}
+
+static void
+gd_bookmarks_init (GdBookmarks *bookmarks)
+{
+}
+
+guint
+gd_bookmarks_get_n_items (GdBookmarks *bookmarks)
+{
+ g_return_val_if_fail (GD_IS_BOOKMARKS (bookmarks), 0);
+
+ return g_list_length (bookmarks->items);
+}
+
+static void
+gd_bookmarks_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdBookmarks *self = GD_BOOKMARKS (object);
+
+ switch (prop_id) {
+ case PROP_N_ITEMS:
+ g_value_set_uint (value, gd_bookmarks_get_n_items (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gd_bookmarks_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdBookmarks *self = GD_BOOKMARKS (object);
+
+ switch (prop_id) {
+ case PROP_METADATA:
+ self->metadata = (GdMetadata *)g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gd_bookmarks_constructed (GObject *object)
+{
+ GdBookmarks *self = GD_BOOKMARKS (object);
+ const char *bm_list_str;
+ GVariant *bm_list;
+ GVariantIter iter;
+ GVariant *child;
+ GError *error = NULL;
+
+ if (!gd_metadata_get_string (self->metadata, "bookmarks", &bm_list_str)) {
+ return;
+ }
+
+ if (bm_list_str == NULL || bm_list_str[0] == '\0') {
+ return;
+ }
+
+ bm_list = g_variant_parse ((const GVariantType *)"a(us)",
+ bm_list_str, NULL, NULL,
+ &error);
+ if (bm_list == NULL) {
+ g_warning ("Error getting bookmarks: %s\n", error->message);
+ g_error_free (error);
+
+ return;
+ }
+
+ g_variant_iter_init (&iter, bm_list);
+ while ((child = g_variant_iter_next_value (&iter))) {
+ guint page_num;
+ const char *title = NULL;
+
+ g_variant_get (child, "(u&s)", &page_num, &title);
+ if (title != NULL) {
+ GdBookmark *bm = gd_bookmark_new ();
+ gd_bookmark_set_title (bm, title);
+ gd_bookmark_set_page_number (bm, page_num);
+ self->items = g_list_prepend (self->items, bm);
+ g_object_notify (G_OBJECT (self), "n-items");
+ }
+ g_variant_unref (child);
+ }
+ g_variant_unref (bm_list);
+
+ self->items = g_list_reverse (self->items);
+}
+
+static void
+gd_bookmarks_class_init (GdBookmarksClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->get_property = gd_bookmarks_get_property;
+ gobject_class->set_property = gd_bookmarks_set_property;
+ gobject_class->finalize = gd_bookmarks_finalize;
+ gobject_class->constructed = gd_bookmarks_constructed;
+
+ g_object_class_install_property (gobject_class,
+ PROP_METADATA,
+ g_param_spec_object ("metadata",
+ "Metadata",
+ "The document metadata",
+ GD_TYPE_METADATA,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class,
+ PROP_N_ITEMS,
+ g_param_spec_uint ("n-items",
+ "N Items",
+ "Number of bookmark items",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /* Signals */
+ signals[CHANGED] = g_signal_new ("changed",
+ GD_TYPE_BOOKMARKS,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdBookmarksClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+GdBookmarks *
+gd_bookmarks_new (GdMetadata *metadata)
+{
+ g_return_val_if_fail (GD_IS_METADATA (metadata), NULL);
+
+ return GD_BOOKMARKS (g_object_new (GD_TYPE_BOOKMARKS,
+ "metadata", metadata,
+ NULL));
+}
+
+static void
+gd_bookmarks_save (GdBookmarks *self)
+{
+ GList *l;
+ GVariantBuilder builder;
+ GVariant *bm_list;
+ char *bm_list_str;
+
+ if (self->items == NULL) {
+ gd_metadata_set_string (self->metadata, "bookmarks", "");
+ return;
+ }
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+ for (l = self->items; l; l = g_list_next (l)) {
+ GdBookmark *bm = (GdBookmark *)l->data;
+ const char *title = gd_bookmark_get_title (bm);
+ guint page_num = gd_bookmark_get_page_number (bm);
+
+ g_variant_builder_add (&builder, "(u&s)",
+ page_num,
+ title != NULL ? title : "");
+ }
+ bm_list = g_variant_builder_end (&builder);
+
+ bm_list_str = g_variant_print (bm_list, FALSE);
+ g_variant_unref (bm_list);
+ gd_metadata_set_string (self->metadata, "bookmarks", bm_list_str);
+ g_free (bm_list_str);
+}
+
+/**
+ * gd_bookmarks_find_bookmark:
+ * @bookmarks:
+ * @bookmark:
+ *
+ * Returns: (transfer none)
+ */
+GdBookmark *
+gd_bookmarks_find_bookmark (GdBookmarks *bookmarks,
+ GdBookmark *bookmark)
+{
+ GList *l;
+
+ l = g_list_find_custom (bookmarks->items, bookmark, (GCompareFunc)gd_bookmark_compare);
+ if (l != NULL)
+ return l->data;
+
+ return NULL;
+}
+
+/**
+ * gd_bookmarks_get_bookmarks:
+ * @bookmarks:
+ *
+ * Returns: (transfer container) (element-type GdBookmark): A list of #GdBookmark objects
+ */
+GList *
+gd_bookmarks_get_bookmarks (GdBookmarks *bookmarks)
+{
+ g_return_val_if_fail (GD_IS_BOOKMARKS (bookmarks), NULL);
+
+ return g_list_copy (bookmarks->items);
+}
+
+void
+gd_bookmarks_add (GdBookmarks *bookmarks,
+ GdBookmark *bookmark)
+{
+ GdBookmark *bm;
+
+ g_return_if_fail (GD_IS_BOOKMARKS (bookmarks));
+
+ bm = gd_bookmarks_find_bookmark (bookmarks, bookmark);
+ if (bm != NULL) {
+ return;
+ }
+
+ bookmarks->items = g_list_append (bookmarks->items, g_object_ref (bookmark));
+ g_object_notify (G_OBJECT (bookmarks), "n-items");
+ g_signal_emit (bookmarks, signals[CHANGED], 0);
+ gd_bookmarks_save (bookmarks);
+}
+
+void
+gd_bookmarks_remove (GdBookmarks *bookmarks,
+ GdBookmark *bookmark)
+{
+ GdBookmark *bm;
+
+ g_return_if_fail (GD_IS_BOOKMARKS (bookmarks));
+
+ bm = gd_bookmarks_find_bookmark (bookmarks, bookmark);
+ if (bm == NULL) {
+ return;
+ }
+
+ bookmarks->items = g_list_remove (bookmarks->items, bm);
+ g_object_unref (bm);
+ g_object_notify (G_OBJECT (bookmarks), "n-items");
+ g_signal_emit (bookmarks, signals[CHANGED], 0);
+ gd_bookmarks_save (bookmarks);
+}
+
+void
+gd_bookmarks_update (GdBookmarks *bookmarks,
+ GdBookmark *bookmark)
+{
+ GList *bm_link;
+ GdBookmark *bm;
+ const char *title_a;
+ const char *title_b;
+
+ g_return_if_fail (GD_IS_BOOKMARKS (bookmarks));
+
+ bm_link = g_list_find_custom (bookmarks->items, bookmark, (GCompareFunc)gd_bookmark_compare);
+ if (bm_link == NULL) {
+ return;
+ }
+
+ bm = (GdBookmark *)bm_link->data;
+
+ title_a = gd_bookmark_get_title (bm);
+ title_b = gd_bookmark_get_title (bookmark);
+
+ if (g_strcmp0 (title_a, title_b) == 0) {
+ return;
+ }
+
+ g_signal_emit (bookmarks, signals[CHANGED], 0);
+ gd_bookmarks_save (bookmarks);
+}
diff --git a/src/lib/gd-bookmarks.h b/src/lib/gd-bookmarks.h
new file mode 100644
index 0000000..a6e5855
--- /dev/null
+++ b/src/lib/gd-bookmarks.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8; -*-
+ *
+ * Copyright (C) 2010 Carlos Garcia Campos <carlosgc gnome org>
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef GD_BOOKMARKS_H
+#define GD_BOOKMARKS_H
+
+#include <glib-object.h>
+
+#include "gd-bookmark.h"
+#include "gd-metadata.h"
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_BOOKMARKS (gd_bookmarks_get_type())
+#define GD_BOOKMARKS(object) (G_TYPE_CHECK_INSTANCE_CAST((object), GD_TYPE_BOOKMARKS, GdBookmarks))
+#define GD_BOOKMARKS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GD_TYPE_BOOKMARKS, GdBookmarksClass))
+#define GD_IS_BOOKMARKS(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), GD_TYPE_BOOKMARKS))
+
+typedef struct _GdBookmarks GdBookmarks;
+typedef struct _GdBookmarksClass GdBookmarksClass;
+
+GType gd_bookmarks_get_type (void) G_GNUC_CONST;
+
+GdBookmarks *gd_bookmarks_new (GdMetadata *metadata);
+guint gd_bookmarks_get_number (GdBookmarks *bookmarks);
+GList *gd_bookmarks_get_bookmarks (GdBookmarks *bookmarks);
+GdBookmark *gd_bookmarks_find_bookmark (GdBookmarks *bookmarks,
+ GdBookmark *bookmark);
+void gd_bookmarks_add (GdBookmarks *bookmarks,
+ GdBookmark *bookmark);
+void gd_bookmarks_remove (GdBookmarks *bookmarks,
+ GdBookmark *bookmark);
+void gd_bookmarks_update (GdBookmarks *bookmarks,
+ GdBookmark *bookmark);
+
+G_END_DECLS
+
+#endif /* GD_BOOKMARKS_H */
diff --git a/src/lib/gd-nav-bar.c b/src/lib/gd-nav-bar.c
index a071164..6df2480 100644
--- a/src/lib/gd-nav-bar.c
+++ b/src/lib/gd-nav-bar.c
@@ -917,7 +917,7 @@ gd_nav_bar_init (GdNavBar *self)
gtk_widget_set_hexpand (GTK_WIDGET (inner_box), TRUE);
gtk_container_add (GTK_CONTAINER (self), inner_box);
- priv->button_area = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+ priv->button_area = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_margin_left (priv->button_area, 5);
gtk_widget_set_margin_right (priv->button_area, 5);
gtk_widget_show (priv->button_area);
diff --git a/src/lib/gd-places-bookmarks.c b/src/lib/gd-places-bookmarks.c
new file mode 100644
index 0000000..73c021b
--- /dev/null
+++ b/src/lib/gd-places-bookmarks.c
@@ -0,0 +1,708 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8; -*-
+ *
+ * Copyright (C) 2010 Carlos Garcia Campos <carlosgc gnome org>
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include <evince-document.h>
+#include <evince-view.h>
+
+#include <libgd/gd.h>
+
+#include "gd-places-bookmarks.h"
+#include "gd-places-page.h"
+
+struct _GdPlacesBookmarksPrivate {
+ EvDocumentModel *document_model;
+ GdBookmarks *bookmarks;
+ const char *name;
+ GtkWidget *tree_view;
+
+ EvJob *job;
+
+ guint activated_id;
+};
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ PROP_DOCUMENT_MODEL,
+ PROP_BOOKMARKS,
+};
+
+enum {
+ COLUMN_MARKUP,
+ COLUMN_PAGE_LABEL,
+ COLUMN_BOOKMARK,
+ N_COLUMNS
+};
+
+enum {
+ BOOKMARK_ACTIVATED,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS];
+
+static void gd_places_bookmarks_page_iface_init (GdPlacesPageInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GdPlacesBookmarks,
+ gd_places_bookmarks,
+ GTK_TYPE_BOX,
+ 0,
+ G_IMPLEMENT_INTERFACE (GD_TYPE_PLACES_PAGE,
+ gd_places_bookmarks_page_iface_init))
+
+static GdBookmark *
+gd_places_bookmarks_get_selected_bookmark (GdPlacesBookmarks *self,
+ GtkTreeSelection *selection)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ GdBookmark *bookmark;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_BOOKMARK, &bookmark,
+ -1);
+ return bookmark;
+ }
+
+ return NULL;
+}
+
+typedef struct {
+ EvDocument *document;
+ guint page_number;
+ char *markup;
+} LinkModelData;
+
+static gboolean
+link_model_foreach (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ LinkModelData *data = user_data;
+ EvLink *link = NULL;
+ char *markup = NULL;
+ int link_page;
+ gboolean ret = FALSE;
+
+ gtk_tree_model_get (model, iter,
+ EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
+ EV_DOCUMENT_LINKS_COLUMN_MARKUP, &markup,
+ -1);
+ if (link != NULL) {
+ link_page = ev_document_links_get_link_page (EV_DOCUMENT_LINKS (data->document), link);
+ if (link_page == data->page_number) {
+ GtkTreeIter parent;
+
+ if (gtk_tree_model_iter_parent (model, &parent, iter)) {
+ char *parent_markup = NULL;
+ gtk_tree_model_get (model, &parent,
+ EV_DOCUMENT_LINKS_COLUMN_MARKUP, &parent_markup,
+ -1);
+ if (parent_markup != NULL) {
+ data->markup = g_strdup_printf ("%s ï %s", parent_markup, markup);
+ g_free (parent_markup);
+ }
+ }
+
+ if (data->markup == NULL) {
+ data->markup = g_strdup (markup);
+ }
+
+ ret = TRUE;
+ }
+ }
+
+ g_free (markup);
+ g_clear_object (&link);
+
+ return ret;
+}
+
+static char *
+get_link_title_for_page (EvDocument *document,
+ GtkTreeModel *links_model,
+ guint page)
+{
+ LinkModelData *data;
+ char *ret;
+
+ data = g_new0 (LinkModelData, 1);
+ data->page_number = page;
+ data->document = document;
+ gtk_tree_model_foreach (links_model, link_model_foreach, data);
+ ret = data->markup;
+ g_free (data);
+
+ return ret;
+}
+
+static void
+enable_selection (GdPlacesBookmarks *self,
+ gboolean enabled)
+{
+ GtkTreeSelection *selection;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->tree_view));
+ gtk_tree_selection_set_mode (selection, enabled ? GTK_SELECTION_SINGLE : GTK_SELECTION_NONE);
+}
+
+#define MAX_LEN_LABEL 200
+#define MIN_LEN_LABEL 20
+
+static char *
+get_pretty_name (const char *text)
+{
+ char *name = NULL;
+ char trimmed[MAX_LEN_LABEL];
+ char basename[MAX_LEN_LABEL];
+ int i;
+ int last_word = -1;
+ int last_sentence = -1;
+ int last_nonspace = -1;
+ int num_attrs;
+ PangoLogAttr *attrs;
+ gboolean ellipse = TRUE;
+
+ num_attrs = MIN (g_utf8_strlen (text, -1) + 1, MAX_LEN_LABEL);
+ attrs = g_new (PangoLogAttr, num_attrs);
+ g_utf8_strncpy (trimmed, text, num_attrs - 1);
+ pango_get_log_attrs (trimmed, -1, -1, pango_language_get_default (), attrs, num_attrs);
+
+ /* since the end of the text will always match a word boundary don't include it */
+ for (i = 0; (i < num_attrs - 1); i++) {
+ if (!attrs[i].is_white) {
+ last_nonspace = i;
+ }
+ if (attrs[i].is_sentence_end) {
+ last_sentence = i;
+ }
+ if (attrs[i].is_word_boundary) {
+ last_word = last_nonspace;
+ }
+ }
+ g_free (attrs);
+
+ if (last_sentence > 0) {
+ i = last_sentence;
+ ellipse = FALSE;
+ } else {
+ i = last_word;
+ }
+
+ g_utf8_strncpy (basename, trimmed, i);
+ if (ellipse) {
+ name = g_strdup_printf ("â%sââ", basename);
+ } else {
+ name = g_strdup_printf ("â%sâ", basename);
+ }
+
+ return name;
+}
+
+static char *
+remove_duplicate_whitespace (const char *old)
+{
+ char *new;
+ GRegex *re;
+ GError *error;
+
+ error = NULL;
+ re = g_regex_new ("[ \t\n\r]+", G_REGEX_MULTILINE, 0, &error);
+ if (re == NULL) {
+ g_warning ("Error building regex: %s", error->message);
+ g_error_free (error);
+ return g_strdup (old);
+ }
+
+ new = g_regex_replace (re, old, -1, 0, " ", 0, &error);
+ g_regex_unref (re);
+ if (new == NULL) {
+ g_warning ("Error replacing string: %s", error->message);
+ g_error_free (error);
+ return g_strdup (old);
+ }
+
+ return new;
+}
+
+static void
+load_bookmark_model (GdPlacesBookmarks *self,
+ GtkTreeModel *links_model)
+{
+ GtkListStore *model;
+ GList *items;
+ GList *l;
+ EvDocument *document;
+
+ if (self->priv->bookmarks == NULL) {
+ return;
+ }
+
+ model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->tree_view)));
+
+ document = ev_document_model_get_document (self->priv->document_model);
+
+ items = gd_bookmarks_get_bookmarks (self->priv->bookmarks);
+ items = g_list_sort (items, (GCompareFunc)gd_bookmark_compare);
+ for (l = items; l; l = g_list_next (l)) {
+ GdBookmark *bookmark = (GdBookmark *)l->data;
+ GtkTreeIter iter;
+ const char *title;
+ char *label = NULL;
+ char *markup = NULL;
+ guint page;
+
+ title = gd_bookmark_get_title (bookmark);
+ page = gd_bookmark_get_page_number (bookmark);
+
+ if (ev_document_has_text_page_labels (document)) {
+ label = ev_document_get_page_label (document, page);
+ } else {
+ label = g_strdup_printf ("%d", page + 1);
+ }
+
+ if (links_model != NULL) {
+ markup = get_link_title_for_page (document, links_model, page);
+ }
+
+ if (markup == NULL && EV_IS_DOCUMENT_TEXT (document)) {
+ char *text;
+ char *trimmed;
+ char *stripped;
+ EvPage *ev_page;
+
+ ev_page = ev_document_get_page (document, page);
+ text = ev_document_text_get_text (EV_DOCUMENT_TEXT (document), ev_page);
+ trimmed = g_utf8_substring (text, 0, MAX_LEN_LABEL * 2);
+ g_free (text);
+ stripped = remove_duplicate_whitespace (trimmed);
+ g_free (trimmed);
+ markup = get_pretty_name (stripped);
+ g_free (stripped);
+ }
+
+ if (markup == NULL) {
+ markup = g_strdup_printf (_("Page %s"), label);
+ }
+
+ gtk_list_store_append (model, &iter);
+ gtk_list_store_set (model, &iter,
+ COLUMN_MARKUP, markup != NULL ? markup : title,
+ COLUMN_PAGE_LABEL, label,
+ COLUMN_BOOKMARK, bookmark,
+ -1);
+ g_free (label);
+ g_free (markup);
+ }
+
+ enable_selection (self, TRUE);
+
+ g_list_free (items);
+}
+
+static void
+job_finished_cb (EvJobLinks *job,
+ GdPlacesBookmarks *self)
+{
+ GdPlacesBookmarksPrivate *priv = self->priv;
+ GtkListStore *model;
+
+ model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)));
+ gtk_list_store_clear (model);
+ load_bookmark_model (self, job->model);
+
+ g_clear_object (&priv->job);
+}
+
+static void
+gd_places_bookmarks_update (GdPlacesBookmarks *self)
+{
+ GdPlacesBookmarksPrivate *priv = self->priv;
+ GtkListStore *model;
+ GList *l;
+ GtkTreeIter iter;
+ guint n_items = 0;
+ EvDocument *document;
+
+ if (priv->document_model == NULL) {
+ /* not loaded yet */
+ return;
+ }
+
+ if (priv->job != NULL) {
+ ev_job_cancel (priv->job);
+ g_clear_object (&priv->job);
+ }
+
+ model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view)));
+ gtk_list_store_clear (model);
+ enable_selection (self, FALSE);
+
+ if (priv->bookmarks != NULL) {
+ n_items = gd_bookmarks_get_n_items (priv->bookmarks);
+ }
+
+ document = ev_document_model_get_document (priv->document_model);
+ if (n_items == 0) {
+ gtk_list_store_append (model, &iter);
+ gtk_list_store_set (model, &iter,
+ COLUMN_MARKUP, _("No bookmarks"),
+ COLUMN_PAGE_LABEL, NULL,
+ COLUMN_BOOKMARK, NULL,
+ -1);
+ } else if (ev_document_links_has_document_links (EV_DOCUMENT_LINKS (document))) {
+ gtk_list_store_append (model, &iter);
+ gtk_list_store_set (model, &iter,
+ COLUMN_MARKUP, _("Loadingâ"),
+ COLUMN_PAGE_LABEL, NULL,
+ COLUMN_BOOKMARK, NULL,
+ -1);
+ priv->job = ev_job_links_new (document);
+ g_signal_connect (priv->job,
+ "finished",
+ G_CALLBACK (job_finished_cb),
+ self);
+
+ /* The priority doesn't matter for this job */
+ ev_job_scheduler_push_job (priv->job, EV_JOB_PRIORITY_NONE);
+ } else {
+ load_bookmark_model (self, NULL);
+ }
+}
+
+static void
+gd_places_bookmarks_changed (GdBookmarks *bookmarks,
+ GdPlacesBookmarks *self)
+{
+ gd_places_bookmarks_update (self);
+}
+
+static gboolean
+emit_activated (GdPlacesBookmarks *self)
+{
+ GtkTreeSelection *selection;
+ GdBookmark *bookmark;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->tree_view));
+ bookmark = gd_places_bookmarks_get_selected_bookmark (self, selection);
+
+ if (bookmark != NULL) {
+ g_signal_emit (self, signals[BOOKMARK_ACTIVATED], 0, bookmark);
+
+ g_object_unref (bookmark);
+ }
+
+ self->priv->activated_id = 0;
+
+ return FALSE;
+}
+
+static void
+schedule_emit_activated (GdPlacesBookmarks *self)
+{
+ /* jump through some hoops to avoid destroying in the middle
+ of a button release handler */
+ if (self->priv->activated_id == 0) {
+ self->priv->activated_id = g_idle_add ((GSourceFunc) emit_activated, self);
+ }
+}
+
+static void
+gd_places_bookmarks_set_document_model (GdPlacesPage *page,
+ EvDocumentModel *model)
+{
+ GdPlacesBookmarks *self = GD_PLACES_BOOKMARKS (page);
+ GdPlacesBookmarksPrivate *priv = self->priv;
+
+ if (priv->document_model == model)
+ return;
+
+ if (priv->document_model != NULL) {
+ g_signal_handlers_disconnect_by_func (priv->document_model,
+ gd_places_bookmarks_update,
+ page);
+ }
+
+ g_clear_object (&priv->document_model);
+ priv->document_model = model;
+
+ if (priv->document_model != NULL) {
+ g_object_ref (priv->document_model);
+ g_signal_connect_swapped (priv->document_model,
+ "notify::document",
+ G_CALLBACK (gd_places_bookmarks_update),
+ page);
+ }
+
+ gd_places_bookmarks_update (self);
+}
+
+void
+gd_places_bookmarks_set_bookmarks (GdPlacesBookmarks *self,
+ GdBookmarks *bookmarks)
+{
+ GdPlacesBookmarksPrivate *priv = self->priv;
+
+ g_return_if_fail (GD_IS_BOOKMARKS (bookmarks));
+
+ if (priv->bookmarks == bookmarks)
+ return;
+
+ if (priv->bookmarks != NULL) {
+ g_signal_handlers_disconnect_by_func (priv->bookmarks,
+ G_CALLBACK (gd_places_bookmarks_update),
+ self);
+ }
+
+ g_clear_object (&priv->bookmarks);
+ priv->bookmarks = g_object_ref (bookmarks);
+ g_signal_connect_swapped (priv->bookmarks, "changed",
+ G_CALLBACK (gd_places_bookmarks_update),
+ self);
+
+ gd_places_bookmarks_update (self);
+}
+
+static void
+gd_places_bookmarks_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+
+ GdPlacesBookmarks *self = GD_PLACES_BOOKMARKS (object);
+
+ switch (prop_id) {
+ case PROP_DOCUMENT_MODEL:
+ gd_places_bookmarks_set_document_model (GD_PLACES_PAGE (self), g_value_get_object (value));
+ break;
+ case PROP_BOOKMARKS:
+ gd_places_bookmarks_set_bookmarks (self, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gd_places_bookmarks_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdPlacesBookmarks *self = GD_PLACES_BOOKMARKS (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, self->priv->name);
+ break;
+ case PROP_DOCUMENT_MODEL:
+ g_value_set_object (value, self->priv->document_model);
+ break;
+ case PROP_BOOKMARKS:
+ g_value_set_object (value, self->priv->bookmarks);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gd_places_bookmarks_dispose (GObject *object)
+{
+ GdPlacesBookmarks *self = GD_PLACES_BOOKMARKS (object);
+ GdPlacesBookmarksPrivate *priv = self->priv;
+
+ if (priv->bookmarks != NULL) {
+ g_signal_handlers_disconnect_by_func (priv->bookmarks,
+ G_CALLBACK (gd_places_bookmarks_changed),
+ self);
+ }
+
+ if (priv->document_model != NULL) {
+ g_signal_handlers_disconnect_by_func (priv->document_model,
+ gd_places_bookmarks_update,
+ self);
+ }
+
+ if (self->priv->job != NULL) {
+ ev_job_cancel (self->priv->job);
+ g_clear_object (&self->priv->job);
+ }
+
+ if (self->priv->activated_id > 0) {
+ g_source_remove (self->priv->activated_id);
+ self->priv->activated_id = 0;
+ }
+
+ g_clear_object (&priv->document_model);
+ g_clear_object (&priv->bookmarks);
+
+ G_OBJECT_CLASS (gd_places_bookmarks_parent_class)->dispose (object);
+}
+
+static void
+gd_places_bookmarks_construct (GdPlacesBookmarks *self)
+{
+ GdPlacesBookmarksPrivate *priv = self->priv;
+ GtkWidget *swindow;
+ GtkWidget *hbox;
+ GtkListStore *model;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+
+ swindow = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (self), swindow, TRUE, TRUE, 0);
+ gtk_widget_show (swindow);
+
+ model = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, GD_TYPE_BOOKMARK);
+ priv->tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+ enable_selection (self, FALSE);
+ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (priv->tree_view), TRUE);
+ gtk_tree_view_set_activate_on_single_click (GTK_TREE_VIEW (priv->tree_view), TRUE);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
+ g_object_unref (model);
+
+ g_signal_connect_swapped (priv->tree_view, "row-activated",
+ G_CALLBACK (schedule_emit_activated),
+ self);
+
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer,
+ "wrap-mode", PANGO_WRAP_WORD,
+ "wrap-width", 350,
+ "weight", PANGO_WEIGHT_BOLD,
+ "xpad", 10,
+ NULL);
+ gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN (column), renderer, TRUE);
+ gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
+ "markup", COLUMN_MARKUP,
+ NULL);
+
+ renderer = gd_styled_text_renderer_new ();
+ gd_styled_text_renderer_add_class (GD_STYLED_TEXT_RENDERER (renderer), "dim-label");
+ g_object_set (renderer,
+ "max-width-chars", 12,
+ "scale", PANGO_SCALE_SMALL,
+ "xalign", 1.0,
+ "xpad", 10,
+ NULL);
+ gtk_tree_view_column_pack_end (GTK_TREE_VIEW_COLUMN (column), renderer, FALSE);
+ gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
+ "text", COLUMN_PAGE_LABEL,
+ NULL);
+
+ gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
+ gtk_widget_show (priv->tree_view);
+
+ hbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+
+ gtk_box_pack_end (GTK_BOX (self), hbox, FALSE, TRUE, 0);
+ gtk_widget_show (hbox);
+ gtk_widget_show (GTK_WIDGET (self));
+}
+
+static void
+gd_places_bookmarks_init (GdPlacesBookmarks *self)
+{
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ GD_TYPE_PLACES_BOOKMARKS,
+ GdPlacesBookmarksPrivate);
+
+ self->priv->name = _("Bookmarks");
+
+ gd_places_bookmarks_construct (self);
+}
+
+static void
+gd_places_bookmarks_class_init (GdPlacesBookmarksClass *klass)
+{
+ GObjectClass *oclass = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
+
+ oclass->get_property = gd_places_bookmarks_get_property;
+ oclass->set_property = gd_places_bookmarks_set_property;
+ oclass->dispose = gd_places_bookmarks_dispose;
+
+ signals[BOOKMARK_ACTIVATED] = g_signal_new ("bookmark-activated",
+ G_TYPE_FROM_CLASS (oclass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+ g_object_class_install_property (oclass,
+ PROP_BOOKMARKS,
+ g_param_spec_object ("bookmarks",
+ "Bookmarks",
+ "Bookmarks",
+ GD_TYPE_BOOKMARKS,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_override_property (oclass, PROP_NAME, "name");
+ g_object_class_override_property (oclass, PROP_DOCUMENT_MODEL, "document-model");
+
+ g_type_class_add_private (oclass, sizeof (GdPlacesBookmarksPrivate));
+}
+
+GtkWidget *
+gd_places_bookmarks_new (void)
+{
+ return GTK_WIDGET (g_object_new (GD_TYPE_PLACES_BOOKMARKS, NULL));
+}
+
+static gboolean
+gd_places_bookmarks_supports_document (GdPlacesPage *page,
+ EvDocument *document)
+{
+ return TRUE;
+}
+
+static const char *
+gd_places_bookmarks_get_name (GdPlacesPage *page)
+{
+ return GD_PLACES_BOOKMARKS (page)->priv->name;
+}
+
+static void
+gd_places_bookmarks_page_iface_init (GdPlacesPageInterface *iface)
+{
+ iface->supports_document = gd_places_bookmarks_supports_document;
+ iface->set_document_model = gd_places_bookmarks_set_document_model;
+ iface->get_name = gd_places_bookmarks_get_name;
+}
diff --git a/src/lib/gd-places-bookmarks.h b/src/lib/gd-places-bookmarks.h
new file mode 100644
index 0000000..ddc7330
--- /dev/null
+++ b/src/lib/gd-places-bookmarks.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8; -*-
+ *
+ * Copyright (C) 2010 Carlos Garcia Campos <carlosgc gnome org>
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GD_PLACES_BOOKMARKS_H__
+#define __GD_PLACES_BOOKMARKS_H__
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+#include "gd-bookmarks.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GdPlacesBookmarks GdPlacesBookmarks;
+typedef struct _GdPlacesBookmarksClass GdPlacesBookmarksClass;
+typedef struct _GdPlacesBookmarksPrivate GdPlacesBookmarksPrivate;
+
+#define GD_TYPE_PLACES_BOOKMARKS (gd_places_bookmarks_get_type())
+#define GD_PLACES_BOOKMARKS(object) (G_TYPE_CHECK_INSTANCE_CAST((object), GD_TYPE_PLACES_BOOKMARKS, GdPlacesBookmarks))
+#define GD_PLACES_BOOKMARKS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GD_TYPE_PLACES_BOOKMARKS, GdPlacesBookmarksClass))
+#define GD_IS_PLACES_BOOKMARKS(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), GD_TYPE_PLACES_BOOKMARKS))
+#define GD_IS_PLACES_BOOKMARKS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GD_TYPE_PLACES_BOOKMARKS))
+#define GD_PLACES_BOOKMARKS_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), GD_TYPE_PLACES_BOOKMARKS, GdPlacesBookmarksClass))
+
+struct _GdPlacesBookmarks {
+ GtkBox base_instance;
+
+ GdPlacesBookmarksPrivate *priv;
+};
+
+struct _GdPlacesBookmarksClass {
+ GtkBoxClass base_class;
+};
+
+GType gd_places_bookmarks_get_type (void) G_GNUC_CONST;
+GtkWidget *gd_places_bookmarks_new (void);
+void gd_places_bookmarks_set_bookmarks (GdPlacesBookmarks *places_bookmarks,
+ GdBookmarks *bookmarks);
+G_END_DECLS
+
+#endif /* __GD_PLACES_BOOKMARKS_H__ */
diff --git a/src/lib/gd-places-links.c b/src/lib/gd-places-links.c
index 5b8e5c2..04dd289 100644
--- a/src/lib/gd-places-links.c
+++ b/src/lib/gd-places-links.c
@@ -46,11 +46,10 @@ struct _GdPlacesLinksPrivate {
const char *name;
};
-enum
-{
- PROP_0,
- PROP_NAME,
- PROP_DOCUMENT_MODEL,
+enum {
+ PROP_0,
+ PROP_NAME,
+ PROP_DOCUMENT_MODEL,
};
enum {
@@ -392,7 +391,7 @@ gd_places_links_document_changed_cb (EvDocumentModel *model,
}
static gboolean
-gd_places_links_supports_document (GdPlacesPage *places_page,
+gd_places_links_supports_document (GdPlacesPage *page,
EvDocument *document)
{
return (EV_IS_DOCUMENT_LINKS (document) &&
@@ -400,10 +399,10 @@ gd_places_links_supports_document (GdPlacesPage *places_page,
}
static void
-gd_places_links_set_document_model (GdPlacesPage *places_page,
+gd_places_links_set_document_model (GdPlacesPage *page,
EvDocumentModel *model)
{
- GdPlacesLinks *self = GD_PLACES_LINKS (places_page);
+ GdPlacesLinks *self = GD_PLACES_LINKS (page);
GdPlacesLinksPrivate *priv = self->priv;
if (priv->document_model == model) {
@@ -418,7 +417,7 @@ gd_places_links_set_document_model (GdPlacesPage *places_page,
if (priv->document_model != NULL) {
g_signal_handlers_disconnect_by_func (priv->document_model,
gd_places_links_document_changed_cb,
- places_page);
+ page);
}
g_clear_object (&priv->document_model);
@@ -430,7 +429,7 @@ gd_places_links_set_document_model (GdPlacesPage *places_page,
g_signal_connect (priv->document_model,
"notify::document",
G_CALLBACK (gd_places_links_document_changed_cb),
- places_page);
+ page);
gd_places_links_document_changed_cb (priv->document_model,
NULL,
self);
@@ -438,9 +437,9 @@ gd_places_links_set_document_model (GdPlacesPage *places_page,
}
static const char *
-gd_places_links_get_name (GdPlacesPage *places_page)
+gd_places_links_get_name (GdPlacesPage *page)
{
- return GD_PLACES_LINKS (places_page)->priv->name;
+ return GD_PLACES_LINKS (page)->priv->name;
}
static void
diff --git a/src/places.js b/src/places.js
index 76c7bf7..24051c4 100644
--- a/src/places.js
+++ b/src/places.js
@@ -21,20 +21,23 @@ const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const _ = imports.gettext.gettext;
+const Gd = imports.gi.Gd;
const EvDocument = imports.gi.EvinceDocument;
const GdPrivate = imports.gi.GdPrivate;
const Application = imports.application;
const Documents = imports.documents;
const Mainloop = imports.mainloop;
+const MainToolbar = imports.mainToolbar;
const Lang = imports.lang;
const PlacesDialog = new Lang.Class({
Name: 'PlacesDialog',
- _init: function(model) {
+ _init: function(model, bookmarks) {
this._model = model;
+ this._bookmarks = bookmarks;
this._createWindow();
this.widget.show_all();
},
@@ -49,12 +52,29 @@ const PlacesDialog = new Lang.Class({
default_height: 600,
title: "",
hexpand: true });
- this.widget.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE);
+
+ let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
+ let contentArea = this.widget.get_content_area();
+ contentArea.pack_start(box, true, true, 0);
+
+ this._toolbar = new Gd.MainToolbar({ icon_size: Gtk.IconSize.MENU,
+ show_modes: true,
+ vexpand: false });
+ this._toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_MENUBAR);
+ let button = this._toolbar.add_button(null, _('Close'), false);
+ button.connect('clicked', Lang.bind(this,
+ function() {
+ this.widget.response(Gtk.ResponseType.CLOSE);
+ }));
+
+ box.pack_start(this._toolbar, false, false, 0);
this._notebook = new Gtk.Notebook ({ show_tabs: false,
- border_width: 5 });
+ border_width: 5,
+ vexpand: true });
+ box.pack_start(this._notebook, true, true, 0);
- this._linksPage = new GdPrivate.PlacesLinks ();
+ this._linksPage = new GdPrivate.PlacesLinks();
this._linksPage.connect('link-activated', Lang.bind(this,
function(widget, link) {
this._handleLink(link);
@@ -62,8 +82,12 @@ const PlacesDialog = new Lang.Class({
this._addPage(this._linksPage);
- let contentArea = this.widget.get_content_area();
- contentArea.pack_start(this._notebook, true, true, 0);
+ this._bookmarksPage = new GdPrivate.PlacesBookmarks({ bookmarks: this._bookmarks });
+ this._bookmarksPage.connect('bookmark-activated', Lang.bind(this,
+ function(widget, link) {
+ this._handleBookmark(link);
+ }));
+ this._addPage(this._bookmarksPage);
},
_handleLink: function(link) {
@@ -73,6 +97,11 @@ const PlacesDialog = new Lang.Class({
this.widget.response(Gtk.ResponseType.CLOSE);
},
+ _handleBookmark: function(bookmark) {
+ this._model.set_page(bookmark.page_number);
+ this.widget.response(Gtk.ResponseType.CLOSE);
+ },
+
_gotoDest: function(dest) {
switch (dest.type) {
case EvDocument.LinkDestType.PAGE:
@@ -92,9 +121,14 @@ const PlacesDialog = new Lang.Class({
},
_addPage: function(widget) {
- let label = new Gtk.Label({ label: widget.get_label() });
- widget.set_document_model(this._model);
- this._notebook.append_page(widget, label);
+ let label = new Gtk.Label({ label: widget.name });
+ widget.document_model = this._model;
+ let index = this._notebook.append_page(widget, label);
+ let button = this._toolbar.add_mode(widget.name);
+ button.connect('toggled', Lang.bind(this,
+ function() {
+ this._notebook.page = index;
+ }));
}
});
diff --git a/src/preview.js b/src/preview.js
index 52c8ee2..b4d1a6d 100644
--- a/src/preview.js
+++ b/src/preview.js
@@ -78,6 +78,10 @@ const PreviewView = new Lang.Class({
this.widget.show_all();
+ Application.application.connect('action-state-changed::bookmark-page',
+ Lang.bind(this, this._onActionStateChanged));
+ this._onActionStateChanged(Application.application, 'bookmark-page', Application.application.get_action_state('bookmark-page'));
+
this._zoomIn = Application.application.lookup_action('zoom-in');
this._zoomIn.connect('activate', Lang.bind(this,
function() {
@@ -115,10 +119,51 @@ const PreviewView = new Lang.Class({
}));
let showPlaces = Application.application.lookup_action('places');
showPlaces.connect('activate', Lang.bind(this, this._showPlaces));
+
+ Application.documentManager.connect('load-started',
+ Lang.bind(this, this._onLoadStarted));
+ Application.documentManager.connect('load-finished',
+ Lang.bind(this, this._onLoadFinished));
+ },
+
+ _onLoadStarted: function() {
+ this._showPlaces.enabled = false;
+ },
+
+ _onLoadFinished: function(manager, doc, docModel) {
+ this._showPlaces.enabled = true;
+
+ if (!Application.documentManager.metadata)
+ return;
+
+ this._bookmarks = new GdPrivate.Bookmarks({ metadata: Application.documentManager.metadata });
+ },
+
+ _onActionStateChanged: function(source, actionName, state) {
+ if (!this._model)
+ return;
+
+ let page_number = this._model.page;
+ let bookmark = new GdPrivate.Bookmark({ page_number: page_number });
+
+ if (state.get_boolean())
+ this._bookmarks.add(bookmark);
+ else
+ this._bookmarks.remove(bookmark);
+ },
+
+ _onPageChanged: function() {
+ this._pageChanged = true;
+
+ let page_number = this._model.page;
+ let bookmark = new GdPrivate.Bookmark({ page_number: page_number });
+
+ let bookmark = this._bookmarks.find_bookmark(bookmark);
+ Application.application.change_action_state('bookmark-page', GLib.Variant.new('b', (bookmark != null)));
},
_showPlaces: function() {
- let dialog = new Places.PlacesDialog(this._model);
+ let dialog = new Places.PlacesDialog(this._model, this._bookmarks);
dialog.widget.connect('response', Lang.bind(this,
function(widget, response) {
widget.destroy();
@@ -349,11 +394,7 @@ const PreviewView = new Lang.Class({
this._createView();
this.view.set_model(this._model);
this._navBar.widget.document_model = model;
- this._model.connect('page-changed', Lang.bind(this,
- function() {
- this._pageChanged = true;
- }));
-
+ this._model.connect('page-changed', Lang.bind(this, this._onPageChanged));
}
},
@@ -388,6 +429,13 @@ const PreviewNav = new Lang.Class({
let button_area = this.widget.get_button_area();
button_area.pack_start(button, false, false, 0);
+ let button = new Gtk.ToggleButton({ action_name: 'app.bookmark-page',
+ child: new Gtk.Image({ icon_name: 'bookmark-add-symbolic',
+ pixel_size: 16 }),
+ valign: Gtk.Align.CENTER
+ });
+ button_area.pack_start(button, false, false, 0);
+
this.actor = new GtkClutter.Actor({ contents: this.widget,
visible: false,
margin_top: _PREVIEW_NAVBAR_MARGIN,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]