[yelp] First pass at bookmarks



commit efbdbbf5f36697d4f0402d76b759720bf5b25b9b
Author: Shaun McCance <shaunm gnome org>
Date:   Thu Apr 29 16:44:11 2010 -0500

    First pass at bookmarks
    
    Bookmarks are now stored per-document in GSettings. They're stored as
    page IDs along with the icon and title. Problems to fix:
    
    * On the index page, the page ID might be NULL. Yelp will crash if
      you try to bookmark this. We need to track the real page ID from
      the view. This will also help us solve the double-index-entry in
      the location entry drop-down.
    * Bookmarks aren't sorted in any meaningful way. We should sort by
      page type (icon) first, then title. Again, same thing for location
      entry completion.
    * There's no way to remove bookmarks.
    * (Wish list) We should automatically reset the icon and title on
      existing bookmarks if they're different (e.g. after an upgrade).
    * We need to hook up the bookmarking to the location entry.

 data/org.gnome.yelp.gschema.xml |    3 +
 src/yelp-application.c          |  114 +++++++++++++++++++++++-----
 src/yelp-application.h          |   10 ++-
 src/yelp-window.c               |  158 ++++++++++++++++++++++++++++++++++++++-
 4 files changed, 262 insertions(+), 23 deletions(-)
---
diff --git a/data/org.gnome.yelp.gschema.xml b/data/org.gnome.yelp.gschema.xml
index 046f9d5..250c12a 100644
--- a/data/org.gnome.yelp.gschema.xml
+++ b/data/org.gnome.yelp.gschema.xml
@@ -20,5 +20,8 @@
   <key name="geometry" type="(ii)">
     <default>(520,580)</default>
   </key>
+  <key name="bookmarks" type="a(sss)">
+    <default>[]</default>
+  </key>
 </schema>
 </schemalist>
diff --git a/src/yelp-application.c b/src/yelp-application.c
index 341c00a..f239292 100644
--- a/src/yelp-application.c
+++ b/src/yelp-application.c
@@ -45,6 +45,12 @@
 
 static gboolean editor_mode = FALSE;
 
+enum {
+    BOOKMARKS_CHANGED,
+    LAST_SIGNAL
+};
+static gint signals[LAST_SIGNAL] = { 0 };
+
 static const GOptionEntry entries[] = {
     {"editor-mode", 0, 0, G_OPTION_ARG_NONE, &editor_mode, N_("Turn on editor mode"), NULL},
     { NULL }
@@ -68,6 +74,8 @@ static void          application_uri_resolved          (YelpUri               *u
 static gboolean      application_window_deleted        (YelpWindow            *window,
                                                         GdkEvent              *event,
                                                         YelpApplication       *app);
+GSettings *          application_get_doc_settings      (YelpApplication       *app,
+                                                        const gchar           *doc_uri);
 static gboolean      application_maybe_quit            (YelpApplication       *app);
 static void          application_adjust_font           (GtkAction             *action,
                                                         YelpApplication       *app);
@@ -123,6 +131,14 @@ yelp_application_class_init (YelpApplicationClass *klass)
     object_class->dispose = yelp_application_dispose;
     object_class->finalize = yelp_application_finalize;
 
+    signals[BOOKMARKS_CHANGED] =
+        g_signal_new ("bookmarks-changed",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      0, NULL, NULL,
+                      g_cclosure_marshal_VOID__STRING,
+                      G_TYPE_NONE, 1, G_TYPE_STRING);
+
     dbus_g_object_type_install_info (YELP_TYPE_APPLICATION,
                                      &dbus_glib_yelp_object_info);
 
@@ -424,28 +440,12 @@ application_uri_resolved (YelpUri             *uri,
 
     if (window == NULL) {
         gint width, height;
-        GSettings *settings = g_hash_table_lookup (priv->docsettings, doc_uri);
-        if (settings == NULL) {
-            gchar *tmp;
-            gchar *settings_path;
-            tmp = g_uri_escape_string (doc_uri, "", FALSE);
-            settings_path = g_strconcat ("/apps/yelp/documents/", tmp, "/", NULL);
-            g_free (tmp);
-            if (priv->gsettings_context)
-                settings = g_settings_new_with_context_and_path ("org.gnome.yelp.documents",
-                                                                 priv->gsettings_context,
-                                                                 settings_path);
-            else
-                settings = g_settings_new_with_path ("org.gnome.yelp.document",
-                                                     settings_path);
-            g_hash_table_insert (priv->docsettings, g_strdup (doc_uri), settings);
-            g_free (settings_path);
-        }
+        GSettings *settings = application_get_doc_settings (data->app, doc_uri);
 
         g_settings_get (settings, "geometry", "(ii)", &width, &height);
         window = yelp_window_new (data->app);
         gtk_window_set_default_size (GTK_WINDOW (window), width, height);
-        g_signal_connect (window, "resized", window_resized, data->app);
+        g_signal_connect (window, "resized", G_CALLBACK (window_resized), data->app);
         priv->windows = g_slist_prepend (priv->windows, window);
 
         if (!data->new) {
@@ -476,6 +476,30 @@ application_uri_resolved (YelpUri             *uri,
     g_free (data);
 }
 
+GSettings *
+application_get_doc_settings (YelpApplication *app, const gchar *doc_uri)
+{
+    YelpApplicationPrivate *priv = GET_PRIV (app);
+    GSettings *settings = g_hash_table_lookup (priv->docsettings, doc_uri);
+    if (settings == NULL) {
+        gchar *tmp;
+        gchar *settings_path;
+        tmp = g_uri_escape_string (doc_uri, "", FALSE);
+        settings_path = g_strconcat ("/apps/yelp/documents/", tmp, "/", NULL);
+        g_free (tmp);
+        if (priv->gsettings_context)
+            settings = g_settings_new_with_context_and_path ("org.gnome.yelp.documents",
+                                                             priv->gsettings_context,
+                                                             settings_path);
+        else
+            settings = g_settings_new_with_path ("org.gnome.yelp.document",
+                                                 settings_path);
+        g_hash_table_insert (priv->docsettings, g_strdup (doc_uri), settings);
+        g_free (settings_path);
+    }
+    return settings;
+}
+
 static gboolean
 application_window_deleted (YelpWindow      *window,
                               GdkEvent        *event,
@@ -539,6 +563,60 @@ packages_installed (DBusGProxy     *proxy,
 }
 
 void
+yelp_application_add_bookmark (YelpApplication   *app,
+                               YelpUri           *uri,
+                               const gchar       *icon,
+                               const gchar       *title)
+{
+    GSettings *settings;
+    gchar *doc_uri, *page_id;
+    YelpApplicationPrivate *priv = GET_PRIV (app);
+
+    doc_uri = yelp_uri_get_document_uri (uri);
+    page_id = yelp_uri_get_page_id (uri);
+    settings = application_get_doc_settings (app, doc_uri);
+
+    if (settings) {
+        GVariantBuilder builder;
+        GVariantIter *iter;
+        gchar *this_id, *this_icon, *this_title;
+        gboolean broken = FALSE;
+        g_settings_get (settings, "bookmarks", "a(sss)", &iter);
+        g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sss)"));
+        while (g_variant_iter_loop (iter, "(&s&s&s)", &this_id, &this_icon, &this_title)) {
+            if (g_str_equal (page_id, this_id)) {
+                /* Already have this bookmark */
+                broken = TRUE;
+                break;
+            }
+            g_variant_builder_add (&builder, "(sss)", this_id, this_icon, this_title);
+        }
+        g_variant_iter_free (iter);
+
+        if (!broken) {
+            GVariant *value;
+            g_variant_builder_add (&builder, "(sss)", page_id, icon, title);
+            value = g_variant_builder_end (&builder);
+            g_settings_set_value (settings, "bookmarks", value);
+            g_variant_unref (value);
+            g_signal_emit (app, signals[BOOKMARKS_CHANGED], 0, doc_uri, 0);
+        }
+    }
+
+    g_free (doc_uri);
+    g_free (page_id);
+}
+
+GVariant *
+yelp_application_get_bookmarks (YelpApplication *app,
+                                const gchar     *doc_uri)
+{
+    GSettings *settings = application_get_doc_settings (app, doc_uri);
+
+    return g_settings_get_value (settings, "bookmarks");
+}
+
+void
 yelp_application_install_package (YelpApplication  *app,
                                   const gchar      *pkg,
                                   const gchar      *alt)
diff --git a/src/yelp-application.h b/src/yelp-application.h
index c753212..9ae9d1c 100644
--- a/src/yelp-application.h
+++ b/src/yelp-application.h
@@ -25,6 +25,8 @@
 
 #include <glib-object.h>
 
+#include "yelp-uri.h"
+
 #define YELP_TYPE_APPLICATION            (yelp_application_get_type ())
 #define YELP_APPLICATION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), YELP_TYPE_APPLICATION, YelpApplication))
 #define YELP_APPLICATION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), YELP_TYPE_APPLICATION, YelpApplicationClass))
@@ -55,7 +57,13 @@ gboolean          yelp_application_load_uri    (YelpApplication  *app,
                                                 GError          **error);
 void              yelp_application_new_window  (YelpApplication  *app,
                                                 const gchar      *uri);
-GtkActionGroup *  yelp_application_get_action_group     (YelpApplication  *app);
+GtkActionGroup *  yelp_application_get_action_group     (YelpApplication   *app);
+void              yelp_application_add_bookmark         (YelpApplication   *app,
+                                                         YelpUri           *uri,
+                                                         const gchar       *icon,
+                                                         const gchar       *title);
+GVariant *        yelp_application_get_bookmarks        (YelpApplication   *app,
+                                                         const gchar       *doc_uri);
 void              yelp_application_install_package      (YelpApplication   *app,
                                                          const gchar       *pkg,
                                                          const gchar       *alt);
diff --git a/src/yelp-window.c b/src/yelp-window.c
index 59c930b..1724d87 100644
--- a/src/yelp-window.c
+++ b/src/yelp-window.c
@@ -58,9 +58,19 @@ static gboolean      window_configure_event       (YelpWindow         *window,
 static gboolean      window_resize_signal         (YelpWindow         *window);
 static void          window_close                 (GtkAction          *action,
                                                    YelpWindow         *window);
+static void          window_add_bookmark          (GtkAction          *action,
+                                                   YelpWindow         *window);
+static void          window_load_bookmark         (GtkAction          *action,
+                                                   YelpWindow         *window);
 static void          window_open_location         (GtkAction          *action,
                                                    YelpWindow         *window);
 
+static void          app_bookmarks_changed        (YelpApplication    *app,
+                                                   const gchar        *doc_uri,
+                                                   YelpWindow         *window);
+static void          window_set_bookmarks         (YelpWindow         *window,
+                                                   const gchar        *doc_uri);
+
 static void          entry_location_selected      (YelpLocationEntry  *entry,
                                                    YelpWindow         *window);
 static void          entry_completion_selected    (YelpLocationEntry  *entry,
@@ -143,6 +153,11 @@ static const gchar *YELP_UI =
     "<menuitem action='YelpViewGoPrevious'/>"
     "<menuitem action='YelpViewGoNext'/>"
     "</menu>"
+    "<menu action='BookmarksMenu'>"
+    "<menuitem action='AddBookmark'/>"
+    "<separator/>"
+    "<placeholder name='Bookmarks'/>"
+    "</menu>"
     "</menubar>"
     "<accelerator action='OpenLocation'/>"
     "</ui>";
@@ -167,6 +182,11 @@ struct _YelpWindowPrivate {
 
     gulong entry_location_selected;
 
+    gchar *doc_uri;
+
+    GtkActionGroup *bookmark_actions;
+    guint bookmarks_merge_id;
+
     guint resize_signal;
     gint width;
     gint height;
@@ -176,6 +196,7 @@ static const GtkActionEntry entries[] = {
     { "PageMenu",      NULL, N_("_Page")      },
     { "ViewMenu",      NULL, N_("_View")      },
     { "GoMenu",        NULL, N_("_Go")        },
+    { "BookmarksMenu", NULL, N_("_Bookmarks")        },
 
     { "NewWindow", GTK_STOCK_NEW,
       N_("_New Window"),
@@ -187,6 +208,11 @@ static const GtkActionEntry entries[] = {
       "<Control>W",
       NULL,
       G_CALLBACK (window_close) },
+    { "AddBookmark", NULL,
+      N_("_Add Bookmark"),
+      "<Control>D",
+      NULL,
+      G_CALLBACK (window_add_bookmark) },
     { "OpenLocation", NULL,
       N_("Open Location"),
       "<Control>L",
@@ -197,7 +223,7 @@ static const GtkActionEntry entries[] = {
 static void
 yelp_window_init (YelpWindow *window)
 {
-    g_signal_connect (window, "configure-event", window_configure_event, NULL);
+    g_signal_connect (window, "configure-event", G_CALLBACK (window_configure_event), NULL);
 }
 
 static void
@@ -246,6 +272,11 @@ yelp_window_dispose (GObject *object)
         priv->action_group = NULL;
     }
 
+    if (priv->bookmark_actions) {
+        g_object_unref (priv->bookmark_actions);
+        priv->bookmark_actions = NULL;
+    }
+
     if (priv->align_location) {
         g_object_unref (priv->align_location);
         priv->align_location = NULL;
@@ -262,6 +293,8 @@ yelp_window_dispose (GObject *object)
 static void
 yelp_window_finalize (GObject *object)
 {
+    YelpWindowPrivate *priv = GET_PRIV (object);
+    g_free (priv->doc_uri);
     G_OBJECT_CLASS (yelp_window_parent_class)->finalize (object);
 }
 
@@ -323,19 +356,25 @@ window_construct (YelpWindow *window)
 				  entries, G_N_ELEMENTS (entries),
 				  window);
 
+    priv->bookmark_actions = gtk_action_group_new ("BookmarkActions");
+    gtk_action_group_set_translate_func (priv->bookmark_actions, NULL, NULL, NULL);
+
     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),
-                                        1);
+                                        2);
     view_actions = yelp_view_get_action_group (priv->view);
-    gtk_ui_manager_insert_action_group (priv->ui_manager, view_actions, 2);
+    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 (vbox),
                         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);
+    g_signal_connect (priv->application, "bookmarks-changed", G_CALLBACK (app_bookmarks_changed), window);
 
     priv->hbox = gtk_hbox_new (FALSE, 0);
     gtk_box_pack_start (GTK_BOX (vbox), priv->hbox, FALSE, FALSE, 0);
@@ -520,6 +559,107 @@ window_close (GtkAction *action, YelpWindow *window)
 }
 
 static void
+window_add_bookmark (GtkAction  *action,
+                     YelpWindow *window)
+{
+    YelpUri *uri;
+    gchar *icon, *title;
+    YelpWindowPrivate *priv = GET_PRIV (window);
+
+    g_object_get (priv->view,
+                  "yelp-uri", &uri,
+                  "page-icon", &icon,
+                  "page-title", &title,
+                  NULL);
+    yelp_application_add_bookmark (priv->application, uri, icon, title);
+    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,
+                       const gchar     *doc_uri,
+                       YelpWindow      *window)
+{
+    YelpUri *uri;
+    gchar *this_doc_uri;
+    YelpWindowPrivate *priv = GET_PRIV (window);
+
+    g_object_get (priv->view, "yelp-uri", &uri, NULL);
+    this_doc_uri = yelp_uri_get_document_uri (uri);
+
+    if (g_str_equal (this_doc_uri, doc_uri))
+        window_set_bookmarks (window, doc_uri);
+
+    g_free (this_doc_uri);
+    g_object_unref (uri);
+}
+
+static void
+window_set_bookmarks (YelpWindow  *window,
+                      const gchar *doc_uri)
+{
+    GVariant *value;
+    GVariantIter *iter;
+    gchar *page_id, *icon, *title;
+    YelpWindowPrivate *priv = GET_PRIV (window);
+
+    gtk_ui_manager_remove_ui (priv->ui_manager, priv->bookmarks_merge_id);
+
+    value = yelp_application_get_bookmarks (priv->application, doc_uri);
+    g_variant_get (value, "a(sss)", &iter);
+    while (g_variant_iter_loop (iter, "(&s&s&s)", &page_id, &icon, &title)) {
+        GSList *cur;
+        GtkAction *bookmark;
+        gchar *action_id = g_strconcat ("LoadBookmark-", page_id, NULL);
+
+        bookmark = gtk_action_group_get_action (priv->bookmark_actions, action_id);
+        if (bookmark == NULL) {
+            bookmark = gtk_action_new (action_id, title, NULL, NULL);
+            g_signal_connect (bookmark, "activate", window_load_bookmark, window);
+            gtk_action_set_icon_name (bookmark, 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);
+    }
+
+    g_variant_iter_free (iter);
+    g_variant_unref (value);
+}
+
+static void
 window_open_location (GtkAction *action, YelpWindow *window)
 {
     YelpUri *yuri = NULL;
@@ -674,13 +814,23 @@ view_uri_selected (YelpView     *view,
     gchar *iter_uri;
     gboolean cont;
     YelpUri *uri;
-    gchar *struri;
+    gchar *struri, *doc_uri;
     YelpWindowPrivate *priv = GET_PRIV (window);
 
     g_object_get (G_OBJECT (view), "yelp-uri", &uri, NULL);
     if (uri == NULL)
         return;
 
+    doc_uri = yelp_uri_get_document_uri (uri);
+    if (priv->doc_uri == NULL || !g_str_equal (priv->doc_uri, doc_uri)) {
+        window_set_bookmarks (window, doc_uri);
+        g_free (priv->doc_uri);
+        priv->doc_uri = doc_uri;
+    }
+    else {
+        g_free (doc_uri);
+    }
+
     struri = yelp_uri_get_canonical_uri (uri);
 
     cont = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->history), &iter);



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