[Xfce4-commits] <garcon:jannis/global-monitoring> Add garcon_menu_directory_load(). Further work on monitoring.
Jannis Pohlmann
noreply at xfce.org
Sat Mar 20 13:22:13 CET 2010
Updating branch refs/heads/jannis/global-monitoring
to 2848ad424cc5a701211ffd228ce922db781082c2 (commit)
from 46ae3adfa271ca458d1e5e750256fb2d2daeb32c (commit)
commit 2848ad424cc5a701211ffd228ce922db781082c2
Author: Jannis Pohlmann <jannis at xfce.org>
Date: Wed Mar 10 20:57:01 2010 +0100
Add garcon_menu_directory_load(). Further work on monitoring.
Require GarconMenuDirectories to be created and loaded in two steps (one
for creating the object, another one for loading), just like we do it
for GarconMenu.
Make it possible to reload GarconMenus with garcon_menu_load() without
creating a new GarconMenu before (this was required in libxfce4menu but
it is inconvenient). Split the monitoring initialization into several
methods, add TODOs for the missing pieces: reacting on app dir changes.
garcon/garcon-config.c | 2 +-
garcon/garcon-config.h | 39 +++-
garcon/garcon-menu-directory.c | 95 ++++---
garcon/garcon-menu-directory.h | 6 +-
garcon/garcon-menu.c | 595 +++++++++++++++++++++++++++++++---------
tests/test-display-menu.c | 35 +++-
6 files changed, 591 insertions(+), 181 deletions(-)
diff --git a/garcon/garcon-config.c b/garcon/garcon-config.c
index 75693d3..7e487a6 100644
--- a/garcon/garcon-config.c
+++ b/garcon/garcon-config.c
@@ -1,6 +1,6 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
- * Copyright (c) 2009 Jannis Pohlmann <jannis at xfce.org>
+ * Copyright (c) 2009-2010 Jannis Pohlmann <jannis at xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
diff --git a/garcon/garcon-config.h b/garcon/garcon-config.h
index c7494e8..ee4c119 100644
--- a/garcon/garcon-config.h
+++ b/garcon/garcon-config.h
@@ -30,10 +30,47 @@
G_BEGIN_DECLS
+/**
+ * GARCON_MAJOR_VERSION:
+ *
+ * The major version number of the garcon library.
+ * Like garcon_major_version, but from the headers used at
+ * application compile time, rather than from the library
+ * linked against at application run time.
+ **/
#define GARCON_MAJOR_VERSION 0
+
+/**
+ * GARCON_MINOR_VERSION:
+ *
+ * The minor version number of the garcon library.
+ * Like garcon_minor_version, but from the headers used at
+ * application compile time, rather than from the library
+ * linked against at application run time.
+ **/
#define GARCON_MINOR_VERSION 1
-#define GARCON_MICRO_VERSION 0
+/**
+ * GARCON_MICRO_VERSION:
+ *
+ * The micro version number of the garcon library.
+ * Like garcon_micro_version, but from the headers used at
+ * application compile time, rather than from the library
+ * linked against at application run time.
+ **/
+#define GARCON_MICRO_VERSION 1
+
+/**
+ * GARCON_CHECK_VERSION:
+ * @major : the major version number.
+ * @minor : the minor version number.
+ * @micro : the micro version number.
+ *
+ * Checks the version of the garcon library.
+ *
+ * Returns: %TRUE if the version of the garcon header files is
+ * the same as or newer than the passed-in version.
+ **/
#define GARCON_CHECK_VERSION(major,minor,micro) \
(GARCON_MAJOR_VERSION > (major) \
|| (GARCON_MAJOR_VERSION == (major) \
diff --git a/garcon/garcon-menu-directory.c b/garcon/garcon-menu-directory.c
index 1203059..fffb3f2 100644
--- a/garcon/garcon-menu-directory.c
+++ b/garcon/garcon-menu-directory.c
@@ -1,6 +1,6 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
- * Copyright (c) 2006-2009 Jannis Pohlmann <jannis at xfce.org>
+ * Copyright (c) 2006-2010 Jannis Pohlmann <jannis at xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -340,69 +340,86 @@ garcon_menu_directory_set_property (GObject *object,
GarconMenuDirectory *
garcon_menu_directory_new (GFile *file)
{
- GarconMenuDirectory *directory = NULL;
- gchar *contents;
- gsize length;
- GKeyFile *rc;
- gchar *name;
- gchar *comment;
- gchar *icon_name;
- gboolean no_display;
- gboolean succeed;
-
- g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+ return g_object_new (GARCON_TYPE_MENU_DIRECTORY, "file", file, NULL);
+}
+
+
+gboolean
+garcon_menu_directory_load (GarconMenuDirectory *directory,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GKeyFile *rc;
+ gboolean no_display;
+ gboolean file_parsed = FALSE;
+ gchar *contents;
+ gsize length;
+ gchar *name;
+ gchar *comment;
+ gchar *icon_name;
+
+ g_return_val_if_fail (GARCON_IS_MENU_DIRECTORY (directory), FALSE);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* Load the contents of the file */
- if (!g_file_load_contents (file, NULL, &contents, &length, NULL, NULL))
- return NULL;
+ if (!g_file_load_contents (directory->priv->file, cancellable, &contents, &length,
+ NULL, error))
+ {
+ return FALSE;
+ }
- /* Open the keyfile */
+ /* Open and parse the keyfile */
rc = g_key_file_new ();
- succeed = g_key_file_load_from_data (rc, contents, length, G_KEY_FILE_NONE, NULL);
+ file_parsed = g_key_file_load_from_data (rc, contents, length, G_KEY_FILE_NONE, error);
g_free (contents);
- if (G_UNLIKELY (!succeed))
+
+ if (G_UNLIKELY (!file_parsed))
{
/* Cleanup and leave */
g_key_file_free (rc);
- return NULL;
+ return FALSE;
}
- /* Parse name, exec command and icon name */
name = g_key_file_get_locale_string (rc, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
+ garcon_menu_directory_set_name (directory, name);
+ g_free (name);
+
comment = g_key_file_get_locale_string (rc, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL);
+ garcon_menu_directory_set_comment (directory, comment);
+ g_free (comment);
+
icon_name = g_key_file_get_string (rc, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
+ garcon_menu_directory_set_icon_name (directory, icon_name);
+ g_free (icon_name);
+
no_display = g_key_file_get_boolean (rc, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL);
+ garcon_menu_directory_set_no_display (directory, no_display);
- /* Allocate a new directory instance */
- directory = g_object_new (GARCON_TYPE_MENU_DIRECTORY,
- "file", file,
- "name", name,
- "comment", comment,
- "icon-name", icon_name,
- "no-display", no_display,
- NULL);
+ /* Set rest of the private data directly */
+ g_strfreev (directory->priv->only_show_in);
+ directory->priv->only_show_in =
+ g_key_file_get_string_list (rc, G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, NULL, NULL);
- /* Free strings */
- g_free (name);
- g_free (comment);
- g_free (icon_name);
+ g_strfreev (directory->priv->not_show_in);
+ directory->priv->not_show_in =
+ g_key_file_get_string_list (rc, G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, NULL, NULL);
- /* Set rest of the private data directly */
- directory->priv->only_show_in = g_key_file_get_string_list (rc, G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, NULL, NULL);
- directory->priv->not_show_in = g_key_file_get_string_list (rc, G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, NULL, NULL);
- directory->priv->hidden = g_key_file_get_boolean (rc, G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL);
+ directory->priv->hidden =
+ g_key_file_get_boolean (rc, G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL);
/* Cleanup */
g_key_file_free (rc);
- return directory;
+ return TRUE;
}
diff --git a/garcon/garcon-menu-directory.h b/garcon/garcon-menu-directory.h
index f70ab3d..8ecfd20 100644
--- a/garcon/garcon-menu-directory.h
+++ b/garcon/garcon-menu-directory.h
@@ -1,6 +1,6 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
- * Copyright (c) 2007-2009 Jannis Pohlmann <jannis at xfce.org>
+ * Copyright (c) 2007-2010 Jannis Pohlmann <jannis at xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -59,6 +59,10 @@ GType garcon_menu_directory_get_type (void) G_GNUC
GarconMenuDirectory *garcon_menu_directory_new (GFile *file) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+gboolean garcon_menu_directory_load (GarconMenuDirectory *directory,
+ GCancellable *cancellable,
+ GError **error);
+
GFile *garcon_menu_directory_get_file (GarconMenuDirectory *directory);
const gchar *garcon_menu_directory_get_name (GarconMenuDirectory *directory);
void garcon_menu_directory_set_name (GarconMenuDirectory *directory,
diff --git a/garcon/garcon-menu.c b/garcon/garcon-menu.c
index c80e980..9642683 100644
--- a/garcon/garcon-menu.c
+++ b/garcon/garcon-menu.c
@@ -1,6 +1,6 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
- * Copyright (c) 2007-2009 Jannis Pohlmann <jannis at xfce.org>
+ * Copyright (c) 2007-2010 Jannis Pohlmann <jannis at xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -121,9 +121,12 @@ static void garcon_menu_set_property (GObject
static void garcon_menu_set_directory (GarconMenu *menu,
GarconMenuDirectory *directory);
static void garcon_menu_resolve_menus (GarconMenu *menu);
-static void garcon_menu_resolve_directory (GarconMenu *menu);
+static void garcon_menu_resolve_directory (GarconMenu *menu,
+ GCancellable *cancellable,
+ gboolean recursive);
static GarconMenuDirectory *garcon_menu_lookup_directory (GarconMenu *menu,
- const gchar *filename);
+ const gchar *filename,
+ GCancellable *cancellable);
static void garcon_menu_collect_files (GarconMenu *menu,
GHashTable *desktop_id_table);
static void garcon_menu_collect_files_from_path (GarconMenu *menu,
@@ -150,6 +153,13 @@ static gboolean garcon_menu_get_element_show_in_environment (GarconM
static gboolean garcon_menu_get_element_no_display (GarconMenuElement *element);
static void garcon_menu_start_monitoring (GarconMenu *menu);
static void garcon_menu_stop_monitoring (GarconMenu *menu);
+static void garcon_menu_monitor_menu_files (GarconMenu *menu);
+static void garcon_menu_monitor_merge_files (GarconMenu *menu);
+static void garcon_menu_monitor_merge_dirs (GarconMenu *menu);
+static void garcon_menu_monitor_directory_dirs (GarconMenu *menu);
+static void garcon_menu_monitor_app_dirs (GarconMenu *menu);
+static void garcon_menu_monitor_app_dir (GarconMenu *menu,
+ GFile *dir);
static void garcon_menu_file_changed (GarconMenu *menu,
GFile *file,
GFile *other_file,
@@ -170,6 +180,12 @@ static void garcon_menu_directory_file_changed (GarconM
GFile *other_file,
GFileMonitorEvent event_type,
GFileMonitor *monitor);
+static void garcon_menu_app_dir_changed (GarconMenu *menu,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GFileMonitor *monitor);
+
@@ -202,6 +218,9 @@ struct _GarconMenuPrivate
/* Shared menu item cache */
GarconMenuItemCache *cache;
+
+ /* Flag for marking custom path menus */
+ guint uses_custom_path : 1;
};
@@ -211,7 +230,8 @@ G_DEFINE_TYPE_WITH_CODE (GarconMenu, garcon_menu, G_TYPE_OBJECT,
-static guint menu_signals[LAST_SIGNAL];
+static guint menu_signals[LAST_SIGNAL];
+static GQuark garcon_menu_file_quark;
@@ -279,6 +299,8 @@ garcon_menu_class_init (GarconMenuClass *klass)
2,
GARCON_TYPE_MENU_DIRECTORY,
GARCON_TYPE_MENU_DIRECTORY);
+
+ garcon_menu_file_quark = g_quark_from_string ("garcon-menu-file-quark");
}
@@ -308,6 +330,7 @@ garcon_menu_init (GarconMenu *menu)
menu->priv->submenus = NULL;
menu->priv->parent = NULL;
menu->priv->pool = garcon_menu_item_pool_new ();
+ menu->priv->uses_custom_path = TRUE;
/* Take reference on the menu item cache */
menu->priv->cache = garcon_menu_item_cache_get_default ();
@@ -348,7 +371,10 @@ garcon_menu_clear (GarconMenu *menu)
/* Free directory */
if (G_LIKELY (menu->priv->directory != NULL))
- g_object_unref (menu->priv->directory);
+ {
+ g_object_unref (menu->priv->directory);
+ menu->priv->directory = NULL;
+ }
/* Clear the item pool */
garcon_menu_item_pool_clear (menu->priv->pool);
@@ -515,26 +541,9 @@ GarconMenu *
garcon_menu_new_applications (void)
{
GarconMenu *menu = NULL;
- GFile *file;
- gchar *filename;
- guint n;
-
- /* Search for a usable applications menu file */
- for (n = 0; menu == NULL && n < G_N_ELEMENTS (GARCON_MENU_ROOT_SPECS); ++n)
- {
- /* Search for the applications menu file */
- filename = garcon_config_lookup (GARCON_MENU_ROOT_SPECS[n]);
- /* Create menu if the file exists */
- if (G_UNLIKELY (filename != NULL))
- {
- file = _garcon_file_new_for_unknown_input (filename, NULL);
- menu = garcon_menu_new (file);
- g_object_unref (file);
- }
-
- g_free (filename);
- }
+ menu = g_object_new (GARCON_TYPE_MENU, NULL);
+ menu->priv->uses_custom_path = FALSE;
return menu;
}
@@ -652,13 +661,48 @@ garcon_menu_load (GarconMenu *menu,
GarconMenuMerger *merger;
GHashTable *desktop_id_table;
gboolean success = TRUE;
+ gchar *filename;
+ guint n;
g_return_val_if_fail (GARCON_IS_MENU (menu), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- /* make sure to reset the menu to a loadable state */
+ /* Make sure to reset the menu to a loadable state */
garcon_menu_clear (menu);
+ /* Check if we need to locate the applications menu file */
+ if (!menu->priv->uses_custom_path)
+ {
+ /* Release the old file if there is one */
+ if (menu->priv->file != NULL)
+ {
+ g_object_unref (menu->priv->file);
+ menu->priv->file = NULL;
+ }
+
+ /* Search for a usable applications menu file */
+ for (n = 0; menu->priv->file == NULL && n < G_N_ELEMENTS (GARCON_MENU_ROOT_SPECS); ++n)
+ {
+ /* Search for the applications menu file */
+ filename = garcon_config_lookup (GARCON_MENU_ROOT_SPECS[n]);
+
+ /* Use the file if it exists */
+ if (G_UNLIKELY (filename != NULL))
+ menu->priv->file = _garcon_file_new_for_unknown_input (filename, NULL);
+
+ /* Free the filename string */
+ g_free (filename);
+ }
+
+ /* Abort with an error if no suitable applications menu file was found */
+ if (menu->priv->file == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
+ _("No suitable application menu file found"));
+ return FALSE;
+ }
+ }
+
parser = garcon_menu_parser_new (menu->priv->file);
if (G_LIKELY (garcon_menu_parser_run (parser, cancellable, error)))
@@ -692,7 +736,7 @@ garcon_menu_load (GarconMenu *menu,
garcon_menu_resolve_menus (menu);
/* Resolve the menu directory */
- garcon_menu_resolve_directory (menu);
+ garcon_menu_resolve_directory (menu, cancellable, TRUE);
desktop_id_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
@@ -864,13 +908,23 @@ garcon_menu_get_directories (GarconMenu *menu)
static void
-garcon_menu_resolve_directory (GarconMenu *menu)
+garcon_menu_resolve_directory (GarconMenu *menu,
+ GCancellable *cancellable,
+ gboolean recursive)
{
GarconMenuDirectory *directory = NULL;
GList *directories = NULL;
GList *iter;
g_return_if_fail (GARCON_IS_MENU (menu));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ /* release the old directory if there is one */
+ if (menu->priv->directory != NULL)
+ {
+ g_object_unref (menu->priv->directory);
+ menu->priv->directory = NULL;
+ }
/* Determine all directories for this menu */
directories = garcon_menu_get_directories (menu);
@@ -879,7 +933,7 @@ garcon_menu_resolve_directory (GarconMenu *menu)
for (iter = directories; directory == NULL && iter != NULL; iter = g_list_next (iter))
{
/* Try to load the directory with this name */
- directory = garcon_menu_lookup_directory (menu, iter->data);
+ directory = garcon_menu_lookup_directory (menu, iter->data, cancellable);
}
if (G_LIKELY (directory != NULL))
@@ -891,9 +945,12 @@ garcon_menu_resolve_directory (GarconMenu *menu)
/* Free reverse list copy */
g_list_free (directories);
- /* Resolve directories of submenus recursively */
- for (iter = menu->priv->submenus; iter != NULL; iter = g_list_next (iter))
- garcon_menu_resolve_directory (iter->data);
+ if (recursive)
+ {
+ /* Resolve directories of submenus recursively */
+ for (iter = menu->priv->submenus; iter != NULL; iter = g_list_next (iter))
+ garcon_menu_resolve_directory (iter->data, cancellable, recursive);
+ }
}
@@ -917,8 +974,9 @@ garcon_menu_get_directory_dirs (GarconMenu *menu)
static GarconMenuDirectory *
-garcon_menu_lookup_directory (GarconMenu *menu,
- const gchar *filename)
+garcon_menu_lookup_directory (GarconMenu *menu,
+ const gchar *filename,
+ GCancellable *cancellable)
{
GarconMenuDirectory *directory = NULL;
GList *dirs = NULL;
@@ -929,6 +987,7 @@ garcon_menu_lookup_directory (GarconMenu *menu,
g_return_val_if_fail (GARCON_IS_MENU (menu), NULL);
g_return_val_if_fail (filename != NULL, NULL);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
dirs = garcon_menu_get_directory_dirs (menu);
@@ -943,9 +1002,15 @@ garcon_menu_lookup_directory (GarconMenu *menu,
{
/* Load menu directory */
directory = garcon_menu_directory_new (file);
-
- /* Update search status */
- found = TRUE;
+ if (garcon_menu_directory_load (directory, cancellable, NULL))
+ {
+ /* Update search status */
+ found = TRUE;
+ }
+ else
+ {
+ g_object_unref (directory);
+ }
}
/* Destroy the file objects */
@@ -962,31 +1027,32 @@ garcon_menu_lookup_directory (GarconMenu *menu,
static GList *
-garcon_menu_get_app_dirs (GarconMenu *menu)
+garcon_menu_get_app_dirs (GarconMenu *menu,
+ gboolean recursive)
{
GList *dirs = NULL;
+ GList *lp;
+ GList *sp;
+ GList *submenu_app_dirs;
+
+ g_return_val_if_fail (GARCON_IS_MENU (menu), NULL);
/* Fetch all application directories */
dirs = garcon_menu_node_tree_get_string_children (menu->priv->tree,
GARCON_MENU_NODE_TYPE_APP_DIR,
TRUE);
-#if 0
- /* A submenu always inherits the application directories of its parent,
- * that is the reason the call below was added.
- * It only turned out we were looking in that same directories for
- * .desktop files multiple times.
- *
- * This was caused by the combination of the parent call below and
- * traversing the children in garcon_menu_collect_files(). For each
- * submenu the appdirs of the root were added and traversed again.
- *
- * This is not needed because we always start at the root and traverse
- * in "pre-order", so all the desktop files are added in the hash-table.
- */
- if (menu->priv->parent != NULL)
- dirs = g_list_concat (dirs, garcon_menu_get_app_dirs (menu->priv->parent));
-#endif
+ if (recursive)
+ {
+ for (lp = menu->priv->submenus; lp != NULL; lp = lp->next)
+ {
+ submenu_app_dirs = garcon_menu_get_app_dirs (lp->data, recursive);
+
+ for (sp = g_list_last (submenu_app_dirs); sp != NULL; sp = sp->prev)
+ if (!g_list_find_custom (dirs, sp->data, (GCompareFunc) g_strcmp0))
+ dirs = g_list_prepend (dirs, sp->data);
+ }
+ }
return dirs;
}
@@ -1003,7 +1069,7 @@ garcon_menu_collect_files (GarconMenu *menu,
g_return_if_fail (GARCON_IS_MENU (menu));
- app_dirs = garcon_menu_get_app_dirs (menu);
+ app_dirs = garcon_menu_get_app_dirs (menu, FALSE);
/* Collect desktop entry filenames */
for (iter = app_dirs; iter != NULL; iter = g_list_next (iter))
@@ -1137,16 +1203,16 @@ garcon_menu_resolve_items (GarconMenu *menu,
GHashTable *desktop_id_table,
gboolean only_unallocated)
{
- GList *rules = NULL;
- GList *iter;
gboolean menu_only_unallocated = FALSE;
+ GList *rules = NULL;
+ GList *iter;
g_return_if_fail (menu != NULL && GARCON_IS_MENU (menu));
menu_only_unallocated = garcon_menu_node_tree_get_boolean_child (menu->priv->tree,
GARCON_MENU_NODE_TYPE_ONLY_UNALLOCATED);
- /* Resolve items in this menu (if it matches the only_unallocated argument.
+ /* Resolve items in this menu (if it matches the only_unallocated argument).
* This means that in the first pass, all items of menus without
* <OnlyUnallocated /> are resolved and in the second pass, only items of
* menus with <OnlyUnallocated /> are resolved */
@@ -1677,18 +1743,40 @@ garcon_menu_get_element_no_display (GarconMenuElement *element)
static void
garcon_menu_start_monitoring (GarconMenu *menu)
{
+ GList *lp;
+
+ g_return_if_fail (GARCON_IS_MENU (menu));
+
+ /* Let only the root menu monitor menu files, merge directories and app dirs */
+ if (menu->priv->parent == NULL)
+ {
+ garcon_menu_monitor_menu_files (menu);
+ garcon_menu_monitor_merge_files (menu);
+ garcon_menu_monitor_merge_dirs (menu);
+ }
+
+ garcon_menu_monitor_directory_dirs (menu);
+ garcon_menu_monitor_app_dirs (menu);
+
+ /* Recurse into child menus */
+ for (lp = menu->priv->submenus; lp != NULL; lp = lp->next)
+ garcon_menu_start_monitoring (lp->data);
+}
+
+
+
+static void
+garcon_menu_monitor_menu_files (GarconMenu *menu)
+{
GFileMonitor *monitor;
- GFile *dir;
GFile *file;
- GList *directory_files;
- GList *directory_dirs;
- GList *dp;
- GList *lp;
+ gchar **paths;
+ guint n;
+ guint i;
g_return_if_fail (GARCON_IS_MENU (menu));
- /* Let only the root menu monitor .menu files and merge directories */
- if (menu->priv->parent == NULL)
+ if (menu->priv->uses_custom_path)
{
/* Monitor the root .menu file */
monitor = g_file_monitor (menu->priv->file, G_FILE_MONITOR_NONE, NULL, NULL);
@@ -1698,32 +1786,94 @@ garcon_menu_start_monitoring (GarconMenu *menu)
g_signal_connect_swapped (monitor, "changed",
G_CALLBACK (garcon_menu_file_changed), menu);
}
-
- /* Monitor all .menu files that were merged into the root menu */
- for (lp = menu->priv->merge_files; lp != NULL; lp = lp->next)
+ }
+ else
+ {
+ /* Monitor all applications menu candidates */
+ for (n = 0; n < G_N_ELEMENTS (GARCON_MENU_ROOT_SPECS); ++n)
{
- monitor = g_file_monitor (lp->data, G_FILE_MONITOR_NONE, NULL, NULL);
- if (monitor != NULL)
+ paths = garcon_config_build_paths (GARCON_MENU_ROOT_SPECS[n]);
+
+ for (i = 0; paths != NULL && paths[i] != NULL; ++i)
{
- menu->priv->monitors = g_list_prepend (menu->priv->monitors, monitor);
- g_signal_connect_swapped (monitor, "changed",
- G_CALLBACK (garcon_menu_merge_file_changed), menu);
+ file = g_file_new_for_path (paths[i]);
+
+ monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, NULL);
+ if (monitor != NULL)
+ {
+ menu->priv->monitors = g_list_prepend (menu->priv->monitors, monitor);
+ g_signal_connect_swapped (monitor, "changed",
+ G_CALLBACK (garcon_menu_file_changed), menu);
+ }
+
+ g_object_unref (file);
}
+
+ g_strfreev (paths);
}
+ }
+}
- /* Monitor all merge directories from which .menu files were merged into
- * the root menu */
- for (lp = menu->priv->merge_dirs; lp != NULL; lp = lp->next)
+
+
+static void
+garcon_menu_monitor_merge_files (GarconMenu *menu)
+{
+ GFileMonitor *monitor;
+ GList *lp;
+
+ g_return_if_fail (GARCON_IS_MENU (menu));
+
+ /* Monitor all .menu files that were merged into the root menu */
+ for (lp = menu->priv->merge_files; lp != NULL; lp = lp->next)
+ {
+ monitor = g_file_monitor (lp->data, G_FILE_MONITOR_NONE, NULL, NULL);
+ if (monitor != NULL)
{
- monitor = g_file_monitor (lp->data, G_FILE_MONITOR_NONE, NULL, NULL);
- if (monitor != NULL)
- {
- menu->priv->monitors = g_list_prepend (menu->priv->monitors, monitor);
- g_signal_connect_swapped (monitor, "changed",
- G_CALLBACK (garcon_menu_merge_dir_changed), menu);
- }
+ menu->priv->monitors = g_list_prepend (menu->priv->monitors, monitor);
+ g_signal_connect_swapped (monitor, "changed",
+ G_CALLBACK (garcon_menu_merge_file_changed), menu);
+ }
+ }
+}
+
+
+
+static void
+garcon_menu_monitor_merge_dirs (GarconMenu *menu)
+{
+ GFileMonitor *monitor;
+ GList *lp;
+
+ g_return_if_fail (GARCON_IS_MENU (menu));
+
+ /* Monitor all merge directories from which .menu files were merged into the root menu */
+ for (lp = menu->priv->merge_dirs; lp != NULL; lp = lp->next)
+ {
+ monitor = g_file_monitor (lp->data, G_FILE_MONITOR_NONE, NULL, NULL);
+ if (monitor != NULL)
+ {
+ menu->priv->monitors = g_list_prepend (menu->priv->monitors, monitor);
+ g_signal_connect_swapped (monitor, "changed",
+ G_CALLBACK (garcon_menu_merge_dir_changed), menu);
}
}
+}
+
+
+
+static void
+garcon_menu_monitor_directory_dirs (GarconMenu *menu)
+{
+ GFileMonitor *monitor;
+ GFile *file;
+ GFile *dir;
+ GList *directory_files;
+ GList *directory_dirs;
+ GList *dp;
+ GList *lp;
+
+ g_return_if_fail (GARCON_IS_MENU (menu));
/* Determine all .directory files we are interested in for this menu */
directory_files = garcon_menu_get_directories (menu);
@@ -1753,10 +1903,101 @@ garcon_menu_start_monitoring (GarconMenu *menu)
/* Free lists */
g_list_free (directory_dirs);
g_list_free (directory_files);
+}
- /* Recurse into child menus */
- for (lp = menu->priv->submenus; lp != NULL; lp = lp->next)
- garcon_menu_start_monitoring (lp->data);
+
+
+static void
+garcon_menu_monitor_app_dirs (GarconMenu *menu)
+{
+ GFile *dir;
+ GList *app_dirs;
+ GList *lp;
+
+ g_return_if_fail (GARCON_IS_MENU (menu));
+
+ /* Determine all application directories we are interested in for this menu */
+ app_dirs = garcon_menu_get_app_dirs (menu, TRUE);
+
+ for (lp = app_dirs; lp != NULL; lp = lp->next)
+ {
+ dir = _garcon_file_new_relative_to_file (lp->data, menu->priv->file);
+ garcon_menu_monitor_app_dir (menu, dir);
+ g_object_unref (dir);
+ }
+
+ /* Free app dir list */
+ g_list_free (app_dirs);
+}
+
+
+
+static gint
+find_monitor (GFileMonitor *monitor,
+ GFile *file)
+{
+ GFile *monitored_file;
+
+ monitored_file = g_object_get_qdata (G_OBJECT (monitor), garcon_menu_file_quark);
+
+ if (monitored_file != NULL && g_file_equal (monitored_file, file))
+ return 0;
+ else
+ return -1;
+}
+
+
+
+static void
+garcon_menu_monitor_app_dir (GarconMenu *menu,
+ GFile *dir)
+{
+ GFileEnumerator *enumerator;
+ GFileMonitor *monitor;
+ GFileInfo *info;
+ GFile *file;
+
+ g_return_if_fail (GARCON_IS_MENU (menu));
+ g_return_if_fail (G_IS_FILE (dir));
+
+ if (g_list_find_custom (menu->priv->monitors, dir, (GCompareFunc) find_monitor))
+ return;
+
+ monitor = g_file_monitor (dir, G_FILE_MONITOR_NONE, NULL, NULL);
+ if (monitor != NULL)
+ {
+ g_object_set_qdata_full (G_OBJECT (monitor), garcon_menu_file_quark,
+ g_object_ref (dir), g_object_unref);
+
+ menu->priv->monitors = g_list_prepend (menu->priv->monitors, monitor);
+
+ g_signal_connect_swapped (monitor, "changed",
+ G_CALLBACK (garcon_menu_app_dir_changed), menu);
+ }
+
+ enumerator = g_file_enumerate_children (dir, "standard::name,standard::type",
+ G_FILE_QUERY_INFO_NONE, NULL, NULL);
+
+ if (enumerator == NULL)
+ return;
+
+ while (TRUE)
+ {
+ info = g_file_enumerator_next_file (enumerator, NULL, NULL);
+
+ if (info == NULL)
+ break;
+
+ file = g_file_resolve_relative_path (dir, g_file_info_get_name (info));
+
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+ garcon_menu_monitor_app_dir (menu, file);
+
+ g_object_unref (file);
+ g_object_unref (info);
+ }
+
+ g_object_unref (enumerator);
}
@@ -1768,10 +2009,50 @@ garcon_menu_file_changed (GarconMenu *menu,
GFileMonitorEvent event_type,
GFileMonitor *monitor)
{
+ gboolean higher_priority = FALSE;
+ GFile *menu_file;
+ gchar **paths;
+ guint n;
+ guint i;
+
g_return_if_fail (GARCON_IS_MENU (menu));
g_return_if_fail (menu->priv->parent == NULL);
- g_signal_emit (menu, menu_signals[RELOAD_REQUIRED], 0);
+ /* Quick check: reloading is needed if the menu file being used has changed */
+ if (g_file_equal (menu->priv->file, file))
+ {
+ g_signal_emit (menu, menu_signals[RELOAD_REQUIRED], 0);
+ return;
+ }
+
+ /* Check if the event file has higher priority than the file currently being used */
+ for (n = 0; !higher_priority && n < G_N_ELEMENTS (GARCON_MENU_ROOT_SPECS); ++n)
+ {
+ paths = garcon_config_build_paths (GARCON_MENU_ROOT_SPECS[n]);
+
+ for (i = 0; !higher_priority && paths != NULL && paths[i] != NULL; ++i)
+ {
+ menu_file = g_file_new_for_path (paths[i]);
+
+ if (g_file_equal (menu_file, menu->priv->file))
+ {
+ g_object_unref (menu_file);
+ break;
+ }
+
+ if (g_file_equal (menu_file, file))
+ higher_priority = TRUE;
+
+ g_object_unref (menu_file);
+ }
+
+ g_strfreev (paths);
+ }
+
+ /* If the event file has higher priority, a menu reload is needed */
+ if (higher_priority)
+ g_signal_emit (menu, menu_signals[RELOAD_REQUIRED], 0);
+
}
@@ -1813,76 +2094,116 @@ garcon_menu_directory_file_changed (GarconMenu *menu,
GFileMonitorEvent event_type,
GFileMonitor *monitor)
{
- GFile *directory_file;
+ GarconMenuDirectory *old_directory;
g_return_if_fail (GARCON_IS_MENU (menu));
- if (event_type == G_FILE_MONITOR_EVENT_CHANGED
- || event_type == G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED)
+ if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
+ || event_type == G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED
+ || event_type == G_FILE_MONITOR_EVENT_DELETED
+ || event_type == G_FILE_MONITOR_EVENT_CREATED)
{
- g_debug ("directory file %s changed", g_file_get_path (file));
-
+ /* take a reference on the current menu directory */
if (menu->priv->directory != NULL)
- {
- directory_file = garcon_menu_directory_get_file (menu->priv->directory);
+ old_directory = g_object_ref (menu->priv->directory);
+
+ /* reset the menu directory of the menu and load a new one */
+ garcon_menu_resolve_directory (menu, NULL, FALSE);
+
+ /* notify listeners about the old and new menu directories */
+ g_signal_emit (menu, menu_signals[DIRECTORY_CHANGED], 0,
+ old_directory, menu->priv->directory);
+
+ /* release the old menu directory we no longer need */
+ if (old_directory != NULL)
+ g_object_unref (old_directory);
+ }
+}
- if (g_file_equal (directory_file, file))
- {
- g_debug (" reload the directory file");
-#if 0
- if (garcon_menu_directory_load (menu->priv->directory, NULL, &error))
- {
- /* TODO reload the menu directory (we need a new method
- * garcon_menu_directory_load() for this) and emit a
- * GarconMenu::directory-changed signal */
- }
- else
- {
- g_signal_emit (menu, menu_signals[DIRECTORY_CHANGED], 0,
- menu->priv->directory, NULL);
- g_object_unref (menu->priv->directory);
- menu->priv->directory = NULL;
- }
-#endif
- }
+static void
+garcon_menu_app_dir_changed (GarconMenu *menu,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GFileMonitor *monitor)
+{
+ GarconMenuItem *item;
+ GFileInfo *info;
+ GFileType file_type;
+ gboolean can_read;
- g_object_unref (directory_file);
- }
- }
- else if (event_type == G_FILE_MONITOR_EVENT_DELETED)
+ g_return_if_fail (GARCON_IS_MENU (menu));
+
+ if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
+ || event_type == G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED)
{
- g_debug ("directory file %s deleted", g_file_get_path (file));
+ g_debug ("app dir/file changed: %s: %s: %d",
+ garcon_menu_element_get_name (GARCON_MENU_ELEMENT (menu)),
+ g_file_get_path (file), event_type);
- if (menu->priv->directory != NULL)
+ file_type = g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL);
+
+ if (file_type == G_FILE_TYPE_DIRECTORY)
{
- directory_file = garcon_menu_directory_get_file (menu->priv->directory);
+ info = g_file_query_info (file, G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
+ G_FILE_QUERY_INFO_NONE, NULL, NULL);
- if (g_file_equal (directory_file, file))
+ can_read = g_file_info_get_attribute_boolean (info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
+
+ if (!can_read)
{
- g_debug (" current .directory file deleted");
-
- /* TODO check if there is another MenuDirectory
- * element that we can use and load instead. If this is
- * the case, change the file of the current menu directory,
- * reload it and emit a directory-changed signal.
- * otherwise destroy the menu directory and emit
- * a directory-changed signal with the GarconMenuDirectory
- * parameter set to NULL */
+ /* TODO emit 'item-removed' with all items that are included in the
+ * item pool and reside inside the unreadable directory */
}
-
- g_object_unref (directory_file);
+ }
+ else
+ {
+ item = garcon_menu_item_pool_lookup_file (menu->priv->pool, file);
+
+ /* TODO
+ * - find the menu(s) this item belongs to
+ * - reload the item
+ * - find the menu(s) the item belongs to after the reload
+ * (make sure to emit 'item-changed' or 'item-removed' and
+ * 'item-added' signals were appropriate)
+ */
}
}
else if (event_type == G_FILE_MONITOR_EVENT_CREATED)
{
- g_debug ("directory file %s created", g_file_get_path (file));
-
- /* TODO check if the file corresponds a MenuDirectory element that comes
- * after the one currently used by the Menu. If this is the case,
- * change the file of the GarconMenuElement, reload it
- * and emit a 'directory-changed' signal */
+ g_debug ("app dir/file created: %s: %s: %d",
+ garcon_menu_element_get_name (GARCON_MENU_ELEMENT (menu)),
+ g_file_get_path (file), event_type);
+
+ /* TODO
+ * 1) the new file is a directory
+ * - collect files from the newly created directory
+ * - add files to the correct menus (make sure to emit
+ * 'item-added' signals)
+ * - monitor the newly created directory
+ * 2) the new file is a regular file
+ * - load it into a GarconMenuItem
+ * - find the correct menu(s) for it
+ * - add it to those menus (make sure to emit 'item-added'
+ * signals)
+ */
+ }
+ else if (event_type == G_FILE_MONITOR_EVENT_DELETED)
+ {
+ g_debug ("app dir/file deleted: %s: %s: %d",
+ garcon_menu_element_get_name (GARCON_MENU_ELEMENT (menu)),
+ g_file_get_path (file), event_type);
+
+ /* TODO
+ * 1) the deleted file is a directory
+ * - remove all menu items coming from the removed directory
+ * from item pools (make sure to emit 'item-removed' signals)
+ * 2) the deleted file is a regular file
+ *
+ */
}
}
diff --git a/tests/test-display-menu.c b/tests/test-display-menu.c
index 13773fd..459c73d 100644
--- a/tests/test-display-menu.c
+++ b/tests/test-display-menu.c
@@ -173,6 +173,32 @@ create_item_widgets (GarconMenuItem *item,
static void
+directory_changed (GarconMenu *menu,
+ GarconMenuDirectory *old_directory,
+ GarconMenuDirectory *new_directory,
+ GtkWidget *item)
+{
+ const gchar *display_name;
+ const gchar *icon_name;
+ GtkWidget *image;
+
+ g_debug ("directory changed from %s to %s",
+ old_directory != NULL ? garcon_menu_directory_get_name (old_directory) : NULL,
+ new_directory != NULL ? garcon_menu_directory_get_name (new_directory) : NULL);
+
+ display_name = garcon_menu_element_get_name (GARCON_MENU_ELEMENT (menu));
+ gtk_menu_item_set_label (GTK_MENU_ITEM (item), display_name);
+
+ icon_name = garcon_menu_element_get_icon_name (GARCON_MENU_ELEMENT (menu));
+ if (icon_name == NULL)
+ icon_name = "applications-other";
+ image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (item));
+ gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, ICON_SIZE);
+}
+
+
+
+static void
create_menu_widgets (GtkWidget *gtk_menu,
GarconMenu *menu)
{
@@ -240,6 +266,9 @@ create_menu_widgets (GtkWidget *gtk_menu,
gtk_submenu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (gtk_item), gtk_submenu);
+ /* Update the menu item when the menu directory changes */
+ g_signal_connect (submenu, "directory-changed", G_CALLBACK (directory_changed), gtk_item);
+
/* Create widgets for submenu */
create_menu_widgets (gtk_submenu, submenu);
@@ -268,8 +297,6 @@ static void
show_menu (GtkButton *button,
GtkWidget *menu)
{
- gtk_container_foreach (GTK_CONTAINER (menu), (GtkCallback) remove_child, menu);
-
/* Create menu widgets if not already done */
if (g_list_length (gtk_container_get_children (GTK_CONTAINER (menu))) == 0)
create_menu_widgets (menu, root);
@@ -334,11 +361,15 @@ reload_required (GarconMenu *menu)
g_return_if_fail (GARCON_IS_MENU (menu));
+ g_debug ("reload required");
+
if (!reload_menu (&error))
{
g_error ("Failed to reload the menu: %s", error != NULL ? error->message : "No error");
g_error_free (error);
}
+
+ gtk_container_foreach (GTK_CONTAINER (gtk_root), (GtkCallback) remove_child, gtk_root);
}
More information about the Xfce4-commits
mailing list