aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Gould <ted@gould.cx>2010-01-07 17:08:43 -0600
committerTed Gould <ted@gould.cx>2010-01-07 17:08:43 -0600
commit2fef3d43177eb79c8a52b834de7d374dae8bc97a (patch)
tree5a9671b8183fd77050e43a089253377a08efe665
parent13de14e2a1944a8bee33bdd574924e0d9c8c66ed (diff)
parentb1ffef34b05cecbad73a0278dcfe67b4c3a22f90 (diff)
downloadlibayatana-appindicator-2fef3d43177eb79c8a52b834de7d374dae8bc97a.tar.gz
libayatana-appindicator-2fef3d43177eb79c8a52b834de7d374dae8bc97a.tar.bz2
libayatana-appindicator-2fef3d43177eb79c8a52b834de7d374dae8bc97a.zip
* Upstream merge:
* Adding support for icons changing through the service and in the indicator.
-rw-r--r--debian/changelog8
-rw-r--r--src/application-service-appstore.c328
-rw-r--r--src/application-service-appstore.h1
-rw-r--r--src/application-service-marshal.list1
-rw-r--r--src/application-service.xml4
-rw-r--r--src/indicator-application.c33
6 files changed, 348 insertions, 27 deletions
diff --git a/debian/changelog b/debian/changelog
index b6ebc19..e06fdc4 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+indicator-application (0.0.6-0ubuntu1~ppa2~icon3) UNRELEASED; urgency=low
+
+ * Upstream merge:
+ * Adding support for icons changing through the service and
+ in the indicator.
+
+ -- Ted Gould <ted@ubuntu.com> Thu, 07 Jan 2010 17:08:10 -0600
+
indicator-application (0.0.6-0ubuntu1~ppa2~icon2ubuntu1) karmic; urgency=low
* Debug message
diff --git a/src/application-service-appstore.c b/src/application-service-appstore.c
index 1ac309a..a712d30 100644
--- a/src/application-service-appstore.c
+++ b/src/application-service-appstore.c
@@ -43,6 +43,10 @@ static gboolean _application_service_server_get_applications (ApplicationService
#define NOTIFICATION_ITEM_PROP_ICON_PATH "IconPath"
#define NOTIFICATION_ITEM_PROP_MENU "Menu"
+#define NOTIFICATION_ITEM_SIG_NEW_ICON "NewIcon"
+#define NOTIFICATION_ITEM_SIG_NEW_AICON "NewAttentionIcon"
+#define NOTIFICATION_ITEM_SIG_NEW_STATUS "NewStatus"
+
/* Private Stuff */
typedef struct _ApplicationServiceAppstorePrivate ApplicationServiceAppstorePrivate;
struct _ApplicationServiceAppstorePrivate {
@@ -50,6 +54,17 @@ struct _ApplicationServiceAppstorePrivate {
GList * applications;
};
+#define APP_STATUS_PASSIVE_STR "passive"
+#define APP_STATUS_ACTIVE_STR "active"
+#define APP_STATUS_ATTENTION_STR "attention"
+
+typedef enum _ApplicationStatus ApplicationStatus;
+enum _ApplicationStatus {
+ APP_STATUS_PASSIVE,
+ APP_STATUS_ACTIVE,
+ APP_STATUS_ATTENTION
+};
+
typedef struct _Application Application;
struct _Application {
gchar * dbus_name;
@@ -57,6 +72,12 @@ struct _Application {
ApplicationServiceAppstore * appstore; /* not ref'd */
DBusGProxy * dbus_proxy;
DBusGProxy * prop_proxy;
+ gboolean validated; /* Whether we've gotten all the parameters and they look good. */
+ ApplicationStatus status;
+ gchar * icon;
+ gchar * aicon;
+ gchar * menu;
+ gchar * icon_path;
};
#define APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(o) \
@@ -66,6 +87,7 @@ struct _Application {
enum {
APPLICATION_ADDED,
APPLICATION_REMOVED,
+ APPLICATION_ICON_CHANGED,
LAST_SIGNAL
};
@@ -76,6 +98,8 @@ static void application_service_appstore_class_init (ApplicationServiceAppstoreC
static void application_service_appstore_init (ApplicationServiceAppstore *self);
static void application_service_appstore_dispose (GObject *object);
static void application_service_appstore_finalize (GObject *object);
+static ApplicationStatus string_to_status(const gchar * status_string);
+static void apply_status (Application * app, ApplicationStatus status);
G_DEFINE_TYPE (ApplicationServiceAppstore, application_service_appstore, G_TYPE_OBJECT);
@@ -103,6 +127,13 @@ application_service_appstore_class_init (ApplicationServiceAppstoreClass *klass)
NULL, NULL,
g_cclosure_marshal_VOID__INT,
G_TYPE_NONE, 1, G_TYPE_INT, G_TYPE_NONE);
+ signals[APPLICATION_ICON_CHANGED] = g_signal_new ("application-icon-changed",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ApplicationServiceAppstore, application_icon_changed),
+ NULL, NULL,
+ _application_service_marshal_VOID__INT_STRING,
+ G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_STRING, G_TYPE_NONE);
dbus_g_object_type_install_info(APPLICATION_SERVICE_APPSTORE_TYPE,
@@ -156,6 +187,9 @@ application_service_appstore_finalize (GObject *object)
return;
}
+/* Return from getting the properties from the item. We're looking at those
+ and making sure we have everythign that we need. If we do, then we'll
+ move on up to sending this onto the indicator. */
static void
get_all_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * error, gpointer data)
{
@@ -167,39 +201,60 @@ get_all_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * err
Application * app = (Application *)data;
if (g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_MENU) == NULL ||
+ g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_STATUS) == NULL ||
g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ICON_NAME) == NULL) {
g_warning("Notification Item on object %s of %s doesn't have enough properties.", app->dbus_object, app->dbus_name);
g_free(app); // Need to do more than this, but it gives the idea of the flow we're going for.
return;
}
- ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(app->appstore);
- priv->applications = g_list_prepend(priv->applications, app);
-
- /* TODO: We need to have the position determined better. This
- would involve looking at the name and category and sorting
- it with the other entries. */
+ app->validated = TRUE;
+
+ app->icon = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ICON_NAME));
+ app->menu = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_MENU));
+ if (g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_AICON_NAME) != NULL) {
+ app->aicon = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ICON_NAME));
+ }
- const gchar * icon_path = NULL;
gpointer icon_path_data = g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ICON_PATH);
if (icon_path_data != NULL) {
- icon_path = g_value_get_string((GValue *)icon_path_data);
+ app->icon_path = g_value_dup_string((GValue *)icon_path_data);
} else {
- icon_path = "";
+ app->icon_path = g_strdup("");
}
- g_signal_emit(G_OBJECT(app->appstore),
- signals[APPLICATION_ADDED], 0,
- g_value_get_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ICON_NAME)),
- 0, /* Position */
- app->dbus_name,
- g_value_get_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_MENU)),
- icon_path,
- TRUE);
+ apply_status(app, string_to_status(g_value_get_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_STATUS))));
return;
}
+/* Simple translation function -- could be optimized */
+static ApplicationStatus
+string_to_status(const gchar * status_string)
+{
+ if (!g_strcmp0(status_string, APP_STATUS_ACTIVE_STR))
+ return APP_STATUS_ACTIVE;
+ if (!g_strcmp0(status_string, APP_STATUS_ATTENTION_STR))
+ return APP_STATUS_ATTENTION;
+ return APP_STATUS_PASSIVE;
+}
+
+/* A small helper function to get the position of an application
+ in the app list. */
+static gint
+get_position (Application * app) {
+ ApplicationServiceAppstore * appstore = app->appstore;
+ ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(appstore);
+
+ GList * applistitem = g_list_find(priv->applications, app);
+ if (applistitem == NULL) {
+ g_warning("Change the icon of an application that isn't in the application list?");
+ return -1;
+ }
+
+ return g_list_position(priv->applications, applistitem);
+}
+
/* A simple global function for dealing with freeing the information
in an Application structure */
static void
@@ -213,6 +268,18 @@ application_free (Application * app)
if (app->dbus_object != NULL) {
g_free(app->dbus_object);
}
+ if (app->icon != NULL) {
+ g_free(app->icon);
+ }
+ if (app->aicon != NULL) {
+ g_free(app->aicon);
+ }
+ if (app->menu != NULL) {
+ g_free(app->menu);
+ }
+ if (app->icon_path != NULL) {
+ g_free(app->icon_path);
+ }
g_free(app);
return;
@@ -224,24 +291,197 @@ static void
application_removed_cb (DBusGProxy * proxy, gpointer userdata)
{
Application * app = (Application *)userdata;
+
+ /* Remove from the panel */
+ apply_status(app, APP_STATUS_PASSIVE);
+
+ /* Destroy the data */
+ application_free(app);
+ return;
+}
+
+/* Change the status of the application. If we're going passive
+ it removes it from the panel. If we're coming online, then
+ it add it to the panel. Otherwise it changes the icon. */
+static void
+apply_status (Application * app, ApplicationStatus status)
+{
+ if (app->status == status) {
+ return;
+ }
+
ApplicationServiceAppstore * appstore = app->appstore;
ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(appstore);
- GList * applistitem = g_list_find(priv->applications, app);
- if (applistitem == NULL) {
- g_warning("Removing an application that isn't in the application list?");
+ /* This means we're going off line */
+ if (status == APP_STATUS_PASSIVE) {
+ gint position = get_position(app);
+ if (position == -1) return;
+
+ g_signal_emit(G_OBJECT(appstore),
+ signals[APPLICATION_REMOVED], 0,
+ position, TRUE);
+
+ priv->applications = g_list_remove(priv->applications, app);
+ } else {
+ /* Figure out which icon we should be using */
+ gchar * newicon = app->icon;
+ if (status == APP_STATUS_ATTENTION && app->aicon != NULL) {
+ newicon = app->aicon;
+ }
+
+ /* Determine whether we're already shown or not */
+ if (app->status == APP_STATUS_PASSIVE) {
+ /* Put on panel */
+ priv->applications = g_list_prepend(priv->applications, app);
+
+ /* TODO: We need to have the position determined better. This
+ would involve looking at the name and category and sorting
+ it with the other entries. */
+
+ g_signal_emit(G_OBJECT(app->appstore),
+ signals[APPLICATION_ADDED], 0,
+ newicon,
+ 0, /* Position */
+ app->dbus_name,
+ app->menu,
+ app->icon_path,
+ TRUE);
+ } else {
+ /* Icon update */
+ gint position = get_position(app);
+ if (position == -1) return;
+
+ g_signal_emit(G_OBJECT(appstore),
+ signals[APPLICATION_ICON_CHANGED], 0,
+ position, newicon, TRUE);
+ }
+ }
+
+ app->status = status;
+
+ return;
+}
+
+/* Gets the data back on an updated icon signal. Hopefully
+ a new fun icon. */
+static void
+new_icon_cb (DBusGProxy * proxy, GValue value, GError * error, gpointer userdata)
+{
+ /* Check for errors */
+ if (error != NULL) {
+ g_warning("Unable to get updated icon name: %s", error->message);
return;
}
- gint position = g_list_position(priv->applications, applistitem);
+ /* Grab the icon and make sure we have one */
+ const gchar * newicon = g_value_get_string(&value);
+ if (newicon == NULL) {
+ g_warning("Bad new icon :(");
+ return;
+ }
- g_signal_emit(G_OBJECT(appstore),
- signals[APPLICATION_REMOVED], 0,
- position, TRUE);
+ Application * app = (Application *) userdata;
- priv->applications = g_list_remove(priv->applications, app);
+ if (g_strcmp0(newicon, app->icon)) {
+ /* If the new icon is actually a new icon */
+ if (app->icon != NULL) g_free(app->icon);
+ app->icon = g_strdup(newicon);
+
+ if (app->status == APP_STATUS_ACTIVE) {
+ gint position = get_position(app);
+ if (position == -1) return;
+
+ g_signal_emit(G_OBJECT(app->appstore),
+ signals[APPLICATION_ICON_CHANGED], 0,
+ position, newicon, TRUE);
+ }
+ }
+
+ return;
+}
+
+/* Gets the data back on an updated aicon signal. Hopefully
+ a new fun icon. */
+static void
+new_aicon_cb (DBusGProxy * proxy, GValue value, GError * error, gpointer userdata)
+{
+ /* Check for errors */
+ if (error != NULL) {
+ g_warning("Unable to get updated icon name: %s", error->message);
+ return;
+ }
+
+ /* Grab the icon and make sure we have one */
+ const gchar * newicon = g_value_get_string(&value);
+ if (newicon == NULL) {
+ g_warning("Bad new icon :(");
+ return;
+ }
+
+ Application * app = (Application *) userdata;
+
+ if (g_strcmp0(newicon, app->aicon)) {
+ /* If the new icon is actually a new icon */
+ if (app->aicon != NULL) g_free(app->aicon);
+ app->aicon = g_strdup(newicon);
+
+ if (app->status == APP_STATUS_ATTENTION) {
+ gint position = get_position(app);
+ if (position == -1) return;
+
+ g_signal_emit(G_OBJECT(app->appstore),
+ signals[APPLICATION_ICON_CHANGED], 0,
+ position, newicon, TRUE);
+ }
+ }
+
+ return;
+}
+
+/* Called when the Notification Item signals that it
+ has a new icon. */
+static void
+new_icon (DBusGProxy * proxy, gpointer data)
+{
+ Application * app = (Application *)data;
+ if (!app->validated) return;
+
+ org_freedesktop_DBus_Properties_get_async(app->prop_proxy,
+ NOTIFICATION_ITEM_DBUS_IFACE,
+ NOTIFICATION_ITEM_PROP_ICON_NAME,
+ new_icon_cb,
+ app);
+ return;
+}
+
+/* Called when the Notification Item signals that it
+ has a new attention icon. */
+static void
+new_aicon (DBusGProxy * proxy, gpointer data)
+{
+ Application * app = (Application *)data;
+ if (!app->validated) return;
+
+ org_freedesktop_DBus_Properties_get_async(app->prop_proxy,
+ NOTIFICATION_ITEM_DBUS_IFACE,
+ NOTIFICATION_ITEM_PROP_AICON_NAME,
+ new_aicon_cb,
+ app);
+
+ return;
+}
+
+/* Called when the Notification Item signals that it
+ has a new status. */
+static void
+new_status (DBusGProxy * proxy, const gchar * status, gpointer data)
+{
+ Application * app = (Application *)data;
+ if (!app->validated) return;
+
+ apply_status(app, string_to_status(status));
- application_free(app);
return;
}
@@ -261,11 +501,17 @@ application_service_appstore_application_add (ApplicationServiceAppstore * appst
/* Build the application entry. This will be carried
along until we're sure we've got everything. */
- Application * app = g_new(Application, 1);
+ Application * app = g_new0(Application, 1);
+ app->validated = FALSE;
app->dbus_name = g_strdup(dbus_name);
app->dbus_object = g_strdup(dbus_object);
app->appstore = appstore;
+ app->status = APP_STATUS_PASSIVE;
+ app->icon = NULL;
+ app->aicon = NULL;
+ app->menu = NULL;
+ app->icon_path = NULL;
/* Get the DBus proxy for the NotificationItem interface */
GError * error = NULL;
@@ -300,6 +546,34 @@ application_service_appstore_application_add (ApplicationServiceAppstore * appst
return;
}
+ /* Connect to signals */
+ dbus_g_proxy_add_signal(app->dbus_proxy,
+ NOTIFICATION_ITEM_SIG_NEW_ICON,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal(app->dbus_proxy,
+ NOTIFICATION_ITEM_SIG_NEW_AICON,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal(app->dbus_proxy,
+ NOTIFICATION_ITEM_SIG_NEW_STATUS,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+
+ dbus_g_proxy_connect_signal(app->dbus_proxy,
+ NOTIFICATION_ITEM_SIG_NEW_ICON,
+ G_CALLBACK(new_icon),
+ app,
+ NULL);
+ dbus_g_proxy_connect_signal(app->dbus_proxy,
+ NOTIFICATION_ITEM_SIG_NEW_AICON,
+ G_CALLBACK(new_aicon),
+ app,
+ NULL);
+ dbus_g_proxy_connect_signal(app->dbus_proxy,
+ NOTIFICATION_ITEM_SIG_NEW_STATUS,
+ G_CALLBACK(new_status),
+ app,
+ NULL);
+
/* Get all the propertiees */
org_freedesktop_DBus_Properties_get_all_async(app->prop_proxy,
NOTIFICATION_ITEM_DBUS_IFACE,
diff --git a/src/application-service-appstore.h b/src/application-service-appstore.h
index edf1a37..7ab20c5 100644
--- a/src/application-service-appstore.h
+++ b/src/application-service-appstore.h
@@ -47,6 +47,7 @@ struct _ApplicationServiceAppstore {
void (*application_added) (ApplicationServiceAppstore * appstore, gchar *, gint, gchar *, gchar *, gpointer);
void (*application_removed) (ApplicationServiceAppstore * appstore, gint, gpointer);
+ void (*application_icon_changed)(ApplicationServiceAppstore * appstore, gint, const gchar *, gpointer);
};
GType application_service_appstore_get_type (void);
diff --git a/src/application-service-marshal.list b/src/application-service-marshal.list
index 6cfbada..4ac8398 100644
--- a/src/application-service-marshal.list
+++ b/src/application-service-marshal.list
@@ -17,3 +17,4 @@
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
VOID: STRING, INT, STRING, STRING, STRING
+VOID: INT, STRING
diff --git a/src/application-service.xml b/src/application-service.xml
index d354943..d74aaa9 100644
--- a/src/application-service.xml
+++ b/src/application-service.xml
@@ -40,6 +40,10 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
<signal name="ApplicationRemoved">
<arg type="i" name="position" direction="out" />
</signal>
+ <signal name="ApplicationIconChanged">
+ <arg type="i" name="position" direction="out" />
+ <arg type="s" name="icon_name" direction="out" />
+ </signal>
</interface>
</node>
diff --git a/src/indicator-application.c b/src/indicator-application.c
index fbfbd40..c330645 100644
--- a/src/indicator-application.c
+++ b/src/indicator-application.c
@@ -94,6 +94,7 @@ static GList * get_entries (IndicatorObject * io);
static void connected (IndicatorServiceManager * sm, gboolean connected, IndicatorApplication * application);
static void application_added (DBusGProxy * proxy, const gchar * iconname, gint position, const gchar * dbusaddress, const gchar * dbusobject, const gchar * icon_path, IndicatorApplication * application);
static void application_removed (DBusGProxy * proxy, gint position , IndicatorApplication * application);
+static void application_icon_changed (DBusGProxy * proxy, gint position, const gchar * iconname, IndicatorApplication * application);
static void get_applications (DBusGProxy *proxy, GPtrArray *OUT_applications, GError *error, gpointer userdata);
G_DEFINE_TYPE (IndicatorApplication, indicator_application, INDICATOR_OBJECT_TYPE);
@@ -120,6 +121,11 @@ indicator_application_class_init (IndicatorApplicationClass *klass)
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_INVALID);
+ dbus_g_object_register_marshaller(_application_service_marshal_VOID__INT_STRING,
+ G_TYPE_NONE,
+ G_TYPE_INT,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
return;
}
@@ -219,6 +225,11 @@ connected (IndicatorServiceManager * sm, gboolean connected, IndicatorApplicatio
"ApplicationRemoved",
G_TYPE_INT,
G_TYPE_INVALID);
+ dbus_g_proxy_add_signal(priv->service_proxy,
+ "ApplicationIconChanged",
+ G_TYPE_INT,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
/* Connect to them */
g_debug("Connect to them.");
@@ -232,6 +243,11 @@ connected (IndicatorServiceManager * sm, gboolean connected, IndicatorApplicatio
G_CALLBACK(application_removed),
application,
NULL /* Disconnection Signal */);
+ dbus_g_proxy_connect_signal(priv->service_proxy,
+ "ApplicationIconChanged",
+ G_CALLBACK(application_icon_changed),
+ application,
+ NULL /* Disconnection Signal */);
/* Query it for existing applications */
g_debug("Request current apps");
@@ -330,6 +346,23 @@ application_removed (DBusGProxy * proxy, gint position, IndicatorApplication * a
return;
}
+/* The callback for the signal that the icon for an application
+ has changed. */
+static void
+application_icon_changed (DBusGProxy * proxy, gint position, const gchar * iconname, IndicatorApplication * application)
+{
+ IndicatorApplicationPrivate * priv = INDICATOR_APPLICATION_GET_PRIVATE(application);
+ ApplicationEntry * app = (ApplicationEntry *)g_list_nth_data(priv->applications, position);
+
+ if (app == NULL) {
+ g_warning("Unable to find application at position: %d", position);
+ return;
+ }
+
+ gtk_image_set_from_icon_name(app->entry.image, iconname, GTK_ICON_SIZE_MENU);
+ return;
+}
+
/* This repsonds to the list of applications that the service
has and calls application_added on each one of them. */
static void