summaryrefslogtreecommitdiff
path: root/services
diff options
authorDidier Roche <didier.roche@canonical.com>2012-03-23 13:26:38 +0100
committerDidier Roche <didier.roche@canonical.com>2012-03-23 13:26:38 +0100
commit8648d018a08f7a0fe2591f6584b485eb424b1403 (patch)
tree19cdac6518fc32ea86456076a34420a63d5e402a /services
parent35f21e1425c5c1093bae256307f2f560fec934f3 (diff)
parent5b6b06bcfa6ecf2d37d7eeefcad2c2a8ac945cbd (diff)
New upstream release.
(bzr r55.3.616)
Diffstat (limited to 'services')
-rw-r--r--services/CMakeLists.txt2
-rw-r--r--services/panel-indicator-entry-accessible.c8
-rw-r--r--services/panel-main.c37
-rw-r--r--services/panel-marshal.list1
-rw-r--r--services/panel-root-accessible.c10
-rw-r--r--services/panel-service.c562
-rw-r--r--services/panel-service.h11
7 files changed, 455 insertions, 176 deletions
diff --git a/services/CMakeLists.txt b/services/CMakeLists.txt
index e38cade90..38fb6eb4e 100644
--- a/services/CMakeLists.txt
+++ b/services/CMakeLists.txt
@@ -2,7 +2,7 @@
# Panel Service
#
find_package(PkgConfig)
-pkg_check_modules(SERVICE_DEPS REQUIRED gtk+-3.0>=3.3 gobject-2.0 gio-2.0 gthread-2.0 indicator3-0.4 x11 gconf-2.0)
+pkg_check_modules(SERVICE_DEPS REQUIRED gtk+-3.0>=3.3 gobject-2.0 gio-2.0 gthread-2.0 indicator3-0.4>=0.4.90 x11 gconf-2.0)
execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} indicator3-0.4 --variable indicatordir OUTPUT_VARIABLE _indicatordir OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} indicator3-0.4 --variable iconsdir OUTPUT_VARIABLE _iconsdir OUTPUT_STRIP_TRAILING_WHITESPACE)
diff --git a/services/panel-indicator-entry-accessible.c b/services/panel-indicator-entry-accessible.c
index 2b490f72c..58d890a03 100644
--- a/services/panel-indicator-entry-accessible.c
+++ b/services/panel-indicator-entry-accessible.c
@@ -46,7 +46,8 @@ G_DEFINE_TYPE_WITH_CODE(PanelIndicatorEntryAccessible,
#define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PANEL_TYPE_INDICATOR_ENTRY_ACCESSIBLE, PanelIndicatorEntryAccessiblePrivate))
static void
-on_entry_activated_cb (PanelService *service, const gchar *entry_id, gpointer user_data)
+on_entry_activated_cb (PanelService *service, const gchar *entry_id,
+ gint x, gint y, guint w, guint h, gpointer user_data)
{
gchar *s;
gboolean adding = FALSE;
@@ -251,7 +252,8 @@ panel_indicator_entry_accessible_get_n_children (AtkObject *accessible)
g_return_val_if_fail (PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE (accessible), 0);
piea = PANEL_INDICATOR_ENTRY_ACCESSIBLE (accessible);
- if (GTK_IS_MENU (piea->priv->entry->menu))
+
+ if (piea->priv->entry->parent_object && GTK_IS_MENU (piea->priv->entry->menu))
n_children = 1;
return n_children;
@@ -266,7 +268,7 @@ panel_indicator_entry_accessible_ref_child (AtkObject *accessible, gint i)
g_return_val_if_fail (PANEL_IS_INDICATOR_ENTRY_ACCESSIBLE (accessible), NULL);
piea = PANEL_INDICATOR_ENTRY_ACCESSIBLE (accessible);
- if (GTK_IS_MENU (piea->priv->entry->menu))
+ if (piea->priv->entry->parent_object && GTK_IS_MENU (piea->priv->entry->menu))
{
child = gtk_widget_get_accessible (GTK_WIDGET (piea->priv->entry->menu));
atk_object_set_parent (child, accessible);
diff --git a/services/panel-main.c b/services/panel-main.c
index 317cf6c24..e48f2b993 100644
--- a/services/panel-main.c
+++ b/services/panel-main.c
@@ -58,13 +58,21 @@ static const gchar introspection_xml[] =
" <method name='SyncGeometries'>"
" <arg type='a(ssiiii)' name='geometries' direction='in'/>"
" </method>"
- ""
+ ""
" <method name='ShowEntry'>"
" <arg type='s' name='entry_id' direction='in'/>"
+ " <arg type='u' name='xid' direction='in'/>"
+ " <arg type='i' name='x' direction='in'/>"
+ " <arg type='i' name='y' direction='in'/>"
+ " <arg type='u' name='button' direction='in'/>"
" <arg type='u' name='timestamp' direction='in'/>"
+ " </method>"
+ ""
+ " <method name='ShowAppMenu'>"
+ " <arg type='u' name='xid' direction='in'/>"
" <arg type='i' name='x' direction='in'/>"
" <arg type='i' name='y' direction='in'/>"
- " <arg type='i' name='button' direction='in'/>"
+ " <arg type='u' name='timestamp' direction='in'/>"
" </method>"
""
" <method name='SecondaryActivateEntry'>"
@@ -79,6 +87,7 @@ static const gchar introspection_xml[] =
""
" <signal name='EntryActivated'>"
" <arg type='s' name='entry_id' />"
+ " <arg type='(iiuu)' name='entry_geometry' />"
" </signal>"
""
" <signal name='ReSync'>"
@@ -174,18 +183,31 @@ handle_method_call (GDBusConnection *connection,
}
else if (g_strcmp0 (method_name, "ShowEntry") == 0)
{
+ guint32 xid;
gchar *entry_id;
- guint32 timestamp;
gint32 x;
gint32 y;
- gint32 button;
- g_variant_get (parameters, "(suiii)", &entry_id, &timestamp, &x, &y, &button, NULL);
+ guint32 button;
+ guint32 timestamp;
+ g_variant_get (parameters, "(suiiuu)", &entry_id, &xid, &x, &y, &button, &timestamp, NULL);
- panel_service_show_entry (service, entry_id, timestamp, x, y, button);
+ panel_service_show_entry (service, entry_id, xid, x, y, button, timestamp);
g_dbus_method_invocation_return_value (invocation, NULL);
g_free (entry_id);
}
+ else if (g_strcmp0 (method_name, "ShowAppMenu") == 0)
+ {
+ guint32 xid;
+ gint32 x;
+ gint32 y;
+ guint32 timestamp;
+ g_variant_get (parameters, "(uiiu)", &xid, &x, &y, &timestamp, NULL);
+
+ panel_service_show_app_menu (service, xid, x, y, timestamp);
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ }
else if (g_strcmp0 (method_name, "SecondaryActivateEntry") == 0)
{
gchar *entry_id;
@@ -229,6 +251,7 @@ on_service_resync (PanelService *service, const gchar *indicator_id, GDBusConnec
static void
on_service_entry_activated (PanelService *service,
const gchar *entry_id,
+ gint x, gint y, guint w, guint h,
GDBusConnection *connection)
{
GError *error = NULL;
@@ -237,7 +260,7 @@ on_service_entry_activated (PanelService *service,
S_PATH,
S_IFACE,
"EntryActivated",
- g_variant_new ("(s)", entry_id),
+ g_variant_new ("(s(iiuu))", entry_id, x, y, w, h),
&error);
if (error)
diff --git a/services/panel-marshal.list b/services/panel-marshal.list
index 701b5c38b..fcaae690f 100644
--- a/services/panel-marshal.list
+++ b/services/panel-marshal.list
@@ -1,2 +1,3 @@
NONE:OBJECT,POINTER,INT,INT,INT,INT
VOID:STRING,BOOLEAN
+VOID:STRING,INT,INT,UINT,UINT
diff --git a/services/panel-root-accessible.c b/services/panel-root-accessible.c
index d35f4c7dc..6881941d1 100644
--- a/services/panel-root-accessible.c
+++ b/services/panel-root-accessible.c
@@ -134,16 +134,16 @@ panel_root_accessible_initialize (AtkObject *accessible, gpointer data)
if (INDICATOR_IS_OBJECT (indicator))
{
AtkObject *child;
- gpointer *data;
+ gpointer *weak_data;
child = panel_indicator_accessible_new (indicator);
/* FIXME: use proper signals once we support dynamic adding/removing
* of indicators */
- data = g_new0 (gpointer, 2);
- data[0] = root;
- data[1] = child;
+ weak_data = g_new0 (gpointer, 2);
+ weak_data[0] = root;
+ weak_data[1] = child;
g_object_weak_ref (G_OBJECT (indicator),
- (GWeakNotify) on_indicator_removed, data);
+ (GWeakNotify) on_indicator_removed, weak_data);
atk_object_set_parent (child, accessible);
root->priv->a11y_children = g_slist_append (root->priv->a11y_children, child);
diff --git a/services/panel-service.c b/services/panel-service.c
index 35225d97a..c39709959 100644
--- a/services/panel-service.c
+++ b/services/panel-service.c
@@ -1,6 +1,6 @@
// -*- Mode: C; indent-tabs-mode: nil; tab-width: 2 -*-
/*
- * Copyright (C) 2010 Canonical Ltd
+ * Copyright (C) 2010-2012 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
@@ -27,8 +27,10 @@
#include "panel-service.h"
#include <stdlib.h>
+#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
+#include <gconf/gconf-client.h>
#include <X11/extensions/XInput2.h>
#include <X11/XKBlib.h>
@@ -44,12 +46,15 @@ G_DEFINE_TYPE (PanelService, panel_service, G_TYPE_OBJECT);
#define N_TIMEOUT_SLOTS 50
#define MAX_INDICATOR_ENTRIES 50
+#define COMPIZ_OPTIONS_PATH "/apps/compiz-1/plugins/unityshell/screen0/options"
+#define MENU_TOGGLE_KEYBINDING_PATH COMPIZ_OPTIONS_PATH"/panel_first_menu"
+
static PanelService *static_service = NULL;
struct _PanelServicePrivate
{
GSList *indicators;
- GHashTable *entry2indicator_hash;
+ GHashTable *id2entry_hash;
GHashTable *panel2entries_hash;
guint initial_sync_id;
@@ -69,6 +74,10 @@ struct _PanelServicePrivate
gint last_bottom;
guint32 last_menu_button;
+ KeyCode toggle_key;
+ guint32 toggle_modifiers;
+ guint32 key_monitor_id;
+
IndicatorObjectEntry *pressed_entry;
gboolean use_event;
};
@@ -121,7 +130,6 @@ static void load_indicators (PanelService *self);
static void sort_indicators (PanelService *self);
static void notify_object (IndicatorObject *object);
-static IndicatorObjectEntry *get_entry_at (PanelService *self, gint x, gint y);
static GdkFilterReturn event_filter (GdkXEvent *ev,
GdkEvent *gev,
@@ -137,24 +145,27 @@ panel_service_class_dispose (GObject *object)
PanelServicePrivate *priv = PANEL_SERVICE (object)->priv;
gint i;
- g_hash_table_destroy (priv->entry2indicator_hash);
+ g_hash_table_destroy (priv->id2entry_hash);
g_hash_table_destroy (priv->panel2entries_hash);
gdk_window_remove_filter (NULL, (GdkFilterFunc)event_filter, object);
- if (G_IS_OBJECT (priv->menubar))
+ if (GTK_IS_WIDGET (priv->menubar) &&
+ gtk_widget_get_realized (GTK_WIDGET (priv->menubar)))
{
g_object_unref (priv->menubar);
priv->menubar = NULL;
}
- if (G_IS_OBJECT (priv->offscreen_window))
+ if (GTK_IS_WIDGET (priv->offscreen_window) &&
+ gtk_widget_get_realized (GTK_WIDGET (priv->offscreen_window)))
{
g_object_unref (priv->offscreen_window);
priv->offscreen_window = NULL;
}
- if (G_IS_OBJECT (priv->last_menu))
+ if (GTK_IS_WIDGET (priv->last_menu) &&
+ gtk_widget_get_realized (GTK_WIDGET (priv->last_menu)))
{
g_object_unref (priv->last_menu);
priv->last_menu = NULL;
@@ -175,15 +186,28 @@ panel_service_class_dispose (GObject *object)
}
}
+ if (priv->key_monitor_id)
+ {
+ gconf_client_notify_remove (gconf_client_get_default(), priv->key_monitor_id);
+ priv->key_monitor_id = 0;
+ }
+
G_OBJECT_CLASS (panel_service_parent_class)->dispose (object);
}
static void
+panel_service_class_finalize (GObject *object)
+{
+ static_service = NULL;
+}
+
+static void
panel_service_class_init (PanelServiceClass *klass)
{
GObjectClass *obj_class = G_OBJECT_CLASS (klass);
obj_class->dispose = panel_service_class_dispose;
+ obj_class->finalize = panel_service_class_finalize;
/* Signals */
_service_signals[ENTRY_ACTIVATED] =
@@ -192,8 +216,9 @@ panel_service_class_init (PanelServiceClass *klass)
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1, G_TYPE_STRING);
+ panel_marshal_VOID__STRING_INT_INT_UINT_UINT,
+ G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT,
+ G_TYPE_UINT, G_TYPE_UINT);
_service_signals[RE_SYNC] =
g_signal_new ("re-sync",
@@ -212,6 +237,7 @@ panel_service_class_init (PanelServiceClass *klass)
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING);
+
_service_signals[GEOMETRIES_CHANGED] =
g_signal_new ("geometries-changed",
G_OBJECT_CLASS_TYPE (obj_class),
@@ -245,6 +271,82 @@ panel_service_class_init (PanelServiceClass *klass)
g_type_class_add_private (obj_class, sizeof (PanelServicePrivate));
}
+static IndicatorObjectEntry *
+get_entry_at (PanelService *self, gint x, gint y)
+{
+ GHashTableIter panel_iter, entries_iter;
+ gpointer key, value, k, v;
+
+ g_hash_table_iter_init (&panel_iter, self->priv->panel2entries_hash);
+ while (g_hash_table_iter_next (&panel_iter, &key, &value))
+ {
+ GHashTable *entry2geometry_hash = value;
+ g_hash_table_iter_init (&entries_iter, entry2geometry_hash);
+
+ while (g_hash_table_iter_next (&entries_iter, &k, &v))
+ {
+ IndicatorObjectEntry *entry = k;
+ GdkRectangle *geo = v;
+
+ if (x >= geo->x && x <= (geo->x + geo->width) &&
+ y >= geo->y && y <= (geo->y + geo->height))
+ {
+ return entry;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static IndicatorObject *
+get_entry_parent_indicator (IndicatorObjectEntry *entry)
+{
+ if (!entry || !INDICATOR_IS_OBJECT (entry->parent_object))
+ return NULL;
+
+ return INDICATOR_OBJECT (entry->parent_object);
+}
+
+static gchar *
+get_indicator_entry_id_by_entry (IndicatorObjectEntry *entry)
+{
+ gchar *entry_id = NULL;
+
+ if (entry)
+ entry_id = g_strdup_printf ("%p", entry);
+
+ return entry_id;
+}
+
+static IndicatorObjectEntry *
+get_indicator_entry_by_id (PanelService *self, const gchar *entry_id)
+{
+ IndicatorObjectEntry *entry;
+
+ entry = g_hash_table_lookup (self->priv->id2entry_hash, entry_id);
+
+ if (entry)
+ {
+ if (g_slist_find (self->priv->indicators, entry->parent_object))
+ {
+ if (!INDICATOR_IS_OBJECT (entry->parent_object))
+ entry = NULL;
+ }
+ else
+ {
+ entry = NULL;
+ }
+
+ if (!entry)
+ {
+ g_warning("The entry id '%s' you're trying to lookup is not a valid IndicatorObjectEntry!", entry_id);
+ }
+ }
+
+ return entry;
+}
+
static GdkFilterReturn
event_filter (GdkXEvent *ev, GdkEvent *gev, PanelService *self)
{
@@ -269,9 +371,9 @@ event_filter (GdkXEvent *ev, GdkEvent *gev, PanelService *self)
if (!event)
return ret;
- if (event->evtype == XI_KeyRelease)
+ if (event->evtype == XI_KeyPress)
{
- if (XkbKeycodeToKeysym(event->display, event->detail, 0, 0) == GDK_KEY_F10)
+ if (event->mods.base == priv->toggle_modifiers && event->detail == priv->toggle_key)
{
if (GTK_IS_MENU (priv->last_menu))
gtk_menu_popdown (GTK_MENU (priv->last_menu));
@@ -288,21 +390,21 @@ event_filter (GdkXEvent *ev, GdkEvent *gev, PanelService *self)
}
if (event->evtype == XI_ButtonRelease)
- {
+ {
IndicatorObjectEntry *entry;
gboolean event_is_a_click = FALSE;
entry = get_entry_at (self, event->root_x, event->root_y);
- if (XIMaskIsSet (event->buttons.mask, 1) || XIMaskIsSet (event->buttons.mask, 3))
+ if (event->detail == 1 || event->detail == 3)
{
/* Consider only right and left clicks over the indicators entries */
event_is_a_click = TRUE;
}
- else if (XIMaskIsSet (event->buttons.mask, 2) && entry)
+ else if (entry && event->detail == 2)
{
/* Middle clicks over an appmenu entry are considered just like
* all other clicks */
- IndicatorObject *obj = g_hash_table_lookup (priv->entry2indicator_hash, entry);
+ IndicatorObject *obj = get_entry_parent_indicator (entry);
if (g_strcmp0 (g_object_get_data (G_OBJECT (obj), "id"), "libappmenu.so") == 0)
{
@@ -330,25 +432,23 @@ event_filter (GdkXEvent *ev, GdkEvent *gev, PanelService *self)
/* If we were navigating over indicators using the keyboard
* and now we click over the indicator under the mouse, we
* must force it to show back again, not make it close */
- gchar *entry_id = g_strdup_printf ("%p", entry);
+ gchar *entry_id = get_indicator_entry_id_by_entry (entry);
g_signal_emit (self, _service_signals[ENTRY_ACTIVATE_REQUEST], 0, entry_id);
g_free (entry_id);
}
}
}
}
- else if ((XIMaskIsSet (event->buttons.mask, 2) ||
- XIMaskIsSet (event->buttons.mask, 4) ||
- XIMaskIsSet (event->buttons.mask, 5)) && entry)
+ else if (entry && (event->detail == 2 || event->detail == 4 || event->detail == 5))
{
/* If we're scrolling or middle-clicking over an indicator
* (which is not an appmenu entry) then we need to send the
* event to the indicator itself, and avoid it to close */
- gchar *entry_id = g_strdup_printf ("%p", entry);
+ gchar *entry_id = get_indicator_entry_id_by_entry (entry);
- if (XIMaskIsSet (event->buttons.mask, 4) || XIMaskIsSet (event->buttons.mask, 5))
+ if (event->detail == 4 || event->detail == 5)
{
- gint32 delta = XIMaskIsSet (event->buttons.mask, 4) ? 120 : -120;
+ gint32 delta = (event->detail == 4) ? 120 : -120;
panel_service_scroll_entry (self, entry_id, delta);
}
else if (entry == priv->pressed_entry)
@@ -365,69 +465,83 @@ event_filter (GdkXEvent *ev, GdkEvent *gev, PanelService *self)
return ret;
}
-static IndicatorObjectEntry *
-get_entry_at (PanelService *self, gint x, gint y)
+static gboolean
+initial_resync (PanelService *self)
{
- GHashTableIter panel_iter, entries_iter;
- gpointer key, value, k, v;
-
- g_hash_table_iter_init (&panel_iter, self->priv->panel2entries_hash);
- while (g_hash_table_iter_next (&panel_iter, &key, &value))
+ if (PANEL_IS_SERVICE (self))
{
- GHashTable *entry2geometry_hash = value;
- g_hash_table_iter_init (&entries_iter, entry2geometry_hash);
-
- while (g_hash_table_iter_next (&entries_iter, &k, &v))
- {
- IndicatorObjectEntry *entry = k;
- GdkRectangle *geo = v;
-
- if (x >= geo->x && x <= (geo->x + geo->width) &&
- y >= geo->y && y <= (geo->y + geo->height))
- {
- return entry;
- }
- }
+ g_signal_emit (self, _service_signals[RE_SYNC], 0, "");
+ self->priv->initial_sync_id = 0;
}
-
- return NULL;
+ return FALSE;
}
static void
-panel_service_get_indicator_entry_by_id (PanelService *self,
- const gchar *entry_id,
- IndicatorObjectEntry **entry,
- IndicatorObject **object)
+panel_service_update_menu_keybinding (PanelService *self)
{
- IndicatorObject *indicator;
- IndicatorObjectEntry *probably_entry;
- PanelServicePrivate *priv = self->priv;
+ GConfClient *client = gconf_client_get_default ();
+ gchar *binding = gconf_client_get_string (client, MENU_TOGGLE_KEYBINDING_PATH, NULL);
- /* FIXME: eeek, why do we even do this? */
- if (sscanf (entry_id, "%p", &probably_entry) == 1)
- {
- /* check that there really is such IndicatorObjectEntry */
- indicator = g_hash_table_lookup (priv->entry2indicator_hash,
- probably_entry);
- if (object) *object = indicator;
- if (entry) *entry = indicator != NULL ? probably_entry : NULL;
- }
- else
+ KeyCode keycode = 0;
+ KeySym keysym = NoSymbol;
+ guint32 modifiers = 0;
+
+ gchar *keystart = (binding) ? strrchr (binding, '>') : NULL;
+
+ if (!keystart)
+ keystart = binding;
+
+ while (keystart && !g_ascii_isalnum (*keystart))
+ keystart++;
+
+ gchar *keyend = keystart;
+
+ while (keyend && g_ascii_isalnum (*keyend))
+ keyend++;
+
+ if (keystart != keyend)
{
- if (object) *object = NULL;
- if (entry) *entry = NULL;
+ gchar *keystr = g_strndup (keystart, keyend-keystart);
+ keysym = XStringToKeysym (keystr);
+ g_free (keystr);
}
-}
-static gboolean
-initial_resync (PanelService *self)
-{
- if (PANEL_IS_SERVICE (self))
+ if (keysym != NoSymbol)
{
- g_signal_emit (self, _service_signals[RE_SYNC], 0, "");
- self->priv->initial_sync_id = 0;
+ Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+ keycode = XKeysymToKeycode(dpy, keysym);
+
+ if (g_strrstr (binding, "<Shift>"))
+ {
+ modifiers |= GDK_SHIFT_MASK;
+ }
+ if (g_strrstr (binding, "<Control>"))
+ {
+ modifiers |= GDK_CONTROL_MASK;
+ }
+ if (g_strrstr (binding, "<Alt>") || g_strrstr (binding, "<Mod1>"))
+ {
+ modifiers |= GDK_MOD1_MASK;
+ }
+ if (g_strrstr (binding, "<Super>"))
+ {
+ modifiers |= GDK_SUPER_MASK;
+ }
}
- return FALSE;
+
+ self->priv->toggle_key = keycode;
+ self->priv->toggle_modifiers = modifiers;
+
+ g_free (binding);
+}
+
+void
+on_keybinding_changed (GConfClient* client, guint id, GConfEntry* entry, gpointer data)
+{
+ PanelService *self = data;
+ g_return_if_fail (PANEL_IS_SERVICE (data));
+
+ panel_service_update_menu_keybinding (self);
}
static void
@@ -442,7 +556,7 @@ panel_service_init (PanelService *self)
gdk_window_add_filter (NULL, (GdkFilterFunc)event_filter, self);
- priv->entry2indicator_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
+ priv->id2entry_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
priv->panel2entries_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free,
(GDestroyNotify) g_hash_table_destroy);
@@ -452,6 +566,14 @@ panel_service_init (PanelService *self)
sort_indicators (self);
suppress_signals = FALSE;
+ panel_service_update_menu_keybinding (self);
+
+ GConfClient *client = gconf_client_get_default ();
+ gconf_client_add_dir (client, COMPIZ_OPTIONS_PATH, GCONF_CLIENT_PRELOAD_NONE, NULL);
+ priv->key_monitor_id = gconf_client_notify_add (client, MENU_TOGGLE_KEYBINDING_PATH,
+ on_keybinding_changed, self,
+ NULL, NULL);
+
priv->initial_sync_id = g_idle_add ((GSourceFunc)initial_resync, self);
}
@@ -481,17 +603,23 @@ panel_service_actually_remove_indicator (PanelService *self, IndicatorObject *in
{
for (l = entries; l; l = l->next)
{
- g_hash_table_remove (self->priv->entry2indicator_hash, l->data);
-
+ IndicatorObjectEntry *entry;
+ gchar *entry_id;
GHashTableIter iter;
gpointer key, value;
+
+ entry = l->data;
+ entry_id = get_indicator_entry_id_by_entry (entry);
+ g_hash_table_remove (self->priv->id2entry_hash, entry_id);
+ g_free (entry_id);
+
g_hash_table_iter_init (&iter, self->priv->panel2entries_hash);
while (g_hash_table_iter_next (&iter, &key, &value))
{
GHashTable *entry2geometry_hash = value;
if (g_hash_table_size (entry2geometry_hash) > 1)
- g_hash_table_remove (entry2geometry_hash, l->data);
+ g_hash_table_remove (entry2geometry_hash, entry);
else
g_hash_table_iter_remove (&iter);
}
@@ -586,7 +714,7 @@ panel_service_remove_indicator (PanelService *self, IndicatorObject *indicator)
gpointer timeout = g_object_get_data (G_OBJECT (indicator), "remove-timeout");
if (timeout)
- g_source_remove (GPOINTER_TO_UINT (timeout));
+ g_source_remove (GPOINTER_TO_UINT (timeout));
g_object_set_data (G_OBJECT (indicator), "remove", GINT_TO_POINTER (TRUE));
notify_object (indicator);
@@ -695,13 +823,11 @@ on_entry_added (IndicatorObject *object,
IndicatorObjectEntry *entry,
PanelService *self)
{
- PanelServicePrivate *priv;
-
g_return_if_fail (PANEL_IS_SERVICE (self));
g_return_if_fail (entry != NULL);
- priv = self->priv;
- g_hash_table_insert (priv->entry2indicator_hash, entry, object);
+ gchar *entry_id = get_indicator_entry_id_by_entry (entry);
+ g_hash_table_insert (self->priv->id2entry_hash, entry_id, entry);
if (GTK_IS_LABEL (entry->label))
{
@@ -748,18 +874,18 @@ on_entry_removed (IndicatorObject *object,
IndicatorObjectEntry *entry,
PanelService *self)
{
- PanelServicePrivate *priv;
g_return_if_fail (PANEL_IS_SERVICE (self));
g_return_if_fail (entry != NULL);
- priv = self->priv;
-
- g_hash_table_remove (priv->entry2indicator_hash, entry);
- /* Don't remove here the value from priv->panel2entries_hash, this should
- * be done in during the sync, to avoid false positive.
+ /* Don't remove here the value from panel2entries_hash, this should be
+ * done during the geometries sync, to avoid false positive.
* FIXME this in libappmenu.so to avoid to send an "entry-removed" signal
* when switching the focus from a window to one of its dialog children */
+ gchar *entry_id = get_indicator_entry_id_by_entry (entry);
+ g_hash_table_remove (self->priv->id2entry_hash, entry_id);
+ g_free (entry_id);
+
notify_object (object);
}
@@ -786,10 +912,8 @@ on_indicator_menu_show (IndicatorObject *object,
return;
}
- entry_id = g_strdup_printf ("%p", entry);
-
+ entry_id = get_indicator_entry_id_by_entry (entry);
g_signal_emit (self, _service_signals[ENTRY_ACTIVATE_REQUEST], 0, entry_id);
-
g_free (entry_id);
}
@@ -808,10 +932,8 @@ on_indicator_menu_show_now_changed (IndicatorObject *object,
return;
}
- entry_id = g_strdup_printf ("%p", entry);
-
+ entry_id = get_indicator_entry_id_by_entry (entry);
g_signal_emit (self, _service_signals[ENTRY_SHOW_NOW_CHANGED], 0, entry_id, show_now_changed);
-
g_free (entry_id);
}
@@ -829,7 +951,7 @@ load_indicator (PanelService *self, IndicatorObject *object, const gchar *_name)
gchar *name;
GList *entries, *entry;
- indicator_object_set_environment(object, (const GStrv)indicator_environment);
+ indicator_object_set_environment(object, (GStrv)indicator_environment);
if (_name != NULL)
name = g_strdup (_name);
@@ -1079,7 +1201,7 @@ indicator_object_to_variant (IndicatorObject *object, const gchar *indicator_id,
{
gint prio = -1;
IndicatorObjectEntry *entry = e->data;
- gchar *id = g_strdup_printf ("%p", entry);
+ gchar *id = get_indicator_entry_id_by_entry (entry);
if (entry->name_hint)
{
@@ -1095,13 +1217,14 @@ indicator_object_to_variant (IndicatorObject *object, const gchar *indicator_id,
indicator_entry_to_variant (entry, id, indicator_id, b, prio);
g_free (id);
}
+
+ g_list_free (entries);
}
else
{
/* Add a null entry to indicate that there is an indicator here, it's just empty */
indicator_entry_null_to_variant (indicator_id, b);
}
- g_list_free (entries);
}
static void
@@ -1147,7 +1270,7 @@ on_active_menu_hidden (GtkMenu *menu, PanelService *self)
priv->use_event = FALSE;
priv->pressed_entry = NULL;
- g_signal_emit (self, _service_signals[ENTRY_ACTIVATED], 0, "");
+ g_signal_emit (self, _service_signals[ENTRY_ACTIVATED], 0, "", 0, 0, 0, 0);
}
/*
@@ -1226,15 +1349,29 @@ panel_service_sync_geometry (PanelService *self,
{
IndicatorObject *object;
IndicatorObjectEntry *entry;
+ gboolean valid_entry = TRUE;
PanelServicePrivate *priv = self->priv;
- panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
+ entry = get_indicator_entry_by_id (self, entry_id);
+
+ /* If the entry we read is not valid, maybe it has already been removed
+ * or unparented, so we need to make sure that the related key on the
+ * entry2geometry_hash is correctly removed and the value is free'd */
+ if (!entry)
+ {
+ IndicatorObjectEntry *invalid_entry;
+ if (sscanf (entry_id, "%p", &invalid_entry) == 1)
+ {
+ entry = invalid_entry;
+ valid_entry = FALSE;
+ }
+ }
if (entry)
{
GHashTable *entry2geometry_hash = g_hash_table_lookup (priv->panel2entries_hash, panel_id);
- if (width < 0 || height < 0)
+ if (width < 0 || height < 0 || !valid_entry)
{
if (entry2geometry_hash)
{
@@ -1247,63 +1384,110 @@ panel_service_sync_geometry (PanelService *self,
g_hash_table_remove (priv->panel2entries_hash, panel_id);
}
}
+
+ /* If the entry has been removed let's make sure that its menu is closed */
+ if (valid_entry && GTK_IS_MENU (priv->last_menu) && priv->last_menu == entry->menu)
+ {
+ gtk_menu_popdown (entry->menu);
+ }
}
else
{
GdkRectangle *geo = NULL;
if (entry2geometry_hash == NULL)
- {
- //FIXME - this leaks memory but i'm not 100% on the logic,
- // using g_free as the keys destructor function causes all
- // kinds of problems
- entry2geometry_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
- NULL, g_free);
- g_hash_table_insert (priv->panel2entries_hash, g_strdup (panel_id),
- entry2geometry_hash);
- }
+ {
+ entry2geometry_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, g_free);
+ g_hash_table_insert (priv->panel2entries_hash, g_strdup (panel_id),
+ entry2geometry_hash);
+ }
else
- {
- geo = g_hash_table_lookup (entry2geometry_hash, entry);
- }
+ {
+ geo = g_hash_table_lookup (entry2geometry_hash, entry);
+ }
if (geo == NULL)
{
- geo = g_new (GdkRectangle, 1);
+ geo = g_new0 (GdkRectangle, 1);
g_hash_table_insert (entry2geometry_hash, entry, geo);
}
+ /* If the current entry geometry has changed, we need to move the menu
+ * accordingly to the change we recorded! */
+ if (GTK_IS_MENU (priv->last_menu) && priv->last_menu == entry->menu)
+ {
+ GtkWidget *top_widget = gtk_widget_get_toplevel (GTK_WIDGET (priv->last_menu));
+
+ if (GTK_IS_WINDOW (top_widget))
+ {
+ GtkWindow *top_win = GTK_WINDOW (top_widget);
+ gint old_x, old_y;
+
+ gtk_window_get_position (top_win, &old_x, &old_y);
+ gtk_window_move (top_win, old_x - (geo->x - x), old_y - (geo->y - y));
+ }
+ }
+
geo->x = x;
geo->y = y;
geo->width = width;
geo->height = height;
}
- g_signal_emit (self, _service_signals[GEOMETRIES_CHANGED], 0, object, entry, x, y, width, height);
+ if (valid_entry)
+ {
+ object = get_entry_parent_indicator (entry);
+ g_signal_emit (self, _service_signals[GEOMETRIES_CHANGED], 0, object, entry, x, y, width, height);
+ }
}
}
static gboolean
-should_skip_menu (IndicatorObjectEntry *entry)
+panel_service_entry_is_visible (PanelService *self, IndicatorObjectEntry *entry)
{
- gboolean label_ok = FALSE;
- gboolean image_ok = FALSE;
+ GHashTableIter panel_iter;
+ gpointer key, value;
+ gboolean found_geo;
+
+ g_return_val_if_fail (PANEL_IS_SERVICE (self), FALSE);
+ g_return_val_if_fail (entry != NULL, FALSE);
+
+ found_geo = FALSE;
+ g_hash_table_iter_init (&panel_iter, self->priv->panel2entries_hash);
+
+ while (g_hash_table_iter_next (&panel_iter, &key, &value) && !found_geo)
+ {
+ GHashTable *entry2geometry_hash = value;
+
+ if (g_hash_table_lookup (entry2geometry_hash, entry))
+ {
+ found_geo = TRUE;
+ }
+ }
- g_return_val_if_fail (entry != NULL, TRUE);
+ if (!found_geo)
+ return FALSE;
if (GTK_IS_LABEL (entry->label))
{
- label_ok = gtk_widget_get_visible (GTK_WIDGET (entry->label))
- && gtk_widget_is_sensitive (GTK_WIDGET (entry->label));
+ if (gtk_widget_get_visible (GTK_WIDGET (entry->label)) &&
+ gtk_widget_is_sensitive (GTK_WIDGET (entry->label)))
+ {
+ return TRUE;
+ }
}
if (GTK_IS_IMAGE (entry->image))
{
- image_ok = gtk_widget_get_visible (GTK_WIDGET (entry->image))
- && gtk_widget_is_sensitive (GTK_WIDGET (entry->image));
+ if (gtk_widget_get_visible (GTK_WIDGET (entry->image)) &&
+ gtk_widget_is_sensitive (GTK_WIDGET (entry->image)))
+ {
+ return TRUE;
+ }
}
- return !label_ok && !image_ok;
+ return TRUE;
}
static int
@@ -1341,7 +1525,7 @@ activate_next_prev_menu (PanelService *self,
gint prio = -1;
new_entry = ll->data;
- if (should_skip_menu (new_entry))
+ if (!panel_service_entry_is_visible (self, new_entry))
continue;
if (new_entry->name_hint)
@@ -1388,7 +1572,7 @@ activate_next_prev_menu (PanelService *self,
if (new_entry)
{
- id = g_strdup_printf ("%p", new_entry);
+ id = get_indicator_entry_id_by_entry (new_entry);
g_signal_emit (self, _service_signals[ENTRY_ACTIVATE_REQUEST], 0, id);
g_free (id);
}
@@ -1433,7 +1617,7 @@ on_active_menu_move_current (GtkMenu *menu,
}
/* Find the next/prev indicator */
- object = g_hash_table_lookup (priv->entry2indicator_hash, priv->last_entry);
+ object = get_entry_parent_indicator(priv->last_entry);
if (object == NULL)
{
g_warning ("Unable to find IndicatorObject for entry");
@@ -1450,23 +1634,25 @@ menu_deactivated (GtkWidget *menu)
gtk_widget_destroy (menu);
}
-void
-panel_service_show_entry (PanelService *self,
- const gchar *entry_id,
- guint32 timestamp,
- gint32 x,
- gint32 y,
- gint32 button)
+static void
+panel_service_show_entry_common (PanelService *self,
+ IndicatorObject *object,
+ IndicatorObjectEntry *entry,
+ guint32 xid,
+ gint32 x,
+ gint32 y,
+ guint32 button,
+ guint32 timestamp)
{
- IndicatorObject *object;
- IndicatorObjectEntry *entry;
- GtkWidget *last_menu;
- PanelServicePrivate *priv = self->priv;
-
- panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
+ PanelServicePrivate *priv;
+ GtkWidget *last_menu;
+ g_return_if_fail (PANEL_IS_SERVICE (self));
+ g_return_if_fail (INDICATOR_IS_OBJECT (object));
g_return_if_fail (entry);
+ priv = self->priv;
+
if (priv->last_entry == entry)
return;
@@ -1489,7 +1675,14 @@ panel_service_show_entry (PanelService *self,
if (entry != NULL)
{
- g_signal_emit (self, _service_signals[ENTRY_ACTIVATED], 0, entry_id);
+ if (xid > 0)
+ {
+ indicator_object_entry_activate_window (object, entry, xid, CurrentTime);
+ }
+ else
+ {
+ indicator_object_entry_activate (object, entry, CurrentTime);
+ }
if (GTK_IS_MENU (entry->menu))
{
@@ -1526,29 +1719,33 @@ panel_service_show_entry (PanelService *self,
priv->last_menu_move_id = g_signal_connect_after (priv->last_menu, "move-current",
G_CALLBACK (on_active_menu_move_current), self);
- indicator_object_entry_activate (object, entry, CurrentTime);
-
gtk_menu_popup (priv->last_menu, NULL, NULL, positon_menu, self, 0, CurrentTime);
GdkWindow *gdkwin = gtk_widget_get_window (GTK_WIDGET (priv->last_menu));
+
if (gdkwin != NULL)
- {
- gint left=0, top=0, width=0, height=0;
+ {
+ gint left=0, top=0, width=0, height=0;
- gdk_window_get_geometry (gdkwin, NULL, NULL, &width, &height);
- gdk_window_get_origin (gdkwin, &left, &top);
+ gdk_window_get_geometry (gdkwin, NULL, NULL, &width, &height);
+ gdk_window_get_origin (gdkwin, &left, &top);
- priv->last_left = left;
- priv->last_right = left + width -1;
- priv->last_top = top;
- priv->last_bottom = top + height -1;
- }
+ gchar *entry_id = get_indicator_entry_id_by_entry (entry);
+ g_signal_emit (self, _service_signals[ENTRY_ACTIVATED], 0, entry_id,
+ left, top, width, height);
+ g_free (entry_id);
+
+ priv->last_left = left;
+ priv->last_right = left + width -1;
+ priv->last_top = top;
+ priv->last_bottom = top + height -1;
+ }
else
- {
- priv->last_left = 0;
- priv->last_right = 0;
- priv->last_top = 0;
- priv->last_bottom = 0;
- }
+ {
+ priv->last_left = 0;
+ priv->last_right = 0;
+ priv->last_top = 0;
+ priv->last_bottom = 0;
+ }
}
/* We popdown the old one last so we don't accidently send key focus back to the
@@ -1560,6 +1757,53 @@ panel_service_show_entry (PanelService *self,
}
void
+panel_service_show_entry (PanelService *self,
+ const gchar *entry_id,
+ guint32 xid,
+ gint32 x,
+ gint32 y,
+ guint32 button,
+ guint32 timestamp)
+{
+ IndicatorObject *object;
+ IndicatorObjectEntry *entry;
+
+ g_return_if_fail (PANEL_IS_SERVICE (self));
+
+ entry = get_indicator_entry_by_id (self, entry_id);
+ object = get_entry_parent_indicator (entry);
+
+ panel_service_show_entry_common (self, object, entry, xid, x, y, button, timestamp);
+}
+
+void
+panel_service_show_app_menu (PanelService *self,
+ guint32 xid,
+ gint32 x,
+ gint32 y,
+ guint32 timestamp)
+{
+ IndicatorObject *object;
+ IndicatorObjectEntry *entry;
+ GList *entries;
+
+ g_return_if_fail (PANEL_IS_SERVICE (self));
+
+ object = panel_service_get_indicator (self, "libappmenu.so");
+ g_return_if_fail (INDICATOR_IS_OBJECT (object));
+
+ entries = indicator_object_get_entries (object);
+
+ if (entries)
+ {
+ entry = entries->data;
+ g_list_free (entries);
+
+ panel_service_show_entry_common (self, object, entry, xid, x, y, 1, timestamp);
+ }
+}
+
+void
panel_service_secondary_activate_entry (PanelService *self,
const gchar *entry_id,
guint32 timestamp)
@@ -1567,9 +1811,10 @@ panel_service_secondary_activate_entry (PanelService *self,
IndicatorObject *object;
IndicatorObjectEntry *entry;
- panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
+ entry = get_indicator_entry_by_id (self, entry_id);
g_return_if_fail (entry);
+ object = get_entry_parent_indicator (entry);
g_signal_emit_by_name(object, INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE, entry,
timestamp);
}
@@ -1582,11 +1827,12 @@ panel_service_scroll_entry (PanelService *self,
IndicatorObject *object;
IndicatorObjectEntry *entry;
- panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
+ entry = get_indicator_entry_by_id (self, entry_id);
g_return_if_fail (entry);
GdkScrollDirection direction = delta < 0 ? GDK_SCROLL_DOWN : GDK_SCROLL_UP;
+ object = get_entry_parent_indicator (entry);
g_signal_emit_by_name(object, INDICATOR_OBJECT_SIGNAL_ENTRY_SCROLLED, entry,
abs(delta/120), direction);
}
diff --git a/services/panel-service.h b/services/panel-service.h
index 861a67cf7..b7514f04b 100644
--- a/services/panel-service.h
+++ b/services/panel-service.h
@@ -95,10 +95,17 @@ void panel_service_sync_geometry (PanelService *self,
void panel_service_show_entry (PanelService *self,
const gchar *entry_id,
- guint32 timestamp,
+ guint32 xid,
gint32 x,
gint32 y,
- gint32 button);
+ guint32 button,
+ guint32 timestamp);
+
+void panel_service_show_app_menu (PanelService *self,
+ guint32 xid,
+ gint32 x,
+ gint32 y,
+ guint32 timestamp);
void panel_service_secondary_activate_entry (PanelService *self,
const gchar *entry_id,