[gnome-documents] Add a places dialog



commit 64d200a6edfd41c21add329952136aed21b42f4a
Author: William Jon McCann <jmccann redhat com>
Date:   Sat Jan 5 09:51:58 2013 -0500

    Add a places dialog
    
    https://bugzilla.gnome.org/show_bug.cgi?id=691215

 src/Makefile-js.am        |    1 +
 src/Makefile-lib.am       |    4 +
 src/application.js        |    3 +
 src/lib/gd-places-links.c |  523 +++++++++++++++++++++++++++++++++++++++++++++
 src/lib/gd-places-links.h |   59 +++++
 src/lib/gd-places-page.c  |   96 +++++++++
 src/lib/gd-places-page.h  |   66 ++++++
 src/places.js             |  100 +++++++++
 src/preview.js            |   11 +
 9 files changed, 863 insertions(+), 0 deletions(-)
---
diff --git a/src/Makefile-js.am b/src/Makefile-js.am
index 4f7a576..0e609f4 100644
--- a/src/Makefile-js.am
+++ b/src/Makefile-js.am
@@ -10,6 +10,7 @@ dist_js_DATA = \
     manager.js \
     miners.js \
     notifications.js \
+    places.js \
     preview.js \
     properties.js\
     query.js \
diff --git a/src/Makefile-lib.am b/src/Makefile-lib.am
index 78b1172..da1b13e 100644
--- a/src/Makefile-lib.am
+++ b/src/Makefile-lib.am
@@ -11,6 +11,8 @@ gdprivate_source_h = \
     lib/gd-metadata.h \
     lib/gd-pdf-loader.h \
     lib/gd-nav-bar.h \
+    lib/gd-places-page.h \
+    lib/gd-places-links.h \
     $(NULL)
 
 gdprivate_source_c = \
@@ -18,6 +20,8 @@ gdprivate_source_c = \
     lib/gd-metadata.c \
     lib/gd-pdf-loader.c \
     lib/gd-nav-bar.c \
+    lib/gd-places-page.c \
+    lib/gd-places-links.c \
     $(NULL)
 
 pkglib_LTLIBRARIES += libgdprivate-1.0.la
diff --git a/src/application.js b/src/application.js
index 9e0ae56..7838a6e 100644
--- a/src/application.js
+++ b/src/application.js
@@ -399,6 +399,9 @@ const Application = new Lang.Class({
               window_mode: WindowMode.WindowMode.OVERVIEW },
             { name: 'properties',
               callback: this._onActionProperties,
+              window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'places',
+              accel: 'F3',
               window_mode: WindowMode.WindowMode.PREVIEW }
         ];
 
diff --git a/src/lib/gd-places-links.c b/src/lib/gd-places-links.c
new file mode 100644
index 0000000..aab1ca2
--- /dev/null
+++ b/src/lib/gd-places-links.c
@@ -0,0 +1,523 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8;  -*-
+ *
+ *  Copyright (C) 2004 Red Hat, Inc.
+ *  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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <evince-document.h>
+#include <evince-view.h>
+
+#include "gd-places-links.h"
+#include "gd-places-page.h"
+
+struct _GdPlacesLinksPrivate {
+        GtkWidget *tree_view;
+
+        guint selection_id;
+        guint page_changed_id;
+        guint link_activated_id;
+
+        EvJob *job;
+        GtkTreeModel *model;
+        EvDocument *document;
+        EvDocumentModel *document_model;
+};
+
+enum {
+        LINK_ACTIVATED,
+        N_SIGNALS
+};
+
+static guint signals[N_SIGNALS];
+
+static void gd_places_links_page_iface_init (GdPlacesPageInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GdPlacesLinks,
+                        gd_places_links,
+                        GTK_TYPE_BOX,
+                        0,
+                        G_IMPLEMENT_INTERFACE (GD_TYPE_PLACES_PAGE,
+                                               gd_places_links_page_iface_init))
+
+
+#define GD_PLACES_LINKS_GET_PRIVATE(object) \
+        (G_TYPE_INSTANCE_GET_PRIVATE ((object), GD_TYPE_PLACES_LINKS, GdPlacesLinksPrivate))
+
+static gboolean
+emit_link_activated (GdPlacesLinks *self)
+{
+        GtkTreeSelection *selection;
+        GtkTreeModel *model;
+        GtkTreeIter iter;
+
+        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->tree_view));
+
+        if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+                EvLink *link;
+                EvDocumentModel *document_model;
+
+                gtk_tree_model_get (model, &iter,
+                                    EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
+                                    -1);
+
+                if (link == NULL) {
+                        return;
+                }
+
+
+                document_model = g_object_ref (self->priv->document_model);
+                if (self->priv->page_changed_id > 0) {
+                        g_signal_handler_block (document_model,
+                                                self->priv->page_changed_id);
+                }
+                g_signal_emit (self, signals[LINK_ACTIVATED], 0, link);
+                if (self->priv->page_changed_id > 0) {
+                        g_signal_handler_unblock (document_model,
+                                                  self->priv->page_changed_id);
+                }
+                g_object_unref (document_model);
+
+                g_object_unref (link);
+        }
+
+        self->priv->link_activated_id = 0;
+
+        return FALSE;
+}
+
+static void
+selection_changed_cb (GtkTreeSelection *selection,
+                      GdPlacesLinks    *self)
+{
+        g_return_if_fail (self->priv->document != NULL);
+
+        /* jump through some hoops to avoid destroying in the middle
+           of a button press handler */
+        if (self->priv->link_activated_id == 0) {
+                self->priv->link_activated_id = g_idle_add ((GSourceFunc)emit_link_activated, self);
+        }
+}
+
+static gboolean
+update_page_cb_foreach (GtkTreeModel  *model,
+                        GtkTreePath   *path,
+                        GtkTreeIter   *iter,
+                        GdPlacesLinks *self)
+{
+        EvLink *link;
+        int current_page;
+        int dest_page;
+        EvDocumentLinks *document_links;
+
+        gtk_tree_model_get (model, iter,
+                            EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
+                            -1);
+        if (link == NULL) {
+                return FALSE;
+        }
+
+        document_links = EV_DOCUMENT_LINKS (self->priv->document);
+
+        dest_page = ev_document_links_get_link_page (document_links, link);
+        g_object_unref (link);
+
+        current_page = ev_document_model_get_page (self->priv->document_model);
+
+        if (dest_page == current_page) {
+                gtk_tree_view_expand_to_path (GTK_TREE_VIEW (self->priv->tree_view),
+                                              path);
+                gtk_tree_view_set_cursor (GTK_TREE_VIEW (self->priv->tree_view),
+                                          path, NULL, FALSE);
+
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static void
+gd_places_links_set_current_page (GdPlacesLinks *self,
+                                  int            current_page)
+{
+        GtkTreeSelection *selection;
+        GtkTreeModel *model;
+        GtkTreeIter iter;
+
+        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->tree_view));
+
+        if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+                EvLink *link;
+
+                gtk_tree_model_get (model, &iter,
+                                    EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
+                                    -1);
+                if (link != NULL) {
+                        int dest_page;
+                        EvDocumentLinks *document_links = EV_DOCUMENT_LINKS (self->priv->document);
+
+                        dest_page = ev_document_links_get_link_page (document_links, link);
+                        g_object_unref (link);
+
+                        if (dest_page == current_page) {
+                                return;
+                        }
+                }
+        }
+
+        /* We go through the tree linearly looking for the first page that
+         * matches.  This is pretty inefficient.  We can do something neat with
+         * a GtkTreeModelSort here to make it faster, if it turns out to be
+         * slow.
+         */
+        g_signal_handler_block (selection, self->priv->selection_id);
+
+        gtk_tree_model_foreach (model,
+                                (GtkTreeModelForeachFunc)update_page_cb_foreach,
+                                self);
+
+        g_signal_handler_unblock (selection, self->priv->selection_id);
+}
+
+static void
+update_page_cb (GdPlacesLinks *self,
+                int            old_page,
+                int            new_page)
+{
+        gd_places_links_set_current_page (self, new_page);
+}
+
+static void
+job_finished_cb (EvJobLinks     *job,
+                 GdPlacesLinks *self)
+{
+        GdPlacesLinksPrivate *priv = self->priv;
+        GtkTreeSelection *selection;
+
+        g_clear_object (&priv->model);
+        priv->model = g_object_ref (job->model);
+
+        gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), job->model);
+
+        g_clear_object (&priv->job);
+
+        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
+        gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+        gtk_tree_view_expand_all (GTK_TREE_VIEW (priv->tree_view));
+
+        if (priv->selection_id <= 0) {
+                priv->selection_id =
+                        g_signal_connect (selection, "changed",
+                                          G_CALLBACK (selection_changed_cb),
+                                          self);
+        }
+
+        if (priv->page_changed_id <= 0) {
+                priv->page_changed_id =
+                        g_signal_connect_swapped (priv->document_model, "page-changed",
+                                                  G_CALLBACK (update_page_cb),
+                                                  self);
+        }
+
+        gd_places_links_set_current_page (self,
+                                          ev_document_model_get_page (priv->document_model));
+}
+
+static GtkTreeModel *
+create_loading_model (void)
+{
+        GtkTreeModel *retval;
+        GtkTreeIter iter;
+
+        /* Creates a fake model to indicate that we're loading */
+        retval = (GtkTreeModel *)gtk_list_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
+                                                     G_TYPE_STRING,
+                                                     G_TYPE_OBJECT,
+                                                     G_TYPE_BOOLEAN,
+                                                     G_TYPE_STRING);
+
+        gtk_list_store_append (GTK_LIST_STORE (retval), &iter);
+        gtk_list_store_set (GTK_LIST_STORE (retval), &iter,
+                            EV_DOCUMENT_LINKS_COLUMN_MARKUP, _("Loadingâ"),
+                            EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE,
+                            EV_DOCUMENT_LINKS_COLUMN_LINK, NULL,
+                            -1);
+
+        return retval;
+}
+
+static void
+gd_places_links_construct (GdPlacesLinks *self)
+{
+        GdPlacesLinksPrivate *priv;
+        GtkWidget *swindow;
+        GtkTreeViewColumn *column;
+        GtkCellRenderer *renderer;
+        GtkTreeSelection *selection;
+
+        priv = self->priv;
+
+        swindow = gtk_scrolled_window_new (NULL, NULL);
+
+        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
+                                             GTK_SHADOW_IN);
+
+        /* Create tree view */
+        priv->tree_view = gtk_tree_view_new ();
+
+        gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (priv->tree_view), FALSE);
+        gtk_tree_view_set_level_indentation (GTK_TREE_VIEW (priv->tree_view), 20);
+
+        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
+        gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
+        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
+        gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
+
+        gtk_box_pack_start (GTK_BOX (self), swindow, TRUE, TRUE, 0);
+        gtk_widget_show_all (GTK_WIDGET (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 = (GtkCellRenderer *)
+                g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
+                              "ellipsize", PANGO_ELLIPSIZE_END,
+                              "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", EV_DOCUMENT_LINKS_COLUMN_MARKUP,
+                                             NULL);
+
+        renderer = (GtkCellRenderer *)
+                g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
+                              "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
+                              "foreground", "#cccccc",
+                              "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", EV_DOCUMENT_LINKS_COLUMN_PAGE_LABEL,
+                                             NULL);
+}
+
+static GtkTreeModel *
+create_failed_model (void)
+{
+        GtkTreeModel *retval;
+        GtkTreeIter iter;
+
+        /* Creates a fake model to indicate there is no contents */
+        retval = (GtkTreeModel *)gtk_list_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
+                                                     G_TYPE_STRING,
+                                                     G_TYPE_OBJECT,
+                                                     G_TYPE_BOOLEAN,
+                                                     G_TYPE_STRING);
+
+        gtk_list_store_append (GTK_LIST_STORE (retval), &iter);
+        gtk_list_store_set (GTK_LIST_STORE (retval), &iter,
+                            EV_DOCUMENT_LINKS_COLUMN_MARKUP, _("No table of contents"),
+                            EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE,
+                            EV_DOCUMENT_LINKS_COLUMN_LINK, NULL,
+                            -1);
+
+        return retval;
+}
+
+static void
+gd_places_links_document_changed_cb (EvDocumentModel *model,
+                                     GParamSpec      *pspec,
+                                     GdPlacesLinks   *self)
+{
+        EvDocument *document = ev_document_model_get_document (model);
+        GdPlacesLinksPrivate *priv = self->priv;
+
+        if (!EV_IS_DOCUMENT_LINKS (document)) {
+                return;
+        }
+
+        g_clear_object (&priv->document);
+        priv->document = g_object_ref (document);
+
+        if (priv->job != NULL) {
+                ev_job_cancel (self->priv->job);
+                g_clear_object (&priv->job);
+        }
+
+        if (!gd_places_page_supports_document (GD_PLACES_PAGE (self), document)) {
+                GtkTreeModel *failed_model;
+
+                failed_model = create_failed_model ();
+                gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), failed_model);
+                g_object_unref (failed_model);
+        } else {
+                GtkTreeModel *loading_model;
+
+                loading_model = create_loading_model ();
+                gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), loading_model);
+                g_object_unref (loading_model);
+
+                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);
+        }
+}
+
+static gboolean
+gd_places_links_supports_document (GdPlacesPage *places_page,
+                                   EvDocument   *document)
+{
+        return (EV_IS_DOCUMENT_LINKS (document) &&
+                ev_document_links_has_document_links (EV_DOCUMENT_LINKS (document)));
+}
+
+static const char *
+gd_places_links_get_label (GdPlacesPage *places_page)
+{
+        return _("Contents");
+}
+
+static const char *
+gd_places_links_get_icon_name (GdPlacesPage *places_page)
+{
+        return "view-list-symbolic";
+}
+
+static void
+gd_places_links_set_document_model (GdPlacesPage    *places_page,
+                                    EvDocumentModel *model)
+{
+        GdPlacesLinks *self = GD_PLACES_LINKS (places_page);
+        GdPlacesLinksPrivate *priv = self->priv;
+
+        if (priv->document_model == model) {
+                return;
+        }
+
+        if (priv->page_changed_id > 0) {
+                g_signal_handler_disconnect (priv->document_model, priv->page_changed_id);
+                priv->page_changed_id = 0;
+        }
+
+        if (priv->document_model != NULL) {
+                g_signal_handlers_disconnect_by_func (priv->document_model,
+                                                      gd_places_links_document_changed_cb,
+                                                      places_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 (priv->document_model,
+                                  "notify::document",
+                                  G_CALLBACK (gd_places_links_document_changed_cb),
+                                  places_page);
+                gd_places_links_document_changed_cb (priv->document_model,
+                                                     NULL,
+                                                     self);
+        }
+}
+
+static void
+gd_places_links_dispose (GObject *object)
+{
+        GdPlacesLinks *self = GD_PLACES_LINKS (object);
+
+        if (self->priv->link_activated_id > 0) {
+                g_source_remove (self->priv->link_activated_id);
+                self->priv->link_activated_id = 0;
+        }
+
+        if (self->priv->job != NULL) {
+                ev_job_cancel (self->priv->job);
+                g_clear_object (&self->priv->job);
+        }
+
+        if (self->priv->page_changed_id > 0) {
+                g_signal_handler_disconnect (self->priv->document_model, self->priv->page_changed_id);
+                self->priv->page_changed_id = 0;
+        }
+
+        g_clear_object (&self->priv->model);
+        g_clear_object (&self->priv->document);
+        g_clear_object (&self->priv->document_model);
+
+        G_OBJECT_CLASS (gd_places_links_parent_class)->dispose (object);
+}
+
+
+static void
+gd_places_links_init (GdPlacesLinks *self)
+{
+        self->priv = GD_PLACES_LINKS_GET_PRIVATE (self);
+
+        gd_places_links_construct (self);
+}
+
+static void
+gd_places_links_page_iface_init (GdPlacesPageInterface *iface)
+{
+        iface->supports_document = gd_places_links_supports_document;
+        iface->set_document_model = gd_places_links_set_document_model;
+        iface->get_label = gd_places_links_get_label;
+}
+
+static void
+gd_places_links_class_init (GdPlacesLinksClass *klass)
+{
+        GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+        oclass->dispose = gd_places_links_dispose;
+
+        signals[LINK_ACTIVATED] = g_signal_new ("link-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_type_class_add_private (oclass, sizeof (GdPlacesLinksPrivate));
+}
+
+GtkWidget *
+gd_places_links_new (void)
+{
+        return g_object_new (GD_TYPE_PLACES_LINKS, NULL);
+}
diff --git a/src/lib/gd-places-links.h b/src/lib/gd-places-links.h
new file mode 100644
index 0000000..3dc6644
--- /dev/null
+++ b/src/lib/gd-places-links.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8;  -*-
+ *
+ *  Copyright (C) 2004 Red Hat, Inc.
+ *  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_LINKS_H__
+#define __GD_PLACES_LINKS_H__
+
+#include <gtk/gtk.h>
+
+#include <evince-document.h>
+#include <evince-view.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GdPlacesLinks GdPlacesLinks;
+typedef struct _GdPlacesLinksClass GdPlacesLinksClass;
+typedef struct _GdPlacesLinksPrivate GdPlacesLinksPrivate;
+
+#define GD_TYPE_PLACES_LINKS              (gd_places_links_get_type())
+#define GD_PLACES_LINKS(object)           (G_TYPE_CHECK_INSTANCE_CAST((object), GD_TYPE_PLACES_LINKS, GdPlacesLinks))
+#define GD_PLACES_LINKS_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GD_TYPE_PLACES_LINKS, GdPlacesLinksClass))
+#define GD_IS_PLACES_LINKS(object)        (G_TYPE_CHECK_INSTANCE_TYPE((object), GD_TYPE_PLACES_LINKS))
+#define GD_IS_PLACES_LINKS_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE((klass), GD_TYPE_PLACES_LINKS))
+#define GD_PLACES_LINKS_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), GD_TYPE_PLACES_LINKS, GdPlacesLinksClass))
+
+struct _GdPlacesLinks {
+        GtkBox base_instance;
+
+        GdPlacesLinksPrivate *priv;
+};
+
+struct _GdPlacesLinksClass {
+        GtkBoxClass base_class;
+};
+
+GType      gd_places_links_get_type       (void);
+GtkWidget *gd_places_links_new            (void);
+
+G_END_DECLS
+
+#endif /* __GD_PLACES_LINKS_H__ */
+
+
diff --git a/src/lib/gd-places-page.c b/src/lib/gd-places-page.c
new file mode 100644
index 0000000..f1de582
--- /dev/null
+++ b/src/lib/gd-places-page.c
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2005 Marco Pesenti Gritti
+ *  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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include "gd-places-page.h"
+
+G_DEFINE_INTERFACE (GdPlacesPage, gd_places_page, G_TYPE_INVALID)
+
+gboolean
+gd_places_page_supports_document (GdPlacesPage *places_page,
+                                  EvDocument   *document)
+{
+        GdPlacesPageInterface *iface;
+
+        g_return_val_if_fail (GD_IS_PLACES_PAGE (places_page), FALSE);
+        g_return_val_if_fail (EV_IS_DOCUMENT (document), FALSE);
+
+        iface = GD_PLACES_PAGE_GET_IFACE (places_page);
+
+        g_return_val_if_fail (iface->supports_document, FALSE);
+
+        return iface->supports_document (places_page, document);
+}
+
+void
+gd_places_page_set_document_model (GdPlacesPage    *places_page,
+                                   EvDocumentModel *model)
+{
+        GdPlacesPageInterface *iface;
+
+        g_return_if_fail (GD_IS_PLACES_PAGE (places_page));
+        g_return_if_fail (EV_IS_DOCUMENT_MODEL (model));
+
+        iface = GD_PLACES_PAGE_GET_IFACE (places_page);
+
+        g_assert (iface->set_document_model);
+
+        iface->set_document_model (places_page, model);
+}
+
+const char *
+gd_places_page_get_label (GdPlacesPage *places_page)
+{
+        GdPlacesPageInterface *iface;
+
+        g_return_val_if_fail (GD_IS_PLACES_PAGE (places_page), NULL);
+
+        iface = GD_PLACES_PAGE_GET_IFACE (places_page);
+
+        g_assert (iface->get_label);
+
+        return iface->get_label (places_page);
+}
+
+const char *
+gd_places_page_get_icon_name (GdPlacesPage *places_page)
+{
+        GdPlacesPageInterface *iface;
+
+        g_return_val_if_fail (GD_IS_PLACES_PAGE (places_page), NULL);
+
+        iface = GD_PLACES_PAGE_GET_IFACE (places_page);
+
+        g_assert (iface->get_icon_name);
+
+        return iface->get_icon_name (places_page);
+}
+
+
+static void
+gd_places_page_default_init (GdPlacesPageInterface *iface)
+{
+}
diff --git a/src/lib/gd-places-page.h b/src/lib/gd-places-page.h
new file mode 100644
index 0000000..1c123f0
--- /dev/null
+++ b/src/lib/gd-places-page.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8;  -*-
+ *
+ *  Copyright (C) 2005 Marco Pesenti Gritti
+ *  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_PAGE_H
+#define GD_PLACES_PAGE_H
+
+#include <glib-object.h>
+#include <glib.h>
+
+#include <evince-document.h>
+#include <evince-view.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_PLACES_PAGE            (gd_places_page_get_type ())
+#define GD_PLACES_PAGE(o)              (G_TYPE_CHECK_INSTANCE_CAST ((o), GD_TYPE_PLACES_PAGE, GdPlacesPage))
+#define GD_PLACES_PAGE_IFACE(k)        (G_TYPE_CHECK_CLASS_CAST((k), GD_TYPE_PLACES_PAGE, GdPlacesPageInterface))
+#define GD_IS_PLACES_PAGE(o)           (G_TYPE_CHECK_INSTANCE_TYPE ((o), GD_TYPE_PLACES_PAGE))
+#define GD_IS_PLACES_PAGE_IFACE(k)     (G_TYPE_CHECK_CLASS_TYPE ((k), GD_TYPE_PLACES_PAGE))
+#define GD_PLACES_PAGE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GD_TYPE_PLACES_PAGE, GdPlacesPageInterface))
+
+typedef struct _GdPlacesPage            GdPlacesPage;
+typedef struct _GdPlacesPageInterface   GdPlacesPageInterface;
+
+struct _GdPlacesPageInterface
+{
+        GTypeInterface base_iface;
+
+        /* Methods  */
+        gboolean     (* supports_document)  (GdPlacesPage    *places_page,
+                                             EvDocument      *document);
+        void         (* set_document_model) (GdPlacesPage    *places_page,
+                                             EvDocumentModel *model);
+        const char * (* get_label)          (GdPlacesPage    *places_page);
+        const char * (* get_icon_name    )  (GdPlacesPage    *places_page);
+};
+
+GType         gd_places_page_get_type           (void) G_GNUC_CONST;
+gboolean      gd_places_page_supports_document  (GdPlacesPage    *places_page,
+                                                 EvDocument      *document);
+void          gd_places_page_set_document_model (GdPlacesPage    *places_page,
+                                                 EvDocumentModel *model);
+const char *  gd_places_page_get_label          (GdPlacesPage    *page);
+const char *  gd_places_page_get_icon_name      (GdPlacesPage    *page);
+
+G_END_DECLS
+
+#endif /* GD_PLACES_PAGE */
diff --git a/src/places.js b/src/places.js
new file mode 100644
index 0000000..76c7bf7
--- /dev/null
+++ b/src/places.js
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * Gnome Documents 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 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Documents 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 Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+const _ = imports.gettext.gettext;
+
+const EvDocument = imports.gi.EvinceDocument;
+const GdPrivate = imports.gi.GdPrivate;
+const Application = imports.application;
+const Documents = imports.documents;
+const Mainloop = imports.mainloop;
+
+const Lang = imports.lang;
+
+const PlacesDialog = new Lang.Class({
+    Name: 'PlacesDialog',
+
+    _init: function(model) {
+        this._model = model;
+        this._createWindow();
+        this.widget.show_all();
+    },
+
+    _createWindow: function() {
+        let toplevel = Application.application.get_windows()[0];
+        this.widget = new Gtk.Dialog ({ resizable: true,
+                                        transient_for: toplevel,
+                                        modal: true,
+                                        destroy_with_parent: true,
+                                        default_width: 600, // FIXME use toplevel size
+                                        default_height: 600,
+                                        title: "",
+                                        hexpand: true });
+        this.widget.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE);
+
+        this._notebook = new Gtk.Notebook ({ show_tabs: false,
+                                             border_width: 5 });
+
+        this._linksPage = new GdPrivate.PlacesLinks ();
+        this._linksPage.connect('link-activated', Lang.bind(this,
+            function(widget, link) {
+                this._handleLink(link);
+            }));
+
+        this._addPage(this._linksPage);
+
+        let contentArea = this.widget.get_content_area();
+        contentArea.pack_start(this._notebook, true, true, 0);
+    },
+
+    _handleLink: function(link) {
+        if (link.action.type == EvDocument.LinkActionType.GOTO_DEST) {
+            this._gotoDest(link.action.dest);
+        }
+        this.widget.response(Gtk.ResponseType.CLOSE);
+    },
+
+    _gotoDest: function(dest) {
+        switch (dest.type) {
+        case EvDocument.LinkDestType.PAGE:
+        case EvDocument.LinkDestType.XYZ:
+            this._model.set_page(dest.page);
+            break;
+        case EvDocument.LinkDestType.NAMED:
+            let doc = this._model.get_document();
+            let dest2 = doc.find_link_dest(dest.named);
+            if (dest2)
+                this._gotoDest(dest2);
+            break;
+        case EvDocument.LinkDestType.PAGE_LABEL:
+            this._model.set_page_by_label(dest.page_label);
+            break;
+        }
+    },
+
+    _addPage: function(widget) {
+        let label = new Gtk.Label({ label: widget.get_label() });
+        widget.set_document_model(this._model);
+        this._notebook.append_page(widget, label);
+    }
+
+});
diff --git a/src/preview.js b/src/preview.js
index e00cae6..a3f92b1 100644
--- a/src/preview.js
+++ b/src/preview.js
@@ -35,6 +35,7 @@ const Signals = imports.signals;
 const Application = imports.application;
 const Tweener = imports.util.tweener;
 const MainToolbar = imports.mainToolbar;
+const Places = imports.places;
 const Searchbar = imports.searchbar;
 const Utils = imports.utils;
 const View = imports.view;
@@ -114,6 +115,16 @@ const PreviewView = new Lang.Class({
             function() {
                 this._changeRotation(90);
             }));
+        let showPlaces = Application.application.lookup_action('places');
+        showPlaces.connect('activate', Lang.bind(this, this._showPlaces));
+    },
+
+    _showPlaces: function() {
+        let dialog = new Places.PlacesDialog(this._model);
+        dialog.widget.connect('response', Lang.bind(this,
+            function(widget, response) {
+                widget.destroy();
+            }));
     },
 
     _onViewSelectionChanged: function() {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]