aboutsummaryrefslogtreecommitdiff
path: root/src/app-indicator.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/app-indicator.c')
-rw-r--r--src/app-indicator.c879
1 files changed, 572 insertions, 307 deletions
diff --git a/src/app-indicator.c b/src/app-indicator.c
index 98663c1..babdaf5 100644
--- a/src/app-indicator.c
+++ b/src/app-indicator.c
@@ -31,22 +31,18 @@ License version 3 and version 2.1 along with this program. If not, see
#include "config.h"
#endif
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-bindings.h>
-
+#include <libdbusmenu-glib/menuitem.h>
#include <libdbusmenu-glib/server.h>
-#ifdef HAVE_GTK3
-#include <libdbusmenu-gtk3/client.h>
-#else
#include <libdbusmenu-gtk/client.h>
-#endif
+
+#include <libindicator/indicator-desktop-shortcuts.h>
#include "app-indicator.h"
#include "app-indicator-enum-types.h"
#include "application-service-marshal.h"
-#include "notification-item-server.h"
-#include "notification-watcher-client.h"
+#include "gen-notification-watcher.xml.h"
+#include "gen-notification-item.xml.h"
#include "dbus-shared.h"
#include "generate-id.h"
@@ -89,9 +85,13 @@ struct _AppIndicatorPrivate {
gint fallback_timer;
/* Fun stuff */
- DBusGProxy *watcher_proxy;
- DBusGConnection *connection;
- DBusGProxy * dbus_proxy;
+ GDBusProxy *watcher_proxy;
+ GDBusConnection *connection;
+ guint dbus_registration;
+ gchar * path;
+
+ /* Might be used */
+ IndicatorDesktopShortcuts * shorties;
};
/* Signals Stuff */
@@ -100,7 +100,6 @@ enum {
NEW_ATTENTION_ICON,
NEW_STATUS,
NEW_LABEL,
- X_NEW_LABEL,
CONNECTION_CHANGED,
NEW_ICON_THEME_PATH,
LAST_SIGNAL
@@ -118,14 +117,11 @@ enum {
PROP_ICON_NAME,
PROP_ATTENTION_ICON_NAME,
PROP_ICON_THEME_PATH,
- PROP_MENU,
PROP_CONNECTED,
PROP_LABEL,
PROP_LABEL_GUIDE,
- PROP_X_LABEL,
- PROP_X_LABEL_GUIDE,
PROP_ORDERING_INDEX,
- PROP_X_ORDERING_INDEX
+ PROP_DBUS_MENU_SERVER
};
/* The strings so that they can be slowly looked up. */
@@ -135,28 +131,28 @@ enum {
#define PROP_ICON_NAME_S "icon-name"
#define PROP_ATTENTION_ICON_NAME_S "attention-icon-name"
#define PROP_ICON_THEME_PATH_S "icon-theme-path"
-#define PROP_MENU_S "menu"
#define PROP_CONNECTED_S "connected"
#define PROP_LABEL_S "label"
#define PROP_LABEL_GUIDE_S "label-guide"
-#define PROP_X_LABEL_S ("x-ayatana-" PROP_LABEL_S)
-#define PROP_X_LABEL_GUIDE_S ("x-ayatana-" PROP_LABEL_GUIDE_S)
#define PROP_ORDERING_INDEX_S "ordering-index"
-#define PROP_X_ORDERING_INDEX_S ("x-ayatana-" PROP_ORDERING_INDEX_S)
+#define PROP_DBUS_MENU_SERVER_S "dbus-menu-server"
/* Private macro, shhhh! */
#define APP_INDICATOR_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), APP_INDICATOR_TYPE, AppIndicatorPrivate))
-/* Signal wrapper */
-#define APP_INDICATOR_SIGNAL_X_NEW_LABEL ("x-ayatana-" APP_INDICATOR_SIGNAL_NEW_LABEL)
-
/* Default Path */
#define DEFAULT_ITEM_PATH "/org/ayatana/NotificationItem"
/* More constants */
#define DEFAULT_FALLBACK_TIMER 100 /* in milliseconds */
+/* Globals */
+static GDBusNodeInfo * item_node_info = NULL;
+static GDBusInterfaceInfo * item_interface_info = NULL;
+static GDBusNodeInfo * watcher_node_info = NULL;
+static GDBusInterfaceInfo * watcher_interface_info = NULL;
+
/* Boiler plate */
static void app_indicator_class_init (AppIndicatorClass *klass);
static void app_indicator_init (AppIndicator *self);
@@ -168,7 +164,7 @@ static void app_indicator_get_property (GObject * object, guint prop_id, GValue
/* Other stuff */
static void signal_label_change (AppIndicator * self);
static void check_connect (AppIndicator * self);
-static void register_service_cb (DBusGProxy * proxy, GError * error, gpointer data);
+static void register_service_cb (GObject * obj, GAsyncResult * res, gpointer user_data);
static void start_fallback_timer (AppIndicator * self, gboolean disable_timeout);
static gboolean fallback_timer_expire (gpointer data);
static GtkStatusIcon * fallback (AppIndicator * self);
@@ -177,11 +173,19 @@ static void status_icon_changes (AppIndicator * self, gpointer data);
static void status_icon_activate (GtkStatusIcon * icon, gpointer data);
static void unfallback (AppIndicator * self, GtkStatusIcon * status_icon);
static gchar * append_panel_icon_suffix (const gchar * icon_name);
-static void watcher_proxy_destroyed (GObject * object, gpointer data);
+static void watcher_owner_changed (GObject * obj, GParamSpec * pspec, gpointer user_data);
static void client_menu_changed (GtkWidget *widget, GtkWidget *child, AppIndicator *indicator);
static void submenu_changed (GtkWidget *widget, GtkWidget *child, gpointer data);
-
static void theme_changed_cb (GtkIconTheme * theme, 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_creation (GObject * obj, GAsyncResult * res, gpointer user_data);
+static void bus_watcher_ready (GObject * obj, GAsyncResult * res, gpointer user_data);
+
+static const GDBusInterfaceVTable item_interface_table = {
+ method_call: NULL, /* No methods on this object */
+ get_property: bus_get_prop,
+ set_property: NULL /* No properties that can be set */
+};
/* GObject type */
G_DEFINE_TYPE (AppIndicator, app_indicator, G_TYPE_OBJECT);
@@ -290,19 +294,6 @@ app_indicator_class_init (AppIndicatorClass *klass)
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/**
- AppIndicator:menu:
-
- A method for getting the menu path as a string for DBus.
- */
- g_object_class_install_property(object_class,
- PROP_MENU,
- g_param_spec_boxed (PROP_MENU_S,
- "The object path of the menu on DBus.",
- "A method for getting the menu path as a string for DBus.",
- DBUS_TYPE_G_OBJECT_PATH,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- /**
AppIndicator:connected:
Pretty simple, %TRUE if we have a reasonable expectation of being
@@ -369,46 +360,19 @@ app_indicator_class_init (AppIndicatorClass *klass)
"A way to override the default ordering of the applications by providing a very specific idea of where this entry should be placed.",
0, G_MAXUINT32, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- /**
- AppIndicator:x-ayatana-ordering-index:
-
- A wrapper for #AppIndicator:ordering-index so that it can match the
- dbus interface currently. It will hopefully be retired, please don't
- use it anywhere.
- */
- g_object_class_install_property(object_class,
- PROP_X_ORDERING_INDEX,
- g_param_spec_uint (PROP_X_ORDERING_INDEX_S,
- "A wrapper, please don't use.",
- "A wrapper, please don't use.",
- 0, G_MAXUINT32, 0,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
- AppIndicator:x-ayatana-label:
+ AppIndicator:dbus-menu-server:
- Wrapper for #AppIndicator:label. Please use that in all of your
- code.
+ A way to get the internal dbusmenu server if it is available.
+ This should only be used for testing.
*/
g_object_class_install_property(object_class,
- PROP_X_LABEL,
- g_param_spec_string (PROP_X_LABEL_S,
- "A wrapper, please don't use.",
- "A wrapper, please don't use.",
- NULL,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- /**
- AppIndicator:x-ayatana-label-guide:
-
- Wrapper for #AppIndicator:label-guide. Please use that in all of your
- code.
- */
- g_object_class_install_property(object_class,
- PROP_X_LABEL_GUIDE,
- g_param_spec_string (PROP_X_LABEL_GUIDE_S,
- "A wrapper, please don't use.",
- "A wrapper, please don't use.",
- NULL,
+ PROP_DBUS_MENU_SERVER,
+ g_param_spec_object (PROP_DBUS_MENU_SERVER_S,
+ "The internal DBusmenu Server",
+ "DBusmenu server which is available for testing the application indicators.",
+ DBUSMENU_TYPE_SERVER,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/* Signals */
@@ -474,22 +438,6 @@ app_indicator_class_init (AppIndicatorClass *klass)
_application_service_marshal_VOID__STRING_STRING,
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
- /**
- AppIndicator::x-ayatana-new-label:
- @arg0: The #AppIndicator object
- @arg1: The string for the label
- @arg1: The string for the guide
-
- Wrapper for #AppIndicator::new-label, please don't use this signal
- use the other one.
- */
- signals[X_NEW_LABEL] = g_signal_new (APP_INDICATOR_SIGNAL_X_NEW_LABEL,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (AppIndicatorClass, new_label),
- NULL, NULL,
- _application_service_marshal_VOID__STRING_STRING,
- G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
/**
AppIndicator::connection-changed:
@@ -521,9 +469,42 @@ app_indicator_class_init (AppIndicatorClass *klass)
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING);
- /* Initialize the object as a DBus type */
- dbus_g_object_type_install_info(APP_INDICATOR_TYPE,
- &dbus_glib__notification_item_server_object_info);
+ /* DBus interfaces */
+ if (item_node_info == NULL) {
+ GError * error = NULL;
+
+ item_node_info = g_dbus_node_info_new_for_xml(_notification_item, &error);
+ if (error != NULL) {
+ g_error("Unable to parse Notification Item DBus interface: %s", error->message);
+ g_error_free(error);
+ }
+ }
+
+ if (item_interface_info == NULL && item_node_info != NULL) {
+ item_interface_info = g_dbus_node_info_lookup_interface(item_node_info, NOTIFICATION_ITEM_DBUS_IFACE);
+
+ if (item_interface_info == NULL) {
+ g_error("Unable to find interface '" NOTIFICATION_ITEM_DBUS_IFACE "'");
+ }
+ }
+
+ if (watcher_node_info == NULL) {
+ GError * error = NULL;
+
+ watcher_node_info = g_dbus_node_info_new_for_xml(_notification_watcher, &error);
+ if (error != NULL) {
+ g_error("Unable to parse Notification Item DBus interface: %s", error->message);
+ g_error_free(error);
+ }
+ }
+
+ if (watcher_interface_info == NULL && watcher_node_info != NULL) {
+ watcher_interface_info = g_dbus_node_info_lookup_interface(watcher_node_info, NOTIFICATION_WATCHER_DBUS_IFACE);
+
+ if (watcher_interface_info == NULL) {
+ g_error("Unable to find interface '" NOTIFICATION_WATCHER_DBUS_IFACE "'");
+ }
+ }
return;
}
@@ -549,20 +530,17 @@ app_indicator_init (AppIndicator *self)
priv->watcher_proxy = NULL;
priv->connection = NULL;
- priv->dbus_proxy = NULL;
+ priv->dbus_registration = 0;
+ priv->path = NULL;
priv->status_icon = NULL;
priv->fallback_timer = 0;
- /* Put the object on DBus */
- GError * error = NULL;
- priv->connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
- if (error != NULL) {
- g_error("Unable to connect to the session bus when creating application indicator: %s", error->message);
- g_error_free(error);
- return;
- }
- dbus_g_connection_ref(priv->connection);
+ priv->shorties = NULL;
+
+ /* 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);
g_signal_connect(G_OBJECT(gtk_icon_theme_get_default()),
"changed", G_CALLBACK(theme_changed_cb), self);
@@ -580,6 +558,16 @@ app_indicator_dispose (GObject *object)
AppIndicator *self = APP_INDICATOR (object);
AppIndicatorPrivate *priv = self->priv;
+ if (priv->dbus_registration != 0) {
+ g_dbus_connection_unregister_object(priv->connection, priv->dbus_registration);
+ priv->dbus_registration = 0;
+ }
+
+ if (priv->shorties != NULL) {
+ g_object_unref(G_OBJECT(priv->shorties));
+ priv->shorties = NULL;
+ }
+
if (priv->status != APP_INDICATOR_STATUS_PASSIVE) {
app_indicator_set_status(self, APP_INDICATOR_STATUS_PASSIVE);
}
@@ -610,18 +598,12 @@ app_indicator_dispose (GObject *object)
priv->menu = NULL;
}
- if (priv->menuservice != NULL) {
- g_object_unref (priv->menuservice);
- }
-
- if (priv->dbus_proxy != NULL) {
- g_object_unref(G_OBJECT(priv->dbus_proxy));
- priv->dbus_proxy = NULL;
+ if (priv->menuservice != NULL) {
+ g_object_unref (priv->menuservice);
}
if (priv->watcher_proxy != NULL) {
- dbus_g_connection_flush(priv->connection);
- g_signal_handlers_disconnect_by_func(G_OBJECT(priv->watcher_proxy), watcher_proxy_destroyed, self);
+ g_signal_handlers_disconnect_by_func(G_OBJECT(priv->watcher_proxy), watcher_owner_changed, self);
g_object_unref(G_OBJECT(priv->watcher_proxy));
priv->watcher_proxy = NULL;
@@ -630,7 +612,7 @@ app_indicator_dispose (GObject *object)
}
if (priv->connection != NULL) {
- dbus_g_connection_unref(priv->connection);
+ g_object_unref(G_OBJECT(priv->connection));
priv->connection = NULL;
}
@@ -643,8 +625,8 @@ app_indicator_dispose (GObject *object)
static void
app_indicator_finalize (GObject *object)
{
- AppIndicator * self = APP_INDICATOR(object);
- AppIndicatorPrivate *priv = self->priv;
+ AppIndicator * self = APP_INDICATOR(object);
+ AppIndicatorPrivate *priv = self->priv;
if (priv->status != APP_INDICATOR_STATUS_PASSIVE) {
g_warning("Finalizing Application Status with the status set to: %d", priv->status);
@@ -685,6 +667,11 @@ app_indicator_finalize (GObject *object)
priv->label_guide = NULL;
}
+ if (priv->path != NULL) {
+ g_free(priv->path);
+ priv->path = NULL;
+ }
+
G_OBJECT_CLASS (app_indicator_parent_class)->finalize (object);
return;
}
@@ -755,7 +742,6 @@ app_indicator_set_property (GObject * object, guint prop_id, const GValue * valu
check_connect (self);
break;
- case PROP_X_LABEL:
case PROP_LABEL: {
gchar * oldlabel = priv->label;
priv->label = g_value_dup_string(value);
@@ -774,7 +760,6 @@ app_indicator_set_property (GObject * object, guint prop_id, const GValue * valu
}
break;
}
- case PROP_X_LABEL_GUIDE:
case PROP_LABEL_GUIDE: {
gchar * oldguide = priv->label_guide;
priv->label_guide = g_value_dup_string(value);
@@ -793,11 +778,18 @@ app_indicator_set_property (GObject * object, guint prop_id, const GValue * valu
}
break;
}
- case PROP_X_ORDERING_INDEX:
case PROP_ORDERING_INDEX:
priv->ordering_index = g_value_get_uint(value);
break;
+ case PROP_DBUS_MENU_SERVER:
+ if (priv->menuservice != NULL) {
+ g_object_unref (priv->menuservice);
+ }
+ gpointer val = g_value_dup_object(value);
+ priv->menuservice = DBUSMENU_SERVER(val);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -841,35 +833,37 @@ app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GPa
g_value_set_string (value, priv->icon_theme_path);
break;
- case PROP_MENU:
- if (priv->menuservice != NULL) {
- GValue strval = { 0 };
- g_value_init(&strval, G_TYPE_STRING);
- g_object_get_property (G_OBJECT (priv->menuservice), DBUSMENU_SERVER_PROP_DBUS_OBJECT, &strval);
- g_value_set_boxed(value, g_value_get_string(&strval));
- g_value_unset(&strval);
- }
- break;
+ case PROP_CONNECTED: {
+ gboolean connected = FALSE;
- case PROP_CONNECTED:
- g_value_set_boolean (value, priv->watcher_proxy != NULL ? TRUE : FALSE);
- break;
+ if (priv->watcher_proxy != NULL) {
+ gchar * name = g_dbus_proxy_get_name_owner(priv->watcher_proxy);
+ if (name != NULL) {
+ connected = TRUE;
+ g_free(name);
+ }
+ }
+
+ g_value_set_boolean (value, connected);
+ break;
+ }
- case PROP_X_LABEL:
case PROP_LABEL:
g_value_set_string (value, priv->label);
break;
- case PROP_X_LABEL_GUIDE:
case PROP_LABEL_GUIDE:
g_value_set_string (value, priv->label_guide);
break;
- case PROP_X_ORDERING_INDEX:
case PROP_ORDERING_INDEX:
g_value_set_uint(value, priv->ordering_index);
break;
+ case PROP_DBUS_MENU_SERVER:
+ g_value_set_object(value, priv->menuservice);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -878,6 +872,81 @@ app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GPa
return;
}
+/* DBus bus has been created, well maybe, but we got a call
+ back about it so we need to check into it. */
+static void
+bus_creation (GObject * obj, GAsyncResult * res, gpointer user_data)
+{
+ GError * error = NULL;
+
+ GDBusConnection * connection = g_bus_get_finish(res, &error);
+ if (error != NULL) {
+ g_warning("Unable to get the session bus: %s", error->message);
+ g_error_free(error);
+ g_object_unref(G_OBJECT(user_data));
+ return;
+ }
+
+ AppIndicator * app = APP_INDICATOR(user_data);
+ app->priv->connection = connection;
+
+ /* If the connection was blocking the exporting of the
+ object this function will export everything. */
+ check_connect(app);
+
+ g_object_unref(G_OBJECT(app));
+
+ return;
+}
+
+/* DBus is asking for a property so we should figure out what it
+ wants and try and deliver. */
+static GVariant *
+bus_get_prop (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * property, GError ** error, gpointer user_data)
+{
+ g_return_val_if_fail(IS_APP_INDICATOR(user_data), NULL);
+ AppIndicator * app = APP_INDICATOR(user_data);
+ AppIndicatorPrivate *priv = app->priv;
+
+ if (g_strcmp0(property, "Id") == 0) {
+ return g_variant_new_string(app->priv->id);
+ } else if (g_strcmp0(property, "Category") == 0) {
+ GEnumValue *enum_value;
+ enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_CATEGORY), priv->category);
+ return g_variant_new_string(enum_value->value_nick);
+ } else if (g_strcmp0(property, "Status") == 0) {
+ GEnumValue *enum_value;
+ enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS), priv->status);
+ return g_variant_new_string(enum_value->value_nick);
+ } else if (g_strcmp0(property, "IconName") == 0) {
+ return g_variant_new_string(priv->icon_name);
+ } else if (g_strcmp0(property, "AttentionIconName") == 0) {
+ return g_variant_new_string(priv->attention_icon_name);
+ } else if (g_strcmp0(property, "IconThemePath") == 0) {
+ return g_variant_new_string(priv->icon_theme_path);
+ } else if (g_strcmp0(property, "Menu") == 0) {
+ if (priv->menuservice != NULL) {
+ GValue strval = { 0 };
+ g_value_init(&strval, G_TYPE_STRING);
+ g_object_get_property (G_OBJECT (priv->menuservice), DBUSMENU_SERVER_PROP_DBUS_OBJECT, &strval);
+ GVariant * var = g_variant_new("o", g_value_get_string(&strval));
+ g_value_unset(&strval);
+ return var;
+ } else {
+ return g_variant_new("o", "/");
+ }
+ } else if (g_strcmp0(property, "XAyatanaLabel") == 0) {
+ return g_variant_new_string(priv->label);
+ } else if (g_strcmp0(property, "XAyatanaLabelGuide") == 0) {
+ return g_variant_new_string(priv->label_guide);
+ } else if (g_strcmp0(property, "XAyatanaOrderingIndex") == 0) {
+ return g_variant_new_uint32(priv->ordering_index);
+ }
+
+ *error = g_error_new(0, 0, "Unknown property: %s", property);
+ return NULL;
+}
+
/* Sends the label changed signal and resets the source ID */
static gboolean
signal_label_change_idle (gpointer user_data)
@@ -885,14 +954,27 @@ signal_label_change_idle (gpointer user_data)
AppIndicator * self = (AppIndicator *)user_data;
AppIndicatorPrivate *priv = self->priv;
+ gchar * label = priv->label != NULL ? priv->label : "";
+ gchar * guide = priv->label_guide != NULL ? priv->label_guide : "";
+
g_signal_emit(G_OBJECT(self), signals[NEW_LABEL], 0,
- priv->label != NULL ? priv->label : "",
- priv->label_guide != NULL ? priv->label_guide : "",
- TRUE);
- g_signal_emit(G_OBJECT(self), signals[X_NEW_LABEL], 0,
- priv->label != NULL ? priv->label : "",
- priv->label_guide != NULL ? priv->label_guide : "",
- TRUE);
+ label, guide, TRUE);
+ if (priv->dbus_registration != 0 && priv->connection != NULL) {
+ GError * error = NULL;
+
+ g_dbus_connection_emit_signal(priv->connection,
+ NULL,
+ priv->path,
+ NOTIFICATION_ITEM_DBUS_IFACE,
+ "XAyatanaNewLabel",
+ g_variant_new("(ss)", label, guide),
+ &error);
+
+ if (error != NULL) {
+ g_warning("Unable to send signal for NewIcon: %s", error->message);
+ g_error_free(error);
+ }
+ }
priv->label_change_idle = 0;
@@ -923,93 +1005,197 @@ check_connect (AppIndicator *self)
{
AppIndicatorPrivate *priv = self->priv;
- /* We're alreadying connecting or trying to connect. */
- if (priv->watcher_proxy != NULL) return;
+ /* Do we have a connection? */
+ if (priv->connection == NULL) return;
+
+ /* If we already have a proxy, let's see if it has someone
+ implementing it. If not, we can't do much more than to
+ do nothing. */
+ if (priv->watcher_proxy != NULL) {
+ gchar * name = g_dbus_proxy_get_name_owner(priv->watcher_proxy);
+ if (name == NULL) {
+ return;
+ }
+ g_free(name);
+ }
/* Do we have enough information? */
if (priv->menu == NULL) return;
if (priv->icon_name == NULL) return;
if (priv->id == NULL) return;
- gchar * path = g_strdup_printf(DEFAULT_ITEM_PATH "/%s", priv->clean_id);
+ if (priv->path == NULL) {
+ priv->path = g_strdup_printf(DEFAULT_ITEM_PATH "/%s", priv->clean_id);
+ }
+
+ if (priv->dbus_registration == 0) {
+ GError * error = NULL;
+ priv->dbus_registration = g_dbus_connection_register_object(priv->connection,
+ priv->path,
+ item_interface_info,
+ &item_interface_table,
+ self,
+ NULL,
+ &error);
+ if (error != NULL) {
+ g_warning("Unable to register object on path '%s': %s", priv->path, error->message);
+ g_error_free(error);
+ return;
+ }
+ }
- dbus_g_connection_register_g_object(priv->connection,
- path,
- G_OBJECT(self));
+ /* NOTE: It's really important the order here. We make sure to *publish*
+ the object on the bus and *then* get the proxy. The reason is that we
+ want to ensure all the filters are setup before talking to the watcher
+ and that's where the order is important. */
+
+ g_object_ref(G_OBJECT(self)); /* Unref in watcher_ready() */
+ if (priv->watcher_proxy == NULL) {
+ /* Build Watcher Proxy */
+ g_dbus_proxy_new(priv->connection,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, /* We don't use these, don't bother with them */
+ watcher_interface_info,
+ NOTIFICATION_WATCHER_DBUS_ADDR,
+ NOTIFICATION_WATCHER_DBUS_OBJ,
+ NOTIFICATION_WATCHER_DBUS_IFACE,
+ NULL, /* cancellable */
+ bus_watcher_ready,
+ self);
+ } else {
+ bus_watcher_ready(NULL, NULL, self);
+ }
+ return;
+}
+
+/* Callback for when the watcher proxy has been created, or not
+ but we got called none-the-less. */
+static void
+bus_watcher_ready (GObject * obj, GAsyncResult * res, gpointer user_data)
+{
GError * error = NULL;
- priv->watcher_proxy = dbus_g_proxy_new_for_name_owner(priv->connection,
- NOTIFICATION_WATCHER_DBUS_ADDR,
- NOTIFICATION_WATCHER_DBUS_OBJ,
- NOTIFICATION_WATCHER_DBUS_IFACE,
- &error);
+
+ GDBusProxy * proxy = NULL;
+ if (res != NULL) {
+ proxy = g_dbus_proxy_new_finish(res, &error);
+ }
+
if (error != NULL) {
/* Unable to get proxy, but we're handling that now so
it's not a warning anymore. */
g_error_free(error);
- dbus_g_connection_unregister_g_object(priv->connection,
- G_OBJECT(self));
- start_fallback_timer(self, FALSE);
- g_free(path);
+
+ if (IS_APP_INDICATOR(user_data)) {
+ start_fallback_timer(APP_INDICATOR(user_data), FALSE);
+ }
+
+ g_object_unref(G_OBJECT(user_data));
return;
}
- g_signal_connect(G_OBJECT(priv->watcher_proxy), "destroy", G_CALLBACK(watcher_proxy_destroyed), self);
- org_kde_StatusNotifierWatcher_register_status_notifier_item_async(priv->watcher_proxy, path, register_service_cb, self);
- g_free(path);
+ AppIndicator * app = APP_INDICATOR(user_data);
- /* Emit the AppIndicator::connection-changed signal*/
- g_signal_emit (self, signals[CONNECTION_CHANGED], 0, TRUE);
+ if (res != NULL) {
+ app->priv->watcher_proxy = proxy;
+
+ /* Setting up a signal to watch when the unique name
+ changes */
+ g_signal_connect(G_OBJECT(app->priv->watcher_proxy), "notify::g-name-owner", G_CALLBACK(watcher_owner_changed), user_data);
+ }
+
+ /* Let's insure that someone is on the other side, else we're
+ still in a fallback scenario. */
+ gchar * name = g_dbus_proxy_get_name_owner(app->priv->watcher_proxy);
+ if (name == NULL) {
+ start_fallback_timer(APP_INDICATOR(user_data), FALSE);
+ g_object_unref(G_OBJECT(user_data));
+ return;
+ }
+
+ /* g_object_unref(G_OBJECT(user_data)); */
+ /* Why is this commented out? Oh, wait, we don't want to
+ unref in this case because we need to ref again to do the
+ register callback. Let's not unref to ref again. */
+
+ g_dbus_proxy_call(app->priv->watcher_proxy,
+ "RegisterStatusNotifierItem",
+ g_variant_new("(s)", app->priv->path),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL, /* cancelable */
+ register_service_cb,
+ user_data);
return;
}
-/* A function that gets called when the watcher dies. Like
- dies dies. Not our friend anymore. */
+/* Watching for when the name owner changes on the interface
+ to know whether we should be connected or not. */
static void
-watcher_proxy_destroyed (GObject * object, gpointer data)
+watcher_owner_changed (GObject * obj, GParamSpec * pspec, gpointer user_data)
{
- AppIndicator * self = APP_INDICATOR(data);
+ AppIndicator * self = APP_INDICATOR(user_data);
g_return_if_fail(self != NULL);
+ g_return_if_fail(self->priv->watcher_proxy != NULL);
- dbus_g_connection_unregister_g_object(self->priv->connection,
- G_OBJECT(self));
- self->priv->watcher_proxy = NULL;
+ gchar * name = g_dbus_proxy_get_name_owner(self->priv->watcher_proxy);
+
+ if (name == NULL) {
+ /* Emit the AppIndicator::connection-changed signal*/
+ g_signal_emit (self, signals[CONNECTION_CHANGED], 0, FALSE);
+
+ start_fallback_timer(self, FALSE);
+ } else {
+ if (self->priv->fallback_timer != 0) {
+ /* Stop the timer */
+ g_source_remove(self->priv->fallback_timer);
+ self->priv->fallback_timer = 0;
+ }
+
+ check_connect(self);
+ }
- /* Emit the AppIndicator::connection-changed signal*/
- g_signal_emit (self, signals[CONNECTION_CHANGED], 0, FALSE);
-
- start_fallback_timer(self, FALSE);
return;
}
/* Responce from the DBus command to register a service
with a NotificationWatcher. */
static void
-register_service_cb (DBusGProxy * proxy, GError * error, gpointer data)
+register_service_cb (GObject * obj, GAsyncResult * res, gpointer user_data)
{
- g_return_if_fail(IS_APP_INDICATOR(data));
- AppIndicatorPrivate * priv = APP_INDICATOR(data)->priv;
+ GError * error = NULL;
+ GVariant * returns = g_dbus_proxy_call_finish(G_DBUS_PROXY(obj), res, &error);
+
+ /* We don't care about any return values */
+ if (returns != NULL) {
+ g_variant_unref(returns);
+ }
if (error != NULL) {
/* They didn't respond, ewww. Not sure what they could
be doing */
g_warning("Unable to connect to the Notification Watcher: %s", error->message);
- dbus_g_connection_unregister_g_object(priv->connection,
- G_OBJECT(data));
- g_object_unref(G_OBJECT(priv->watcher_proxy));
- priv->watcher_proxy = NULL;
- start_fallback_timer(APP_INDICATOR(data), TRUE);
+ start_fallback_timer(APP_INDICATOR(user_data), TRUE);
+ g_object_unref(G_OBJECT(user_data));
+ return;
}
+ g_return_if_fail(IS_APP_INDICATOR(user_data));
+ AppIndicator * app = APP_INDICATOR(user_data);
+ AppIndicatorPrivate * priv = app->priv;
+
+ /* Emit the AppIndicator::connection-changed signal*/
+ g_signal_emit (app, signals[CONNECTION_CHANGED], 0, TRUE);
+
if (priv->status_icon) {
- AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(data);
+ AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(app);
if (class->unfallback != NULL) {
- class->unfallback(APP_INDICATOR(data), priv->status_icon);
+ class->unfallback(app, priv->status_icon);
priv->status_icon = NULL;
}
}
+ g_object_unref(G_OBJECT(user_data));
return;
}
@@ -1024,89 +1210,6 @@ category_from_enum (AppIndicatorCategory category)
return value->value_nick;
}
-/* Watching the dbus owner change events to see if someone
- we care about pops up! */
-static void
-dbus_owner_change (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, gpointer data)
-{
- if (new == NULL || new[0] == '\0') {
- /* We only care about folks coming on the bus. Exit quickly otherwise. */
- return;
- }
-
- if (g_strcmp0(name, NOTIFICATION_WATCHER_DBUS_ADDR)) {
- /* We only care about this address, reject all others. */
- return;
- }
-
- /* Woot, there's a new notification watcher in town. */
-
- AppIndicatorPrivate * priv = APP_INDICATOR_GET_PRIVATE(data);
-
- if (priv->fallback_timer != 0) {
- /* Stop a timer */
- g_source_remove(priv->fallback_timer);
-
- /* Stop listening to bus events */
- g_object_unref(G_OBJECT(priv->dbus_proxy));
- priv->dbus_proxy = NULL;
- }
-
- /* Let's start from the very beginning */
- check_connect(APP_INDICATOR(data));
-
- return;
-}
-
-/* Checking to see if someone already has the name we're looking for */
-static void
-check_owner_cb (DBusGProxy *proxy, gboolean exists, GError *error, gpointer userdata)
-{
- if (error != NULL) {
- g_warning("Unable to check for '" NOTIFICATION_WATCHER_DBUS_ADDR "' on DBus. No worries, but concerning.");
- return;
- }
-
- if (exists) {
- g_debug("Woah, we actually has a race condition with dbus");
- dbus_owner_change(proxy, NOTIFICATION_WATCHER_DBUS_ADDR, NULL, "Non NULL", userdata);
- }
-
- return;
-}
-
-/* This is an idle function to create the proxy. This is mostly
- because start_fallback_timer can get called in the distruction
- of a proxy and thus the proxy manager gets confused when creating
- a new proxy as part of destroying an old one. This function being
- on idle means that we'll just do it outside of the same stack where
- the previous proxy is being destroyed. */
-static gboolean
-setup_name_owner_proxy (gpointer data)
-{
- g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE);
- AppIndicatorPrivate * priv = APP_INDICATOR(data)->priv;
-
- if (priv->dbus_proxy == NULL) {
- priv->dbus_proxy = dbus_g_proxy_new_for_name(priv->connection,
- DBUS_SERVICE_DBUS,
- DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS);
- dbus_g_proxy_add_signal(priv->dbus_proxy, "NameOwnerChanged",
- G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->dbus_proxy, "NameOwnerChanged",
- G_CALLBACK(dbus_owner_change), data, NULL);
-
- /* Check to see if anyone has the name we're looking for
- just incase we missed it changing. */
-
- org_freedesktop_DBus_name_has_owner_async(priv->dbus_proxy, NOTIFICATION_WATCHER_DBUS_ADDR, check_owner_cb, data);
- }
-
- return FALSE;
-}
-
/* A function that will start the fallback timer if it's not
already started. It sets up the DBus watcher to see if
there is a change. Also, provides an override mode for cases
@@ -1128,11 +1231,6 @@ start_fallback_timer (AppIndicator * self, gboolean disable_timeout)
return;
}
- if (priv->dbus_proxy == NULL) {
- /* NOTE: Read the comment on setup_name_owner_proxy */
- g_idle_add(setup_name_owner_proxy, self);
- }
-
if (disable_timeout) {
fallback_timer_expire(self);
} else {
@@ -1174,6 +1272,28 @@ static void
theme_changed_cb (GtkIconTheme * theme, gpointer user_data)
{
g_signal_emit (user_data, signals[NEW_ICON], 0, TRUE);
+
+ AppIndicator * self = (AppIndicator *)user_data;
+ AppIndicatorPrivate *priv = self->priv;
+
+ if (priv->dbus_registration != 0 && priv->connection != NULL) {
+ GError * error = NULL;
+
+ g_dbus_connection_emit_signal(priv->connection,
+ NULL,
+ priv->path,
+ NOTIFICATION_ITEM_DBUS_IFACE,
+ "NewIcon",
+ NULL,
+ &error);
+
+ if (error != NULL) {
+ g_warning("Unable to send signal for NewIcon: %s", error->message);
+ g_error_free(error);
+ }
+ }
+
+ return;
}
/* Creates a StatusIcon that can be used when the application
@@ -1377,15 +1497,33 @@ app_indicator_new_with_path (const gchar *id,
void
app_indicator_set_status (AppIndicator *self, AppIndicatorStatus status)
{
- g_return_if_fail (IS_APP_INDICATOR (self));
+ g_return_if_fail (IS_APP_INDICATOR (self));
- if (self->priv->status != status)
- {
- GEnumValue *value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS), status);
+ if (self->priv->status != status) {
+ GEnumValue *value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS), status);
- self->priv->status = status;
- g_signal_emit (self, signals[NEW_STATUS], 0, value->value_nick);
- }
+ self->priv->status = status;
+ g_signal_emit (self, signals[NEW_STATUS], 0, value->value_nick);
+
+ if (self->priv->dbus_registration != 0 && self->priv->connection != NULL) {
+ GError * error = NULL;
+
+ g_dbus_connection_emit_signal(self->priv->connection,
+ NULL,
+ self->priv->path,
+ NOTIFICATION_ITEM_DBUS_IFACE,
+ "NewStatus",
+ g_variant_new("(s)", value->value_nick),
+ &error);
+
+ if (error != NULL) {
+ g_warning("Unable to send signal for NewStatus: %s", error->message);
+ g_error_free(error);
+ }
+ }
+ }
+
+ return;
}
/**
@@ -1398,20 +1536,36 @@ app_indicator_set_status (AppIndicator *self, AppIndicatorStatus status)
void
app_indicator_set_attention_icon (AppIndicator *self, const gchar *icon_name)
{
- g_return_if_fail (IS_APP_INDICATOR (self));
- g_return_if_fail (icon_name != NULL);
+ g_return_if_fail (IS_APP_INDICATOR (self));
+ g_return_if_fail (icon_name != NULL);
- if (g_strcmp0 (self->priv->attention_icon_name, icon_name) != 0)
- {
- if (self->priv->attention_icon_name)
- g_free (self->priv->attention_icon_name);
+ if (g_strcmp0 (self->priv->attention_icon_name, icon_name) != 0) {
+ if (self->priv->attention_icon_name)
+ g_free (self->priv->attention_icon_name);
- self->priv->attention_icon_name = g_strdup(icon_name);
+ self->priv->attention_icon_name = g_strdup(icon_name);
- g_signal_emit (self, signals[NEW_ATTENTION_ICON], 0, TRUE);
- }
+ g_signal_emit (self, signals[NEW_ATTENTION_ICON], 0, TRUE);
- return;
+ if (self->priv->dbus_registration != 0 && self->priv->connection != NULL) {
+ GError * error = NULL;
+
+ g_dbus_connection_emit_signal(self->priv->connection,
+ NULL,
+ self->priv->path,
+ NOTIFICATION_ITEM_DBUS_IFACE,
+ "NewAttentionIcon",
+ NULL,
+ &error);
+
+ if (error != NULL) {
+ g_warning("Unable to send signal for NewAttentionIcon: %s", error->message);
+ g_error_free(error);
+ }
+ }
+ }
+
+ return;
}
/**
@@ -1427,20 +1581,36 @@ app_indicator_set_attention_icon (AppIndicator *self, const gchar *icon_name)
void
app_indicator_set_icon (AppIndicator *self, const gchar *icon_name)
{
- g_return_if_fail (IS_APP_INDICATOR (self));
- g_return_if_fail (icon_name != NULL);
+ g_return_if_fail (IS_APP_INDICATOR (self));
+ g_return_if_fail (icon_name != NULL);
- if (g_strcmp0 (self->priv->icon_name, icon_name) != 0)
- {
- if (self->priv->icon_name)
- g_free (self->priv->icon_name);
+ if (g_strcmp0 (self->priv->icon_name, icon_name) != 0) {
+ if (self->priv->icon_name)
+ g_free (self->priv->icon_name);
- self->priv->icon_name = g_strdup(icon_name);
+ self->priv->icon_name = g_strdup(icon_name);
- g_signal_emit (self, signals[NEW_ICON], 0, TRUE);
- }
+ g_signal_emit (self, signals[NEW_ICON], 0, TRUE);
- return;
+ if (self->priv->dbus_registration != 0 && self->priv->connection != NULL) {
+ GError * error = NULL;
+
+ g_dbus_connection_emit_signal(self->priv->connection,
+ NULL,
+ self->priv->path,
+ NOTIFICATION_ITEM_DBUS_IFACE,
+ "NewIcon",
+ NULL,
+ &error);
+
+ if (error != NULL) {
+ g_warning("Unable to send signal for NewIcon: %s", error->message);
+ g_error_free(error);
+ }
+ }
+ }
+
+ return;
}
/**
@@ -1478,19 +1648,35 @@ app_indicator_set_label (AppIndicator *self, const gchar * label, const gchar *
void
app_indicator_set_icon_theme_path (AppIndicator *self, const gchar *icon_theme_path)
{
- g_return_if_fail (IS_APP_INDICATOR (self));
+ g_return_if_fail (IS_APP_INDICATOR (self));
- if (g_strcmp0 (self->priv->icon_theme_path, icon_theme_path) != 0)
- {
- if (self->priv->icon_theme_path != NULL)
- g_free(self->priv->icon_theme_path);
+ if (g_strcmp0 (self->priv->icon_theme_path, icon_theme_path) != 0) {
+ if (self->priv->icon_theme_path != NULL)
+ g_free(self->priv->icon_theme_path);
- self->priv->icon_theme_path = g_strdup(icon_theme_path);
+ self->priv->icon_theme_path = g_strdup(icon_theme_path);
- g_signal_emit (self, signals[NEW_ICON_THEME_PATH], 0, g_strdup(self->priv->icon_theme_path));
- }
+ g_signal_emit (self, signals[NEW_ICON_THEME_PATH], 0, self->priv->icon_theme_path, TRUE);
- return;
+ if (self->priv->dbus_registration != 0 && self->priv->connection != NULL) {
+ GError * error = NULL;
+
+ g_dbus_connection_emit_signal(self->priv->connection,
+ NULL,
+ self->priv->path,
+ NOTIFICATION_ITEM_DBUS_IFACE,
+ "NewIconThemePath",
+ g_variant_new("(s)", self->priv->icon_theme_path),
+ &error);
+
+ if (error != NULL) {
+ g_warning("Unable to send signal for NewIconThemePath: %s", error->message);
+ g_error_free(error);
+ }
+ }
+ }
+
+ return;
}
static void
@@ -2110,9 +2296,88 @@ app_indicator_get_ordering_index (AppIndicator *self)
g_return_val_if_fail (IS_APP_INDICATOR (self), 0);
if (self->priv->ordering_index == 0) {
- return generate_id(self->priv->category, self->priv->id);
+ return _generate_id(self->priv->category, self->priv->id);
} else {
return self->priv->ordering_index;
}
}
+#define APP_INDICATOR_SHORTY_NICK "app-indicator-shorty-nick"
+
+/* Callback when an item from the desktop shortcuts gets
+ called. */
+static void
+shorty_activated_cb (DbusmenuMenuitem * mi, guint timestamp, gpointer user_data)
+{
+ gchar * nick = g_object_get_data(G_OBJECT(mi), APP_INDICATOR_SHORTY_NICK);
+ g_return_if_fail(nick != NULL);
+
+ g_return_if_fail(IS_APP_INDICATOR(user_data));
+ AppIndicator * self = APP_INDICATOR(user_data);
+ AppIndicatorPrivate *priv = self->priv;
+
+ g_return_if_fail(priv->shorties != NULL);
+
+ indicator_desktop_shortcuts_nick_exec(priv->shorties, nick);
+
+ return;
+}
+
+/**
+ app_indicator_build_menu_from_desktop:
+ @self: The #AppIndicator object to use
+ @desktop_file: A path to the desktop file to build the menu from
+ @desktop_profile: Which entries should be used from the desktop file
+
+ This function allows for building the Application Indicator menu
+ from a static desktop file.
+*/
+void
+app_indicator_build_menu_from_desktop (AppIndicator * self, const gchar * desktop_file, const gchar * desktop_profile)
+{
+ g_return_if_fail(IS_APP_INDICATOR(self));
+ AppIndicatorPrivate *priv = self->priv;
+
+ /* Build a new shortcuts object */
+ if (priv->shorties != NULL) {
+ g_object_unref(priv->shorties);
+ priv->shorties = NULL;
+ }
+ priv->shorties = indicator_desktop_shortcuts_new(desktop_file, desktop_profile);
+ g_return_if_fail(priv->shorties != NULL);
+
+ const gchar ** nicks = indicator_desktop_shortcuts_get_nicks(priv->shorties);
+ int nick_num;
+
+ /* Place the items on a dbusmenu */
+ DbusmenuMenuitem * root = dbusmenu_menuitem_new();
+
+ for (nick_num = 0; nicks[nick_num] != NULL; nick_num++) {
+ DbusmenuMenuitem * item = dbusmenu_menuitem_new();
+ g_object_set_data(G_OBJECT(item), APP_INDICATOR_SHORTY_NICK, (gpointer)nicks[nick_num]);
+
+ gchar * name = indicator_desktop_shortcuts_nick_get_name(priv->shorties, nicks[nick_num]);
+ dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, name);
+ g_free(name);
+
+ g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(shorty_activated_cb), self);
+
+ dbusmenu_menuitem_child_append(root, item);
+ }
+
+ /* Swap it if needed */
+ if (priv->menuservice == NULL) {
+ gchar * path = g_strdup_printf(DEFAULT_ITEM_PATH "/%s/Menu", priv->clean_id);
+ priv->menuservice = dbusmenu_server_new (path);
+ g_free(path);
+ }
+
+ dbusmenu_server_set_root (priv->menuservice, root);
+
+ if (priv->menu != NULL) {
+ g_object_unref(G_OBJECT(priv->menu));
+ priv->menu = NULL;
+ }
+
+ return;
+}