Cody Russell has proposed merging lp:~bratsche/appmenu-gtk/almost-rewrite into lp:appmenu-gtk.
Requested reviews: Canonical Desktop Experience Team (canonical-dx-team) So in the process of trying to store root and server per-window, I ended up rewriting almost the entire appmenu-gtk. -- https://code.launchpad.net/~bratsche/appmenu-gtk/almost-rewrite/+merge/27638 Your team ayatana-commits is subscribed to branch lp:appmenu-gtk.
=== modified file 'src/bridge.c' --- src/bridge.c 2010-06-10 21:00:06 +0000 +++ src/bridge.c 2010-06-15 15:04:25 +0000 @@ -21,6 +21,7 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <dbus/dbus.h> #include <dbus/dbus-glib.h> @@ -46,34 +47,155 @@ GtkWidget *child, guint position); static gboolean app_menu_bridge_show_local (UbuntuMenuProxy *proxy); +static void dbus_owner_change (DBusGProxy *proxy, + const gchar *name, + const gchar *prev, + const gchar *new, + AppMenuBridge *bridge); +static void toplevel_realized (GtkWidget *widget, + gpointer user_data); +static void toplevel_notify_cb (GtkWidget *widget, + GParamSpec *pspec, + UbuntuMenuProxy *proxy); + +typedef struct _AppWindowContext AppWindowContext; + +struct _AppWindowContext +{ + GtkWidget *window; + DbusmenuServer *server; + DbusmenuMenuitem *root; + gchar *path; + gboolean registered; +}; struct _AppMenuBridgePrivate { - GHashTable *items; /* <GtkWidget *, DbusmenuMenuitem *> */ + GHashTable *windows; /* XID, AppWindowContext *> */ - DbusmenuServer *server; + DBusGConnection *session_bus; + DBusGProxy *dbusproxy; + DBusGProxy *appmenuproxy; + gboolean online; }; -static DBusGProxy *dbusproxy = NULL; -static gboolean registered = FALSE; +typedef struct _RecurseContext +{ + AppMenuBridge *bridge; + AppWindowContext *context; + + gint count; + DbusmenuMenuitem *stack[30]; +} RecurseContext; G_DEFINE_DYNAMIC_TYPE(AppMenuBridge, app_menu_bridge, UBUNTU_TYPE_MENU_PROXY) static void +app_menu_bridge_setup_proxy (AppMenuBridge *bridge) +{ + bridge->priv->appmenuproxy = dbus_g_proxy_new_for_name_owner (bridge->priv->session_bus, + APP_MENU_DBUS_NAME, + APP_MENU_DBUS_OBJECT, + APP_MENU_INTERFACE, + NULL); + + bridge->priv->online = TRUE; +} + +static void app_menu_bridge_init (AppMenuBridge *bridge) { bridge->priv = G_TYPE_INSTANCE_GET_PRIVATE (bridge, APP_MENU_TYPE_BRIDGE, AppMenuBridgePrivate); - bridge->priv->items = g_hash_table_new (g_direct_hash, g_direct_equal); - bridge->priv->items = g_hash_table_new (g_direct_hash, g_direct_equal); - - bridge->priv->server = dbusmenu_server_new (APP_MENU_PATH); + bridge->priv->windows = g_hash_table_new (g_int64_hash, g_int64_equal); + + bridge->priv->session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + + bridge->priv->dbusproxy = dbus_g_proxy_new_for_name_owner (bridge->priv->session_bus, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + NULL); + + if (bridge->priv->dbusproxy) + { + dbus_g_proxy_add_signal (bridge->priv->dbusproxy, + "NameOwnerChanged", + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (bridge->priv->dbusproxy, + "NameOwnerChanged", + G_CALLBACK (dbus_owner_change), + bridge, + NULL); + } + + app_menu_bridge_setup_proxy (bridge); +} + +static void +app_menu_bridge_dispose (GObject *object) +{ + AppMenuBridge *bridge = APP_MENU_BRIDGE (object); + + if (bridge->priv->appmenuproxy) + { + g_object_unref (bridge->priv->appmenuproxy); + } + + if (bridge->priv->dbusproxy) + { + g_object_unref (bridge->priv->dbusproxy); + } + + if (bridge->priv->session_bus) + { + g_object_unref (bridge->priv->session_bus); + } +} + +static void +destroy_window_context (gpointer key, + gpointer value, + gpointer user_data) +{ + AppWindowContext *context = (AppWindowContext *)value; + + if (context->root) + { + g_object_unref (context->root); + context->root = NULL; + } + + if (context->server) + { + g_object_unref (context->server); + context->server = NULL; + } + + if (context->path) + { + g_free (context->path); + } } static void app_menu_bridge_finalize (GObject *object) { - g_hash_table_destroy (APP_MENU_BRIDGE (object)->priv->items); + AppMenuBridge *bridge = APP_MENU_BRIDGE (object); + + if (bridge->priv->windows) + { + g_hash_table_foreach (bridge->priv->windows, + destroy_window_context, + NULL); + g_hash_table_destroy (bridge->priv->windows); + } + + G_OBJECT_CLASS (app_menu_bridge_parent_class)->finalize (object); } static void @@ -90,6 +212,7 @@ proxy_class->insert = app_menu_bridge_insert; proxy_class->show_local = app_menu_bridge_show_local; + object_class->dispose = app_menu_bridge_dispose; object_class->finalize = app_menu_bridge_finalize; g_type_class_add_private (class, sizeof (AppMenuBridgePrivate)); @@ -100,6 +223,96 @@ { } +static void +register_application_window (gpointer key, + gpointer value, + gpointer user_data) +{ + AppWindowContext *context = (AppWindowContext *)value; + GtkWidget *widget = context->window; + AppMenuBridge *bridge = APP_MENU_BRIDGE (user_data); + + if (!context->registered && context->server != NULL && context->root != NULL && GTK_IS_WINDOW (widget)) + { + dbusmenu_server_set_root (context->server, context->root); + org_ayatana_WindowMenu_Registrar_register_window (bridge->priv->appmenuproxy, + GDK_WINDOW_XID (gtk_widget_get_window (widget)), + context->path, + NULL); + + context->registered = TRUE; + } +} + +static void +register_application_windows (AppMenuBridge *bridge) +{ + g_hash_table_foreach (bridge->priv->windows, + register_application_window, + bridge); +} + +static void +unregister_application_window (gpointer key, + gpointer value, + gpointer user_data) +{ + AppWindowContext *context = (AppWindowContext *)value; + + context->registered = FALSE; +} + +static void +unregister_application_windows (AppMenuBridge *bridge) +{ + g_hash_table_foreach (bridge->priv->windows, + unregister_application_window, + bridge); +} + +static void +app_menu_bridge_proxy_vanished (AppMenuBridge *bridge) +{ + if (bridge->priv->online) + g_print ("VANISH\n"); + + bridge->priv->online = FALSE; + + unregister_application_windows (bridge); +} + +static void +app_menu_bridge_proxy_appeared (AppMenuBridge *bridge) +{ + g_print ("APPEAR\n"); + + bridge->priv->online = TRUE; + + app_menu_bridge_setup_proxy (bridge); + + register_application_windows (bridge); +} + +static void +dbus_owner_change (DBusGProxy *proxy, + const gchar *name, + const gchar *prev, + const gchar *new, + AppMenuBridge *bridge) +{ + if (strlen (new) > 0 && strlen (prev) == 0) + { + if (g_strcmp0 (name, APP_MENU_DBUS_NAME) == 0) + { + app_menu_bridge_proxy_appeared (bridge); + } + } + else if (strlen (new) == 0 && strlen (prev) > 0) + { + app_menu_bridge_proxy_vanished (bridge); + } +} + static GtkWidget * find_menu_label (GtkWidget *widget) { @@ -184,15 +397,187 @@ } static void +checkbox_toggled (GtkWidget *widget, DbusmenuMenuitem *mi) +{ + dbusmenu_menuitem_property_set_int (mi, + DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, + gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); +} + +static DbusmenuMenuitem * +construct_dbusmenu_for_widget (GtkWidget *widget) +{ + DbusmenuMenuitem *mi = dbusmenu_menuitem_new (); + + if (GTK_IS_MENU_ITEM (widget)) + { + if (GTK_IS_SEPARATOR_MENU_ITEM (widget)) + { + dbusmenu_menuitem_property_set (mi, + "type", + "separator"); + } + else + { + if (GTK_IS_CHECK_MENU_ITEM (widget)) + { + dbusmenu_menuitem_property_set (mi, + DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, + GTK_IS_RADIO_MENU_ITEM (widget) ? DBUSMENU_MENUITEM_TOGGLE_RADIO : DBUSMENU_MENUITEM_TOGGLE_CHECK); + + dbusmenu_menuitem_property_set_int (mi, + DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, + gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); + + g_signal_connect (widget, + "toggle", + G_CALLBACK (checkbox_toggled), + mi); + } + + dbusmenu_menuitem_property_set (mi, + "label", + get_menu_label_text (widget)); + + dbusmenu_menuitem_property_set_bool (mi, + DBUSMENU_MENUITEM_PROP_VISIBLE, + gtk_widget_get_visible (widget)); + + dbusmenu_menuitem_property_set_bool (mi, + DBUSMENU_MENUITEM_PROP_ENABLED, + gtk_widget_get_sensitive (widget)); + + g_signal_connect (G_OBJECT (widget), + "notify", + G_CALLBACK (widget_notify_cb), + mi); + + g_signal_connect (G_OBJECT (mi), + "item_activated", + G_CALLBACK (item_activated), + widget); + } + } + + return mi; +} + +static void +rebuild_item (GtkWidget *widget, + RecurseContext *recurse) +{ + if (GTK_IS_CONTAINER (widget)) + { + gboolean increment = GTK_IS_MENU_BAR (widget) || GTK_IS_MENU_ITEM (widget); + + if (increment) + recurse->count++; + + if (recurse->count > -1 && increment) + { + recurse->stack[recurse->count] = construct_dbusmenu_for_widget (widget); + + if (recurse->count > 0) + { + if (recurse->count == 1) + dbusmenu_menuitem_child_append (recurse->stack[recurse->count - 1], + recurse->stack[recurse->count]); + else + dbusmenu_menuitem_child_prepend (recurse->stack[recurse->count - 1], + recurse->stack[recurse->count]); + } + } + + gtk_container_foreach (GTK_CONTAINER (widget), + (GtkCallback)rebuild_item, + recurse); + + if (GTK_IS_MENU_ITEM (widget)) + { + GtkWidget *menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); + + if (menu != NULL) + { + rebuild_item (menu, recurse); + } + } + + if (increment) + recurse->count--; + } +} + +static void +rebuild_window_items (AppMenuBridge *bridge, + GtkWidget *toplevel) +{ + XID xid; + AppWindowContext *context; + RecurseContext recurse; + + if (!GTK_IS_WINDOW (toplevel)) + { + g_signal_connect (G_OBJECT (toplevel), + "notify", + G_CALLBACK (toplevel_notify_cb), + bridge); + + return; + } + + if (!gtk_widget_get_realized (toplevel)) + { + g_signal_connect (toplevel, "realize", + G_CALLBACK (toplevel_realized), + bridge); + + return; + } + + xid = GDK_WINDOW_XID (gtk_widget_get_window (toplevel)); + context = g_hash_table_lookup (bridge->priv->windows, &xid); + + if (!context) + { + context = g_new0 (AppWindowContext, 1); + g_hash_table_insert (bridge->priv->windows, &xid, context); + } + + context->window = toplevel; + + if (!context->path) + context->path = g_strdup_printf ("/org/ayatana/menu/%X", (guint)xid); + + if (!context->server) + context->server = dbusmenu_server_new (context->path); + + recurse.bridge = bridge; + recurse.context = context; + recurse.count = -1; + + gtk_container_foreach (GTK_CONTAINER (toplevel), + (GtkCallback)rebuild_item, + &recurse); + + context->root = recurse.stack[0]; + + dbusmenu_server_set_root (context->server, context->root); +} + + +static void toplevel_realized (GtkWidget *widget, gpointer user_data) { - /* Register the toplevel window now that it's been realized. */ - org_ayatana_WindowMenu_Registrar_register_window (dbusproxy, - GDK_WINDOW_XID (gtk_widget_get_window (widget)), - APP_MENU_PATH, - NULL); - registered = TRUE; + AppMenuBridge *bridge = APP_MENU_BRIDGE (user_data); + + if (GTK_IS_WINDOW (widget)) + { + rebuild_window_items (bridge, widget); + register_application_windows (bridge); + + return; + } } static void @@ -202,58 +587,26 @@ { if (pspec->name == g_intern_static_string ("parent")) { + GtkWidget *toplevel = gtk_widget_get_toplevel (widget); AppMenuBridge *bridge = APP_MENU_BRIDGE (proxy); - DbusmenuMenuitem *root = g_hash_table_lookup (bridge->priv->items, widget); - - if (root) - { - dbusmenu_server_set_root (bridge->priv->server, root); - } - - if (!registered) - { - GtkWidget *parent = gtk_widget_get_toplevel (widget); - - if (!GTK_IS_WINDOW (parent)) - { - /* The current toplevel widget is not our final toplevel widget, as it's - * not a GtkWindow. Let's defer registration until we have a real toplevel. - */ - g_signal_connect (G_OBJECT (parent), - "notify", - G_CALLBACK (toplevel_notify_cb), - proxy); - - return; - } - else - { - /* This is the real toplevel window widget. If it's already - * realized then go ahead and register it, otherwise wait until - * it's been realized. - */ - if (gtk_widget_get_realized (widget)) { - org_ayatana_WindowMenu_Registrar_register_window (dbusproxy, - GDK_WINDOW_XID (gtk_widget_get_window (widget)), - APP_MENU_PATH, - NULL); - registered = TRUE; - } else { - g_signal_connect (parent, "realize", - G_CALLBACK (toplevel_realized), - NULL); - } - } - } + + rebuild_window_items (bridge, toplevel); } } static void -checkbox_toggled (GtkWidget *widget, DbusmenuMenuitem *mi) +attach_notify_cb (GtkWidget *widget, + GParamSpec *pspec, + AppMenuBridge *bridge) { - dbusmenu_menuitem_property_set_int (mi, - DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, - gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); + if (pspec->name == g_intern_static_string ("attach-widget")) + { + GtkWidget *attach = NULL; + + g_object_get (widget, "attach-widget", &attach, NULL); + + rebuild_window_items (bridge, attach); + } } static void @@ -264,8 +617,7 @@ { AppMenuBridge *bridge; AppMenuBridgePrivate *priv; - DbusmenuMenuitem *item; - DbusmenuMenuitem *parent_item = NULL; + GtkWidget *toplevel = NULL; gboolean append = FALSE; if (GTK_IS_TEAROFF_MENU_ITEM (child)) @@ -274,24 +626,16 @@ bridge = APP_MENU_BRIDGE (proxy); priv = bridge->priv; - if (g_hash_table_lookup (bridge->priv->items, child) != NULL) - return; + /* + g_print ("INSERT (parent %s %p, child %s %p\n", + G_OBJECT_TYPE_NAME (parent), parent, + G_OBJECT_TYPE_NAME (child), child); + */ + + toplevel = gtk_widget_get_toplevel (parent); if (GTK_IS_MENU_BAR (parent)) { - if (g_hash_table_lookup (bridge->priv->items, parent) == NULL) - { - DbusmenuMenuitem *root = dbusmenu_menuitem_new (); - g_hash_table_insert (bridge->priv->items, parent, root); - parent_item = root; - } - else - { - parent_item = g_hash_table_lookup (bridge->priv->items, parent); - } - - GtkWidget *toplevel = gtk_widget_get_toplevel (parent); - g_signal_connect (G_OBJECT (toplevel), "notify", G_CALLBACK (toplevel_notify_cb), @@ -306,80 +650,20 @@ g_object_get (parent, "attach-widget", &attach, NULL); if (attach == NULL) - return; - - if (g_hash_table_lookup (bridge->priv->items, parent) != NULL) - { - parent_item = g_hash_table_lookup (bridge->priv->items, parent); - } - else - { - if (g_hash_table_lookup (bridge->priv->items, attach) != NULL) - { - parent_item = g_hash_table_lookup (bridge->priv->items, attach); - } - else - { - // XXX insert the attach item? - } + { + g_signal_connect (G_OBJECT (parent), + "notify", + G_CALLBACK (attach_notify_cb), + bridge); + return; } } - if (GTK_IS_MENU_ITEM (child)) + if (GTK_IS_WINDOW (toplevel)) { - item = dbusmenu_menuitem_new (); - g_hash_table_insert (bridge->priv->items, child, item); - - if (GTK_IS_SEPARATOR_MENU_ITEM (child)) - { - dbusmenu_menuitem_property_set (item, - "type", - "separator"); - } - else - { - if (GTK_IS_CHECK_MENU_ITEM (child)) - { - dbusmenu_menuitem_property_set (item, - DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, - GTK_IS_RADIO_MENU_ITEM (child) ? DBUSMENU_MENUITEM_TOGGLE_RADIO : DBUSMENU_MENUITEM_TOGGLE_CHECK); - - dbusmenu_menuitem_property_set_int (item, - DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, - gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (child)) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); - - g_signal_connect (child, - "toggled", - G_CALLBACK (checkbox_toggled), - item); - } - - dbusmenu_menuitem_property_set (item, - "label", - get_menu_label_text (child)); - - dbusmenu_menuitem_property_set_bool (item, - DBUSMENU_MENUITEM_PROP_ENABLED, - gtk_widget_get_sensitive (child)); - - g_signal_connect (G_OBJECT (child), - "notify", - G_CALLBACK (widget_notify_cb), - item); - - g_signal_connect (G_OBJECT (item), - "item_activated", - G_CALLBACK (item_activated), - child); - - if (parent_item) - { - if (append) - dbusmenu_menuitem_child_append (parent_item, item); - else - dbusmenu_menuitem_child_prepend (parent_item, item); - } - } + g_signal_connect (toplevel, "realize", + G_CALLBACK (toplevel_realized), + bridge); } } @@ -398,20 +682,6 @@ if (!registered) { - DBusGConnection *connection; - - connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); - - g_return_if_fail (connection != NULL); - - dbusproxy = dbus_g_proxy_new_for_name_owner (connection, - APP_MENU_DBUS_NAME, - APP_MENU_DBUS_OBJECT, - APP_MENU_INTERFACE, - NULL); - - g_return_if_fail (dbusproxy != NULL); - app_menu_bridge_register_type (G_TYPE_MODULE (module)); registered = TRUE;
_______________________________________________ Mailing list: https://launchpad.net/~ayatana-commits Post to : ayatana-commits@lists.launchpad.net Unsubscribe : https://launchpad.net/~ayatana-commits More help : https://help.launchpad.net/ListHelp