/*
* Copyright 2010 Canonical, Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 3, as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see .
*
* Authors:
* Cody Russell
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "bridge.h"
#include "application-menu-registrar-client.h"
#define APP_MENU_DBUS_NAME "org.ayatana.WindowMenu.Registrar"
#define APP_MENU_DBUS_OBJECT "/org/ayatana/WindowMenu/Registrar"
#define APP_MENU_INTERFACE "org.ayatana.WindowMenu.Registrar"
#define APP_MENU_PATH "/this/is/a/long/object/path"
static void app_menu_bridge_insert (UbuntuMenuProxy *proxy,
GtkWidget *shell,
GtkWidget *child,
guint position);
static gboolean app_menu_bridge_show_local (UbuntuMenuProxy *proxy);
struct _AppMenuBridgePrivate
{
GHashTable *items; /* */
DbusmenuServer *server;
};
static DBusGProxy *dbusproxy = NULL;
static gboolean registered = FALSE;
G_DEFINE_DYNAMIC_TYPE(AppMenuBridge, app_menu_bridge, UBUNTU_TYPE_MENU_PROXY)
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);
}
static void
app_menu_bridge_finalize (GObject *object)
{
g_hash_table_destroy (APP_MENU_BRIDGE (object)->priv->items);
}
static void
app_menu_bridge_class_init (AppMenuBridgeClass *class)
{
UbuntuMenuProxyClass *proxy_class;
GObjectClass *object_class;
app_menu_bridge_parent_class = g_type_class_peek_parent (class);
proxy_class = UBUNTU_MENU_PROXY_CLASS (class);
object_class = G_OBJECT_CLASS (class);
proxy_class->insert = app_menu_bridge_insert;
proxy_class->show_local = app_menu_bridge_show_local;
object_class->finalize = app_menu_bridge_finalize;
g_type_class_add_private (class, sizeof (AppMenuBridgePrivate));
}
static void
app_menu_bridge_class_finalize (AppMenuBridgeClass *class)
{
}
static GtkWidget *
find_menu_label (GtkWidget *widget)
{
GtkWidget *label = NULL;
if (GTK_IS_LABEL (widget))
return widget;
if (GTK_IS_CONTAINER (widget))
{
GList *children;
GList *l;
children = gtk_container_get_children (GTK_CONTAINER (widget));
for (l = children; l; l = l->next)
{
label = find_menu_label (l->data);
if (label)
break;
}
g_list_free (children);
}
return label;
}
static const gchar *
get_menu_label_text (GtkWidget *menuitem)
{
GtkWidget *label = find_menu_label (menuitem);
if (label)
return gtk_label_get_text (GTK_LABEL (label));
return NULL;
}
static void
item_activated (DbusmenuMenuitem *item, guint timestamp, gpointer user_data)
{
GtkWidget *child;
if (user_data != NULL)
{
child = (GtkWidget *)user_data;
if (GTK_IS_MENU_ITEM (child))
{
gtk_menu_item_activate (GTK_MENU_ITEM (child));
}
}
}
static void
widget_notify_cb (GtkWidget *widget,
GParamSpec *pspec,
gpointer data)
{
DbusmenuMenuitem *child = (DbusmenuMenuitem *)data;
if (pspec->name == g_intern_static_string ("sensitive"))
{
dbusmenu_menuitem_property_set_bool (child,
DBUSMENU_MENUITEM_PROP_ENABLED,
gtk_widget_get_sensitive (widget));
}
else if (pspec->name == g_intern_static_string ("label"))
{
dbusmenu_menuitem_property_set (child,
DBUSMENU_MENUITEM_PROP_LABEL,
gtk_menu_item_get_label (GTK_MENU_ITEM (widget)));
}
else if (pspec->name == g_intern_static_string ("visible"))
{
dbusmenu_menuitem_property_set_bool (child,
DBUSMENU_MENUITEM_PROP_VISIBLE,
gtk_widget_get_visible (widget));
}
}
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;
}
static void
toplevel_notify_cb (GtkWidget *widget,
GParamSpec *pspec,
UbuntuMenuProxy *proxy)
{
if (pspec->name == g_intern_static_string ("parent"))
{
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);
}
}
}
}
}
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 void
app_menu_bridge_insert (UbuntuMenuProxy *proxy,
GtkWidget *parent,
GtkWidget *child,
guint position)
{
AppMenuBridge *bridge;
AppMenuBridgePrivate *priv;
DbusmenuMenuitem *item;
DbusmenuMenuitem *parent_item = NULL;
gboolean append = FALSE;
if (GTK_IS_TEAROFF_MENU_ITEM (child))
return;
bridge = APP_MENU_BRIDGE (proxy);
priv = bridge->priv;
if (g_hash_table_lookup (bridge->priv->items, child) != NULL)
return;
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),
proxy);
append = TRUE;
}
else if (GTK_IS_MENU (parent))
{
GtkWidget *attach;
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?
}
}
}
if (GTK_IS_MENU_ITEM (child))
{
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);
}
}
}
}
static gboolean
app_menu_bridge_show_local (UbuntuMenuProxy *proxy)
{
const gchar *env = g_getenv ("APPMENU_DISPLAY_BOTH");
return (g_strcmp0 (env, "1") == 0);
}
G_MODULE_EXPORT void
menu_proxy_module_load (UbuntuMenuProxyModule *module)
{
static gboolean registered = FALSE;
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;
}
}
G_MODULE_EXPORT void
menu_proxy_module_unload (UbuntuMenuProxyModule *module)
{
}