diff options
Diffstat (limited to 'src/libappindicator')
-rw-r--r-- | src/libappindicator/app-indicator-enum-types.gen.c.in (renamed from src/libappindicator/app-indicator-enum-types.c.in) | 0 | ||||
-rw-r--r-- | src/libappindicator/app-indicator.c | 532 | ||||
-rw-r--r-- | src/libappindicator/app-indicator.h | 22 |
3 files changed, 511 insertions, 43 deletions
diff --git a/src/libappindicator/app-indicator-enum-types.c.in b/src/libappindicator/app-indicator-enum-types.gen.c.in index 449f3fc..449f3fc 100644 --- a/src/libappindicator/app-indicator-enum-types.c.in +++ b/src/libappindicator/app-indicator-enum-types.gen.c.in diff --git a/src/libappindicator/app-indicator.c b/src/libappindicator/app-indicator.c index bb68cb2..b0f721e 100644 --- a/src/libappindicator/app-indicator.c +++ b/src/libappindicator/app-indicator.c @@ -63,12 +63,17 @@ struct _AppIndicatorPrivate { AppIndicatorStatus status; gchar *icon_name; gchar *attention_icon_name; - DbusmenuServer *menuservice; - GtkWidget *menu; + gchar * icon_path; + DbusmenuServer *menuservice; + GtkWidget *menu; + + GtkStatusIcon * status_icon; + gint fallback_timer; /* Fun stuff */ DBusGProxy *watcher_proxy; DBusGConnection *connection; + DBusGProxy * dbus_proxy; }; /* Signals Stuff */ @@ -91,6 +96,7 @@ enum { PROP_STATUS, PROP_ICON_NAME, PROP_ATTENTION_ICON_NAME, + PROP_ICON_THEME_PATH, PROP_MENU, PROP_CONNECTED }; @@ -101,6 +107,7 @@ enum { #define PROP_STATUS_S "status" #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" @@ -108,6 +115,13 @@ enum { #define APP_INDICATOR_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), APP_INDICATOR_TYPE, AppIndicatorPrivate)) +/* Default Paths */ +#define DEFAULT_ITEM_PATH "/org/ayatana/NotificationItem" +#define DEFAULT_MENU_PATH "/org/ayatana/NotificationItem/Menu" + +/* More constants */ +#define DEFAULT_FALLBACK_TIMER 100 /* in milliseconds */ + /* Boiler plate */ static void app_indicator_class_init (AppIndicatorClass *klass); static void app_indicator_init (AppIndicator *self); @@ -119,6 +133,14 @@ static void app_indicator_get_property (GObject * object, guint prop_id, GValue /* Other stuff */ static void check_connect (AppIndicator * self); static void register_service_cb (DBusGProxy * proxy, GError * error, gpointer data); +static void start_fallback_timer (AppIndicator * self, gboolean disable_timeout); +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 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 void watcher_proxy_destroyed (GObject * object, gpointer data); /* GObject type */ G_DEFINE_TYPE (AppIndicator, app_indicator, G_TYPE_OBJECT); @@ -138,6 +160,10 @@ app_indicator_class_init (AppIndicatorClass *klass) object_class->set_property = app_indicator_set_property; object_class->get_property = app_indicator_get_property; + /* Our own funcs */ + klass->fallback = fallback; + klass->unfallback = unfallback; + /* Properties */ g_object_class_install_property (object_class, PROP_ID, @@ -179,6 +205,14 @@ app_indicator_class_init (AppIndicatorClass *klass) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property(object_class, + PROP_ICON_THEME_PATH, + g_param_spec_string (PROP_ICON_THEME_PATH_S, + "An additional path for custom icons.", + "An additional place to look for icon names that may be installed by the application.", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property(object_class, PROP_MENU, g_param_spec_string (PROP_MENU_S, @@ -277,11 +311,16 @@ app_indicator_init (AppIndicator *self) priv->status = APP_INDICATOR_STATUS_PASSIVE; priv->icon_name = NULL; priv->attention_icon_name = NULL; + priv->icon_path = NULL; priv->menu = NULL; priv->menuservice = NULL; priv->watcher_proxy = NULL; priv->connection = NULL; + priv->dbus_proxy = NULL; + + priv->status_icon = NULL; + priv->fallback_timer = 0; /* Put the object on DBus */ GError * error = NULL; @@ -293,7 +332,7 @@ app_indicator_init (AppIndicator *self) } dbus_g_connection_register_g_object(priv->connection, - "/need/a/path", + DEFAULT_ITEM_PATH, G_OBJECT(self)); self->priv = priv; @@ -306,20 +345,43 @@ app_indicator_init (AppIndicator *self) static void app_indicator_dispose (GObject *object) { - AppIndicator *self = APP_INDICATOR (object); + AppIndicator *self = APP_INDICATOR (object); AppIndicatorPrivate *priv = self->priv; if (priv->status != APP_INDICATOR_STATUS_PASSIVE) { app_indicator_set_status(self, APP_INDICATOR_STATUS_PASSIVE); } + if (priv->status_icon != NULL) { + AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(object); + if (class->unfallback != NULL) { + class->unfallback(self, priv->status_icon); + } + priv->status_icon = NULL; + } + + if (priv->fallback_timer != 0) { + g_source_remove(priv->fallback_timer); + priv->fallback_timer = 0; + } + if (priv->menu != NULL) { g_object_unref(G_OBJECT(priv->menu)); 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->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_object_unref(G_OBJECT(priv->watcher_proxy)); priv->watcher_proxy = NULL; } @@ -355,6 +417,11 @@ app_indicator_finalize (GObject *object) priv->attention_icon_name = NULL; } + if (priv->icon_path != NULL) { + g_free(priv->icon_path); + priv->icon_path = NULL; + } + G_OBJECT_CLASS (app_indicator_parent_class)->finalize (object); return; } @@ -423,6 +490,13 @@ app_indicator_set_property (GObject * object, guint prop_id, const GValue * valu g_value_get_string (value)); break; + case PROP_ICON_THEME_PATH: + if (priv->icon_path != NULL) { + g_free(priv->icon_path); + } + priv->icon_path = g_value_dup_string(value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -462,6 +536,10 @@ app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GPa g_value_set_string (value, priv->attention_icon_name); break; + case PROP_ICON_THEME_PATH: + g_value_set_string (value, priv->icon_path); + break; + case PROP_MENU: if (G_VALUE_HOLDS_STRING(value)) { if (priv->menuservice != NULL) { @@ -490,7 +568,7 @@ app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GPa static void check_connect (AppIndicator *self) { - AppIndicatorPrivate *priv = self->priv; + AppIndicatorPrivate *priv = self->priv; /* We're alreadying connecting or trying to connect. */ if (priv->watcher_proxy != NULL) return; @@ -502,35 +580,67 @@ check_connect (AppIndicator *self) GError * error = NULL; priv->watcher_proxy = dbus_g_proxy_new_for_name_owner(priv->connection, - INDICATOR_APPLICATION_DBUS_ADDR, + NOTIFICATION_WATCHER_DBUS_ADDR, NOTIFICATION_WATCHER_DBUS_OBJ, NOTIFICATION_WATCHER_DBUS_IFACE, &error); if (error != NULL) { - g_warning("Unable to create Ayatana Watcher proxy! %s", error->message); - /* TODO: This is where we should start looking at fallbacks */ + /* Unable to get proxy, but we're handling that now so + it's not a warning anymore. */ g_error_free(error); + start_fallback_timer(self, FALSE); return; } - org_ayatana_indicator_application_NotificationWatcher_register_service_async(priv->watcher_proxy, "/need/a/path", register_service_cb, self); + g_signal_connect(G_OBJECT(priv->watcher_proxy), "destroy", G_CALLBACK(watcher_proxy_destroyed), self); + org_freedesktop_StatusNotifierWatcher_register_status_notifier_item_async(priv->watcher_proxy, DEFAULT_ITEM_PATH, register_service_cb, self); + + return; +} + +/* A function that gets called when the watcher dies. Like + dies dies. Not our friend anymore. */ +static void +watcher_proxy_destroyed (GObject * object, gpointer data) +{ + AppIndicator * self = APP_INDICATOR(data); + g_return_if_fail(self != NULL); + self->priv->watcher_proxy = NULL; + 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) { - AppIndicatorPrivate * priv = APP_INDICATOR_GET_PRIVATE(data); + g_return_if_fail(IS_APP_INDICATOR(data)); + AppIndicatorPrivate * priv = APP_INDICATOR(data)->priv; 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); g_object_unref(G_OBJECT(priv->watcher_proxy)); priv->watcher_proxy = NULL; + start_fallback_timer(APP_INDICATOR(data), TRUE); + } + + if (priv->status_icon) { + AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(data); + if (class->unfallback != NULL) { + class->unfallback(APP_INDICATOR(data), priv->status_icon); + priv->status_icon = NULL; + } } + return; } +/* A helper function to get the nick out of a given + category enum value. */ static const gchar * category_from_enum (AppIndicatorCategory category) { @@ -540,6 +650,191 @@ 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; +} + +/* 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 + where it's unlikely that a timer will help anything. */ +static void +start_fallback_timer (AppIndicator * self, gboolean disable_timeout) +{ + g_return_if_fail(IS_APP_INDICATOR(self)); + AppIndicatorPrivate * priv = APP_INDICATOR(self)->priv; + + if (priv->fallback_timer != 0) { + /* The timer is set, let's just be happy with the one + we've already got running */ + return; + } + + 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), self, NULL); + } + + if (disable_timeout) { + fallback_timer_expire(self); + } else { + priv->fallback_timer = g_timeout_add(DEFAULT_FALLBACK_TIMER, fallback_timer_expire, self); + } + + return; +} + +/* A function that gets executed when we want to change the + state of the fallback. */ +static gboolean +fallback_timer_expire (gpointer data) +{ + g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE); + + AppIndicatorPrivate * priv = APP_INDICATOR(data)->priv; + AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(data); + + if (priv->status_icon == NULL) { + if (class->fallback != NULL) { + priv->status_icon = class->fallback(APP_INDICATOR(data)); + } + } else { + if (class->unfallback != NULL) { + class->unfallback(APP_INDICATOR(data), priv->status_icon); + priv->status_icon = NULL; + } else { + g_warning("No 'unfallback' function but the 'fallback' function returned a non-NULL result."); + } + } + + priv->fallback_timer = 0; + return FALSE; +} + +/* Creates a StatusIcon that can be used when the application + indicator area isn't available. */ +static GtkStatusIcon * +fallback (AppIndicator * self) +{ + GtkStatusIcon * icon = gtk_status_icon_new(); + + gtk_status_icon_set_title(icon, app_indicator_get_id(self)); + + g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_STATUS, + G_CALLBACK(status_icon_status_wrapper), icon); + g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_ICON, + G_CALLBACK(status_icon_changes), icon); + g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON, + G_CALLBACK(status_icon_changes), icon); + + status_icon_changes(self, icon); + + g_signal_connect(G_OBJECT(icon), "activate", G_CALLBACK(status_icon_activate), self); + + return icon; +} + +/* A wrapper as the status update prototype is a little + bit different, but we want to handle it the same. */ +static void +status_icon_status_wrapper (AppIndicator * self, const gchar * status, gpointer data) +{ + return status_icon_changes(self, data); +} + +/* This tracks changes to either the status or the icons + that are associated with the app indicator */ +static void +status_icon_changes (AppIndicator * self, gpointer data) +{ + GtkStatusIcon * icon = GTK_STATUS_ICON(data); + + switch (app_indicator_get_status(self)) { + case APP_INDICATOR_STATUS_PASSIVE: + gtk_status_icon_set_visible(icon, FALSE); + gtk_status_icon_set_from_icon_name(icon, app_indicator_get_icon(self)); + break; + case APP_INDICATOR_STATUS_ACTIVE: + gtk_status_icon_set_from_icon_name(icon, app_indicator_get_icon(self)); + gtk_status_icon_set_visible(icon, TRUE); + break; + case APP_INDICATOR_STATUS_ATTENTION: + gtk_status_icon_set_from_icon_name(icon, app_indicator_get_attention_icon(self)); + gtk_status_icon_set_visible(icon, TRUE); + break; + }; + + return; +} + +/* Handles the activate action by the status icon by showing + the menu in a popup. */ +static void +status_icon_activate (GtkStatusIcon * icon, gpointer data) +{ + GtkMenu * menu = app_indicator_get_menu(APP_INDICATOR(data)); + if (menu == NULL) + return; + + gtk_menu_popup(menu, + NULL, /* Parent Menu */ + NULL, /* Parent item */ + gtk_status_icon_position_menu, + icon, + 1, /* Button */ + gtk_get_current_event_time()); + + return; +} + +/* Removes the status icon as the application indicator area + is now up and running again. */ +static void +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_object_unref(G_OBJECT(status_icon)); + return; +} + /* ************************* */ /* Public Functions */ @@ -564,15 +859,45 @@ app_indicator_new (const gchar *id, AppIndicatorCategory category) { AppIndicator *indicator = g_object_new (APP_INDICATOR_TYPE, - "id", id, - "category", category_from_enum (category), - "icon-name", icon_name, + PROP_ID_S, id, + PROP_CATEGORY_S, category_from_enum (category), + PROP_ICON_NAME_S, icon_name, NULL); return indicator; } /** + app_indicator_new_with_path: + @id: The unique id of the indicator to create. + @icon_name: The icon name for this indicator + @category: The category of indicator. + @icon_path: A custom path for finding icons. + + Creates a new #AppIndicator setting the properties: + #AppIndicator::id with @id, #AppIndicator::category + with @category, #AppIndicator::icon-name with + @icon_name and #AppIndicator::icon-theme-path with @icon_path. + + Return value: A pointer to a new #AppIndicator object. + */ +AppIndicator * +app_indicator_new_with_path (const gchar *id, + const gchar *icon_name, + AppIndicatorCategory category, + const gchar *icon_path) +{ + AppIndicator *indicator = g_object_new (APP_INDICATOR_TYPE, + PROP_ID_S, id, + PROP_CATEGORY_S, category_from_enum (category), + PROP_ICON_NAME_S, icon_name, + PROP_ICON_THEME_PATH_S, icon_path, + NULL); + + return indicator; +} + +/** app_indicator_get_type: Generates or returns the unique #GType for #AppIndicator. @@ -640,14 +965,14 @@ 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); - if (g_strcmp0 (self->priv->attention_icon_name, icon_name) != 0) + if (g_strcmp0 (self->priv->icon_name, icon_name) != 0) { - if (self->priv->attention_icon_name) - g_free (self->priv->attention_icon_name); + if (self->priv->icon_name) + g_free (self->priv->icon_name); - self->priv->attention_icon_name = g_strdup (icon_name); + self->priv->icon_name = g_strdup (icon_name); - g_signal_emit (self, signals[NEW_ATTENTION_ICON], 0, TRUE); + g_signal_emit (self, signals[NEW_ICON], 0, TRUE); } } @@ -660,6 +985,14 @@ activate_menuitem (DbusmenuMenuitem *mi, gpointer user_data) } static void +widget_toggled (GtkWidget *widget, DbusmenuMenuitem *mi) +{ + dbusmenu_menuitem_property_set (mi, + DBUSMENU_MENUITEM_PROP_TOGGLE_CHECKED, + gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); +} + +static void menuitem_iterate (GtkWidget *widget, gpointer data) { @@ -674,6 +1007,85 @@ menuitem_iterate (GtkWidget *widget, } static void +update_icon_name (DbusmenuMenuitem *menuitem, + GtkImage *image) +{ + if (gtk_image_get_storage_type (image) != GTK_IMAGE_ICON_NAME) + return; + + dbusmenu_menuitem_property_set (menuitem, + DBUSMENU_MENUITEM_PROP_ICON, + image->data.name.icon_name); +} + +/* return value specifies whether the label is set or not */ +static gboolean +update_stock_item (DbusmenuMenuitem *menuitem, + GtkImage *image) +{ + GtkStockItem stock; + + if (gtk_image_get_storage_type (image) != GTK_IMAGE_STOCK) + return FALSE; + + gtk_stock_lookup (image->data.stock.stock_id, &stock); + + dbusmenu_menuitem_property_set (menuitem, + DBUSMENU_MENUITEM_PROP_ICON, + image->data.stock.stock_id); + + if (stock.label != NULL) + { + dbusmenu_menuitem_property_set (menuitem, + DBUSMENU_MENUITEM_PROP_LABEL, + stock.label); + + return TRUE; + } + + return FALSE; +} + +static void +image_notify_cb (GtkWidget *widget, + GParamSpec *pspec, + gpointer data) +{ + DbusmenuMenuitem *child = (DbusmenuMenuitem *)data; + GtkImage *image = GTK_IMAGE (widget); + + if (pspec->name == g_intern_static_string ("stock")) + { + update_stock_item (child, image); + } + else if (pspec->name == g_intern_static_string ("icon-name")) + { + update_icon_name (child, image); + } +} + +static void +widget_notify_cb (GtkWidget *widget, + GParamSpec *pspec, + gpointer data) +{ + DbusmenuMenuitem *child = (DbusmenuMenuitem *)data; + + if (pspec->name == g_intern_static_string ("sensitive")) + { + dbusmenu_menuitem_property_set_bool (child, + DBUSMENU_MENUITEM_PROP_SENSITIVE, + GTK_WIDGET_IS_SENSITIVE (widget)); + } + else if (pspec->name == g_intern_static_string ("label")) + { + dbusmenu_menuitem_property_set (child, + DBUSMENU_MENUITEM_PROP_LABEL, + gtk_menu_item_get_label (GTK_MENU_ITEM (widget))); + } +} + +static void container_iterate (GtkWidget *widget, gpointer data) { @@ -692,29 +1104,52 @@ container_iterate (GtkWidget *widget, } else { - label = gtk_menu_item_get_label (GTK_MENU_ITEM (widget)); - - if (GTK_IS_IMAGE_MENU_ITEM (widget)) + if (GTK_IS_CHECK_MENU_ITEM (widget)) { - GtkWidget *image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (widget)); + GtkCheckMenuItem *check; - if (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_STOCK) - { - GtkStockItem stock; + check = GTK_CHECK_MENU_ITEM (widget); + label = gtk_menu_item_get_label (GTK_MENU_ITEM (widget)); - gtk_stock_lookup (GTK_IMAGE (image)->data.stock.stock_id, &stock); + dbusmenu_menuitem_property_set (child, + DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, + GTK_IS_RADIO_MENU_ITEM (widget) ? DBUSMENU_MENUITEM_TOGGLE_RADIO : DBUSMENU_MENUITEM_TOGGLE_CHECK); - dbusmenu_menuitem_property_set (child, - DBUSMENU_MENUITEM_PROP_ICON, - GTK_IMAGE (image)->data.stock.stock_id); + dbusmenu_menuitem_property_set (child, + DBUSMENU_MENUITEM_PROP_LABEL, + label); - if (stock.label != NULL) - { - dbusmenu_menuitem_property_set (child, - DBUSMENU_MENUITEM_PROP_LABEL, - stock.label); - label_set = TRUE; - } + label_set = TRUE; + + dbusmenu_menuitem_property_set (child, + DBUSMENU_MENUITEM_PROP_TOGGLE_CHECKED, + gtk_check_menu_item_get_active (check) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); + + g_signal_connect (widget, + "toggled", + G_CALLBACK (widget_toggled), + child); + } + else if (GTK_IS_IMAGE_MENU_ITEM (widget)) + { + GtkWidget *image; + GtkImageType image_type; + + image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (widget)); + image_type = gtk_image_get_storage_type (GTK_IMAGE (image)); + + g_signal_connect (image, + "notify", + G_CALLBACK (image_notify_cb), + child); + + if (image_type == GTK_IMAGE_STOCK) + { + label_set = update_stock_item (child, GTK_IMAGE (image)); + } + else if (image_type == GTK_IMAGE_ICON_NAME) + { + update_icon_name (child, GTK_IMAGE (image)); } } } @@ -736,6 +1171,9 @@ container_iterate (GtkWidget *widget, } } + g_signal_connect (widget, "notify", + G_CALLBACK (widget_notify_cb), child); + g_signal_connect (G_OBJECT (child), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK (activate_menuitem), widget); @@ -756,7 +1194,7 @@ setup_dbusmenu (AppIndicator *self) root); if (priv->menuservice == NULL) { - priv->menuservice = dbusmenu_server_new ("/need/a/menu/path"); + priv->menuservice = dbusmenu_server_new (DEFAULT_MENU_PATH); } dbusmenu_server_set_root (priv->menuservice, root); @@ -875,3 +1313,23 @@ app_indicator_get_attention_icon (AppIndicator *self) return self->priv->attention_icon_name; } + +/** + app_indicator_get_menu: + @self: The #AppIndicator object to use + + Gets the menu being used for this application indicator. + + Return value: A menu object or #NULL if one hasn't been set. +*/ +GtkMenu * +app_indicator_get_menu (AppIndicator *self) +{ + AppIndicatorPrivate *priv; + + g_return_val_if_fail (IS_APP_INDICATOR (self), NULL); + + priv = self->priv; + + return GTK_MENU(priv->menu); +} diff --git a/src/libappindicator/app-indicator.h b/src/libappindicator/app-indicator.h index e966a49..03656ce 100644 --- a/src/libappindicator/app-indicator.h +++ b/src/libappindicator/app-indicator.h @@ -128,8 +128,8 @@ typedef enum { /*< prefix=APP_INDICATOR_CATEGORY >*/ These are the states that the indicator can be on in the user's panel. The indicator by default starts - in the state @APP_INDICATOR_STATUS_OFF and can be - shown by setting it to @APP_INDICATOR_STATUS_ON. + in the state @APP_INDICATOR_STATUS_PASSIVE and can be + shown by setting it to @APP_INDICATOR_STATUS_ACTIVE. */ typedef enum { /*< prefix=APP_INDICATOR_STATUS >*/ APP_INDICATOR_STATUS_PASSIVE, @@ -148,10 +148,12 @@ typedef struct _AppIndicatorPrivate AppIndicatorPrivate; @new_attention_icon: Slot for #AppIndicator::new-attention-icon. @new_status: Slot for #AppIndicator::new-status. @connection_changed: Slot for #AppIndicator::connection-changed. + @fallback: Function that gets called to make a #GtkStatusIcon when + there is no Application Indicator area available. + @unfallback: The function that gets called if an Application + Indicator area appears after the fallback has been created. @app_indicator_reserved_1: Reserved for future use. @app_indicator_reserved_2: Reserved for future use. - @app_indicator_reserved_3: Reserved for future use. - @app_indicator_reserved_4: Reserved for future use. The signals and external functions that make up the #AppIndicator class object. @@ -174,11 +176,14 @@ struct _AppIndicatorClass { gboolean connected, gpointer user_data); + /* Overridable Functions */ + GtkStatusIcon * (*fallback) (AppIndicator * indicator); + void (*unfallback) (AppIndicator * indicator, + GtkStatusIcon * status_icon); + /* Reserved */ void (*app_indicator_reserved_1)(void); void (*app_indicator_reserved_2)(void); - void (*app_indicator_reserved_3)(void); - void (*app_indicator_reserved_4)(void); }; /** @@ -202,6 +207,10 @@ GType app_indicator_get_type (void) G_GNUC_C AppIndicator *app_indicator_new (const gchar *id, const gchar *icon_name, AppIndicatorCategory category); +AppIndicator *app_indicator_new_with_path (const gchar *id, + const gchar *icon_name, + AppIndicatorCategory category, + const gchar *icon_path); /* Set properties */ void app_indicator_set_status (AppIndicator *self, @@ -219,6 +228,7 @@ AppIndicatorCategory app_indicator_get_category (AppIndicator * AppIndicatorStatus app_indicator_get_status (AppIndicator *self); const gchar * app_indicator_get_icon (AppIndicator *self); const gchar * app_indicator_get_attention_icon (AppIndicator *self); +GtkMenu * app_indicator_get_menu (AppIndicator *self); G_END_DECLS |