[Xfce4-commits] <garcon:jannis/global-monitoring> Add 'reload-required' signal to GarconMenu.
Jannis Pohlmann
noreply at xfce.org
Wed Mar 10 21:02:04 CET 2010
Updating branch refs/heads/jannis/global-monitoring
to f35e60f4c666808ebc08f370f0f46dc81d4846ea (commit)
from 32e3f0914eeb5f1766e66e4d1490134c53162b14 (commit)
commit f35e60f4c666808ebc08f370f0f46dc81d4846ea
Author: Jannis Pohlmann <jannis at xfce.org>
Date: Mon Mar 8 21:40:42 2010 +0100
Add 'reload-required' signal to GarconMenu.
This signal is emitted when any of the .menu files (including those
merged by GarconMenuMerger) or merge directories are changed. It is only
emitted by the root menu which takes care of resolving filesystem change
events.
garcon_menu_merger_run() gained two new parameters: GList **merge_files
and GList **merge_dirs that are used to report merged .menu files and
merge directories back to the caller.
garcon/garcon-menu-merger.c | 73 +++++++++----
garcon/garcon-menu-merger.h | 2 +
garcon/garcon-menu-parser.h | 2 +-
garcon/garcon-menu.c | 233 ++++++++++++++++++++++++++++++++++++++++--
tests/test-display-menu.c | 61 +++++++++--
tests/test-menu-parser.c | 2 +-
6 files changed, 324 insertions(+), 49 deletions(-)
diff --git a/garcon/garcon-menu-merger.c b/garcon/garcon-menu-merger.c
index ad58639..4bf7fbf 100644
--- a/garcon/garcon-menu-merger.c
+++ b/garcon/garcon-menu-merger.c
@@ -35,16 +35,6 @@
typedef struct _GarconMenuMergerContext GarconMenuMergerContext;
-struct _GarconMenuMergerContext
-{
- GarconMenuNodeType node_type;
- GarconMenuMerger *merger;
- GCancellable *cancellable;
- GError **error;
- gboolean success;
- GList *file_stack;
-};
-
#define GARCON_MENU_MERGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GARCON_TYPE_MENU_MERGER, GarconMenuMergerPrivate))
@@ -98,6 +88,18 @@ struct _GarconMenuMergerPrivate
GList *file_stack;
};
+struct _GarconMenuMergerContext
+{
+ GarconMenuNodeType node_type;
+ GarconMenuMerger *merger;
+ GCancellable *cancellable;
+ GError **error;
+ gboolean success;
+ GList *file_stack;
+ GList **merge_files;
+ GList **merge_dirs;
+};
+
G_DEFINE_TYPE_WITH_CODE (GarconMenuMerger, garcon_menu_merger, G_TYPE_OBJECT,
@@ -266,6 +268,8 @@ garcon_menu_merger_prepare_merging (GarconMenuMerger *merger,
gboolean
garcon_menu_merger_run (GarconMenuMerger *merger,
+ GList **merge_files,
+ GList **merge_dirs,
GCancellable *cancellable,
GError **error)
{
@@ -280,6 +284,8 @@ garcon_menu_merger_run (GarconMenuMerger *merger,
context.error = error;
context.success = TRUE;
context.file_stack = NULL;
+ context.merge_files = merge_files;
+ context.merge_dirs = merge_dirs;
file = garcon_menu_tree_provider_get_file (GARCON_MENU_TREE_PROVIDER (merger));
context.file_stack = g_list_concat (context.file_stack, merger->priv->file_stack);
@@ -315,7 +321,7 @@ garcon_menu_merger_run (GarconMenuMerger *merger,
(GNodeTraverseFunc) garcon_menu_merger_resolve_relative_paths,
&context);
- garcon_menu_merger_remove_duplicate_paths (merger->priv->menu,GARCON_MENU_NODE_TYPE_DIRECTORY_DIR);
+ garcon_menu_merger_remove_duplicate_paths (merger->priv->menu, GARCON_MENU_NODE_TYPE_DIRECTORY_DIR);
garcon_menu_merger_remove_duplicate_paths (merger->priv->menu, GARCON_MENU_NODE_TYPE_DIRECTORY);
garcon_menu_merger_resolve_moves (merger->priv->menu);
@@ -439,6 +445,16 @@ garcon_menu_merger_insert_default_dirs (GNode *parent,
+static gint
+compare_files (GFile *file,
+ GFile *other_file)
+{
+ return g_file_equal (file, other_file) ? 0 : 1;
+}
+
+
+
+
static void
garcon_menu_merger_insert_default_merge_dirs (GNode *parent,
GNode *defaults_node)
@@ -750,11 +766,21 @@ garcon_menu_merger_resolve_merge_dirs (GNode *node,
dir = _garcon_file_new_for_unknown_input (garcon_menu_node_tree_get_string (node), NULL);
+ if (dir == NULL)
+ return FALSE;
+
enumerator = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME,
G_FILE_QUERY_INFO_NONE, NULL, NULL);
if (G_UNLIKELY (enumerator != NULL))
{
+ /* Add merge dir to the list */
+ if (context->merge_dirs != NULL
+ && !g_list_find_custom (*context->merge_dirs, dir, (GCompareFunc) compare_files))
+ {
+ *context->merge_dirs = g_list_prepend (*context->merge_dirs, g_object_ref (dir));
+ }
+
while (TRUE)
{
file_info = g_file_enumerator_next_file (enumerator, NULL, NULL);
@@ -813,16 +839,6 @@ garcon_menu_parser_insert_elements (GNode *node,
-static gint
-compare_files (GFile *file,
- GFile *other_file)
-{
- return g_file_equal (file, other_file) ? 0 : 1;
-}
-
-
-
-
static gboolean
garcon_menu_merger_process_merge_files (GNode *node,
GarconMenuMergerContext *context)
@@ -850,7 +866,6 @@ garcon_menu_merger_process_merge_files (GNode *node,
}
parser = garcon_menu_parser_new (file);
- g_object_unref (file);
if (G_LIKELY (garcon_menu_parser_run (parser, NULL, NULL)))
{
@@ -860,7 +875,10 @@ garcon_menu_merger_process_merge_files (GNode *node,
merger->priv->file_stack = g_list_copy (context->file_stack);
g_list_foreach (merger->priv->file_stack, (GFunc) g_object_ref, NULL);
- if (G_LIKELY (garcon_menu_merger_run (merger, NULL, NULL)))
+ if (G_LIKELY (garcon_menu_merger_run (merger,
+ context->merge_files,
+ context->merge_dirs,
+ context->cancellable, NULL)))
{
tree = garcon_menu_tree_provider_get_tree (GARCON_MENU_TREE_PROVIDER (merger));
g_object_unref (merger);
@@ -869,11 +887,20 @@ garcon_menu_merger_process_merge_files (GNode *node,
g_node_traverse (tree, G_IN_ORDER, G_TRAVERSE_ALL, 2,
(GNodeTraverseFunc) garcon_menu_parser_insert_elements, tree);
g_node_destroy (tree);
+
+ /* Add merged file to the list */
+ if (context->merge_files != NULL
+ && !g_list_find_custom (*context->merge_files, file, (GCompareFunc) compare_files))
+ {
+ *context->merge_files = g_list_prepend (*context->merge_files, g_object_ref (file));
+ }
}
}
garcon_menu_node_tree_free (node);
+ g_object_unref (file);
+
return FALSE;
}
diff --git a/garcon/garcon-menu-merger.h b/garcon/garcon-menu-merger.h
index 45a63ca..129fc43 100644
--- a/garcon/garcon-menu-merger.h
+++ b/garcon/garcon-menu-merger.h
@@ -45,6 +45,8 @@ GType garcon_menu_merger_get_type (void) G_GNUC_CONST;
GarconMenuMerger *garcon_menu_merger_new (GarconMenuTreeProvider *provider) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
gboolean garcon_menu_merger_run (GarconMenuMerger *merger,
+ GList **merge_files,
+ GList **merge_dirs,
GCancellable *cancellable,
GError **error);
diff --git a/garcon/garcon-menu-parser.h b/garcon/garcon-menu-parser.h
index 8d5095d..53a16e8 100644
--- a/garcon/garcon-menu-parser.h
+++ b/garcon/garcon-menu-parser.h
@@ -42,7 +42,7 @@ typedef struct _GarconMenuParser GarconMenuParser;
GType garcon_menu_parser_get_type (void) G_GNUC_CONST;
-GarconMenuParser *garcon_menu_parser_new (GFile *file) G_GNUC_MALLOC;
+GarconMenuParser *garcon_menu_parser_new (GFile *file) G_GNUC_MALLOC;
gboolean garcon_menu_parser_run (GarconMenuParser *parser,
GCancellable *cancellable,
GError **error);
diff --git a/garcon/garcon-menu.c b/garcon/garcon-menu.c
index edfb0f7..7b11fc6 100644
--- a/garcon/garcon-menu.c
+++ b/garcon/garcon-menu.c
@@ -93,6 +93,15 @@ enum
+/* Signal identifiers */
+enum
+{
+ RELOAD_REQUIRED,
+ LAST_SIGNAL,
+};
+
+
+
static void garcon_menu_element_init (GarconMenuElementIface *iface);
static void garcon_menu_finalize (GObject *object);
static void garcon_menu_get_property (GObject *object,
@@ -133,6 +142,23 @@ static const gchar *garcon_menu_get_element_icon_name (GarconM
static gboolean garcon_menu_get_element_visible (GarconMenuElement *element);
static gboolean garcon_menu_get_element_show_in_environment (GarconMenuElement *element);
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_file_changed (GarconMenu *menu,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GFileMonitor *monitor);
+static void garcon_menu_merge_file_changed (GarconMenu *menu,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GFileMonitor *monitor);
+static void garcon_menu_merge_dir_changed (GarconMenu *menu,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GFileMonitor *monitor);
@@ -144,6 +170,13 @@ struct _GarconMenuPrivate
/* DOM tree */
GNode *tree;
+ /* Merged menu files and merge directories */
+ GList *merge_files;
+ GList *merge_dirs;
+
+ /* File and directory monitors */
+ GList *monitors;
+
/* Directory */
GarconMenuDirectory *directory;
@@ -167,6 +200,10 @@ G_DEFINE_TYPE_WITH_CODE (GarconMenu, garcon_menu, G_TYPE_OBJECT,
+static guint menu_signals[LAST_SIGNAL];
+
+
+
static void
garcon_menu_class_init (GarconMenuClass *klass)
{
@@ -207,6 +244,17 @@ garcon_menu_class_init (GarconMenuClass *klass)
GARCON_TYPE_MENU_DIRECTORY,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ menu_signals[RELOAD_REQUIRED] =
+ g_signal_new ("reload-required",
+ GARCON_TYPE_MENU,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
}
@@ -230,6 +278,8 @@ garcon_menu_init (GarconMenu *menu)
menu->priv = GARCON_MENU_GET_PRIVATE (menu);
menu->priv->file = NULL;
menu->priv->tree = NULL;
+ menu->priv->merge_files = NULL;
+ menu->priv->merge_dirs = NULL;
menu->priv->directory = NULL;
menu->priv->submenus = NULL;
menu->priv->parent = NULL;
@@ -242,24 +292,59 @@ garcon_menu_init (GarconMenu *menu)
static void
-garcon_menu_finalize (GObject *object)
+garcon_menu_clear (GarconMenu *menu)
{
- GarconMenu *menu = GARCON_MENU (object);
+ g_return_if_fail (GARCON_IS_MENU (menu));
- /* Destroy the menu tree */
+ /* Check if the menu is a root menu */
if (menu->priv->parent == NULL)
- garcon_menu_node_tree_free (menu->priv->tree);
+ {
+ /* Stop monitoring (recursively) */
+ garcon_menu_stop_monitoring (menu);
+
+ /* Destroy the menu tree */
+ garcon_menu_node_tree_free (menu->priv->tree);
+ menu->priv->tree = NULL;
+
+ /* Release the merge files */
+ g_list_foreach (menu->priv->merge_files, (GFunc) g_object_unref, NULL);
+ g_list_free (menu->priv->merge_files);
+ menu->priv->merge_files = NULL;
+
+ /* Release the merge dirs */
+ g_list_foreach (menu->priv->merge_dirs, (GFunc) g_object_unref, NULL);
+ g_list_free (menu->priv->merge_dirs);
+ menu->priv->merge_dirs = NULL;
+ }
- /* Free file */
- g_object_unref (menu->priv->file);
+ /* Free submenus */
+ g_list_foreach (menu->priv->submenus, (GFunc) g_object_unref, NULL);
+ g_list_free (menu->priv->submenus);
+ menu->priv->submenus = NULL;
/* Free directory */
if (G_LIKELY (menu->priv->directory != NULL))
g_object_unref (menu->priv->directory);
- /* Free submenus */
- g_list_foreach (menu->priv->submenus, (GFunc) g_object_unref, NULL);
- g_list_free (menu->priv->submenus);
+ /* Clear the item pool */
+ garcon_menu_item_pool_clear (menu->priv->pool);
+
+ /* Clear the item cache */
+ garcon_menu_item_cache_invalidate (menu->priv->cache);
+}
+
+
+
+static void
+garcon_menu_finalize (GObject *object)
+{
+ GarconMenu *menu = GARCON_MENU (object);
+
+ /* Clear resources allocated in the load process */
+ garcon_menu_clear (menu);
+
+ /* Free file */
+ g_object_unref (menu->priv->file);
/* Free item pool */
g_object_unref (menu->priv->pool);
@@ -546,16 +631,27 @@ garcon_menu_load (GarconMenu *menu,
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 */
+ garcon_menu_clear (menu);
+
parser = garcon_menu_parser_new (menu->priv->file);
if (G_LIKELY (garcon_menu_parser_run (parser, cancellable, error)))
{
merger = garcon_menu_merger_new (GARCON_MENU_TREE_PROVIDER (parser));
- if (garcon_menu_merger_run (merger, cancellable, error))
- menu->priv->tree = garcon_menu_tree_provider_get_tree (GARCON_MENU_TREE_PROVIDER (merger));
+ if (garcon_menu_merger_run (merger,
+ &menu->priv->merge_files,
+ &menu->priv->merge_dirs,
+ cancellable, error))
+ {
+ menu->priv->tree =
+ garcon_menu_tree_provider_get_tree (GARCON_MENU_TREE_PROVIDER (merger));
+ }
else
- success = FALSE;
+ {
+ success = FALSE;
+ }
g_object_unref (merger);
}
@@ -585,6 +681,9 @@ garcon_menu_load (GarconMenu *menu,
g_hash_table_unref (desktop_id_table);
+ /* Initiate monitoring */
+ garcon_menu_start_monitoring (menu);
+
return TRUE;
}
@@ -1493,3 +1592,113 @@ garcon_menu_get_element_no_display (GarconMenuElement *element)
else
return garcon_menu_directory_get_no_display (menu->priv->directory);
}
+
+
+
+static void
+garcon_menu_start_monitoring (GarconMenu *menu)
+{
+ GFileMonitor *monitor;
+ GList *lp;
+
+ g_return_if_fail (GARCON_IS_MENU (menu));
+
+ /* Let only the root menu monitor .menu files and merge directories */
+ if (menu->priv->parent == NULL)
+ {
+ monitor = g_file_monitor (menu->priv->file, G_FILE_MONITOR_NONE, NULL, NULL);
+ if (monitor != NULL)
+ {
+ g_debug ("monitoring menu file %s", g_file_get_path (menu->priv->file));
+
+ menu->priv->monitors = g_list_prepend (menu->priv->monitors, monitor);
+ g_signal_connect_swapped (monitor, "changed",
+ G_CALLBACK (garcon_menu_file_changed), 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)
+ {
+ g_debug ("monitoring merged file %s", g_file_get_path (lp->data));
+
+ menu->priv->monitors = g_list_prepend (menu->priv->monitors, monitor);
+ g_signal_connect_swapped (monitor, "changed",
+ G_CALLBACK (garcon_menu_merge_file_changed), 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)
+ {
+ g_debug ("monitoring merged dir %s", g_file_get_path (lp->data));
+
+ menu->priv->monitors = g_list_prepend (menu->priv->monitors, monitor);
+ g_signal_connect_swapped (monitor, "changed",
+ G_CALLBACK (garcon_menu_merge_dir_changed), menu);
+ }
+ }
+ }
+
+ /* TODO monitor the .directory file */
+
+ /* TODO monitor desktop directories */
+
+ /* TODO recurse into child menus */
+}
+
+
+
+static void
+garcon_menu_file_changed (GarconMenu *menu,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GFileMonitor *monitor)
+{
+ 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);
+}
+
+
+
+static void
+garcon_menu_merge_file_changed (GarconMenu *menu,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GFileMonitor *monitor)
+{
+ 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);
+}
+
+
+
+static void
+garcon_menu_merge_dir_changed (GarconMenu *menu,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GFileMonitor *monitor)
+{
+ 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);
+}
+
+
+
+static void
+garcon_menu_stop_monitoring (GarconMenu *menu)
+{
+ g_return_if_fail (GARCON_IS_MENU (menu));
+}
diff --git a/tests/test-display-menu.c b/tests/test-display-menu.c
index c62dbd8..53ab92e 100644
--- a/tests/test-display-menu.c
+++ b/tests/test-display-menu.c
@@ -38,6 +38,7 @@
/* Root menu */
static GarconMenu *root = NULL;
+static GtkWidget *gtk_root = NULL;
@@ -172,7 +173,7 @@ create_item_widgets (GarconMenuItem *item,
static void
-create_menu_widgets (GtkWidget *gtk_menu,
+create_menu_widgets (GtkWidget *gtk_menu,
GarconMenu *menu)
{
GarconMenuDirectory *directory;
@@ -255,9 +256,20 @@ create_menu_widgets (GtkWidget *gtk_menu,
static void
+remove_child (GtkWidget *child,
+ GtkMenu *menu)
+{
+ gtk_container_remove (GTK_CONTAINER (menu), child);
+}
+
+
+
+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);
@@ -281,7 +293,6 @@ create_main_window (void)
{
GtkWidget *window;
GtkWidget *button;
- GtkWidget *menu;
/* Create main window */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
@@ -299,10 +310,35 @@ create_main_window (void)
gtk_widget_show (button);
/* Create GTK+ root menu */
- menu = gtk_menu_new ();
+ gtk_root = gtk_menu_new ();
/* Display root menu when the button is clicked */
- g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (show_menu), menu);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (show_menu), gtk_root);
+}
+
+
+
+static gboolean
+reload_menu (GError **error)
+{
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ return garcon_menu_load (root, NULL, error);
+}
+
+
+
+static void
+rebuild_required (GarconMenu *menu)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (GARCON_IS_MENU (menu));
+
+ if (!reload_menu (&error))
+ {
+ g_error ("Failed to reload the menu: %s", error != NULL ? error->message : "No error");
+ g_error_free (error);
+ }
}
@@ -327,25 +363,26 @@ main (gint argc,
root = garcon_menu_new_applications ();
/* Check if the menu was loaded */
- if (root != NULL
- && garcon_menu_load (root, NULL, &error))
+ if (reload_menu (&error))
{
- /* Create main window */
+ /* create the main window */
create_main_window ();
+ /* be notified when a menu rebuild is required */
+ g_signal_connect (root, "rebuild-required", G_CALLBACK (rebuild_required), NULL);
+
/* Enter main loop */
gtk_main ();
-
- /* Destroy the root menu */
- g_object_unref (root);
}
else
{
g_error ("Failed to load the menu: %s", error != NULL ? error->message : "No error");
- if (error != NULL)
- g_error_free (error);
+ g_error_free (error);
exit_code = EXIT_FAILURE;
}
+ /* Destroy the root menu */
+ g_object_unref (root);
+
return exit_code;
}
diff --git a/tests/test-menu-parser.c b/tests/test-menu-parser.c
index 334e0fc..792dc4b 100644
--- a/tests/test-menu-parser.c
+++ b/tests/test-menu-parser.c
@@ -268,7 +268,7 @@ main (int argc,
merger = garcon_menu_merger_new (GARCON_MENU_TREE_PROVIDER (parser));
- if (G_LIKELY (garcon_menu_merger_run (merger, NULL, &error)))
+ if (G_LIKELY (garcon_menu_merger_run (merger, NULL, NULL, NULL, &error)))
{
g_print ("\n\n");
print_tree (GARCON_MENU_TREE_PROVIDER (merger));
More information about the Xfce4-commits
mailing list