[Xfce4-commits] <garcon:master> React on app dir changes which, in most cases, requires a menu reload.

Jannis Pohlmann noreply at xfce.org
Sun Sep 5 21:44:01 CEST 2010


Updating branch refs/heads/master
         to 0a6bf53a0838266fae5d7df7c71d2cb7f81ce80e (commit)
       from 04256aea7dacea084af219c190e8c2925e513464 (commit)

commit 0a6bf53a0838266fae5d7df7c71d2cb7f81ce80e
Author: Jannis Pohlmann <jannis at xfce.org>
Date:   Sun Sep 5 19:08:48 2010 +0200

    React on app dir changes which, in most cases, requires a menu reload.
    
    Add a boolean affects_the_outside return parameter to
    garcon_menu_item_reload() and garcon_menu_item_reload_from_file() and
    set it to TRUE if the categories are different before and after
    reloading the file.
    
    Most app dir changes result in the emission of a "reload-required"
    signal of the root menu, except for when a change is made to a .desktop
    file that only affects the item itself, not its position in the menu
    hierarchy.
    
    The monitoring implement is very similar to the basic monitoring
    algorithm we had in libxfce4menu, except that it's a little bit smarter
    and takes merged files and menu file priorities into account. So a
    reload can sometimes be avoided without handling all the different
    scenarios that would have to be handled ideally.

 garcon/garcon-menu-item.c |   53 +++++++++++++++-
 garcon/garcon-menu-item.h |    4 +-
 garcon/garcon-menu.c      |  157 ++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 211 insertions(+), 3 deletions(-)

diff --git a/garcon/garcon-menu-item.c b/garcon/garcon-menu-item.c
index 26e0d4f..ffaa3c4 100644
--- a/garcon/garcon-menu-item.c
+++ b/garcon/garcon-menu-item.c
@@ -88,6 +88,8 @@ static gboolean     garcon_menu_item_get_element_show_in_environment (GarconMenu
 static gboolean     garcon_menu_item_get_element_no_display          (GarconMenuElement      *element);
 static gboolean     garcon_menu_item_get_element_equal               (GarconMenuElement      *element,
                                                                       GarconMenuElement      *other);
+static gboolean     garcon_menu_item_category_lists_equal            (GList                  *categories1,
+                                                                      GList                  *categories2);
 
 
 
@@ -668,6 +670,33 @@ garcon_menu_item_get_element_icon_name (GarconMenuElement *element)
 
 
 
+static gboolean
+garcon_menu_item_category_lists_equal (GList *categories1,
+                                       GList *categories2)
+{
+  gboolean element_missing = FALSE;
+  GList   *lp;
+
+  guint size1;
+  guint size2;
+
+  size1 = g_list_length (categories1);
+  size2 = g_list_length (categories2);
+
+  if (size1 != size2)
+    return FALSE;
+
+  for (lp = categories1; !element_missing && lp != NULL; lp = lp->next)
+    {
+      if (g_list_find_custom (categories2, lp->data, (GCompareFunc) g_strcmp0) == NULL)
+        element_missing = TRUE;
+    }
+
+  return !element_missing;
+}
+
+
+
 GarconMenuItem *
 garcon_menu_item_new (GFile *file)
 {
@@ -819,12 +848,13 @@ garcon_menu_item_new_for_uri (const gchar *uri)
 
 gboolean
 garcon_menu_item_reload (GarconMenuItem  *item,
+                         gboolean        *affects_the_outside,
                          GError         **error)
 {
   g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-  return garcon_menu_item_reload_from_file (item, item->priv->file, error);
+  return garcon_menu_item_reload_from_file (item, item->priv->file, affects_the_outside, error);
 }
 
 
@@ -832,12 +862,15 @@ garcon_menu_item_reload (GarconMenuItem  *item,
 gboolean
 garcon_menu_item_reload_from_file (GarconMenuItem  *item,
                                    GFile           *file,
+                                   gboolean        *affects_the_outside,
                                    GError         **error)
 {
   GKeyFile *rc;
   gboolean  boolean;
   gboolean  succeed;
   GList    *categories = NULL;
+  GList    *lp;
+  GList    *old_categories = NULL;
   gchar   **mt;
   gchar   **str_list;
   gchar    *contents;
@@ -935,6 +968,14 @@ garcon_menu_item_reload_from_file (GarconMenuItem  *item,
   boolean = GET_KEY (boolean, G_KEY_FILE_DESKTOP_KEY_HIDDEN);
   garcon_menu_item_set_hidden (item, boolean);
 
+  if (affects_the_outside != NULL)
+    {
+      /* create a deep copy the old categories list */
+      old_categories = g_list_copy (item->priv->categories);
+      for (lp = old_categories; lp != NULL; lp = lp->next)
+        lp->data = g_strdup (lp->data);
+    }
+
   /* Determine the categories this application should be shown in */
   str_list = GET_STRING_LIST (G_KEY_FILE_DESKTOP_KEY_CATEGORIES);
   if (G_LIKELY (str_list != NULL))
@@ -957,6 +998,16 @@ garcon_menu_item_reload_from_file (GarconMenuItem  *item,
       garcon_menu_item_set_categories (item, NULL);
     }
 
+  if (affects_the_outside != NULL)
+    {
+      if (!garcon_menu_item_category_lists_equal (old_categories, categories))
+        *affects_the_outside = TRUE;
+
+      g_list_foreach (old_categories, (GFunc) g_free, NULL);
+      g_list_free (old_categories);
+    }
+    
+
   /* Set the rest of the private data directly */
   item->priv->only_show_in = GET_STRING_LIST (G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN);
   item->priv->not_show_in = GET_STRING_LIST (G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN);
diff --git a/garcon/garcon-menu-item.h b/garcon/garcon-menu-item.h
index 9ebfe01..9a1bc21 100644
--- a/garcon/garcon-menu-item.h
+++ b/garcon/garcon-menu-item.h
@@ -1,6 +1,6 @@
 /* vi:set et ai sw=2 sts=2 ts=2: */
 /*-
- * Copyright (c) 2006-2009 Jannis Pohlmann <jannis at xfce.org>
+ * Copyright (c) 2006-2010 Jannis Pohlmann <jannis at xfce.org>
  * Copyright (c) 2009      Nick Schermer <nick at xfce.org>
  *
  * This library is free software; you can redistribute it and/or
@@ -67,10 +67,12 @@ GarconMenuItem *garcon_menu_item_new_for_path                      (const gchar
 GarconMenuItem *garcon_menu_item_new_for_uri                       (const gchar     *uri) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
 
 gboolean        garcon_menu_item_reload                            (GarconMenuItem  *item,
+                                                                    gboolean        *affects_the_outside,
                                                                     GError         **error);
 
 gboolean        garcon_menu_item_reload_from_file                  (GarconMenuItem  *item,
                                                                     GFile           *file,
+                                                                    gboolean        *affects_the_outside,
                                                                     GError         **error);
 
 GFile          *garcon_menu_item_get_file                          (GarconMenuItem  *item);
diff --git a/garcon/garcon-menu.c b/garcon/garcon-menu.c
index f2e226f..6fda392 100644
--- a/garcon/garcon-menu.c
+++ b/garcon/garcon-menu.c
@@ -186,6 +186,8 @@ static void                 garcon_menu_directory_file_changed          (GarconM
                                                                          GFile                   *other_file,
                                                                          GFileMonitorEvent        event_type,
                                                                          GFileMonitor            *monitor);
+static GarconMenuItem      *garcon_menu_find_file_item                  (GarconMenu              *menu,
+                                                                         GFile                   *file);
 
 
 
@@ -2105,10 +2107,139 @@ garcon_menu_app_dir_changed (GarconMenu       *menu,
                              GFileMonitorEvent event_type,
                              GFileMonitor     *monitor)
 {
+  GarconMenuItem *item;
+  GFileType       file_type;
+  gboolean        affects_the_outside = FALSE;
+  gchar          *path;
+
   g_return_if_fail (GARCON_IS_MENU (menu));
   g_return_if_fail (menu->priv->parent == NULL);
 
-  /* TODO */
+  if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
+      || event_type == G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED)
+    {
+      g_debug ("app dir/file changed: %s: %s: %d", 
+               garcon_menu_element_get_name (GARCON_MENU_ELEMENT (menu)),
+               g_file_get_path (file), event_type);
+
+      /* query the type of the changed file */
+      file_type = g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL);
+
+      if (file_type == G_FILE_TYPE_DIRECTORY)
+        {
+          /* in this situation, an app dir could have become unreadable for the
+           * current user. like most other situations, this is not very easy
+           * to deal with, so we simply enforce a menu reload */
+          g_signal_emit (menu, menu_signals[RELOAD_REQUIRED], 0);
+        }
+      else
+        {
+          /* a regular file changed, try to find the corresponding menu item */
+          item = garcon_menu_find_file_item (menu, file);
+          if (item != NULL)
+            {
+              /* try to reload the item */
+              if (garcon_menu_item_reload (item, &affects_the_outside, NULL))
+                {
+                  if (affects_the_outside)
+                    {
+                      /* if the categories changed, the item might have to be
+                       * moved around between different menus. this is slightly
+                       * more complicated than one would first think, so just
+                       * enforce a complete menu reload for now */
+                      g_signal_emit (menu, menu_signals[RELOAD_REQUIRED], 0);
+                    }
+                  else
+                    {
+                      /* nothing to do here. the item should emit a 'changed'
+                       * signal to which users of this library can react */
+                    }
+                }
+              else
+                {
+                  /* failed to reload the menu item. this can have many reasons,
+                   * one of them being that the file permissions might have changed.
+                   * handling this situation can be tricky, so, again, we just
+                   * enfore a menu reload until we have something better */
+                  g_signal_emit (menu, menu_signals[RELOAD_REQUIRED], 0);
+                }
+            }
+          else
+            {
+              /* there could be a lot of stuff happening here. seriously, this
+               * stuff is complicated. for now, simply enforce a complete reload 
+               * of the menu structure */
+              g_signal_emit (menu, menu_signals[RELOAD_REQUIRED], 0);
+            }
+        }
+    }
+  else if (event_type == G_FILE_MONITOR_EVENT_CREATED)
+    {
+      g_debug ("app dir/file created: %s: %s: %d", 
+               garcon_menu_element_get_name (GARCON_MENU_ELEMENT (menu)),
+               g_file_get_path (file), event_type);
+
+      /* query the type of the changed file */
+      file_type = g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL);
+
+      if (file_type == G_FILE_TYPE_DIRECTORY)
+        {
+          /* either a previously non-existent app dir or a new subdirectory 
+           * has been created. we need to load all the .desktop files that
+           * are now available in addition to the old ones. so enforce a
+           * menu reload and they will be picked up */
+          g_signal_emit (menu, menu_signals[RELOAD_REQUIRED], 0);
+        }
+      else
+        {
+          path = g_file_get_path (file);
+          if (path != NULL && g_str_has_suffix (path, ".desktop"))
+            {
+              /* a new .desktop file has been created. does it override another
+               * .desktop file that is currently in use? is it overriden itself
+               * and thus, can be ignored? it's not trivial to determine all this,
+               * so what we do is... force a menu reload */
+              g_signal_emit (menu, menu_signals[RELOAD_REQUIRED], 0);
+            }
+          g_free (path);
+        }
+    }
+  else if (event_type == G_FILE_MONITOR_EVENT_DELETED)
+    {
+      g_debug ("app dir/file deleted: %s: %s: %d", 
+               garcon_menu_element_get_name (GARCON_MENU_ELEMENT (menu)),
+               g_file_get_path (file), event_type);
+
+      /* query the type of the changed file */
+      file_type = g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL);
+
+      if (file_type == G_FILE_TYPE_DIRECTORY)
+        {
+          /* an existing app dir (or a subdirectory) has been deleted. we
+           * could remove all the items that are in use and reside inside
+           * this root directory. but for now... enforce a menu reload! */
+          g_signal_emit (menu, menu_signals[RELOAD_REQUIRED], 0);
+        }
+      else 
+        {
+          /* a regular file was deleted, try to find the corresponding menu item */
+          item = garcon_menu_find_file_item (menu, file);
+          if (item != NULL)
+            {
+              /* ok, so a .desktop file was removed. of course we don't know
+               * yet whether there is a replacement in another app dir
+               * with lower priority. we could try to find out but for now
+               * it's easier to simply enforce a menu reload */
+              g_signal_emit (menu, menu_signals[RELOAD_REQUIRED], 0);
+            }
+          else
+            {
+              /* the deleted file hasn't been in use anyway, so removing it
+               * doesn't change anything. so we have nothing to do for a
+               * change, no f****ing menu reload! */
+            }
+        }
+    }
 }
 
 
@@ -2145,3 +2276,27 @@ garcon_menu_directory_file_changed (GarconMenu       *menu,
         g_object_unref (old_directory);
     }
 }
+
+
+
+static GarconMenuItem *
+garcon_menu_find_file_item (GarconMenu *menu,
+                            GFile      *file)
+{
+  GarconMenuItem *item = NULL;
+  GList          *lp;
+
+  g_return_val_if_fail (GARCON_IS_MENU (menu), NULL);
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  item = garcon_menu_item_pool_lookup_file (menu->priv->pool, file);
+
+  if (item == NULL)
+    {
+      for (lp = menu->priv->submenus; item == NULL && lp != NULL; lp = lp->next)
+        item = garcon_menu_find_file_item (lp->data, file);
+    }
+
+  return item;
+
+}



More information about the Xfce4-commits mailing list