=== modified file 'src/bridge.c' --- src/bridge.c 2011-06-17 02:49:46 +0000 +++ src/bridge.c 2011-06-17 17:03:13 +0000 @@ -84,6 +84,7 @@ GDBusProxy *appmenuproxy; gboolean online; + GHashTable *rebuild_schedule; }; typedef struct _RecurseContext @@ -98,7 +99,6 @@ G_DEFINE_DYNAMIC_TYPE(AppMenuBridge, app_menu_bridge, UBUNTU_TYPE_MENU_PROXY) -static GHashTable *rebuild_ids = NULL; static GDBusNodeInfo * registrar_node_info = NULL; static GDBusInterfaceInfo * registrar_interface_info = NULL; @@ -123,7 +123,8 @@ bridge->priv = G_TYPE_INSTANCE_GET_PRIVATE (bridge, APP_MENU_TYPE_BRIDGE, AppMenuBridgePrivate); bridge->priv->windows = NULL; - + bridge->priv->rebuild_schedule = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, g_free); bridge->priv->appmenuproxy = NULL; g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, @@ -205,6 +206,11 @@ g_list_foreach (bridge->priv->windows, (GFunc)context_free, NULL); g_list_free (bridge->priv->windows); bridge->priv->windows = NULL; + g_hash_table_destroy (bridge->priv->rebuild_schedule); + /* There cannot be any pending rebuild timeouts, because each timeout + owns a reference to the bridge. Therefore the hash table will be + empty and destroying it is safe. + */ G_OBJECT_CLASS (app_menu_bridge_parent_class)->finalize (object); } @@ -475,59 +481,44 @@ do_rebuild (gpointer arg) { RebuildData *data = arg; - if (gtk_widget_is_toplevel (data->widget)) + AppMenuBridge *bridge = data->bridge; + GtkWidget *widget = data->widget; + + g_hash_table_remove (bridge->priv->rebuild_schedule, widget); + /* side-effect: frees data */ + + if (gtk_widget_is_toplevel (widget)) { - rebuild_window_items (data->bridge, - data->widget); + rebuild_window_items (bridge, widget); } - g_hash_table_remove (rebuild_ids, data->widget); - g_object_unref(data->widget); - g_free (data); + g_object_unref (bridge); + g_object_unref (widget); return FALSE; } -struct rebuild_table_entry { - guint id; - RebuildData *data; -}; - +/* + The idea here is to ensure that a given menu is rebuilt at most + once every 100ms, irrespective of how many times rebuild is called. +*/ static void rebuild (AppMenuBridge *bridge, GtkWidget *toplevel) { - struct rebuild_table_entry *entry; - - if (rebuild_ids != NULL) - { - entry = (g_hash_table_lookup (rebuild_ids, toplevel)); - if (entry) - { - g_source_remove (entry->id); - g_object_unref(entry->data->widget); - g_free (entry->data); - g_hash_table_remove (rebuild_ids, toplevel); - } - } - - RebuildData *data = g_new0 (RebuildData, 1); - data->bridge = bridge; - data->widget = g_object_ref(toplevel); - - entry = g_new(struct rebuild_table_entry, 1); - entry->id = g_timeout_add (100, - do_rebuild, - data); - entry->data = data; - - if (rebuild_ids == NULL) - { - rebuild_ids = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); - } - - g_hash_table_insert (rebuild_ids, toplevel, entry); + RebuildData *data = g_hash_table_lookup (bridge->priv->rebuild_schedule, + toplevel); + if (!data) + { + data = g_new (RebuildData, 1); + data->widget = g_object_ref(toplevel); + data->bridge = g_object_ref(bridge); + g_hash_table_insert (bridge->priv->rebuild_schedule, + toplevel, data); + g_timeout_add (100, do_rebuild, data); + } + /* Otherwise, do_rebuild has already been scheduled. Do nothing. */ } static DbusmenuMenuitem * find_menu_bar (GtkWidget * widget); @@ -937,9 +928,6 @@ G_MODULE_EXPORT void menu_proxy_module_unload (UbuntuMenuProxyModule *module) { - if (rebuild_ids) - { - g_hash_table_destroy (rebuild_ids); - rebuild_ids = NULL; - } + } +