ayatana-commits team mailing list archive
-
ayatana-commits team
-
Mailing list archive
-
Message #01598
[Merge] lp:~bratsche/appmenu-gtk/almost-rewrite into lp:appmenu-gtk
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;
Follow ups