[yelp] [libyelp] Show bookmarks in location entry with YelpBookmarks



commit c799c996a8a679631d45c0a08e8a7c8d73e279e5
Author: Shaun McCance <shaunm gnome org>
Date:   Tue Aug 10 12:03:39 2010 -0400

    [libyelp] Show bookmarks in location entry with YelpBookmarks
    
    The bookmarks are managed by the application, not libyelp, so we need a way for
    the application to tell libyelp about bookmarks. Add a YelpBookmarks interface,
    which is implemented by YelpApplication. Then we can pass a YelpBookmarks to
    any libyelp class that needs it.
    
    Still need to hook up the bookmarks-changed signal and do something useful when
    the bookmark-new icon is clicked.

 libyelp/Makefile.am           |    2 +
 libyelp/yelp-bookmarks.c      |   86 +++++++++
 libyelp/yelp-bookmarks.h      |   74 ++++++++
 libyelp/yelp-location-entry.c |  390 +++++++++++++++++++++++------------------
 libyelp/yelp-location-entry.h |   32 +---
 libyelp/yelp-settings.c       |    5 +
 src/yelp-application.c        |   51 +++++-
 src/yelp-application.h        |    3 +
 src/yelp-window.c             |    3 +-
 9 files changed, 437 insertions(+), 209 deletions(-)
---
diff --git a/libyelp/Makefile.am b/libyelp/Makefile.am
index 01217d1..7aa7907 100644
--- a/libyelp/Makefile.am
+++ b/libyelp/Makefile.am
@@ -1,6 +1,7 @@
 lib_LTLIBRARIES = libyelp.la
 
 libyelp_la_SOURCES =				\
+	yelp-bookmarks.c			\
 	yelp-debug.c				\
 	yelp-error.c				\
 	yelp-docbook-document.c			\
@@ -51,6 +52,7 @@ libyelp_la_LIBADD =				\
 	$(YELP_LIBS)
 
 libyelp_headers =				\
+	yelp-bookmarks.h			\
 	yelp-docbook-document.h			\
 	yelp-document.h				\
 	yelp-info-document.h			\
diff --git a/libyelp/yelp-bookmarks.c b/libyelp/yelp-bookmarks.c
new file mode 100644
index 0000000..e68d8a8
--- /dev/null
+++ b/libyelp/yelp-bookmarks.c
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance <shaunm gnome org>
+ */
+
+#include "yelp-bookmarks.h"
+
+enum {
+    BOOKMARKS_CHANGED,
+    LAST_SIGNAL
+};
+
+G_DEFINE_INTERFACE (YelpBookmarks, yelp_bookmarks, G_TYPE_OBJECT)
+
+static void
+yelp_bookmarks_default_init (YelpBookmarksInterface *iface)
+{
+}
+
+void
+yelp_bookmarks_add_bookmark (YelpBookmarks *bookmarks,
+                             const gchar   *doc_uri,
+                             const gchar   *page_id,
+                             const gchar   *icon,
+                             const gchar   *title)
+{
+    YelpBookmarksInterface *iface;
+
+    g_return_if_fail (YELP_IS_BOOKMARKS (bookmarks));
+
+    iface = YELP_BOOKMARKS_GET_INTERFACE (bookmarks);
+
+    if (iface->add_bookmark)
+        (* iface->add_bookmark) (bookmarks,
+                                 doc_uri, page_id,
+                                 icon, title);
+}
+
+void
+yelp_bookmarks_remove_bookmark (YelpBookmarks *bookmarks,
+                                const gchar   *doc_uri,
+                                const gchar   *page_id)
+{
+    YelpBookmarksInterface *iface;
+
+    g_return_if_fail (YELP_IS_BOOKMARKS (bookmarks));
+
+    iface = YELP_BOOKMARKS_GET_INTERFACE (bookmarks);
+
+    if (iface->remove_bookmark)
+        (* iface->remove_bookmark) (bookmarks, doc_uri, page_id);
+}
+
+gboolean
+yelp_bookmarks_is_bookmarked (YelpBookmarks *bookmarks,
+                              const gchar   *doc_uri,
+                              const gchar   *page_id)
+{
+    YelpBookmarksInterface *iface;
+
+    g_return_if_fail (YELP_IS_BOOKMARKS (bookmarks));
+
+    iface = YELP_BOOKMARKS_GET_INTERFACE (bookmarks);
+
+    if (iface->is_bookmarked)
+        return (* iface->is_bookmarked) (bookmarks, doc_uri, page_id);
+    else
+        return FALSE;
+}
diff --git a/libyelp/yelp-bookmarks.h b/libyelp/yelp-bookmarks.h
new file mode 100644
index 0000000..4c02518
--- /dev/null
+++ b/libyelp/yelp-bookmarks.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance <shaunm gnome org>
+ */
+
+#ifndef __YELP_BOOKMARKS_H__
+#define __YELP_BOOKMARKS_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define YELP_TYPE_BOOKMARKS               (yelp_bookmarks_get_type ())
+#define YELP_BOOKMARKS(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), YELP_TYPE_BOOKMARKS, YelpBookmarks))
+#define YELP_IS_BOOKMARKS(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YELP_TYPE_BOOKMARKS))
+#define YELP_BOOKMARKS_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), YELP_TYPE_BOOKMARKS, YelpBookmarksInterface))
+
+typedef struct _YelpBookmarks           YelpBookmarks;
+typedef struct _YelpBookmarksInterface  YelpBookmarksInterface;
+
+struct _YelpBookmarksInterface
+{
+    GTypeInterface g_iface;
+
+    /* Signals */
+    void      (* bookmarks_changed)     (YelpBookmarks *bookmarks);
+
+    /* Virtual Table */
+    void      (* add_bookmark)          (YelpBookmarks *bookmarks,
+                                         const gchar   *doc_uri,
+                                         const gchar   *page_id,
+                                         const gchar   *icon,
+                                         const gchar   *title);
+    void      (* remove_bookmark)       (YelpBookmarks *bookmarks,
+                                         const gchar   *doc_uri,
+                                         const gchar   *page_id);
+    gboolean  (* is_bookmarked)         (YelpBookmarks *bookmarks,
+                                         const gchar   *doc_uri,
+                                         const gchar   *page_id);
+};
+
+GType    yelp_bookmarks_get_type        (void);
+void     yelp_bookmarks_add_bookmark    (YelpBookmarks *bookmarks,
+                                         const gchar   *doc_uri,
+                                         const gchar   *page_id,
+                                         const gchar   *icon,
+                                         const gchar   *title);
+void     yelp_bookmarks_remove_bookmark  (YelpBookmarks *bookmarks,
+                                          const gchar   *doc_uri,
+                                          const gchar   *page_id);
+gboolean yelp_bookmarks_is_bookmarked    (YelpBookmarks *bookmarks,
+                                          const gchar   *doc_uri,
+                                          const gchar   *page_id);
+
+G_END_DECLS
+
+#endif /* __YELP_BOOKMARKS_H__ */
diff --git a/libyelp/yelp-location-entry.c b/libyelp/yelp-location-entry.c
index 86e1ab1..61fd456 100644
--- a/libyelp/yelp-location-entry.c
+++ b/libyelp/yelp-location-entry.c
@@ -172,7 +172,9 @@ typedef struct _YelpLocationEntryPrivate  YelpLocationEntryPrivate;
 struct _YelpLocationEntryPrivate
 {
     YelpView *view;
+    YelpBookmarks *bookmarks;
     GtkTreeRowReference *row;
+    gchar *completion_uri;
 
     /* do not free below */
     GtkWidget          *text_entry;
@@ -181,7 +183,6 @@ struct _YelpLocationEntryPrivate
     GtkEntryCompletion *completion;
 
     gboolean   enable_search;
-    gboolean   enable_bookmarks;
 
     gboolean   view_uri_selected;
 
@@ -190,6 +191,12 @@ struct _YelpLocationEntryPrivate
 };
 
 enum {
+    LOCATION_ENTRY_IS_LOADING    = 1 << 0,
+    LOCATION_ENTRY_IS_SEPARATOR  = 1 << 1,
+    LOCATION_ENTRY_IS_SEARCH     = 1 << 2
+};
+
+enum {
     HISTORY_COL_TITLE,
     HISTORY_COL_DESC,
     HISTORY_COL_ICON,
@@ -209,20 +216,19 @@ enum {
 };
 
 enum {
-  LOCATION_SELECTED,
-  SEARCH_ACTIVATED,
-  BOOKMARK_CLICKED,
-  LAST_SIGNAL
+    LOCATION_SELECTED,
+    SEARCH_ACTIVATED,
+    BOOKMARK_CLICKED,
+    LAST_SIGNAL
 };
 
 enum {  
-  PROP_0,
-  PROP_VIEW,
-  PROP_ENABLE_SEARCH,
-  PROP_ENABLE_BOOKMARKS
+    PROP_0,
+    PROP_VIEW,
+    PROP_BOOKMARKS,
+    PROP_ENABLE_SEARCH
 };
 
-static GHashTable *bookmarks;
 static GHashTable *completions;
 
 static guint location_entry_signals[LAST_SIGNAL] = {0,};
@@ -319,6 +325,22 @@ yelp_location_entry_class_init (YelpLocationEntryClass *klass)
                                                           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));
+
+    /**
      * YelpLocationEntry:enable-search
      *
      * Whether the location entry can act as a search entry.  If search is not
@@ -335,46 +357,30 @@ yelp_location_entry_class_init (YelpLocationEntryClass *klass)
                                                            G_PARAM_READWRITE |
                                                            G_PARAM_STATIC_STRINGS));
 
-    /**
-     * YelpLocationEntry:enable-bookmarks
-     *
-     * Whether the location entry should show bookmark icons.  If bookmarks
-     * are enabled, the location entry will show an "add bookmark" icon on the
-     * active location if it is not bookmarked and show bookmark flags next to
-     * rows in the history and completion drop-downs for locations that are
-     * bookmarked. Use yelp_location_entry_add_bookmark() and
-     * yelp_location_entry_remove_bookmark() to manage bookmarks.
-     **/
-    g_object_class_install_property (object_class,
-                                     PROP_ENABLE_BOOKMARKS,
-                                     g_param_spec_boolean ("enable-bookmarks",
-                                                           N_("Enable Bookmarks"),
-                                                           N_("Whether the location entry should show bookmark icons"),
-                                                           TRUE,
-                                                           G_PARAM_CONSTRUCT |
-                                                           G_PARAM_READWRITE |
-                                                           G_PARAM_STATIC_STRINGS));
-
     g_type_class_add_private ((GObjectClass *) klass,
                               sizeof (YelpLocationEntryPrivate));
 
-    bookmarks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
     completions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
 }
 
 static void
 yelp_location_entry_init (YelpLocationEntry *entry)
 {
-    GtkCellRenderer *bookmark_cell;
-    GList *cells;
     YelpLocationEntryPrivate *priv = GET_PRIV (entry);
-
     priv->search_mode = FALSE;
-
     g_object_set (entry, "text-column", HISTORY_COL_TITLE, NULL);
+}
+
+static void
+location_entry_constructed (GObject *object)
+{
+    GtkCellRenderer *bookmark_cell;
+    GList *cells;
+    GtkTreeIter iter;
+    YelpLocationEntryPrivate *priv = GET_PRIV (object);
 
     /* Set up the text entry child */
-    priv->text_entry = gtk_bin_get_child (GTK_BIN (entry));
+    priv->text_entry = gtk_bin_get_child (GTK_BIN (object));
     gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
                                        GTK_ENTRY_ICON_PRIMARY,
                                        "help-browser");
@@ -384,6 +390,12 @@ yelp_location_entry_init (YelpLocationEntry *entry)
     gtk_entry_set_icon_activatable (GTK_ENTRY (priv->text_entry),
                                     GTK_ENTRY_ICON_SECONDARY,
                                     TRUE);
+    gtk_editable_set_editable (GTK_EDITABLE (priv->text_entry),
+                               priv->enable_search);
+    if (!priv->enable_search) {
+        priv->search_mode = FALSE;
+        location_entry_set_entry ((YelpLocationEntry *) object, FALSE);
+    }
 
     /* Set up the history model */
     priv->history = gtk_list_store_new (8,
@@ -398,8 +410,20 @@ yelp_location_entry_init (YelpLocationEntry *entry)
                                         );
     g_signal_connect (priv->history, "row-changed",
                       G_CALLBACK (history_row_changed),
-                      entry);
-    g_object_set (entry, "model", priv->history, NULL);
+                      object);
+    g_object_set (object, "model", priv->history, NULL);
+    if (priv->enable_search) {
+        gtk_list_store_append (priv->history, &iter);
+        gtk_list_store_set (priv->history, &iter,
+                            HISTORY_COL_FLAGS, LOCATION_ENTRY_IS_SEPARATOR,
+                            -1);
+        gtk_list_store_append (priv->history, &iter);
+        gtk_list_store_set (priv->history, &iter,
+                            HISTORY_COL_ICON, "system-search",
+                            HISTORY_COL_TITLE, _("Search..."),
+                            HISTORY_COL_FLAGS, LOCATION_ENTRY_IS_SEARCH,
+                            -1);
+    }
 
     /* Set up the history drop-down */
     /* Trying to get the text to line up with the text in the GtkEntry.
@@ -407,71 +431,56 @@ yelp_location_entry_init (YelpLocationEntry *entry)
      * yet called reorder.  I realize using a guesstimate pixel value
      * won't be perfect all the time, but 4 looks niceish.
      */
-    cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (entry));
+    cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (object));
     g_object_set (cells->data, "xpad", 4, NULL);
-    gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (entry),
+    gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (object),
                                         GTK_CELL_RENDERER (cells->data),
                                         (GtkCellLayoutDataFunc) cell_set_text_cell,
-                                        entry, NULL);
+                                        object, NULL);
     g_object_set (cells->data, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
     g_list_free (cells);
 
     priv->icon_cell = gtk_cell_renderer_pixbuf_new ();
-    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (entry), priv->icon_cell, FALSE);
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (object), priv->icon_cell, FALSE);
     g_object_set (priv->icon_cell, "yalign", 0.2, NULL);
-    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (entry),
+    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (object),
                                     priv->icon_cell,
                                     "icon-name",
                                     HISTORY_COL_ICON,
                                     NULL);
-
-    gtk_cell_layout_reorder (GTK_CELL_LAYOUT (entry), priv->icon_cell, 0);
-
-    bookmark_cell = gtk_cell_renderer_pixbuf_new ();
-    gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (entry), bookmark_cell, FALSE);
-    gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (entry),
-                                        bookmark_cell,
-                                        (GtkCellLayoutDataFunc) cell_set_bookmark_icon,
-                                        entry, NULL);
+    gtk_cell_layout_reorder (GTK_CELL_LAYOUT (object), priv->icon_cell, 0);
+
+    if (priv->bookmarks) {
+        bookmark_cell = gtk_cell_renderer_pixbuf_new ();
+        gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (object), bookmark_cell, FALSE);
+        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (object),
+                                            bookmark_cell,
+                                            (GtkCellLayoutDataFunc) cell_set_bookmark_icon,
+                                            object, NULL);
+    }
+    gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (object),
+                                          (GtkTreeViewRowSeparatorFunc)
+                                          combo_box_row_separator_func,
+                                          object, NULL);
+    /* Without this, you get a warning about the popup widget
+     * being NULL the firt time you click the arrow.
+     */
+    gtk_widget_show_all (GTK_WIDGET (object));
 
     /* Connect signals */
-    g_signal_connect (entry, "changed",
+    g_signal_connect (object, "changed",
                       G_CALLBACK (combo_box_changed_cb), NULL);
 
     g_signal_connect (priv->text_entry, "focus-in-event",
-                      G_CALLBACK (entry_focus_in_cb), entry);
+                      G_CALLBACK (entry_focus_in_cb), object);
     g_signal_connect (priv->text_entry, "focus-out-event",
-                      G_CALLBACK (entry_focus_out_cb), entry);
+                      G_CALLBACK (entry_focus_out_cb), object);
     g_signal_connect (priv->text_entry, "icon-press",
-                      G_CALLBACK (entry_icon_press_cb), entry);
+                      G_CALLBACK (entry_icon_press_cb), object);
     g_signal_connect (priv->text_entry, "key-press-event",
-                      G_CALLBACK (entry_key_press_cb), entry);
+                      G_CALLBACK (entry_key_press_cb), object);
     g_signal_connect (priv->text_entry, "activate",
-                      G_CALLBACK (entry_activate_cb), entry);
-}
-
-static void
-location_entry_constructed (GObject *object)
-{
-    GtkTreeIter iter;
-    YelpLocationEntryPrivate *priv = GET_PRIV (object);
-
-    if (priv->enable_search) {
-        gtk_list_store_append (priv->history, &iter);
-        gtk_list_store_set (priv->history, &iter,
-                            HISTORY_COL_FLAGS, YELP_LOCATION_ENTRY_IS_SEPARATOR,
-                            -1);
-        gtk_list_store_append (priv->history, &iter);
-        gtk_list_store_set (priv->history, &iter,
-                            HISTORY_COL_ICON, "system-search",
-                            HISTORY_COL_TITLE, _("Search..."),
-                            HISTORY_COL_FLAGS, YELP_LOCATION_ENTRY_IS_SEARCH,
-                            -1);
-    }
-    gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (object),
-                                          (GtkTreeViewRowSeparatorFunc)
-                                          combo_box_row_separator_func,
-                                          object, NULL);
+                      G_CALLBACK (entry_activate_cb), object);
 
     g_signal_connect (priv->view, "loaded", G_CALLBACK (view_loaded), object);
     g_signal_connect (priv->view, "notify::yelp-uri", G_CALLBACK (view_uri_selected), object);
@@ -490,6 +499,11 @@ location_entry_dispose (GObject *object)
         priv->view = NULL;
     }
 
+    if (priv->bookmarks) {
+        g_object_unref (priv->bookmarks);
+        priv->bookmarks = NULL;
+    }
+
     if (priv->row) {
         gtk_tree_row_reference_free (priv->row);
         priv->row = NULL;
@@ -506,6 +520,10 @@ location_entry_dispose (GObject *object)
 static void
 location_entry_finalize (GObject *object)
 {
+    YelpLocationEntryPrivate *priv = GET_PRIV (object);
+
+    g_free (priv->completion_uri);
+
     G_OBJECT_CLASS (yelp_location_entry_parent_class)->finalize (object);
 }
 
@@ -521,12 +539,12 @@ location_entry_get_property   (GObject      *object,
     case PROP_VIEW:
         g_value_set_object (value, priv->view);
         break;
+    case PROP_BOOKMARKS:
+        g_value_set_object (value, priv->bookmarks);
+        break;
     case PROP_ENABLE_SEARCH:
         g_value_set_boolean (value, priv->enable_search);
         break;
-    case PROP_ENABLE_BOOKMARKS:
-        g_value_set_boolean (value, priv->enable_bookmarks);
-        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -545,17 +563,11 @@ location_entry_set_property   (GObject      *object,
     case PROP_VIEW:
         priv->view = g_value_dup_object (value);
         break;
+    case PROP_BOOKMARKS:
+        priv->bookmarks = g_value_dup_object (value);
+        break;
     case PROP_ENABLE_SEARCH:
         priv->enable_search = g_value_get_boolean (value);
-        gtk_editable_set_editable (GTK_EDITABLE (priv->text_entry),
-                                   priv->enable_search);
-        if (!priv->enable_search) {
-            priv->search_mode = FALSE;
-            location_entry_set_entry ((YelpLocationEntry *) object, FALSE);
-        }
-        break;
-    case PROP_ENABLE_BOOKMARKS:
-        priv->enable_bookmarks = g_value_get_boolean (value);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -672,14 +684,14 @@ location_entry_set_completion (YelpLocationEntry *entry,
                                     "icon-name",
                                     COMPLETION_COL_ICON,
                                     NULL);
-
-    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);
-
+    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 (priv->text_entry),
                               priv->completion);
 }
@@ -710,15 +722,17 @@ location_entry_set_entry (YelpLocationEntry *entry, gboolean emit)
         path = gtk_tree_row_reference_get_path (priv->row);
 
     if (path) {
-        gchar *text;
+        gchar *text, *doc_uri, *page_id;
         gint flags;
         gtk_tree_model_get_iter (model, &iter, path);
         gtk_tree_model_get (model, &iter,
                             HISTORY_COL_TITLE, &text,
                             HISTORY_COL_ICON, &icon_name,
                             HISTORY_COL_FLAGS, &flags,
+                            HISTORY_COL_DOC, &doc_uri,
+                            HISTORY_COL_PAGE, &page_id,
                             -1);
-        if (flags & YELP_LOCATION_ENTRY_IS_LOADING) {
+        if (flags & LOCATION_ENTRY_IS_LOADING) {
             gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
                                                GTK_ENTRY_ICON_PRIMARY,
                                                "image-loading");
@@ -731,19 +745,23 @@ location_entry_set_entry (YelpLocationEntry *entry, gboolean emit)
                                                GTK_ENTRY_ICON_PRIMARY,
                                                icon_name);
         }
-        if (flags & YELP_LOCATION_ENTRY_CAN_BOOKMARK) {
-            gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
-                                               GTK_ENTRY_ICON_SECONDARY,
-                                               "bookmark-new");
-            gtk_entry_set_icon_tooltip_text (GTK_ENTRY (priv->text_entry),
-                                             GTK_ENTRY_ICON_SECONDARY,
-                                             "Bookmark this page");
-        }
-        else {
-            gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
-                                               GTK_ENTRY_ICON_SECONDARY,
-                                               NULL);
+        if (priv->bookmarks && doc_uri && page_id) {
+            if (!yelp_bookmarks_is_bookmarked (priv->bookmarks, doc_uri, page_id)) {
+                gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
+                                                   GTK_ENTRY_ICON_SECONDARY,
+                                                   "bookmark-new");
+                gtk_entry_set_icon_tooltip_text (GTK_ENTRY (priv->text_entry),
+                                                 GTK_ENTRY_ICON_SECONDARY,
+                                                 "Bookmark this page");
+            }
+            else {
+                gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
+                                                   GTK_ENTRY_ICON_SECONDARY,
+                                                   NULL);
+            }
         }
+        g_free (doc_uri);
+        g_free (page_id);
         gtk_entry_set_text (GTK_ENTRY (priv->text_entry), text);
         if (emit)
             g_signal_emit (entry, location_entry_signals[LOCATION_SELECTED], 0);
@@ -779,14 +797,14 @@ location_entry_pulse (gpointer data)
         gtk_tree_path_free (path);
     }
 
-    if (flags & YELP_LOCATION_ENTRY_IS_LOADING && !priv->search_mode) {
+    if (flags & LOCATION_ENTRY_IS_LOADING && !priv->search_mode) {
         gtk_entry_progress_pulse (GTK_ENTRY (priv->text_entry));
     }
     else {
         gtk_entry_set_progress_fraction (GTK_ENTRY (priv->text_entry), 0.0);
     }
 
-    return flags & YELP_LOCATION_ENTRY_IS_LOADING;
+    return flags & LOCATION_ENTRY_IS_LOADING;
 }
 
 static void
@@ -804,7 +822,7 @@ combo_box_changed_cb (GtkComboBox  *widget,
         gtk_tree_model_get (model, &iter,
                             HISTORY_COL_FLAGS, &flags,
                             -1);
-        if (flags & YELP_LOCATION_ENTRY_IS_SEARCH) {
+        if (flags & LOCATION_ENTRY_IS_SEARCH) {
             location_entry_start_search ((YelpLocationEntry *) widget, TRUE);
         }
         else {            
@@ -838,7 +856,7 @@ combo_box_row_separator_func (GtkTreeModel  *model,
     gtk_tree_model_get (model, iter,
                         HISTORY_COL_FLAGS, &flags,
                         -1);
-    return (flags & YELP_LOCATION_ENTRY_IS_SEPARATOR);
+    return (flags & LOCATION_ENTRY_IS_SEPARATOR);
 }
 
 static void
@@ -976,17 +994,32 @@ cell_set_bookmark_icon (GtkCellLayout     *layout,
                         YelpLocationEntry *entry)
 {
     gint flags;
+    gchar *doc_uri, *page_id;
     YelpLocationEntryPrivate *priv = GET_PRIV (entry);
 
+    if (priv->bookmarks == NULL) {
+        g_object_set (cell, "icon-name", NULL, NULL);
+        return;
+    }
+
+    gtk_tree_model_get (model, iter, HISTORY_COL_FLAGS, &flags, -1);
+    if (flags & (LOCATION_ENTRY_IS_SEPARATOR | LOCATION_ENTRY_IS_SEARCH)) {
+        g_object_set (cell, "icon-name", NULL, NULL);
+        return;
+    }
+    
     gtk_tree_model_get (model, iter,
-                        HISTORY_COL_FLAGS, &flags,
+                        HISTORY_COL_DOC, &doc_uri,
+                        HISTORY_COL_PAGE, &page_id,
                         -1);
-    if (!(flags & YELP_LOCATION_ENTRY_IS_SEPARATOR) &&
-        !(flags & YELP_LOCATION_ENTRY_IS_SEARCH) &&
-        (flags & YELP_LOCATION_ENTRY_IS_BOOKMARKED))
+    if (doc_uri && page_id &&
+        yelp_bookmarks_is_bookmarked (priv->bookmarks, doc_uri, page_id))
         g_object_set (cell, "icon-name", "bookmark", NULL);
     else
         g_object_set (cell, "icon-name", NULL, NULL);
+
+    g_free (doc_uri);
+    g_free (page_id);
 }
 
 static void
@@ -996,16 +1029,22 @@ cell_set_completion_bookmark_icon (GtkCellLayout     *layout,
                                    GtkTreeIter       *iter,
                                    YelpLocationEntry *entry)
 {
-    gint flags;
     YelpLocationEntryPrivate *priv = GET_PRIV (entry);
 
-    gtk_tree_model_get (model, iter,
-                        COMPLETION_COL_FLAGS, &flags,
-                        -1);
-    if (flags & YELP_LOCATION_ENTRY_IS_BOOKMARKED)
-        g_object_set (cell, "icon-name", "bookmark", NULL);
-    else
-        g_object_set (cell, "icon-name", NULL, NULL);
+    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", "bookmark", NULL);
+        else
+            g_object_set (cell, "icon-name", NULL, NULL);
+
+        g_free (page_id);
+    }
 }
 
 static void
@@ -1160,8 +1199,8 @@ view_loaded (YelpView          *view,
     gtk_tree_model_get (GTK_TREE_MODEL (priv->history), &iter,
                         HISTORY_COL_FLAGS, &flags,
                         -1);
-    if (flags & YELP_LOCATION_ENTRY_IS_LOADING) {
-        flags = flags & ~YELP_LOCATION_ENTRY_IS_LOADING;
+    if (flags & LOCATION_ENTRY_IS_LOADING) {
+        flags = flags & ~LOCATION_ENTRY_IS_LOADING;
         gtk_list_store_set (priv->history, &iter, HISTORY_COL_FLAGS, flags, -1);
     }
 
@@ -1176,46 +1215,47 @@ view_loaded (YelpView          *view,
                         -1);
     g_free (page_id);
 
-    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, doc_uri, completion);
-        ids = yelp_document_list_page_ids (document);
-        for (i = 0; ids[i]; i++) {
-            GtkTreeIter iter;
-            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);
+    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);
+            ids = yelp_document_list_page_ids (document);
+            for (i = 0; ids[i]; i++) {
+                GtkTreeIter iter;
+                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_object_unref (base);
+            g_strfreev (ids);
         }
-        g_object_unref (base);
-        g_strfreev (ids);
+        g_free (priv->completion_uri);
+        priv->completion_uri = doc_uri;
+        location_entry_set_completion (entry, completion);
     }
-    else {
-        g_free (doc_uri);
-    }
-
-    location_entry_set_completion (entry, completion);
 
     g_object_unref (uri);
 }
@@ -1265,7 +1305,7 @@ view_uri_selected (YelpView          *view,
                             HISTORY_COL_TITLE, _("Loading"),
                             HISTORY_COL_ICON, "help-contents",
                             HISTORY_COL_URI, struri,
-                            HISTORY_COL_FLAGS, YELP_LOCATION_ENTRY_IS_LOADING,
+                            HISTORY_COL_FLAGS, LOCATION_ENTRY_IS_LOADING,
                             -1);
         /* Limit to 15 entries. There are two extra for the search entry and
          * the separator above it.
@@ -1373,13 +1413,15 @@ view_page_icon (YelpView          *view,
  * Returns: A new #YelpLocationEntry.
  **/
 GtkWidget*
-yelp_location_entry_new (YelpView *view)
+yelp_location_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_LOCATION_ENTRY,
                                     "view", view,
+                                    "bookmarks", bookmarks,
                                     NULL));
 
     return ret;
diff --git a/libyelp/yelp-location-entry.h b/libyelp/yelp-location-entry.h
index 34cd72c..9ba427c 100644
--- a/libyelp/yelp-location-entry.h
+++ b/libyelp/yelp-location-entry.h
@@ -25,6 +25,7 @@
 
 #include <gtk/gtk.h>
 
+#include "yelp-bookmarks.h"
 #include "yelp-view.h"
 
 G_BEGIN_DECLS
@@ -70,37 +71,10 @@ struct _YelpLocationEntryClass
     void (*_gtk_reserved3) (void);
 };
 
-/**
- * YelpLocationEntryFlags:
- * @YELP_LOCATION_ENTRY_CAN_BOOKMARK: This location can be bookmarked.  When a
- * bookmarkable location is selected, the secondary icon of the embedded text
- * entry will be a clickable bookmark icon.
- * @YELP_LOCATION_ENTRY_IS_BOOKMARKED: This location is already bookmarked.
- * Bookmarked locations will have an emblem in drop-down lists.
- * @YELP_LOCATION_ENTRY_IS_LOADING: Page data for this location is still loading.
- * The #YelpLocationEntry widget will display an indeterminate progress indicator.
- * @YELP_LOCATION_ENTRY_IS_SEPARATOR: This row should be displayed as a separator.
- * @YELP_LOCATION_ENTRY_IS_SEARCH: Selecting this row initiates a search instead
- * of selecting a location.
- *
- * Flags which can be used to provide additional information about rows
- * to be displayed by a #YelpLocationEntry.
- **/
-typedef enum {
-    YELP_LOCATION_ENTRY_CAN_BOOKMARK  = 1 << 0,
-    YELP_LOCATION_ENTRY_IS_BOOKMARKED = 1 << 1,
-    YELP_LOCATION_ENTRY_IS_LOADING    = 1 << 2,
-    YELP_LOCATION_ENTRY_IS_SEPARATOR  = 1 << 3,
-    YELP_LOCATION_ENTRY_IS_SEARCH     = 1 << 4
-} YelpLocationEntryFlags;
-
 GType           yelp_location_entry_get_type          (void);
-GtkWidget *     yelp_location_entry_new               (YelpView           *window);
+GtkWidget *     yelp_location_entry_new               (YelpView           *window,
+                                                       YelpBookmarks      *bookmarks);
 void            yelp_location_entry_start_search      (YelpLocationEntry  *entry);
-void            yelp_location_entry_add_bookmark      (const gchar        *doc_uri,
-                                                       const gchar        *page_id);
-void            yelp_location_entry_remove_bookmark   (const gchar        *doc_uri,
-                                                       const gchar        *page_id);
 
 G_END_DECLS
 
diff --git a/libyelp/yelp-settings.c b/libyelp/yelp-settings.c
index 478b378..69a474d 100644
--- a/libyelp/yelp-settings.c
+++ b/libyelp/yelp-settings.c
@@ -48,6 +48,8 @@ struct _YelpSettingsPriv {
     gboolean      show_text_cursor;
 
     gboolean      editor_mode;
+
+    GHashTable   *bookmarks;
 };
 
 enum {
@@ -227,6 +229,9 @@ yelp_settings_init (YelpSettings *settings)
 	settings->priv->setfonts[i] = NULL;
 	settings->priv->fonts[i] = NULL;
     }
+
+    settings->priv->bookmarks = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                       g_free, NULL);
 }
 
 static void
diff --git a/src/yelp-application.c b/src/yelp-application.c
index 9a9c621..a4635a1 100644
--- a/src/yelp-application.c
+++ b/src/yelp-application.c
@@ -32,6 +32,7 @@
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
 
+#include "yelp-bookmarks.h"
 #include "yelp-settings.h"
 #include "yelp-view.h"
 
@@ -73,10 +74,11 @@ static const gchar introspection_xml[] =
     "</node>";
 GDBusNodeInfo *introspection_data;
 
-static void          yelp_application_init             (YelpApplication       *app);
-static void          yelp_application_class_init       (YelpApplicationClass  *klass);
-static void          yelp_application_dispose          (GObject               *object);
-static void          yelp_application_finalize         (GObject               *object);
+static void          yelp_application_init             (YelpApplication        *app);
+static void          yelp_application_class_init       (YelpApplicationClass   *klass);
+static void          yelp_application_iface_init       (YelpBookmarksInterface *iface);
+static void          yelp_application_dispose          (GObject                *object);
+static void          yelp_application_finalize         (GObject                *object);
 
 static void          yelp_application_method           (GDBusConnection       *connection,
                                                         const gchar           *sender,
@@ -103,7 +105,9 @@ static void          application_set_font_sensitivity  (YelpApplication       *a
 static gboolean      window_resized                    (YelpWindow            *window,
                                                         YelpApplication       *app);
 
-G_DEFINE_TYPE (YelpApplication, yelp_application, G_TYPE_OBJECT);
+G_DEFINE_TYPE_WITH_CODE (YelpApplication, yelp_application, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (YELP_TYPE_BOOKMARKS,
+                                                yelp_application_iface_init))
 #define GET_PRIV(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_APPLICATION, YelpApplicationPrivate))
 
 GDBusInterfaceVTable yelp_dbus_vtable = {
@@ -174,6 +178,14 @@ yelp_application_class_init (YelpApplicationClass *klass)
 }
 
 static void
+yelp_application_iface_init (YelpBookmarksInterface *iface)
+{
+    iface->add_bookmark = yelp_application_add_bookmark;
+    iface->remove_bookmark = yelp_application_remove_bookmark;
+    iface->is_bookmarked = yelp_application_is_bookmarked;
+}
+
+static void
 yelp_application_dispose (GObject *object)
 {
     YelpApplicationPrivate *priv = GET_PRIV (object);
@@ -678,6 +690,35 @@ yelp_application_remove_bookmark (YelpApplication   *app,
     }
 }
 
+gboolean
+yelp_application_is_bookmarked (YelpApplication   *app,
+                                const gchar       *doc_uri,
+                                const gchar       *page_id)
+{
+    GVariant *bookmarks;
+    GVariantIter *iter;
+    gboolean ret = FALSE;
+    gchar *this_id = NULL;
+    GSettings *settings;
+
+    settings = application_get_doc_settings (app, doc_uri);
+    if (settings == NULL)
+        return FALSE;
+
+    bookmarks = g_settings_get_value (settings, "bookmarks");
+    g_settings_get (settings, "bookmarks", "a(sss)", &iter);
+    while (g_variant_iter_loop (iter, "(&s&s&s)", &this_id, NULL, NULL)) {
+        if (g_str_equal (page_id, this_id)) {
+            ret = TRUE;
+            break;
+        }
+    }
+
+    g_variant_iter_free (iter);
+    g_variant_unref (bookmarks);
+    return ret;
+}
+
 void
 yelp_application_update_bookmarks (YelpApplication   *app,
                                    const gchar       *doc_uri,
diff --git a/src/yelp-application.h b/src/yelp-application.h
index 1060683..98839e9 100644
--- a/src/yelp-application.h
+++ b/src/yelp-application.h
@@ -68,6 +68,9 @@ void              yelp_application_add_bookmark         (YelpApplication   *app,
 void              yelp_application_remove_bookmark      (YelpApplication   *app,
                                                          const gchar       *doc_uri,
                                                          const gchar       *page_id);
+gboolean          yelp_application_is_bookmarked        (YelpApplication   *app,
+                                                         const gchar       *doc_uri,
+                                                         const gchar       *page_id);
 void              yelp_application_update_bookmarks     (YelpApplication   *app,
                                                          const gchar       *doc_uri,
                                                          const gchar       *page_id,
diff --git a/src/yelp-window.c b/src/yelp-window.c
index a6c5680..a082527 100644
--- a/src/yelp-window.c
+++ b/src/yelp-window.c
@@ -509,7 +509,8 @@ window_construct (YelpWindow *window)
                         button,
                         FALSE, FALSE, 0);
 
-    priv->entry = (YelpLocationEntry *) yelp_location_entry_new (priv->view);
+    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",



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