From 0e4c805aa984369a1aad72e1a6591d013edaf280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 18 Jul 2011 18:08:49 +0200 Subject: Add signal "secondary-activate" and emit it when needed When a libappindicator app get the "SecondaryActivate" dbus method call, the library redirects it to the app emitting the "secondary-activate" signal. This should be conform to the StatusNotifierItem specifications. --- src/app-indicator.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) (limited to 'src/app-indicator.c') diff --git a/src/app-indicator.c b/src/app-indicator.c index bc08eea..c745641 100644 --- a/src/app-indicator.c +++ b/src/app-indicator.c @@ -104,8 +104,9 @@ enum { NEW_STATUS, NEW_LABEL, CONNECTION_CHANGED, - NEW_ICON_THEME_PATH, - SCROLL_EVENT, + NEW_ICON_THEME_PATH, + SCROLL_EVENT, + SECONDARY_ACTIVATE, LAST_SIGNAL }; @@ -508,8 +509,7 @@ app_indicator_class_init (AppIndicatorClass *klass) @arg1: How many steps the scroll wheel has taken @arg2: (type Gdk.ScrollDirection) Which direction the wheel went in - Signaled when there is a new icon set for the - object. + Signaled when the #AppIndicator receives a scroll event. */ signals[SCROLL_EVENT] = g_signal_new (APP_INDICATOR_SIGNAL_SCROLL_EVENT, G_TYPE_FROM_CLASS(klass), @@ -519,6 +519,23 @@ app_indicator_class_init (AppIndicatorClass *klass) _application_service_marshal_VOID__INT_UINT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_UINT); + /** + AppIndicator::secondary-activate: + @arg0: The #AppIndicator object + @arg1: The x pointer position of the secondary activation + @arg2: The y pointer position of the secondary activation + + Signaled when the #AppIndicator receives a secondary activate event + (i.e. a middle-click over the #AppIndicator icon/label). + */ + signals[SECONDARY_ACTIVATE] = g_signal_new (APP_INDICATOR_SIGNAL_SECONDARY_ACTIVATE, + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (AppIndicatorClass, secondary_activate), + NULL, NULL, + _application_service_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); + /* DBus interfaces */ if (item_node_info == NULL) { GError * error = NULL; @@ -1011,6 +1028,11 @@ bus_method_call (GDBusConnection * connection, const gchar * sender, delta = ABS(delta); g_signal_emit(app, signals[SCROLL_EVENT], 0, delta, direction); + } else if (g_strcmp0(method, "SecondaryActivate") == 0) { + gint x, y; + + g_variant_get(params, "(ii)", &x, &y); + g_signal_emit(app, signals[SECONDARY_ACTIVATE], 0, x, y); } else { g_warning("Calling method '%s' on the app-indicator and it's unknown", method); } -- cgit v1.2.3 From a758223f67a87b382d1484c650cdc1ac499dbc93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 18 Jul 2011 18:43:03 +0200 Subject: app-indicator: added middle click support for wrapper tray When a trayicon is used, the middle click over the icon is considered as a secondary activation. --- src/app-indicator.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'src/app-indicator.c') diff --git a/src/app-indicator.c b/src/app-indicator.c index c745641..229221d 100644 --- a/src/app-indicator.c +++ b/src/app-indicator.c @@ -179,6 +179,7 @@ static gboolean fallback_timer_expire (gpointer data); static GtkStatusIcon * fallback (AppIndicator * self); static void status_icon_status_wrapper (AppIndicator * self, const gchar * status, gpointer data); static gboolean scroll_event_wrapper(GtkWidget *status_icon, GdkEventScroll *event, gpointer user_data); +static gboolean middle_click_wrapper(GtkWidget *status_icon, GdkEventButton *event, gpointer user_data); static void status_icon_changes (AppIndicator * self, gpointer data); static void status_icon_activate (GtkStatusIcon * icon, gpointer data); static void status_icon_menu_activate (GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data); @@ -1464,6 +1465,7 @@ fallback (AppIndicator * self) g_signal_connect(G_OBJECT(icon), "activate", G_CALLBACK(status_icon_activate), self); g_signal_connect(G_OBJECT(icon), "popup-menu", G_CALLBACK(status_icon_menu_activate), self); g_signal_connect(G_OBJECT(icon), "scroll-event", G_CALLBACK(scroll_event_wrapper), self); + g_signal_connect(G_OBJECT(icon), "button-release-event", G_CALLBACK(middle_click_wrapper), self); return icon; } @@ -1479,12 +1481,37 @@ status_icon_status_wrapper (AppIndicator * self, const gchar * status, gpointer /* A wrapper for redirecting the scroll events to the app-indicator from status icon widget. */ static gboolean -scroll_event_wrapper(GtkWidget *status_icon, GdkEventScroll *event, gpointer data) +scroll_event_wrapper (GtkWidget *status_icon, GdkEventScroll *event, gpointer data) { g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE); AppIndicator * app = APP_INDICATOR(data); g_signal_emit(app, signals[SCROLL_EVENT], 0, 1, event->direction); + return TRUE; +} + +static gboolean +middle_click_wrapper (GtkWidget *status_icon, GdkEventButton *event, gpointer data) +{ + g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE); + AppIndicator * app = APP_INDICATOR(data); + + if (event->button == 2 && event->type == GDK_BUTTON_RELEASE) { + GtkAllocation alloc; + gint px = event->x; + gint py = event->y; + gtk_widget_get_allocation (status_icon, &alloc); + + if (px >= 0 && px < alloc.width && py >= 0 && py < alloc.height) { + gint wx, wy; + gdk_window_get_origin(event->window, &wx, &wy); + + g_signal_emit(app, signals[SCROLL_EVENT], wx+px, wy+py); + + return TRUE; + } + } + return FALSE; } @@ -1557,6 +1584,7 @@ unfallback (AppIndicator * self, GtkStatusIcon * status_icon) g_signal_handlers_disconnect_by_func(G_OBJECT(self), status_icon_status_wrapper, status_icon); g_signal_handlers_disconnect_by_func(G_OBJECT(self), status_icon_changes, status_icon); g_signal_handlers_disconnect_by_func(G_OBJECT(self), scroll_event_wrapper, status_icon); + g_signal_handlers_disconnect_by_func(G_OBJECT(self), middle_click_wrapper, status_icon); gtk_status_icon_set_visible(status_icon, FALSE); g_object_unref(G_OBJECT(status_icon)); return; -- cgit v1.2.3 From d54d1e78545ee8cbeb7d3b0d276665290de7f296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 19 Jul 2011 20:25:58 +0200 Subject: Call the right signal on middle-click over the tray icon! --- src/app-indicator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/app-indicator.c') diff --git a/src/app-indicator.c b/src/app-indicator.c index 229221d..30e2e0a 100644 --- a/src/app-indicator.c +++ b/src/app-indicator.c @@ -1506,7 +1506,7 @@ middle_click_wrapper (GtkWidget *status_icon, GdkEventButton *event, gpointer da gint wx, wy; gdk_window_get_origin(event->window, &wx, &wy); - g_signal_emit(app, signals[SCROLL_EVENT], wx+px, wy+py); + g_signal_emit(app, signals[SECONDARY_ACTIVATE], wx+px, wy+py); return TRUE; } -- cgit v1.2.3 From 8f71358509ec3e2a6e2fe633c4dfc07c58f450ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 20 Jul 2011 02:57:51 +0200 Subject: app-indicator: Move from "secondary-activate" signal to secondary_activate target To avoid appindicators writers to use the "secondary-activate" event to perform actions that can't be done via a menu item we added a new API: - app_indicator_set_secondary_activate_target - app_indicator_get_secondary_activate_target With it, an appindicator writer can simply define a new GtkWidget that will be activated when a secondary activation will occur. The GtkWidget must be a sensitive and visible inner child of the appindicator MenuItem. --- src/app-indicator.c | 150 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 120 insertions(+), 30 deletions(-) (limited to 'src/app-indicator.c') diff --git a/src/app-indicator.c b/src/app-indicator.c index 30e2e0a..1dc91d8 100644 --- a/src/app-indicator.c +++ b/src/app-indicator.c @@ -77,6 +77,8 @@ struct _AppIndicatorPrivate { gchar *icon_theme_path; DbusmenuServer *menuservice; GtkWidget *menu; + GtkWidget *sec_activate_target; + gboolean sec_activate_enabled; guint32 ordering_index; gchar * label; gchar * label_guide; @@ -106,7 +108,6 @@ enum { CONNECTION_CHANGED, NEW_ICON_THEME_PATH, SCROLL_EVENT, - SECONDARY_ACTIVATE, LAST_SIGNAL }; @@ -187,6 +188,7 @@ static void unfallback (AppIndicator * self, GtkStatusIcon * status_icon); static gchar * append_panel_icon_suffix (const gchar * icon_name); static void watcher_owner_changed (GObject * obj, GParamSpec * pspec, gpointer user_data); static void theme_changed_cb (GtkIconTheme * theme, gpointer user_data); +static void sec_activate_target_parent_changed(GtkWidget *menuitem, GtkWidget *old_parent, gpointer user_data); static GVariant * bus_get_prop (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * property, GError ** error, gpointer user_data); static void bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data); static void bus_creation (GObject * obj, GAsyncResult * res, gpointer user_data); @@ -520,23 +522,6 @@ app_indicator_class_init (AppIndicatorClass *klass) _application_service_marshal_VOID__INT_UINT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_UINT); - /** - AppIndicator::secondary-activate: - @arg0: The #AppIndicator object - @arg1: The x pointer position of the secondary activation - @arg2: The y pointer position of the secondary activation - - Signaled when the #AppIndicator receives a secondary activate event - (i.e. a middle-click over the #AppIndicator icon/label). - */ - signals[SECONDARY_ACTIVATE] = g_signal_new (APP_INDICATOR_SIGNAL_SECONDARY_ACTIVATE, - G_TYPE_FROM_CLASS(klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (AppIndicatorClass, secondary_activate), - NULL, NULL, - _application_service_marshal_VOID__INT_INT, - G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); - /* DBus interfaces */ if (item_node_info == NULL) { GError * error = NULL; @@ -606,6 +591,9 @@ app_indicator_init (AppIndicator *self) priv->shorties = NULL; + priv->sec_activate_target = NULL; + priv->sec_activate_enabled = FALSE; + /* Start getting the session bus */ g_object_ref(self); /* ref for the bus creation callback */ g_bus_get(G_BUS_TYPE_SESSION, NULL, bus_creation, self); @@ -681,6 +669,12 @@ app_indicator_dispose (GObject *object) priv->connection = NULL; } + if (priv->sec_activate_target != NULL) { + g_signal_handlers_disconnect_by_func (priv->sec_activate_target, sec_activate_target_parent_changed, self); + g_object_unref(G_OBJECT(priv->sec_activate_target)); + priv->sec_activate_target = NULL; + } + g_signal_handlers_disconnect_by_func(gtk_icon_theme_get_default(), G_CALLBACK(theme_changed_cb), self); G_OBJECT_CLASS (app_indicator_parent_class)->dispose (object); @@ -1008,6 +1002,7 @@ bus_method_call (GDBusConnection * connection, const gchar * sender, g_return_if_fail(IS_APP_INDICATOR(user_data)); AppIndicator * app = APP_INDICATOR(user_data); + AppIndicatorPrivate * priv = app->priv; GVariant * retval = NULL; if (g_strcmp0(method, "Scroll") == 0) { @@ -1030,10 +1025,14 @@ bus_method_call (GDBusConnection * connection, const gchar * sender, g_signal_emit(app, signals[SCROLL_EVENT], 0, delta, direction); } else if (g_strcmp0(method, "SecondaryActivate") == 0) { - gint x, y; - - g_variant_get(params, "(ii)", &x, &y); - g_signal_emit(app, signals[SECONDARY_ACTIVATE], 0, x, y); + GtkWidget *menuitem = priv->sec_activate_target; + + if (priv->sec_activate_enabled && menuitem && + gtk_widget_get_visible (menuitem) && + gtk_widget_get_sensitive (menuitem)) + { + gtk_widget_activate (menuitem); + } } else { g_warning("Calling method '%s' on the app-indicator and it's unknown", method); } @@ -1495,19 +1494,21 @@ middle_click_wrapper (GtkWidget *status_icon, GdkEventButton *event, gpointer da { g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE); AppIndicator * app = APP_INDICATOR(data); + AppIndicatorPrivate *priv = app->priv; if (event->button == 2 && event->type == GDK_BUTTON_RELEASE) { GtkAllocation alloc; gint px = event->x; gint py = event->y; gtk_widget_get_allocation (status_icon, &alloc); - - if (px >= 0 && px < alloc.width && py >= 0 && py < alloc.height) { - gint wx, wy; - gdk_window_get_origin(event->window, &wx, &wy); - - g_signal_emit(app, signals[SECONDARY_ACTIVATE], wx+px, wy+py); - + GtkWidget *menuitem = priv->sec_activate_target; + + if (px >= 0 && px < alloc.width && py >= 0 && py < alloc.height && + priv->sec_activate_enabled && menuitem && + gtk_widget_get_visible (menuitem) && + gtk_widget_get_sensitive (menuitem)) + { + gtk_widget_activate (menuitem); return TRUE; } } @@ -1607,6 +1608,38 @@ append_panel_icon_suffix (const gchar *icon_name) return long_name; } +static gboolean +widget_is_menu_child(AppIndicator * self, GtkWidget *child) +{ + g_return_val_if_fail(IS_APP_INDICATOR(self), FALSE); + + if (!self->priv->menu) return FALSE; + if (!child) return FALSE; + + GtkWidget *parent; + + while ((parent = gtk_widget_get_parent(child))) { + if (parent == self->priv->menu) + return TRUE; + + if (GTK_IS_MENU(parent)) + child = gtk_menu_get_attach_widget(GTK_MENU(parent)); + else + child = parent; + } + + return FALSE; +} + +static void +sec_activate_target_parent_changed(GtkWidget *menuitem, GtkWidget *old_parent, + gpointer data) +{ + g_return_if_fail(IS_APP_INDICATOR(data)); + AppIndicator *self = data; + self->priv->sec_activate_enabled = widget_is_menu_child(self, menuitem); +} + /* ************************* */ /* Public Functions */ @@ -1996,6 +2029,8 @@ app_indicator_set_menu (AppIndicator *self, GtkMenu *menu) setup_dbusmenu (self); + priv->sec_activate_enabled = widget_is_menu_child (self, priv->sec_activate_target); + check_connect (self); return; @@ -2022,6 +2057,45 @@ app_indicator_set_ordering_index (AppIndicator *self, guint32 ordering_index) return; } +/** + app_indicator_set_secondary_activate_target: + @self: The #AppIndicator + @menuitem: A #GtkWidget to be activated on secondary activation + + Set the @menuitem to be activated when a secondary activation event (i.e. a + middle-click) is emitted over the #AppIndicator icon/label. + + The @menuitem can be also a complex #GtkWidget, but to get activated when + a secondary activation occurs in the #Appindicator, it must be a visible and + active child (or inner-child) of the #AppIndicator:menu. + + Setting @menuitem to %NULL causes to disable this feature. +**/ +void +app_indicator_set_secondary_activate_target (AppIndicator *self, GtkWidget *menuitem) +{ + g_return_if_fail (IS_APP_INDICATOR (self)); + AppIndicatorPrivate *priv = self->priv; + + if (menuitem == NULL) { + if (priv->sec_activate_target) { + g_object_unref(G_OBJECT(priv->sec_activate_target)); + g_signal_handlers_disconnect_by_func (priv->sec_activate_target, + sec_activate_target_parent_changed, + self); + priv->sec_activate_target = NULL; + } + + return; + } + + g_return_if_fail (GTK_IS_WIDGET (menuitem)); + + priv->sec_activate_target = g_object_ref(G_OBJECT(menuitem)); + priv->sec_activate_enabled = widget_is_menu_child(self, menuitem); + g_signal_connect(menuitem, "parent-set", G_CALLBACK(sec_activate_target_parent_changed), self); +} + /** app_indicator_get_id: @self: The #AppIndicator object to use @@ -2157,7 +2231,7 @@ app_indicator_get_attention_icon_desc (AppIndicator *self) Gets the menu being used for this application indicator. Wrapper function for property #AppIndicator:menu. - Return value: (transfer none): A #GtkMenu object or %NULL if one hasn't been set. + Return value: (transfer full): A #GtkMenu object or %NULL if one hasn't been set. */ GtkMenu * app_indicator_get_menu (AppIndicator *self) @@ -2223,6 +2297,22 @@ app_indicator_get_ordering_index (AppIndicator *self) } } +/** + app_indicator_get_secondary_activate_target: + @self: The #AppIndicator object to use + + Gets the menuitem being called on secondary-activate event. + + Return value: (transfer full): A #GtkWidget object or %NULL if none has been set. +*/ +GtkWidget * +app_indicator_get_secondary_activate_target (AppIndicator *self) +{ + g_return_val_if_fail (IS_APP_INDICATOR (self), NULL); + + return GTK_WIDGET(self->priv->sec_activate_target); +} + #define APP_INDICATOR_SHORTY_NICK "app-indicator-shorty-nick" /* Callback when an item from the desktop shortcuts gets -- cgit v1.2.3 From 195a64f16c356b1b75c81ec374699c94c636fbe2 Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Fri, 22 Jul 2011 13:50:27 -0500 Subject: Always clear the sec_activate_target --- src/app-indicator.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src/app-indicator.c') diff --git a/src/app-indicator.c b/src/app-indicator.c index 1dc91d8..98aec7a 100644 --- a/src/app-indicator.c +++ b/src/app-indicator.c @@ -2077,15 +2077,15 @@ app_indicator_set_secondary_activate_target (AppIndicator *self, GtkWidget *menu g_return_if_fail (IS_APP_INDICATOR (self)); AppIndicatorPrivate *priv = self->priv; - if (menuitem == NULL) { - if (priv->sec_activate_target) { - g_object_unref(G_OBJECT(priv->sec_activate_target)); - g_signal_handlers_disconnect_by_func (priv->sec_activate_target, - sec_activate_target_parent_changed, - self); - priv->sec_activate_target = NULL; - } + if (priv->sec_activate_target) { + g_signal_handlers_disconnect_by_func (priv->sec_activate_target, + sec_activate_target_parent_changed, + self); + g_object_unref(G_OBJECT(priv->sec_activate_target)); + priv->sec_activate_target = NULL; + } + if (menuitem == NULL) { return; } -- cgit v1.2.3 From d59df9bc7c25cd9983174b3a917a743ee4dcff1b Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Fri, 22 Jul 2011 13:56:05 -0500 Subject: Watching for the 'XAyatanaSecondaryActivate' method as well --- src/app-indicator.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/app-indicator.c') diff --git a/src/app-indicator.c b/src/app-indicator.c index 98aec7a..bdc4ea9 100644 --- a/src/app-indicator.c +++ b/src/app-indicator.c @@ -1024,7 +1024,8 @@ bus_method_call (GDBusConnection * connection, const gchar * sender, delta = ABS(delta); g_signal_emit(app, signals[SCROLL_EVENT], 0, delta, direction); - } else if (g_strcmp0(method, "SecondaryActivate") == 0) { + } else if (g_strcmp0(method, "SecondaryActivate") == 0 || + g_strcmp0(method, "XAyatanaSecondaryActivate") == 0) { GtkWidget *menuitem = priv->sec_activate_target; if (priv->sec_activate_enabled && menuitem && -- cgit v1.2.3