File 0005-backport-from-gnome-menus.patch of Package mate-menus.10913

From 524d3e4fc324c2cbc2454e8b1fcddc6622dc4714 Mon Sep 17 00:00:00 2001
From: yetist <xiaotian.wu@i-soft.com.cn>
Date: Wed, 2 May 2018 22:46:52 +0800
Subject: [PATCH 5/6] backport from gnome-menus

---
 libmenu/desktop-entries.c   |  691 +++++++-------
 libmenu/desktop-entries.h   |   46 +-
 libmenu/entry-directories.c |  248 +++--
 libmenu/matemenu-tree.c     | 1729 +++++++++++++++++++++--------------
 libmenu/matemenu-tree.h     |  189 ++--
 libmenu/menu-layout.c       |   50 +-
 6 files changed, 1793 insertions(+), 1160 deletions(-)

diff --git a/libmenu/desktop-entries.c b/libmenu/desktop-entries.c
index c9fcc0f..4793bdd 100644
--- a/libmenu/desktop-entries.c
+++ b/libmenu/desktop-entries.c
@@ -20,39 +20,49 @@
 #include <config.h>
 
 #include "desktop-entries.h"
+#include <gio/gdesktopappinfo.h>
 
 #include <string.h>
 
 #include "menu-util.h"
 
 #define DESKTOP_ENTRY_GROUP     "Desktop Entry"
-#define KDE_DESKTOP_ENTRY_GROUP "KDE Desktop Entry"
 
-enum {
-	DESKTOP_ENTRY_NO_DISPLAY     = 1 << 0,
-	DESKTOP_ENTRY_HIDDEN         = 1 << 1,
-	DESKTOP_ENTRY_SHOW_IN_MATE   = 1 << 2,
-	DESKTOP_ENTRY_TRYEXEC_FAILED = 1 << 3
+struct DesktopEntry
+{
+  guint       refcount;
+
+  char       *path;
+  const char *basename;
+
+  guint       type      : 2;
+  guint       reserved  : 30;
 };
 
-struct DesktopEntry {
-	char* path;
-	char* basename;
+typedef struct
+{
+  DesktopEntry     base;
+
+  GDesktopAppInfo *appinfo;
+  GQuark          *categories;
+} DesktopEntryDesktop;
 
-	GQuark* categories;
+typedef struct
+{
+  DesktopEntry base;
 
-	char* name;
-	char* generic_name;
+  char     *name;
+  char     *generic_name;
+  char     *comment;
+  GIcon    *icon;
 	char* full_name;
-	char* comment;
-	char* icon;
 	char* exec;
-	gboolean terminal;
 
-	guint type: 2;
-	guint flags: 4;
-	guint refcount: 24;
-};
+  guint     nodisplay   : 1;
+  guint     hidden      : 1;
+  guint     showin      : 1;
+	guint terminal:1;
+} DesktopEntryDirectory;
 
 struct DesktopEntrySet {
 	int refcount;
@@ -63,54 +73,110 @@ struct DesktopEntrySet {
  * Desktop entries
  */
 
-static guint get_flags_from_key_file(DesktopEntry* entry, GKeyFile* key_file, const char* desktop_entry_group)
-{
-  GError    *error;
-  char     **strv;
-  gboolean   no_display;
-  gboolean   hidden;
-  gboolean   show_in_mate;
-  gboolean   tryexec_failed;
-  char      *tryexec;
-  guint      flags;
-  int        i;
-
-  error = NULL;
-  no_display = g_key_file_get_boolean (key_file,
-                                       desktop_entry_group,
-                                       "NoDisplay",
-                                       &error);
-  if (error)
-    {
-      no_display = FALSE;
-      g_error_free (error);
-    }
+/**
+ * unix_basename_from_path:
+ * @path: Path string
+ *
+ * Returns: A constant pointer into the basename of @path
+ */
+static const char *
+unix_basename_from_path (const char *path)
+{
+  const char *basename = g_strrstr (path, "/");
+  if (basename)
+    return basename + 1;
+  else
+    return path;
+}
+
+static const char *
+get_current_desktop (void)
+{
+  static char *current_desktop = NULL;
 
-  error = NULL;
-  hidden = g_key_file_get_boolean (key_file,
-                                   desktop_entry_group,
-                                   "Hidden",
-                                   &error);
-  if (error)
+  /* Support XDG_CURRENT_DESKTOP environment variable; this can be used
+   * to abuse mate-menus in non-MATE desktops. */
+  if (!current_desktop)
     {
-      hidden = FALSE;
-      g_error_free (error);
+      const char *desktop;
+
+      desktop = g_getenv ("XDG_CURRENT_DESKTOP");
+
+      /* Note: if XDG_CURRENT_DESKTOP is set but empty, do as if it
+       * was not set */
+      if (!desktop || desktop[0] == '\0')
+        current_desktop = g_strdup ("MATE");
+      else
+        current_desktop = g_strdup (desktop);
     }
 
-  show_in_mate = TRUE;
+  /* Using "*" means skipping desktop-related checks */
+  if (g_strcmp0 (current_desktop, "*") == 0)
+    return NULL;
+
+  return current_desktop;
+}
+
+static GIcon *
+key_file_get_icon (GKeyFile *key_file)
+{
+  GIcon *icon = NULL;
+  gchar *icon_name;
+
+  icon_name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP,
+                                            "Icon", NULL, NULL);
+  if (!icon_name)
+    return NULL;
+
+  if (g_path_is_absolute (icon_name)) {
+    GFile *file;
+
+    file = g_file_new_for_path (icon_name);
+    icon = g_file_icon_new (file);
+    g_object_unref (file);
+  } else {
+    char *p;
+
+    /* Work around a common mistake in desktop files */
+    if ((p = strrchr (icon_name, '.')) != NULL &&
+        (strcmp (p, ".png") == 0 ||
+         strcmp (p, ".xpm") == 0 ||
+         strcmp (p, ".svg") == 0))
+      *p = 0;
+
+    icon = g_themed_icon_new (icon_name);
+  }
+
+  g_free (icon_name);
+
+  return icon;
+}
+
+static gboolean
+key_file_get_show_in (GKeyFile *key_file)
+{
+  const gchar *current_desktop;
+  gchar **strv;
+  gboolean show_in = TRUE;
+  int i;
+
+  current_desktop = get_current_desktop ();
+  if (!current_desktop)
+    return TRUE;
+
   strv = g_key_file_get_string_list (key_file,
-                                     desktop_entry_group,
+                                     DESKTOP_ENTRY_GROUP,
                                      "OnlyShowIn",
                                      NULL,
                                      NULL);
   if (strv)
     {
-      show_in_mate = FALSE;
+      show_in = FALSE;
       for (i = 0; strv[i]; i++)
         {
-          if (!strcmp (strv[i], "MATE"))
+          if (!strcmp (strv[i], current_desktop))
             {
-              show_in_mate = TRUE;
+              show_in = TRUE;
               break;
             }
         }
@@ -118,198 +184,141 @@ static guint get_flags_from_key_file(DesktopEntry* entry, GKeyFile* key_file, co
   else
     {
       strv = g_key_file_get_string_list (key_file,
-                                         desktop_entry_group,
+                                         DESKTOP_ENTRY_GROUP,
                                          "NotShowIn",
                                          NULL,
                                          NULL);
       if (strv)
         {
-          show_in_mate = TRUE;
+          show_in = TRUE;
           for (i = 0; strv[i]; i++)
             {
-              if (!strcmp (strv[i], "MATE"))
+              if (!strcmp (strv[i], current_desktop))
                 {
-                  show_in_mate = FALSE;
+                  show_in = FALSE;
                 }
             }
         }
     }
   g_strfreev (strv);
 
-  tryexec_failed = FALSE;
-  tryexec = g_key_file_get_string (key_file,
-                                   desktop_entry_group,
-                                   "TryExec",
-                                   NULL);
-  if (tryexec)
-    {
-      char *path;
-
-      path = g_find_program_in_path (g_strstrip (tryexec));
-
-      tryexec_failed = (path == NULL);
-
-      g_free (path);
-      g_free (tryexec);
-    }
-
-  flags = 0;
-  if (no_display)
-    flags |= DESKTOP_ENTRY_NO_DISPLAY;
-  if (hidden)
-    flags |= DESKTOP_ENTRY_HIDDEN;
-  if (show_in_mate)
-    flags |= DESKTOP_ENTRY_SHOW_IN_MATE;
-  if (tryexec_failed)
-    flags |= DESKTOP_ENTRY_TRYEXEC_FAILED;
-
-  return flags;
+  return show_in;
 }
 
-static GQuark* get_categories_from_key_file (DesktopEntry* entry, GKeyFile* key_file, const char* desktop_entry_group)
+static gboolean
+desktop_entry_load_directory (DesktopEntry  *entry,
+                              GKeyFile      *key_file,
+                              GError       **error)
 {
-  GQuark  *retval;
-  char   **strv;
-  gsize    len;
-  int      i;
+  DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*)entry;
+  char *type_str;
 
-  strv = g_key_file_get_string_list (key_file,
-                                     desktop_entry_group,
-                                     "Categories",
-                                     &len,
-                                     NULL);
-  if (!strv)
-    return NULL;
+  type_str = g_key_file_get_string (key_file, DESKTOP_ENTRY_GROUP, "Type", error);
+  if (!type_str)
+    return FALSE;
 
-  retval = g_new0 (GQuark, len + 1);
+  if (strcmp (type_str, "Directory") != 0)
+    {
+      g_set_error (error,
+                   G_KEY_FILE_ERROR,
+                   G_KEY_FILE_ERROR_INVALID_VALUE,
+                   "\"%s\" does not contain the correct \"Type\" value\n", entry->path);
+      g_free (type_str);
+      return FALSE;
+    }
 
-  for (i = 0; strv[i]; i++)
-    retval[i] = g_quark_from_string (strv[i]);
+  g_free (type_str);
 
-  g_strfreev (strv);
+  entry_directory->name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Name", NULL, error);
+  if (entry_directory->name == NULL)
+    return FALSE;
 
-  return retval;
+  entry_directory->generic_name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "GenericName", NULL, NULL);
+  entry_directory->comment      = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Comment", NULL, NULL);
+  entry_directory->icon         = key_file_get_icon (key_file);
+  entry_directory->nodisplay    = g_key_file_get_boolean (key_file,
+                                                          DESKTOP_ENTRY_GROUP,
+                                                          "NoDisplay",
+                                                          NULL);
+  entry_directory->hidden       = g_key_file_get_boolean (key_file,
+                                                          DESKTOP_ENTRY_GROUP,
+                                                          "Hidden",
+                                                          NULL);
+  entry_directory->showin       = key_file_get_show_in (key_file);
+
+  return TRUE;
 }
 
-static DesktopEntry* desktop_entry_load(DesktopEntry* entry)
+static gboolean
+desktop_entry_load (DesktopEntry *entry)
 {
-  DesktopEntry *retval = NULL;
-  GKeyFile     *key_file;
-  GError       *error;
-  const char   *desktop_entry_group;
-  char         *name_str;
-  char         *type_str;
-
-  key_file = g_key_file_new ();
-
-  error = NULL;
-  if (!g_key_file_load_from_file (key_file, entry->path, 0, &error))
-    {
-      menu_verbose ("Failed to load \"%s\": %s\n",
-                    entry->path, error->message);
-      g_error_free (error);
-      goto out;
-    }
-
-  if (g_key_file_has_group (key_file, DESKTOP_ENTRY_GROUP))
-    {
-      desktop_entry_group = DESKTOP_ENTRY_GROUP;
-    }
-  else
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
     {
-      menu_verbose ("\"%s\" contains no \"" DESKTOP_ENTRY_GROUP "\" group\n",
-                    entry->path);
+      DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop*)entry;
+      const char *categories_str;
 
-      if (g_key_file_has_group (key_file, KDE_DESKTOP_ENTRY_GROUP))
-        {
-          desktop_entry_group = KDE_DESKTOP_ENTRY_GROUP;
-          menu_verbose ("\"%s\" contains deprecated \"" KDE_DESKTOP_ENTRY_GROUP "\" group\n",
-                        entry->path);
-        }
-      else
+      entry_desktop->appinfo = g_desktop_app_info_new_from_filename (entry->path);
+      if (!entry_desktop->appinfo ||
+          !g_app_info_get_name (G_APP_INFO (entry_desktop->appinfo)) ||
+          !g_app_info_get_executable (G_APP_INFO (entry_desktop->appinfo)))
         {
-          goto out;
+          menu_verbose ("Failed to load \"%s\"\n", entry->path);
+          return FALSE;
         }
-    }
 
-  if (!g_key_file_has_key (key_file, desktop_entry_group, "Name", NULL))
-    {
-      menu_verbose ("\"%s\" contains no \"Name\" key\n", entry->path);
-      goto out;
-    }
+      categories_str = g_desktop_app_info_get_categories (entry_desktop->appinfo);
+      if (categories_str)
+        {
+          char **categories;
+          int i;
 
-  name_str = g_key_file_get_locale_string (key_file, desktop_entry_group, "Name", NULL, NULL);
-  if (!name_str)
-    {
-      menu_verbose ("\"%s\" contains an invalid \"Name\" key\n", entry->path);
-      goto out;
-    }
+          categories = g_strsplit (categories_str, ";", -1);
+          entry_desktop->categories = g_new0 (GQuark, g_strv_length (categories) + 1);
 
-  g_free (name_str);
+          for (i = 0; categories[i]; i++)
+            entry_desktop->categories[i] = g_quark_from_string (categories[i]);
 
-  type_str = g_key_file_get_string (key_file, desktop_entry_group, "Type", NULL);
-  if (!type_str)
-    {
-      menu_verbose ("\"%s\" contains no \"Type\" key\n", entry->path);
-      goto out;
-    }
+          g_strfreev (categories);
+        }
 
-  if ((entry->type == DESKTOP_ENTRY_DESKTOP && strcmp (type_str, "Application") != 0) ||
-      (entry->type == DESKTOP_ENTRY_DIRECTORY && strcmp (type_str, "Directory") != 0))
-    {
-      menu_verbose ("\"%s\" does not contain the correct \"Type\" value\n", entry->path);
-      g_free (type_str);
-      goto out;
+      return TRUE;
     }
-
-  g_free (type_str);
-
-  if (entry->type == DESKTOP_ENTRY_DESKTOP &&
-      !g_key_file_has_key (key_file, desktop_entry_group, "Exec", NULL))
+  else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
     {
-      menu_verbose ("\"%s\" does not contain an \"Exec\" key\n", entry->path);
-      goto out;
-    }
-
-  retval = entry;
+      GKeyFile *key_file = NULL;
+      GError   *error = NULL;
+      gboolean  retval = FALSE;
 
-#define GET_LOCALE_STRING(n) g_key_file_get_locale_string (key_file, desktop_entry_group, (n), NULL, NULL)
+      key_file = g_key_file_new ();
 
-  retval->name         = GET_LOCALE_STRING ("Name");
-  retval->generic_name = GET_LOCALE_STRING ("GenericName");
-  retval->full_name    = GET_LOCALE_STRING ("X-MATE-FullName");
-  retval->comment      = GET_LOCALE_STRING ("Comment");
-  retval->icon         = GET_LOCALE_STRING ("Icon");
-  retval->flags        = get_flags_from_key_file (retval, key_file, desktop_entry_group);
-  retval->categories   = get_categories_from_key_file (retval, key_file, desktop_entry_group);
+      if (!g_key_file_load_from_file (key_file, entry->path, 0, &error))
+        goto out;
 
-  if (entry->type == DESKTOP_ENTRY_DESKTOP)
-    {
-      retval->exec = g_key_file_get_string (key_file, desktop_entry_group, "Exec", NULL);
-      retval->terminal = g_key_file_get_boolean (key_file, desktop_entry_group, "Terminal", NULL);
-    }
+      if (!desktop_entry_load_directory (entry, key_file, &error))
+        goto out;
 
-#undef GET_LOCALE_STRING
+      retval = TRUE;
 
-  menu_verbose ("Desktop entry \"%s\" (%s, %s, %s, %s, %s) flags: NoDisplay=%s, Hidden=%s, ShowInMATE=%s, TryExecFailed=%s\n",
-                retval->basename,
-                retval->name,
-                retval->generic_name ? retval->generic_name : "(null)",
-                retval->full_name ? retval->full_name : "(null)",
-                retval->comment ? retval->comment : "(null)",
-                retval->icon ? retval->icon : "(null)",
-                retval->flags & DESKTOP_ENTRY_NO_DISPLAY     ? "(true)" : "(false)",
-                retval->flags & DESKTOP_ENTRY_HIDDEN         ? "(true)" : "(false)",
-                retval->flags & DESKTOP_ENTRY_SHOW_IN_MATE  ? "(true)" : "(false)",
-                retval->flags & DESKTOP_ENTRY_TRYEXEC_FAILED ? "(true)" : "(false)");
+    out:
+      g_key_file_free (key_file);
 
- out:
-  g_key_file_free (key_file);
+      if (!retval)
+        {
+          if (error)
+            {
+              menu_verbose ("Failed to load \"%s\": %s\n", entry->path, error->message);
+              g_error_free (error);
+            }
+          else
+            menu_verbose ("Failed to load \"%s\"\n", entry->path);
+        }
 
-  if (!retval)
-    desktop_entry_unref (entry);
+      return retval;
+    }
+  else
+    g_assert_not_reached ();
 
-  return retval;
+  return FALSE;
 }
 
 DesktopEntry* desktop_entry_new(const char* path)
@@ -322,10 +331,12 @@ DesktopEntry* desktop_entry_new(const char* path)
   if (g_str_has_suffix (path, ".desktop"))
     {
       type = DESKTOP_ENTRY_DESKTOP;
+      retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1);
     }
   else if (g_str_has_suffix (path, ".directory"))
     {
       type = DESKTOP_ENTRY_DIRECTORY;
+      retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1);
     }
   else
     {
@@ -334,14 +345,18 @@ DesktopEntry* desktop_entry_new(const char* path)
       return NULL;
     }
 
-  retval = g_new0 (DesktopEntry, 1);
-
   retval->refcount = 1;
   retval->type     = type;
-  retval->basename = g_path_get_basename (path);
   retval->path     = g_strdup (path);
+  retval->basename = unix_basename_from_path (retval->path);
+
+  if (!desktop_entry_load (retval))
+    {
+      desktop_entry_unref (retval);
+      return NULL;
+    }
 
-  return desktop_entry_load (retval);
+  return retval;
 }
 
 DesktopEntry* desktop_entry_reload(DesktopEntry* entry)
@@ -350,31 +365,39 @@ DesktopEntry* desktop_entry_reload(DesktopEntry* entry)
 
   menu_verbose ("Re-loading desktop entry \"%s\"\n", entry->path);
 
-  g_free (entry->categories);
-  entry->categories = NULL;
-
-  g_free (entry->name);
-  entry->name = NULL;
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    {
+      DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop *) entry;
 
-  g_free (entry->generic_name);
-  entry->generic_name = NULL;
+      g_object_unref (entry_desktop->appinfo);
+      entry_desktop->appinfo = NULL;
 
-  g_free (entry->full_name);
-  entry->full_name = NULL;
+      g_free (entry_desktop->categories);
+      entry_desktop->categories = NULL;
+    }
+  else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
+    {
+      DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*) entry;
 
-  g_free (entry->comment);
-  entry->comment = NULL;
+      g_free (entry_directory->name);
+      entry_directory->name = NULL;
 
-  g_free (entry->icon);
-  entry->icon = NULL;
+      g_free (entry_directory->comment);
+      entry_directory->comment = NULL;
 
-  g_free (entry->exec);
-  entry->exec = NULL;
+      g_object_unref (entry_directory->icon);
+      entry_directory->icon = NULL;
+    }
+  else
+    g_assert_not_reached ();
 
-  entry->terminal = 0;
-  entry->flags = 0;
+  if (!desktop_entry_load (entry))
+    {
+      desktop_entry_unref (entry);
+      return NULL;
+    }
 
-  return desktop_entry_load (entry);
+  return entry;
 }
 
 DesktopEntry* desktop_entry_ref(DesktopEntry* entry)
@@ -390,39 +413,55 @@ DesktopEntry* desktop_entry_ref(DesktopEntry* entry)
 DesktopEntry* desktop_entry_copy(DesktopEntry* entry)
 {
   DesktopEntry *retval;
-  int           i;
 
   menu_verbose ("Copying desktop entry \"%s\"\n",
                 entry->basename);
 
-  retval = g_new0 (DesktopEntry, 1);
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1);
+  else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
+    retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1);
+  else
+    g_assert_not_reached ();
 
   retval->refcount     = 1;
   retval->type         = entry->type;
-  retval->basename     = g_strdup (entry->basename);
   retval->path         = g_strdup (entry->path);
-  retval->name         = g_strdup (entry->name);
-  retval->generic_name = g_strdup (entry->generic_name);
-  retval->full_name    = g_strdup (entry->full_name);
-  retval->comment      = g_strdup (entry->comment);
-  retval->icon         = g_strdup (entry->icon);
-  retval->exec         = g_strdup (entry->exec);
-  retval->terminal     = entry->terminal;
-  retval->flags        = entry->flags;
-
-  i = 0;
-  if (entry->categories != NULL)
+  retval->basename     = unix_basename_from_path (retval->path);
+
+  if (retval->type == DESKTOP_ENTRY_DESKTOP)
     {
-      for (; entry->categories[i]; i++);
-    }
+      DesktopEntryDesktop *desktop_entry = (DesktopEntryDesktop*) entry;
+      DesktopEntryDesktop *retval_desktop_entry = (DesktopEntryDesktop*) retval;
+      int i;
+
+      retval_desktop_entry->appinfo = g_object_ref (desktop_entry->appinfo);
+
+      if (desktop_entry->categories != NULL)
+        {
+          i = 0;
+          for (; desktop_entry->categories[i]; i++);
 
-  retval->categories = g_new0 (GQuark, i + 1);
+          retval_desktop_entry->categories = g_new0 (GQuark, i + 1);
 
-  i = 0;
-  if (entry->categories != NULL)
+          i = 0;
+          for (; desktop_entry->categories[i]; i++)
+            retval_desktop_entry->categories[i] = desktop_entry->categories[i];
+        }
+      else
+        retval_desktop_entry->categories = NULL;
+    }
+  else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
     {
-      for (; entry->categories[i]; i++)
-        retval->categories[i] = entry->categories[i];
+      DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*)entry;
+      DesktopEntryDirectory *retval_directory = (DesktopEntryDirectory*)retval;
+
+      retval_directory->name         = g_strdup (entry_directory->name);
+      retval_directory->comment      = g_strdup (entry_directory->comment);
+      retval_directory->icon         = g_object_ref (entry_directory->icon);
+      retval_directory->nodisplay    = entry_directory->nodisplay;
+      retval_directory->hidden       = entry_directory->hidden;
+      retval_directory->showin       = entry_directory->showin;
     }
 
   return retval;
@@ -434,37 +473,39 @@ void desktop_entry_unref(DesktopEntry* entry)
   g_return_if_fail (entry->refcount > 0);
 
   entry->refcount -= 1;
-  if (entry->refcount == 0)
-    {
-      g_free (entry->categories);
-      entry->categories = NULL;
+  if (entry->refcount != 0)
+    return;
 
-      g_free (entry->name);
-      entry->name = NULL;
+  g_free (entry->path);
+  entry->path = NULL;
 
-      g_free (entry->generic_name);
-      entry->generic_name = NULL;
-
-      g_free (entry->full_name);
-      entry->full_name = NULL;
-
-      g_free (entry->comment);
-      entry->comment = NULL;
-
-      g_free (entry->icon);
-      entry->icon = NULL;
-
-      g_free (entry->exec);
-      entry->exec = NULL;
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    {
+      DesktopEntryDesktop *desktop_entry = (DesktopEntryDesktop*) entry;
+      g_free (desktop_entry->categories);
+      if (desktop_entry->appinfo)
+        g_object_unref (desktop_entry->appinfo);
+    }
+  else if (entry->type == DESKTOP_ENTRY_DIRECTORY)
+    {
+      DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*) entry;
 
-      g_free (entry->basename);
-      entry->basename = NULL;
+      g_free (entry_directory->name);
+      entry_directory->name = NULL;
 
-      g_free (entry->path);
-      entry->path = NULL;
+      g_free (entry_directory->comment);
+      entry_directory->comment = NULL;
 
-      g_free (entry);
+      if (entry_directory->icon != NULL)
+        {
+          g_object_unref (entry_directory->icon);
+          entry_directory->icon = NULL;
+        }
     }
+  else
+    g_assert_not_reached ();
+
+  g_free (entry);
 }
 
 DesktopEntryType desktop_entry_get_type(DesktopEntry* entry)
@@ -485,78 +526,99 @@ desktop_entry_get_basename (DesktopEntry *entry)
 
 const char* desktop_entry_get_name(DesktopEntry* entry)
 {
-	return entry->name;
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    return g_app_info_get_name (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo));
+  return ((DesktopEntryDirectory*)entry)->name;
 }
 
 const char* desktop_entry_get_generic_name(DesktopEntry* entry)
 {
-	return entry->generic_name;
-}
-
-const char* desktop_entry_get_full_name(DesktopEntry* entry)
-{
-  return entry->full_name;
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    return g_desktop_app_info_get_generic_name (((DesktopEntryDesktop*)entry)->appinfo);
+  return ((DesktopEntryDirectory*)entry)->generic_name;
 }
 
 const char* desktop_entry_get_comment(DesktopEntry* entry)
 {
-	return entry->comment;
-}
-
-const char* desktop_entry_get_icon(DesktopEntry* entry)
-{
-	return entry->icon;
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    return g_app_info_get_description (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo));
+  return ((DesktopEntryDirectory*)entry)->comment;
 }
 
-const char* desktop_entry_get_exec(DesktopEntry* entry)
+GIcon *
+desktop_entry_get_icon (DesktopEntry *entry)
 {
-	return entry->exec;
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    return g_app_info_get_icon (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo));
+  return ((DesktopEntryDirectory*)entry)->icon;
 }
 
-gboolean desktop_entry_get_launch_in_terminal(DesktopEntry* entry)
+gboolean desktop_entry_get_no_display (DesktopEntry *entry)
 {
-	return entry->terminal;
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    return g_desktop_app_info_get_nodisplay (((DesktopEntryDesktop*)entry)->appinfo);
+  return ((DesktopEntryDirectory*)entry)->nodisplay;
 }
 
 gboolean desktop_entry_get_hidden(DesktopEntry* entry)
 {
-	return (entry->flags & DESKTOP_ENTRY_HIDDEN) != 0;
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    return g_desktop_app_info_get_is_hidden (((DesktopEntryDesktop*)entry)->appinfo);
+  return ((DesktopEntryDirectory*)entry)->hidden;
 }
 
-gboolean desktop_entry_get_no_display(DesktopEntry* entry)
+gboolean
+desktop_entry_get_show_in (DesktopEntry *entry)
 {
-	return (entry->flags & DESKTOP_ENTRY_NO_DISPLAY) != 0;
-}
+  if (entry->type == DESKTOP_ENTRY_DESKTOP)
+    {
+      const char *current_desktop = get_current_desktop ();
 
-gboolean desktop_entry_get_show_in_mate(DesktopEntry* entry)
-{
-	return (entry->flags & DESKTOP_ENTRY_SHOW_IN_MATE) != 0;
+      if (current_desktop == NULL)
+        return TRUE;
+      else
+        return g_desktop_app_info_get_show_in (((DesktopEntryDesktop*)entry)->appinfo, current_desktop);
+    }
+  return ((DesktopEntryDirectory*)entry)->showin;
 }
 
-gboolean desktop_entry_get_tryexec_failed(DesktopEntry* entry)
+GDesktopAppInfo  *
+desktop_entry_get_app_info (DesktopEntry *entry)
 {
-	return (entry->flags & DESKTOP_ENTRY_TRYEXEC_FAILED) != 0;
+  g_return_val_if_fail (entry->type == DESKTOP_ENTRY_DESKTOP, NULL);
+  return ((DesktopEntryDesktop*)entry)->appinfo;
 }
 
 gboolean desktop_entry_has_categories(DesktopEntry* entry)
 {
-	return (entry->categories != NULL && entry->categories[0] != 0);
+  DesktopEntryDesktop *desktop_entry;
+  if (entry->type != DESKTOP_ENTRY_DESKTOP)
+    return FALSE;
+
+  desktop_entry = (DesktopEntryDesktop*) entry;
+  return (desktop_entry->categories != NULL && desktop_entry->categories[0] != 0);
 }
 
 gboolean desktop_entry_has_category(DesktopEntry* entry, const char* category)
 {
   GQuark quark;
   int    i;
+  DesktopEntryDesktop *desktop_entry;
+
+  if (entry->type != DESKTOP_ENTRY_DESKTOP)
+    return FALSE;
+
+  desktop_entry = (DesktopEntryDesktop*) entry;
 
-  if (entry->categories == NULL)
+  if (desktop_entry->categories == NULL)
     return FALSE;
 
   if (!(quark = g_quark_try_string (category)))
     return FALSE;
 
-  for (i = 0; entry->categories[i]; i++)
+  for (i = 0; desktop_entry->categories[i]; i++)
     {
-      if (quark == entry->categories[i])
+      if (quark == desktop_entry->categories[i])
         return TRUE;
     }
 
@@ -567,29 +629,36 @@ void desktop_entry_add_legacy_category(DesktopEntry* entry)
 {
   GQuark *categories;
   int     i;
+  DesktopEntryDesktop *desktop_entry;
+
+  g_return_if_fail (entry->type == DESKTOP_ENTRY_DESKTOP);
+
+  desktop_entry = (DesktopEntryDesktop*) entry;
 
   menu_verbose ("Adding Legacy category to \"%s\"\n",
                 entry->basename);
 
-  i = 0;
-  if (entry->categories != NULL)
+  if (desktop_entry->categories != NULL)
     {
-      for (; entry->categories[i]; i++);
-    }
+      i = 0;
+      for (; desktop_entry->categories[i]; i++);
 
-  categories = g_new0 (GQuark, i + 2);
+      categories = g_new0 (GQuark, i + 2);
 
-  i = 0;
-  if (entry->categories != NULL)
+      i = 0;
+      for (; desktop_entry->categories[i]; i++)
+        categories[i] = desktop_entry->categories[i];
+    }
+  else
     {
-      for (; entry->categories[i]; i++)
-        categories[i] = entry->categories[i];
+      categories = g_new0 (GQuark, 2);
+      i = 0;
     }
 
   categories[i] = g_quark_from_string ("Legacy");
 
-  g_free (entry->categories);
-  entry->categories = categories;
+  g_free (desktop_entry->categories);
+  desktop_entry->categories = categories;
 }
 
 /*
diff --git a/libmenu/desktop-entries.h b/libmenu/desktop-entries.h
index a67cc9f..4c86b4c 100644
--- a/libmenu/desktop-entries.h
+++ b/libmenu/desktop-entries.h
@@ -20,11 +20,9 @@
 #ifndef __DESKTOP_ENTRIES_H__
 #define __DESKTOP_ENTRIES_H__
 
-#include <glib.h>
+#include <gio/gdesktopappinfo.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 typedef enum {
 	DESKTOP_ENTRY_INVALID = 0,
@@ -41,29 +39,25 @@ DesktopEntry* desktop_entry_copy(DesktopEntry* entry);
 DesktopEntry* desktop_entry_reload(DesktopEntry* entry);
 void desktop_entry_unref(DesktopEntry* entry);
 
-DesktopEntryType desktop_entry_get_type(DesktopEntry* entry);
-const char* desktop_entry_get_path(DesktopEntry* entry);
-const char* desktop_entry_get_basename(DesktopEntry* entry);
-
-const char* desktop_entry_get_name(DesktopEntry* entry);
-const char* desktop_entry_get_generic_name(DesktopEntry* entry);
-const char* desktop_entry_get_full_name(DesktopEntry* entry);
-const char* desktop_entry_get_comment(DesktopEntry* entry);
-const char* desktop_entry_get_icon(DesktopEntry* entry);
-const char* desktop_entry_get_exec(DesktopEntry* entry);
-gboolean desktop_entry_get_launch_in_terminal(DesktopEntry* entry);
-
-gboolean desktop_entry_get_hidden(DesktopEntry* entry);
-gboolean desktop_entry_get_no_display(DesktopEntry* entry);
-gboolean desktop_entry_get_show_in_mate(DesktopEntry* entry);
-gboolean desktop_entry_get_tryexec_failed(DesktopEntry* entry);
-
-gboolean desktop_entry_has_categories(DesktopEntry* entry);
-gboolean desktop_entry_has_category(DesktopEntry* entry, const char* category);
+DesktopEntryType  desktop_entry_get_type     (DesktopEntry *entry);
+const char       *desktop_entry_get_path     (DesktopEntry *entry);
+const char       *desktop_entry_get_basename (DesktopEntry *entry);
+const char       *desktop_entry_get_name     (DesktopEntry *entry);
+const char       *desktop_entry_get_generic_name (DesktopEntry *entry);
+const char       *desktop_entry_get_comment  (DesktopEntry *entry);
+GIcon            *desktop_entry_get_icon     (DesktopEntry *entry);
+gboolean          desktop_entry_get_hidden   (DesktopEntry *entry);
+gboolean          desktop_entry_get_no_display (DesktopEntry *entry);
+gboolean          desktop_entry_get_show_in  (DesktopEntry *entry);
+
+/* Only valid for DESKTOP_ENTRY_DESKTOP */
+GDesktopAppInfo  *desktop_entry_get_app_info (DesktopEntry *entry);
+gboolean desktop_entry_has_categories (DesktopEntry *entry);
+gboolean desktop_entry_has_category   (DesktopEntry *entry,
+                                       const char   *category);
 
 void desktop_entry_add_legacy_category(DesktopEntry* src);
 
-
 typedef struct DesktopEntrySet DesktopEntrySet;
 
 DesktopEntrySet* desktop_entry_set_new(void);
@@ -83,8 +77,6 @@ typedef void (*DesktopEntrySetForeachFunc) (const char* file_id, DesktopEntry* e
 
 void desktop_entry_set_foreach(DesktopEntrySet* set, DesktopEntrySetForeachFunc func, gpointer user_data);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* __DESKTOP_ENTRIES_H__ */
diff --git a/libmenu/entry-directories.c b/libmenu/entry-directories.c
index 2b59b37..2a21807 100644
--- a/libmenu/entry-directories.c
+++ b/libmenu/entry-directories.c
@@ -61,7 +61,10 @@ struct CachedDir {
 	guint have_read_entries: 1;
 	guint deleted: 1;
 
-	guint references: 28;
+  guint references;
+
+  GFunc    notify;
+  gpointer notify_data;
 };
 
 struct CachedDirMonitor {
@@ -70,10 +73,23 @@ struct CachedDirMonitor {
 	gpointer user_data;
 };
 
-static void cached_dir_free(CachedDir* dir);
-static gboolean cached_dir_load_entries_recursive(CachedDir* dir, const char* dirname);
-
-static void handle_cached_dir_changed(MenuMonitor* monitor, MenuMonitorEvent event, const char* path, CachedDir* dir);
+static void     cached_dir_add_reference          (CachedDir *dir);
+static void     cached_dir_remove_reference       (CachedDir *dir);
+static void     cached_dir_free                   (CachedDir  *dir);
+static gboolean cached_dir_load_entries_recursive (CachedDir  *dir,
+                                                   const char *dirname);
+static void     cached_dir_unref                  (CachedDir *dir);
+static void     cached_dir_unref_noparent         (CachedDir *dir);
+static CachedDir * cached_dir_add_subdir          (CachedDir  *dir,
+                                                   const char *basename,
+                                                   const char *path);
+static gboolean cached_dir_remove_subdir          (CachedDir  *dir,
+                                                   const char *basename);
+
+static void handle_cached_dir_changed (MenuMonitor      *monitor,
+				       MenuMonitorEvent  event,
+				       const char       *path,
+				       CachedDir        *dir);
 
 /*
  * Entry directory cache
@@ -81,18 +97,41 @@ static void handle_cached_dir_changed(MenuMonitor* monitor, MenuMonitorEvent eve
 
 static CachedDir* dir_cache = NULL;
 
-static CachedDir* cached_dir_new(const char *name)
+static void
+clear_cache (CachedDir *dir,
+             gpointer  *cache)
 {
-	CachedDir* dir;
+  *cache = NULL;
+}
 
-	dir = g_new0(CachedDir, 1);
+static CachedDir *
+cached_dir_new (const char *name)
+{
+	CachedDir* dir;
 
-	dir->name = g_strdup(name);
+  dir = g_new0 (CachedDir, 1);
+  dir->name = g_strdup (name);
 
 	return dir;
 }
 
-static void cached_dir_free(CachedDir* dir)
+static CachedDir *
+cached_dir_new_full (const char *name,
+                     GFunc       notify,
+                     gpointer    notify_data)
+{
+  CachedDir *dir;
+
+  dir = cached_dir_new (name);
+
+  dir->notify = notify;
+  dir->notify_data = notify_data;
+
+  return dir;
+}
+
+static void
+cached_dir_free (CachedDir *dir)
 {
   if (dir->dir_monitor)
     {
@@ -114,7 +153,7 @@ static void cached_dir_free(CachedDir* dir)
   dir->entries = NULL;
 
   g_slist_foreach (dir->subdirs,
-                   (GFunc) cached_dir_free,
+                   (GFunc) cached_dir_unref_noparent,
                    NULL);
   g_slist_free (dir->subdirs);
   dir->subdirs = NULL;
@@ -123,6 +162,44 @@ static void cached_dir_free(CachedDir* dir)
   g_free (dir);
 }
 
+static CachedDir *
+cached_dir_ref (CachedDir *dir)
+{
+  dir->references++;
+  return dir;
+}
+
+static void
+cached_dir_unref (CachedDir *dir)
+{
+  if (--dir->references == 0)
+    {
+      CachedDir *parent;
+
+      parent = dir->parent;
+
+      if (parent != NULL)
+        cached_dir_remove_subdir (parent, dir->name);
+
+      if (dir->notify)
+        dir->notify (dir, dir->notify_data);
+
+      cached_dir_free (dir);
+    }
+}
+
+static void
+cached_dir_unref_noparent (CachedDir *dir)
+{
+  if (--dir->references == 0)
+    {
+      if (dir->notify)
+        dir->notify (dir, dir->notify_data);
+
+      cached_dir_free (dir);
+    }
+}
+
 static inline CachedDir* find_subdir(CachedDir* dir, const char* subdir)
 {
   GSList *tmp;
@@ -194,7 +271,9 @@ static CachedDir* cached_dir_lookup(const char* canonical)
   int         i;
 
   if (dir_cache == NULL)
-    dir_cache = cached_dir_new ("/");
+    dir_cache = cached_dir_new_full ("/",
+                                     (GFunc) clear_cache,
+                                     &dir_cache);
   dir = dir_cache;
 
   g_assert (canonical != NULL && canonical[0] == G_DIR_SEPARATOR);
@@ -208,12 +287,7 @@ static CachedDir* cached_dir_lookup(const char* canonical)
     {
       CachedDir *subdir;
 
-      if ((subdir = find_subdir (dir, split[i])) == NULL)
-        {
-          subdir = cached_dir_new (split[i]);
-          dir->subdirs = g_slist_prepend (dir->subdirs, subdir);
-          subdir->parent = dir;
-        }
+      subdir = cached_dir_add_subdir (dir, split[i], NULL);
 
       dir = subdir;
 
@@ -283,7 +357,10 @@ static gboolean cached_dir_remove_entry(CachedDir* dir, const char* basename)
   return FALSE;
 }
 
-static gboolean cached_dir_add_subdir(CachedDir* dir, const char* basename, const char* path)
+static CachedDir *
+cached_dir_add_subdir (CachedDir  *dir,
+                       const char *basename,
+                       const char *path)
 {
   CachedDir *subdir;
 
@@ -292,23 +369,23 @@ static gboolean cached_dir_add_subdir(CachedDir* dir, const char* basename, cons
   if (subdir != NULL)
     {
       subdir->deleted = FALSE;
-      return TRUE;
+      return subdir;
     }
 
   subdir = cached_dir_new (basename);
 
-  if (!cached_dir_load_entries_recursive (subdir, path))
+  if (path != NULL && !cached_dir_load_entries_recursive (subdir, path))
     {
       cached_dir_free (subdir);
-      return FALSE;
+      return NULL;
     }
 
   menu_verbose ("Caching dir \"%s\"\n", basename);
 
   subdir->parent = dir;
-  dir->subdirs = g_slist_prepend (dir->subdirs, subdir);
+  dir->subdirs = g_slist_prepend (dir->subdirs, cached_dir_ref (subdir));
 
-  return TRUE;
+  return subdir;
 }
 
 static gboolean cached_dir_remove_subdir(CachedDir* dir, const char* basename)
@@ -321,11 +398,8 @@ static gboolean cached_dir_remove_subdir(CachedDir* dir, const char* basename)
     {
       subdir->deleted = TRUE;
 
-      if (subdir->references == 0)
-        {
-          cached_dir_free (subdir);
-          dir->subdirs = g_slist_remove (dir->subdirs, subdir);
-        }
+      cached_dir_unref (subdir);
+      dir->subdirs = g_slist_remove (dir->subdirs, subdir);
 
       return TRUE;
     }
@@ -333,7 +407,11 @@ static gboolean cached_dir_remove_subdir(CachedDir* dir, const char* basename)
   return FALSE;
 }
 
-static void cached_dir_invoke_monitors(CachedDir* dir)
+static guint   monitors_idle_handler = 0;
+static GSList *pending_monitors_dirs = NULL;
+
+static void
+cached_dir_invoke_monitors (CachedDir *dir)
 {
   GSList *tmp;
 
@@ -348,9 +426,70 @@ static void cached_dir_invoke_monitors(CachedDir* dir)
       tmp = next;
     }
 
+  /* we explicitly don't invoke monitors of the parent since an
+   * event has been queued for it too */
+}
+
+static gboolean
+emit_monitors_in_idle (void)
+{
+  GSList *monitors_to_emit;
+  GSList *tmp;
+
+  monitors_to_emit = pending_monitors_dirs;
+
+  pending_monitors_dirs = NULL;
+  monitors_idle_handler = 0;
+
+  tmp = monitors_to_emit;
+  while (tmp != NULL)
+    {
+      CachedDir *dir = tmp->data;
+
+      cached_dir_invoke_monitors (dir);
+      cached_dir_remove_reference (dir);
+
+      tmp = tmp->next;
+    }
+
+  g_slist_free (monitors_to_emit);
+
+  return FALSE;
+}
+
+static void
+cached_dir_queue_monitor_event (CachedDir *dir)
+{
+  GSList *tmp;
+
+  tmp = pending_monitors_dirs;
+  while (tmp != NULL)
+    {
+      CachedDir *d    = tmp->data;
+      GSList    *next = tmp->next;
+
+      if (dir->parent == d->parent &&
+          g_strcmp0 (dir->name, d->name) == 0)
+        break;
+
+      tmp = next;
+    }
+
+  /* not found, so let's queue it */
+  if (tmp == NULL)
+    {
+      cached_dir_add_reference (dir);
+      pending_monitors_dirs = g_slist_append (pending_monitors_dirs, dir);
+    }
+
   if (dir->parent)
     {
-      cached_dir_invoke_monitors (dir->parent);
+      cached_dir_queue_monitor_event (dir->parent);
+    }
+
+  if (monitors_idle_handler == 0)
+    {
+      monitors_idle_handler = g_idle_add ((GSourceFunc) emit_monitors_in_idle, NULL);
     }
 }
 
@@ -360,16 +499,11 @@ static void handle_cached_dir_changed (MenuMonitor* monitor, MenuMonitorEvent ev
   char     *basename;
   char     *dirname;
 
-  menu_verbose ("'%s' notified of '%s' %s - invalidating cache\n",
-		dir->name,
-                path,
-                event == MENU_MONITOR_EVENT_CREATED ? ("created") :
-                event == MENU_MONITOR_EVENT_DELETED ? ("deleted") : ("changed"));
-
   dirname  = g_path_get_dirname  (path);
   basename = g_path_get_basename (path);
 
   dir = cached_dir_lookup (dirname);
+  cached_dir_add_reference (dir);
 
   if (g_str_has_suffix (basename, ".desktop") ||
       g_str_has_suffix (basename, ".directory"))
@@ -390,12 +524,12 @@ static void handle_cached_dir_changed (MenuMonitor* monitor, MenuMonitorEvent ev
           break;
         }
     }
-  else /* Try recursing */
+  else if (g_file_test (path, G_FILE_TEST_IS_DIR)) /* Try recursing */
     {
       switch (event)
         {
         case MENU_MONITOR_EVENT_CREATED:
-          handled = cached_dir_add_subdir (dir, basename, path);
+          handled = cached_dir_add_subdir (dir, basename, path) != NULL;
           break;
 
         case MENU_MONITOR_EVENT_CHANGED:
@@ -416,14 +550,22 @@ static void handle_cached_dir_changed (MenuMonitor* monitor, MenuMonitorEvent ev
 
   if (handled)
     {
+      menu_verbose ("'%s' notified of '%s' %s - invalidating cache\n",
+                    dir->name,
+                    path,
+                    event == MENU_MONITOR_EVENT_CREATED ? ("created") :
+                    event == MENU_MONITOR_EVENT_DELETED ? ("deleted") : ("changed"));
+
       /* CHANGED events don't change the set of desktop entries */
       if (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED)
         {
           _entry_directory_list_empty_desktop_cache ();
         }
 
-      cached_dir_invoke_monitors (dir);
+      cached_dir_queue_monitor_event (dir);
     }
+
+  cached_dir_remove_reference (dir);
 }
 
 static void cached_dir_ensure_monitor(CachedDir* dir, const char* dirname)
@@ -554,7 +696,7 @@ static void cached_dir_remove_monitor(CachedDir* dir, EntryDirectory* ed, EntryD
 
 static void cached_dir_add_reference(CachedDir* dir)
 {
-  dir->references++;
+  cached_dir_ref (dir);
 
   if (dir->parent != NULL)
     {
@@ -568,29 +710,7 @@ static void cached_dir_remove_reference(CachedDir* dir)
 
   parent = dir->parent;
 
-  if (--dir->references == 0 && dir->deleted)
-    {
-      if (dir->parent != NULL)
-	{
-	  GSList *tmp;
-
-	  tmp = parent->subdirs;
-	  while (tmp != NULL)
-	    {
-	      CachedDir *subdir = tmp->data;
-
-	      if (!strcmp (subdir->name, dir->name))
-		{
-		  parent->subdirs = g_slist_delete_link (parent->subdirs, tmp);
-		  break;
-		}
-
-	      tmp = tmp->next;
-	    }
-	}
-
-      cached_dir_free (dir);
-    }
+  cached_dir_unref (dir);
 
   if (parent != NULL)
     {
diff --git a/libmenu/matemenu-tree.c b/libmenu/matemenu-tree.c
index 31623cf..4ce4d55 100644
--- a/libmenu/matemenu-tree.c
+++ b/libmenu/matemenu-tree.c
@@ -30,59 +30,72 @@
 #include "menu-util.h"
 #include "canonicalize.h"
 
-/*
- * FIXME: it might be useful to be able to construct a menu
- * tree from a traditional directory based menu hierarchy
- * too.
- */
+/* private */
+typedef struct MateMenuTreeItem MateMenuTreeItem;
+#define MATEMENU_TREE_ITEM(i)      ((MateMenuTreeItem *)(i))
+#define MATEMENU_TREE_DIRECTORY(i) ((MateMenuTreeDirectory *)(i))
+#define MATEMENU_TREE_ENTRY(i)     ((MateMenuTreeEntry *)(i))
+#define MATEMENU_TREE_SEPARATOR(i) ((MateMenuTreeSeparator *)(i))
+#define MATEMENU_TREE_HEADER(i)    ((MateMenuTreeHeader *)(i))
+#define MATEMENU_TREE_ALIAS(i)     ((MateMenuTreeAlias *)(i))
+
+enum {
+  PROP_0,
+
+  PROP_MENU_BASENAME,
+  PROP_MENU_PATH,
+  PROP_FLAGS
+};
 
-typedef enum
+/* Signals */
+enum
 {
-  MATEMENU_TREE_ABSOLUTE = 0,
-  MATEMENU_TREE_BASENAME = 1
-} MateMenuTreeType;
+  CHANGED,
+  LAST_SIGNAL
+};
+
+static guint matemenu_tree_signals [LAST_SIGNAL] = { 0 };
 
-struct MateMenuTree
+struct _MateMenuTree
 {
-  MateMenuTreeType type;
-  guint         refcount;
+  GObject       parent_instance;
 
   char *basename;
-  char *absolute_path;
+  char *non_prefixed_basename;
+  char *path;
   char *canonical_path;
 
   MateMenuTreeFlags flags;
-  MateMenuTreeSortKey sort_key;
 
   GSList *menu_file_monitors;
 
   MenuLayoutNode *layout;
   MateMenuTreeDirectory *root;
-
-  GSList *monitors;
-
-  gpointer       user_data;
-  GDestroyNotify dnotify;
+  GHashTable *entries_by_id;
 
   guint canonical : 1;
+  guint loaded    : 1;
 };
 
-typedef struct
-{
-  MateMenuTreeChangedFunc callback;
-  gpointer             user_data;
-} MateMenuTreeMonitor;
+G_DEFINE_TYPE (MateMenuTree, matemenu_tree, G_TYPE_OBJECT)
 
 struct MateMenuTreeItem
 {
+  volatile gint refcount;
+
   MateMenuTreeItemType type;
 
   MateMenuTreeDirectory *parent;
+  MateMenuTree *tree;
+};
 
-  gpointer       user_data;
-  GDestroyNotify dnotify;
+struct MateMenuTreeIter
+{
+  volatile gint refcount;
 
-  guint refcount;
+  MateMenuTreeItem *item;
+  GSList        *contents;
+  GSList        *contents_iter;
 };
 
 struct MateMenuTreeDirectory
@@ -100,23 +113,15 @@ struct MateMenuTreeDirectory
 	GSList           *layout_info;
 	GSList           *contents;
 
-	guint only_unallocated : 1;
-	guint is_root : 1;
-	guint is_nodisplay : 1;
-	guint layout_pending_separator : 1;
-	guint preprocessed : 1;
+  guint only_unallocated : 1;
+  guint is_nodisplay : 1;
+  guint layout_pending_separator : 1;
+  guint preprocessed : 1;
 
 	/* 16 bits should be more than enough; G_MAXUINT16 means no inline header */
 	guint will_inline_header : 16;
 };
 
-typedef struct
-{
-  MateMenuTreeDirectory directory;
-
-  MateMenuTree *tree;
-} MateMenuTreeDirectoryRoot;
-
 struct MateMenuTreeEntry
 {
   MateMenuTreeItem item;
@@ -125,7 +130,7 @@ struct MateMenuTreeEntry
   char         *desktop_file_id;
 
   guint is_excluded : 1;
-  guint is_nodisplay : 1;
+  guint is_unallocated : 1;
 };
 
 struct MateMenuTreeSeparator
@@ -148,13 +153,11 @@ struct MateMenuTreeAlias
   MateMenuTreeItem      *aliased_item;
 };
 
-static MateMenuTree *matemenu_tree_new                 (MateMenuTreeType    type,
-						  const char      *menu_file,
-						  gboolean         canonical,
-						  MateMenuTreeFlags   flags);
-static void      matemenu_tree_load_layout          (MateMenuTree       *tree);
+static gboolean  matemenu_tree_load_layout          (MateMenuTree       *tree,
+                                                  GError         **error);
 static void      matemenu_tree_force_reload         (MateMenuTree       *tree);
-static void      matemenu_tree_build_from_layout    (MateMenuTree       *tree);
+static gboolean  matemenu_tree_build_from_layout    (MateMenuTree       *tree,
+                                                  GError         **error);
 static void      matemenu_tree_force_rebuild        (MateMenuTree       *tree);
 static void      matemenu_tree_resolve_files        (MateMenuTree       *tree,
 						  GHashTable      *loaded_menu_files,
@@ -164,106 +167,6 @@ static void      matemenu_tree_invoke_monitors      (MateMenuTree       *tree);
 
 static void matemenu_tree_item_unref_and_unset_parent (gpointer itemp);
 
-/*
- * The idea is that we cache the menu tree for either a given
- * menu basename or an absolute menu path.
- * If no files exist in $XDG_DATA_DIRS for the basename or the
- * absolute path doesn't exist we just return (and cache) the
- * empty menu tree.
- * We also add a file monitor for the basename in each dir in
- * $XDG_DATA_DIRS, or the absolute path to the menu file, and
- * re-compute if there are any changes.
- */
-
-static GHashTable *matemenu_tree_cache = NULL;
-
-static inline char *
-get_cache_key (MateMenuTree      *tree,
-	       MateMenuTreeFlags  flags)
-{
-  const char *tree_name;
-
-  switch (tree->type)
-    {
-    case MATEMENU_TREE_ABSOLUTE:
-      tree_name = tree->canonical ? tree->canonical_path : tree->absolute_path;
-      break;
-
-    case MATEMENU_TREE_BASENAME:
-      tree_name = tree->basename;
-      break;
-
-    default:
-      g_assert_not_reached ();
-      break;
-    }
-
-  return g_strdup_printf ("%s:0x%x", tree_name, flags);
-}
-
-static void
-matemenu_tree_add_to_cache (MateMenuTree      *tree,
-			 MateMenuTreeFlags  flags)
-{
-  char *cache_key;
-
-  if (matemenu_tree_cache == NULL)
-    {
-      matemenu_tree_cache =
-        g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-    }
-
-  cache_key = get_cache_key (tree, flags);
-
-  menu_verbose ("Adding menu tree to cache: %s\n", cache_key);
-
-  g_hash_table_replace (matemenu_tree_cache, cache_key, tree);
-}
-
-static void
-matemenu_tree_remove_from_cache (MateMenuTree      *tree,
-			      MateMenuTreeFlags  flags)
-{
-  char *cache_key;
-
-  cache_key = get_cache_key (tree, flags);
-
-  menu_verbose ("Removing menu tree from cache: %s\n", cache_key);
-
-  g_hash_table_remove (matemenu_tree_cache, cache_key);
-
-  g_free (cache_key);
-
-  if (g_hash_table_size (matemenu_tree_cache) == 0)
-    {
-      g_hash_table_destroy (matemenu_tree_cache);
-      matemenu_tree_cache = NULL;
-
-      _entry_directory_list_empty_desktop_cache ();
-    }
-}
-
-static MateMenuTree *
-matemenu_tree_lookup_from_cache (const char    *tree_name,
-			      MateMenuTreeFlags  flags)
-{
-  MateMenuTree *retval;
-  char     *cache_key;
-
-  if (matemenu_tree_cache == NULL)
-    return NULL;
-
-  cache_key = g_strdup_printf ("%s:0x%x", tree_name, flags);
-
-  menu_verbose ("Looking up '%s' from menu cache\n", cache_key);
-
-  retval = g_hash_table_lookup (matemenu_tree_cache, cache_key);
-
-  g_free (cache_key);
-
-  return retval ? matemenu_tree_ref (retval) : NULL;
-}
-
 typedef enum
 {
   MENU_FILE_MONITOR_INVALID = 0,
@@ -336,7 +239,7 @@ matemenu_tree_add_menu_file_monitor (MateMenuTree           *tree,
 {
   MenuFileMonitor *monitor;
 
-  monitor = g_new0 (MenuFileMonitor, 1);
+  monitor = g_slice_new0 (MenuFileMonitor);
 
   monitor->type = type;
 
@@ -411,7 +314,7 @@ remove_menu_file_monitor (MenuFileMonitor *monitor,
 
   monitor->type = MENU_FILE_MONITOR_INVALID;
 
-  g_free (monitor);
+  g_slice_free (MenuFileMonitor, monitor);
 }
 
 static void
@@ -426,63 +329,10 @@ matemenu_tree_remove_menu_file_monitors (MateMenuTree *tree)
   tree->menu_file_monitors = NULL;
 }
 
-static MateMenuTree *
-matemenu_tree_lookup_absolute (const char    *absolute,
-			    MateMenuTreeFlags  flags)
-{
-  MateMenuTree  *tree;
-  gboolean    canonical;
-  const char *canonical_path;
-  char       *freeme;
-
-  menu_verbose ("Looking up absolute path in tree cache: \"%s\"\n", absolute);
-
-  if ((tree = matemenu_tree_lookup_from_cache (absolute, flags)) != NULL)
-    return tree;
-
-  canonical = TRUE;
-  canonical_path = freeme = menu_canonicalize_file_name (absolute, FALSE);
-  if (canonical_path == NULL)
-    {
-      menu_verbose ("Failed to canonicalize absolute menu path \"%s\": %s\n",
-                    absolute, g_strerror (errno));
-      canonical = FALSE;
-      canonical_path = absolute;
-    }
-
-  if ((tree = matemenu_tree_lookup_from_cache (canonical_path, flags)) != NULL)
-    return tree;
-
-  tree = matemenu_tree_new (MATEMENU_TREE_ABSOLUTE, canonical_path, canonical, flags);
-
-  g_free (freeme);
-
-  return tree;
-}
-
-static MateMenuTree *
-matemenu_tree_lookup_basename (const char    *basename,
-			    MateMenuTreeFlags  flags)
-{
-  MateMenuTree *tree;
-
-  menu_verbose ("Looking up menu file in tree cache: \"%s\"\n", basename);
-
-  if ((tree = matemenu_tree_lookup_from_cache (basename, flags)) != NULL)
-    return tree;
-
-  return matemenu_tree_new (MATEMENU_TREE_BASENAME, basename, FALSE, flags);
-}
-
 static gboolean
-canonicalize_basename_with_config_dir (MateMenuTree   *tree,
-                                       const char *basename,
-                                       const char *config_dir)
+canonicalize_path (MateMenuTree  *tree,
+                   const char *path)
 {
-  char *path;
-
-  path = g_build_filename (config_dir, "menus",  basename,  NULL);
-
   tree->canonical_path = menu_canonicalize_file_name (path, FALSE);
   if (tree->canonical_path)
     {
@@ -498,9 +348,22 @@ canonicalize_basename_with_config_dir (MateMenuTree   *tree,
 					MENU_FILE_MONITOR_NONEXISTENT_FILE);
     }
 
+  return tree->canonical;
+}
+
+static gboolean
+canonicalize_basename_with_config_dir (MateMenuTree   *tree,
+                                       const char *basename,
+                                       const char *config_dir)
+{
+  gboolean  ret;
+  char     *path;
+
+  path = g_build_filename (config_dir, "menus",  basename,  NULL);
+  ret = canonicalize_path (tree, path);
   g_free (path);
 
-  return tree->canonical;
+  return ret;
 }
 
 static void
@@ -529,60 +392,75 @@ canonicalize_basename (MateMenuTree  *tree,
     }
 }
 
-static gboolean matemenu_tree_canonicalize_path(MateMenuTree* tree)
+static gboolean matemenu_tree_canonicalize_path(MateMenuTree* tree,
+                              GError   **error)
 {
-	if (tree->canonical)
-		return TRUE;
-
-	g_assert(tree->canonical_path == NULL);
-
-	if (tree->type == MATEMENU_TREE_BASENAME)
-	{
-		matemenu_tree_remove_menu_file_monitors (tree);
+  const char *menu_file = NULL;
 
-		if (strcmp(tree->basename, "mate-applications.menu") == 0 && g_getenv("XDG_MENU_PREFIX"))
-		{
-			char* prefixed_basename;
-			prefixed_basename = g_strdup_printf("%s%s", g_getenv("XDG_MENU_PREFIX"), tree->basename);
-			canonicalize_basename(tree, prefixed_basename);
-			g_free(prefixed_basename);
-		}
-
-		if (!tree->canonical)
-			canonicalize_basename(tree, tree->basename);
+  if (tree->canonical)
+    return TRUE;
 
-		if (tree->canonical)
-			menu_verbose("Successfully looked up menu_file for \"%s\": %s\n", tree->basename, tree->canonical_path);
-		else
-			menu_verbose("Failed to look up menu_file for \"%s\"\n", tree->basename);
-	}
-	else /* if (tree->type == MATEMENU_TREE_ABSOLUTE) */
-	{
-		tree->canonical_path = menu_canonicalize_file_name(tree->absolute_path, FALSE);
+	g_assert(tree->canonical_path == NULL);
 
-		if (tree->canonical_path != NULL)
-		{
-			menu_verbose("Successfully looked up menu_file for \"%s\": %s\n", tree->absolute_path, tree->canonical_path);
+  matemenu_tree_remove_menu_file_monitors (tree);
 
-			/*
-			* Replace the cache entry with the canonicalized version
-			*/
-			matemenu_tree_remove_from_cache (tree, tree->flags);
+  if (tree->path)
+    {
+      menu_file = tree->path;
+      canonicalize_path (tree, tree->path);
+    }
+  else
+    {
+      const gchar *xdg_menu_prefix;
 
-			matemenu_tree_remove_menu_file_monitors(tree);
-			matemenu_tree_add_menu_file_monitor(tree, tree->canonical_path, MENU_FILE_MONITOR_FILE);
+      menu_file = tree->basename;
+      xdg_menu_prefix = g_getenv ("XDG_MENU_PREFIX");
 
-			tree->canonical = TRUE;
+      if (xdg_menu_prefix != NULL)
+        {
+          gchar *prefixed_basename;
+
+          prefixed_basename = g_strdup_printf ("%sapplications.menu",
+                                               xdg_menu_prefix);
+
+          /* Some gnome-menus using applications just use "applications.menu"
+           * as the basename and expect gnome-menus to prefix it. Others (e.g.
+           * Alacarte) explicitly use "${XDG_MENU_PREFIX}applications.menu" as
+           * the basename, because they want to save changes to the right files
+           * in ~. In both cases, we want to use "applications-merged" as the
+           * merge directory (as required by the fd.o menu spec), so we save
+           * the non-prefixed basename and use it later when calling
+           * menu_layout_load().
+           */
+          if (!g_strcmp0 (tree->basename, "mate-applications.menu") ||
+              !g_strcmp0 (tree->basename, prefixed_basename))
+            {
+              canonicalize_basename (tree, prefixed_basename);
+              g_free (tree->non_prefixed_basename);
+              tree->non_prefixed_basename = g_strdup ("mate-applications.menu");
+            }
+          g_free (prefixed_basename);
+        }
 
-			matemenu_tree_add_to_cache (tree, tree->flags);
-		}
-		else
-		{
-			menu_verbose("Failed to look up menu_file for \"%s\"\n", tree->absolute_path);
-		}
-	}
+      if (!tree->canonical)
+        canonicalize_basename (tree, tree->basename);
+    }
 
-	return tree->canonical;
+  if (tree->canonical)
+    {
+      menu_verbose ("Successfully looked up menu_file for \"%s\": %s\n",
+                    menu_file, tree->canonical_path);
+      return TRUE;
+    }
+  else
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_FAILED,
+                   "Failed to look up menu_file for \"%s\"\n",
+                   menu_file);
+      return FALSE;
+    }
 }
 
 static void
@@ -601,96 +479,126 @@ matemenu_tree_force_recanonicalize (MateMenuTree *tree)
     }
 }
 
-MateMenuTree* matemenu_tree_lookup(const char* menu_file, MateMenuTreeFlags flags)
+/**
+ * matemenu_tree_new:
+ * @menu_basename: Basename of menu file
+ * @flags: Flags controlling menu content
+ *
+ * Returns: (transfer full): A new #MateMenuTree instance
+ */
+MateMenuTree *
+matemenu_tree_new (const char     *menu_basename,
+                MateMenuTreeFlags  flags)
 {
-  MateMenuTree *retval;
-
-  g_return_val_if_fail (menu_file != NULL, NULL);
+  g_return_val_if_fail (menu_basename != NULL, NULL);
 
-  flags &= MATEMENU_TREE_FLAGS_MASK;
-
-  if (g_path_is_absolute (menu_file))
-    retval = matemenu_tree_lookup_absolute (menu_file, flags);
-  else
-    retval = matemenu_tree_lookup_basename (menu_file, flags);
-
-  g_assert (retval != NULL);
-
-  return retval;
+  return g_object_new (MATEMENU_TYPE_TREE,
+                       "menu-basename", menu_basename,
+                       "flags", flags,
+                       NULL);
 }
 
-static MateMenuTree *
-matemenu_tree_new (MateMenuTreeType   type,
-		const char     *menu_file,
-		gboolean        canonical,
-		MateMenuTreeFlags  flags)
+/**
+ * matemenu_tree_new_fo_path:
+ * @menu_path: Path of menu file
+ * @flags: Flags controlling menu content
+ *
+ * Returns: (transfer full): A new #MateMenuTree instance
+ */
+MateMenuTree *
+matemenu_tree_new_for_path (const char     *menu_path,
+                         MateMenuTreeFlags  flags)
 {
-  MateMenuTree *tree;
+  g_return_val_if_fail (menu_path != NULL, NULL);
 
-  tree = g_new0 (MateMenuTree, 1);
+  return g_object_new (MATEMENU_TYPE_TREE,
+                       "menu-path", menu_path,
+                       "flags", flags,
+                       NULL);
+}
 
-  tree->type     = type;
-  tree->flags    = flags;
-  tree->refcount = 1;
+static GObject *
+matemenu_tree_constructor (GType                  type,
+                        guint                  n_construct_properties,
+                        GObjectConstructParam *construct_properties)
+{
+	GObject   *obj;
+	MateMenuTree *self;
 
-  tree->sort_key = MATEMENU_TREE_SORT_NAME;
+	obj = G_OBJECT_CLASS (matemenu_tree_parent_class)->constructor (type,
+                                                                     n_construct_properties,
+                                                                     construct_properties);
 
-  if (tree->type == MATEMENU_TREE_BASENAME)
-    {
-      g_assert (canonical == FALSE);
-      tree->basename = g_strdup (menu_file);
-    }
-  else
-    {
-      tree->canonical     = canonical != FALSE;
-      tree->absolute_path = g_strdup (menu_file);
+        /* If MateMenuTree:menu-path is set, then we should make sure that
+         * MateMenuTree:menu-basename is unset (especially as it has a default
+         * value). This has to be done here, in the constructor, since the
+         * properties are construct-only. */
 
-      if (tree->canonical)
-	{
-	  tree->canonical_path = g_strdup (menu_file);
-	  matemenu_tree_add_menu_file_monitor (tree,
-					    tree->canonical_path,
-					    MENU_FILE_MONITOR_FILE);
-	}
-      else
-	{
-	  matemenu_tree_add_menu_file_monitor (tree,
-					    tree->absolute_path,
-					    MENU_FILE_MONITOR_NONEXISTENT_FILE);
-	}
-    }
+	self = MATEMENU_TREE (obj);
 
-  matemenu_tree_add_to_cache (tree, tree->flags);
+        if (self->path != NULL)
+          g_object_set (self, "menu-basename", NULL, NULL);
 
-  return tree;
+	return obj;
 }
 
-MateMenuTree *
-matemenu_tree_ref (MateMenuTree *tree)
+static void
+matemenu_tree_set_property (GObject         *object,
+                         guint            prop_id,
+                         const GValue    *value,
+                         GParamSpec      *pspec)
 {
-  g_return_val_if_fail (tree != NULL, NULL);
-  g_return_val_if_fail (tree->refcount > 0, NULL);
+  MateMenuTree *self = MATEMENU_TREE (object);
+
+  switch (prop_id)
+    {
+    case PROP_MENU_BASENAME:
+      self->basename = g_value_dup_string (value);
+      break;
 
-  tree->refcount++;
+    case PROP_MENU_PATH:
+      self->path = g_value_dup_string (value);
+      break;
+
+    case PROP_FLAGS:
+      self->flags = g_value_get_flags (value);
+      break;
 
-  return tree;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
 }
 
-void
-matemenu_tree_unref (MateMenuTree *tree)
+static void
+matemenu_tree_get_property (GObject         *object,
+                         guint            prop_id,
+                         GValue          *value,
+                         GParamSpec      *pspec)
 {
-  g_return_if_fail (tree != NULL);
-  g_return_if_fail (tree->refcount >= 1);
+  MateMenuTree *self = MATEMENU_TREE (object);
 
-  if (--tree->refcount > 0)
-    return;
-
-  if (tree->dnotify)
-    tree->dnotify (tree->user_data);
-  tree->user_data = NULL;
-  tree->dnotify   = NULL;
+  switch (prop_id)
+    {
+    case PROP_MENU_BASENAME:
+      g_value_set_string (value, self->basename);
+      break;
+    case PROP_MENU_PATH:
+      g_value_set_string (value, self->path);
+      break;
+    case PROP_FLAGS:
+      g_value_set_flags (value, self->flags);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
 
-  matemenu_tree_remove_from_cache (tree, tree->flags);
+static void
+matemenu_tree_finalize (GObject *object)
+{
+  MateMenuTree *tree = MATEMENU_TREE (object);
 
   matemenu_tree_force_recanonicalize (tree);
 
@@ -698,81 +606,155 @@ matemenu_tree_unref (MateMenuTree *tree)
     g_free (tree->basename);
   tree->basename = NULL;
 
-  if (tree->absolute_path != NULL)
-    g_free (tree->absolute_path);
-  tree->absolute_path = NULL;
+  g_free (tree->non_prefixed_basename);
+  tree->non_prefixed_basename = NULL;
 
-  g_slist_foreach (tree->monitors, (GFunc) g_free, NULL);
-  g_slist_free (tree->monitors);
-  tree->monitors = NULL;
+  if (tree->path != NULL)
+    g_free (tree->path);
+  tree->path = NULL;
 
-  g_free (tree);
-}
+  if (tree->canonical_path != NULL)
+    g_free (tree->canonical_path);
+  tree->canonical_path = NULL;
 
-void
-matemenu_tree_set_user_data (MateMenuTree       *tree,
-			  gpointer        user_data,
-			  GDestroyNotify  dnotify)
-{
-  g_return_if_fail (tree != NULL);
+  g_hash_table_destroy (tree->entries_by_id);
+  tree->entries_by_id = NULL;
 
-  if (tree->dnotify != NULL)
-    tree->dnotify (tree->user_data);
+  G_OBJECT_CLASS (matemenu_tree_parent_class)->finalize (object);
+}
 
-  tree->dnotify   = dnotify;
-  tree->user_data = user_data;
+static void
+matemenu_tree_init (MateMenuTree *self)
+{
+  self->entries_by_id = g_hash_table_new (g_str_hash, g_str_equal);
 }
 
-gpointer
-matemenu_tree_get_user_data (MateMenuTree *tree)
+static void
+matemenu_tree_class_init (MateMenuTreeClass *klass)
 {
-  g_return_val_if_fail (tree != NULL, NULL);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
-  return tree->user_data;
-}
+  gobject_class->constructor = matemenu_tree_constructor;
+  gobject_class->get_property = matemenu_tree_get_property;
+  gobject_class->set_property = matemenu_tree_set_property;
+  gobject_class->finalize = matemenu_tree_finalize;
 
+  /**
+   * MateMenuTree:menu-basename:
+   *
+   * The name of the menu file; must be a basename or a relative path. The file
+   * will be looked up in $XDG_CONFIG_DIRS/menus/. See the Desktop Menu
+   * specification.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_MENU_BASENAME,
+                                   g_param_spec_string ("menu-basename", "", "",
+                                                        "applications.menu",
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  /**
+   * MateMenuTree:menu-path:
+   *
+   * The full path of the menu file. If set, MateMenuTree:menu-basename will get
+   * ignored.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_MENU_PATH,
+                                   g_param_spec_string ("menu-path", "", "",
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  /**
+   * MateMenuTree:flags:
+   *
+   * Flags controlling the content of the menu.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_FLAGS,
+                                   g_param_spec_flags ("flags", "", "",
+                                                       MATEMENU_TYPE_TREE_FLAGS,
+                                                       MATEMENU_TREE_FLAGS_NONE,
+                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  /**
+   * MateMenuTree:changed:
+   *
+   * This signal is emitted when applications are added, removed, or
+   * upgraded.  But note the new data will only be visible after
+   * matemenu_tree_load_sync() or a variant thereof is invoked.
+   */
+  matemenu_tree_signals[CHANGED] =
+      g_signal_new ("changed",
+                    G_TYPE_FROM_CLASS (klass),
+                    G_SIGNAL_RUN_LAST,
+                    0,
+                    NULL, NULL,
+                    g_cclosure_marshal_VOID__VOID,
+                    G_TYPE_NONE, 0);
+}
+
+/**
+ * matemenu_tree_get_canonical_menu_path:
+ * @tree: a #MateMenuTree
+ *
+ * This function is only available if the tree has been loaded via
+ * matemenu_tree_load_sync() or a variant thereof.
+ *
+ * Returns: The absolute and canonicalized path to the loaded menu file
+ */
 const char *
-matemenu_tree_get_menu_file (MateMenuTree *tree)
+matemenu_tree_get_canonical_menu_path (MateMenuTree *tree)
 {
-  /* FIXME: this is horribly ugly. But it's done to keep the API. Would be bad
-   * to break the API only for a "const char *" => "char *" change. The other
-   * alternative is to leak the memory, which is bad too. */
-  static char *ugly_result_cache = NULL;
+  g_return_val_if_fail (MATEMENU_IS_TREE (tree), NULL);
+  g_return_val_if_fail (tree->loaded, NULL);
 
-  g_return_val_if_fail (tree != NULL, NULL);
+  return tree->canonical_path;
+}
 
-  /* we need to canonicalize the path so we actually find out the real menu
-   * file that is being used -- and take into account XDG_MENU_PREFIX */
-  if (!matemenu_tree_canonicalize_path (tree))
-    return NULL;
+/**
+ * matemenu_tree_load_sync:
+ * @tree: a #MateMenuTree
+ * @error: a #GError
+ *
+ * Synchronously load the menu contents.  This function
+ * performs a significant amount of blocking I/O if the
+ * tree has not been loaded yet.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ */
+gboolean
+matemenu_tree_load_sync (MateMenuTree  *tree,
+                      GError    **error)
+{
+  GError *local_error = NULL;
 
-  if (ugly_result_cache != NULL)
-    {
-      g_free (ugly_result_cache);
-      ugly_result_cache = NULL;
-    }
+  if (tree->loaded)
+    return TRUE;
 
-  if (tree->type == MATEMENU_TREE_BASENAME)
+  if (!matemenu_tree_build_from_layout (tree, &local_error))
     {
-      ugly_result_cache = g_path_get_basename (tree->canonical_path);
-      return ugly_result_cache;
+      if (local_error)
+        g_propagate_error (error, local_error);
+      return FALSE;
     }
-  else
-    return tree->absolute_path;
+
+  tree->loaded = TRUE;
+
+  return TRUE;
 }
 
+/**
+ * matemenu_tree_get_root_directory:
+ * @tree: a #MateMenuTree
+ *
+ * Get the root directory; you must have loaded the tree first (at
+ * least once) via matemenu_tree_load_sync() or a variant thereof.
+ *
+ * Returns: (transfer full): Root of the tree
+ */
 MateMenuTreeDirectory *
 matemenu_tree_get_root_directory (MateMenuTree *tree)
 {
   g_return_val_if_fail (tree != NULL, NULL);
-
-  if (!tree->root)
-    {
-      matemenu_tree_build_from_layout (tree);
-
-      if (!tree->root)
-        return NULL;
-    }
+  g_return_val_if_fail (tree->loaded, NULL);
 
   return matemenu_tree_item_ref (tree->root);
 }
@@ -809,7 +791,7 @@ find_path (MateMenuTreeDirectory *directory,
     {
       MateMenuTreeItem *item = tmp->data;
 
-      if (matemenu_tree_item_get_type (item) != MATEMENU_TREE_ITEM_DIRECTORY)
+      if (item->type != MATEMENU_TREE_ITEM_DIRECTORY)
         {
           tmp = tmp->next;
           continue;
@@ -856,154 +838,283 @@ matemenu_tree_get_directory_from_path (MateMenuTree  *tree,
   return directory ? matemenu_tree_item_ref (directory) : NULL;
 }
 
-MateMenuTreeSortKey
-matemenu_tree_get_sort_key (MateMenuTree *tree)
+/**
+ * matemenu_tree_get_entry_by_id:
+ * @tree: a #MateMenuTree
+ * @id: a desktop file ID
+ * 
+ * Look up the entry corresponding to the given "desktop file id".
+ *
+ * Returns: (transfer full): A newly referenced #MateMenuTreeEntry, or %NULL if none
+ */
+MateMenuTreeEntry     *
+matemenu_tree_get_entry_by_id (MateMenuTree  *tree,
+			    const char *id)
 {
-  g_return_val_if_fail (tree != NULL, MATEMENU_TREE_SORT_NAME);
-  g_return_val_if_fail (tree->refcount > 0, MATEMENU_TREE_SORT_NAME);
+  MateMenuTreeEntry *entry;
+
+  g_return_val_if_fail (tree->loaded, NULL);
 
-  return tree->sort_key;
+  entry = g_hash_table_lookup (tree->entries_by_id, id);
+  if (entry != NULL)
+    matemenu_tree_item_ref (entry);
+
+  return entry;
 }
 
-void
-matemenu_tree_set_sort_key (MateMenuTree        *tree,
-			 MateMenuTreeSortKey  sort_key)
+static void
+matemenu_tree_invoke_monitors (MateMenuTree *tree)
 {
-  g_return_if_fail (tree != NULL);
-  g_return_if_fail (tree->refcount > 0);
-  g_return_if_fail (sort_key >= MATEMENU_TREE_SORT_FIRST);
-  g_return_if_fail (sort_key <= MATEMENU_TREE_SORT_LAST);
-
-  if (sort_key == tree->sort_key)
-    return;
+  g_signal_emit (tree, matemenu_tree_signals[CHANGED], 0);
+}
 
-  tree->sort_key = sort_key;
-  matemenu_tree_force_rebuild (tree);
+static MateMenuTreeDirectory *
+get_parent (MateMenuTreeItem *item)
+{
+  g_return_val_if_fail (item != NULL, NULL);
+  return item->parent ? matemenu_tree_item_ref (item->parent) : NULL;
 }
 
-void
-matemenu_tree_add_monitor (MateMenuTree            *tree,
-                       MateMenuTreeChangedFunc   callback,
-                       gpointer               user_data)
+/**
+ * matemenu_tree_directory_get_parent:
+ * @directory: a #MateMenuTreeDirectory
+ *
+ * Returns: (transfer full): The parent directory, or %NULL if none
+ */
+MateMenuTreeDirectory *
+matemenu_tree_directory_get_parent (MateMenuTreeDirectory *directory)
 {
-  MateMenuTreeMonitor *monitor;
-  GSList           *tmp;
+  return get_parent ((MateMenuTreeItem *)directory);
+}
 
-  g_return_if_fail (tree != NULL);
-  g_return_if_fail (callback != NULL);
+/**
+ * matemenu_tree_entry_get_parent:
+ * @entry: a #MateMenuTreeEntry
+ *
+ * Returns: (transfer full): The parent directory, or %NULL if none
+ */
+MateMenuTreeDirectory *
+matemenu_tree_entry_get_parent (MateMenuTreeEntry *entry)
+{
+  return get_parent ((MateMenuTreeItem *)entry);
+}
 
-  tmp = tree->monitors;
-  while (tmp != NULL)
-    {
-      monitor = tmp->data;
+/**
+ * matemenu_tree_alias_get_parent:
+ * @alias: a #MateMenuTreeAlias
+ *
+ * Returns: (transfer full): The parent directory, or %NULL if none
+ */
+MateMenuTreeDirectory *
+matemenu_tree_alias_get_parent (MateMenuTreeAlias *alias)
+{
+  return get_parent ((MateMenuTreeItem *)alias);
+}
 
-      if (monitor->callback  == callback &&
-          monitor->user_data == user_data)
-        break;
+/**
+ * matemenu_tree_header_get_parent:
+ * @header: a #MateMenuTreeHeader
+ *
+ * Returns: (transfer full): The parent directory, or %NULL if none
+ */
+MateMenuTreeDirectory *
+matemenu_tree_header_get_parent (MateMenuTreeHeader *header)
+{
+  return get_parent ((MateMenuTreeItem *)header);
+}
 
-      tmp = tmp->next;
-    }
+/**
+ * matemenu_tree_separator_get_parent:
+ * @separator: a #MateMenuTreeSeparator
+ *
+ * Returns: (transfer full): The parent directory, or %NULL if none
+ */
+MateMenuTreeDirectory *
+matemenu_tree_separator_get_parent (MateMenuTreeSeparator *separator)
+{
+  return get_parent ((MateMenuTreeItem *)separator);
+}
 
-  if (tmp == NULL)
-    {
-      monitor = g_new0 (MateMenuTreeMonitor, 1);
+static void
+matemenu_tree_item_set_parent (MateMenuTreeItem      *item,
+			    MateMenuTreeDirectory *parent)
+{
+  g_return_if_fail (item != NULL);
 
-      monitor->callback  = callback;
-      monitor->user_data = user_data;
+  item->parent = parent;
+}
 
-      tree->monitors = g_slist_append (tree->monitors, monitor);
-    }
+/**
+ * matemenu_tree_iter_ref: (skip)
+ * @iter: iter
+ *
+ * Increment the reference count of @iter
+ */
+MateMenuTreeIter *
+matemenu_tree_iter_ref (MateMenuTreeIter *iter)
+{
+  g_atomic_int_inc (&iter->refcount);
+  return iter;
 }
 
+/**
+ * matemenu_tree_iter_unref: (skip)
+ * @iter: iter
+ *
+ * Decrement the reference count of @iter
+ */
 void
-matemenu_tree_remove_monitor (MateMenuTree            *tree,
-			   MateMenuTreeChangedFunc  callback,
-			   gpointer              user_data)
+matemenu_tree_iter_unref (MateMenuTreeIter *iter)
 {
-  GSList *tmp;
-
-  g_return_if_fail (tree != NULL);
-  g_return_if_fail (callback != NULL);
-
-  tmp = tree->monitors;
-  while (tmp != NULL)
-    {
-      MateMenuTreeMonitor *monitor = tmp->data;
-      GSList          *next = tmp->next;
+  if (!g_atomic_int_dec_and_test (&iter->refcount))
+    return;
 
-      if (monitor->callback  == callback &&
-          monitor->user_data == user_data)
-        {
-          tree->monitors = g_slist_delete_link (tree->monitors, tmp);
-          g_free (monitor);
-        }
+  g_slist_foreach (iter->contents, (GFunc)matemenu_tree_item_unref, NULL);
+  g_slist_free (iter->contents);
 
-      tmp = next;
-    }
+  g_slice_free (MateMenuTreeIter, iter);
 }
 
-static void
-matemenu_tree_invoke_monitors (MateMenuTree *tree)
+/**
+ * matemenu_tree_directory_iter:
+ * @directory: directory
+ *
+ * Returns: (transfer full): A new iterator over the directory contents
+ */
+MateMenuTreeIter *
+matemenu_tree_directory_iter (MateMenuTreeDirectory *directory)
 {
-  GSList *tmp;
+  MateMenuTreeIter *iter;
 
-  tmp = tree->monitors;
-  while (tmp != NULL)
-    {
-      MateMenuTreeMonitor *monitor = tmp->data;
-      GSList           *next    = tmp->next;
+  g_return_val_if_fail (directory != NULL, NULL);
 
-      monitor->callback (tree, monitor->user_data);
+  iter = g_slice_new0 (MateMenuTreeIter);
+  iter->refcount = 1;
 
-      tmp = next;
-    }
+  iter->contents = g_slist_copy (directory->contents);
+  iter->contents_iter = iter->contents;
+  g_slist_foreach (iter->contents, (GFunc) matemenu_tree_item_ref, NULL);
+
+  return iter;
 }
 
+/**
+ * matemenu_tree_iter_next:
+ * @iter: iter
+ *
+ * Change the iterator to the next item, and return its type.  If
+ * there are no more items, %MATEMENU_TREE_ITEM_INVALID is returned.
+ *
+ * Returns: The type of the next item that can be retrived from the iterator
+ */
 MateMenuTreeItemType
-matemenu_tree_item_get_type (MateMenuTreeItem *item)
+matemenu_tree_iter_next (MateMenuTreeIter *iter)
 {
-  g_return_val_if_fail (item != NULL, 0);
+  g_return_val_if_fail (iter != NULL, MATEMENU_TREE_ITEM_INVALID);
 
-  return item->type;
+  if (iter->contents_iter)
+    {
+      iter->item = iter->contents_iter->data;
+      iter->contents_iter = iter->contents_iter->next;
+      return iter->item->type;
+    }
+  else
+    return MATEMENU_TREE_ITEM_INVALID;
 }
 
+/**
+ * matemenu_tree_iter_get_directory:
+ * @iter: iter
+ *
+ * This method may only be called if matemenu_tree_iter_next()
+ * returned MATEMENU_TREE_ITEM_DIRECTORY.
+ *
+ * Returns: (transfer full): A directory
+ */
 MateMenuTreeDirectory *
-matemenu_tree_item_get_parent (MateMenuTreeItem *item)
+matemenu_tree_iter_get_directory (MateMenuTreeIter *iter)
 {
-  g_return_val_if_fail (item != NULL, NULL);
+  g_return_val_if_fail (iter != NULL, NULL);
+  g_return_val_if_fail (iter->item != NULL, NULL);
+  g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_DIRECTORY, NULL);
 
-  return item->parent ? matemenu_tree_item_ref (item->parent) : NULL;
+  return (MateMenuTreeDirectory*)matemenu_tree_item_ref (iter->item);
 }
 
-static void
-matemenu_tree_item_set_parent (MateMenuTreeItem      *item,
-			    MateMenuTreeDirectory *parent)
+/**
+ * matemenu_tree_iter_get_entry:
+ * @iter: iter
+ *
+ * This method may only be called if matemenu_tree_iter_next()
+ * returned MATEMENU_TREE_ITEM_ENTRY.
+ *
+ * Returns: (transfer full): An entry
+ */
+MateMenuTreeEntry *
+matemenu_tree_iter_get_entry (MateMenuTreeIter *iter)
 {
-  g_return_if_fail (item != NULL);
+  g_return_val_if_fail (iter != NULL, NULL);
+  g_return_val_if_fail (iter->item != NULL, NULL);
+  g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_ENTRY, NULL);
 
-  item->parent = parent;
+  return (MateMenuTreeEntry*)matemenu_tree_item_ref (iter->item);
 }
 
-GSList *
-matemenu_tree_directory_get_contents (MateMenuTreeDirectory *directory)
+/**
+ * matemenu_tree_iter_get_header:
+ * @iter: iter
+ *
+ * This method may only be called if matemenu_tree_iter_next()
+ * returned MATEMENU_TREE_ITEM_HEADER.
+ *
+ * Returns: (transfer full): A header
+ */
+MateMenuTreeHeader *
+matemenu_tree_iter_get_header (MateMenuTreeIter *iter)
 {
-  GSList *retval;
-  GSList *tmp;
+  g_return_val_if_fail (iter != NULL, NULL);
+  g_return_val_if_fail (iter->item != NULL, NULL);
+  g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_HEADER, NULL);
 
-  g_return_val_if_fail (directory != NULL, NULL);
+  return (MateMenuTreeHeader*)matemenu_tree_item_ref (iter->item);
+}
 
-  retval = NULL;
+/**
+ * matemenu_tree_iter_get_alias:
+ * @iter: iter
+ *
+ * This method may only be called if matemenu_tree_iter_next()
+ * returned MATEMENU_TREE_ITEM_ALIAS.
+ *
+ * Returns: (transfer full): An alias
+ */
+MateMenuTreeAlias *
+matemenu_tree_iter_get_alias (MateMenuTreeIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, NULL);
+  g_return_val_if_fail (iter->item != NULL, NULL);
+  g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_ALIAS, NULL);
 
-  tmp = directory->contents;
-  while (tmp != NULL)
-    {
-      retval = g_slist_prepend (retval,
-                                matemenu_tree_item_ref (tmp->data));
+  return (MateMenuTreeAlias*)matemenu_tree_item_ref (iter->item);
+}
 
-      tmp = tmp->next;
-    }
+/**
+ * matemenu_tree_iter_get_separator:
+ * @iter: iter
+ *
+ * This method may only be called if matemenu_tree_iter_next()
+ * returned #MATEMENU_TREE_ITEM_SEPARATOR.
+ *
+ * Returns: (transfer full): A separator
+ */
+MateMenuTreeSeparator *
+matemenu_tree_iter_get_separator (MateMenuTreeIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, NULL);
+  g_return_val_if_fail (iter->item != NULL, NULL);
+  g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_SEPARATOR, NULL);
 
-  return g_slist_reverse (retval);
+  return (MateMenuTreeSeparator*)matemenu_tree_item_ref (iter->item);
 }
 
 const char *
@@ -1017,6 +1128,17 @@ matemenu_tree_directory_get_name (MateMenuTreeDirectory *directory)
   return desktop_entry_get_name (directory->directory_entry);
 }
 
+const char *
+matemenu_tree_directory_get_generic_name (MateMenuTreeDirectory *directory)
+{
+  g_return_val_if_fail (directory != NULL, NULL);
+
+  if (!directory->directory_entry)
+    return NULL;
+
+  return desktop_entry_get_generic_name (directory->directory_entry);
+}
+
 const char *
 matemenu_tree_directory_get_comment (MateMenuTreeDirectory *directory)
 {
@@ -1028,7 +1150,16 @@ matemenu_tree_directory_get_comment (MateMenuTreeDirectory *directory)
   return desktop_entry_get_comment (directory->directory_entry);
 }
 
-const char* matemenu_tree_directory_get_icon(MateMenuTreeDirectory* directory)
+/**
+ * matemenu_tree_directory_get_icon:
+ * @directory: a #MateMenuTreeDirectory
+ *
+ * Gets the icon for the directory.
+ *
+ * Returns: (transfer none): The #GIcon for this directory
+ */
+GIcon *
+matemenu_tree_directory_get_icon (MateMenuTreeDirectory *directory)
 {
 	g_return_val_if_fail(directory != NULL, NULL);
 
@@ -1057,47 +1188,28 @@ matemenu_tree_directory_get_menu_id (MateMenuTreeDirectory *directory)
   return directory->name;
 }
 
-static void
-matemenu_tree_directory_set_tree (MateMenuTreeDirectory *directory,
-			       MateMenuTree          *tree)
+gboolean
+matemenu_tree_directory_get_is_nodisplay (MateMenuTreeDirectory *directory)
 {
-  MateMenuTreeDirectoryRoot *root;
-
-  g_assert (directory != NULL);
-  g_assert (directory->is_root);
-
-  root = (MateMenuTreeDirectoryRoot *) directory;
+  g_return_val_if_fail (directory != NULL, FALSE);
 
-  root->tree = tree;
+  return directory->is_nodisplay;
 }
 
+/**
+ * matemenu_tree_directory_get_tree:
+ * @directory: A #MateMenuTreeDirectory
+ *
+ * Grab the tree associated with a #MateMenuTreeItem.
+ *
+ * Returns: (transfer full): The #MateMenuTree
+ */
 MateMenuTree *
 matemenu_tree_directory_get_tree (MateMenuTreeDirectory *directory)
 {
-  MateMenuTreeDirectoryRoot *root;
-
   g_return_val_if_fail (directory != NULL, NULL);
 
-  while (MATEMENU_TREE_ITEM (directory)->parent != NULL)
-    directory = MATEMENU_TREE_DIRECTORY (MATEMENU_TREE_ITEM (directory)->parent);
-
-  if (!directory->is_root)
-    return NULL;
-
-  root = (MateMenuTreeDirectoryRoot *) directory;
-
-  if (root->tree)
-    matemenu_tree_ref (root->tree);
-
-  return root->tree;
-}
-
-gboolean
-matemenu_tree_directory_get_is_nodisplay (MateMenuTreeDirectory *directory)
-{
-  g_return_val_if_fail (directory != NULL, FALSE);
-
-  return directory->is_nodisplay;
+  return g_object_ref (directory->item.tree);
 }
 
 static void
@@ -1136,98 +1248,124 @@ matemenu_tree_directory_make_path (MateMenuTreeDirectory *directory,
   return g_string_free (path, FALSE);
 }
 
-const char *
-matemenu_tree_entry_get_name (MateMenuTreeEntry *entry)
+/**
+ * matemenu_tree_entry_get_app_info:
+ * @entry: a #MateMenuTreeEntry
+ *
+ * Returns: (transfer none): The #GDesktopAppInfo for this entry
+ */
+GDesktopAppInfo *
+matemenu_tree_entry_get_app_info (MateMenuTreeEntry *entry)
 {
   g_return_val_if_fail (entry != NULL, NULL);
 
-  return desktop_entry_get_name (entry->desktop_entry);
+  return desktop_entry_get_app_info (entry->desktop_entry);
 }
 
 const char *
-matemenu_tree_entry_get_generic_name (MateMenuTreeEntry *entry)
+matemenu_tree_entry_get_desktop_file_path (MateMenuTreeEntry *entry)
 {
   g_return_val_if_fail (entry != NULL, NULL);
 
-  return desktop_entry_get_generic_name (entry->desktop_entry);
+  return desktop_entry_get_path (entry->desktop_entry);
 }
 
 const char *
-matemenu_tree_entry_get_display_name (MateMenuTreeEntry *entry)
+matemenu_tree_entry_get_desktop_file_id (MateMenuTreeEntry *entry)
 {
-  const char *display_name;
-
   g_return_val_if_fail (entry != NULL, NULL);
 
-  display_name = desktop_entry_get_full_name (entry->desktop_entry);
-  if (!display_name || display_name[0] == '\0')
-    display_name = desktop_entry_get_name (entry->desktop_entry);
-
-  return display_name;
+  return entry->desktop_file_id;
 }
 
-const char *
-matemenu_tree_entry_get_comment (MateMenuTreeEntry *entry)
+gboolean
+matemenu_tree_entry_get_is_nodisplay_recurse (MateMenuTreeEntry *entry)
 {
-  g_return_val_if_fail (entry != NULL, NULL);
+  MateMenuTreeDirectory *directory;
+  GDesktopAppInfo *app_info;
 
-  return desktop_entry_get_comment (entry->desktop_entry);
-}
+  g_return_val_if_fail (entry != NULL, FALSE);
 
-const char* matemenu_tree_entry_get_icon(MateMenuTreeEntry *entry)
-{
-	g_return_val_if_fail (entry != NULL, NULL);
+  app_info = matemenu_tree_entry_get_app_info (entry);
 
-	return desktop_entry_get_icon(entry->desktop_entry);
-}
+  if (g_desktop_app_info_get_nodisplay (app_info))
+    return TRUE;
 
-const char* matemenu_tree_entry_get_exec(MateMenuTreeEntry* entry)
-{
-	g_return_val_if_fail(entry != NULL, NULL);
+  directory = entry->item.parent;
+  while (directory != NULL)
+    {
+      if (directory->is_nodisplay)
+        return TRUE;
+
+      directory = directory->item.parent;
+    }
 
-	return desktop_entry_get_exec(entry->desktop_entry);
+  return FALSE;
 }
 
-gboolean matemenu_tree_entry_get_launch_in_terminal(MateMenuTreeEntry* entry)
+gboolean
+matemenu_tree_entry_get_is_excluded (MateMenuTreeEntry *entry)
 {
   g_return_val_if_fail(entry != NULL, FALSE);
 
-  return desktop_entry_get_launch_in_terminal(entry->desktop_entry);
+  return entry->is_excluded;
 }
 
-const char* matemenu_tree_entry_get_desktop_file_path(MateMenuTreeEntry* entry)
+gboolean
+matemenu_tree_entry_get_is_unallocated (MateMenuTreeEntry *entry)
 {
-	g_return_val_if_fail(entry != NULL, NULL);
+  g_return_val_if_fail (entry != NULL, FALSE);
 
-	return desktop_entry_get_path(entry->desktop_entry);
+  return entry->is_unallocated;
 }
 
-const char* matemenu_tree_entry_get_desktop_file_id(MateMenuTreeEntry* entry)
+/**
+ * matemenu_tree_entry_get_tree:
+ * @entry: A #MateMenuTreeEntry
+ *
+ * Grab the tree associated with a #MateMenuTreeEntry.
+ *
+ * Returns: (transfer full): The #MateMenuTree
+ */
+MateMenuTree *
+matemenu_tree_entry_get_tree (MateMenuTreeEntry *entry)
 {
 	g_return_val_if_fail(entry != NULL, NULL);
 
-	return entry->desktop_file_id;
+  return g_object_ref (entry->item.tree);
 }
 
-gboolean matemenu_tree_entry_get_is_excluded(MateMenuTreeEntry* entry)
+MateMenuTreeDirectory *
+matemenu_tree_header_get_directory (MateMenuTreeHeader *header)
 {
-	g_return_val_if_fail(entry != NULL, FALSE);
+  g_return_val_if_fail (header != NULL, NULL);
 
-	return entry->is_excluded;
+  return matemenu_tree_item_ref (header->directory);
 }
 
-gboolean matemenu_tree_entry_get_is_nodisplay(MateMenuTreeEntry* entry)
+/**
+ * matemenu_tree_header_get_tree:
+ * @header: A #MateMenuTreeHeader
+ *
+ * Grab the tree associated with a #MateMenuTreeHeader.
+ *
+ * Returns: (transfer full): The #MateMenuTree
+ */
+MateMenuTree *
+matemenu_tree_header_get_tree (MateMenuTreeHeader *header)
 {
-	g_return_val_if_fail(entry != NULL, FALSE);
+  g_return_val_if_fail (header != NULL, NULL);
 
-	return entry->is_nodisplay;
+  return g_object_ref (header->item.tree);
 }
 
-MateMenuTreeDirectory* matemenu_tree_header_get_directory(MateMenuTreeHeader* header)
+MateMenuTreeItemType
+matemenu_tree_alias_get_aliased_item_type (MateMenuTreeAlias *alias)
 {
-	g_return_val_if_fail (header != NULL, NULL);
+  g_return_val_if_fail (alias != NULL, MATEMENU_TREE_ITEM_INVALID);
 
-	return matemenu_tree_item_ref(header->directory);
+  g_assert (alias->aliased_item != NULL);
+  return alias->aliased_item->type;
 }
 
 MateMenuTreeDirectory* matemenu_tree_alias_get_directory(MateMenuTreeAlias* alias)
@@ -1237,40 +1375,81 @@ MateMenuTreeDirectory* matemenu_tree_alias_get_directory(MateMenuTreeAlias* alia
 	return matemenu_tree_item_ref(alias->directory);
 }
 
-MateMenuTreeItem *
-matemenu_tree_alias_get_item (MateMenuTreeAlias *alias)
+/**
+ * matemenu_tree_alias_get_tree:
+ * @alias: A #MateMenuTreeAlias
+ *
+ * Grab the tree associated with a #MateMenuTreeAlias.
+ *
+ * Returns: (transfer full): The #MateMenuTree
+ */
+MateMenuTree *
+matemenu_tree_alias_get_tree (MateMenuTreeAlias *alias)
 {
   g_return_val_if_fail (alias != NULL, NULL);
 
-  return matemenu_tree_item_ref (alias->aliased_item);
+  return g_object_ref (alias->item.tree);
 }
 
-static MateMenuTreeDirectory *
-matemenu_tree_directory_new (MateMenuTreeDirectory *parent,
-			  const char         *name,
-			  gboolean            is_root)
+/**
+ * matemenu_tree_separator_get_tree:
+ * @separator: A #MateMenuTreeSeparator
+ *
+ * Grab the tree associated with a #MateMenuTreeSeparator.
+ *
+ * Returns: (transfer full): The #MateMenuTree
+ */
+MateMenuTree *
+matemenu_tree_separator_get_tree (MateMenuTreeSeparator *separator)
 {
-  MateMenuTreeDirectory *retval;
+  g_return_val_if_fail (separator != NULL, NULL);
 
-  if (!is_root)
-    {
-      retval = g_new0 (MateMenuTreeDirectory, 1);
-    }
-  else
-    {
-      MateMenuTreeDirectoryRoot *root;
+  return g_object_ref (separator->item.tree);
+}
+
+/**
+ * matemenu_tree_alias_get_aliased_directory:
+ * @alias: alias
+ *
+ * Returns: (transfer full): The aliased directory entry
+ */
+MateMenuTreeDirectory *
+matemenu_tree_alias_get_aliased_directory (MateMenuTreeAlias *alias)
+{
+  g_return_val_if_fail (alias != NULL, NULL);
+  g_return_val_if_fail (alias->aliased_item->type == MATEMENU_TREE_ITEM_DIRECTORY, NULL);
 
-      root = g_new0 (MateMenuTreeDirectoryRoot, 1);
+  return (MateMenuTreeDirectory *) matemenu_tree_item_ref (alias->aliased_item);
+}
+
+/**
+ * matemenu_tree_alias_get_aliased_entry:
+ * @alias: alias
+ *
+ * Returns: (transfer full): The aliased entry
+ */
+MateMenuTreeEntry *
+matemenu_tree_alias_get_aliased_entry (MateMenuTreeAlias *alias)
+{
+  g_return_val_if_fail (alias != NULL, NULL);
+  g_return_val_if_fail (alias->aliased_item->type == MATEMENU_TREE_ITEM_ENTRY, NULL);
 
-      retval = MATEMENU_TREE_DIRECTORY (root);
+  return (MateMenuTreeEntry *) matemenu_tree_item_ref (alias->aliased_item);
+}
 
-      retval->is_root = TRUE;
-    }
+static MateMenuTreeDirectory *
+matemenu_tree_directory_new (MateMenuTree          *tree,
+                          MateMenuTreeDirectory *parent,
+			  const char         *name)
+{
+  MateMenuTreeDirectory *retval;
 
+  retval = g_slice_new0 (MateMenuTreeDirectory);
 
   retval->item.type     = MATEMENU_TREE_ITEM_DIRECTORY;
   retval->item.parent   = parent;
   retval->item.refcount = 1;
+  retval->item.tree     = tree;
 
   retval->name                = g_strdup (name);
   retval->directory_entry     = NULL;
@@ -1336,6 +1515,8 @@ matemenu_tree_directory_finalize (MateMenuTreeDirectory *directory)
 
   g_free (directory->name);
   directory->name = NULL;
+
+  g_slice_free (MateMenuTreeDirectory, directory);
 }
 
 static MateMenuTreeSeparator *
@@ -1343,26 +1524,36 @@ matemenu_tree_separator_new (MateMenuTreeDirectory *parent)
 {
   MateMenuTreeSeparator *retval;
 
-  retval = g_new0 (MateMenuTreeSeparator, 1);
+  retval = g_slice_new0 (MateMenuTreeSeparator);
 
   retval->item.type     = MATEMENU_TREE_ITEM_SEPARATOR;
   retval->item.parent   = parent;
   retval->item.refcount = 1;
+  retval->item.tree     = parent->item.tree;
 
   return retval;
 }
 
+static void
+matemenu_tree_separator_finalize (MateMenuTreeSeparator *separator)
+{
+  g_assert (separator->item.refcount == 0);
+
+  g_slice_free (MateMenuTreeSeparator, separator);
+}
+
 static MateMenuTreeHeader *
 matemenu_tree_header_new (MateMenuTreeDirectory *parent,
 		       MateMenuTreeDirectory *directory)
 {
   MateMenuTreeHeader *retval;
 
-  retval = g_new0 (MateMenuTreeHeader, 1);
+  retval = g_slice_new0 (MateMenuTreeHeader);
 
   retval->item.type     = MATEMENU_TREE_ITEM_HEADER;
   retval->item.parent   = parent;
   retval->item.refcount = 1;
+  retval->item.tree     = parent->item.tree;
 
   retval->directory = matemenu_tree_item_ref (directory);
 
@@ -1379,6 +1570,8 @@ matemenu_tree_header_finalize (MateMenuTreeHeader *header)
   if (header->directory != NULL)
     matemenu_tree_item_unref (header->directory);
   header->directory = NULL;
+
+  g_slice_free (MateMenuTreeHeader, header);
 }
 
 static MateMenuTreeAlias *
@@ -1388,17 +1581,21 @@ matemenu_tree_alias_new (MateMenuTreeDirectory *parent,
 {
   MateMenuTreeAlias *retval;
 
-  retval = g_new0 (MateMenuTreeAlias, 1);
+  retval = g_slice_new0 (MateMenuTreeAlias);
 
   retval->item.type     = MATEMENU_TREE_ITEM_ALIAS;
   retval->item.parent   = parent;
   retval->item.refcount = 1;
+  retval->item.tree     = parent->item.tree;
 
   retval->directory    = matemenu_tree_item_ref (directory);
   if (item->type != MATEMENU_TREE_ITEM_ALIAS)
     retval->aliased_item = matemenu_tree_item_ref (item);
   else
-    retval->aliased_item = matemenu_tree_item_ref (matemenu_tree_alias_get_item (MATEMENU_TREE_ALIAS (item)));
+    {
+      MateMenuTreeAlias *alias = MATEMENU_TREE_ALIAS (item);
+      retval->aliased_item = matemenu_tree_item_ref (alias->aliased_item);
+    }
 
   matemenu_tree_item_set_parent (MATEMENU_TREE_ITEM (retval->directory), NULL);
   matemenu_tree_item_set_parent (retval->aliased_item, NULL);
@@ -1418,6 +1615,8 @@ matemenu_tree_alias_finalize (MateMenuTreeAlias *alias)
   if (alias->aliased_item != NULL)
     matemenu_tree_item_unref (alias->aliased_item);
   alias->aliased_item = NULL;
+
+  g_slice_free (MateMenuTreeAlias, alias);
 }
 
 static MateMenuTreeEntry *
@@ -1425,20 +1624,21 @@ matemenu_tree_entry_new (MateMenuTreeDirectory *parent,
 		      DesktopEntry       *desktop_entry,
 		      const char         *desktop_file_id,
 		      gboolean            is_excluded,
-                      gboolean            is_nodisplay)
+                      gboolean            is_unallocated)
 {
   MateMenuTreeEntry *retval;
 
-  retval = g_new0 (MateMenuTreeEntry, 1);
+  retval = g_slice_new0 (MateMenuTreeEntry);
 
   retval->item.type     = MATEMENU_TREE_ITEM_ENTRY;
   retval->item.parent   = parent;
   retval->item.refcount = 1;
+  retval->item.tree     = parent->item.tree;
 
   retval->desktop_entry   = desktop_entry_ref (desktop_entry);
   retval->desktop_file_id = g_strdup (desktop_file_id);
   retval->is_excluded     = is_excluded != FALSE;
-  retval->is_nodisplay    = is_nodisplay != FALSE;
+  retval->is_unallocated  = is_unallocated != FALSE;
 
   return retval;
 }
@@ -1454,6 +1654,8 @@ matemenu_tree_entry_finalize (MateMenuTreeEntry *entry)
   if (entry->desktop_entry)
     desktop_entry_unref (entry->desktop_entry);
   entry->desktop_entry = NULL;
+
+  g_slice_free (MateMenuTreeEntry, entry);
 }
 
 static int
@@ -1470,14 +1672,21 @@ matemenu_tree_entry_compare_by_id (MateMenuTreeItem *a,
                  MATEMENU_TREE_ENTRY (b)->desktop_file_id);
 }
 
-gpointer matemenu_tree_item_ref(gpointer itemp)
+/**
+ * matemenu_tree_item_ref:
+ * @item: a #MateMenuTreeItem
+ *
+ * Returns: (transfer full): The same @item, or %NULL if @item is not a valid #MateMenuTreeItem
+ */
+gpointer
+matemenu_tree_item_ref (gpointer itemp)
 {
 	MateMenuTreeItem* item = (MateMenuTreeItem*) itemp;
 
 	g_return_val_if_fail(item != NULL, NULL);
 	g_return_val_if_fail(item->refcount > 0, NULL);
 
-	item->refcount++;
+  g_atomic_int_inc (&item->refcount);
 
 	return item;
 }
@@ -1492,7 +1701,7 @@ matemenu_tree_item_unref (gpointer itemp)
   g_return_if_fail (item != NULL);
   g_return_if_fail (item->refcount > 0);
 
-  if (--item->refcount == 0)
+  if (g_atomic_int_dec_and_test (&(item->refcount)))
     {
       switch (item->type)
 	{
@@ -1505,6 +1714,7 @@ matemenu_tree_item_unref (gpointer itemp)
 	  break;
 
 	case MATEMENU_TREE_ITEM_SEPARATOR:
+	  matemenu_tree_separator_finalize (MATEMENU_TREE_SEPARATOR (item));
 	  break;
 
 	case MATEMENU_TREE_ITEM_HEADER:
@@ -1519,15 +1729,6 @@ matemenu_tree_item_unref (gpointer itemp)
 	  g_assert_not_reached ();
 	  break;
 	}
-
-      if (item->dnotify)
-	item->dnotify (item->user_data);
-      item->user_data = NULL;
-      item->dnotify   = NULL;
-
-      item->parent = NULL;
-
-      g_free (item);
     }
 }
 
@@ -1544,31 +1745,9 @@ matemenu_tree_item_unref_and_unset_parent (gpointer itemp)
   matemenu_tree_item_unref (item);
 }
 
-void
-matemenu_tree_item_set_user_data (MateMenuTreeItem  *item,
-			       gpointer        user_data,
-			       GDestroyNotify  dnotify)
-{
-  g_return_if_fail (item != NULL);
-
-  if (item->dnotify != NULL)
-    item->dnotify (item->user_data);
-
-  item->dnotify   = dnotify;
-  item->user_data = user_data;
-}
-
-gpointer
-matemenu_tree_item_get_user_data (MateMenuTreeItem *item)
-{
-  g_return_val_if_fail (item != NULL, NULL);
-
-  return item->user_data;
-}
-
 static inline const char *
 matemenu_tree_item_compare_get_name_helper (MateMenuTreeItem    *item,
-					 MateMenuTreeSortKey  sort_key)
+					 MateMenuTreeFlags    flags)
 {
   const char *name;
 
@@ -1584,25 +1763,17 @@ matemenu_tree_item_compare_get_name_helper (MateMenuTreeItem    *item,
       break;
 
     case MATEMENU_TREE_ITEM_ENTRY:
-      switch (sort_key)
-	{
-	case MATEMENU_TREE_SORT_NAME:
-	  name = desktop_entry_get_name (MATEMENU_TREE_ENTRY (item)->desktop_entry);
-	  break;
-	case MATEMENU_TREE_SORT_DISPLAY_NAME:
-	  name = matemenu_tree_entry_get_display_name (MATEMENU_TREE_ENTRY (item));
-	  break;
-	default:
-	  g_assert_not_reached ();
-	  break;
-	}
+      if (flags & MATEMENU_TREE_FLAGS_SORT_DISPLAY_NAME)
+        name = g_app_info_get_display_name (G_APP_INFO (matemenu_tree_entry_get_app_info (MATEMENU_TREE_ENTRY (item))));
+      else
+        name = desktop_entry_get_name (MATEMENU_TREE_ENTRY (item)->desktop_entry);
       break;
 
     case MATEMENU_TREE_ITEM_ALIAS:
       {
         MateMenuTreeItem *dir;
         dir = MATEMENU_TREE_ITEM (MATEMENU_TREE_ALIAS (item)->directory);
-        name = matemenu_tree_item_compare_get_name_helper (dir, sort_key);
+        name = matemenu_tree_item_compare_get_name_helper (dir, flags);
       }
       break;
 
@@ -1619,16 +1790,16 @@ matemenu_tree_item_compare_get_name_helper (MateMenuTreeItem    *item,
 static int
 matemenu_tree_item_compare (MateMenuTreeItem *a,
 			 MateMenuTreeItem *b,
-			 gpointer       sort_key_p)
+			 gpointer       flags_p)
 {
   const char       *name_a;
   const char       *name_b;
-  MateMenuTreeSortKey  sort_key;
+  MateMenuTreeFlags    flags;
 
-  sort_key = GPOINTER_TO_INT (sort_key_p);
+  flags = GPOINTER_TO_INT (flags_p);
 
-  name_a = matemenu_tree_item_compare_get_name_helper (a, sort_key);
-  name_b = matemenu_tree_item_compare_get_name_helper (b, sort_key);
+  name_a = matemenu_tree_item_compare_get_name_helper (a, flags);
+  name_b = matemenu_tree_item_compare_get_name_helper (b, flags);
 
   return g_utf8_collate (name_a, name_b);
 }
@@ -1744,7 +1915,7 @@ load_merge_file (MateMenuTree      *tree,
 
   menu_verbose ("Merging file \"%s\"\n", canonical);
 
-  to_merge = menu_layout_load (canonical, NULL, NULL);
+  to_merge = menu_layout_load (canonical, tree->non_prefixed_basename, NULL);
   if (to_merge == NULL)
     {
       menu_verbose ("No menu for file \"%s\" found when merging\n",
@@ -2906,33 +3077,26 @@ matemenu_tree_execute_moves (MateMenuTree      *tree,
     matemenu_tree_strip_duplicate_children (tree, layout);
 }
 
-static void
-matemenu_tree_load_layout (MateMenuTree *tree)
+static gboolean
+matemenu_tree_load_layout (MateMenuTree  *tree,
+                        GError    **error)
 {
   GHashTable *loaded_menu_files;
-  GError     *error;
 
   if (tree->layout)
-    return;
+    return TRUE;
 
-  if (!matemenu_tree_canonicalize_path (tree))
-    return;
+  if (!matemenu_tree_canonicalize_path (tree, error))
+    return FALSE;
 
   menu_verbose ("Loading menu layout from \"%s\"\n",
                 tree->canonical_path);
 
-  error = NULL;
   tree->layout = menu_layout_load (tree->canonical_path,
-                                   tree->type == MATEMENU_TREE_BASENAME ?
-                                        tree->basename : NULL,
-                                   &error);
-  if (tree->layout == NULL)
-    {
-      g_warning ("Error loading menu layout from \"%s\": %s",
-                 tree->canonical_path, error->message);
-      g_error_free (error);
-      return;
-    }
+                                   tree->non_prefixed_basename,
+                                   error);
+  if (!tree->layout)
+    return FALSE;
 
   loaded_menu_files = g_hash_table_new (g_str_hash, g_str_equal);
   g_hash_table_insert (loaded_menu_files, tree->canonical_path, GUINT_TO_POINTER (TRUE));
@@ -2941,6 +3105,8 @@ matemenu_tree_load_layout (MateMenuTree *tree)
 
   matemenu_tree_strip_duplicate_children (tree, tree->layout);
   matemenu_tree_execute_moves (tree, tree->layout, NULL);
+
+  return TRUE;
 }
 
 static void
@@ -3192,7 +3358,7 @@ entries_listify_foreach (const char         *desktop_file_id,
                                            desktop_entry,
                                            desktop_file_id,
                                            FALSE,
-                                           desktop_entry_get_no_display (desktop_entry)));
+                                           FALSE));
 }
 
 static void
@@ -3206,7 +3372,21 @@ excluded_entries_listify_foreach (const char         *desktop_file_id,
 					   desktop_entry,
 					   desktop_file_id,
 					   TRUE,
-                                           desktop_entry_get_no_display (desktop_entry)));
+                                           FALSE));
+}
+
+static void
+unallocated_entries_listify_foreach (const char         *desktop_file_id,
+                                     DesktopEntry       *desktop_entry,
+                                     MateMenuTreeDirectory *directory)
+{
+  directory->entries =
+    g_slist_prepend (directory->entries,
+		     matemenu_tree_entry_new (directory,
+                                           desktop_entry,
+                                           desktop_file_id,
+                                           FALSE,
+                                           TRUE));
 }
 
 static void
@@ -3256,9 +3436,8 @@ process_layout (MateMenuTree          *tree,
   g_assert (menu_layout_node_get_type (layout) == MENU_LAYOUT_NODE_MENU);
   g_assert (menu_layout_node_menu_get_name (layout) != NULL);
 
-  directory = matemenu_tree_directory_new (parent,
-					menu_layout_node_menu_get_name (layout),
-					parent == NULL);
+  directory = matemenu_tree_directory_new (tree, parent,
+					menu_layout_node_menu_get_name (layout));
 
   menu_verbose ("=== Menu name = %s ===\n", directory->name);
 
@@ -3462,9 +3641,9 @@ process_layout (MateMenuTree          *tree,
             }
         }
 
-      if (!desktop_entry_get_show_in_mate (directory->directory_entry))
+      if (!desktop_entry_get_show_in (directory->directory_entry))
         {
-          menu_verbose ("Not showing menu %s because OnlyShowIn!=MATE or NotShowIn=MATE\n",
+          menu_verbose ("Not showing menu %s because OnlyShowIn!=$DESKTOP or NotShowIn=$DESKTOP (with $DESKTOP=${XDG_CURRENT_DESKTOP:-GNOME})\n",
                         desktop_entry_get_name (directory->directory_entry));
           deleted = TRUE;
         }
@@ -3509,6 +3688,9 @@ process_layout (MateMenuTree          *tree,
       GSList         *next  = tmp->next;
       gboolean        delete = FALSE;
 
+      /* If adding a new condition to delete here, it has to be added to
+       * get_still_unallocated_foreach() too */
+
       if (desktop_entry_get_hidden (entry->desktop_entry))
         {
           menu_verbose ("Deleting %s because Hidden=true\n",
@@ -3524,19 +3706,15 @@ process_layout (MateMenuTree          *tree,
           delete = TRUE;
         }
 
-      if (!desktop_entry_get_show_in_mate (entry->desktop_entry))
+      if (!desktop_entry_get_show_in (entry->desktop_entry))
         {
-          menu_verbose ("Deleting %s because OnlyShowIn!=MATE or NotShowIn=MATE\n",
+          menu_verbose ("Deleting %s because OnlyShowIn!=$DESKTOP or NotShowIn=$DESKTOP (with $DESKTOP=${XDG_CURRENT_DESKTOP:-GNOME})\n",
                         desktop_entry_get_name (entry->desktop_entry));
           delete = TRUE;
         }
 
-      if (desktop_entry_get_tryexec_failed (entry->desktop_entry))
-        {
-          menu_verbose ("Deleting %s because TryExec failed\n",
-                        desktop_entry_get_name (entry->desktop_entry));
-          delete = TRUE;
-        }
+      /* No need to filter out based on TryExec since GDesktopAppInfo cannot
+       * deal with .desktop files with a failed TryExec. */
 
       if (delete)
         {
@@ -3556,7 +3734,8 @@ process_layout (MateMenuTree          *tree,
 static void
 process_only_unallocated (MateMenuTree          *tree,
 			  MateMenuTreeDirectory *directory,
-			  DesktopEntrySet    *allocated)
+			  DesktopEntrySet    *allocated,
+			  DesktopEntrySet    *unallocated_used)
 {
   GSList *tmp;
 
@@ -3578,6 +3757,10 @@ process_only_unallocated (MateMenuTree          *tree,
                                                         tmp);
               matemenu_tree_item_unref_and_unset_parent (entry);
             }
+          else
+            {
+              desktop_entry_set_add_entry (unallocated_used, entry->desktop_entry, entry->desktop_file_id);
+            }
 
           tmp = next;
         }
@@ -3588,12 +3771,45 @@ process_only_unallocated (MateMenuTree          *tree,
     {
       MateMenuTreeDirectory *subdir = tmp->data;
 
-      process_only_unallocated (tree, subdir, allocated);
+      process_only_unallocated (tree, subdir, allocated, unallocated_used);
 
       tmp = tmp->next;
    }
 }
 
+typedef struct
+{
+  MateMenuTree *tree;
+  DesktopEntrySet *allocated;
+  DesktopEntrySet *unallocated_used;
+  DesktopEntrySet *still_unallocated;
+} GetStillUnallocatedForeachData;
+
+static void
+get_still_unallocated_foreach (const char                     *file_id,
+                               DesktopEntry                   *entry,
+                               GetStillUnallocatedForeachData *data)
+{
+  if (desktop_entry_set_lookup (data->allocated, file_id))
+    return;
+
+  if (desktop_entry_set_lookup (data->unallocated_used, file_id))
+    return;
+
+  /* Same rules than at the end of process_layout() */
+  if (desktop_entry_get_hidden (entry))
+    return;
+
+  if (!(data->tree->flags & MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY) &&
+      desktop_entry_get_no_display (entry))
+    return;
+
+  if (!desktop_entry_get_show_in (entry))
+    return;
+
+  desktop_entry_set_add_entry (data->still_unallocated, entry, file_id);
+}
+
 static void preprocess_layout_info (MateMenuTree          *tree,
                                     MateMenuTreeDirectory *directory);
 
@@ -3729,7 +3945,7 @@ preprocess_layout_info_subdir_helper (MateMenuTree          *tree,
 
           menu_verbose ("Inline aliasing '%s' to '%s'\n",
                         item->type == MATEMENU_TREE_ITEM_ENTRY ?
-                          matemenu_tree_entry_get_name (MATEMENU_TREE_ENTRY (item)) :
+                          g_app_info_get_name (G_APP_INFO (matemenu_tree_entry_get_app_info (MATEMENU_TREE_ENTRY (item)))) :
                           (item->type == MATEMENU_TREE_ITEM_DIRECTORY ?
                              matemenu_tree_directory_get_name (MATEMENU_TREE_DIRECTORY (item)) :
                              matemenu_tree_directory_get_name (MATEMENU_TREE_ALIAS (item)->directory)),
@@ -4148,7 +4364,7 @@ merge_subdirs (MateMenuTree          *tree,
 
   subdirs = g_slist_sort_with_data (subdirs,
 				    (GCompareDataFunc) matemenu_tree_item_compare,
-				     GINT_TO_POINTER (MATEMENU_TREE_SORT_NAME));
+                                    GINT_TO_POINTER (MATEMENU_TREE_FLAGS_NONE));
 
   tmp = subdirs;
   while (tmp != NULL)
@@ -4193,7 +4409,7 @@ merge_entries (MateMenuTree          *tree,
 
   entries = g_slist_sort_with_data (entries,
 				    (GCompareDataFunc) matemenu_tree_item_compare,
-				    GINT_TO_POINTER (tree->sort_key));
+                                    GINT_TO_POINTER (tree->flags));
 
   tmp = entries;
   while (tmp != NULL)
@@ -4242,7 +4458,7 @@ merge_subdirs_and_entries (MateMenuTree          *tree,
 
   items = g_slist_sort_with_data (items,
 				  (GCompareDataFunc) matemenu_tree_item_compare,
-				  GINT_TO_POINTER (tree->sort_key));
+                                  GINT_TO_POINTER (tree->flags));
 
   tmp = items;
   while (tmp != NULL)
@@ -4250,7 +4466,7 @@ merge_subdirs_and_entries (MateMenuTree          *tree,
       MateMenuTreeItem     *item = tmp->data;
       MateMenuTreeItemType  type;
 
-      type = matemenu_tree_item_get_type (item);
+      type = item->type;
 
       if (type == MATEMENU_TREE_ITEM_ALIAS)
         {
@@ -4497,16 +4713,55 @@ handle_entries_changed (MenuLayoutNode *layout,
 }
 
 static void
-matemenu_tree_build_from_layout (MateMenuTree *tree)
+update_entry_index (MateMenuTree           *tree,
+		    MateMenuTreeDirectory  *dir)
+{
+  MateMenuTreeIter *iter = matemenu_tree_directory_iter (dir);
+  MateMenuTreeItemType next_type;
+
+  while ((next_type = matemenu_tree_iter_next (iter)) != MATEMENU_TREE_ITEM_INVALID)
+    {
+      gpointer item = NULL;
+
+      switch (next_type)
+        {
+        case MATEMENU_TREE_ITEM_ENTRY:
+          {
+	    const char *id;
+
+            item = matemenu_tree_iter_get_entry (iter);
+            id = matemenu_tree_entry_get_desktop_file_id (item);
+            if (id != NULL)
+              g_hash_table_insert (tree->entries_by_id, (char*)id, item);
+          }
+          break;
+        case MATEMENU_TREE_ITEM_DIRECTORY:
+          {
+            item = matemenu_tree_iter_get_directory (iter);
+            update_entry_index (tree, (MateMenuTreeDirectory*)item);
+          }
+          break;
+        default:
+          break;
+        }
+      if (item != NULL)
+        matemenu_tree_item_unref (item);
+    }
+
+  matemenu_tree_iter_unref (iter);
+}
+
+static gboolean
+matemenu_tree_build_from_layout (MateMenuTree  *tree,
+                              GError    **error)
 {
   DesktopEntrySet *allocated;
 
   if (tree->root)
-    return;
+    return TRUE;
 
-  matemenu_tree_load_layout (tree);
-  if (!tree->layout)
-    return;
+  if (!matemenu_tree_load_layout (tree, error))
+    return FALSE;
 
   menu_verbose ("Building menu tree from layout\n");
 
@@ -4519,9 +4774,39 @@ matemenu_tree_build_from_layout (MateMenuTree *tree)
                                allocated);
   if (tree->root)
     {
-      matemenu_tree_directory_set_tree (tree->root, tree);
+      DesktopEntrySet *unallocated_used;
+
+      unallocated_used = desktop_entry_set_new ();
+
+      process_only_unallocated (tree, tree->root, allocated, unallocated_used);
+      if (tree->flags & MATEMENU_TREE_FLAGS_INCLUDE_UNALLOCATED)
+        {
+          DesktopEntrySet *entry_pool;
+          DesktopEntrySet *still_unallocated;
+          GetStillUnallocatedForeachData data;
+
+          entry_pool = _entry_directory_list_get_all_desktops (menu_layout_node_menu_get_app_dirs (find_menu_child (tree->layout)));
+          still_unallocated = desktop_entry_set_new ();
+
+          data.tree = tree;
+          data.allocated = allocated;
+          data.unallocated_used = unallocated_used;
+          data.still_unallocated = still_unallocated;
+
+          desktop_entry_set_foreach (entry_pool,
+                                     (DesktopEntrySetForeachFunc) get_still_unallocated_foreach,
+                                     &data);
+
+          desktop_entry_set_unref (entry_pool);
 
-      process_only_unallocated (tree, tree->root, allocated);
+          desktop_entry_set_foreach (still_unallocated,
+                                     (DesktopEntrySetForeachFunc) unallocated_entries_listify_foreach,
+                                     tree->root);
+
+          desktop_entry_set_unref (still_unallocated);
+        }
+
+      desktop_entry_set_unref (unallocated_used);
 
       /* process the layout info part that can move/remove items:
        * inline, show_empty, etc. */
@@ -4530,12 +4815,16 @@ matemenu_tree_build_from_layout (MateMenuTree *tree)
        * according to the layout info */
       process_layout_info (tree, tree->root);
 
+      update_entry_index (tree, tree->root);
+
       menu_layout_node_root_add_entries_monitor (tree->layout,
                                                  (MenuLayoutNodeEntriesChangedFunc) handle_entries_changed,
                                                  tree);
     }
 
   desktop_entry_set_unref (allocated);
+
+  return TRUE;
 }
 
 static void
@@ -4543,9 +4832,10 @@ matemenu_tree_force_rebuild (MateMenuTree *tree)
 {
   if (tree->root)
     {
-      matemenu_tree_directory_set_tree (tree->root, NULL);
+      g_hash_table_remove_all (tree->entries_by_id);
       matemenu_tree_item_unref (tree->root);
       tree->root = NULL;
+      tree->loaded = FALSE;
 
       g_assert (tree->layout != NULL);
 
@@ -4554,3 +4844,102 @@ matemenu_tree_force_rebuild (MateMenuTree *tree)
                                                     tree);
     }
 }
+
+GType
+matemenu_tree_iter_get_type (void)
+{
+  static GType gtype = G_TYPE_INVALID;
+  if (gtype == G_TYPE_INVALID)
+    {
+      gtype = g_boxed_type_register_static ("MateMenuTreeIter",
+          (GBoxedCopyFunc)matemenu_tree_iter_ref,
+          (GBoxedFreeFunc)matemenu_tree_iter_unref);
+    }
+  return gtype;
+}
+
+GType
+matemenu_tree_directory_get_type (void)
+{
+  static GType gtype = G_TYPE_INVALID;
+  if (gtype == G_TYPE_INVALID)
+    {
+      gtype = g_boxed_type_register_static ("MateMenuTreeDirectory",
+          (GBoxedCopyFunc)matemenu_tree_item_ref,
+          (GBoxedFreeFunc)matemenu_tree_item_unref);
+    }
+  return gtype;
+}
+
+GType
+matemenu_tree_entry_get_type (void)
+{
+  static GType gtype = G_TYPE_INVALID;
+  if (gtype == G_TYPE_INVALID)
+    {
+      gtype = g_boxed_type_register_static ("MateMenuTreeEntry",
+          (GBoxedCopyFunc)matemenu_tree_item_ref,
+          (GBoxedFreeFunc)matemenu_tree_item_unref);
+    }
+  return gtype;
+}
+
+GType
+matemenu_tree_separator_get_type (void)
+{
+  static GType gtype = G_TYPE_INVALID;
+  if (gtype == G_TYPE_INVALID)
+    {
+      gtype = g_boxed_type_register_static ("MateMenuTreeSeparator",
+          (GBoxedCopyFunc)matemenu_tree_item_ref,
+          (GBoxedFreeFunc)matemenu_tree_item_unref);
+    }
+  return gtype;
+}
+
+GType
+matemenu_tree_header_get_type (void)
+{
+  static GType gtype = G_TYPE_INVALID;
+  if (gtype == G_TYPE_INVALID)
+    {
+      gtype = g_boxed_type_register_static ("MateMenuTreeHeader",
+          (GBoxedCopyFunc)matemenu_tree_item_ref,
+          (GBoxedFreeFunc)matemenu_tree_item_unref);
+    }
+  return gtype;
+}
+
+GType
+matemenu_tree_alias_get_type (void)
+{
+  static GType gtype = G_TYPE_INVALID;
+  if (gtype == G_TYPE_INVALID)
+    {
+      gtype = g_boxed_type_register_static ("MateMenuTreeAlias",
+          (GBoxedCopyFunc)matemenu_tree_item_ref,
+          (GBoxedFreeFunc)matemenu_tree_item_unref);
+    }
+  return gtype;
+}
+
+GType
+matemenu_tree_flags_get_type (void)
+{
+  static GType enum_type_id = 0;
+  if (G_UNLIKELY (!enum_type_id))
+    {
+      static const GFlagsValue values[] = {
+        { MATEMENU_TREE_FLAGS_NONE, "MATEMENU_TREE_FLAGS_NONE", "none" },
+        { MATEMENU_TREE_FLAGS_INCLUDE_EXCLUDED, "MATEMENU_TREE_FLAGS_INCLUDE_EXCLUDED", "include-excluded" },
+        { MATEMENU_TREE_FLAGS_SHOW_EMPTY, "MATEMENU_TREE_FLAGS_SHOW_EMPTY", "show-empty" },
+        { MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY, "MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY", "include-nodisplay" },
+        { MATEMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS, "MATEMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS", "show-all-separators" },
+        { MATEMENU_TREE_FLAGS_SORT_DISPLAY_NAME, "MATEMENU_TREE_FLAGS_SORT_DISPLAY_NAME", "sort-display-name" },
+        { MATEMENU_TREE_FLAGS_INCLUDE_UNALLOCATED, "MATEMENU_TREE_FLAGS_INCLUDE_UNALLOCATED,", "include-unallocated" },
+        { 0, NULL, NULL }
+      };
+      enum_type_id = g_flags_register_static ("MateMenuTreeFlags", values);
+    }
+  return enum_type_id;
+}
diff --git a/libmenu/matemenu-tree.h b/libmenu/matemenu-tree.h
index cd3e45b..3fd3260 100644
--- a/libmenu/matemenu-tree.h
+++ b/libmenu/matemenu-tree.h
@@ -20,116 +20,143 @@
 #ifndef __MATEMENU_TREE_H__
 #define __MATEMENU_TREE_H__
 
-#include <glib.h>
-
-#ifdef __cplusplus
-extern "C" {
+#ifndef MATEMENU_I_KNOW_THIS_IS_UNSTABLE
+#error "libmate-menu should only be used if you understand that it's subject to frequent change, and is not supported as a fixed API/ABI or as part of the platform"
 #endif
 
-typedef struct MateMenuTree          MateMenuTree;
-typedef struct MateMenuTreeItem      MateMenuTreeItem;
+#include <gio/gdesktopappinfo.h>
+
+G_BEGIN_DECLS
+
+#define MATEMENU_TYPE_TREE         (matemenu_tree_get_type ())
+#define MATEMENU_TREE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), MATEMENU_TYPE_TREE, MateMenuTree))
+#define MATEMENU_TREE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), MATEMENU_TYPE_TREE, MateMenuTreeClass))
+#define MATEMENU_IS_TREE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATEMENU_TYPE_TREE))
+#define MATEMENU_IS_TREE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), MATEMENU_TYPE_TREE))
+#define MATEMENU_TREE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DESKTOP_APP_INFO, MateMenuTreeClass))
+
+typedef struct _MateMenuTree        MateMenuTree;
+typedef struct _MateMenuTreeClass   MateMenuTreeClass;
+
+struct _MateMenuTreeClass
+{
+  GObjectClass parent_class;
+};
+
+GType matemenu_tree_get_type (void) G_GNUC_CONST;
+
+typedef struct MateMenuTreeIter      MateMenuTreeIter;
 typedef struct MateMenuTreeDirectory MateMenuTreeDirectory;
 typedef struct MateMenuTreeEntry     MateMenuTreeEntry;
 typedef struct MateMenuTreeSeparator MateMenuTreeSeparator;
 typedef struct MateMenuTreeHeader    MateMenuTreeHeader;
 typedef struct MateMenuTreeAlias     MateMenuTreeAlias;
 
-typedef void (*MateMenuTreeChangedFunc) (MateMenuTree* tree, gpointer user_data);
-
-typedef enum {
-	MATEMENU_TREE_ITEM_INVALID = 0,
-	MATEMENU_TREE_ITEM_DIRECTORY,
-	MATEMENU_TREE_ITEM_ENTRY,
-	MATEMENU_TREE_ITEM_SEPARATOR,
-	MATEMENU_TREE_ITEM_HEADER,
-	MATEMENU_TREE_ITEM_ALIAS
+typedef enum
+{
+  MATEMENU_TREE_ITEM_INVALID = 0,
+  MATEMENU_TREE_ITEM_DIRECTORY,
+  MATEMENU_TREE_ITEM_ENTRY,
+  MATEMENU_TREE_ITEM_SEPARATOR,
+  MATEMENU_TREE_ITEM_HEADER,
+  MATEMENU_TREE_ITEM_ALIAS
 } MateMenuTreeItemType;
 
-#define MATEMENU_TREE_ITEM(i)      ((MateMenuTreeItem*)(i))
-#define MATEMENU_TREE_DIRECTORY(i) ((MateMenuTreeDirectory*)(i))
-#define MATEMENU_TREE_ENTRY(i)     ((MateMenuTreeEntry*)(i))
-#define MATEMENU_TREE_SEPARATOR(i) ((MateMenuTreeSeparator*)(i))
-#define MATEMENU_TREE_HEADER(i)    ((MateMenuTreeHeader*)(i))
-#define MATEMENU_TREE_ALIAS(i)     ((MateMenuTreeAlias*)(i))
-
-typedef enum {
-	MATEMENU_TREE_FLAGS_NONE                = 0,
-	MATEMENU_TREE_FLAGS_INCLUDE_EXCLUDED    = 1 << 0,
-	MATEMENU_TREE_FLAGS_SHOW_EMPTY          = 1 << 1,
-	MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY   = 1 << 2,
-	MATEMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS = 1 << 3,
-	MATEMENU_TREE_FLAGS_MASK                = 0x0f
+GType matemenu_tree_iter_get_type (void);
+
+/* Explicitly skip item, it's a "hidden" base class */
+GType matemenu_tree_directory_get_type (void);
+GType matemenu_tree_entry_get_type (void);
+GType matemenu_tree_separator_get_type (void);
+GType matemenu_tree_header_get_type (void);
+GType matemenu_tree_alias_get_type (void);
+
+typedef enum
+{
+  MATEMENU_TREE_FLAGS_NONE                = 0,
+  MATEMENU_TREE_FLAGS_INCLUDE_EXCLUDED    = 1 << 0,
+  MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY   = 1 << 1,
+  MATEMENU_TREE_FLAGS_INCLUDE_UNALLOCATED = 1 << 2,
+  /* leave some space for more include flags */
+  MATEMENU_TREE_FLAGS_SHOW_EMPTY          = 1 << 8,
+  MATEMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS = 1 << 9,
+  /* leave some space for more show flags */
+  MATEMENU_TREE_FLAGS_SORT_DISPLAY_NAME   = 1 << 16
 } MateMenuTreeFlags;
+GType matemenu_tree_flags_get_type (void);
+#define MATEMENU_TYPE_TREE_FLAGS (matemenu_tree_flags_get_type ())
 
-typedef enum {
-	#define MATEMENU_TREE_SORT_FIRST MATEMENU_TREE_SORT_NAME
-	MATEMENU_TREE_SORT_NAME = 0,
-	MATEMENU_TREE_SORT_DISPLAY_NAME
-	#define MATEMENU_TREE_SORT_LAST MATEMENU_TREE_SORT_DISPLAY_NAME
-} MateMenuTreeSortKey;
-
-MateMenuTree* matemenu_tree_lookup(const char* menu_file, MateMenuTreeFlags flags);
+MateMenuTree *matemenu_tree_new (const char     *menu_basename,
+                           MateMenuTreeFlags  flags);
 
-MateMenuTree* matemenu_tree_ref(MateMenuTree* tree);
-void matemenu_tree_unref(MateMenuTree* tree);
+MateMenuTree *matemenu_tree_new_for_path (const char     *menu_path,
+                                    MateMenuTreeFlags  flags);
 
-void matemenu_tree_set_user_data(MateMenuTree* tree, gpointer user_data, GDestroyNotify dnotify);
-gpointer matemenu_tree_get_user_data(MateMenuTree* tree);
+gboolean   matemenu_tree_load_sync (MateMenuTree  *tree,
+                                 GError    **error);
 
-const char* matemenu_tree_get_menu_file(MateMenuTree* tree);
-MateMenuTreeDirectory* matemenu_tree_get_root_directory(MateMenuTree* tree);
-MateMenuTreeDirectory* matemenu_tree_get_directory_from_path(MateMenuTree* tree, const char* path);
+const char         *matemenu_tree_get_canonical_menu_path (MateMenuTree  *tree);
+MateMenuTreeDirectory *matemenu_tree_get_root_directory      (MateMenuTree  *tree);
+MateMenuTreeDirectory *matemenu_tree_get_directory_from_path (MateMenuTree  *tree,
+							const char *path);
+MateMenuTreeEntry     *matemenu_tree_get_entry_by_id         (MateMenuTree  *tree,
+							const char *id);
 
-MateMenuTreeSortKey matemenu_tree_get_sort_key(MateMenuTree* tree);
-void matemenu_tree_set_sort_key(MateMenuTree* tree, MateMenuTreeSortKey sort_key);
+gpointer matemenu_tree_item_ref   (gpointer item);
+void     matemenu_tree_item_unref (gpointer item);
 
+MateMenuTreeDirectory *matemenu_tree_directory_get_parent    (MateMenuTreeDirectory *directory);
+const char *matemenu_tree_directory_get_name              (MateMenuTreeDirectory *directory);
+const char *matemenu_tree_directory_get_generic_name      (MateMenuTreeDirectory *directory);
+const char *matemenu_tree_directory_get_comment           (MateMenuTreeDirectory *directory);
+GIcon      *matemenu_tree_directory_get_icon              (MateMenuTreeDirectory *directory);
+const char *matemenu_tree_directory_get_desktop_file_path (MateMenuTreeDirectory *directory);
+const char *matemenu_tree_directory_get_menu_id           (MateMenuTreeDirectory *directory);
+MateMenuTree  *matemenu_tree_directory_get_tree              (MateMenuTreeDirectory *directory);
 
+gboolean matemenu_tree_directory_get_is_nodisplay (MateMenuTreeDirectory *directory);
 
-gpointer matemenu_tree_item_ref(gpointer item);
-void matemenu_tree_item_unref(gpointer item);
+MateMenuTreeIter      *matemenu_tree_directory_iter            (MateMenuTreeDirectory *directory);
 
-void matemenu_tree_item_set_user_data(MateMenuTreeItem* item, gpointer user_data, GDestroyNotify dnotify);
-gpointer matemenu_tree_item_get_user_data(MateMenuTreeItem* item);
+MateMenuTreeIter      *matemenu_tree_iter_ref                  (MateMenuTreeIter *iter);
+void                matemenu_tree_iter_unref                (MateMenuTreeIter *iter);
 
-MateMenuTreeItemType matemenu_tree_item_get_type(MateMenuTreeItem* item);
-MateMenuTreeDirectory* matemenu_tree_item_get_parent(MateMenuTreeItem* item);
+MateMenuTreeItemType   matemenu_tree_iter_next                 (MateMenuTreeIter *iter);
+MateMenuTreeDirectory *matemenu_tree_iter_get_directory        (MateMenuTreeIter *iter);
+MateMenuTreeEntry     *matemenu_tree_iter_get_entry            (MateMenuTreeIter *iter);
+MateMenuTreeHeader    *matemenu_tree_iter_get_header           (MateMenuTreeIter *iter);
+MateMenuTreeAlias     *matemenu_tree_iter_get_alias            (MateMenuTreeIter *iter);
+MateMenuTreeSeparator *matemenu_tree_iter_get_separator        (MateMenuTreeIter *iter);
 
+char *matemenu_tree_directory_make_path (MateMenuTreeDirectory *directory,
+				      MateMenuTreeEntry     *entry);
 
-GSList* matemenu_tree_directory_get_contents(MateMenuTreeDirectory* directory);
-const char* matemenu_tree_directory_get_name(MateMenuTreeDirectory* directory);
-const char* matemenu_tree_directory_get_comment(MateMenuTreeDirectory* directory);
-const char* matemenu_tree_directory_get_icon(MateMenuTreeDirectory* directory);
-const char* matemenu_tree_directory_get_desktop_file_path(MateMenuTreeDirectory* directory);
-const char* matemenu_tree_directory_get_menu_id(MateMenuTreeDirectory* directory);
-MateMenuTree* matemenu_tree_directory_get_tree(MateMenuTreeDirectory* directory);
 
-gboolean matemenu_tree_directory_get_is_nodisplay(MateMenuTreeDirectory* directory);
+GDesktopAppInfo    *matemenu_tree_entry_get_app_info       (MateMenuTreeEntry *entry);
+MateMenuTreeDirectory *matemenu_tree_entry_get_parent         (MateMenuTreeEntry *entry);
+MateMenuTree          *matemenu_tree_entry_get_tree           (MateMenuTreeEntry *entry);
 
-char* matemenu_tree_directory_make_path(MateMenuTreeDirectory* directory, MateMenuTreeEntry* entry);
+const char *matemenu_tree_entry_get_desktop_file_path (MateMenuTreeEntry *entry);
+const char *matemenu_tree_entry_get_desktop_file_id   (MateMenuTreeEntry *entry);
 
+gboolean matemenu_tree_entry_get_is_nodisplay_recurse  (MateMenuTreeEntry *entry);
+gboolean matemenu_tree_entry_get_is_excluded  (MateMenuTreeEntry *entry);
+gboolean matemenu_tree_entry_get_is_unallocated  (MateMenuTreeEntry *entry);
 
-const char* matemenu_tree_entry_get_name(MateMenuTreeEntry* entry);
-const char* matemenu_tree_entry_get_generic_name(MateMenuTreeEntry* entry);
-const char* matemenu_tree_entry_get_display_name(MateMenuTreeEntry* entry);
-const char* matemenu_tree_entry_get_comment(MateMenuTreeEntry* entry);
-const char* matemenu_tree_entry_get_icon(MateMenuTreeEntry* entry);
-const char* matemenu_tree_entry_get_exec(MateMenuTreeEntry* entry);
-gboolean matemenu_tree_entry_get_launch_in_terminal(MateMenuTreeEntry* entry);
-const char* matemenu_tree_entry_get_desktop_file_path(MateMenuTreeEntry* entry);
-const char* matemenu_tree_entry_get_desktop_file_id(MateMenuTreeEntry* entry);
-gboolean matemenu_tree_entry_get_is_excluded(MateMenuTreeEntry* entry);
-gboolean matemenu_tree_entry_get_is_nodisplay(MateMenuTreeEntry* entry);
+MateMenuTreeDirectory *matemenu_tree_header_get_directory (MateMenuTreeHeader *header);
+MateMenuTree          *matemenu_tree_header_get_tree      (MateMenuTreeHeader *header);
+MateMenuTreeDirectory *matemenu_tree_header_get_parent    (MateMenuTreeHeader *header);
 
-MateMenuTreeDirectory* matemenu_tree_header_get_directory(MateMenuTreeHeader* header);
+MateMenuTreeDirectory *matemenu_tree_alias_get_directory         (MateMenuTreeAlias *alias);
+MateMenuTreeItemType   matemenu_tree_alias_get_aliased_item_type (MateMenuTreeAlias *alias);
+MateMenuTreeDirectory *matemenu_tree_alias_get_aliased_directory (MateMenuTreeAlias *alias);
+MateMenuTreeEntry     *matemenu_tree_alias_get_aliased_entry     (MateMenuTreeAlias *alias);
+MateMenuTree          *matemenu_tree_alias_get_tree              (MateMenuTreeAlias *alias);
+MateMenuTreeDirectory *matemenu_tree_alias_get_parent            (MateMenuTreeAlias *alias);
 
-MateMenuTreeDirectory* matemenu_tree_alias_get_directory(MateMenuTreeAlias* alias);
-MateMenuTreeItem* matemenu_tree_alias_get_item(MateMenuTreeAlias* alias);
+MateMenuTree          *matemenu_tree_separator_get_tree (MateMenuTreeSeparator *separator);
+MateMenuTreeDirectory *matemenu_tree_separator_get_parent (MateMenuTreeSeparator *separator);
 
-void matemenu_tree_add_monitor(MateMenuTree* tree, MateMenuTreeChangedFunc callback, gpointer user_data);
-void matemenu_tree_remove_monitor(MateMenuTree* tree, MateMenuTreeChangedFunc callback, gpointer user_data);
-
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* __MATEMENU_TREE_H__ */
diff --git a/libmenu/menu-layout.c b/libmenu/menu-layout.c
index 89e9752..30d11b3 100644
--- a/libmenu/menu-layout.c
+++ b/libmenu/menu-layout.c
@@ -64,7 +64,10 @@ struct MenuLayoutNodeRoot
   char *basedir;
   char *name;
 
+  GMainContext *main_context;
+
   GSList *monitors;
+  GSource *monitors_idle_handler;
 };
 
 struct MenuLayoutNodeMenu
@@ -133,16 +136,14 @@ node_next (MenuLayoutNode *node)
   return node->next;
 }
 
-static void
-handle_entry_directory_changed (EntryDirectory *dir,
-                                MenuLayoutNode *node)
+static gboolean
+menu_layout_invoke_monitors (MenuLayoutNodeRoot *nr)
 {
-  MenuLayoutNodeRoot *nr;
-  GSList             *tmp;
+  GSList *tmp;
 
-  g_assert (node->type == MENU_LAYOUT_NODE_MENU);
+  g_assert (nr->node.type == MENU_LAYOUT_NODE_ROOT);
 
-  nr = (MenuLayoutNodeRoot *) menu_layout_node_get_root (node);
+  nr->monitors_idle_handler = NULL;
 
   tmp = nr->monitors;
   while (tmp != NULL)
@@ -154,6 +155,28 @@ handle_entry_directory_changed (EntryDirectory *dir,
 
       tmp = next;
     }
+
+  return FALSE;
+}
+
+static void
+handle_entry_directory_changed (EntryDirectory *dir,
+                                MenuLayoutNode *node)
+{
+  MenuLayoutNodeRoot *nr;
+
+  g_assert (node->type == MENU_LAYOUT_NODE_MENU);
+
+  nr = (MenuLayoutNodeRoot *) menu_layout_node_get_root (node);
+
+  if (nr->monitors_idle_handler == NULL)
+    {
+      nr->monitors_idle_handler = g_idle_source_new ();
+      g_source_set_callback (nr->monitors_idle_handler,
+                             (GSourceFunc) menu_layout_invoke_monitors, nr, NULL);
+      g_source_attach (nr->monitors_idle_handler, nr->main_context);
+      g_source_unref (nr->monitors_idle_handler);
+    }
 }
 
 static void
@@ -224,6 +247,14 @@ menu_layout_node_unref (MenuLayoutNode *node)
           g_slist_foreach (nr->monitors, (GFunc) g_free, NULL);
           g_slist_free (nr->monitors);
 
+          if (nr->monitors_idle_handler != NULL)
+            g_source_destroy (nr->monitors_idle_handler);
+          nr->monitors_idle_handler = NULL;
+
+          if (nr->main_context != NULL)
+            g_main_context_unref (nr->main_context);
+          nr->main_context = NULL;
+
           g_free (nr->basedir);
           g_free (nr->name);
         }
@@ -2266,6 +2297,7 @@ menu_layout_load (const char  *filename,
                   const char  *non_prefixed_basename,
                   GError     **err)
 {
+  GMainContext        *main_context;
   GMarkupParseContext *context;
   MenuLayoutNodeRoot  *root;
   MenuLayoutNode      *retval;
@@ -2281,6 +2313,8 @@ menu_layout_load (const char  *filename,
   retval = NULL;
   context = NULL;
 
+  main_context = g_main_context_get_thread_default ();
+
   menu_verbose ("Loading \"%s\" from disk\n", filename);
 
   if (!g_file_get_contents (filename,
@@ -2328,6 +2362,8 @@ menu_layout_load (const char  *filename,
   error = NULL;
   g_markup_parse_context_end_parse (context, &error);
 
+  root->main_context = main_context ? g_main_context_ref (main_context) : NULL;
+
  out:
   if (context)
     g_markup_parse_context_free (context);
-- 
2.22.0

openSUSE Build Service is sponsored by