[yelp/new-aday-design] Initial work on implementing aday's new yelp designs



commit 8bafa65569937e1aab9649866bee57caa4472f29
Author: Shaun McCance <shaunm gnome org>
Date:   Sat Feb 22 15:17:10 2014 -0500

    Initial work on implementing aday's new yelp designs

 configure.ac                |    2 +-
 libyelp/Makefile.am         |    3 +-
 libyelp/yelp-search-entry.c |  673 ++++++++++++++++++++++++++++++++++++++++
 libyelp/yelp-search-entry.h |   77 +++++
 libyelp/yelp-view.c         |   82 +++++-
 libyelp/yelp-view.h         |   11 +
 src/yelp-application.c      |   16 +
 src/yelp-window.c           |  717 +++++++++++++++++++------------------------
 src/yelp-window.h           |    4 +-
 9 files changed, 1178 insertions(+), 407 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index bed7006..ac12a13 100644
--- a/configure.ac
+++ b/configure.ac
@@ -27,7 +27,7 @@ GLIB_GSETTINGS
 # Library dependecies
 PKG_CHECK_MODULES(YELP,
 [
-       gio-2.0 >= 2.25.11
+       gio-2.0 >= 2.32.0
        gio-unix-2.0
        gtk+-unix-print-3.0
        gtk+-3.0 >= 3.8.0
diff --git a/libyelp/Makefile.am b/libyelp/Makefile.am
index a4dd68b..1314060 100644
--- a/libyelp/Makefile.am
+++ b/libyelp/Makefile.am
@@ -9,12 +9,12 @@ libyelp_la_SOURCES =                \
        yelp-help-list.c            \
        yelp-info-document.c        \
        yelp-info-parser.c          \
-       yelp-location-entry.c       \
        yelp-magic-decompressor.c   \
        yelp-mallard-document.c     \
        yelp-man-document.c         \
        yelp-man-parser.c           \
        yelp-marshal.c              \
+       yelp-search-entry.c         \
        yelp-settings.c             \
        yelp-simple-document.c      \
        yelp-sqlite-storage.c       \
@@ -48,6 +48,7 @@ endif
 
 libyelp_la_CFLAGS =                             \
        $(YELP_CFLAGS)                          \
+       -Wno-deprecated-declarations            \
        -DDATADIR=\""$(datadir)"\"              \
        -DYELP_ICON_PATH=\"$(YELP_ICON_PATH)\"
 
diff --git a/libyelp/yelp-search-entry.c b/libyelp/yelp-search-entry.c
new file mode 100644
index 0000000..d8908e6
--- /dev/null
+++ b/libyelp/yelp-search-entry.c
@@ -0,0 +1,673 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2009-2014 Shaun McCance <shaunm gnome org>
+ *
+ * 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 of the
+ * License, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Shaun McCance <shaunm gnome org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "yelp-search-entry.h"
+#include "yelp-marshal.h"
+#include "yelp-settings.h"
+
+static void     search_entry_constructed     (GObject           *object);
+static void     search_entry_dispose         (GObject           *object);
+static void     search_entry_finalize        (GObject           *object);
+static void     search_entry_get_property    (GObject           *object,
+                                             guint              prop_id,
+                                             GValue            *value,
+                                             GParamSpec        *pspec);
+static void     search_entry_set_property    (GObject           *object,
+                                             guint              prop_id,
+                                             const GValue      *value,
+                                             GParamSpec        *pspec);
+
+/* Signals */
+static void     search_entry_location_selected (YelpSearchEntry *entry);
+static void     search_entry_search_activated  (YelpSearchEntry *entry);
+static void     search_entry_bookmark_clicked  (YelpSearchEntry *entry);
+
+
+/* Utilities */
+static void     search_entry_set_completion  (YelpSearchEntry *entry,
+                                                GtkTreeModel      *model);
+
+
+/* GtkEntry callbacks */
+static void     entry_activate_cb                   (GtkEntry          *text_entry,
+                                                     gpointer           user_data);
+
+/* GtkEntryCompletion callbacks */
+static void     cell_set_completion_bookmark_icon   (GtkCellLayout     *layout,
+                                                     GtkCellRenderer   *cell,
+                                                     GtkTreeModel      *model,
+                                                     GtkTreeIter       *iter,
+                                                     YelpSearchEntry *entry);
+static void     cell_set_completion_text_cell       (GtkCellLayout     *layout,
+                                                     GtkCellRenderer   *cell,
+                                                     GtkTreeModel      *model,
+                                                     GtkTreeIter       *iter,
+                                                     YelpSearchEntry *entry);
+static gboolean entry_match_func                    (GtkEntryCompletion *completion,
+                                                     const gchar        *key,
+                                                     GtkTreeIter        *iter,
+                                                     YelpSearchEntry  *entry);
+static gint     entry_completion_sort               (GtkTreeModel       *model,
+                                                     GtkTreeIter        *iter1,
+                                                     GtkTreeIter        *iter2,
+                                                     gpointer            user_data);
+static gboolean entry_match_selected                (GtkEntryCompletion *completion,
+                                                     GtkTreeModel       *model,
+                                                     GtkTreeIter        *iter,
+                                                     YelpSearchEntry  *entry);
+/* YelpView callbacks */
+static void          view_loaded                    (YelpView           *view,
+                                                     YelpSearchEntry    *entry);
+
+
+typedef struct _YelpSearchEntryPrivate  YelpSearchEntryPrivate;
+struct _YelpSearchEntryPrivate
+{
+    YelpView *view;
+    YelpBookmarks *bookmarks;
+    gchar *completion_uri;
+
+    /* do not free below */
+    GtkCellRenderer    *icon_cell;
+    GtkEntryCompletion *completion;
+};
+
+enum {
+    COMPLETION_COL_TITLE,
+    COMPLETION_COL_DESC,
+    COMPLETION_COL_ICON,
+    COMPLETION_COL_PAGE,
+    COMPLETION_COL_FLAGS
+};
+
+enum {
+    COMPLETION_FLAG_ACTIVATE_SEARCH = 1<<0
+};
+
+enum {
+    SEARCH_ACTIVATED,
+    LAST_SIGNAL
+};
+
+enum {  
+    PROP_0,
+    PROP_VIEW,
+    PROP_BOOKMARKS
+};
+
+static GHashTable *completions;
+
+static guint search_entry_signals[LAST_SIGNAL] = {0,};
+
+G_DEFINE_TYPE (YelpSearchEntry, yelp_search_entry, GTK_TYPE_SEARCH_ENTRY)
+#define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE((object), YELP_TYPE_SEARCH_ENTRY, 
YelpSearchEntryPrivate))
+
+static void
+yelp_search_entry_class_init (YelpSearchEntryClass *klass)
+{
+    GObjectClass *object_class;
+
+    klass->search_activated = search_entry_search_activated;
+    klass->bookmark_clicked = search_entry_bookmark_clicked;
+
+    object_class = G_OBJECT_CLASS (klass);
+  
+    object_class->constructed = search_entry_constructed;
+    object_class->dispose = search_entry_dispose;
+    object_class->finalize = search_entry_finalize;
+    object_class->get_property = search_entry_get_property;
+    object_class->set_property = search_entry_set_property;
+
+    /**
+     * YelpSearchEntry::search-activated
+     * @widget: The #YelpLocationEntry for which the signal was emitted.
+     * @text: The search text.
+     * @user_data: User data set when the handler was connected.
+     *
+     * This signal will be emitted whenever the user activates a search, generally
+     * by pressing <keycap>Enter</keycap> in the embedded text entry while @widget
+     * is in search mode.
+     **/
+    search_entry_signals[SEARCH_ACTIVATED] =
+        g_signal_new ("search-activated",
+                      G_OBJECT_CLASS_TYPE (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (YelpSearchEntryClass, search_activated),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__STRING,
+                      G_TYPE_NONE, 1,
+                      G_TYPE_STRING);
+
+    /**
+     * YelpLocationEntry:view
+     *
+     * The YelpView instance that this location entry controls.
+     **/
+    g_object_class_install_property (object_class,
+                                     PROP_VIEW,
+                                     g_param_spec_object ("view",
+                                                         _("View"),
+                                                         _("A YelpView instance to control"),
+                                                          YELP_TYPE_VIEW,
+                                                          G_PARAM_CONSTRUCT_ONLY |
+                                                         G_PARAM_READWRITE |
+                                                          G_PARAM_STATIC_STRINGS));
+
+    /**
+     * YelpLocationEntry:bookmarks
+     *
+     * An instance of an implementation of YelpBookmarks to provide
+     * bookmark information for this location entry.
+     **/
+    g_object_class_install_property (object_class,
+                                     PROP_BOOKMARKS,
+                                     g_param_spec_object ("bookmarks",
+                                                         _("Bookmarks"),
+                                                         _("A YelpBookmarks implementation instance"),
+                                                          YELP_TYPE_BOOKMARKS,
+                                                          G_PARAM_CONSTRUCT_ONLY |
+                                                         G_PARAM_READWRITE |
+                                                          G_PARAM_STATIC_STRINGS));
+
+    g_type_class_add_private ((GObjectClass *) klass,
+                              sizeof (YelpSearchEntryPrivate));
+
+    completions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+}
+
+static void
+yelp_search_entry_init (YelpSearchEntry *entry)
+{
+    YelpSearchEntryPrivate *priv = GET_PRIV (entry);
+}
+
+static void
+search_entry_constructed (GObject *object)
+{
+    GtkCellRenderer *bookmark_cell;
+    GList *cells;
+    GtkTreeIter iter;
+    YelpSearchEntryPrivate *priv = GET_PRIV (object);
+
+    g_signal_connect (object, "activate",
+                      G_CALLBACK (entry_activate_cb), object);
+
+    g_signal_connect (priv->view, "loaded", G_CALLBACK (view_loaded), object);
+}
+
+static void
+search_entry_dispose (GObject *object)
+{
+    YelpSearchEntryPrivate *priv = GET_PRIV (object);
+
+    if (priv->view) {
+        g_object_unref (priv->view);
+        priv->view = NULL;
+    }
+
+    if (priv->bookmarks) {
+        g_object_unref (priv->bookmarks);
+        priv->bookmarks = NULL;
+    }
+
+    G_OBJECT_CLASS (yelp_search_entry_parent_class)->dispose (object);
+}
+
+static void
+search_entry_finalize (GObject *object)
+{
+    YelpSearchEntryPrivate *priv = GET_PRIV (object);
+
+    g_free (priv->completion_uri);
+
+    G_OBJECT_CLASS (yelp_search_entry_parent_class)->finalize (object);
+}
+
+static void
+search_entry_get_property   (GObject      *object,
+                               guint         prop_id,
+                               GValue       *value,
+                               GParamSpec   *pspec)
+{
+    YelpSearchEntryPrivate *priv = GET_PRIV (object);
+
+    switch (prop_id) {
+    case PROP_VIEW:
+        g_value_set_object (value, priv->view);
+        break;
+    case PROP_BOOKMARKS:
+        g_value_set_object (value, priv->bookmarks);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+search_entry_set_property   (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+    YelpSearchEntryPrivate *priv = GET_PRIV (object);
+
+    switch (prop_id) {
+    case PROP_VIEW:
+        priv->view = g_value_dup_object (value);
+        break;
+    case PROP_BOOKMARKS:
+        priv->bookmarks = g_value_dup_object (value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+search_entry_search_activated  (YelpSearchEntry *entry)
+{
+    YelpUri *base, *uri;
+    YelpSearchEntryPrivate *priv = GET_PRIV (entry);
+
+    g_object_get (priv->view, "yelp-uri", &base, NULL);
+    if (base == NULL)
+        return;
+    uri = yelp_uri_new_search (base,
+                               gtk_entry_get_text (GTK_ENTRY (entry)));
+    g_object_unref (base);
+    yelp_view_load_uri (priv->view, uri);
+    gtk_widget_grab_focus (GTK_WIDGET (priv->view));
+}
+
+static void
+search_entry_bookmark_clicked  (YelpSearchEntry *entry)
+{
+    YelpUri *uri;
+    gchar *doc_uri, *page_id;
+    YelpSearchEntryPrivate *priv = GET_PRIV (entry);
+
+    g_object_get (priv->view,
+                  "yelp-uri", &uri,
+                  "page-id", &page_id,
+                  NULL);
+    doc_uri = yelp_uri_get_document_uri (uri);
+    if (priv->bookmarks && doc_uri && page_id) {
+        if (!yelp_bookmarks_is_bookmarked (priv->bookmarks, doc_uri, page_id)) {
+            gchar *icon, *title;
+            g_object_get (priv->view,
+                          "page-icon", &icon,
+                          "page-title", &title,
+                          NULL);
+            yelp_bookmarks_add_bookmark (priv->bookmarks, doc_uri, page_id, icon, title);
+            g_free (icon);
+            g_free (title);
+        }
+        else {
+            yelp_bookmarks_remove_bookmark (priv->bookmarks, doc_uri, page_id);
+        }
+    }
+    g_free (doc_uri);
+    g_free (page_id);
+    g_object_unref (uri);
+}
+
+static void
+search_entry_set_completion (YelpSearchEntry *entry,
+                            GtkTreeModel    *model)
+{
+    YelpSearchEntryPrivate *priv = GET_PRIV (entry);
+    GList *cells;
+    GtkCellRenderer *icon_cell, *bookmark_cell;
+
+    priv->completion = gtk_entry_completion_new ();
+    gtk_entry_completion_set_minimum_key_length (priv->completion, 3);
+    gtk_entry_completion_set_model (priv->completion, model);
+    gtk_entry_completion_set_text_column (priv->completion, COMPLETION_COL_TITLE);
+    gtk_entry_completion_set_match_func (priv->completion,
+                                         (GtkEntryCompletionMatchFunc) entry_match_func,
+                                         entry, NULL);
+    g_signal_connect (priv->completion, "match-selected",
+                      G_CALLBACK (entry_match_selected), entry);
+
+    cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->completion));
+    g_object_set (cells->data, "xpad", 4, NULL);
+    gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->completion),
+                                        GTK_CELL_RENDERER (cells->data),
+                                        (GtkCellLayoutDataFunc) cell_set_completion_text_cell,
+                                        entry, NULL);
+    g_object_set (cells->data, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+    g_list_free (cells);
+
+    icon_cell = gtk_cell_renderer_pixbuf_new ();
+    g_object_set (priv->icon_cell, "yalign", 0.2, NULL);
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->completion), icon_cell, FALSE);
+    gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->completion), icon_cell, 0);
+    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->completion),
+                                    icon_cell,
+                                    "icon-name",
+                                    COMPLETION_COL_ICON,
+                                    NULL);
+    if (priv->bookmarks) {
+        bookmark_cell = gtk_cell_renderer_pixbuf_new ();
+        gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (priv->completion), bookmark_cell, FALSE);
+        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->completion),
+                                            bookmark_cell,
+                                            (GtkCellLayoutDataFunc) cell_set_completion_bookmark_icon,
+                                            entry, NULL);
+    }
+    gtk_entry_set_completion (GTK_ENTRY (entry), priv->completion);
+}
+
+static void
+entry_activate_cb (GtkEntry  *text_entry,
+                   gpointer   user_data)
+{
+    YelpSearchEntryPrivate *priv = GET_PRIV (user_data);
+    gchar *text = g_strdup (gtk_entry_get_text (text_entry));
+
+    if (text == NULL || strlen(text) == 0)
+        return;
+
+    g_signal_emit (user_data, search_entry_signals[SEARCH_ACTIVATED], 0, text);
+
+    g_free (text);
+}
+
+static void
+cell_set_completion_bookmark_icon (GtkCellLayout     *layout,
+                                   GtkCellRenderer   *cell,
+                                   GtkTreeModel      *model,
+                                   GtkTreeIter       *iter,
+                                   YelpSearchEntry *entry)
+{
+    YelpSearchEntryPrivate *priv = GET_PRIV (entry);
+
+    if (priv->completion_uri) {
+        gchar *page_id = NULL;
+        gtk_tree_model_get (model, iter,
+                            COMPLETION_COL_PAGE, &page_id,
+                            -1);
+
+        if (page_id && yelp_bookmarks_is_bookmarked (priv->bookmarks,
+                                                     priv->completion_uri, page_id))
+            g_object_set (cell, "icon-name", "yelp-bookmark-remove-symbolic", NULL);
+        else
+            g_object_set (cell, "icon-name", NULL, NULL);
+
+        g_free (page_id);
+    }
+}
+
+static void
+cell_set_completion_text_cell (GtkCellLayout     *layout,
+                               GtkCellRenderer   *cell,
+                               GtkTreeModel      *model,
+                               GtkTreeIter       *iter,
+                               YelpSearchEntry *entry)
+{
+    gchar *title;
+    gint flags;
+    YelpSearchEntryPrivate *priv = GET_PRIV (entry);
+
+    gtk_tree_model_get (model, iter, COMPLETION_COL_FLAGS, &flags, -1);
+    if (flags & COMPLETION_FLAG_ACTIVATE_SEARCH) {
+        title = g_strdup_printf (_("Search for ā€œ%sā€"),
+                                 gtk_entry_get_text (GTK_ENTRY (entry)));
+        g_object_set (cell, "text", title, NULL);
+        g_free (title);
+        return;
+    }
+
+    gtk_tree_model_get (model, iter,
+                        COMPLETION_COL_TITLE, &title,
+                        -1);
+    g_object_set (cell, "text", title, NULL);
+    g_free (title);
+}
+
+static gboolean
+entry_match_func (GtkEntryCompletion *completion,
+                  const gchar        *key,
+                  GtkTreeIter        *iter,
+                  YelpSearchEntry  *entry)
+{
+    gint stri;
+    gchar *title, *desc, *titlecase = NULL, *desccase = NULL;
+    gboolean ret = FALSE;
+    gchar **strs;
+    gint flags;
+    GtkTreeModel *model = gtk_entry_completion_get_model (completion);
+    YelpSearchEntryPrivate *priv = GET_PRIV (entry);
+    static GRegex *nonword = NULL;
+
+    if (nonword == NULL)
+        nonword = g_regex_new ("\\W", 0, 0, NULL);
+    if (nonword == NULL)
+        return FALSE;
+
+    gtk_tree_model_get (model, iter, COMPLETION_COL_FLAGS, &flags, -1);
+    if (flags & COMPLETION_FLAG_ACTIVATE_SEARCH)
+        return TRUE;
+
+    gtk_tree_model_get (model, iter,
+                        COMPLETION_COL_TITLE, &title,
+                        COMPLETION_COL_DESC, &desc,
+                        -1);
+    if (title) {
+        titlecase = g_utf8_casefold (title, -1);
+        g_free (title);
+    }
+    if (desc) {
+        desccase = g_utf8_casefold (desc, -1);
+        g_free (desc);
+    }
+
+    strs = g_regex_split (nonword, key, 0);
+    ret = TRUE;
+    for (stri = 0; strs[stri]; stri++) {
+        if (!titlecase || !strstr (titlecase, strs[stri])) {
+            if (!desccase || !strstr (desccase, strs[stri])) {
+                ret = FALSE;
+                break;
+            }
+        }
+    }
+
+    g_free (titlecase);
+    g_free (desccase);
+    g_strfreev (strs);
+
+    return ret;
+}
+
+static gint
+entry_completion_sort (GtkTreeModel *model,
+                       GtkTreeIter  *iter1,
+                       GtkTreeIter  *iter2,
+                       gpointer      user_data)
+{
+    gint ret = 0;
+    gint flags1, flags2;
+    gchar *key1, *key2;
+
+    gtk_tree_model_get (model, iter1, COMPLETION_COL_FLAGS, &flags1, -1);
+    gtk_tree_model_get (model, iter2, COMPLETION_COL_FLAGS, &flags2, -1);
+    if (flags1 & COMPLETION_FLAG_ACTIVATE_SEARCH)
+        return 1;
+    else if (flags2 & COMPLETION_FLAG_ACTIVATE_SEARCH)
+        return -1;
+
+    gtk_tree_model_get (model, iter1, COMPLETION_COL_ICON, &key1, -1);
+    gtk_tree_model_get (model, iter2, COMPLETION_COL_ICON, &key2, -1);
+    ret = yelp_settings_cmp_icons (key1, key2);
+    g_free (key1);
+    g_free (key2);
+
+    if (ret)
+        return ret;
+
+    gtk_tree_model_get (model, iter1, COMPLETION_COL_TITLE, &key1, -1);
+    gtk_tree_model_get (model, iter2, COMPLETION_COL_TITLE, &key2, -1);
+    if (key1 && key2)
+        ret = g_utf8_collate (key1, key2);
+    else if (key2 == NULL)
+        return -1;
+    else if (key1 == NULL)
+        return 1;
+    g_free (key1);
+    g_free (key2);
+
+    return ret;
+}
+
+static gboolean
+entry_match_selected (GtkEntryCompletion *completion,
+                      GtkTreeModel       *model,
+                      GtkTreeIter        *iter,
+                      YelpSearchEntry    *entry)
+{
+    YelpUri *base, *uri;
+    gchar *page, *xref;
+    gint flags;
+    YelpSearchEntryPrivate *priv = GET_PRIV (entry);
+
+    gtk_tree_model_get (model, iter, COMPLETION_COL_FLAGS, &flags, -1);
+    if (flags & COMPLETION_FLAG_ACTIVATE_SEARCH) {
+        entry_activate_cb (GTK_ENTRY (entry), entry);
+        return TRUE;
+    }
+
+    g_object_get (priv->view, "yelp-uri", &base, NULL);
+    gtk_tree_model_get (model, iter, COMPLETION_COL_PAGE, &page, -1);
+
+    xref = g_strconcat ("xref:", page, NULL);
+    uri = yelp_uri_new_relative (base, xref);
+
+    yelp_view_load_uri (priv->view, uri);
+
+    g_free (page);
+    g_free (xref);
+    g_object_unref (uri);
+    g_object_unref (base);
+
+    gtk_widget_grab_focus (GTK_WIDGET (priv->view));
+    return TRUE;
+}
+
+static void
+view_loaded (YelpView          *view,
+             YelpSearchEntry *entry)
+{
+    gchar **ids;
+    gint i;
+    GtkTreeIter iter;
+    gint flags;
+    YelpUri *uri;
+    gchar *doc_uri;
+    GtkTreeModel *completion;
+    YelpSearchEntryPrivate *priv = GET_PRIV (entry);
+    YelpDocument *document = yelp_view_get_document (view);
+
+    g_object_get (view, "yelp-uri", &uri, NULL);
+    doc_uri = yelp_uri_get_document_uri (uri);
+
+    if ((priv->completion_uri == NULL) || 
+        !g_str_equal (doc_uri, priv->completion_uri)) {
+        completion = (GtkTreeModel *) g_hash_table_lookup (completions, doc_uri);
+        if (completion == NULL) {
+            GtkListStore *base = gtk_list_store_new (5,
+                                                     G_TYPE_STRING,  /* title */
+                                                     G_TYPE_STRING,  /* desc */
+                                                     G_TYPE_STRING,  /* icon */
+                                                     G_TYPE_STRING,  /* uri */
+                                                     G_TYPE_INT      /* flags */
+                                                     );
+            completion = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (base));
+            gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (completion),
+                                                     entry_completion_sort,
+                                                     NULL, NULL);
+            g_hash_table_insert (completions, g_strdup (doc_uri), completion);
+            if (document != NULL) {
+                ids = yelp_document_list_page_ids (document);
+                for (i = 0; ids[i]; i++) {
+                    gchar *title, *desc, *icon;
+                    gtk_list_store_insert (GTK_LIST_STORE (base), &iter, 0);
+                    title = yelp_document_get_page_title (document, ids[i]);
+                    desc = yelp_document_get_page_desc (document, ids[i]);
+                    icon = yelp_document_get_page_icon (document, ids[i]);
+                    gtk_list_store_set (base, &iter,
+                                        COMPLETION_COL_TITLE, title,
+                                        COMPLETION_COL_DESC, desc,
+                                        COMPLETION_COL_ICON, icon,
+                                        COMPLETION_COL_PAGE, ids[i],
+                                        -1);
+                    g_free (icon);
+                    g_free (desc);
+                    g_free (title);
+                }
+                g_strfreev (ids);
+                gtk_list_store_insert (GTK_LIST_STORE (base), &iter, 0);
+                gtk_list_store_set (base, &iter,
+                                    COMPLETION_COL_ICON, "edit-find-symbolic",
+                                    COMPLETION_COL_FLAGS, COMPLETION_FLAG_ACTIVATE_SEARCH,
+                                    -1);
+            }
+            g_object_unref (base);
+        }
+        g_free (priv->completion_uri);
+        priv->completion_uri = doc_uri;
+        search_entry_set_completion (entry, completion);
+    }
+
+    g_object_unref (uri);
+}
+
+/**
+ * yelp_search_entry_new:
+ * @view: A #YelpView.
+ *
+ * Creates a new #YelpSearchEntry widget to control @view.
+ *
+ * Returns: A new #YelpSearchEntry.
+ **/
+GtkWidget*
+yelp_search_entry_new (YelpView      *view,
+                      YelpBookmarks *bookmarks)
+{
+    GtkWidget *ret;
+    g_return_val_if_fail (YELP_IS_VIEW (view), NULL);
+
+    ret = GTK_WIDGET (g_object_new (YELP_TYPE_SEARCH_ENTRY,
+                                    "view", view,
+                                    "bookmarks", bookmarks,
+                                    NULL));
+
+    return ret;
+}
diff --git a/libyelp/yelp-search-entry.h b/libyelp/yelp-search-entry.h
new file mode 100644
index 0000000..df7e3fe
--- /dev/null
+++ b/libyelp/yelp-search-entry.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2009-2014 Shaun McCance <shaunm gnome org>
+ *
+ * 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 of the
+ * License, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Shaun McCance <shaunm gnome org>
+ */
+
+#ifndef __YELP_SEARCH_ENTRY_H__
+#define __YELP_SEARCH_ENTRY_H__
+
+#include <gtk/gtk.h>
+
+#include "yelp-bookmarks.h"
+#include "yelp-view.h"
+
+G_BEGIN_DECLS
+
+#define YELP_TYPE_SEARCH_ENTRY (yelp_search_entry_get_type ())
+#define YELP_SEARCH_ENTRY(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), YELP_TYPE_SEARCH_ENTRY, \
+                             YelpSearchEntry))
+#define YELP_SEARCH_ENTRY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), YELP_TYPE_SEARCH_ENTRY, \
+                          YelpSearchEntryClass))
+#define YELP_IS_SEARCH_ENTRY(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), YELP_TYPE_SEARCH_ENTRY))
+#define YELP_IS_SEARCH_ENTRY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), YELP_TYPE_SEARCH_ENTRY))
+#define YELP_SEARCH_ENTRY_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS((obj), YELP_TYPE_SEARCH_ENTRY, \
+                            YelpSearchEntryClass))
+
+/**
+ * YelpSearchEntry:
+ **/
+typedef struct _YelpSearchEntry         YelpSearchEntry;
+typedef struct _YelpSearchEntryClass    YelpSearchEntryClass;
+
+struct _YelpSearchEntry
+{
+    GtkSearchEntry parent;
+};
+
+struct _YelpSearchEntryClass
+{
+    GtkSearchEntryClass parent;
+
+    void     (* search_activated)           (YelpSearchEntry *entry);
+    void     (* bookmark_clicked)           (YelpSearchEntry *entry);
+
+    /* Padding for future expansion */
+    void (*_gtk_reserved0) (void);
+    void (*_gtk_reserved1) (void);
+    void (*_gtk_reserved2) (void);
+    void (*_gtk_reserved3) (void);
+};
+
+GType           yelp_search_entry_get_type          (void);
+GtkWidget *     yelp_search_entry_new               (YelpView           *window,
+                                                    YelpBookmarks      *bookmarks);
+
+G_END_DECLS
+
+#endif /* __YELP_SEARCH_ENTRY_H__ */
diff --git a/libyelp/yelp-view.c b/libyelp/yelp-view.c
index 1529137..4764cf2 100644
--- a/libyelp/yelp-view.c
+++ b/libyelp/yelp-view.c
@@ -104,7 +104,8 @@ static void        view_document_loaded           (WebKitWebView             *vi
 
 static void        view_print                     (GtkAction          *action,
                                                    YelpView           *view);
-static void        view_history_action            (GtkAction          *action,
+static void        view_history_action            (GAction            *action,
+                                                   GVariant           *parameter,
                                                    YelpView           *view);
 static void        view_navigation_action         (GtkAction          *action,
                                                    YelpView           *view);
@@ -130,6 +131,7 @@ static const GtkActionEntry entries[] = {
      "<Control>P",
      NULL,
      G_CALLBACK (view_print) },
+#if 0
     {"YelpViewGoBack", GTK_STOCK_GO_BACK,
      N_("_Back"),
      "<Alt>Left",
@@ -140,6 +142,7 @@ static const GtkActionEntry entries[] = {
      "<Alt>Right",
      NULL,
      G_CALLBACK (view_history_action) },
+#endif
     {"YelpViewGoPrevious", NULL,
      N_("_Previous Page"),
      "<Control>Page_Up",
@@ -247,7 +250,12 @@ struct _YelpViewPrivate {
     GList          *back_cur;
     gboolean        back_load;
 
+    GSimpleAction  *back_action;
+    GSimpleAction  *forward_action;
+
+#if 0
     GtkActionGroup *action_group;
+#endif
 
     GSList         *link_actions;
 
@@ -287,6 +295,7 @@ yelp_view_init (YelpView *view)
     g_signal_connect (view, "script-alert",
                       G_CALLBACK (view_script_alert), NULL);
 
+#if 0
     priv->action_group = gtk_action_group_new ("YelpView");
     gtk_action_group_set_translation_domain (priv->action_group, GETTEXT_PACKAGE);
     gtk_action_group_add_actions (priv->action_group,
@@ -296,6 +305,21 @@ yelp_view_init (YelpView *view)
     gtk_action_set_sensitive (action, FALSE);
     action = gtk_action_group_get_action (priv->action_group, "YelpViewGoForward");
     gtk_action_set_sensitive (action, FALSE);
+#endif
+
+    priv->back_action = g_simple_action_new ("yelp-view-go-back", NULL);
+    g_simple_action_set_enabled (priv->back_action, FALSE);
+    g_signal_connect (priv->back_action,
+                      "activate",
+                      G_CALLBACK (view_history_action),
+                      view);
+
+    priv->forward_action = g_simple_action_new ("yelp-view-go-forward", NULL);
+    g_simple_action_set_enabled (priv->forward_action, FALSE);
+    g_signal_connect (priv->forward_action,
+                      "activate",
+                      G_CALLBACK (view_history_action),
+                      view);
 }
 
 static void
@@ -315,10 +339,22 @@ yelp_view_dispose (GObject *object)
         priv->hadjuster = 0;
     }
 
+#if 0
     if (priv->action_group) {
         g_object_unref (priv->action_group);
         priv->action_group = NULL;
     }
+#endif
+
+    if (priv->back_action) {
+        g_object_unref (priv->back_action);
+        priv->back_action = NULL;
+    }
+
+    if (priv->forward_action) {
+        g_object_unref (priv->forward_action);
+        priv->forward_action = NULL;
+    }
 
     if (priv->document) {
         g_object_unref (priv->document);
@@ -571,12 +607,14 @@ yelp_view_load_uri (YelpView *view,
 
     g_object_set (view, "state", YELP_VIEW_STATE_LOADING, NULL);
 
+#if 0
     gtk_action_set_sensitive (gtk_action_group_get_action (priv->action_group,
                                                            "YelpViewGoPrevious"),
                               FALSE);
     gtk_action_set_sensitive (gtk_action_group_get_action (priv->action_group,
                                                            "YelpViewGoNext"),
                               FALSE);
+#endif
 
     if (!yelp_uri_is_resolved (uri)) {
         if (priv->resolve_uri != NULL) {
@@ -630,6 +668,17 @@ yelp_view_get_document (YelpView *view)
     return priv->document;
 }
 
+void
+yelp_view_register_actions (YelpView   *view,
+                            GActionMap *map)
+{
+    YelpViewPrivate *priv = GET_PRIV (view);
+    g_action_map_add_action (map, priv->back_action);
+    g_action_map_add_action (map, priv->forward_action);
+}
+
+
+#if 0
 GtkActionGroup *
 yelp_view_get_action_group (YelpView *view)
 {
@@ -637,6 +686,22 @@ yelp_view_get_action_group (YelpView *view)
     return priv->action_group;
 }
 
+GAction *
+yelp_view_get_back_action (YelpView *view)
+{
+    YelpViewPrivate *priv = GET_PRIV (view);
+    return G_ACTION (priv->back_action);
+}
+
+GAction *
+yelp_view_get_forward_action (YelpView *view)
+{
+    YelpViewPrivate *priv = GET_PRIV (view);
+    return G_ACTION (priv->forward_action);
+}
+#endif
+
+
 /******************************************************************************/
 
 void
@@ -1326,12 +1391,14 @@ view_populate_popup (YelpView *view,
         }
     }
     else {
+#if 0
         item = gtk_action_create_menu_item (gtk_action_group_get_action (priv->action_group,
                                                                          "YelpViewGoBack"));
         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
         item = gtk_action_create_menu_item (gtk_action_group_get_action (priv->action_group,
                                                                          "YelpViewGoForward"));
         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+#endif
     }
 
     if ((context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) ||
@@ -1522,7 +1589,8 @@ view_print (GtkAction *action, YelpView  *view)
 }
 
 static void
-view_history_action (GtkAction *action,
+view_history_action (GAction   *action,
+                     GVariant  *parameter,
                      YelpView  *view)
 {
     GList *newcur;
@@ -1531,7 +1599,7 @@ view_history_action (GtkAction *action,
     if (priv->back_cur == NULL)
         return;
 
-    if (g_str_equal (gtk_action_get_name (action), "YelpViewGoBack"))
+    if (g_str_equal (g_action_get_name (action), "yelp-view-go-back"))
         newcur = priv->back_cur->next;
     else
         newcur = priv->back_cur->prev;
@@ -1932,6 +2000,7 @@ uri_resolved (YelpUri  *uri,
     }
     priv->back_load = FALSE;
 
+#if 0
     action = gtk_action_group_get_action (priv->action_group, "YelpViewGoBack");
     gtk_action_set_sensitive (action, FALSE);
     proxies = gtk_action_get_proxies (action);
@@ -1959,7 +2028,10 @@ uri_resolved (YelpUri  *uri,
         for (cur = proxies; cur != NULL; cur = cur->next)
             gtk_widget_set_tooltip_text (GTK_WIDGET (cur->data), "");
     }
+#endif
+    g_simple_action_set_enabled (priv->back_action, (priv->back_cur->next && priv->back_cur->next->data));
 
+#if 0
     action = gtk_action_group_get_action (priv->action_group, "YelpViewGoForward");
     gtk_action_set_sensitive (action, FALSE);
     proxies = gtk_action_get_proxies (action);
@@ -1987,6 +2059,8 @@ uri_resolved (YelpUri  *uri,
         for (cur = proxies; cur != NULL; cur = cur->next)
             gtk_widget_set_tooltip_text (GTK_WIDGET (cur->data), "");
     }
+#endif
+    g_simple_action_set_enabled (priv->forward_action, (priv->back_cur->prev && priv->back_cur->prev->data));
 
     spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
                                          "yelp-uri");
@@ -2081,6 +2155,7 @@ document_callback (YelpDocument       *document,
             back->desc = g_strdup (priv->page_desc);
         }
 
+#if 0
         prev_id = yelp_document_get_prev_id (document, priv->page_id);
         action = gtk_action_group_get_action (priv->action_group, "YelpViewGoPrevious");
         gtk_action_set_sensitive (action, prev_id != NULL);
@@ -2090,6 +2165,7 @@ document_callback (YelpDocument       *document,
         action = gtk_action_group_get_action (priv->action_group, "YelpViewGoNext");
         gtk_action_set_sensitive (action, next_id != NULL);
         g_free (next_id);
+#endif
 
         spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
                                              "root-title");
diff --git a/libyelp/yelp-view.h b/libyelp/yelp-view.h
index 53fe511..a0c1995 100644
--- a/libyelp/yelp-view.h
+++ b/libyelp/yelp-view.h
@@ -72,7 +72,18 @@ void               yelp_view_load_document        (YelpView                *view
                                                    YelpUri                 *uri,
                                                    YelpDocument            *document);
 YelpDocument *     yelp_view_get_document         (YelpView                *view);
+
+#if 0
 GtkActionGroup *   yelp_view_get_action_group     (YelpView                *view);
+#endif
+
+void               yelp_view_register_actions     (YelpView                *view,
+                                                   GActionMap              *map);
+
+#if 0
+GAction *          yelp_view_get_back_action      (YelpView                *view);
+GAction *          yelp_view_get_forward_action   (YelpView                *view);
+#endif
 
 void               yelp_view_add_link_action      (YelpView                *view,
                                                    GtkAction               *action,
diff --git a/src/yelp-application.c b/src/yelp-application.c
index 2ac1f99..0b810cc 100644
--- a/src/yelp-application.c
+++ b/src/yelp-application.c
@@ -141,6 +141,22 @@ yelp_application_init (YelpApplication *app)
     priv->docsettings = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                (GDestroyNotify) g_free,
                                                (GDestroyNotify) g_object_unref);
+    gtk_application_add_accelerator (GTK_APPLICATION (app),
+                                     "<Control>f",
+                                     "win.find",
+                                     NULL);
+    gtk_application_add_accelerator (GTK_APPLICATION (app),
+                                     "<Control>s",
+                                     "win.search",
+                                     NULL);
+    gtk_application_add_accelerator (GTK_APPLICATION (app),
+                                     "<Alt>Left",
+                                     "win.yelp-view-go-back",
+                                     NULL);
+    gtk_application_add_accelerator (GTK_APPLICATION (app),
+                                     "<Alt>Right",
+                                     "win.yelp-view-go-forward",
+                                     NULL);
 }
 
 static void
diff --git a/src/yelp-window.c b/src/yelp-window.c
index d36c8fe..9ce10da 100644
--- a/src/yelp-window.c
+++ b/src/yelp-window.c
@@ -26,9 +26,10 @@
 
 #include <gdk/gdkkeysyms.h>
 #include <glib/gi18n.h>
+#include <gio/gio.h>
 #include <gtk/gtk.h>
 
-#include "yelp-location-entry.h"
+#include "yelp-search-entry.h"
 #include "yelp-settings.h"
 #include "yelp-uri.h"
 #include "yelp-view.h"
@@ -50,8 +51,7 @@ static void          yelp_window_set_property     (GObject            *object,
                                                    GParamSpec         *pspec);
 
 static void          window_construct             (YelpWindow         *window);
-static void          window_new                   (GtkAction          *action,
-                                                   YelpWindow         *window);
+
 static gboolean      window_map_event             (YelpWindow         *window,
                                                    GdkEvent           *event,
                                                    gpointer            user_data);
@@ -67,19 +67,18 @@ static void          window_drag_received         (YelpWindow         *window,
                                                    guint               time,
                                                    gpointer            userdata);
 static gboolean      window_resize_signal         (YelpWindow         *window);
-static void          window_close                 (GtkAction          *action,
-                                                   YelpWindow         *window);
-static void          window_go_all                (GtkAction          *action,
-                                                   YelpWindow         *window);
-static void          window_add_bookmark          (GtkAction          *action,
-                                                   YelpWindow         *window);
-static void          window_remove_bookmark       (GtkAction          *action,
+
+static void          bookmark_activated           (GtkListBox         *box,
+                                                   GtkListBoxRow      *row,
                                                    YelpWindow         *window);
-static void          window_load_bookmark         (GtkAction          *action,
+static void          bookmark_removed             (GtkButton          *button,
                                                    YelpWindow         *window);
-static void          window_find_in_page          (GtkAction          *action,
+static void          bookmark_added               (GtkButton          *button,
                                                    YelpWindow         *window);
-static void          window_start_search          (GtkAction          *action,
+
+
+/* FIXME: all below */
+static void          window_go_all                (GtkAction          *action,
                                                    YelpWindow         *window);
 static void          window_open_location         (GtkAction          *action,
                                                    YelpWindow         *window);
@@ -95,23 +94,28 @@ static void          app_bookmarks_changed        (YelpApplication    *app,
                                                    YelpWindow         *window);
 static void          window_set_bookmarks         (YelpWindow         *window,
                                                    const gchar        *doc_uri);
-static void          window_set_bookmark_action   (YelpWindow         *window);
-static gboolean      find_entry_focus_out         (GtkEntry           *entry,
-                                                   GdkEventFocus      *event,
-                                                   YelpWindow         *window);
+static void          window_set_bookmark_buttons  (YelpWindow         *window);
+
+static void          action_new_window            (GSimpleAction      *action,
+                                                   GVariant           *parameter,
+                                                   gpointer           userdata);
+static void          action_print                 (GSimpleAction      *action,
+                                                   GVariant           *parameter,
+                                                   gpointer            userdata);
+static void          action_search                (GSimpleAction      *action,
+                                                   GVariant           *parameter,
+                                                   gpointer            userdata);
+static void          action_find                  (GSimpleAction      *action,
+                                                   GVariant           *parameter,
+                                                   gpointer            userdata);
+
+
 static gboolean      find_entry_key_press         (GtkEntry           *entry,
                                                    GdkEventKey        *event,
                                                    YelpWindow         *window);
 static void          find_entry_changed           (GtkEntry           *entry,
                                                    YelpWindow         *window);
 
-static gboolean      entry_focus_in               (GtkEntry           *entry,
-                                                   GdkEventFocus      *event,
-                                                   YelpWindow         *window);
-static gboolean      entry_focus_out              (YelpLocationEntry  *entry,
-                                                   GdkEventFocus      *event,
-                                                   YelpWindow         *window);
-
 static void          view_new_window              (YelpView           *view,
                                                    YelpUri            *uri,
                                                    YelpWindow         *window);
@@ -147,9 +151,10 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
-G_DEFINE_TYPE (YelpWindow, yelp_window, GTK_TYPE_WINDOW);
+G_DEFINE_TYPE (YelpWindow, yelp_window, GTK_TYPE_APPLICATION_WINDOW);
 #define GET_PRIV(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_WINDOW, YelpWindowPrivate))
 
+#if 0
 static const gchar *YELP_UI =
     "<ui>"
     "<menubar>"
@@ -187,88 +192,51 @@ static const gchar *YELP_UI =
     "<accelerator action='Search'/>"
     "<accelerator action='OpenLocation'/>"
     "</ui>";
+#endif
 
 typedef struct _YelpWindowPrivate YelpWindowPrivate;
 struct _YelpWindowPrivate {
-    GtkListStore *history;
-    GtkUIManager *ui_manager;
-    GtkActionGroup *action_group;
     YelpApplication *application;
 
     gulong        bookmarks_changed;
     gulong        read_later_changed;
 
     /* no refs on these, owned by containers */
-    YelpView *view;
+    GtkWidget *header;
     GtkWidget *vbox_view;
     GtkWidget *vbox_full;
-    GtkWidget *hbox;
-    YelpLocationEntry *entry;
-    GtkWidget *hidden_entry;
+    GtkWidget *search_bar;
+    GtkWidget *search_entry;
+    GtkWidget *find_bar;
     GtkWidget *find_entry;
-    GtkWidget *find_label;
+    GtkWidget *bookmark_menu;
+    GtkWidget *bookmark_list;
+    GtkWidget *bookmark_add;
+    GtkWidget *bookmark_remove;
+    YelpView *view;
+
+    GtkWidget *hidden_entry;
     GtkWidget *read_later_vbox;
 
     /* refs because we dynamically add & remove */
-    GtkWidget *find_bar;
     GtkWidget *align_location;
     GtkWidget *align_hidden;
     GtkWidget *read_later;
 
     gchar *doc_uri;
 
-    GtkActionGroup *bookmark_actions;
-    guint bookmarks_merge_id;
-
     guint resize_signal;
     gint width;
     gint height;
 
-    guint entry_color_animate;
-    gfloat entry_color_step;
-
     gboolean configured;
 };
 
-static const GtkActionEntry entries[] = {
-    { "PageMenu",      NULL, N_("_Page")      },
-    { "ViewMenu",      NULL, N_("_View")      },
-    { "GoMenu",        NULL, N_("_Go")        },
-    { "BookmarksMenu", NULL, N_("_Bookmarks")        },
-
-    { "NewWindow", NULL,
-      N_("_New Window"),
-      "<Control>N",
-      NULL,
-      G_CALLBACK (window_new) },
-    { "CloseWindow", NULL,
-      N_("_Close"),
-      "<Control>W",
-      NULL,
-      G_CALLBACK (window_close) },
+static const GtkActionEntry gtkentries[] = {
     { "GoAll", NULL,
       N_("_All Documents"),
       NULL, NULL,
       G_CALLBACK (window_go_all) },
-    { "AddBookmark", NULL,
-      N_("_Add Bookmark"),
-      "<Control>D",
-      NULL,
-      G_CALLBACK (window_add_bookmark) },
-    { "RemoveBookmark", NULL,
-      N_("_Remove Bookmark"),
-      NULL, NULL,
-      G_CALLBACK (window_remove_bookmark) },
-    { "Find", NULL,
-      N_("Find in Page..."),
-      "<Control>F",
-      NULL,
-      G_CALLBACK (window_find_in_page) },
-    { "Search", NULL,
-      N_("Search..."),
-      "<Control>S",
-      NULL,
-      G_CALLBACK (window_start_search) },
     { "OpenLocation", NULL,
       N_("Open Location"),
       "<Control>L",
@@ -319,21 +287,6 @@ yelp_window_dispose (GObject *object)
 {
     YelpWindowPrivate *priv = GET_PRIV (object);
 
-    if (priv->history) {
-        g_object_unref (priv->history);
-        priv->history = NULL;
-    }
-
-    if (priv->action_group) {
-        g_object_unref (priv->action_group);
-        priv->action_group = NULL;
-    }
-
-    if (priv->bookmark_actions) {
-        g_object_unref (priv->bookmark_actions);
-        priv->bookmark_actions = NULL;
-    }
-
     if (priv->read_later_changed) {
         g_source_remove (priv->read_later_changed);
         priv->read_later_changed = 0;
@@ -354,16 +307,6 @@ yelp_window_dispose (GObject *object)
         priv->align_hidden = NULL;
     }
 
-    if (priv->find_bar) {
-        g_object_unref (priv->find_bar);
-        priv->find_bar = NULL;
-    }
-
-    if (priv->entry_color_animate != 0) {
-        g_source_remove (priv->entry_color_animate);
-        priv->entry_color_animate = 0;
-    }
-
     G_OBJECT_CLASS (yelp_window_parent_class)->dispose (object);
 }
 
@@ -416,14 +359,24 @@ window_construct (YelpWindow *window)
     GtkWidget *scroll;
     GtkActionGroup *view_actions;
     GtkAction *action;
-    GtkWidget *vbox, *button, *label;
+    GtkWidget *box, *button, *sw;
     gchar *color, *text;
     YelpWindowPrivate *priv = GET_PRIV (window);
 
-    gtk_window_set_icon_name (GTK_WINDOW (window), "help-browser");
+    const GActionEntry entries[] = {
+        { "new-window", action_new_window, NULL, NULL, NULL },
+        { "print",      action_print,      NULL, NULL, NULL },
+        { "search",     action_search,     NULL, NULL, NULL },
+        { "find",       action_find,       NULL, NULL, NULL },
+    };
 
+    gtk_window_set_icon_name (GTK_WINDOW (window), "help-browser");
     priv->view = (YelpView *) yelp_view_new ();
 
+    g_action_map_add_action_entries (G_ACTION_MAP (window),
+                                     entries, G_N_ELEMENTS (entries), window);
+    yelp_view_register_actions (priv->view, G_ACTION_MAP (window));
+
     action = gtk_action_new ("ReadLinkLater", _("Read Link _Later"), NULL, NULL);
     g_signal_connect (action, "activate", G_CALLBACK (window_read_later), window);
     yelp_view_add_link_action (priv->view, action,
@@ -436,110 +389,139 @@ window_construct (YelpWindow *window)
     priv->vbox_full = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
     gtk_container_add (GTK_CONTAINER (window), priv->vbox_full);
 
+    priv->header = gtk_header_bar_new ();
+    gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (priv->header), TRUE);
+    gtk_window_set_titlebar (GTK_WINDOW (window), priv->header);
+
+    /** Back/Forward **/
+    box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+    gtk_style_context_add_class (gtk_widget_get_style_context (box), "linked");
+    gtk_header_bar_pack_start (GTK_HEADER_BAR (priv->header), box);
+
+    button = gtk_button_new_from_icon_name ("go-previous-symbolic", GTK_ICON_SIZE_MENU);
+    gtk_style_context_add_class (gtk_widget_get_style_context (button), "image-button");
+    gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+    g_object_set (button, "action-name", "win.yelp-view-go-back", NULL);
+
+    button = gtk_button_new_from_icon_name ("go-next-symbolic", GTK_ICON_SIZE_MENU);
+    gtk_style_context_add_class (gtk_widget_get_style_context (button), "image-button");
+    gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+    g_object_set (button, "action-name", "win.yelp-view-go-forward", NULL);
+
+    /** Menu **/
+    button = gtk_menu_button_new ();
+    gtk_style_context_add_class (gtk_widget_get_style_context (button), "image-button");
+    gtk_button_set_image (GTK_BUTTON (button),
+                          gtk_image_new_from_icon_name ("emblem-system-symbolic",
+                                                        GTK_ICON_SIZE_MENU));
+    gtk_header_bar_pack_end (GTK_HEADER_BAR (priv->header), button);
+
+    /** Search **/
     priv->vbox_view = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
     gtk_box_pack_start (GTK_BOX (priv->vbox_full), priv->vbox_view, TRUE, TRUE, 0);
 
-    priv->action_group = gtk_action_group_new ("YelpWindowActions");
-    gtk_action_group_set_translation_domain (priv->action_group, GETTEXT_PACKAGE);
-    gtk_action_group_add_actions (priv->action_group,
-                                 entries, G_N_ELEMENTS (entries),
-                                 window);
-
+    priv->search_bar = gtk_search_bar_new ();
+    gtk_box_pack_start (GTK_BOX (priv->vbox_view), priv->search_bar, FALSE, FALSE, 0);
+    priv->search_entry = yelp_search_entry_new (priv->view,
+                                                YELP_BOOKMARKS (priv->application));
+    g_object_set (priv->search_entry, "width-request", 400, NULL);
+    gtk_container_add (GTK_CONTAINER (priv->search_bar), priv->search_entry);
+    button = gtk_toggle_button_new ();
+    gtk_style_context_add_class (gtk_widget_get_style_context (button), "image-button");
+    gtk_button_set_image (GTK_BUTTON (button),
+                          gtk_image_new_from_icon_name ("edit-find-symbolic",
+                                                        GTK_ICON_SIZE_MENU));
+    g_object_bind_property (button, "active",
+                            priv->search_bar, "search-mode-enabled",
+                            G_BINDING_BIDIRECTIONAL);
+    gtk_header_bar_pack_end (GTK_HEADER_BAR (priv->header), button);
+
+    /** Bookmarks **/
+    button = gtk_menu_button_new ();
+    gtk_style_context_add_class (gtk_widget_get_style_context (button), "image-button");
+    gtk_button_set_image (GTK_BUTTON (button),
+                          gtk_image_new_from_icon_name ("yelp-bookmark-remove-symbolic",
+                                                        GTK_ICON_SIZE_MENU));
+    gtk_header_bar_pack_end (GTK_HEADER_BAR (priv->header), button);
+
+    priv->bookmark_menu = gtk_popover_new (button);
+    g_object_set (priv->bookmark_menu, "border-width", 12, NULL);
+    gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), priv->bookmark_menu);
+
+    box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+    gtk_container_add (GTK_CONTAINER (priv->bookmark_menu), box);
+    sw = gtk_scrolled_window_new (NULL, NULL);
+    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+                                    GTK_POLICY_AUTOMATIC,
+                                    GTK_POLICY_AUTOMATIC);
+    gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
+    priv->bookmark_list = gtk_list_box_new ();
+    g_object_set (priv->bookmark_list, "selection-mode", GTK_SELECTION_NONE, NULL);
+    g_signal_connect (priv->bookmark_list, "row-activated",
+                      G_CALLBACK (bookmark_activated), window);
+    gtk_container_add (GTK_CONTAINER (box), priv->bookmark_list);
+
+    priv->bookmark_add = gtk_button_new_with_label (_("Add Bookmark"));
+    g_signal_connect (priv->bookmark_add, "clicked",
+                      G_CALLBACK (bookmark_added), window);
+    gtk_box_pack_end (GTK_BOX (box), priv->bookmark_add, FALSE, FALSE, 0);
+    gtk_widget_show_all (box);
+
+    priv->bookmark_remove = gtk_button_new_with_label (_("Remove Bookmark"));
+    g_signal_connect (priv->bookmark_remove, "clicked",
+                      G_CALLBACK (bookmark_removed), window);
+    gtk_box_pack_end (GTK_BOX (box), priv->bookmark_remove, FALSE, FALSE, 0);
+    gtk_widget_show_all (box);
+
+#if 0
     priv->bookmark_actions = gtk_action_group_new ("BookmarkActions");
     gtk_action_group_set_translate_func (priv->bookmark_actions, NULL, NULL, NULL);
+#endif
 
-    priv->ui_manager = gtk_ui_manager_new ();
-    gtk_ui_manager_insert_action_group (priv->ui_manager, priv->action_group, 0);
-    gtk_ui_manager_insert_action_group (priv->ui_manager, priv->bookmark_actions, 1);
-    gtk_ui_manager_insert_action_group (priv->ui_manager,
-                                        yelp_application_get_action_group (priv->application),
-                                        2);
-    view_actions = yelp_view_get_action_group (priv->view);
-    gtk_ui_manager_insert_action_group (priv->ui_manager, view_actions, 3);
-    gtk_window_add_accel_group (GTK_WINDOW (window),
-                                gtk_ui_manager_get_accel_group (priv->ui_manager));
-    gtk_ui_manager_add_ui_from_string (priv->ui_manager, YELP_UI, -1, NULL);
-    gtk_box_pack_start (GTK_BOX (priv->vbox_view),
-                        gtk_ui_manager_get_widget (priv->ui_manager, "/ui/menubar"),
-                        FALSE, FALSE, 0);
-
-    priv->bookmarks_merge_id = gtk_ui_manager_new_merge_id (priv->ui_manager);
     priv->bookmarks_changed =
         g_signal_connect (priv->application, "bookmarks-changed",
                           G_CALLBACK (app_bookmarks_changed), window);
 
-    priv->hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
-    g_object_set (priv->hbox, "border-width", 2, NULL);
-    gtk_box_pack_start (GTK_BOX (priv->vbox_view), priv->hbox, FALSE, FALSE, 0);
+    /** Find **/
+    priv->find_bar = gtk_search_bar_new ();
+    gtk_search_bar_set_show_close_button (GTK_SEARCH_BAR (priv->find_bar), TRUE);
+    gtk_box_pack_start (GTK_BOX (priv->vbox_view), priv->find_bar, FALSE, FALSE, 0);
+    box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+    g_object_set (box, "width-request", 300, NULL);
+    gtk_style_context_add_class (gtk_widget_get_style_context (box), "linked");
+    gtk_container_add (GTK_CONTAINER (priv->find_bar), box);
+
+    priv->find_entry = gtk_search_entry_new ();
+    gtk_box_pack_start (GTK_BOX (box), priv->find_entry, TRUE, TRUE, 0);
+    gtk_search_bar_connect_entry (GTK_SEARCH_BAR (priv->find_bar), GTK_ENTRY (priv->find_entry));
+    g_signal_connect (priv->find_entry, "changed",
+                      G_CALLBACK (find_entry_changed), window);
+    g_signal_connect (priv->find_entry, "key-press-event",
+                      G_CALLBACK (find_entry_key_press), window);
 
-    action = gtk_action_group_get_action (view_actions, "YelpViewGoBack");
-    button = gtk_action_create_tool_item (action);
-    gtk_box_pack_start (GTK_BOX (priv->hbox),
-                        button,
-                        FALSE, FALSE, 0);
-    action = gtk_action_group_get_action (view_actions, "YelpViewGoForward");
-    button = gtk_action_create_tool_item (action);
-    gtk_box_pack_start (GTK_BOX (priv->hbox),
-                        button,
-                        FALSE, FALSE, 0);
+    button = gtk_button_new_from_icon_name ("go-up-symbolic", GTK_ICON_SIZE_MENU);
+    gtk_style_context_add_class (gtk_widget_get_style_context (button), "raised");
+    gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+    /* FIXME: signal_connect */
 
-    priv->entry = (YelpLocationEntry *) yelp_location_entry_new (priv->view,
-                                                                 YELP_BOOKMARKS (priv->application));
-    g_signal_connect (gtk_bin_get_child (GTK_BIN (priv->entry)), "focus-in-event",
-                      G_CALLBACK (entry_focus_in), window);
-    g_signal_connect (priv->entry, "focus-out-event",
-                      G_CALLBACK (entry_focus_out), window);
-
-    priv->align_location = g_object_ref_sink (gtk_alignment_new (0.0, 0.5, 1.0, 0.0));
-    gtk_box_pack_start (GTK_BOX (priv->hbox),
-                        GTK_WIDGET (priv->align_location),
-                        TRUE, TRUE, 0);
-    gtk_container_add (GTK_CONTAINER (priv->align_location), GTK_WIDGET (priv->entry));
-
-    priv->hidden_entry = gtk_entry_new ();
-    priv->align_hidden = g_object_ref_sink (gtk_alignment_new (0.0, 0.5, 1.0, 0.0));
-    gtk_container_add (GTK_CONTAINER (priv->align_hidden), GTK_WIDGET (priv->hidden_entry));
-
-    g_signal_connect (priv->hidden_entry, "activate",
-                      G_CALLBACK (hidden_entry_activate), window);
-    g_signal_connect_swapped (priv->hidden_entry, "focus-out-event",
-                              G_CALLBACK (hidden_entry_hide), window);
-    g_signal_connect (priv->hidden_entry, "key-press-event",
-                      G_CALLBACK (hidden_key_press), window);
+    button = gtk_button_new_from_icon_name ("go-down-symbolic", GTK_ICON_SIZE_MENU);
+    gtk_style_context_add_class (gtk_widget_get_style_context (button), "raised");
+    gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+    /* FIXME: signal_connect */
+
+    gtk_widget_show_all (priv->find_bar);
 
+    /** View **/
     scroll = gtk_scrolled_window_new (NULL, NULL);
+    g_object_set (scroll, "width-request", 420, NULL);
     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
                                     GTK_POLICY_AUTOMATIC,
                                     GTK_POLICY_AUTOMATIC);
-    gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll),
-                                         GTK_SHADOW_IN);
     gtk_box_pack_start (GTK_BOX (priv->vbox_view), scroll, TRUE, TRUE, 0);
 
-    priv->find_bar = g_object_ref_sink (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6));
-    g_object_set (priv->find_bar,
-                  "border-width", 2,
-                  "margin-right", 16,
-                  NULL);
-
-    label = gtk_label_new (_("Find:"));
-    gtk_box_pack_start (GTK_BOX (priv->find_bar), label, FALSE, FALSE, 6);
-
-    priv->find_entry = gtk_entry_new ();
-    g_signal_connect (priv->find_entry, "changed",
-                      G_CALLBACK (find_entry_changed), window);
-    g_signal_connect (priv->find_entry, "key-press-event",
-                      G_CALLBACK (find_entry_key_press), window);
-    g_signal_connect (priv->find_entry, "focus-out-event",
-                      G_CALLBACK (find_entry_focus_out), window);
-    g_object_set (priv->find_entry, "width-request", 300, NULL);
-    gtk_box_pack_start (GTK_BOX (priv->find_bar), priv->find_entry, FALSE, FALSE, 0);
-
-    priv->find_label = gtk_label_new ("");
-    g_object_set (priv->find_label, "xalign", 0.0, NULL);
-    gtk_box_pack_start (GTK_BOX (priv->find_bar), priv->find_label, FALSE, FALSE, 0);
-
+    /** Read Later **/
     priv->read_later = g_object_ref_sink (gtk_info_bar_new ());
-    vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+    box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
     color = yelp_settings_get_color (yelp_settings_get_default (),
                                      YELP_SETTINGS_COLOR_TEXT_LIGHT);
     text = g_markup_printf_escaped ("<span weight='bold' color='%s'>%s</span>",
@@ -548,19 +530,19 @@ window_construct (YelpWindow *window)
     g_object_set (button, "use-markup", TRUE, "xalign", 0.0, NULL);
     g_free (color);
     g_free (text);
-    gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+    gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
     gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (priv->read_later))),
-                        vbox,
+                        box,
                         FALSE, FALSE, 0);
     priv->read_later_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
-    gtk_box_pack_start (GTK_BOX (vbox), priv->read_later_vbox, FALSE, FALSE, 0);
+    gtk_box_pack_start (GTK_BOX (box), priv->read_later_vbox, FALSE, FALSE, 0);
 
     g_signal_connect (priv->view, "new-view-requested", G_CALLBACK (view_new_window), window);
     g_signal_connect (priv->view, "loaded", G_CALLBACK (view_loaded), window);
     g_signal_connect (priv->view, "notify::yelp-uri", G_CALLBACK (view_uri_selected), window);
     g_signal_connect_swapped (priv->view, "notify::page-id",
-                              G_CALLBACK (window_set_bookmark_action), window);
-    window_set_bookmark_action (window);
+                              G_CALLBACK (window_set_bookmark_buttons), window);
+    window_set_bookmark_buttons (window);
     g_signal_connect (priv->view, "notify::root-title", G_CALLBACK (view_root_title), window);
     gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (priv->view));
     gtk_widget_grab_focus (GTK_WIDGET (priv->view));
@@ -617,10 +599,13 @@ yelp_window_get_geometry (YelpWindow  *window,
 /******************************************************************************/
 
 static void
-window_new (GtkAction *action, YelpWindow *window)
+action_new_window (GSimpleAction *action,
+                   GVariant      *parameter,
+                   gpointer       userdata)
 {
     YelpUri *yuri;
     gchar *uri = NULL;
+    YelpWindow *window = YELP_WINDOW (userdata);
     YelpWindowPrivate *priv = GET_PRIV (window);
 
     g_object_get (priv->view, "yelp-uri", &yuri, NULL);
@@ -633,6 +618,40 @@ window_new (GtkAction *action, YelpWindow *window)
 }
 
 static void
+action_print (GSimpleAction *action,
+              GVariant      *parameter,
+              gpointer       userdata)
+{
+    /* FIXME */
+}
+
+static void
+action_search (GSimpleAction *action,
+               GVariant      *parameter,
+               gpointer       userdata)
+{
+    YelpWindowPrivate *priv = GET_PRIV (userdata);
+
+    gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (priv->find_bar), FALSE);
+    gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (priv->search_bar), TRUE);
+    gtk_widget_grab_focus (priv->search_entry);
+}
+
+static void
+action_find (GSimpleAction *action,
+             GVariant      *parameter,
+             gpointer       userdata)
+{
+    YelpWindowPrivate *priv = GET_PRIV (userdata);
+
+    gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (priv->search_bar), FALSE);
+    gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (priv->find_bar), TRUE);
+    gtk_widget_grab_focus (priv->find_entry);
+}
+
+/******************************************************************************/
+
+static void
 window_drag_received (YelpWindow         *window,
                       GdkDragContext     *context,
                       gint                x,
@@ -704,83 +723,90 @@ window_resize_signal (YelpWindow *window)
 }
 
 static void
-window_close (GtkAction *action, YelpWindow *window)
+window_go_all (GtkAction  *action,
+               YelpWindow *window)
 {
-    gboolean ret;
-    g_signal_emit_by_name (window, "delete-event", NULL, &ret);
-    gtk_widget_destroy (GTK_WIDGET (window));
+    YelpWindowPrivate *priv = GET_PRIV (window);
+    yelp_view_load (priv->view, "help-list:");
 }
 
 static void
-window_go_all (GtkAction  *action,
-               YelpWindow *window)
+bookmark_activated (GtkListBox    *box,
+                    GtkListBoxRow *row,
+                    YelpWindow    *window)
 {
+    YelpUri *base, *uri;
+    gchar *xref;
     YelpWindowPrivate *priv = GET_PRIV (window);
-    yelp_view_load (priv->view, "help-list:");
+
+    gtk_widget_hide (priv->bookmark_menu);
+
+    xref = g_strconcat ("xref:",
+                        (gchar *) g_object_get_data (G_OBJECT (row), "page-id"),
+                        NULL);
+    g_object_get (priv->view, "yelp-uri", &base, NULL);
+    uri = yelp_uri_new_relative (base, xref);
+
+    yelp_view_load_uri (priv->view, uri);
+
+    g_object_unref (base);
+    g_object_unref (uri);
+    g_free (xref);
 }
 
 static void
-window_add_bookmark (GtkAction  *action,
-                     YelpWindow *window)
+bookmark_removed (GtkButton  *button,
+                  YelpWindow *window)
 {
     YelpUri *uri;
-    gchar *doc_uri, *page_id, *icon, *title;
+    gchar *doc_uri;
+    gchar *page_id = NULL;
     YelpWindowPrivate *priv = GET_PRIV (window);
 
-    g_object_get (priv->view,
-                  "yelp-uri", &uri,
-                  "page-id", &page_id,
-                  "page-icon", &icon,
-                  "page-title", &title,
-                  NULL);
+    g_object_get (priv->view, "yelp-uri", &uri, NULL);
     doc_uri = yelp_uri_get_document_uri (uri);
-    yelp_application_add_bookmark (YELP_BOOKMARKS (priv->application),
-                                   doc_uri, page_id, icon, title);
+
+    /* The 'Remove Bookmark' button removes a bookmark for the current page.
+       The buttons next to each bookmark have page_id attached to them.
+     */
+    if ((gpointer) button == (gpointer) priv->bookmark_remove)
+        g_object_get (priv->view,
+                      "page-id", &page_id,
+                      NULL);
+
+    yelp_application_remove_bookmark (YELP_BOOKMARKS (priv->application),
+                                      doc_uri,
+                                      page_id ? page_id :
+                                      g_object_get_data (G_OBJECT (button), "page-id"));
+    if (page_id)
+        g_free (page_id);
     g_free (doc_uri);
-    g_free (page_id);
-    g_free (icon);
-    g_free (title);
     g_object_unref (uri);
 }
 
 static void
-window_remove_bookmark (GtkAction  *action,
-                        YelpWindow *window)
+bookmark_added (GtkButton  *button,
+                YelpWindow *window)
 {
     YelpUri *uri;
-    gchar *doc_uri, *page_id;
+    gchar *doc_uri, *page_id, *icon, *title;
     YelpWindowPrivate *priv = GET_PRIV (window);
 
     g_object_get (priv->view,
                   "yelp-uri", &uri,
                   "page-id", &page_id,
+                  "page-icon", &icon,
+                  "page-title", &title,
                   NULL);
     doc_uri = yelp_uri_get_document_uri (uri);
-    yelp_application_remove_bookmark (YELP_BOOKMARKS (priv->application),
-                                      doc_uri, page_id);
+    yelp_application_add_bookmark (YELP_BOOKMARKS (priv->application),
+                                   doc_uri, page_id, icon, title);
     g_free (doc_uri);
     g_free (page_id);
+    g_free (icon);
+    g_free (title);
     g_object_unref (uri);
 }
-static void
-window_load_bookmark (GtkAction  *action,
-                      YelpWindow *window)
-{
-    YelpUri *base, *uri;
-    gchar *xref;
-    YelpWindowPrivate *priv = GET_PRIV (window);
-
-    /* Bookmark action names are prefixed with 'LoadBookmark-' */
-    xref = g_strconcat ("xref:", gtk_action_get_name (action) + 13, NULL);
-    g_object_get (priv->view, "yelp-uri", &base, NULL);
-    uri = yelp_uri_new_relative (base, xref);
-
-    yelp_view_load_uri (priv->view, uri);
-
-    g_object_unref (base);
-    g_object_unref (uri);
-    g_free (xref);
-}
 
 static void
 app_bookmarks_changed (YelpApplication *app,
@@ -834,11 +860,17 @@ window_set_bookmarks (YelpWindow  *window,
     GVariantIter *iter;
     gchar *page_id, *icon, *title;
     YelpWindowPrivate *priv = GET_PRIV (window);
+    GList *children, *cur;
     GSList *entries = NULL;
 
-    window_set_bookmark_action (window);
+    window_set_bookmark_buttons (window);
 
-    gtk_ui_manager_remove_ui (priv->ui_manager, priv->bookmarks_merge_id);
+    children = gtk_container_get_children (GTK_CONTAINER (priv->bookmark_list));
+    for (cur = children ; cur != NULL; cur = cur->next) {
+        gtk_container_remove (GTK_CONTAINER (priv->bookmark_list),
+                              GTK_WIDGET (cur->data));
+    }
+    g_list_free (children);
 
     value = yelp_application_get_bookmarks (priv->application, doc_uri);
     g_variant_get (value, "a(sss)", &iter);
@@ -850,41 +882,29 @@ window_set_bookmarks (YelpWindow  *window,
         entries = g_slist_insert_sorted (entries, entry, (GCompareFunc) entry_compare);
     }
     for ( ; entries != NULL; entries = g_slist_delete_link (entries, entries)) {
-        GSList *cur;
-        GtkAction *bookmark;
+        GtkWidget *row, *box, *button;
         YelpMenuEntry *entry = (YelpMenuEntry *) entries->data;
-        gchar *action_id = g_strconcat ("LoadBookmark-", entry->page_id, NULL);
-
-        bookmark = gtk_action_group_get_action (priv->bookmark_actions, action_id);
-        if (bookmark) {
-            /* The action might have been set by a different document using
-             * the same page ID. We can just reuse the action, since it's
-             * just a page ID relative to the current URI, but we need to
-             * reset the title and icon.
-             */
-            g_object_set (bookmark,
-                          "label", entry->title,
-                          "icon-name", entry->icon,
-                          NULL);
-        } else {
-            bookmark = gtk_action_new (action_id, entry->title, NULL, NULL);
-            g_signal_connect (bookmark, "activate",
-                              G_CALLBACK (window_load_bookmark), window);
-            gtk_action_set_icon_name (bookmark, entry->icon);
-            gtk_action_group_add_action (priv->bookmark_actions, bookmark);
-        }
-        gtk_ui_manager_add_ui (priv->ui_manager,
-                               priv->bookmarks_merge_id,
-                               "ui/menubar/BookmarksMenu/Bookmarks",
-                               action_id, action_id,
-                               GTK_UI_MANAGER_MENUITEM,
-                               FALSE);
-        gtk_ui_manager_ensure_update (priv->ui_manager);
-        for (cur = gtk_action_get_proxies (bookmark); cur != NULL; cur = cur->next) {
-            if (GTK_IS_IMAGE_MENU_ITEM (cur->data))
-                g_object_set (cur->data, "always-show-image", TRUE, NULL);
-        }
-        g_free (action_id);
+
+        row = gtk_list_box_row_new ();
+        g_object_set_data_full (G_OBJECT (row), "page-id",
+                                g_strdup (entry->page_id), (GDestroyNotify) g_free);
+        box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+        gtk_container_add (GTK_CONTAINER (row), box);
+        button = gtk_label_new (entry->title);
+        g_object_set (button, "halign", GTK_ALIGN_START, NULL);
+        gtk_box_pack_start (GTK_BOX (box), button, TRUE, TRUE, 0);
+        button = gtk_button_new_from_icon_name ("edit-delete-symbolic", GTK_ICON_SIZE_MENU);
+        g_object_set (button,
+                      "relief", GTK_RELIEF_NONE,
+                      "focus-on-click", FALSE,
+                      NULL);
+        g_object_set_data_full (G_OBJECT (button), "page-id",
+                                g_strdup (entry->page_id), (GDestroyNotify) g_free);
+        g_signal_connect (button, "clicked", G_CALLBACK (bookmark_removed), window);
+        gtk_box_pack_end (GTK_BOX (box), button, FALSE, FALSE, 0);
+        gtk_box_pack_end (GTK_BOX (box), gtk_separator_new (GTK_ORIENTATION_VERTICAL), FALSE, FALSE, 0);
+        gtk_widget_show_all (row);
+        gtk_container_add (GTK_CONTAINER (priv->bookmark_list), row);
         g_free (entry->icon);
         g_free (entry);
     }
@@ -894,31 +914,29 @@ window_set_bookmarks (YelpWindow  *window,
 }
 
 static void
-window_set_bookmark_action (YelpWindow *window)
+window_set_bookmark_buttons (YelpWindow *window)
 {
     YelpUri *uri = NULL;
     gchar *doc_uri = NULL, *page_id = NULL;
-    GtkAction *action_add, *action_del;
     gboolean bookmarked;
     YelpWindowPrivate *priv = GET_PRIV (window);
 
-    action_add = gtk_action_group_get_action (priv->action_group, "AddBookmark");
-    action_del = gtk_action_group_get_action (priv->action_group, "RemoveBookmark");
 
     g_object_get (priv->view,
                   "yelp-uri", &uri,
                   "page-id", &page_id,
                   NULL);
     if (page_id == NULL || uri == NULL) {
-        gtk_action_set_sensitive (action_add, FALSE);
-        gtk_action_set_sensitive (action_del, FALSE);
+        gtk_widget_hide (priv->bookmark_add);
+        gtk_widget_hide (priv->bookmark_remove);
         goto done;
     }
     doc_uri = yelp_uri_get_document_uri (uri);
     bookmarked = yelp_application_is_bookmarked (YELP_BOOKMARKS (priv->application),
                                                  doc_uri, page_id);
-    gtk_action_set_sensitive (action_add, !bookmarked);
-    gtk_action_set_sensitive (action_del, bookmarked);
+
+    gtk_widget_set_visible (priv->bookmark_add, !bookmarked);
+    gtk_widget_set_visible (priv->bookmark_remove, bookmarked);
 
   done:
     g_free (page_id);
@@ -928,28 +946,15 @@ window_set_bookmark_action (YelpWindow *window)
 }
 
 static void
-window_start_search (GtkAction *action, YelpWindow *window)
-{
-    YelpWindowPrivate *priv = GET_PRIV (window);
-
-    yelp_location_entry_start_search (priv->entry);
-}
-
-static void
 window_open_location (GtkAction *action, YelpWindow *window)
 {
+#if 0
     YelpUri *yuri = NULL;
     gchar *uri = NULL;
     GdkColor yellow;
     gchar *color;
     YelpWindowPrivate *priv = GET_PRIV (window);
 
-    gtk_container_remove (GTK_CONTAINER (priv->hbox),
-                          priv->align_location);
-    gtk_box_pack_start (GTK_BOX (priv->hbox),
-                        priv->align_hidden,
-                        TRUE, TRUE, 0);
-
     gtk_widget_show_all (priv->align_hidden);
     gtk_entry_set_text (GTK_ENTRY (priv->hidden_entry), "");
     gtk_widget_grab_focus (priv->hidden_entry);
@@ -978,6 +983,7 @@ window_open_location (GtkAction *action, YelpWindow *window)
             gtk_editable_select_region (GTK_EDITABLE (priv->hidden_entry), 5, -1);
         g_free (uri);
     }
+#endif
 }
 
 static void
@@ -1101,24 +1107,6 @@ app_read_later_changed (YelpApplication *app,
     }
 }
 
-static void
-window_find_in_page (GtkAction  *action,
-                     YelpWindow *window)
-{
-    YelpWindowPrivate *priv = GET_PRIV (window);
-
-    if (gtk_widget_get_parent (priv->find_bar) != NULL) {
-        gtk_widget_grab_focus (priv->find_entry);
-        return;
-    }
-
-    g_object_set (priv->find_entry, "width-request", 2 * priv->width / 3, NULL);
-
-    gtk_box_pack_end (GTK_BOX (priv->vbox_view), priv->find_bar, FALSE, FALSE, 0);
-    gtk_widget_show_all (priv->find_bar);
-    gtk_widget_grab_focus (priv->find_entry);
-}
-
 static gboolean
 find_entry_key_press (GtkEntry    *entry,
                       GdkEventKey *event,
@@ -1142,19 +1130,6 @@ find_entry_key_press (GtkEntry    *entry,
     return FALSE;
 }
 
-static gboolean
-find_entry_focus_out (GtkEntry      *entry,
-                      GdkEventFocus *event,
-                      YelpWindow    *window)
-{
-    YelpWindowPrivate *priv = GET_PRIV (window);
-    webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (priv->view));
-    webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (priv->view), FALSE);
-    if (gtk_widget_get_parent (priv->find_bar) != NULL)
-        gtk_container_remove (GTK_CONTAINER (priv->vbox_view), priv->find_bar); 
-    return FALSE;
-}
-
 static void
 find_entry_changed (GtkEntry   *entry,
                     YelpWindow *window)
@@ -1167,88 +1142,20 @@ find_entry_changed (GtkEntry   *entry,
 
     text = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
 
-    if (text[0] == '\0') {
-        gtk_label_set_text (GTK_LABEL (priv->find_label), "");
-        return;
-    }
-
     count = webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (priv->view),
                                                text, FALSE, 0);
     if (count > 0) {
-        gchar *label = g_strdup_printf (ngettext ("%i match", "%i matches", count), count);
         webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (priv->view), TRUE);
         webkit_web_view_search_text (WEBKIT_WEB_VIEW (priv->view),
                                      text, FALSE, TRUE, TRUE);
-        gtk_label_set_text (GTK_LABEL (priv->find_label), label);
-        g_free (label);
     }
     else {
         webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (priv->view), FALSE);
-        gtk_label_set_text (GTK_LABEL (priv->find_label), _("No matches"));
     }
 
     g_free (text);
 }
 
-static gboolean
-entry_color_animate (YelpWindow *window)
-{
-    gchar *color;
-    GdkColor yellow, base;
-    YelpWindowPrivate *priv = GET_PRIV (window);
-
-    color = yelp_settings_get_color (yelp_settings_get_default (),
-                                     YELP_SETTINGS_COLOR_YELLOW_BASE);
-    gdk_color_parse (color, &yellow);
-    g_free (color);
-
-    color = yelp_settings_get_color (yelp_settings_get_default (),
-                                     YELP_SETTINGS_COLOR_BASE);
-    gdk_color_parse (color, &base);
-    g_free (color);
-
-    yellow.red = priv->entry_color_step * yellow.red + (1.0 - priv->entry_color_step) * base.red;
-    yellow.green = priv->entry_color_step * yellow.green + (1.0 - priv->entry_color_step) * base.green;
-    yellow.blue = priv->entry_color_step * yellow.blue + (1.0 - priv->entry_color_step) * base.blue;
-
-    gtk_widget_modify_base (gtk_bin_get_child (GTK_BIN (priv->entry)), GTK_STATE_NORMAL, &yellow);
-
-    priv->entry_color_step -= 0.05;
-
-    if (priv->entry_color_step < 0.0) {
-        priv->entry_color_animate = 0;
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
-static gboolean
-entry_focus_in (GtkEntry           *entry,
-                GdkEventFocus      *event,
-                YelpWindow         *window)
-{
-    YelpWindowPrivate *priv = GET_PRIV (window);
-
-    if (priv->entry_color_animate != 0)
-        return FALSE;
-
-    priv->entry_color_step = 1.0;
-    priv->entry_color_animate = g_timeout_add (40, (GSourceFunc) entry_color_animate, window);
-
-    return FALSE;
-}
-
-static gboolean
-entry_focus_out (YelpLocationEntry  *entry,
-                 GdkEventFocus      *event,
-                 YelpWindow         *window)
-{
-    YelpWindowPrivate *priv = GET_PRIV (window);
-    gtk_widget_grab_focus (GTK_WIDGET (priv->view));
-    return FALSE;
-}
-
 static void
 view_new_window (YelpView   *view,
                  YelpUri    *uri,
@@ -1348,14 +1255,22 @@ view_root_title (YelpView    *view,
                  GParamSpec  *pspec,
                  YelpWindow  *window)
 {
-    gchar *title;
-    g_object_get (view, "root-title", &title, NULL);
+    YelpWindowPrivate *priv = GET_PRIV (window);
+    gchar *root_title, *page_title;
+    g_object_get (view, "root-title", &root_title, "page-title", &page_title, NULL);
 
-    if (title) {
-        gtk_window_set_title (GTK_WINDOW (window), title);
-        g_free (title);
+    if (page_title) {
+        gtk_header_bar_set_title (GTK_HEADER_BAR (priv->header), page_title);
+        g_free (page_title);
     } else {
-        gtk_window_set_title (GTK_WINDOW (window), _("Help"));
+        gtk_header_bar_set_title (GTK_HEADER_BAR (priv->header), _("Help"));
+    }
+
+    if (root_title) {
+        gtk_header_bar_set_subtitle (GTK_HEADER_BAR (priv->header), root_title);
+        g_free (root_title);
+    } else {
+        gtk_header_bar_set_subtitle (GTK_HEADER_BAR (priv->header), NULL);
     }
 }
 
@@ -1378,11 +1293,13 @@ hidden_entry_hide (YelpWindow  *window)
     YelpWindowPrivate *priv = GET_PRIV (window);
 
     if (gtk_widget_get_parent (priv->align_hidden) != NULL) {
+#if 0
         gtk_container_remove (GTK_CONTAINER (priv->hbox),
                               priv->align_hidden);
         gtk_box_pack_start (GTK_BOX (priv->hbox),
                             priv->align_location,
                             TRUE, TRUE, 0);
+#endif
     }
 }
 
diff --git a/src/yelp-window.h b/src/yelp-window.h
index 64db4c0..a130306 100644
--- a/src/yelp-window.h
+++ b/src/yelp-window.h
@@ -36,12 +36,12 @@ typedef struct _YelpWindowClass  YelpWindowClass;
 
 struct _YelpWindow
 {
-    GtkWindow       parent;
+    GtkApplicationWindow       parent;
 };
 
 struct _YelpWindowClass
 {
-    GtkWindowClass  parent_class;
+    GtkApplicationWindowClass  parent_class;
 };
 
 GType             yelp_window_get_type     (void);


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