[Xfce4-commits] <xfce4-appfinder:master> Insert and reload history command in model.

Nick Schermer noreply at xfce.org
Sat Oct 29 21:38:02 CEST 2011


Updating branch refs/heads/master
         to fdcee2725970e0a68b2cc50ae4627bd29f31a87b (commit)
       from 5c43bc62f81c7fadd1d1066739eda0c9d985fefd (commit)

commit fdcee2725970e0a68b2cc50ae4627bd29f31a87b
Author: Nick Schermer <nick at xfce.org>
Date:   Sat Oct 29 21:30:48 2011 +0200

    Insert and reload history command in model.
    
    New commands were only saved but not inserted in the
    model. Also reload and clear if the history file was
    handled by someone else.

 configure.ac.in             |    1 +
 src/Makefile.am             |    2 +
 src/appfinder-model.c       |  336 ++++++++++++++++++++++++++++++++++++-------
 src/appfinder-model.h       |    2 +-
 src/appfinder-preferences.c |    2 +-
 5 files changed, 292 insertions(+), 51 deletions(-)

diff --git a/configure.ac.in b/configure.ac.in
index 9caf521..e2dabc0 100644
--- a/configure.ac.in
+++ b/configure.ac.in
@@ -74,6 +74,7 @@ dnl ***********************************
 XDT_CHECK_PACKAGE([GLIB], [glib-2.0], [2.23.0])
 XDT_CHECK_PACKAGE([GTHREAD], [gthread-2.0], [2.23.0])
 XDT_CHECK_PACKAGE([GTK], [gtk+-2.0], [2.20.0])
+XDT_CHECK_PACKAGE([GIO], [gio-2.0], [2.20.0])
 XDT_CHECK_PACKAGE([LIBXFCE4UTIL], [libxfce4util-1.0], [4.8.0])
 XDT_CHECK_PACKAGE([LIBXFCE4UI], [libxfce4ui-1], [4.8.0])
 XDT_CHECK_PACKAGE([GARCON], [garcon-1], [0.1.7])
diff --git a/src/Makefile.am b/src/Makefile.am
index 37e1509..6f74907 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -33,6 +33,7 @@ xfce4_appfinder_CFLAGS = \
 	$(LIBXFCE4UTIL_CFLAGS) \
 	$(LIBXFCE4UI_CFLAGS) \
 	$(GARCON_CFLAGS) \
+	$(GIO_CFLAGS) \
 	$(DBUS_GLIB_CFLAGS) \
 	$(XFCONF_CFLAGS) \
 	$(PLATFORM_CFLAGS)
@@ -45,6 +46,7 @@ xfce4_appfinder_LDADD = \
 	$(LIBXFCE4UI_LIBS) \
 	$(GARCON_LIBS) \
 	$(XFCONF_LIBS) \
+	$(GIO_LIBS) \
 	$(DBUS_GLIB_LIBS)
 
 xfce4_appfinder_LDFLAGS = \
diff --git a/src/appfinder-model.c b/src/appfinder-model.c
index f4e6400..959e6f6 100644
--- a/src/appfinder-model.c
+++ b/src/appfinder-model.c
@@ -71,10 +71,19 @@ static gboolean           xfce_appfinder_model_iter_parent           (GtkTreeMod
 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_item_changed          (GarconMenuItem           *menu_item,
+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);
+
 
 
 struct _XfceAppfinderModelClass
@@ -103,6 +112,10 @@ struct _XfceAppfinderModel
   GSList              *collect_categories;
   GThread             *collect_thread;
   GCancellable        *collect_cancelled;
+
+  GFileMonitor        *history_monitor;
+  GFile               *history_file;
+  guint64              history_mtime;
 };
 
 typedef struct
@@ -215,6 +228,9 @@ xfce_appfinder_model_finalize (GObject *object)
     g_source_remove (model->collect_idle_id);
   g_slist_free (model->collect_categories);
 
+  /* stop history file monitoring */
+  xfce_appfinder_model_history_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));
@@ -789,6 +805,239 @@ xfce_appfinder_model_item_free (gpointer            data,
 
 
 
+static void
+xfce_appfinder_model_history_remove_items (XfceAppfinderModel *model)
+{
+  ModelItem   *item;
+  GSList      *li, *lnext;
+  gint         idx;
+  GtkTreePath *path;
+
+  /* no need to remove the items if the stamp is already zero */
+  if (model->history_mtime == 0)
+    return;
+
+  APPFINDER_DEBUG ("clear history items");
+
+  for (li = model->items, idx = 0; li != NULL; li = lnext, idx++)
+    {
+      lnext = li->next;
+      item = li->data;
+      if (item->item != NULL)
+        continue;
+
+      model->items = g_slist_delete_link (model->items, li);
+
+      path = gtk_tree_path_new_from_indices (idx--, -1);
+      gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
+      gtk_tree_path_free (path);
+
+      xfce_appfinder_model_item_free (item, model);
+    }
+
+  /* unset stamp */
+  model->history_mtime = 0;
+}
+
+
+
+static guint64
+xfce_appfinder_model_history_get_mtime (GFile *file)
+{
+  GFileInfo *info;
+  guint64    mtime = 0;
+
+  if (G_LIKELY (file != NULL))
+    {
+      info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
+                                G_FILE_QUERY_INFO_NONE, NULL, NULL);
+      if (G_LIKELY (info != NULL))
+        {
+          mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+          g_object_unref (G_OBJECT (info));
+        }
+    }
+
+  /* never return 1, because we use that for an empty history */
+  return MAX (mtime, 1);
+}
+
+
+static void
+xfce_appfinder_model_history_insert (XfceAppfinderModel *model,
+                                     const gchar        *command)
+{
+  ModelItem    *item;
+  GtkTreeIter   iter;
+  GtkTreePath  *path;
+  GSList       *lp;
+  guint         idx;
+
+  appfinder_return_if_fail (XFCE_IS_APPFINDER_MODEL (model));
+  appfinder_return_if_fail (IS_STRING (command));
+
+  /* add new command */
+  item = g_slice_new0 (ModelItem);
+  item->command = g_strdup (command);
+  item->icon_small = g_object_ref (G_OBJECT (model->command_icon_small));
+  item->icon_large = g_object_ref (G_OBJECT (model->command_icon_large));
+  model->items = g_slist_insert_sorted (model->items, item, xfce_appfinder_model_item_compare);
+
+  /* find the item and the position */
+  for (lp = model->items, idx = 0; lp != NULL; lp = lp->next, idx++)
+    if (lp->data == item)
+      break;
+  g_assert (lp != NULL);
+
+  APPFINDER_DEBUG ("insert %s in the model", command);
+
+  /* emit the insert-row signal */
+  path = gtk_tree_path_new_from_indices (idx, -1);
+  ITER_INIT (iter, model->stamp, lp);
+  gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
+  gtk_tree_path_free (path);
+
+  g_hash_table_insert (model->items_hash, item->command, item);
+}
+
+
+
+static void
+xfce_appfinder_model_history_changed (GFileMonitor       *monitor,
+                                      GFile              *file,
+                                      GFile              *other_file,
+                                      GFileMonitorEvent   event_type,
+                                      XfceAppfinderModel *model)
+{
+  guint64      mtime;
+  gchar       *path;
+  GError      *error = NULL;
+  gchar       *command;
+  gchar       *contents;
+  gchar       *end;
+  GMappedFile *history;
+
+  appfinder_return_if_fail (XFCE_IS_APPFINDER_MODEL (model));
+  appfinder_return_if_fail (model->history_monitor == monitor);
+  appfinder_return_if_fail (G_IS_FILE_MONITOR (monitor));
+  appfinder_return_if_fail (G_IS_FILE (model->history_file));
+
+  switch (event_type)
+    {
+    case G_FILE_MONITOR_EVENT_DELETED:
+      xfce_appfinder_model_history_remove_items (model);
+      break;
+
+    case G_FILE_MONITOR_EVENT_CREATED:
+      mtime = xfce_appfinder_model_history_get_mtime (model->history_file);
+      if (mtime > model->history_mtime)
+        {
+          /* read the new file and update the commands */
+          path = g_file_get_path (file);
+          history = g_mapped_file_new (path, FALSE, &error);
+          if (G_LIKELY (history != NULL))
+            {
+              contents = g_mapped_file_get_contents (history);
+              if (G_LIKELY (contents != NULL))
+                {
+                  /* walk the file */
+                  for (;;)
+                    {
+                      end = strchr (contents, '\n');
+                      if (G_UNLIKELY (end == NULL))
+                        break;
+
+                      if (end != contents)
+                        {
+                          /* look for new commands */
+                          command = g_strndup (contents, end - contents);
+                          if (g_hash_table_lookup (model->items_hash, command) == NULL)
+                            xfce_appfinder_model_history_insert (model, command);
+                          g_free (command);
+                        }
+                      contents = end + 1;
+                    }
+                }
+
+              g_mapped_file_free (history);
+            }
+          else
+            {
+              g_warning ("Failed to open history file: %s", error->message);
+              g_clear_error (&error);
+            }
+
+          model->history_mtime = mtime;
+        }
+      break;
+
+    default:
+      break;
+    }
+}
+
+
+
+static void
+xfce_appfinder_model_history_monitor_stop (XfceAppfinderModel *model)
+{
+  if (model->history_monitor != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (model->history_monitor,
+          G_CALLBACK (xfce_appfinder_model_history_changed), model);
+
+      g_object_unref (G_OBJECT (model->history_monitor));
+      model->history_monitor = NULL;
+    }
+
+  if (model->history_file != NULL)
+    {
+      g_object_unref (G_OBJECT (model->history_file));
+      model->history_file = NULL;
+    }
+}
+
+
+
+static void
+xfce_appfinder_model_history_monitor (XfceAppfinderModel *model,
+                                      const gchar        *path)
+{
+  GFile  *file;
+  GError *error = NULL;
+
+  file = g_file_new_for_path (path);
+
+  if (model->history_file == NULL
+      || model->history_monitor == NULL
+      || !g_file_equal (file, model->history_file))
+    {
+      xfce_appfinder_model_history_monitor_stop (model);
+
+      /* monitor the file for changes */
+      model->history_monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, model->collect_cancelled, &error);
+      if (model->history_monitor != NULL)
+        {
+          APPFINDER_DEBUG ("monitor history file %s", path);
+
+          model->history_file = g_object_ref (G_OBJECT (file));
+          g_signal_connect (G_OBJECT (model->history_monitor), "changed",
+              G_CALLBACK (xfce_appfinder_model_history_changed), model);
+        }
+      else
+        {
+          g_warning ("Failed to setup a monitor for %s: %s", path, error->message);
+          g_error_free (error);
+        }
+    }
+
+  model->history_mtime = xfce_appfinder_model_history_get_mtime (file);
+
+  g_object_unref (G_OBJECT (file));
+}
+
+
+
 static gint
 xfce_appfinder_model_category_compare (gconstpointer a,
                                        gconstpointer b)
@@ -837,8 +1086,8 @@ static void
 xfce_appfinder_model_collect_history (XfceAppfinderModel *model,
                                       GMappedFile        *history)
 {
-  gchar     *contents, *end;
-  gsize      len;
+  gchar     *contents;
+  gchar     *end;
   ModelItem *item;
 
   contents = g_mapped_file_get_contents (history);
@@ -851,17 +1100,16 @@ xfce_appfinder_model_collect_history (XfceAppfinderModel *model,
       if (G_UNLIKELY (end == NULL))
         break;
 
-      len = end - contents;
-      if (len > 0)
+      if (end != contents)
         {
           item = g_slice_new0 (ModelItem);
-          item->command = g_strndup (contents, len);
+          item->command = g_strndup (contents, end - contents);
           item->icon_small = g_object_ref (G_OBJECT (model->command_icon_small));
           item->icon_large = g_object_ref (G_OBJECT (model->command_icon_large));
           model->collect_items = g_slist_prepend (model->collect_items, item);
         }
 
-      contents += len + 1;
+      contents = end + 1;
     }
 }
 
@@ -1180,8 +1428,13 @@ xfce_appfinder_model_collect_thread (gpointer user_data)
       else
         {
           g_warning ("Failed to open history file: %s", error->message);
-          g_error_free (error);
+          g_clear_error (&error);
         }
+
+      /* start monitoring and update mtime */
+      xfce_appfinder_model_history_monitor (model, filename);
+
+      g_free (filename);
     }
 
   if (model->collect_items != NULL
@@ -1440,11 +1693,12 @@ xfce_appfinder_model_save_command (XfceAppfinderModel  *model,
                                    const gchar         *command,
                                    GError             **error)
 {
-  GSList    *li;
-  GString   *contents;
-  gboolean   succeed = FALSE;
-  gchar     *filename;
-  ModelItem *item;
+  GSList       *li;
+  GString      *contents;
+  gboolean      succeed = FALSE;
+  gchar        *filename;
+  ModelItem    *item;
+  static gsize  old_len = 0;
 
   appfinder_return_val_if_fail (XFCE_IS_APPFINDER_MODEL (model), FALSE);
 
@@ -1452,11 +1706,14 @@ xfce_appfinder_model_save_command (XfceAppfinderModel  *model,
       || g_hash_table_lookup (model->items_hash, command) != NULL)
     return TRUE;
 
-  contents = g_string_new (NULL);
+  /* add command to the model */
+  xfce_appfinder_model_history_insert (model, command);
 
+  /* add to the hashtable */
   APPFINDER_DEBUG ("saving history");
 
   /* store all the custom commands */
+  contents = g_string_sized_new (old_len + strlen (command) + 1);
   for (li = model->items; li != NULL; li = li->next)
     {
       item = li->data;
@@ -1468,24 +1725,22 @@ xfce_appfinder_model_save_command (XfceAppfinderModel  *model,
       g_string_append_c (contents, '\n');
     }
 
-  /* add the new command */
-  g_string_append (contents, command);
-  g_string_append_c (contents, '\n');
-
-  if (contents->len > 0)
-    {
-      filename = xfce_resource_save_location (XFCE_RESOURCE_CACHE, HISTORY_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 history cache file");
-      g_free (filename);
-    }
+  filename = xfce_resource_save_location (XFCE_RESOURCE_CACHE, HISTORY_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 history cache file");
+
+  if (succeed)
     {
-      succeed = TRUE;
+      /* possible restart monitoring and update mtime */
+      xfce_appfinder_model_history_monitor (model, filename);
     }
 
+  /* optimization for next run */
+  old_len = contents->allocated_len;
+
+  g_free (filename);
   g_string_free (contents, TRUE);
 
   return succeed;
@@ -1584,31 +1839,14 @@ xfce_appfinder_model_icon_theme_changed (XfceAppfinderModel *model)
 
 
 void
-xfce_appfinder_model_commands_clear (XfceAppfinderModel *model)
+xfce_appfinder_model_history_clear (XfceAppfinderModel *model)
 {
-  ModelItem   *item;
-  GSList      *li, *lnext;
-  gint         idx;
-  GtkTreePath *path;
-  gchar       *filename;
+  gchar *filename;
 
   appfinder_return_if_fail (XFCE_IS_APPFINDER_MODEL (model));
 
-  for (li = model->items, idx = 0; li != NULL; li = lnext, idx++)
-    {
-      lnext = li->next;
-      item = li->data;
-      if (item->item != NULL)
-        continue;
-
-      model->items = g_slist_delete_link (model->items, li);
-
-      path = gtk_tree_path_new_from_indices (idx--, -1);
-      gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
-      gtk_tree_path_free (path);
-
-      xfce_appfinder_model_item_free (item, model);
-    }
+  /* remove items from model */
+  xfce_appfinder_model_history_remove_items (model);
 
   /* remove the history file */
   filename = xfce_resource_save_location (XFCE_RESOURCE_CACHE, HISTORY_PATH, FALSE);
diff --git a/src/appfinder-model.h b/src/appfinder-model.h
index f5c86fd..34bc486 100644
--- a/src/appfinder-model.h
+++ b/src/appfinder-model.h
@@ -85,7 +85,7 @@ void                 xfce_appfinder_model_icon_theme_changed   (XfceAppfinderMod
 
 GarconMenuDirectory *xfce_appfinder_model_get_command_category (void);
 
-void                 xfce_appfinder_model_commands_clear       (XfceAppfinderModel        *model);
+void                 xfce_appfinder_model_history_clear       (XfceAppfinderModel        *model);
 
 void                 xfce_appfinder_model_category_free        (CategoryItem              *category);
 
diff --git a/src/appfinder-preferences.c b/src/appfinder-preferences.c
index 4bd69ec..6934b0f 100644
--- a/src/appfinder-preferences.c
+++ b/src/appfinder-preferences.c
@@ -177,7 +177,7 @@ xfce_appfinder_preferences_clear_history (XfceAppfinderPreferences *preferences)
                            _("Are you sure you want to clear the command history?")))
     {
       model = xfce_appfinder_model_get ();
-      xfce_appfinder_model_commands_clear (model);
+      xfce_appfinder_model_history_clear (model);
       g_object_unref (G_OBJECT (model));
     }
 }


More information about the Xfce4-commits mailing list