diff options
-rw-r--r-- | bindings/mono/examples/IndicatorExample.cs | 9 | ||||
-rw-r--r-- | bindings/vala/examples/indicator-example.vala | 5 | ||||
-rw-r--r-- | docs/reference/libappindicator-sections.txt | 2 | ||||
-rw-r--r-- | example/simple-client-vala.vala | 13 | ||||
-rw-r--r-- | example/simple-client.c | 103 | ||||
-rw-r--r-- | src/app-indicator.c | 153 | ||||
-rw-r--r-- | src/app-indicator.h | 3 | ||||
-rw-r--r-- | src/application-service-marshal.list | 1 | ||||
-rw-r--r-- | src/notification-item.xml | 7 |
9 files changed, 235 insertions, 61 deletions
diff --git a/bindings/mono/examples/IndicatorExample.cs b/bindings/mono/examples/IndicatorExample.cs index c18a665..7413c54 100644 --- a/bindings/mono/examples/IndicatorExample.cs +++ b/bindings/mono/examples/IndicatorExample.cs @@ -41,12 +41,19 @@ public class IndicatorExample indicator.Status = Status.Attention; Menu menu = new Menu (); - menu.Append (new MenuItem ("Foo")); + var foo = new MenuItem ("Foo"); + menu.Append (foo); + foo.Activated += delegate { + System.Console.WriteLine ("Foo item has been activated"); + }; + menu.Append (new MenuItem ("Bar")); indicator.Menu = menu; indicator.Menu.ShowAll (); + indicator.SecondaryActivateTarget = foo; + win.ShowAll (); Application.Run (); diff --git a/bindings/vala/examples/indicator-example.vala b/bindings/vala/examples/indicator-example.vala index 621c962..7bb1d7d 100644 --- a/bindings/vala/examples/indicator-example.vala +++ b/bindings/vala/examples/indicator-example.vala @@ -47,14 +47,15 @@ public class IndicatorExample { item.show(); menu.append(item); - item = new MenuItem.with_label("Bar"); + var bar = item = new MenuItem.with_label("Bar"); item.show(); item.activate.connect(() => { - indicator.set_status(IndicatorStatus.ATTENTION); + indicator.set_status(IndicatorStatus.ACTIVE); }); menu.append(item); indicator.set_menu(menu); + indicator.set_secondary_activate_target(bar); win.show_all(); diff --git a/docs/reference/libappindicator-sections.txt b/docs/reference/libappindicator-sections.txt index da05f40..6f09364 100644 --- a/docs/reference/libappindicator-sections.txt +++ b/docs/reference/libappindicator-sections.txt @@ -31,6 +31,7 @@ app_indicator_set_icon_full app_indicator_set_icon_theme_path app_indicator_set_label app_indicator_set_ordering_index +app_indicator_set_secondary_activate_target app_indicator_get_id app_indicator_get_category app_indicator_get_status @@ -43,6 +44,7 @@ app_indicator_get_menu app_indicator_get_label app_indicator_get_label_guide app_indicator_get_ordering_index +app_indicator_get_secondary_activate_target app_indicator_build_menu_from_desktop </SECTION> diff --git a/example/simple-client-vala.vala b/example/simple-client-vala.vala index 61e0c2f..8d661ce 100644 --- a/example/simple-client-vala.vala +++ b/example/simple-client-vala.vala @@ -72,6 +72,17 @@ class SimpleClient { menu.append(mi); mi.activate.connect(() => { print("Sub3\n"); }); + mi = new MenuItem.with_label("Toggle Attention"); + menu.append(mi); + mi.activate.connect(() => { + if (ci.get_status() == IndicatorStatus.ATTENTION) + ci.set_status(IndicatorStatus.ACTIVE); + else + ci.set_status(IndicatorStatus.ATTENTION); + }); + + ci.set_secondary_activate_target(mi); + menu.show_all(); item.set_submenu(menu); } @@ -109,7 +120,7 @@ class SimpleClient { chk.show(); var radio = new RadioMenuItem.with_label(new SList<RadioMenuItem>(), "2"); - radio.activate.connect(() => { print("2\n"); }); + radio.activate.connect(() => { print("2\n"); }); menu.append(radio); radio.show(); diff --git a/example/simple-client.c b/example/simple-client.c index 9e63e14..dcee81b 100644 --- a/example/simple-client.c +++ b/example/simple-client.c @@ -31,46 +31,46 @@ static gboolean can_haz_label = TRUE; static void label_toggle_cb (GtkWidget * widget, gpointer data) { - can_haz_label = !can_haz_label; + can_haz_label = !can_haz_label; - if (can_haz_label) { - gtk_menu_item_set_label(GTK_MENU_ITEM(widget), "Hide label"); - } else { - gtk_menu_item_set_label(GTK_MENU_ITEM(widget), "Show label"); - } + if (can_haz_label) { + gtk_menu_item_set_label(GTK_MENU_ITEM(widget), "Hide label"); + } else { + gtk_menu_item_set_label(GTK_MENU_ITEM(widget), "Show label"); + } - return; + return; } static void activate_clicked_cb (GtkWidget *widget, gpointer data) { - AppIndicator * ci = APP_INDICATOR(data); - - if (active) { - app_indicator_set_status (ci, APP_INDICATOR_STATUS_ATTENTION); - gtk_menu_item_set_label(GTK_MENU_ITEM(widget), "I'm okay now"); - active = FALSE; - } else { - app_indicator_set_status (ci, APP_INDICATOR_STATUS_ACTIVE); - gtk_menu_item_set_label(GTK_MENU_ITEM(widget), "Get Attention"); - active = TRUE; - } + AppIndicator * ci = APP_INDICATOR(data); + + if (active) { + app_indicator_set_status (ci, APP_INDICATOR_STATUS_ATTENTION); + gtk_menu_item_set_label(GTK_MENU_ITEM(widget), "I'm okay now"); + active = FALSE; + } else { + app_indicator_set_status (ci, APP_INDICATOR_STATUS_ACTIVE); + gtk_menu_item_set_label(GTK_MENU_ITEM(widget), "Get Attention"); + active = TRUE; + } } static void local_icon_toggle_cb (GtkWidget *widget, gpointer data) { - AppIndicator * ci = APP_INDICATOR(data); + AppIndicator * ci = APP_INDICATOR(data); - if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) { - app_indicator_set_icon_full(ci, LOCAL_ICON, "Local Icon"); - } else { - app_indicator_set_icon_full(ci, "indicator-messages", "System Icon"); - } + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) { + app_indicator_set_icon_full(ci, LOCAL_ICON, "Local Icon"); + } else { + app_indicator_set_icon_full(ci, "indicator-messages", "System Icon"); + } - return; + return; } static void @@ -98,9 +98,9 @@ image_clicked_cb (GtkWidget *widget, gpointer data) } static void -scroll_event_cb (AppIndicator * ci, gint delta, guint direction) +scroll_event_cb (AppIndicator * ci, gint delta, guint direction, gpointer data) { - g_print("Got scroll event! delta: %d, direction: %d\n", delta, direction); + g_print("Got scroll event! delta: %d, direction: %d\n", delta, direction); } static void @@ -138,15 +138,15 @@ guint percentage = 0; static gboolean percent_change (gpointer user_data) { - percentage = (percentage + 1) % 100; - if (can_haz_label) { - gchar * percentstr = g_strdup_printf("%d%%", percentage + 1); - app_indicator_set_label (APP_INDICATOR(user_data), percentstr, "100%"); - g_free(percentstr); - } else { - app_indicator_set_label (APP_INDICATOR(user_data), NULL, NULL); - } - return TRUE; + percentage = (percentage + 1) % 100; + if (can_haz_label) { + gchar * percentstr = g_strdup_printf("%d%%", percentage + 1); + app_indicator_set_label (APP_INDICATOR(user_data), percentstr, "100%"); + g_free(percentstr); + } else { + app_indicator_set_label (APP_INDICATOR(user_data), NULL, NULL); + } + return TRUE; } int @@ -161,17 +161,17 @@ main (int argc, char ** argv) "indicator-messages", APP_INDICATOR_CATEGORY_APPLICATION_STATUS); - g_assert (IS_APP_INDICATOR (ci)); + g_assert (IS_APP_INDICATOR (ci)); g_assert (G_IS_OBJECT (ci)); - app_indicator_set_status (ci, APP_INDICATOR_STATUS_ACTIVE); - app_indicator_set_attention_icon_full(ci, "indicator-messages-new", "System Messages Icon Highlighted"); - app_indicator_set_label (ci, "1%", "100%"); + app_indicator_set_status (ci, APP_INDICATOR_STATUS_ACTIVE); + app_indicator_set_attention_icon_full(ci, "indicator-messages-new", "System Messages Icon Highlighted"); + app_indicator_set_label (ci, "1%", "100%"); - g_signal_connect (ci, "scroll-event", + g_signal_connect (ci, "scroll-event", G_CALLBACK (scroll_event_cb), NULL); - g_timeout_add_seconds(1, percent_change, ci); + g_timeout_add_seconds(1, percent_change, ci); menu = gtk_menu_new (); GtkWidget *item = gtk_check_menu_item_new_with_label ("1"); @@ -195,37 +195,38 @@ main (int argc, char ** argv) g_signal_connect (toggle_item, "activate", G_CALLBACK (toggle_sensitivity_cb), item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), toggle_item); - gtk_widget_show(toggle_item); + gtk_widget_show(toggle_item); item = gtk_image_menu_item_new_from_stock (GTK_STOCK_NEW, NULL); g_signal_connect (item, "activate", G_CALLBACK (image_clicked_cb), NULL); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - gtk_widget_show(item); + gtk_widget_show(item); item = gtk_menu_item_new_with_label ("Get Attention"); g_signal_connect (item, "activate", G_CALLBACK (activate_clicked_cb), ci); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - gtk_widget_show(item); + gtk_widget_show(item); + app_indicator_set_secondary_activate_target(ci, item); item = gtk_menu_item_new_with_label ("Show label"); - label_toggle_cb(item, ci); + label_toggle_cb(item, ci); g_signal_connect (item, "activate", G_CALLBACK (label_toggle_cb), ci); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - gtk_widget_show(item); + gtk_widget_show(item); item = gtk_check_menu_item_new_with_label ("Set Local Icon"); g_signal_connect (item, "activate", G_CALLBACK (local_icon_toggle_cb), ci); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - gtk_widget_show(item); + gtk_widget_show(item); app_indicator_set_menu (ci, GTK_MENU (menu)); - mainloop = g_main_loop_new(NULL, FALSE); - g_main_loop_run(mainloop); + mainloop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(mainloop); - return 0; + return 0; } diff --git a/src/app-indicator.c b/src/app-indicator.c index bc08eea..bdc4ea9 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; @@ -104,8 +106,8 @@ enum { NEW_STATUS, NEW_LABEL, CONNECTION_CHANGED, - NEW_ICON_THEME_PATH, - SCROLL_EVENT, + NEW_ICON_THEME_PATH, + SCROLL_EVENT, LAST_SIGNAL }; @@ -178,6 +180,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); @@ -185,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); @@ -508,8 +512,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), @@ -588,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); @@ -663,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); @@ -990,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) { @@ -1011,6 +1024,16 @@ 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 || + g_strcmp0(method, "XAyatanaSecondaryActivate") == 0) { + 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); } @@ -1442,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; } @@ -1457,12 +1481,39 @@ 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); + 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); + 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; + } + } + return FALSE; } @@ -1535,6 +1586,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; @@ -1557,6 +1609,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 */ @@ -1946,6 +2030,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; @@ -1973,6 +2059,45 @@ app_indicator_set_ordering_index (AppIndicator *self, guint32 ordering_index) } /** + 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 (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; + } + + 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 @@ -2107,7 +2232,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) @@ -2173,6 +2298,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 diff --git a/src/app-indicator.h b/src/app-indicator.h index f96212d..09b7520 100644 --- a/src/app-indicator.h +++ b/src/app-indicator.h @@ -284,6 +284,8 @@ void app_indicator_set_icon_theme_path(AppIndicator const gchar *icon_theme_path); void app_indicator_set_ordering_index (AppIndicator *self, guint32 ordering_index); +void app_indicator_set_secondary_activate_target (AppIndicator *self, + GtkWidget *menuitem); /* Get properties */ const gchar * app_indicator_get_id (AppIndicator *self); @@ -298,6 +300,7 @@ GtkMenu * app_indicator_get_menu (AppIndic const gchar * app_indicator_get_label (AppIndicator *self); const gchar * app_indicator_get_label_guide (AppIndicator *self); guint32 app_indicator_get_ordering_index (AppIndicator *self); +GtkWidget * app_indicator_get_secondary_activate_target (AppIndicator *self); /* Helpers */ void app_indicator_build_menu_from_desktop (AppIndicator * self, diff --git a/src/application-service-marshal.list b/src/application-service-marshal.list index 39a55fe..ae3aa6f 100644 --- a/src/application-service-marshal.list +++ b/src/application-service-marshal.list @@ -22,3 +22,4 @@ VOID: INT, STRING VOID: STRING, STRING VOID: BOOL, STRING, OBJECT VOID: INT, UINT +VOID: INT, INT diff --git a/src/notification-item.xml b/src/notification-item.xml index bbf77c5..589a438 100644 --- a/src/notification-item.xml +++ b/src/notification-item.xml @@ -23,6 +23,13 @@ <arg type="i" name="delta" direction="in" /> <arg type="s" name="orientation" direction="in" /> </method> + <method name="SecondaryActivate"> + <arg type="i" name="x" direction="in" /> + <arg type="i" name="y" direction="in" /> + </method> + <method name="XAyatanaSecondaryActivate"> + <arg type="u" name="timestamp" direction="in" /> + </method> <!-- Signals --> <signal name="NewIcon"> |