aboutsummaryrefslogtreecommitdiff
path: root/src/indicator-messages.c
diff options
context:
space:
mode:
authorLars Uebernickel <lars.uebernickel@canonical.com>2012-08-21 12:07:19 +0200
committerLars Uebernickel <lars.uebernickel@canonical.com>2012-08-21 12:07:19 +0200
commit374fa7bb7eb2526f1cf19bf85edd5793d8ef569b (patch)
treeeae99c80f31d940226680a31100024cd2b4a71c4 /src/indicator-messages.c
parent13f2b1bb69fb69aadcdc838b2321aa2b01657e06 (diff)
parent5df53a2c98d806f3b266d0032986d4dab3beb7a3 (diff)
downloadayatana-indicator-messages-374fa7bb7eb2526f1cf19bf85edd5793d8ef569b.tar.gz
ayatana-indicator-messages-374fa7bb7eb2526f1cf19bf85edd5793d8ef569b.tar.bz2
ayatana-indicator-messages-374fa7bb7eb2526f1cf19bf85edd5793d8ef569b.zip
Merge lp:~larsu/indicator-messags/towards-q-redesign
This branch introduces libmessaging-menu, a new library for applications to integrate with the messaging menu. Libindicate is not supported anymore. libmessaging-menu uses GMenuModel to communicate with indicator-messages-service. In order to take advantage of GMenuModel's architecture (re-exporting menus on a different path), the service now also sends its menu as a GMenuModel to the panel plugin. The plugin uses gtk_menu_new_from_model to create the menu widgets. Custom menu items are created by a small gtk+ patch that watches for x-canonical-type attributes. The branch also contains most of the design changes for quantal.
Diffstat (limited to 'src/indicator-messages.c')
-rw-r--r--src/indicator-messages.c736
1 files changed, 126 insertions, 610 deletions
diff --git a/src/indicator-messages.c b/src/indicator-messages.c
index 32f97f7..2824b0c 100644
--- a/src/indicator-messages.c
+++ b/src/indicator-messages.c
@@ -29,16 +29,15 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>
-#include <libdbusmenu-gtk/menu.h>
-#include <libdbusmenu-gtk/menuitem.h>
-
#include <libindicator/indicator.h>
#include <libindicator/indicator-object.h>
#include <libindicator/indicator-image-helper.h>
#include <libindicator/indicator-service-manager.h>
#include "dbus-data.h"
-#include "gen-messages-service.xml.h"
+
+#include "im-app-menu-item.h"
+#include "im-source-menu-item.h"
#define INDICATOR_MESSAGES_TYPE (indicator_messages_get_type ())
#define INDICATOR_MESSAGES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_MESSAGES_TYPE, IndicatorMessages))
@@ -52,12 +51,16 @@ typedef struct _IndicatorMessagesClass IndicatorMessagesClass;
struct _IndicatorMessagesClass {
IndicatorObjectClass parent_class;
- void (*update_a11y_desc) (IndicatorServiceManager * service, gpointer * user_data);
};
struct _IndicatorMessages {
IndicatorObject parent;
IndicatorServiceManager * service;
+ GActionGroup *actions;
+ GMenu *menu_wrapper;
+ GMenuModel *menu;
+ GtkWidget *image;
+ gchar *accessible_desc;
};
GType indicator_messages_get_type (void);
@@ -66,53 +69,25 @@ GType indicator_messages_get_type (void);
INDICATOR_SET_VERSION
INDICATOR_SET_TYPE(INDICATOR_MESSAGES_TYPE)
-/* Globals */
-static GtkWidget * main_image = NULL;
-static GDBusProxy * icon_proxy = NULL;
-static GDBusNodeInfo * bus_node_info = NULL;
-static GDBusInterfaceInfo * bus_interface_info = NULL;
-static const gchar * accessible_desc = NULL;
-static IndicatorObject * indicator = NULL;
-
/* Prototypes */
static void indicator_messages_class_init (IndicatorMessagesClass *klass);
static void indicator_messages_init (IndicatorMessages *self);
static void indicator_messages_dispose (GObject *object);
static void indicator_messages_finalize (GObject *object);
-static GtkImage * get_icon (IndicatorObject * io);
+static GtkImage * get_image (IndicatorObject * io);
static GtkMenu * get_menu (IndicatorObject * io);
-static void indicator_messages_middle_click (IndicatorObject * io,
- IndicatorObjectEntry * entry,
- guint time, gpointer data);
static const gchar * get_accessible_desc (IndicatorObject * io);
static const gchar * get_name_hint (IndicatorObject * io);
-static void connection_change (IndicatorServiceManager * sm,
- gboolean connected,
- gpointer user_data);
+static void update_root_item (IndicatorMessages * self);
+static void update_menu (IndicatorMessages *self);
+static void menu_items_changed (GMenuModel *menu,
+ gint position,
+ gint removed,
+ gint added,
+ gpointer user_data);
G_DEFINE_TYPE (IndicatorMessages, indicator_messages, INDICATOR_OBJECT_TYPE);
-static void
-update_a11y_desc (void)
-{
- g_return_if_fail(IS_INDICATOR_MESSAGES(indicator));
-
- GList *entries = indicator_object_get_entries(indicator);
- IndicatorObjectEntry * entry = (IndicatorObjectEntry *)entries->data;
-
- entry->accessible_desc = get_accessible_desc(indicator);
-
- g_signal_emit(G_OBJECT(indicator),
- INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE_ID,
- 0,
- entry,
- TRUE);
-
- g_list_free(entries);
-
- return;
-}
-
/* Initialize the one-timers */
static void
indicator_messages_class_init (IndicatorMessagesClass *klass)
@@ -124,47 +99,55 @@ indicator_messages_class_init (IndicatorMessagesClass *klass)
IndicatorObjectClass * io_class = INDICATOR_OBJECT_CLASS(klass);
- io_class->get_image = get_icon;
+ io_class->get_image = get_image;
io_class->get_menu = get_menu;
io_class->get_accessible_desc = get_accessible_desc;
io_class->get_name_hint = get_name_hint;
- io_class->secondary_activate = indicator_messages_middle_click;
-
- if (bus_node_info == NULL) {
- GError * error = NULL;
-
- bus_node_info = g_dbus_node_info_new_for_xml(_messages_service, &error);
- if (error != NULL) {
- g_error("Unable to parse Messaging Menu Interface description: %s", error->message);
- g_error_free(error);
- }
- }
-
- if (bus_interface_info == NULL) {
- bus_interface_info = g_dbus_node_info_lookup_interface(bus_node_info, INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE);
-
- if (bus_interface_info == NULL) {
- g_error("Unable to find interface '" INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE "'");
- }
- }
-
- return;
}
/* Build up our per-instance variables */
static void
indicator_messages_init (IndicatorMessages *self)
{
+ GDBusConnection *bus;
+ GError *error = NULL;
+
/* Default values */
self->service = NULL;
/* Complex stuff */
self->service = indicator_service_manager_new_version(INDICATOR_MESSAGES_DBUS_NAME, 1);
- g_signal_connect(self->service, INDICATOR_SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE, G_CALLBACK(connection_change), self);
- indicator = INDICATOR_OBJECT(self);
+ bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ if (!bus) {
+ g_warning ("error connecting to the session bus: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ self->actions = G_ACTION_GROUP (g_dbus_action_group_get (bus,
+ INDICATOR_MESSAGES_DBUS_NAME,
+ INDICATOR_MESSAGES_DBUS_OBJECT));
- return;
+ self->menu = G_MENU_MODEL (g_dbus_menu_model_get (bus,
+ INDICATOR_MESSAGES_DBUS_NAME,
+ INDICATOR_MESSAGES_DBUS_OBJECT));
+
+ g_signal_connect (self->menu, "items-changed", G_CALLBACK (menu_items_changed), self);
+
+ self->image = g_object_ref_sink (gtk_image_new ());
+ gtk_widget_show (self->image);
+ update_root_item (self);
+
+ self->menu_wrapper = g_menu_new ();
+ update_menu (self);
+
+ g_object_unref (bus);
+
+ /* make sure custom menu item types are registered (so that
+ * gtk_model_new_from_menu can pick them up */
+ im_app_menu_item_get_type ();
+ im_source_menu_item_get_type ();
}
/* Unref stuff */
@@ -174,10 +157,11 @@ indicator_messages_dispose (GObject *object)
IndicatorMessages * self = INDICATOR_MESSAGES(object);
g_return_if_fail(self != NULL);
- if (self->service != NULL) {
- g_object_unref(self->service);
- self->service = NULL;
- }
+ g_clear_object (&self->service);
+ g_clear_object (&self->menu_wrapper);
+ g_clear_object (&self->actions);
+ g_clear_object (&self->menu);
+ g_clear_object (&self->image);
G_OBJECT_CLASS (indicator_messages_parent_class)->dispose (object);
return;
@@ -187,6 +171,9 @@ indicator_messages_dispose (GObject *object)
static void
indicator_messages_finalize (GObject *object)
{
+ IndicatorMessages *self = INDICATOR_MESSAGES (object);
+
+ g_free (self->accessible_desc);
G_OBJECT_CLASS (indicator_messages_parent_class)->finalize (object);
return;
@@ -196,592 +183,121 @@ indicator_messages_finalize (GObject *object)
/* Functions */
-/* Signal off of the proxy */
-static void
-proxy_signal (GDBusProxy * proxy, const gchar * sender, const gchar * signal, GVariant * params, gpointer user_data)
+static GtkImage *
+get_image (IndicatorObject * io)
{
- gboolean prop = g_variant_get_boolean(g_variant_get_child_value(params, 0));
-
- if (g_strcmp0("AttentionChanged", signal) == 0) {
- if (prop) {
- indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages-new");
- accessible_desc = _("New Messages");
- } else {
- indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages");
- accessible_desc = _("Messages");
- }
- } else if (g_strcmp0("IconChanged", signal) == 0) {
- if (prop) {
- gtk_widget_hide(main_image);
- } else {
- gtk_widget_show(main_image);
- }
- } else {
- g_warning("Unknown signal %s", signal);
- }
-
- update_a11y_desc();
+ IndicatorMessages *self = INDICATOR_MESSAGES (io);
- return;
+ return GTK_IMAGE (self->image);
}
-/* Callback from getting the attention status from the service. */
-static void
-attention_cb (GObject * object, GAsyncResult * ares, gpointer user_data)
+static GtkMenu *
+get_menu (IndicatorObject * io)
{
- GError * error = NULL;
- GVariant * res = g_dbus_proxy_call_finish(G_DBUS_PROXY(object), ares, &error);
-
- if (error != NULL) {
- g_warning("Unable to get attention status: %s", error->message);
- g_error_free(error);
- return;
- }
-
- gboolean prop = g_variant_get_boolean(g_variant_get_child_value(res, 0));
+ IndicatorMessages *self = INDICATOR_MESSAGES (io);
+ GtkWidget *menu;
- if (prop) {
- indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages-new");
- accessible_desc = _("New Messages");
- } else {
- indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages");
- accessible_desc = _("Messages");
- }
+ menu = gtk_menu_new_from_model (G_MENU_MODEL (self->menu_wrapper));
+ gtk_widget_insert_action_group (menu, get_name_hint (io), self->actions);
- update_a11y_desc();
-
- return;
+ return GTK_MENU (menu);
}
-/* Change from getting the icon visibility from the service */
-static void
-icon_cb (GObject * object, GAsyncResult * ares, gpointer user_data)
+static const gchar *
+get_accessible_desc (IndicatorObject * io)
{
- GError * error = NULL;
- GVariant * res = g_dbus_proxy_call_finish(G_DBUS_PROXY(object), ares, &error);
-
- if (error != NULL) {
- g_warning("Unable to get icon visibility: %s", error->message);
- g_error_free(error);
- return;
- }
-
- gboolean prop = g_variant_get_boolean(g_variant_get_child_value(res, 0));
-
- if (prop) {
- gtk_widget_hide(main_image);
- } else {
- gtk_widget_show(main_image);
- }
-
- return;
+ IndicatorMessages *self = INDICATOR_MESSAGES (io);
+ return self->accessible_desc;
}
-static guint connection_drop_timeout = 0;
-
-/* Resets the icon to not having messages if we can't get a good
- answer on it from the service. */
-static gboolean
-connection_drop_cb (gpointer user_data)
+static const gchar *
+get_name_hint (IndicatorObject *io)
{
- if (main_image != NULL) {
- indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages");
- }
- connection_drop_timeout = 0;
- return FALSE;
+ return PACKAGE;
}
-/* Proxy is setup now.. whoo! */
static void
-proxy_ready_cb (GObject * obj, GAsyncResult * res, gpointer user_data)
+indicator_messages_accessible_desc_updated (IndicatorMessages *self)
{
- GError * error = NULL;
- GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
+ GList *entries;
- if (error != NULL) {
- g_warning("Unable to get proxy of service: %s", error->message);
- g_error_free(error);
- return;
- }
+ entries = indicator_object_get_entries (INDICATOR_OBJECT (self));
+ g_return_if_fail (entries != NULL);
- icon_proxy = proxy;
-
- g_signal_connect(G_OBJECT(proxy), "g-signal", G_CALLBACK(proxy_signal), user_data);
-
- g_dbus_proxy_call(icon_proxy,
- "AttentionRequested",
- NULL, /* params */
- G_DBUS_CALL_FLAGS_NONE,
- -1, /* timeout */
- NULL, /* cancel */
- attention_cb,
- user_data);
- g_dbus_proxy_call(icon_proxy,
- "IconShown",
- NULL, /* params */
- G_DBUS_CALL_FLAGS_NONE,
- -1, /* timeout */
- NULL, /* cancel */
- icon_cb,
- user_data);
+ g_signal_emit_by_name (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, entries->data);
- return;
+ g_list_free (entries);
}
-/* Sets up all the icon information in the proxy. */
-static void
-connection_change (IndicatorServiceManager * sm, gboolean connected, gpointer user_data)
+static void
+update_root_item (IndicatorMessages * self)
{
- if (connection_drop_timeout != 0) {
- g_source_remove(connection_drop_timeout);
- connection_drop_timeout = 0;
- }
+ gchar *iconstr;
- if (!connected) {
- /* Ensure that we're not saying there are messages
- when we don't have a connection. */
- connection_drop_timeout = g_timeout_add(400, connection_drop_cb, NULL);
+ if (g_menu_model_get_n_items (self->menu) == 0)
return;
- }
-
- if (icon_proxy == NULL) {
- g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION,
- G_DBUS_PROXY_FLAGS_NONE,
- bus_interface_info,
- INDICATOR_MESSAGES_DBUS_NAME,
- INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT,
- INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE,
- NULL, /* cancel */
- proxy_ready_cb,
- sm);
- } else {
- g_dbus_proxy_call(icon_proxy,
- "AttentionRequested",
- NULL, /* params */
- G_DBUS_CALL_FLAGS_NONE,
- -1, /* timeout */
- NULL, /* cancel */
- attention_cb,
- sm);
- g_dbus_proxy_call(icon_proxy,
- "IconShown",
- NULL, /* params */
- G_DBUS_CALL_FLAGS_NONE,
- -1, /* timeout */
- NULL, /* cancel */
- icon_cb,
- sm);
- }
- return;
-}
+ if (g_menu_model_get_item_attribute (self->menu, 0, "x-canonical-icon", "s", &iconstr)) {
+ GIcon *icon;
+ GError *error;
-/* Sets the icon when it changes. */
-static void
-application_icon_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * value, gpointer user_data)
-{
- if (!g_strcmp0(prop, APPLICATION_MENUITEM_PROP_ICON)) {
- /* Set the main icon */
- if (GTK_IS_IMAGE(user_data)) {
- gtk_image_set_from_icon_name(GTK_IMAGE(user_data), g_variant_get_string(value, NULL), GTK_ICON_SIZE_MENU);
+ icon = g_icon_new_for_string (iconstr, &error);
+ if (icon) {
+ gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon, GTK_ICON_SIZE_MENU);
+ g_object_unref (icon);
}
- }
-
- return;
-}
-
-/* Sets the label when it changes. */
-static void
-application_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * value, gpointer user_data)
-{
- if (!g_strcmp0(prop, APPLICATION_MENUITEM_PROP_NAME)) {
- /* Set the main label */
- if (GTK_IS_LABEL(user_data)) {
- gtk_label_set_text(GTK_LABEL(user_data), g_variant_get_string(value, NULL));
+ else {
+ g_warning ("unable to load icon: %s", error->message);
+ g_error_free (error);
}
- }
- if (!g_strcmp0(prop, APPLICATION_MENUITEM_PROP_RUNNING)) {
- /* TODO: should hide/show the triangle live if the menu was open.
- In practice, this is rarely needed. */
+ g_free (iconstr);
}
- return;
-}
-
-/* Draws a triangle on the left, using fg[STATE_TYPE] color. */
-static gboolean
-application_triangle_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
-{
- GtkAllocation allocation;
- GtkStyle *style;
- int x, y, arrow_width, arrow_height;
-
- if (!GTK_IS_WIDGET (widget)) return FALSE;
- if (!DBUSMENU_IS_MENUITEM (data)) return FALSE;
-
- /* render the triangle indicator only if the application is running */
- if (! dbusmenu_menuitem_property_get_bool (DBUSMENU_MENUITEM(data), APPLICATION_MENUITEM_PROP_RUNNING))
- return FALSE;;
-
- /* get style */
- style = gtk_widget_get_style (widget);
-
- /* set arrow position / dimensions */
- arrow_width = 5; /* the pixel-based reference triangle is 5x9 */
- arrow_height = 9;
- gtk_widget_get_allocation (widget, &allocation);
- x = 0;
- y = allocation.height/2.0 - (double)arrow_height/2.0;
-
- cairo_save (cr);
-
- /* set line width */
- cairo_set_line_width (cr, 1.0);
-
- /* cairo drawing code */
- cairo_move_to (cr, x, y);
- cairo_line_to (cr, x, y + arrow_height);
- cairo_line_to (cr, x + arrow_width, y + (double)arrow_height/2.0);
- cairo_close_path (cr);
- cairo_set_source_rgb (cr, style->fg[gtk_widget_get_state(widget)].red/65535.0,
- style->fg[gtk_widget_get_state(widget)].green/65535.0,
- style->fg[gtk_widget_get_state(widget)].blue/65535.0);
- cairo_fill (cr);
-
- cairo_restore (cr);
+ g_free (self->accessible_desc);
+ self->accessible_desc = NULL;
- return FALSE;
+ g_menu_model_get_item_attribute (self->menu, 0, "x-canonical-accessible-description",
+ "s", &self->accessible_desc);
+ indicator_messages_accessible_desc_updated (self);
}
-static gint
-gtk_widget_get_font_size (GtkWidget *widget)
-{
- const PangoFontDescription *font;
-
- font = gtk_style_context_get_font (gtk_widget_get_style_context (widget),
- gtk_widget_get_state_flags (widget));
-
- return pango_font_description_get_size (font) / PANGO_SCALE;
-}
-
-/* Custom function to draw rounded rectangle with max radius */
static void
-custom_cairo_rounded_rectangle (cairo_t *cr,
- double x, double y, double w, double h)
-{
- double radius = h / 2.0;
-
- cairo_move_to (cr, x+radius, y);
- cairo_arc (cr, x+w-radius, y+radius, radius, M_PI*1.5, M_PI*2);
- cairo_arc (cr, x+w-radius, y+h-radius, radius, 0, M_PI*0.5);
- cairo_arc (cr, x+radius, y+h-radius, radius, M_PI*0.5, M_PI);
- cairo_arc (cr, x+radius, y+radius, radius, M_PI, M_PI*1.5);
-}
-
-/* Draws a rounded rectangle with text inside. */
-static gboolean
-numbers_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
-{
- GtkAllocation allocation;
- GtkStyle *style;
- double x, y, w, h;
- PangoLayout * layout;
- PangoRectangle layout_extents;
- gint font_size = gtk_widget_get_font_size (widget);
- gboolean is_lozenge = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "is-lozenge"));
-
- if (!GTK_IS_WIDGET (widget)) return FALSE;
-
- /* let the label handle the drawing if it's not a lozenge */
- if (!is_lozenge)
- return FALSE;
-
- /* get style */
- style = gtk_widget_get_style (widget);
-
- /* set arrow position / dimensions */
- gtk_widget_get_allocation (widget, &allocation);
- x = 0;
- y = 0;
- w = allocation.width;
- h = allocation.height;
-
- layout = gtk_label_get_layout (GTK_LABEL(widget));
- pango_layout_get_extents (layout, NULL, &layout_extents);
- pango_extents_to_pixels (&layout_extents, NULL);
-
- if (layout_extents.width == 0)
- return TRUE;
-
- cairo_save (cr);
-
- /* set line width */
- cairo_set_line_width (cr, 1.0);
-
- cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
-
- /* cairo drawing code */
- custom_cairo_rounded_rectangle (cr, x - font_size/2.0, y, w + font_size, h);
-
- cairo_set_source_rgba (cr, style->fg[gtk_widget_get_state(widget)].red/65535.0,
- style->fg[gtk_widget_get_state(widget)].green/65535.0,
- style->fg[gtk_widget_get_state(widget)].blue/65535.0, 0.5);
-
- x += (allocation.width - layout_extents.width) / 2.0;
- y += (allocation.height - layout_extents.height) / 2.0;
- cairo_move_to (cr, floor (x), floor (y));
- pango_cairo_layout_path (cr, layout);
- cairo_fill (cr);
-
- cairo_restore (cr);
-
- return TRUE;
-}
-
-/* Builds a menu item representing a running application in the
- messaging menu */
-static gboolean
-new_application_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data)
+update_menu (IndicatorMessages *self)
{
- g_debug ("%s (\"%s\")", __func__, dbusmenu_menuitem_property_get(newitem, APPLICATION_MENUITEM_PROP_NAME));
-
- GtkMenuItem * gmi = GTK_MENU_ITEM(gtk_menu_item_new());
-
- gint padding = 4;
- gtk_widget_style_get(GTK_WIDGET(gmi), "toggle-spacing", &padding, NULL);
+ GMenuModel *popup;
+ GMenuItem *item;
- GtkWidget * hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, padding);
-
- GtkWidget * icon = gtk_image_new_from_icon_name(dbusmenu_menuitem_property_get(newitem, APPLICATION_MENUITEM_PROP_ICON), GTK_ICON_SIZE_MENU);
- gtk_misc_set_alignment(GTK_MISC(icon), 1.0 /* right aligned */, 0.5);
- gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
-
- /* Application name in a label */
- GtkWidget * label = gtk_label_new(dbusmenu_menuitem_property_get(newitem, APPLICATION_MENUITEM_PROP_NAME));
- gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
- gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ if (g_menu_model_get_n_items (self->menu) == 0)
+ return;
- gtk_container_add(GTK_CONTAINER(gmi), hbox);
- gtk_widget_show_all (GTK_WIDGET (gmi));
+ popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU);
+ if (popup == NULL)
+ return;
- /* Attach some of the standard GTK stuff */
- dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent);
+ if (g_menu_model_get_n_items (G_MENU_MODEL (self->menu_wrapper)) == 1)
+ g_menu_remove (self->menu_wrapper, 0);
- /* Make sure we can handle the label changing */
- g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(application_prop_change_cb), label);
- g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(application_icon_change_cb), icon);
- g_signal_connect_after(G_OBJECT (gmi), "draw", G_CALLBACK (application_triangle_draw_cb), newitem);
+ item = g_menu_item_new_section (NULL, popup);
+ g_menu_item_set_attribute (item, "action-namespace",
+ "s", get_name_hint (INDICATOR_OBJECT (self)));
+ g_menu_append_item (self->menu_wrapper, item);
- return TRUE;
+ g_object_unref (item);
+ g_object_unref (popup);
}
-typedef struct _indicator_item_t indicator_item_t;
-struct _indicator_item_t {
- GtkWidget * icon;
- GtkWidget * label;
- GtkWidget * right;
-};
-
-/* Whenever we have a property change on a DbusmenuMenuitem
- we need to be responsive to that. */
static void
-indicator_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * value, indicator_item_t * mi_data)
-{
- if (!g_strcmp0(prop, INDICATOR_MENUITEM_PROP_LABEL)) {
- /* Set the main label */
- gtk_label_set_text(GTK_LABEL(mi_data->label), g_variant_get_string(value, NULL));
- } else if (!g_strcmp0(prop, INDICATOR_MENUITEM_PROP_RIGHT)) {
- /* Set the right label */
- gtk_label_set_text(GTK_LABEL(mi_data->right), g_variant_get_string(value, NULL));
- } else if (!g_strcmp0(prop, INDICATOR_MENUITEM_PROP_RIGHT_IS_LOZENGE)) {
- g_object_set_data (G_OBJECT (mi_data->right), "is-lozenge", GINT_TO_POINTER (TRUE));
- gtk_widget_queue_draw (mi_data->right);
- } else if (!g_strcmp0(prop, INDICATOR_MENUITEM_PROP_ICON)) {
- /* We don't use the value here, which is probably less efficient,
- but it's easier to use the easy function. And since th value
- is already cached, shouldn't be a big deal really. */
- GdkPixbuf * pixbuf = dbusmenu_menuitem_property_get_image(mi, INDICATOR_MENUITEM_PROP_ICON);
- if (pixbuf != NULL) {
- /* If we've got a pixbuf we need to make sure it's of a reasonable
- size to fit in the menu. If not, rescale it. */
- GdkPixbuf * resized_pixbuf;
- gint width, height;
- gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
- if (gdk_pixbuf_get_width(pixbuf) > width ||
- gdk_pixbuf_get_height(pixbuf) > height) {
- g_debug("Resizing icon from %dx%d to %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), width, height);
- resized_pixbuf = gdk_pixbuf_scale_simple(pixbuf,
- width,
- height,
- GDK_INTERP_BILINEAR);
- g_object_unref(pixbuf);
- } else {
- g_debug("Happy with icon sized %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
- resized_pixbuf = pixbuf;
- }
-
- gtk_image_set_from_pixbuf(GTK_IMAGE(mi_data->icon), resized_pixbuf);
-
- g_object_unref(resized_pixbuf);
-
- gtk_widget_show(mi_data->icon);
- } else {
- gtk_widget_hide(mi_data->icon);
- }
- }
-
- return;
-}
-
-/* We have a small little menuitem type that handles all
- of the fun stuff for indicators. Mostly this is the
- shifting over and putting the icon in with some right
- side text that'll be determined by the service. */
-static gboolean
-new_indicator_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data)
-{
- g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
- g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
- /* Note: not checking parent, it's reasonable for it to be NULL */
-
- indicator_item_t * mi_data = g_new0(indicator_item_t, 1);
-
- GtkMenuItem * gmi = GTK_MENU_ITEM(gtk_menu_item_new());
-
- gint padding = 4;
- gint font_size = gtk_widget_get_font_size (GTK_WIDGET (gmi));
- gtk_widget_style_get(GTK_WIDGET(gmi), "toggle-spacing", &padding, NULL);
-
- GtkWidget * hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, padding);
-
- /* Icon, probably someone's face or avatar on an IM */
- mi_data->icon = gtk_image_new();
-
- /* Set the minimum size, we always want it to take space */
- gint width, height;
- gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
- gtk_widget_set_size_request(GTK_WIDGET (gmi), -1, height + 4);
-
- gtk_widget_set_margin_left (hbox, width + padding);
-
- GdkPixbuf * pixbuf = dbusmenu_menuitem_property_get_image(newitem, INDICATOR_MENUITEM_PROP_ICON);
- if (pixbuf != NULL) {
- /* If we've got a pixbuf we need to make sure it's of a reasonable
- size to fit in the menu. If not, rescale it. */
- GdkPixbuf * resized_pixbuf;
- if (gdk_pixbuf_get_width(pixbuf) > width ||
- gdk_pixbuf_get_height(pixbuf) > height) {
- g_debug("Resizing icon from %dx%d to %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), width, height);
- resized_pixbuf = gdk_pixbuf_scale_simple(pixbuf,
- width,
- height,
- GDK_INTERP_BILINEAR);
- g_object_unref(pixbuf);
- } else {
- g_debug("Happy with icon sized %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
- resized_pixbuf = pixbuf;
- }
-
- gtk_image_set_from_pixbuf(GTK_IMAGE(mi_data->icon), resized_pixbuf);
-
- g_object_unref(resized_pixbuf);
- }
- gtk_misc_set_alignment(GTK_MISC(mi_data->icon), 0.0, 0.5);
- gtk_box_pack_start(GTK_BOX(hbox), mi_data->icon, FALSE, FALSE, 0);
-
- if (pixbuf != NULL) {
- gtk_widget_show(mi_data->icon);
- }
-
- /* Label, probably a username, chat room or mailbox name */
- mi_data->label = gtk_label_new(dbusmenu_menuitem_property_get(newitem, INDICATOR_MENUITEM_PROP_LABEL));
- gtk_misc_set_alignment(GTK_MISC(mi_data->label), 0.0, 0.5);
- gtk_box_pack_start(GTK_BOX(hbox), mi_data->label, TRUE, TRUE, 0);
- gtk_widget_show(mi_data->label);
-
- /* Usually either the time or the count on the individual
- item. */
- mi_data->right = gtk_label_new(dbusmenu_menuitem_property_get(newitem, INDICATOR_MENUITEM_PROP_RIGHT));
- g_object_set_data (G_OBJECT (mi_data->right),
- "is-lozenge",
- GINT_TO_POINTER (dbusmenu_menuitem_property_get_bool (newitem, INDICATOR_MENUITEM_PROP_RIGHT_IS_LOZENGE)));
- /* install extra decoration overlay */
- g_signal_connect (G_OBJECT (mi_data->right), "draw",
- G_CALLBACK (numbers_draw_cb), NULL);
-
- gtk_misc_set_alignment(GTK_MISC(mi_data->right), 1.0, 0.5);
- gtk_box_pack_start(GTK_BOX(hbox), mi_data->right, FALSE, FALSE, padding + font_size/2.0);
- gtk_label_set_width_chars (GTK_LABEL (mi_data->right), 2);
- gtk_style_context_add_class (gtk_widget_get_style_context (mi_data->right),
- "accelerator");
- gtk_widget_show(mi_data->right);
-
- gtk_container_add(GTK_CONTAINER(gmi), hbox);
- gtk_widget_show(hbox);
-
- dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent);
-
- g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(indicator_prop_change_cb), mi_data);
- g_object_weak_ref(G_OBJECT(newitem), (GWeakNotify)g_free, mi_data);
-
- return TRUE;
-}
-
-/* Builds the main image icon using the libindicator helper. */
-static GtkImage *
-get_icon (IndicatorObject * io)
-{
- main_image = GTK_WIDGET(indicator_image_helper("indicator-messages"));
- gtk_widget_show(main_image);
-
- return GTK_IMAGE(main_image);
-}
-
-/* Builds the menu for the indicator */
-static GtkMenu *
-get_menu (IndicatorObject * io)
-{
- DbusmenuGtkMenu * menu = dbusmenu_gtkmenu_new(INDICATOR_MESSAGES_DBUS_NAME, INDICATOR_MESSAGES_DBUS_OBJECT);
- DbusmenuGtkClient * client = dbusmenu_gtkmenu_get_client(menu);
-
- dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), INDICATOR_MENUITEM_TYPE, new_indicator_item);
- dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), APPLICATION_MENUITEM_TYPE, new_application_item);
-
- return GTK_MENU(menu);
-}
-
-/* Returns the accessible description of the indicator */
-static const gchar *
-get_accessible_desc (IndicatorObject * io)
-{
- return accessible_desc;
-}
-
-/* Returns the name hint of the indicator */
-static const gchar *
-get_name_hint (IndicatorObject *io)
+menu_items_changed (GMenuModel *menu,
+ gint position,
+ gint removed,
+ gint added,
+ gpointer user_data)
{
- return PACKAGE;
-}
+ IndicatorMessages *self = user_data;
-/* Hide the notifications on middle-click over the indicator-messages */
-static void
-indicator_messages_middle_click (IndicatorObject * io, IndicatorObjectEntry * entry,
- guint time, gpointer data)
-{
- if (icon_proxy == NULL) {
- return;
+ if (position == 0) {
+ update_root_item (self);
+ update_menu (self);
}
-
- g_dbus_proxy_call(icon_proxy,
- "ClearAttention",
- NULL, /* params */
- G_DBUS_CALL_FLAGS_NONE,
- -1, /* timeout */
- NULL, /* cancel */
- NULL,
- NULL);
-
- return;
}