[evince] Add navigation history buttons to the toolbar



commit 67dc2d3be3c411c53ec32a9a7b2bea3981322526
Author: Carlos Garcia Campos <carlosgc gnome org>
Date:   Sun Feb 17 19:49:00 2013 +0100

    Add navigation history buttons to the toolbar
    
    Rework EvHistory to use back and forward lists instead of a single
    history list and add back/forward buttons to the toolbar to navigate the
    history of visited links.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=169903

 help/reference/shell/evince-docs.xml     |    2 -
 help/reference/shell/evince-sections.txt |   28 --
 help/reference/shell/evince.types        |    2 -
 po/POTFILES.in                           |    3 +-
 shell/Makefile.am                        |    8 +-
 shell/ev-history-action-widget.c         |  331 ++++++++++++++++++++++++
 shell/ev-history-action-widget.h         |   58 +++++
 shell/ev-history-action.c                |  119 +++++++++
 shell/ev-history-action.h                |   59 +++++
 shell/ev-history.c                       |  402 +++++++++++++++++++++++++-----
 shell/ev-history.h                       |   31 ++-
 shell/ev-navigation-action-widget.c      |  214 ----------------
 shell/ev-navigation-action-widget.h      |   46 ----
 shell/ev-navigation-action.c             |  247 ------------------
 shell/ev-navigation-action.h             |   64 -----
 shell/ev-toolbar.c                       |   12 +
 shell/ev-window.c                        |  173 +++++--------
 17 files changed, 1010 insertions(+), 789 deletions(-)
---
diff --git a/help/reference/shell/evince-docs.xml b/help/reference/shell/evince-docs.xml
index 052ab20..7c1036f 100644
--- a/help/reference/shell/evince-docs.xml
+++ b/help/reference/shell/evince-docs.xml
@@ -93,11 +93,9 @@
     <xi:include href="xml/ev-password-view.xml"/>
     <xi:include href="xml/eggfindbar.xml"/>
     <xi:include href="xml/ev-media-player-keys.xml"/>
-    <xi:include href="xml/ev-navigation-action.xml"/>
     <xi:include href="xml/ev-file-monitor.xml"/>
     <xi:include href="xml/ev-page-action.xml"/>
     <xi:include href="xml/ev-window-title.xml"/>
-    <xi:include href="xml/ev-navigation-action-widget.xml"/>
     <xi:include href="xml/ev-keyring.xml"/>
     <xi:include href="xml/ev-utils.xml"/>
     <xi:include href="xml/ev-page-action-widget.xml"/>
diff --git a/help/reference/shell/evince-sections.txt b/help/reference/shell/evince-sections.txt
index a775f7e..1bf89a5 100644
--- a/help/reference/shell/evince-sections.txt
+++ b/help/reference/shell/evince-sections.txt
@@ -366,23 +366,6 @@ EV_MEDIA_PLAYER_KEYS_GET_CLASS
 </SECTION>
 
 <SECTION>
-<FILE>ev-navigation-action</FILE>
-<TITLE>EvNavigationAction</TITLE>
-EvNavigationAction
-EvNavigationActionPrivate
-EvNavigationActionClass
-ev_navigation_action_set_history
-<SUBSECTION Standard>
-EV_NAVIGATION_ACTION
-EV_IS_NAVIGATION_ACTION
-EV_TYPE_NAVIGATION_ACTION
-ev_navigation_action_get_type
-EV_NAVIGATION_ACTION_CLASS
-EV_IS_NAVIGATION_ACTION_CLASS
-EV_NAVIGATION_ACTION_GET_CLASS
-</SECTION>
-
-<SECTION>
 <FILE>ev-file-monitor</FILE>
 <TITLE>EvFileMonitor</TITLE>
 EvFileMonitor
@@ -430,17 +413,6 @@ ev_window_title_free
 </SECTION>
 
 <SECTION>
-<FILE>ev-navigation-action-widget</FILE>
-<TITLE>EvNavigationActionWidget</TITLE>
-EV_TYPE_NAVIGATION_ACTION_WIDGET
-EV_NAVIGATION_ACTION_WIDGET
-EvNavigationActionWidget
-EvNavigationActionWidgetClass
-ev_navigation_action_widget_get_type
-ev_navigation_action_widget_set_menu
-</SECTION>
-
-<SECTION>
 <FILE>ev-keyring</FILE>
 ev_keyring_is_available
 ev_keyring_lookup_password
diff --git a/help/reference/shell/evince.types b/help/reference/shell/evince.types
index dea7db5..1df224b 100644
--- a/help/reference/shell/evince.types
+++ b/help/reference/shell/evince.types
@@ -5,8 +5,6 @@ ev_history_get_type
 ev_media_player_keys_get_type
 ev_message_area_get_type
 ev_metadata_get_type
-ev_navigation_action_get_type
-ev_navigation_action_widget_get_type
 ev_open_recent_action_get_type
 ev_page_action_get_type
 ev_page_action_widget_get_type
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e09f2fa..5455485 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -36,10 +36,9 @@ shell/eggfindbar.c
 shell/ev-annotation-properties-dialog.c
 shell/ev-application.c
 shell/ev-history.c
+shell/ev-history-action-widget.c
 shell/ev-keyring.c
 shell/ev-loading-message.c
-shell/ev-navigation-action.c
-shell/ev-navigation-action-widget.c
 shell/ev-open-recent-action.c
 shell/ev-password-view.c
 shell/ev-properties-dialog.c
diff --git a/shell/Makefile.am b/shell/Makefile.am
index 38952b6..1ec8dde 100644
--- a/shell/Makefile.am
+++ b/shell/Makefile.am
@@ -39,6 +39,10 @@ evince_SOURCES=                              \
        ev-file-monitor.c               \
        ev-history.c                    \
        ev-history.h                    \
+       ev-history-action.c             \
+       ev-history-action.h             \
+       ev-history-action-widget.c      \
+       ev-history-action-widget.h      \
        ev-keyring.h                    \
        ev-keyring.c                    \
        ev-loading-message.c            \
@@ -47,10 +51,6 @@ evince_SOURCES=                              \
        ev-message-area.h               \
        ev-metadata.c                   \
        ev-metadata.h                   \
-       ev-navigation-action.c          \
-       ev-navigation-action.h          \
-       ev-navigation-action-widget.c   \
-       ev-navigation-action-widget.h   \
        ev-password-view.h              \
        ev-password-view.c              \
        ev-progress-message-area.h      \
diff --git a/shell/ev-history-action-widget.c b/shell/ev-history-action-widget.c
new file mode 100644
index 0000000..fc3a707
--- /dev/null
+++ b/shell/ev-history-action-widget.c
@@ -0,0 +1,331 @@
+/* ev-history-action-widget.c
+ *  this file is part of evince, a gnome document viewer
+ *
+ * Copyright (C) 2013 Carlos Garcia Campos  <carlosgc gnome org>
+ *
+ * Evince 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.
+ *
+ * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "ev-history-action-widget.h"
+
+#include <glib/gi18n.h>
+#include <math.h>
+
+
+enum {
+        PROP_0,
+
+        PROP_POPUP_SHOWN
+};
+
+struct _EvHistoryActionWidgetPrivate {
+        GtkWidget *back_button;
+        GtkWidget *forward_button;
+
+        EvHistory *history;
+        gboolean   popup_shown;
+};
+
+typedef enum {
+        EV_HISTORY_ACTION_BUTTON_BACK,
+        EV_HISTORY_ACTION_BUTTON_FORWARD
+} EvHistoryActionButton;
+
+G_DEFINE_TYPE (EvHistoryActionWidget, ev_history_action_widget, GTK_TYPE_TOOL_ITEM)
+
+static void
+ev_history_action_widget_finalize (GObject *object)
+{
+        EvHistoryActionWidget *control = EV_HISTORY_ACTION_WIDGET (object);
+
+        ev_history_action_widget_set_history (control, NULL);
+
+        G_OBJECT_CLASS (ev_history_action_widget_parent_class)->finalize (object);
+}
+
+static void
+ev_history_action_widget_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+        EvHistoryActionWidget *history_widget = EV_HISTORY_ACTION_WIDGET (object);
+
+        switch (prop_id) {
+        case PROP_POPUP_SHOWN:
+                g_value_set_boolean (value, history_widget->priv->popup_shown);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+ev_history_action_widget_set_popup_shown (EvHistoryActionWidget *history_widget,
+                                          gboolean               popup_shown)
+{
+        if (history_widget->priv->popup_shown == popup_shown)
+                return;
+
+        history_widget->priv->popup_shown = popup_shown;
+        g_object_notify (G_OBJECT (history_widget), "popup-shown");
+}
+
+static void
+history_menu_link_activated (GtkMenuItem           *item,
+                             EvHistoryActionWidget *history_widget)
+{
+        EvLink *link;
+
+        link = EV_LINK (g_object_get_data (G_OBJECT (item), "ev-history-menu-item-link"));
+        if (!link)
+                return;
+
+        ev_history_go_to_link (history_widget->priv->history, link);
+}
+
+static void
+popup_menu_hide_cb (GtkMenu               *menu,
+                    EvHistoryActionWidget *history_widget)
+{
+        ev_history_action_widget_set_popup_shown (history_widget, FALSE);
+}
+
+static void
+ev_history_action_widget_show_popup (EvHistoryActionWidget *history_widget,
+                                     EvHistoryActionButton  action_button,
+                                     guint                  button,
+                                     guint32                event_time)
+{
+        GtkWidget *menu;
+        GList     *list = NULL;
+        GList     *l;
+
+        switch (action_button) {
+        case EV_HISTORY_ACTION_BUTTON_BACK:
+                list = ev_history_get_back_list (history_widget->priv->history);
+                break;
+        case EV_HISTORY_ACTION_BUTTON_FORWARD:
+                list = ev_history_get_forward_list (history_widget->priv->history);
+                break;
+        }
+
+        if (!list)
+                return;
+
+        menu = gtk_menu_new ();
+
+        for (l = list; l; l = g_list_next (l)) {
+                EvLink    *link = EV_LINK (l->data);
+                GtkWidget *item;
+
+                item = gtk_menu_item_new_with_label (ev_link_get_title (link));
+                g_object_set_data_full (G_OBJECT (item), "ev-history-menu-item-link",
+                                        g_object_ref (link), (GDestroyNotify)g_object_unref);
+                g_signal_connect_object (item, "activate",
+                                         G_CALLBACK (history_menu_link_activated),
+                                         history_widget, 0);
+                gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+                gtk_widget_show (item);
+        }
+
+        ev_history_action_widget_set_popup_shown (history_widget, TRUE);
+        g_signal_connect (menu, "hide",
+                          G_CALLBACK (popup_menu_hide_cb),
+                          history_widget);
+        gtk_menu_popup (GTK_MENU (menu),
+                        NULL, NULL, NULL, NULL,
+                        button, event_time);
+}
+
+static void
+button_clicked (GtkWidget             *button,
+                EvHistoryActionWidget *history_widget)
+{
+        EvHistoryActionWidgetPrivate *priv = history_widget->priv;
+
+        if (button == priv->back_button)
+                ev_history_go_back (priv->history);
+        else if (button == priv->forward_button)
+                ev_history_go_forward (priv->history);
+}
+
+static gboolean
+button_pressed (GtkWidget             *button,
+                GdkEventButton        *event,
+                EvHistoryActionWidget *history_widget)
+{
+        EvHistoryActionWidgetPrivate *priv = history_widget->priv;
+
+        /* TODO: Show the popup menu after a long press too */
+        switch (event->button) {
+        case GDK_BUTTON_SECONDARY:
+                ev_history_action_widget_show_popup (history_widget,
+                                                     button == priv->back_button ?
+                                                     EV_HISTORY_ACTION_BUTTON_BACK :
+                                                     EV_HISTORY_ACTION_BUTTON_FORWARD,
+                                                     event->button, event->time);
+                return GDK_EVENT_STOP;
+        default:
+                break;
+        }
+
+        return GDK_EVENT_PROPAGATE;
+}
+
+static gint
+get_icon_margin (EvHistoryActionWidget *history_widget)
+{
+        gint toolbar_size_px, menu_size_px;
+        GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (history_widget));
+
+        gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &menu_size_px, NULL);
+        gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_LARGE_TOOLBAR, &toolbar_size_px, NULL);
+
+        return (gint)floor ((toolbar_size_px - menu_size_px) / 2.0);
+}
+
+static GtkWidget *
+ev_history_action_widget_create_button (EvHistoryActionWidget *history_widget,
+                                        EvHistoryActionButton  action_button)
+{
+        GtkWidget   *button;
+        GtkWidget   *image;
+        const gchar *icon_name = NULL;
+        const gchar *tooltip_text = NULL;
+
+        button = gtk_button_new ();
+        g_signal_connect (button, "clicked",
+                          G_CALLBACK (button_clicked),
+                          history_widget);
+        g_signal_connect (button, "button-press-event",
+                          G_CALLBACK (button_pressed),
+                          history_widget);
+
+        switch (action_button) {
+        case EV_HISTORY_ACTION_BUTTON_BACK:
+                icon_name = "go-previous-symbolic";
+                tooltip_text = _("Go to previous history item");
+                break;
+        case EV_HISTORY_ACTION_BUTTON_FORWARD:
+                icon_name = "go-next-symbolic";
+                tooltip_text = _("Go to next history item");
+                break;
+        }
+
+        image = gtk_image_new ();
+        g_object_set (image, "margin", get_icon_margin (history_widget), NULL);
+        gtk_button_set_image (GTK_BUTTON (button), image);
+        gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, GTK_ICON_SIZE_MENU);
+        gtk_widget_set_tooltip_text (button, tooltip_text);
+        gtk_widget_set_can_focus (button, FALSE);
+
+        return button;
+}
+
+static void
+ev_history_action_widget_init (EvHistoryActionWidget *history_widget)
+{
+        EvHistoryActionWidgetPrivate *priv;
+        GtkWidget                    *box;
+        GtkStyleContext              *style_context;
+
+        history_widget->priv = G_TYPE_INSTANCE_GET_PRIVATE (history_widget, EV_TYPE_HISTORY_ACTION_WIDGET, 
EvHistoryActionWidgetPrivate);
+        priv = history_widget->priv;
+
+        box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+        style_context = gtk_widget_get_style_context (box);
+        gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_RAISED);
+        gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_LINKED);
+
+        priv->back_button = ev_history_action_widget_create_button (history_widget,
+                                                                    EV_HISTORY_ACTION_BUTTON_BACK);
+        gtk_container_add (GTK_CONTAINER (box), priv->back_button);
+        gtk_widget_show (priv->back_button);
+
+        priv->forward_button = ev_history_action_widget_create_button (history_widget,
+                                                                       EV_HISTORY_ACTION_BUTTON_FORWARD);
+        gtk_container_add (GTK_CONTAINER (box), priv->forward_button);
+        gtk_widget_show (priv->forward_button);
+
+        gtk_container_add (GTK_CONTAINER (history_widget), box);
+        gtk_widget_show (box);
+
+        gtk_widget_set_sensitive (priv->back_button, FALSE);
+        gtk_widget_set_sensitive (priv->forward_button, FALSE);
+}
+
+static void
+ev_history_action_widget_class_init (EvHistoryActionWidgetClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->get_property = ev_history_action_widget_get_property;
+        object_class->finalize = ev_history_action_widget_finalize;
+
+        g_object_class_install_property (object_class,
+                                         PROP_POPUP_SHOWN,
+                                         g_param_spec_boolean ("popup-shown",
+                                                               "Popup shown",
+                                                               "Whether the history's dropdown is shown",
+                                                               FALSE,
+                                                               G_PARAM_READABLE |
+                                                               G_PARAM_STATIC_STRINGS));
+
+        g_type_class_add_private (object_class, sizeof (EvHistoryActionWidgetPrivate));
+}
+
+static void
+history_changed_cb (EvHistory             *history,
+                    EvHistoryActionWidget *history_widget)
+{
+        EvHistoryActionWidgetPrivate *priv = history_widget->priv;
+
+        gtk_widget_set_sensitive (priv->back_button, ev_history_can_go_back (history));
+        gtk_widget_set_sensitive (priv->forward_button, ev_history_can_go_forward (history));
+}
+
+void
+ev_history_action_widget_set_history (EvHistoryActionWidget *history_widget,
+                                      EvHistory             *history)
+{
+        g_return_if_fail (EV_IS_HISTORY_ACTION_WIDGET (history_widget));
+
+        if (history_widget->priv->history == history)
+                return;
+
+        if (history_widget->priv->history) {
+                g_object_remove_weak_pointer (G_OBJECT (history_widget->priv->history),
+                                              (gpointer)&history_widget->priv->history);
+                g_signal_handlers_disconnect_by_func (history_widget->priv->history,
+                                                      G_CALLBACK (history_changed_cb),
+                                                      history_widget);
+        }
+
+        history_widget->priv->history = history;
+        if (!history)
+                return;
+
+        g_object_add_weak_pointer (G_OBJECT (history),
+                                   (gpointer)&history_widget->priv->history);
+
+        g_signal_connect (history, "changed",
+                          G_CALLBACK (history_changed_cb),
+                          history_widget);
+}
+
diff --git a/shell/ev-history-action-widget.h b/shell/ev-history-action-widget.h
new file mode 100644
index 0000000..befa24b
--- /dev/null
+++ b/shell/ev-history-action-widget.h
@@ -0,0 +1,58 @@
+/* ev-history-action-widget.h
+ *  this file is part of evince, a gnome document viewer
+ *
+ * Copyright (C) 2013 Carlos Garcia Campos  <carlosgc gnome org>
+ *
+ * Evince 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.
+ *
+ * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef EV_HISTORY_ACTION_WIDGET_H
+#define EV_HISTORY_ACTION_WIDGET_H
+
+#include <gtk/gtk.h>
+
+#include "ev-history.h"
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_HISTORY_ACTION_WIDGET            (ev_history_action_widget_get_type())
+#define EV_HISTORY_ACTION_WIDGET(object)         (G_TYPE_CHECK_INSTANCE_CAST((object), 
EV_TYPE_HISTORY_ACTION_WIDGET, EvHistoryActionWidget))
+#define EV_IS_HISTORY_ACTION_WIDGET(object)      (G_TYPE_CHECK_INSTANCE_TYPE((object), 
EV_TYPE_HISTORY_ACTION_WIDGET))
+#define EV_HISTORY_ACTION_WIDGET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), 
EV_TYPE_HISTORY_ACTION_WIDGET, EvHistoryActionWidgetClass))
+#define EV_IS_HISTORY_ACTION_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), 
EV_TYPE_HISTORY_ACTION_WIDGET))
+#define EV_HISTORY_ACTION_WIDGET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), 
EV_TYPE_HISTORY_ACTION_WIDGET, EvHistoryActionWidgetClass))
+
+typedef struct _EvHistoryActionWidget        EvHistoryActionWidget;
+typedef struct _EvHistoryActionWidgetClass   EvHistoryActionWidgetClass;
+typedef struct _EvHistoryActionWidgetPrivate EvHistoryActionWidgetPrivate;
+
+struct _EvHistoryActionWidget {
+        GtkToolItem parent_object;
+
+        EvHistoryActionWidgetPrivate *priv;
+};
+
+struct _EvHistoryActionWidgetClass {
+        GtkToolItemClass parent_class;
+};
+
+GType      ev_history_action_widget_get_type    (void);
+
+void       ev_history_action_widget_set_history (EvHistoryActionWidget *history_widget,
+                                                 EvHistory             *history);
+
+G_END_DECLS
+
+#endif
diff --git a/shell/ev-history-action.c b/shell/ev-history-action.c
new file mode 100644
index 0000000..cda9fc1
--- /dev/null
+++ b/shell/ev-history-action.c
@@ -0,0 +1,119 @@
+/* ev-history-action.c
+ *  this file is part of evince, a gnome document viewer
+ *
+ * Copyright (C) 2013 Carlos Garcia Campos  <carlosgc gnome org>
+ *
+ * Evince 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.
+ *
+ * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "ev-history-action.h"
+#include "ev-history-action-widget.h"
+
+enum {
+        ACTIVATED,
+        LAST_SIGNAL
+};
+
+
+struct _EvHistoryActionPrivate {
+        EvHistory *history;
+        gboolean   popup_shown;
+};
+
+G_DEFINE_TYPE (EvHistoryAction, ev_history_action, GTK_TYPE_ACTION)
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+popup_shown_cb (GObject         *history_widget,
+                GParamSpec      *pspec,
+                EvHistoryAction *history_action)
+{
+        g_object_get (history_widget, "popup-shown", &history_action->priv->popup_shown, NULL);
+}
+
+static void
+connect_proxy (GtkAction *action,
+               GtkWidget *proxy)
+{
+        if (EV_IS_HISTORY_ACTION_WIDGET (proxy))   {
+                EvHistoryAction       *history_action = EV_HISTORY_ACTION (action);
+                EvHistoryActionWidget *history_widget = EV_HISTORY_ACTION_WIDGET (proxy);
+
+                ev_history_action_widget_set_history (history_widget, history_action->priv->history);
+                g_signal_connect (history_widget, "notify::popup-shown",
+                                  G_CALLBACK (popup_shown_cb),
+                                  action);
+        }
+
+        GTK_ACTION_CLASS (ev_history_action_parent_class)->connect_proxy (action, proxy);
+}
+
+static void
+ev_history_action_class_init (EvHistoryActionClass *class)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (class);
+        GtkActionClass *action_class = GTK_ACTION_CLASS (class);
+
+        action_class->toolbar_item_type = EV_TYPE_HISTORY_ACTION_WIDGET;
+        action_class->connect_proxy = connect_proxy;
+
+        signals[ACTIVATED] =
+                g_signal_new ("activated",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              0, NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+
+        g_type_class_add_private (object_class, sizeof (EvHistoryActionPrivate));
+}
+
+static void
+ev_history_action_init (EvHistoryAction *action)
+{
+        action->priv = G_TYPE_INSTANCE_GET_PRIVATE (action, EV_TYPE_HISTORY_ACTION, EvHistoryActionPrivate);
+}
+
+void
+ev_history_action_set_history (EvHistoryAction *action,
+                               EvHistory       *history)
+{
+        GSList *proxies, *l;
+
+        g_return_if_fail (EV_IS_HISTORY_ACTION (action));
+        g_return_if_fail (EV_IS_HISTORY (history));
+
+        if (action->priv->history == history)
+                return;
+
+        action->priv->history = history;
+        proxies = gtk_action_get_proxies (GTK_ACTION (action));
+        for (l = proxies; l && l->data; l = g_slist_next (l)) {
+                if (EV_IS_HISTORY_ACTION_WIDGET (l->data))
+                        ev_history_action_widget_set_history (EV_HISTORY_ACTION_WIDGET (l->data), history);
+        }
+}
+
+gboolean
+ev_history_action_get_popup_shown (EvHistoryAction *action)
+{
+        g_return_val_if_fail (EV_IS_HISTORY_ACTION (action), FALSE);
+
+        return action->priv->popup_shown;
+}
+
diff --git a/shell/ev-history-action.h b/shell/ev-history-action.h
new file mode 100644
index 0000000..48b3160
--- /dev/null
+++ b/shell/ev-history-action.h
@@ -0,0 +1,59 @@
+/* ev-history-action.h
+ *  this file is part of evince, a gnome document viewer
+ *
+ * Copyright (C) 2013 Carlos Garcia Campos  <carlosgc gnome org>
+ *
+ * Evince 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.
+ *
+ * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef EV_HISTORY_ACTION_H
+#define EV_HISTORY_ACTION_H
+
+#include <gtk/gtk.h>
+
+#include "ev-history.h"
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_HISTORY_ACTION            (ev_history_action_get_type ())
+#define EV_HISTORY_ACTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_HISTORY_ACTION, 
EvHistoryAction))
+#define EV_IS_HISTORY_ACTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_HISTORY_ACTION))
+#define EV_HISTORY_ACTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_HISTORY_ACTION, 
EvHistoryActionClass))
+#define EV_IS_HISTORY_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EV_TYPE_HISTORY_ACTION))
+#define EV_HISTORY_ACTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), EV_TYPE_HISTORY_ACTION, 
EvHistoryActionClass))
+
+typedef struct _EvHistoryAction        EvHistoryAction;
+typedef struct _EvHistoryActionClass   EvHistoryActionClass;
+typedef struct _EvHistoryActionPrivate EvHistoryActionPrivate;
+
+struct _EvHistoryAction {
+        GtkAction parent;
+
+        EvHistoryActionPrivate *priv;
+};
+
+struct _EvHistoryActionClass {
+        GtkActionClass parent_class;
+};
+
+GType    ev_history_action_get_type        (void);
+
+void     ev_history_action_set_history     (EvHistoryAction *action,
+                                            EvHistory       *history);
+gboolean ev_history_action_get_popup_shown (EvHistoryAction *action);
+
+G_END_DECLS
+
+#endif
diff --git a/shell/ev-history.c b/shell/ev-history.c
index 9b59014..0f3ee2d 100644
--- a/shell/ev-history.c
+++ b/shell/ev-history.c
@@ -24,40 +24,40 @@
 
 #include "ev-history.h"
 
+enum {
+       CHANGED,
+        ACTIVATE_LINK,
 
-enum
-{
-       HISTORY_CHANGED,
        N_SIGNALS
 };
 
 static guint signals[N_SIGNALS] = {0, };
 
-struct _EvHistoryPrivate
-{
-       GList *links;
-};
+struct _EvHistoryPrivate {
+        EvLink          *current;
+       GList           *back_list;
+        GList           *forward_list;
 
-static void ev_history_init       (EvHistory *history);
-static void ev_history_class_init (EvHistoryClass *class);
+        EvDocumentModel *model;
+        gulong           page_changed_handler_id;
+        gboolean         activating_current_link;
+};
 
 G_DEFINE_TYPE (EvHistory, ev_history, G_TYPE_OBJECT)
 
-#define EV_HISTORY_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_HISTORY, 
EvHistoryPrivate))
+static void ev_history_set_model (EvHistory       *history,
+                                  EvDocumentModel *model);
 
 static void
-ev_history_init (EvHistory *history)
+ev_history_clear (EvHistory *history)
 {
-       history->priv = EV_HISTORY_GET_PRIVATE (history);
+        g_clear_object (&history->priv->current);
 
-       history->priv->links = NULL;
-}
+        g_list_free_full (history->priv->back_list, (GDestroyNotify)g_object_unref);
+        history->priv->back_list = NULL;
 
-static void
-free_links_list (GList *l)
-{
-       g_list_foreach (l, (GFunc)g_object_unref, NULL);
-       g_list_free (l);
+        g_list_free_full (history->priv->forward_list, (GDestroyNotify)g_object_unref);
+        history->priv->forward_list = NULL;
 }
 
 static void
@@ -65,7 +65,8 @@ ev_history_finalize (GObject *object)
 {
        EvHistory *history = EV_HISTORY (object);
 
-       free_links_list (history->priv->links);
+        ev_history_clear (history);
+        ev_history_set_model (history, NULL);
 
        G_OBJECT_CLASS (ev_history_parent_class)->finalize (object);
 }
@@ -77,72 +78,347 @@ ev_history_class_init (EvHistoryClass *class)
 
        object_class->finalize = ev_history_finalize;
 
-       signals[HISTORY_CHANGED] = 
-                   g_signal_new ("changed",
-                                 G_OBJECT_CLASS_TYPE (object_class),
-                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                                 G_STRUCT_OFFSET (EvHistoryClass, changed),
-                                 NULL, NULL,
-                                 g_cclosure_marshal_VOID__VOID,
-                                 G_TYPE_NONE, 0);
+       signals[CHANGED] =
+                g_signal_new ("changed",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                              G_STRUCT_OFFSET (EvHistoryClass, changed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+
+        signals[ACTIVATE_LINK] =
+                g_signal_new ("activate-link",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                              G_STRUCT_OFFSET (EvHistoryClass, activate_link),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__OBJECT,
+                              G_TYPE_NONE, 1,
+                              G_TYPE_OBJECT);
 
        g_type_class_add_private (object_class, sizeof (EvHistoryPrivate));
 }
 
-#define HISTORY_LENGTH   7
+static void
+ev_history_init (EvHistory *history)
+{
+       history->priv = G_TYPE_INSTANCE_GET_PRIVATE (history, EV_TYPE_HISTORY, EvHistoryPrivate);
+}
 
 void
-ev_history_add_link (EvHistory *history, EvLink *link)
+ev_history_add_link (EvHistory *history,
+                     EvLink    *link)
 {
-       GList *l;
-
        g_return_if_fail (EV_IS_HISTORY (history));
        g_return_if_fail (EV_IS_LINK (link));
 
-       for (l = history->priv->links; l; l = l->next) {
-               if (!strcmp (ev_link_get_title (EV_LINK (l->data)), ev_link_get_title (link))) {
-                       g_object_unref (G_OBJECT (l->data));
-                       history->priv->links = g_list_delete_link (history->priv->links, l);
-                       break;
-               }
-       }
+        if (history->priv->activating_current_link)
+                return;
+
+        if (ev_history_go_to_link (history, link))
+            return;
+
+        if (history->priv->current) {
+                history->priv->back_list = g_list_prepend (history->priv->back_list,
+                                                           history->priv->current);
+        }
+
+        history->priv->current = g_object_ref (link);
+
+        g_list_free_full (history->priv->forward_list, (GDestroyNotify)g_object_unref);
+        history->priv->forward_list = NULL;
+
+        /* TODO: Decide a history limit and delete old links when the limit is reached */
+
+       g_signal_emit (history, signals[CHANGED], 0);
+}
+
+static void
+ev_history_activate_current_link (EvHistory *history)
+{
+        history->priv->activating_current_link = TRUE;
+        g_signal_handler_block (history->priv->model, history->priv->page_changed_handler_id);
+        g_signal_emit (history, signals[ACTIVATE_LINK], 0, history->priv->current);
+        g_signal_handler_unblock (history->priv->model, history->priv->page_changed_handler_id);
+        history->priv->activating_current_link = FALSE;
+
+        g_signal_emit (history, signals[CHANGED], 0);
+}
+
+gboolean
+ev_history_can_go_back (EvHistory *history)
+{
+        g_return_val_if_fail (EV_IS_HISTORY (history), FALSE);
 
-       g_object_ref (link);
-       history->priv->links = g_list_append (history->priv->links,
-                                             link);
-                                             
-       if (g_list_length (history->priv->links) > HISTORY_LENGTH) {
-               g_object_unref (G_OBJECT (history->priv->links->data));
-               history->priv->links = g_list_delete_link (history->priv->links, 
-                                                          history->priv->links);
-       }
-       
-       g_signal_emit (history, signals[HISTORY_CHANGED], 0);
+        return history->priv->back_list != NULL;
 }
 
-EvLink *
-ev_history_get_link_nth        (EvHistory *history, int index)
+void
+ev_history_go_back (EvHistory *history)
 {
-       GList *l;
+        g_return_if_fail (EV_IS_HISTORY (history));
 
-       g_return_val_if_fail (EV_IS_HISTORY (history), NULL);
+        if (!history->priv->current || !history->priv->back_list)
+                return;
 
-       l = g_list_nth (history->priv->links, index);
+        history->priv->forward_list = g_list_prepend (history->priv->forward_list,
+                                                      history->priv->current);
+        history->priv->current = EV_LINK (history->priv->back_list->data);
+        history->priv->back_list = g_list_delete_link (history->priv->back_list,
+                                                       history->priv->back_list);
 
-       return EV_LINK (l->data);
+        ev_history_activate_current_link (history);
 }
 
-int
-ev_history_get_n_links (EvHistory *history)
+gboolean
+ev_history_can_go_forward (EvHistory *history)
 {
-       g_return_val_if_fail (EV_IS_HISTORY (history), -1);
+        g_return_val_if_fail (EV_IS_HISTORY (history), FALSE);
 
-       return g_list_length (history->priv->links);
+        return history->priv->forward_list != NULL;
 }
 
-EvHistory *
-ev_history_new (void)
+void
+ev_history_go_forward (EvHistory *history)
+{
+        g_return_if_fail (EV_IS_HISTORY (history));
+
+        if (!history->priv->current || !history->priv->forward_list)
+                return;
+
+        history->priv->back_list = g_list_prepend (history->priv->back_list,
+                                                   history->priv->current);
+        history->priv->current = EV_LINK (history->priv->forward_list->data);
+        history->priv->forward_list = g_list_delete_link (history->priv->forward_list,
+                                                          history->priv->forward_list);
+
+        ev_history_activate_current_link (history);
+}
+
+static gint
+compare_link (EvLink *a,
+              EvLink *b)
+{
+        if (a == b)
+                return 0;
+
+        return ev_link_action_equal (ev_link_get_action (a), ev_link_get_action (b)) ? 0 : 1;
+}
+
+gboolean
+ev_history_go_to_link (EvHistory *history,
+                       EvLink    *link)
 {
-       return EV_HISTORY (g_object_new (EV_TYPE_HISTORY, NULL));
+        GList *l;
+
+        g_return_val_if_fail (EV_IS_HISTORY (history), FALSE);
+        g_return_val_if_fail (EV_IS_LINK (link), FALSE);
+
+        if (!history->priv->current || (!history->priv->back_list && !history->priv->forward_list))
+                return FALSE;
+
+        l = g_list_find_custom (history->priv->back_list, link, (GCompareFunc)compare_link);
+        if (l) {
+                if (l->next)
+                        l->next->prev = NULL;
+                if (l->prev)
+                        l->prev->next = NULL;
+
+                history->priv->forward_list = g_list_prepend (history->priv->forward_list,
+                                                              history->priv->current);
+                history->priv->forward_list = g_list_concat (g_list_reverse (history->priv->back_list),
+                                                             history->priv->forward_list);
+                history->priv->back_list = l->next;
+                history->priv->current = EV_LINK (l->data);
+                g_list_free_1 (l);
+
+                ev_history_activate_current_link (history);
+
+                return TRUE;
+        }
+
+        l = g_list_find_custom (history->priv->forward_list, link, (GCompareFunc)compare_link);
+        if (l) {
+                if (l->next)
+                        l->next->prev = NULL;
+                if (l->prev)
+                        l->prev->next = NULL;
+
+                history->priv->back_list = g_list_prepend (history->priv->back_list,
+                                                           history->priv->current);
+                history->priv->back_list = g_list_concat (g_list_reverse (history->priv->forward_list),
+                                                          history->priv->back_list);
+                history->priv->forward_list = l->next;
+                history->priv->current = EV_LINK (l->data);
+                g_list_free_1 (l);
+
+                ev_history_activate_current_link (history);
+
+                return TRUE;
+        }
+
+        return FALSE;
 }
 
+GList *
+ev_history_get_back_list (EvHistory *history)
+{
+        g_return_val_if_fail (EV_IS_HISTORY (history), NULL);
+
+        return history->priv->back_list;
+}
+
+GList *
+ev_history_get_forward_list (EvHistory *history)
+{
+        g_return_val_if_fail (EV_IS_HISTORY (history), NULL);
+
+        return history->priv->forward_list;
+}
+
+static gint
+ev_history_get_current_page (EvHistory *history)
+{
+        EvDocument   *document;
+        EvLinkDest   *dest;
+        EvLinkAction *action;
+
+        if (!history->priv->current)
+                return -1;
+
+        action = ev_link_get_action (history->priv->current);
+        if (!action)
+                return -1;
+
+        dest = ev_link_action_get_dest (action);
+        if (!dest)
+                return -1;
+
+        switch (ev_link_dest_get_dest_type (dest)) {
+        case EV_LINK_DEST_TYPE_NAMED:
+                document = ev_document_model_get_document (history->priv->model);
+                if (!EV_IS_DOCUMENT_LINKS (document))
+                        return -1;
+
+                return ev_document_links_find_link_page (EV_DOCUMENT_LINKS (document),
+                                                         ev_link_dest_get_named_dest (dest));
+        case EV_LINK_DEST_TYPE_PAGE_LABEL: {
+                gint page = -1;
+
+                document = ev_document_model_get_document (history->priv->model);
+                ev_document_find_page_by_label (document,
+                                                ev_link_dest_get_page_label (dest),
+                                                &page);
+
+                return page;
+        }
+        default:
+                return ev_link_dest_get_page (dest);
+        }
+
+        return -1;
+}
+
+static void
+ev_history_add_link_for_page (EvHistory *history,
+                              gint       page)
+{
+        EvDocument   *document;
+        EvLinkDest   *dest;
+        EvLinkAction *action;
+        EvLink       *link;
+        gchar        *page_label;
+        gchar        *title;
+
+        if (ev_history_get_current_page (history) == page)
+                return;
+
+        document = ev_document_model_get_document (history->priv->model);
+        if (!document)
+                return;
+
+        page_label = ev_document_get_page_label (document, page);
+        if (!page_label)
+                return;
+
+        title = g_strdup_printf (_("Page %s"), page_label);
+        g_free (page_label);
+
+        dest = ev_link_dest_new_page (page);
+        action = ev_link_action_new_dest (dest);
+        g_object_unref (dest);
+
+        link = ev_link_new (title, action);
+        g_object_unref (action);
+        g_free (title);
+
+        ev_history_add_link (history, link);
+        g_object_unref (link);
+}
+
+static void
+page_changed_cb (EvDocumentModel *model,
+                 gint             old_page,
+                 gint             new_page,
+                 EvHistory       *history)
+{
+        if (ABS (new_page - old_page) > 1)
+                ev_history_add_link_for_page (history, new_page);
+}
+
+static void
+document_changed_cb (EvDocumentModel *model,
+                     GParamSpec      *pspec,
+                     EvHistory       *history)
+{
+        ev_history_clear (history);
+        ev_history_add_link_for_page (history, ev_document_model_get_page (model));
+}
+
+static void
+ev_history_set_model (EvHistory       *history,
+                      EvDocumentModel *model)
+{
+        if (history->priv->model == model)
+                return;
+
+        if (history->priv->model) {
+                g_object_remove_weak_pointer (G_OBJECT (history->priv->model),
+                                              (gpointer)&history->priv->model);
+
+                if (history->priv->page_changed_handler_id) {
+                        g_signal_handler_disconnect (history->priv->model,
+                                                     history->priv->page_changed_handler_id);
+                        history->priv->page_changed_handler_id = 0;
+                }
+        }
+
+        history->priv->model = model;
+        if (!model)
+                return;
+
+        g_object_add_weak_pointer (G_OBJECT (model),
+                                   (gpointer)&history->priv->model);
+
+        g_signal_connect (history->priv->model, "notify::document",
+                          G_CALLBACK (document_changed_cb),
+                          history);
+        history->priv->page_changed_handler_id =
+                g_signal_connect (history->priv->model, "page-changed",
+                                  G_CALLBACK (page_changed_cb),
+                                  history);
+}
+
+EvHistory *
+ev_history_new (EvDocumentModel *model)
+{
+        EvHistory *history;
+
+        g_return_val_if_fail (EV_IS_DOCUMENT_MODEL (model), NULL);
+
+        history = EV_HISTORY (g_object_new (EV_TYPE_HISTORY, NULL));
+        ev_history_set_model (history, model);
+
+        return history;
+}
diff --git a/shell/ev-history.h b/shell/ev-history.h
index d8bb53f..02604c7 100644
--- a/shell/ev-history.h
+++ b/shell/ev-history.h
@@ -21,8 +21,8 @@
 #define EV_HISTORY_H
 
 #include <glib-object.h>
-
-#include "ev-link.h"
+#include <evince-document.h>
+#include <evince-view.h>
 
 G_BEGIN_DECLS
 
@@ -40,7 +40,7 @@ typedef struct _EvHistoryClass                EvHistoryClass;
 struct _EvHistory
 {
        GObject parent;
-       
+
        /*< private >*/
        EvHistoryPrivate *priv;
 };
@@ -48,17 +48,24 @@ struct _EvHistory
 struct _EvHistoryClass
 {
        GObjectClass parent_class;
-       
-       void (*changed) (EvHistory *history);
+
+       void (* changed)       (EvHistory *history);
+        void (* activate_link) (EvHistory *history,
+                                EvLink    *link);
 };
 
-GType          ev_history_get_type             (void);
-EvHistory      *ev_history_new                 (void);
-void           ev_history_add_link             (EvHistory  *history,
-                                                EvLink     *linkk);
-EvLink        *ev_history_get_link_nth         (EvHistory  *history,
-                                                int         index);
-int            ev_history_get_n_links          (EvHistory  *history);
+GType           ev_history_get_type         (void);
+EvHistory      *ev_history_new              (EvDocumentModel *model);
+void            ev_history_add_link         (EvHistory       *history,
+                                             EvLink          *link);
+gboolean        ev_history_can_go_back      (EvHistory       *history);
+void            ev_history_go_back          (EvHistory       *history);
+gboolean        ev_history_can_go_forward   (EvHistory       *history);
+void            ev_history_go_forward       (EvHistory       *history);
+gboolean        ev_history_go_to_link       (EvHistory       *history,
+                                             EvLink          *link);
+GList          *ev_history_get_back_list    (EvHistory       *history);
+GList          *ev_history_get_forward_list (EvHistory       *history);
 
 G_END_DECLS
 
diff --git a/shell/ev-toolbar.c b/shell/ev-toolbar.c
index 20d3aca..a408769 100644
--- a/shell/ev-toolbar.c
+++ b/shell/ev-toolbar.c
@@ -26,6 +26,7 @@
 
 #include "ev-stock-icons.h"
 #include "ev-zoom-action.h"
+#include "ev-history-action.h"
 #include <math.h>
 
 enum
@@ -203,6 +204,13 @@ ev_toolbar_constructed (GObject *object)
         gtk_container_add (GTK_CONTAINER (ev_toolbar), tool_item);
         gtk_widget_show (tool_item);
 
+        /* History */
+        action = gtk_action_group_get_action (action_group, "History");
+        tool_item = gtk_action_create_tool_item (action);
+        gtk_widget_set_margin_right (tool_item, 12);
+        gtk_container_add (GTK_CONTAINER (ev_toolbar), tool_item);
+        gtk_widget_show (tool_item);
+
         /* Zoom selector */
         action = gtk_action_group_get_action (action_group, "ViewZoom");
         tool_item = gtk_action_create_tool_item (action);
@@ -314,6 +322,10 @@ ev_toolbar_has_visible_popups (EvToolbar *ev_toolbar)
         if (ev_zoom_action_get_popup_shown (EV_ZOOM_ACTION (action)))
                 return TRUE;
 
+        action = gtk_action_group_get_action (action_group, "History");
+        if (ev_history_action_get_popup_shown (EV_HISTORY_ACTION (action)))
+                return TRUE;
+
         return FALSE;
 }
 
diff --git a/shell/ev-window.c b/shell/ev-window.c
index 009a67e..2a3e67d 100644
--- a/shell/ev-window.c
+++ b/shell/ev-window.c
@@ -62,9 +62,9 @@
 #include "ev-loading-message.h"
 #include "ev-message-area.h"
 #include "ev-metadata.h"
-#include "ev-navigation-action.h"
 #include "ev-open-recent-action.h"
 #include "ev-page-action.h"
+#include "ev-history-action.h"
 #include "ev-password-view.h"
 #include "ev-properties-dialog.h"
 #include "ev-sidebar-annotations.h"
@@ -237,7 +237,7 @@ struct _EvWindowPrivate {
 
 #define PAGE_SELECTOR_ACTION   "PageSelector"
 #define ZOOM_CONTROL_ACTION    "ViewZoom"
-#define NAVIGATION_ACTION      "Navigation"
+#define HISTORY_ACTION          "History"
 
 #define GS_LOCKDOWN_SCHEMA_NAME  "org.gnome.desktop.lockdown"
 #define GS_LOCKDOWN_SAVE         "disable-save-to-disk"
@@ -481,7 +481,6 @@ ev_window_setup_action_sensitivity (EvWindow *ev_window)
        /* Toolbar-specific actions: */
        ev_window_set_action_sensitive (ev_window, PAGE_SELECTOR_ACTION, has_pages);
        ev_window_set_action_sensitive (ev_window, ZOOM_CONTROL_ACTION,  has_pages);
-       ev_window_set_action_sensitive (ev_window, NAVIGATION_ACTION,  FALSE);
 
         ev_window_update_actions_sensitivity (ev_window);
 }
@@ -801,122 +800,98 @@ ev_window_hide_loading_message (EvWindow *window)
        gtk_widget_hide (window->priv->loading_message);
 }
 
-typedef struct _PageTitleData {
-       const gchar *page_label;
-       gchar       *page_title;
-} PageTitleData;
+typedef struct _LinkTitleData {
+       EvLink      *link;
+       const gchar *link_title;
+} LinkTitleData;
 
 static gboolean
-ev_window_find_page_title (GtkTreeModel  *tree_model,
-                          GtkTreePath   *path,
-                          GtkTreeIter   *iter,
-                          PageTitleData *data)
+find_link_cb (GtkTreeModel  *tree_model,
+             GtkTreePath   *path,
+             GtkTreeIter   *iter,
+             LinkTitleData *data)
 {
-       gchar *page_string;
-       
+       EvLink *link;
+       gboolean retval = FALSE;
+
        gtk_tree_model_get (tree_model, iter,
-                           EV_DOCUMENT_LINKS_COLUMN_PAGE_LABEL, &page_string, 
+                           EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
                            -1);
-       
-       if (!page_string)
-               return FALSE;
-       
-       if (!strcmp (page_string, data->page_label)) {
-               gtk_tree_model_get (tree_model, iter,
-                                   EV_DOCUMENT_LINKS_COLUMN_MARKUP, &data->page_title, 
-                                   -1);
-               g_free (page_string);
-               return TRUE;
+       if (!link)
+               return retval;
+
+       if (ev_link_action_equal (ev_link_get_action (data->link), ev_link_get_action (link))) {
+               data->link_title = ev_link_get_title (link);
+               retval = TRUE;
        }
-       
-       g_free (page_string);
-       return FALSE;
+
+       g_object_unref (link);
+
+       return retval;
 }
 
-static gchar *
-ev_window_get_page_title (EvWindow    *window,
-                         const gchar *page_label)
+static const gchar *
+ev_window_find_title_for_link (EvWindow *window,
+                              EvLink   *link)
 {
        if (EV_IS_DOCUMENT_LINKS (window->priv->document) &&
            ev_document_links_has_document_links (EV_DOCUMENT_LINKS (window->priv->document))) {
-               PageTitleData data;
+               LinkTitleData data;
                GtkTreeModel *model;
 
-               data.page_label = page_label;
-               data.page_title = NULL;
+               data.link = link;
+               data.link_title = NULL;
 
                g_object_get (G_OBJECT (window->priv->sidebar_links),
                              "model", &model,
                              NULL);
                if (model) {
                        gtk_tree_model_foreach (model,
-                                               (GtkTreeModelForeachFunc)ev_window_find_page_title,
+                                               (GtkTreeModelForeachFunc)find_link_cb,
                                                &data);
 
                        g_object_unref (model);
                }
 
-               return data.page_title;
+               return data.link_title;
        }
 
        return NULL;
 }
 
 static void
-ev_window_add_history (EvWindow *window, gint page, EvLink *link)
+view_handle_link_cb (EvView *view, EvLink *link, EvWindow *window)
 {
-       gchar *page_label = NULL;
-       gchar *page_title;
-       gchar *link_title;
-       EvLink *real_link;
-       EvLinkAction *action;
-       EvLinkDest *dest;
-       
-       if (window->priv->history == NULL)
-               return;
+       EvLink *new_link = NULL;
 
-       if (!EV_IS_DOCUMENT_LINKS (window->priv->document))
-               return;
-       
-       if (link) {
-               action = g_object_ref (ev_link_get_action (link));
-               dest = ev_link_action_get_dest (action);
-               page_label = ev_document_links_get_dest_page_label (EV_DOCUMENT_LINKS 
(window->priv->document), dest);
-       } else {
-               dest = ev_link_dest_new_page (page);
-               action = ev_link_action_new_dest (dest);
-               g_object_unref (dest);
-               page_label = ev_document_get_page_label (window->priv->document, page);
-       }
+       if (!ev_link_get_title (link)) {
+               const gchar *link_title;
 
-       if (!page_label)
-               return;
-
-       page_title = ev_window_get_page_title (window, page_label);
-       if (page_title) {
-               link_title = g_strdup_printf (_("Page %s â %s"), page_label, page_title);
-               g_free (page_title);
-       } else {
-               link_title = g_strdup_printf (_("Page %s"), page_label);
-       }
+               link_title = ev_window_find_title_for_link (window, link);
+               if (link_title) {
+                       new_link = ev_link_new (link_title, ev_link_get_action (link));
+               } else {
+                       EvLinkAction *action;
+                       EvLinkDest   *dest;
+                       gchar        *page_label;
+                       gchar        *title;
 
-       real_link = ev_link_new (link_title, action);
-       g_object_unref (action);
+                       action = ev_link_get_action (link);
+                       dest = ev_link_action_get_dest (action);
+                       page_label = ev_document_links_get_dest_page_label (EV_DOCUMENT_LINKS 
(window->priv->document), dest);
+                       if (!page_label)
+                               return;
 
-       ev_history_add_link (window->priv->history, real_link);
+                       title = g_strdup_printf (_("Page %s"), page_label);
+                       g_free (page_label);
 
-       g_free (link_title);
-       g_free (page_label);
-       g_object_unref (real_link);
-}
-
-static void
-view_handle_link_cb (EvView *view, EvLink *link, EvWindow *window)
-{
-       int current_page = ev_document_model_get_page (window->priv->model);
-       
-       ev_window_add_history (window, 0, link);
-       ev_window_add_history (window, current_page, NULL);
+                       new_link = ev_link_new (title, action);
+                       g_free (title);
+               }
+       }
+       ev_history_add_link (window->priv->history, new_link ? new_link : link);
+       if (new_link)
+               g_object_unref (new_link);
 }
 
 static void
@@ -955,11 +930,6 @@ ev_window_page_changed_cb (EvWindow        *ev_window,
 
        ev_window_update_find_status_message (ev_window);
 
-       if (abs (new_page - old_page) > 1) {
-               ev_window_add_history (ev_window, new_page, NULL);
-               ev_window_add_history (ev_window, old_page, NULL);
-       }
-
        if (ev_window->priv->metadata && !ev_window_is_empty (ev_window))
                ev_metadata_set_int (ev_window->priv->metadata, "page", new_page);
 }
@@ -1437,7 +1407,6 @@ ev_window_setup_document (EvWindow *ev_window)
 {
        const EvDocumentInfo *info;
        EvDocument *document = ev_window->priv->document;
-       GtkAction *action;
 
        ev_window->priv->setup_document_idle = 0;
 
@@ -1461,12 +1430,6 @@ ev_window_setup_document (EvWindow *ev_window)
 
        ev_window_setup_action_sensitivity (ev_window);
 
-       if (ev_window->priv->history)
-               g_object_unref (ev_window->priv->history);
-       ev_window->priv->history = ev_history_new ();
-       action = gtk_action_group_get_action (ev_window->priv->action_group, NAVIGATION_ACTION);
-        ev_navigation_action_set_history (EV_NAVIGATION_ACTION (action), ev_window->priv->history);
-       
        if (ev_window->priv->properties) {
                ev_properties_dialog_set_document (EV_PROPERTIES_DIALOG (ev_window->priv->properties),
                                                   ev_window->priv->uri,
@@ -6072,17 +6035,12 @@ register_custom_actions (EvWindow *window, GtkActionGroup *group)
        gtk_action_group_add_action (group, action);
        g_object_unref (action);
 
-       action = g_object_new (EV_TYPE_NAVIGATION_ACTION,
-                              "name", NAVIGATION_ACTION,
-                              "label", _("Navigation"),
-                              "is_important", TRUE,
-                              "short_label", _("Back"),
-                              "stock_id", GTK_STOCK_GO_BACK,
-                              /*translators: this is the history action*/
-                              "tooltip", _("Move across visited pages"),
+       action = g_object_new (EV_TYPE_HISTORY_ACTION,
+                              "name", HISTORY_ACTION,
+                              "label", _("History"),
                               NULL);
-       g_signal_connect (action, "activate_link",
-                         G_CALLBACK (navigation_action_activate_link_cb), window);
+       ev_history_action_set_history (EV_HISTORY_ACTION (action),
+                                      window->priv->history);
        gtk_action_group_add_action (group, action);
        g_object_unref (action);
 
@@ -7082,6 +7040,11 @@ ev_window_init (EvWindow *ev_window)
         ev_window->priv->presentation_mode_inhibit_id = 0;
        ev_window->priv->title = ev_window_title_new (ev_window);
 
+       ev_window->priv->history = ev_history_new (ev_window->priv->model);
+       g_signal_connect (ev_window->priv->history, "activate-link",
+                         G_CALLBACK (activate_link_cb),
+                         ev_window);
+
        ev_window->priv->main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
        gtk_container_add (GTK_CONTAINER (ev_window), ev_window->priv->main_box);
        gtk_widget_show (ev_window->priv->main_box);


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