[Xfce4-commits] <xfce4-appfinder:master> Add bookmarks in the model.

Nick Schermer noreply at xfce.org
Sun Jul 28 21:56:01 CEST 2013


Updating branch refs/heads/master
         to 1f0729aa3e21810f40a80cfed1b476200bd2556d (commit)
       from bb905554342c0442f3011bca4200fd386557147d (commit)

commit 1f0729aa3e21810f40a80cfed1b476200bd2556d
Author: Nick Schermer <nick at xfce.org>
Date:   Sun Jul 28 21:36:45 2013 +0200

    Add bookmarks in the model.

 src/appfinder-category-model.c |    4 +
 src/appfinder-model.c          |  540 ++++++++++++++++++++++++++++++++++------
 src/appfinder-model.h          |   57 +++--
 src/appfinder-window.c         |   62 ++++-
 4 files changed, 559 insertions(+), 104 deletions(-)

diff --git a/src/appfinder-category-model.c b/src/appfinder-category-model.c
index 3f3a060..57e0e55 100644
--- a/src/appfinder-category-model.c
+++ b/src/appfinder-category-model.c
@@ -529,6 +529,10 @@ xfce_appfinder_category_model_set_categories (XfceAppfinderCategoryModel *model,
   model->categories = g_slist_prepend (model->categories, item);
 
   item = g_slice_new0 (CategoryItem);
+  item->directory = xfce_appfinder_model_get_bookmarks_category ();
+  model->categories = g_slist_prepend (model->categories, item);
+
+  item = g_slice_new0 (CategoryItem);
   item->directory = g_object_ref (G_OBJECT (model->all_applications));
   model->categories = g_slist_prepend (model->categories, item);
 
diff --git a/src/appfinder-model.c b/src/appfinder-model.c
index 50de137..355c2eb 100644
--- a/src/appfinder-model.c
+++ b/src/appfinder-model.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 Nick Schermer <nick at xfce.org>
+ * Copyright (C) 2011-2013 Nick Schermer <nick at xfce.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
@@ -33,64 +33,73 @@
 
 
 
-#define HISTORY_PATH "xfce4/xfce4-appfinder/history"
-
-
-
-static void               xfce_appfinder_model_tree_model_init       (GtkTreeModelIface        *iface);
-static void               xfce_appfinder_model_get_property          (GObject                  *object,
-                                                                      guint                     prop_id,
-                                                                      GValue                   *value,
-                                                                      GParamSpec               *pspec);
-static void               xfce_appfinder_model_set_property          (GObject                  *object,
-                                                                      guint                     prop_id,
-                                                                      const GValue             *value,
-                                                                      GParamSpec               *pspec);
-static void               xfce_appfinder_model_finalize              (GObject                  *object);
-static GtkTreeModelFlags  xfce_appfinder_model_get_flags             (GtkTreeModel             *tree_model);
-static gint               xfce_appfinder_model_get_n_columns         (GtkTreeModel             *tree_model);
-static GType              xfce_appfinder_model_get_column_type       (GtkTreeModel             *tree_model,
-                                                                      gint                      column);
-static gboolean           xfce_appfinder_model_get_iter              (GtkTreeModel             *tree_model,
-                                                                      GtkTreeIter              *iter,
-                                                                      GtkTreePath              *path);
-static GtkTreePath       *xfce_appfinder_model_get_path              (GtkTreeModel             *tree_model,
-                                                                      GtkTreeIter              *iter);
-static void               xfce_appfinder_model_get_value             (GtkTreeModel             *tree_model,
-                                                                      GtkTreeIter              *iter,
-                                                                      gint                      column,
-                                                                      GValue                   *value);
-static gboolean           xfce_appfinder_model_iter_next             (GtkTreeModel             *tree_model,
-                                                                      GtkTreeIter              *iter);
-static gboolean           xfce_appfinder_model_iter_children         (GtkTreeModel             *tree_model,
-                                                                      GtkTreeIter              *iter,
-                                                                      GtkTreeIter              *parent);
-static gboolean           xfce_appfinder_model_iter_has_child        (GtkTreeModel             *tree_model,
-                                                                      GtkTreeIter              *iter);
-static gint               xfce_appfinder_model_iter_n_children       (GtkTreeModel             *tree_model,
-                                                                      GtkTreeIter              *iter);
-static gboolean           xfce_appfinder_model_iter_nth_child        (GtkTreeModel             *tree_model,
-                                                                      GtkTreeIter              *iter,
-                                                                      GtkTreeIter              *parent,
-                                                                      gint                      n);
-static gboolean           xfce_appfinder_model_iter_parent           (GtkTreeModel             *tree_model,
-                                                                      GtkTreeIter              *iter,
-                                                                      GtkTreeIter              *child);
-static void               xfce_appfinder_model_menu_changed          (GarconMenu               *menu,
-                                                                      XfceAppfinderModel       *model);
-static gpointer           xfce_appfinder_model_collect_thread        (gpointer                  user_data);
-static void               xfce_appfinder_model_item_changed          (GarconMenuItem           *menu_item,
-                                                                      XfceAppfinderModel       *model);
-static void               xfce_appfinder_model_item_free             (gpointer                  data,
-                                                                      XfceAppfinderModel       *model);
-static void               xfce_appfinder_model_history_changed       (GFileMonitor             *monitor,
-                                                                      GFile                    *file,
-                                                                      GFile                    *other_file,
-                                                                      GFileMonitorEvent         event_type,
-                                                                      XfceAppfinderModel       *model);
-static void               xfce_appfinder_model_history_monitor_stop  (XfceAppfinderModel       *model);
-static void               xfce_appfinder_model_history_monitor       (XfceAppfinderModel       *model,
-                                                                      const gchar              *path);
+#define HISTORY_PATH   "xfce4/xfce4-appfinder/history"
+#define BOOKMARKS_PATH "xfce4/appfinder/bookmarks"
+
+
+
+static void               xfce_appfinder_model_tree_model_init        (GtkTreeModelIface        *iface);
+static void               xfce_appfinder_model_get_property           (GObject                  *object,
+                                                                       guint                     prop_id,
+                                                                       GValue                   *value,
+                                                                       GParamSpec               *pspec);
+static void               xfce_appfinder_model_set_property           (GObject                  *object,
+                                                                       guint                     prop_id,
+                                                                       const GValue             *value,
+                                                                       GParamSpec               *pspec);
+static void               xfce_appfinder_model_finalize               (GObject                  *object);
+static GtkTreeModelFlags  xfce_appfinder_model_get_flags              (GtkTreeModel             *tree_model);
+static gint               xfce_appfinder_model_get_n_columns          (GtkTreeModel             *tree_model);
+static GType              xfce_appfinder_model_get_column_type        (GtkTreeModel             *tree_model,
+                                                                       gint                      column);
+static gboolean           xfce_appfinder_model_get_iter               (GtkTreeModel             *tree_model,
+                                                                       GtkTreeIter              *iter,
+                                                                       GtkTreePath              *path);
+static GtkTreePath       *xfce_appfinder_model_get_path               (GtkTreeModel             *tree_model,
+                                                                       GtkTreeIter              *iter);
+static void               xfce_appfinder_model_get_value              (GtkTreeModel             *tree_model,
+                                                                       GtkTreeIter              *iter,
+                                                                       gint                      column,
+                                                                       GValue                   *value);
+static gboolean           xfce_appfinder_model_iter_next              (GtkTreeModel             *tree_model,
+                                                                       GtkTreeIter              *iter);
+static gboolean           xfce_appfinder_model_iter_children          (GtkTreeModel             *tree_model,
+                                                                       GtkTreeIter              *iter,
+                                                                       GtkTreeIter              *parent);
+static gboolean           xfce_appfinder_model_iter_has_child         (GtkTreeModel             *tree_model,
+                                                                       GtkTreeIter              *iter);
+static gint               xfce_appfinder_model_iter_n_children        (GtkTreeModel             *tree_model,
+                                                                       GtkTreeIter              *iter);
+static gboolean           xfce_appfinder_model_iter_nth_child         (GtkTreeModel             *tree_model,
+                                                                       GtkTreeIter              *iter,
+                                                                       GtkTreeIter              *parent,
+                                                                       gint                      n);
+static gboolean           xfce_appfinder_model_iter_parent            (GtkTreeModel             *tree_model,
+                                                                       GtkTreeIter              *iter,
+                                                                       GtkTreeIter              *child);
+static void               xfce_appfinder_model_menu_changed           (GarconMenu               *menu,
+                                                                       XfceAppfinderModel       *model);
+static gpointer           xfce_appfinder_model_collect_thread         (gpointer                  user_data);
+static void               xfce_appfinder_model_item_changed           (GarconMenuItem           *menu_item,
+                                                                       XfceAppfinderModel       *model);
+static void               xfce_appfinder_model_item_free              (gpointer                  data,
+                                                                       XfceAppfinderModel       *model);
+static void               xfce_appfinder_model_history_changed        (GFileMonitor             *monitor,
+                                                                       GFile                    *file,
+                                                                       GFile                    *other_file,
+                                                                       GFileMonitorEvent         event_type,
+                                                                       XfceAppfinderModel       *model);
+static void               xfce_appfinder_model_history_monitor_stop   (XfceAppfinderModel       *model);
+static void               xfce_appfinder_model_history_monitor        (XfceAppfinderModel       *model,
+                                                                       const gchar              *path);
+static void               xfce_appfinder_model_bookmarks_changed      (GFileMonitor             *monitor,
+                                                                       GFile                    *file,
+                                                                       GFile                    *other_file,
+                                                                       GFileMonitorEvent         event_type,
+                                                                       XfceAppfinderModel       *model);
+static void               xfce_appfinder_model_bookmarks_monitor_stop (XfceAppfinderModel       *model);
+static void               xfce_appfinder_model_bookmarks_monitor      (XfceAppfinderModel       *model,
+                                                                       const gchar              *path);
 
 
 
@@ -102,11 +111,18 @@ struct _XfceAppfinderModelClass
 struct _XfceAppfinderModel
 {
   GObject                __parent__;
+
   gint                   stamp;
 
   GSList                *items;
   GHashTable            *items_hash;
 
+  GHashTable            *bookmarks_hash;
+
+  GFileMonitor          *bookmarks_monitor;
+  GFile                 *bookmarks_file;
+  guint64                bookmarks_mtime;
+
   GarconMenu            *menu;
   guint                  menu_changed_idle_id;
 
@@ -139,6 +155,7 @@ typedef struct
   gchar          *command;
   gchar          *tooltip;
   guint           not_visible : 1;
+  guint           is_bookmark : 1;
 
   GdkPixbuf      *icon;
   GdkPixbuf      *icon_large;
@@ -211,6 +228,7 @@ xfce_appfinder_model_init (XfceAppfinderModel *model)
   /* generate a unique stamp */
   model->stamp = g_random_int ();
   model->items_hash = g_hash_table_new (g_str_hash, g_str_equal);
+  model->bookmarks_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
   model->icon_size = XFCE_APPFINDER_ICON_SIZE_DEFAULT_ITEM;
   model->command_icon = xfce_appfinder_model_load_pixbuf (GTK_STOCK_EXECUTE, model->icon_size);
   model->command_icon_large = xfce_appfinder_model_load_pixbuf (GTK_STOCK_EXECUTE, XFCE_APPFINDER_ICON_SIZE_48);
@@ -247,6 +265,7 @@ xfce_appfinder_model_tree_model_init (GtkTreeModelIface *iface)
 }
 
 
+
 static void
 xfce_appfinder_model_get_property (GObject      *object,
                                    guint         prop_id,
@@ -323,6 +342,9 @@ xfce_appfinder_model_finalize (GObject *object)
   /* stop history file monitoring */
   xfce_appfinder_model_history_monitor_stop (model);
 
+  /* stop monitoring bookmarks file */
+  xfce_appfinder_model_bookmarks_monitor_stop (model);
+
   g_signal_handlers_disconnect_by_func (G_OBJECT (model->menu),
       G_CALLBACK (xfce_appfinder_model_menu_changed), model);
   g_object_unref (G_OBJECT (model->menu));
@@ -338,6 +360,7 @@ xfce_appfinder_model_finalize (GObject *object)
   g_slist_free (model->categories);
 
   g_hash_table_destroy (model->items_hash);
+  g_hash_table_destroy (model->bookmarks_hash);
 
   g_object_unref (G_OBJECT (model->command_icon_large));
   g_object_unref (G_OBJECT (model->command_icon));
@@ -383,6 +406,9 @@ xfce_appfinder_model_get_column_type (GtkTreeModel *tree_model,
     case XFCE_APPFINDER_MODEL_COLUMN_ICON_LARGE:
       return GDK_TYPE_PIXBUF;
 
+    case XFCE_APPFINDER_MODEL_COLUMN_BOOKMARK:
+      return G_TYPE_BOOLEAN;
+
     default:
       g_assert_not_reached ();
       return G_TYPE_INVALID;
@@ -566,6 +592,11 @@ xfce_appfinder_model_get_value (GtkTreeModel *tree_model,
         g_value_take_string (value, garcon_menu_item_get_uri (item->item));
       break;
 
+    case XFCE_APPFINDER_MODEL_COLUMN_BOOKMARK:
+      g_value_init (value, G_TYPE_BOOLEAN);
+      g_value_set_boolean (value, item->is_bookmark);
+      break;
+
     default:
       g_assert_not_reached ();
       break;
@@ -699,6 +730,7 @@ xfce_appfinder_model_collect_idle (gpointer user_data)
   GSList             *li, *lnext;
   GSList             *tmp;
   ModelItem          *item;
+  const gchar        *desktop_id;
 
   appfinder_return_val_if_fail (XFCE_IS_APPFINDER_MODEL (model), FALSE);
   appfinder_return_val_if_fail (model->items == NULL, FALSE);
@@ -734,8 +766,13 @@ xfce_appfinder_model_collect_idle (gpointer user_data)
       /* watch changes */
       item = li->data;
       if (item->item != NULL)
-        g_signal_connect (G_OBJECT (item->item), "changed",
-                          G_CALLBACK (xfce_appfinder_model_item_changed), model);
+        {
+          g_signal_connect (G_OBJECT (item->item), "changed",
+                            G_CALLBACK (xfce_appfinder_model_item_changed), model);
+
+          desktop_id = garcon_menu_item_get_desktop_id (item->item);
+          item->is_bookmark = g_hash_table_lookup (model->bookmarks_hash, desktop_id) != NULL;
+        }
 
       /* insert in hash table */
       if (G_LIKELY (item->command != NULL))
@@ -881,6 +918,7 @@ xfce_appfinder_model_item_changed (GarconMenuItem     *menu_item,
   GtkTreePath *path;
   GPtrArray   *categories;
   gboolean     old_not_visible;
+  const gchar *desktop_id;
 
   /* lookup the item in the list */
   for (li = model->items, idx = 0; li != NULL; li = li->next, idx++)
@@ -901,6 +939,13 @@ xfce_appfinder_model_item_changed (GarconMenuItem     *menu_item,
           item->categories = categories;
           li->data = item;
 
+          /* check if the item should be a bookmark */
+          desktop_id = garcon_menu_item_get_desktop_id (menu_item);
+          if (desktop_id != NULL)
+            item->is_bookmark = g_hash_table_lookup (model->bookmarks_hash, desktop_id) != NULL;
+          else
+            item->is_bookmark = FALSE;
+
           if (G_LIKELY (item->command != NULL))
             g_hash_table_insert (model->items_hash, item->command, item);
 
@@ -992,7 +1037,7 @@ xfce_appfinder_model_history_remove_items (XfceAppfinderModel *model)
 
 
 static guint64
-xfce_appfinder_model_history_get_mtime (GFile *file)
+xfce_appfinder_model_file_get_mtime (GFile *file)
 {
   GFileInfo *info;
   guint64    mtime = 0;
@@ -1008,11 +1053,12 @@ xfce_appfinder_model_history_get_mtime (GFile *file)
         }
     }
 
-  /* never return 1, because we use that for an empty history */
+  /* never return 1, because we use that for an empty file */
   return MAX (mtime, 1);
 }
 
 
+
 static void
 xfce_appfinder_model_history_insert (XfceAppfinderModel *model,
                                      const gchar        *command)
@@ -1079,7 +1125,7 @@ xfce_appfinder_model_history_changed (GFileMonitor       *monitor,
       break;
 
     case G_FILE_MONITOR_EVENT_CREATED:
-      mtime = xfce_appfinder_model_history_get_mtime (model->history_file);
+      mtime = xfce_appfinder_model_file_get_mtime (model->history_file);
       if (mtime > model->history_mtime)
         {
           /* read the new file and update the commands */
@@ -1182,7 +1228,185 @@ xfce_appfinder_model_history_monitor (XfceAppfinderModel *model,
         }
     }
 
-  model->history_mtime = xfce_appfinder_model_history_get_mtime (file);
+  model->history_mtime = xfce_appfinder_model_file_get_mtime (file);
+
+  g_object_unref (G_OBJECT (file));
+}
+
+
+static void
+xfce_appfinder_model_bookmarks_collect (XfceAppfinderModel *model,
+                                        GMappedFile        *mmap)
+{
+  gchar *line;
+  gchar *end;
+  gchar *contents;
+
+  /* empty the database */
+  g_hash_table_remove_all (model->bookmarks_hash);
+
+  contents = g_mapped_file_get_contents (mmap);
+  if (contents == NULL)
+    return;
+
+  /* walk the file */
+  for (;!g_cancellable_is_cancelled (model->collect_cancelled);)
+    {
+      end = strchr (contents, '\n');
+      if (G_UNLIKELY (end == NULL))
+        break;
+
+      if (end != contents)
+        {
+          /* look for new commands */
+          line = g_strndup (contents, end - contents);
+          g_hash_table_insert (model->bookmarks_hash, line, GUINT_TO_POINTER (1));
+        }
+      contents = end + 1;
+    }
+}
+
+
+
+static void
+xfce_appfinder_model_bookmarks_changed (GFileMonitor       *monitor,
+                                        GFile              *file,
+                                        GFile              *other_file,
+                                        GFileMonitorEvent   event_type,
+                                        XfceAppfinderModel *model)
+{
+  guint64      mtime;
+  gchar       *filename;
+  GError      *error = NULL;
+  GMappedFile *mmap;
+  gboolean     is_bookmark;
+  ModelItem   *item;
+  GSList      *li;
+  const gchar *desktop_id;
+  gint         idx;
+  GtkTreePath *path;
+  GtkTreeIter  iter;
+
+  appfinder_return_if_fail (XFCE_IS_APPFINDER_MODEL (model));
+  appfinder_return_if_fail (model->bookmarks_monitor == monitor);
+  appfinder_return_if_fail (G_IS_FILE_MONITOR (monitor));
+  appfinder_return_if_fail (G_IS_FILE (model->bookmarks_file));
+
+  switch (event_type)
+    {
+    case G_FILE_MONITOR_EVENT_DELETED:
+      /* TODO */
+      break;
+
+    case G_FILE_MONITOR_EVENT_CREATED:
+      mtime = xfce_appfinder_model_file_get_mtime (model->bookmarks_file);
+      if (mtime > model->bookmarks_mtime)
+        {
+          APPFINDER_DEBUG ("bookmarks file changed");
+
+          /* read the new file and update the commands */
+          filename = g_file_get_path (file);
+          mmap = g_mapped_file_new (filename, FALSE, &error);
+          g_free (filename);
+
+          if (G_LIKELY (mmap != NULL))
+            {
+              xfce_appfinder_model_bookmarks_collect (model, mmap);
+              g_mapped_file_unref (mmap);
+
+              /* update the model items */
+              for (idx = 0, li = model->items; li != NULL; li = li->next, idx++)
+                {
+                  item = li->data;
+                  if (item->item == NULL)
+                    continue;
+
+                  /* check if the item should be a bookmark */
+                  desktop_id = garcon_menu_item_get_desktop_id (item->item);
+                  if (desktop_id != NULL)
+                    is_bookmark = g_hash_table_lookup (model->bookmarks_hash, desktop_id) != NULL;
+                  else
+                    is_bookmark = FALSE;
+
+                  if (item->is_bookmark != is_bookmark)
+                    {
+                      APPFINDER_DEBUG ("bookmark %s changed", desktop_id);
+
+                      item->is_bookmark = is_bookmark;
+
+                      /* let model know what happened */
+                      path = gtk_tree_path_new_from_indices (idx, -1);
+                      ITER_INIT (iter, model->stamp, li);
+                      gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
+                      gtk_tree_path_free (path);
+                    }
+                }
+            }
+        }
+      break;
+
+    default:
+      break;
+    }
+}
+
+
+
+static void
+xfce_appfinder_model_bookmarks_monitor_stop (XfceAppfinderModel *model)
+{
+  if (model->bookmarks_monitor != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (model->bookmarks_monitor,
+          G_CALLBACK (xfce_appfinder_model_bookmarks_changed), model);
+
+      g_object_unref (G_OBJECT (model->bookmarks_monitor));
+      model->bookmarks_monitor = NULL;
+    }
+
+  if (model->bookmarks_file != NULL)
+    {
+      g_object_unref (G_OBJECT (model->bookmarks_file));
+      model->bookmarks_file = NULL;
+    }
+}
+
+
+
+static void
+xfce_appfinder_model_bookmarks_monitor (XfceAppfinderModel *model,
+                                        const gchar        *path)
+{
+  GFile  *file;
+  GError *error = NULL;
+
+  file = g_file_new_for_path (path);
+
+  if (model->bookmarks_file == NULL
+      || model->bookmarks_monitor == NULL
+      || !g_file_equal (file, model->bookmarks_file))
+    {
+      xfce_appfinder_model_bookmarks_monitor_stop (model);
+
+      /* monitor the file for changes */
+      model->bookmarks_monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, model->collect_cancelled, &error);
+      appfinder_refcount_debug_add (G_OBJECT (model->bookmarks_monitor), "bookmarks file monitor");
+      if (model->bookmarks_monitor != NULL)
+        {
+          APPFINDER_DEBUG ("monitor bookmarks file %s", path);
+
+          model->bookmarks_file = g_object_ref (G_OBJECT (file));
+          g_signal_connect (G_OBJECT (model->bookmarks_monitor), "changed",
+              G_CALLBACK (xfce_appfinder_model_bookmarks_changed), model);
+        }
+      else
+        {
+          g_warning ("Failed to setup a monitor for %s: %s", path, error->message);
+          g_error_free (error);
+        }
+    }
+
+  model->bookmarks_mtime = xfce_appfinder_model_file_get_mtime (file);
 
   g_object_unref (G_OBJECT (file));
 }
@@ -1269,8 +1493,10 @@ xfce_appfinder_model_collect_item (const gchar    *desktop_id,
 
       item->categories = g_ptr_array_new_with_free_func (g_object_unref);
       if (context->category != NULL)
-        g_ptr_array_add (item->categories,
-                         g_object_ref (G_OBJECT (context->category)));
+        {
+          g_ptr_array_add (item->categories,
+                           g_object_ref (G_OBJECT (context->category)));
+        }
 
       context->items = g_slist_prepend (context->items, item);
       g_hash_table_insert (context->desktop_ids, (gchar *) desktop_id, item);
@@ -1592,7 +1818,7 @@ xfce_appfinder_model_collect_thread (gpointer user_data)
   XfceAppfinderModel *model = XFCE_APPFINDER_MODEL (user_data);
   GError             *error = NULL;
   gchar              *filename;
-  GMappedFile        *history;
+  GMappedFile        *mmap;
 
   appfinder_return_val_if_fail (GARCON_IS_MENU (model->menu), NULL);
   appfinder_return_val_if_fail (model->collect_items == NULL, NULL);
@@ -1605,8 +1831,10 @@ xfce_appfinder_model_collect_thread (gpointer user_data)
     {
       if (garcon_menu_load (model->menu, model->collect_cancelled, &error))
         {
-          xfce_appfinder_model_collect_menu (model->menu, model->collect_cancelled,
-                                             &model->collect_items, &model->collect_categories);
+          xfce_appfinder_model_collect_menu (model->menu,
+                                             model->collect_cancelled,
+                                             &model->collect_items,
+                                             &model->collect_categories);
         }
       else
         {
@@ -1621,11 +1849,11 @@ xfce_appfinder_model_collect_thread (gpointer user_data)
     {
       APPFINDER_DEBUG ("load commands from %s", filename);
 
-      history = g_mapped_file_new (filename, FALSE, &error);
-      if (G_LIKELY (history != NULL))
+      mmap = g_mapped_file_new (filename, FALSE, &error);
+      if (G_LIKELY (mmap != NULL))
         {
-          xfce_appfinder_model_collect_history (model, history);
-          g_mapped_file_unref (history);
+          xfce_appfinder_model_collect_history (model, mmap);
+          g_mapped_file_unref (mmap);
         }
       else
         {
@@ -1639,6 +1867,30 @@ xfce_appfinder_model_collect_thread (gpointer user_data)
       g_free (filename);
     }
 
+  /* load bookmarks */
+  filename = xfce_resource_lookup (XFCE_RESOURCE_CONFIG, BOOKMARKS_PATH);
+  if (G_LIKELY (filename != NULL))
+    {
+      APPFINDER_DEBUG ("load bookmarks from %s", filename);
+
+      mmap = g_mapped_file_new (filename, FALSE, &error);
+      if (G_LIKELY (mmap != NULL))
+        {
+          xfce_appfinder_model_bookmarks_collect (model, mmap);
+          g_mapped_file_unref (mmap);
+        }
+      else
+        {
+          g_warning ("Failed to open bookmarks file: %s", error->message);
+          g_clear_error (&error);
+        }
+
+      /* start monitoring and update mtime */
+      xfce_appfinder_model_bookmarks_monitor (model, filename);
+
+      g_free (filename);
+    }
+
   if (model->collect_items != NULL
       && !g_cancellable_is_cancelled (model->collect_cancelled))
     {
@@ -1732,6 +1984,8 @@ xfce_appfinder_model_get_visible (XfceAppfinderModel        *model,
                                   const gchar               *string)
 {
   ModelItem *item;
+  GarconMenuDirectory *bookmarks;
+  gboolean             in_category;
 
   appfinder_return_val_if_fail (XFCE_IS_APPFINDER_MODEL (model), FALSE);
   appfinder_return_val_if_fail (iter->stamp == model->stamp, FALSE);
@@ -1747,9 +2001,22 @@ xfce_appfinder_model_get_visible (XfceAppfinderModel        *model,
       if (item->not_visible)
         return FALSE;
 
-      if (category != NULL
-          && !xfce_appfinder_model_ptr_array_find (item->categories, category))
-        return FALSE;
+      if (category != NULL)
+        {
+          if (!xfce_appfinder_model_ptr_array_find (item->categories, category))
+            {
+              in_category = FALSE;
+              if (item->is_bookmark)
+                {
+                  bookmarks = xfce_appfinder_model_get_bookmarks_category ();
+                  in_category = (bookmarks == category);
+                  g_object_unref (G_OBJECT (bookmarks));
+                }
+
+              if (!in_category)
+                return FALSE;
+            }
+        }
 
       if (string != NULL
           && item->key != NULL)
@@ -1979,6 +2246,7 @@ xfce_appfinder_model_save_command (XfceAppfinderModel  *model,
   static gsize  old_len = 0;
 
   appfinder_return_val_if_fail (XFCE_IS_APPFINDER_MODEL (model), FALSE);
+  appfinder_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
   if (!IS_STRING (command)
       || g_hash_table_lookup (model->items_hash, command) != NULL)
@@ -2142,6 +2410,100 @@ xfce_appfinder_model_history_clear (XfceAppfinderModel *model)
 
 
 
+gboolean
+xfce_appfinder_model_bookmark_toggle (XfceAppfinderModel  *model,
+                                      const gchar         *desktop_id,
+                                      GError             **error)
+{
+  ModelItem    *item;
+  GSList       *li;
+  const gchar  *desktop_id2;
+  static gsize  old_len = 0;
+  GString      *contents;
+  gchar        *filename;
+  gboolean      succeed;
+  GtkTreePath  *path;
+  gint          idx;
+  GtkTreeIter   iter;
+
+  appfinder_return_val_if_fail (XFCE_IS_APPFINDER_MODEL (model), FALSE);
+  appfinder_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  appfinder_return_val_if_fail (desktop_id != NULL, FALSE);
+
+  if (g_hash_table_lookup (model->bookmarks_hash, desktop_id) == NULL)
+    g_hash_table_insert (model->bookmarks_hash, g_strdup (desktop_id), GUINT_TO_POINTER (1));
+  else
+    g_hash_table_remove (model->bookmarks_hash, desktop_id);
+
+  /* string to store custom commands */
+  contents = g_string_sized_new (old_len);
+
+  /* update the model items */
+  for (idx = 0, li = model->items; li != NULL; li = li->next, idx++)
+    {
+      item = li->data;
+      if (item->item == NULL)
+        continue;
+
+      /* find the item we're trying to add/remove */
+      if (desktop_id != NULL)
+        {
+          desktop_id2 = garcon_menu_item_get_desktop_id (item->item);
+          if (desktop_id2 != NULL
+              && strcmp (desktop_id2, desktop_id) == 0)
+            {
+              /* toggle state */
+              item->is_bookmark = !item->is_bookmark;
+
+              /* stop searching, continue collecting */
+              desktop_id = NULL;
+
+              /* update model */
+              path = gtk_tree_path_new_from_indices (idx, -1);
+              ITER_INIT (iter, model->stamp, li);
+              gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
+              gtk_tree_path_free (path);
+            }
+        }
+
+      /* collect bookmarked items */
+      if (item->is_bookmark)
+        {
+          desktop_id2 = garcon_menu_item_get_desktop_id (item->item);
+          if (G_LIKELY (desktop_id2 != NULL))
+            {
+              g_string_append (contents, desktop_id2);
+              g_string_append_c (contents, '\n');
+            }
+        }
+    }
+
+  APPFINDER_DEBUG ("saving bookmarks");
+
+  /* write new bookmarks */
+  filename = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, BOOKMARKS_PATH, TRUE);
+  if (G_LIKELY (filename != NULL))
+    succeed = g_file_set_contents (filename, contents->str, contents->len, error);
+  else
+    g_set_error_literal (error, 0, 0, "Unable to create bookmarks file");
+
+  if (succeed)
+    {
+      /* possible restart monitoring and update mtime */
+      xfce_appfinder_model_bookmarks_monitor (model, filename);
+    }
+
+  /* optimization for next run */
+  old_len = contents->allocated_len;
+
+  g_free (filename);
+  g_string_free (contents, TRUE);
+
+  return succeed;
+}
+
+
+
 GarconMenuDirectory *
 xfce_appfinder_model_get_command_category (void)
 {
@@ -2163,3 +2525,27 @@ xfce_appfinder_model_get_command_category (void)
 
   return category;
 }
+
+
+
+GarconMenuDirectory *
+xfce_appfinder_model_get_bookmarks_category (void)
+{
+  static GarconMenuDirectory *category = NULL;
+
+  if (G_LIKELY (category != NULL))
+    {
+      g_object_ref (G_OBJECT (category));
+    }
+  else
+    {
+      category = g_object_new (GARCON_TYPE_MENU_DIRECTORY,
+                               "name", _("Bookmarks"),
+                               "icon-name", "user-bookmarks",
+                               NULL);
+      appfinder_refcount_debug_add (G_OBJECT (category), "bookmarks");
+      g_object_add_weak_pointer (G_OBJECT (category), (gpointer) &category);
+    }
+
+  return category;
+}
diff --git a/src/appfinder-model.h b/src/appfinder-model.h
index 2b31cf8..9a96f2e 100644
--- a/src/appfinder-model.h
+++ b/src/appfinder-model.h
@@ -42,6 +42,7 @@ enum
   XFCE_APPFINDER_MODEL_COLUMN_ICON_LARGE,
   XFCE_APPFINDER_MODEL_COLUMN_COMMAND,
   XFCE_APPFINDER_MODEL_COLUMN_URI,
+  XFCE_APPFINDER_MODEL_COLUMN_BOOKMARK,
   XFCE_APPFINDER_MODEL_COLUMN_TOOLTIP,
   XFCE_APPFINDER_MODEL_N_COLUMNS,
 };
@@ -64,42 +65,48 @@ XfceAppfinderIconSize;
 
 
 
-GType                xfce_appfinder_model_get_type             (void) G_GNUC_CONST;
+GType                xfce_appfinder_model_get_type               (void) G_GNUC_CONST;
 
-XfceAppfinderModel  *xfce_appfinder_model_get                  (void) G_GNUC_MALLOC;
+XfceAppfinderModel  *xfce_appfinder_model_get                    (void) G_GNUC_MALLOC;
 
-GSList              *xfce_appfinder_model_get_categories       (XfceAppfinderModel        *model);
+GSList              *xfce_appfinder_model_get_categories         (XfceAppfinderModel        *model);
 
-gboolean             xfce_appfinder_model_get_visible          (XfceAppfinderModel        *model,
-                                                                const GtkTreeIter         *iter,
-                                                                const GarconMenuDirectory *category,
-                                                                const gchar               *string);
+gboolean             xfce_appfinder_model_get_visible            (XfceAppfinderModel        *model,
+                                                                  const GtkTreeIter         *iter,
+                                                                  const GarconMenuDirectory *category,
+                                                                  const gchar               *string);
 
-gboolean             xfce_appfinder_model_get_visible_command  (XfceAppfinderModel        *model,
-                                                                const GtkTreeIter         *iter,
-                                                                const gchar               *string);
+gboolean             xfce_appfinder_model_get_visible_command    (XfceAppfinderModel        *model,
+                                                                  const GtkTreeIter         *iter,
+                                                                  const gchar               *string);
 
-gboolean             xfce_appfinder_model_execute              (XfceAppfinderModel        *model,
-                                                                const GtkTreeIter         *iter,
-                                                                GdkScreen                 *screen,
-                                                                gboolean                  *is_regular_command,
-                                                                GError                   **error);
+gboolean             xfce_appfinder_model_execute                (XfceAppfinderModel        *model,
+                                                                  const GtkTreeIter         *iter,
+                                                                  GdkScreen                 *screen,
+                                                                  gboolean                  *is_regular_command,
+                                                                  GError                   **error);
 
-GdkPixbuf           *xfce_appfinder_model_load_pixbuf          (const gchar               *icon_name,
-                                                                XfceAppfinderIconSize      icon_size) G_GNUC_MALLOC;
+GdkPixbuf           *xfce_appfinder_model_load_pixbuf            (const gchar               *icon_name,
+                                                                  XfceAppfinderIconSize      icon_size) G_GNUC_MALLOC;
 
-gboolean             xfce_appfinder_model_save_command         (XfceAppfinderModel        *model,
-                                                                const gchar               *command,
-                                                                GError                   **error);
+gboolean             xfce_appfinder_model_save_command           (XfceAppfinderModel        *model,
+                                                                  const gchar               *command,
+                                                                  GError                   **error);
 
-GdkPixbuf           *xfce_appfinder_model_get_icon_for_command (XfceAppfinderModel        *model,
-                                                                const gchar               *command);
+GdkPixbuf           *xfce_appfinder_model_get_icon_for_command   (XfceAppfinderModel        *model,
+                                                                  const gchar               *command);
 
-void                 xfce_appfinder_model_icon_theme_changed   (XfceAppfinderModel        *model);
+void                 xfce_appfinder_model_icon_theme_changed     (XfceAppfinderModel        *model);
 
-GarconMenuDirectory *xfce_appfinder_model_get_command_category (void);
+void                 xfce_appfinder_model_history_clear          (XfceAppfinderModel        *model);
 
-void                 xfce_appfinder_model_history_clear        (XfceAppfinderModel        *model);
+gboolean             xfce_appfinder_model_bookmark_toggle        (XfceAppfinderModel        *model,
+                                                                  const gchar               *desktop_id,
+                                                                  GError                   **error);
+
+GarconMenuDirectory *xfce_appfinder_model_get_command_category   (void);
+
+GarconMenuDirectory *xfce_appfinder_model_get_bookmarks_category (void);
 
 G_END_DECLS
 
diff --git a/src/appfinder-window.c b/src/appfinder-window.c
index b5ca272..f656a1c 100644
--- a/src/appfinder-window.c
+++ b/src/appfinder-window.c
@@ -813,6 +813,42 @@ xfce_appfinder_window_view_get_selected (XfceAppfinderWindow  *window,
 
 
 static void
+xfce_appfinder_window_popup_menu_toggle_bookmark (GtkWidget           *mi,
+                                                  XfceAppfinderWindow *window)
+{
+  const gchar  *uri;
+  GFile        *gfile;
+  gchar        *desktop_id;
+  GtkWidget    *menu = gtk_widget_get_parent (mi);
+  GtkTreeModel *filter;
+  GtkTreeModel *model;
+  GError       *error = NULL;
+
+  uri = g_object_get_data (G_OBJECT (menu), "uri");
+  if (uri != NULL)
+    {
+      gfile = g_file_new_for_uri (uri);
+      desktop_id = g_file_get_basename (gfile);
+      g_object_unref (G_OBJECT (gfile));
+
+      /* toggle bookmarks */
+      filter = g_object_get_data (G_OBJECT (menu), "model");
+      model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
+      xfce_appfinder_model_bookmark_toggle (XFCE_APPFINDER_MODEL (model), desktop_id, &error);
+
+      g_free (desktop_id);
+
+      if (G_UNLIKELY (error != NULL))
+        {
+          g_printerr ("%s: failed to save bookmarks: %s\n", G_LOG_DOMAIN, error->message);
+          g_error_free (error);
+        }
+    }
+}
+
+
+
+static void
 xfce_appfinder_window_popup_menu_execute (GtkWidget           *mi,
                                           XfceAppfinderWindow *window)
 {
@@ -960,14 +996,18 @@ xfce_appfinder_window_popup_menu (GtkWidget           *view,
   gchar        *title;
   gchar        *uri;
   GtkWidget    *mi;
+  GtkWidget    *image;
   gchar        *path;
   gboolean      uri_is_local;
+  gboolean      is_bookmark;
 
   if (xfce_appfinder_window_view_get_selected (window, &model, &iter))
     {
       gtk_tree_model_get (model, &iter,
                           XFCE_APPFINDER_MODEL_COLUMN_TITLE, &title,
-                          XFCE_APPFINDER_MODEL_COLUMN_URI, &uri, -1);
+                          XFCE_APPFINDER_MODEL_COLUMN_URI, &uri,
+                          XFCE_APPFINDER_MODEL_COLUMN_BOOKMARK, &is_bookmark,
+                          -1);
 
       /* custom command don't have an uri */
       if (uri == NULL)
@@ -981,6 +1021,7 @@ xfce_appfinder_window_popup_menu (GtkWidget           *view,
       menu = gtk_menu_new ();
       g_object_set_data_full (G_OBJECT (menu), "uri", uri, g_free);
       g_object_set_data_full (G_OBJECT (menu), "name", title, g_free);
+      g_object_set_data (G_OBJECT (menu), "model", model);
       g_signal_connect (G_OBJECT (menu), "selection-done",
           G_CALLBACK (gtk_widget_destroy), NULL);
 
@@ -993,6 +1034,22 @@ xfce_appfinder_window_popup_menu (GtkWidget           *view,
       gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
       gtk_widget_show (mi);
 
+      mi = gtk_image_menu_item_new_with_mnemonic (is_bookmark ? _("Remove From Bookmarks") : _("Add to Bookmarks"));
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
+      g_signal_connect (G_OBJECT (mi), "activate",
+          G_CALLBACK (xfce_appfinder_window_popup_menu_toggle_bookmark), window);
+      gtk_widget_show (mi);
+
+      if (is_bookmark)
+        image = gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU);
+      else
+        image = gtk_image_new_from_icon_name ("bookmark-new", GTK_ICON_SIZE_MENU);
+      gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), image);
+
+      mi = gtk_separator_menu_item_new ();
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
+      gtk_widget_show (mi);
+
       mi = gtk_image_menu_item_new_from_stock (GTK_STOCK_EXECUTE, NULL);
       gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
       g_signal_connect (G_OBJECT (mi), "activate",
@@ -1479,7 +1536,8 @@ xfce_appfinder_window_item_visible (GtkTreeModel *model,
   XfceAppfinderWindow *window = XFCE_APPFINDER_WINDOW (data);
 
   return xfce_appfinder_model_get_visible (XFCE_APPFINDER_MODEL (model), iter,
-                                           window->filter_category, window->filter_text);
+                                           window->filter_category,
+                                           window->filter_text);
 }
 
 


More information about the Xfce4-commits mailing list