diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/application-service-appstore.c | 306 | ||||
-rw-r--r-- | src/application-service-marshal.c | 39 | ||||
-rw-r--r-- | src/application-service-marshal.list | 1 | ||||
-rw-r--r-- | src/application-service-watcher.c | 4 | ||||
-rw-r--r-- | src/notification-approver.xml | 7 | ||||
-rw-r--r-- | src/notification-watcher.xml | 2 |
6 files changed, 281 insertions, 78 deletions
diff --git a/src/application-service-appstore.c b/src/application-service-appstore.c index 4ac1576..f52ea2d 100644 --- a/src/application-service-appstore.c +++ b/src/application-service-appstore.c @@ -67,9 +67,15 @@ struct _ApplicationServiceAppstorePrivate { GHashTable * ordering_overrides; }; +typedef enum { + VISIBLE_STATE_HIDDEN, + VISIBLE_STATE_SHOWN +} visible_state_t; + typedef struct _Approver Approver; struct _Approver { DBusGProxy * proxy; + gboolean destroy_by_proxy; }; typedef struct _Application Application; @@ -91,6 +97,8 @@ struct _Application { gchar * guide; gboolean currently_free; guint ordering_index; + GList * approved_by; + visible_state_t visible_state; }; #define APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(o) \ @@ -113,10 +121,11 @@ 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 gint app_sort_func (gconstpointer a, gconstpointer b, gpointer userdata); static void load_override_file (GHashTable * hash, const gchar * filename); static AppIndicatorStatus string_to_status(const gchar * status_string); +static void apply_status (Application * app); static AppIndicatorCategory string_to_cat(const gchar * cat_string); -static void apply_status (Application * app, AppIndicatorStatus status); static void approver_free (gpointer papprover, gpointer user_data); static void check_with_new_approver (gpointer papp, gpointer papprove); static void check_with_old_approver (gpointer papprove, gpointer papp); @@ -174,6 +183,12 @@ application_service_appstore_class_init (ApplicationServiceAppstoreClass *klass) G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_object_register_marshaller(_application_service_marshal_VOID__BOOLEAN_STRING_OBJECT, + G_TYPE_NONE, + G_TYPE_BOOLEAN, + G_TYPE_STRING, + G_TYPE_OBJECT, + G_TYPE_INVALID); dbus_g_object_type_install_info(APPLICATION_SERVICE_APPSTORE_TYPE, &dbus_glib__application_service_server_object_info); @@ -226,7 +241,7 @@ application_service_appstore_dispose (GObject *object) } if (priv->approvers != NULL) { - g_list_foreach(priv->approvers, approver_free, NULL); + g_list_foreach(priv->approvers, approver_free, object); g_list_free(priv->approvers); priv->approvers = NULL; } @@ -298,6 +313,7 @@ load_override_file (GHashTable * hash, const gchar * filename) g_hash_table_insert(hash, g_strdup(key), GINT_TO_POINTER(val)); } + g_strfreev(keys); g_key_file_free(keyfile); return; @@ -311,6 +327,7 @@ get_all_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * err { if (error != NULL) { g_warning("Unable to get properties: %s", error->message); + /* TODO: We need to free all the application data here */ return; } @@ -330,6 +347,8 @@ get_all_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * err app->id = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ID)); app->category = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_CATEGORY)); + app->status = string_to_status(g_value_get_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_STATUS))); + ApplicationServiceAppstorePrivate * priv = app->appstore->priv; app->icon = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ICON_NAME)); @@ -381,10 +400,10 @@ get_all_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * err app->guide = g_strdup(""); } - /* TODO: Calling approvers, but we're ignoring the results. So, eh. */ + priv->applications = g_list_insert_sorted_with_data (priv->applications, app, app_sort_func, NULL); g_list_foreach(priv->approvers, check_with_old_approver, app); - apply_status(app, string_to_status(g_value_get_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_STATUS)))); + apply_status(app); return; } @@ -442,18 +461,34 @@ string_to_cat(const gchar * cat_string) /* A small helper function to get the position of an application - in the app list. */ + in the app list of the applications that are visible. */ static gint get_position (Application * app) { ApplicationServiceAppstore * appstore = app->appstore; ApplicationServiceAppstorePrivate * priv = appstore->priv; - GList * applistitem = g_list_find(priv->applications, app); - if (applistitem == NULL) { - return -1; + GList * lapp; + gint count; + + /* Go through the list and try to find ours */ + for (lapp = priv->applications, count = 0; lapp != NULL; lapp = g_list_next(lapp), count++) { + if (lapp->data == app) { + break; + } + + /* If the selected app isn't visible let's not + count it's position */ + Application * thisapp = (Application *)(lapp->data); + if (thisapp->visible_state == VISIBLE_STATE_HIDDEN) { + count--; + } } - return g_list_position(priv->applications, applistitem); + if (lapp == NULL) { + return -1; + } + + return count; } /* A simple global function for dealing with freeing the information @@ -505,6 +540,9 @@ application_free (Application * app) if (app->guide != NULL) { g_free(app->guide); } + if (app->approved_by != NULL) { + g_list_free(app->approved_by); + } g_free(app); return; @@ -518,35 +556,17 @@ application_removed_cb (DBusGProxy * proxy, gpointer userdata) Application * app = (Application *)userdata; /* Remove from the panel */ - apply_status(app, APP_INDICATOR_STATUS_PASSIVE); + app->status = APP_INDICATOR_STATUS_PASSIVE; + apply_status(app); + + /* Remove from the application list */ + app->appstore->priv->applications = g_list_remove(app->appstore->priv->applications, app); /* Destroy the data */ application_free(app); return; } -static gboolean -can_add_application (GList *applications, Application *app) -{ - if (applications) - { - GList *l = NULL; - - for (l = applications; l != NULL; l = g_list_next (l)) - { - Application *tmp_app = (Application *)l->data; - - if (g_strcmp0 (tmp_app->dbus_name, app->dbus_name) == 0 && - g_strcmp0 (tmp_app->dbus_object, app->dbus_object) == 0) - { - return FALSE; - } - } - } - - return TRUE; -} - /* This function takes two Application structure pointers and uses their ordering index to compare them. */ static gint @@ -561,49 +581,53 @@ app_sort_func (gconstpointer a, gconstpointer b, gpointer userdata) 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, AppIndicatorStatus status) +apply_status (Application * app) { - if (app->status == status) { - return; - } - g_debug("Changing app status to: %d", status); - ApplicationServiceAppstore * appstore = app->appstore; ApplicationServiceAppstorePrivate * priv = appstore->priv; + /* g_debug("Applying status. Status: %d Approved by: %d Approvers: %d Visible: %d", app->status, g_list_length(app->approved_by), g_list_length(priv->approvers), app->visible_state); */ + + visible_state_t goal_state = VISIBLE_STATE_HIDDEN; + + if (app->status != APP_INDICATOR_STATUS_PASSIVE && + g_list_length(app->approved_by) >= g_list_length(priv->approvers)) { + goal_state = VISIBLE_STATE_SHOWN; + } + + /* Nothing needs to change, we're good */ + if (app->visible_state == goal_state) { + return; + } + /* This means we're going off line */ - if (status == APP_INDICATOR_STATUS_PASSIVE) { + if (goal_state == VISIBLE_STATE_HIDDEN) { 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_INDICATOR_STATUS_ATTENTION && app->aicon != NULL && app->aicon[0] != '\0') { + if (app->status == APP_INDICATOR_STATUS_ATTENTION && app->aicon != NULL && app->aicon[0] != '\0') { newicon = app->aicon; } /* Determine whether we're already shown or not */ - if (app->status == APP_INDICATOR_STATUS_PASSIVE) { - if (can_add_application (priv->applications, app)) { - /* Put on panel */ - priv->applications = g_list_insert_sorted_with_data (priv->applications, app, app_sort_func, NULL); - - g_signal_emit(G_OBJECT(app->appstore), - signals[APPLICATION_ADDED], 0, - newicon, - g_list_index(priv->applications, app), /* Position */ - app->dbus_name, - app->menu, - app->icon_theme_path, - app->label, - app->guide, - TRUE); - } + if (app->visible_state == VISIBLE_STATE_HIDDEN) { + /* Put on panel */ + g_signal_emit(G_OBJECT(app->appstore), + signals[APPLICATION_ADDED], 0, + newicon, + g_list_index(priv->applications, app), /* Position */ + app->dbus_name, + app->menu, + app->icon_theme_path, + app->label, + app->guide, + TRUE); } else { /* Icon update */ gint position = get_position(app); @@ -615,7 +639,7 @@ apply_status (Application * app, AppIndicatorStatus status) } } - app->status = status; + app->visible_state = goal_state; return; } @@ -645,7 +669,7 @@ new_icon_cb (DBusGProxy * proxy, GValue value, GError * error, gpointer userdata if (app->icon != NULL) g_free(app->icon); app->icon = g_strdup(newicon); - if (app->status == APP_INDICATOR_STATUS_ACTIVE) { + if (app->visible_state == VISIBLE_STATE_SHOWN && app->status == APP_INDICATOR_STATUS_ACTIVE) { gint position = get_position(app); if (position == -1) return; @@ -683,7 +707,7 @@ new_aicon_cb (DBusGProxy * proxy, GValue value, GError * error, gpointer userdat if (app->aicon != NULL) g_free(app->aicon); app->aicon = g_strdup(newicon); - if (app->status == APP_INDICATOR_STATUS_ATTENTION) { + if (app->visible_state == VISIBLE_STATE_SHOWN && app->status == APP_INDICATOR_STATUS_ATTENTION) { gint position = get_position(app); if (position == -1) return; @@ -737,7 +761,8 @@ new_status (DBusGProxy * proxy, const gchar * status, gpointer data) Application * app = (Application *)data; if (!app->validated) return; - apply_status(app, string_to_status(status)); + app->status = string_to_status(status); + apply_status(app); return; } @@ -755,7 +780,7 @@ new_icon_theme_path (DBusGProxy * proxy, const gchar * icon_theme_path, gpointer if (app->icon_theme_path != NULL) g_free(app->icon_theme_path); app->icon_theme_path = g_strdup(icon_theme_path); - if (app->status == APP_INDICATOR_STATUS_ACTIVE) { + if (app->visible_state != VISIBLE_STATE_HIDDEN) { gint position = get_position(app); if (position == -1) return; @@ -841,6 +866,8 @@ application_service_appstore_application_add (ApplicationServiceAppstore * appst app->guide = NULL; app->currently_free = FALSE; app->ordering_index = 0; + app->approved_by = NULL; + app->visible_state = VISIBLE_STATE_HIDDEN; /* Get the DBus proxy for the NotificationItem interface */ GError * error = NULL; @@ -933,27 +960,40 @@ application_service_appstore_application_add (ApplicationServiceAppstore * appst return; } -/* Removes an application. Currently only works for the apps - that are shown. /TODO Need to fix that. */ -void -application_service_appstore_application_remove (ApplicationServiceAppstore * appstore, const gchar * dbus_name, const gchar * dbus_object) +/* Looks for an application in the list of applications */ +static Application * +find_application (ApplicationServiceAppstore * appstore, const gchar * address, const gchar * object) { - g_return_if_fail(IS_APPLICATION_SERVICE_APPSTORE(appstore)); - g_return_if_fail(dbus_name != NULL && dbus_name[0] != '\0'); - g_return_if_fail(dbus_object != NULL && dbus_object[0] != '\0'); - ApplicationServiceAppstorePrivate * priv = appstore->priv; GList * listpntr; for (listpntr = priv->applications; listpntr != NULL; listpntr = g_list_next(listpntr)) { Application * app = (Application *)listpntr->data; - if (!g_strcmp0(app->dbus_name, dbus_name) && !g_strcmp0(app->dbus_object, dbus_object)) { - application_removed_cb(NULL, app); - break; /* NOTE: Must break as the list will become inconsistent */ + if (!g_strcmp0(app->dbus_name, address) && !g_strcmp0(app->dbus_object, object)) { + return app; } } + return NULL; +} + +/* Removes an application. Currently only works for the apps + that are shown. */ +void +application_service_appstore_application_remove (ApplicationServiceAppstore * appstore, const gchar * dbus_name, const gchar * dbus_object) +{ + g_return_if_fail(IS_APPLICATION_SERVICE_APPSTORE(appstore)); + g_return_if_fail(dbus_name != NULL && dbus_name[0] != '\0'); + g_return_if_fail(dbus_object != NULL && dbus_object[0] != '\0'); + + Application * app = find_application(appstore, dbus_name, dbus_object); + if (app != NULL) { + application_removed_cb(NULL, app); + } else { + g_warning("Unable to find application %s:%s", dbus_name, dbus_object); + } + return; } @@ -978,6 +1018,10 @@ _application_service_server_get_applications (ApplicationServiceAppstore * appst for (listpntr = priv->applications; listpntr != NULL; listpntr = g_list_next(listpntr)) { Application * app = (Application *)listpntr->data; + if (app->visible_state == VISIBLE_STATE_HIDDEN) { + continue; + } + GValueArray * values = g_value_array_new(5); GValue value = {0}; @@ -1030,15 +1074,32 @@ _application_service_server_get_applications (ApplicationServiceAppstore * appst return TRUE; } +/* Removes and approver from our list of approvers and + then sees if that changes our status. Most likely this + could make us visible if this approver rejected us. */ +static void +remove_approver (gpointer papp, gpointer pproxy) +{ + Application * app = (Application *)papp; + app->approved_by = g_list_remove(app->approved_by, pproxy); + apply_status(app); + return; +} + /* Frees the data associated with an approver */ static void approver_free (gpointer papprover, gpointer user_data) { Approver * approver = (Approver *)papprover; g_return_if_fail(approver != NULL); + + ApplicationServiceAppstore * appstore = APPLICATION_SERVICE_APPSTORE(user_data); + g_list_foreach(appstore->priv->applications, remove_approver, approver->proxy); if (approver->proxy != NULL) { - g_object_unref(approver->proxy); + if (!approver->destroy_by_proxy) { + g_object_unref(approver->proxy); + } approver->proxy = NULL; } @@ -1050,7 +1111,21 @@ approver_free (gpointer papprover, gpointer user_data) static void approver_request_cb (DBusGProxy *proxy, gboolean OUT_approved, GError *error, gpointer userdata) { - g_debug("Approver responded: %s", OUT_approved ? "approve" : "rejected"); + if (error == NULL) { + g_debug("Approver responded: %s", OUT_approved ? "approve" : "rejected"); + } else { + g_debug("Approver responded error: %s", error->message); + } + + Application * app = (Application *)userdata; + + if (OUT_approved || error != NULL) { + app->approved_by = g_list_prepend(app->approved_by, proxy); + } else { + app->approved_by = g_list_remove(app->approved_by, proxy); + } + + apply_status(app); return; } @@ -1073,6 +1148,72 @@ check_with_new_approver (gpointer papp, gpointer papprove) return; } +/* Look through all the approvers and find the one with a given + proxy. */ +static gint +approver_find_by_proxy (gconstpointer papprover, gconstpointer pproxy) +{ + Approver * approver = (Approver *)papprover; + + if (approver->proxy == pproxy) { + return 0; + } + + return -1; +} + +/* Tracks when a proxy gets destroyed so that we know that the + approver has dropped off the bus. */ +static void +approver_destroyed (gpointer pproxy, gpointer pappstore) +{ + ApplicationServiceAppstore * appstore = APPLICATION_SERVICE_APPSTORE(pappstore); + + GList * lapprover = g_list_find_custom(appstore->priv->approvers, pproxy, approver_find_by_proxy); + if (lapprover == NULL) { + g_warning("Approver proxy died, but we don't seem to have that approver."); + return; + } + + Approver * approver = (Approver *)lapprover->data; + approver->destroy_by_proxy = TRUE; + + appstore->priv->approvers = g_list_remove(appstore->priv->approvers, approver); + approver_free(approver, appstore); + + return; +} + +/* A signal when an approver changes the why that it thinks about + a particular indicator. */ +void +approver_revise_judgement (DBusGProxy * proxy, gboolean new_status, gchar * address, DBusGProxy * get_path, gpointer user_data) +{ + g_return_if_fail(IS_APPLICATION_SERVICE_APPSTORE(user_data)); + g_return_if_fail(address != NULL && address[0] != '\0'); + g_return_if_fail(get_path != NULL); + const gchar * path = dbus_g_proxy_get_path(get_path); + g_return_if_fail(path != NULL && path[0] != '\0'); + + ApplicationServiceAppstore * appstore = APPLICATION_SERVICE_APPSTORE(user_data); + + Application * app = find_application(appstore, address, path); + + if (app == NULL) { + g_warning("Unable to update approver status of application (%s:%s) as it was not found", address, path); + return; + } + + if (new_status) { + app->approved_by = g_list_prepend(app->approved_by, proxy); + } else { + app->approved_by = g_list_remove(app->approved_by, proxy); + } + apply_status(app); + + return; +} + /* Adds a new approver to the app store */ void application_service_appstore_approver_add (ApplicationServiceAppstore * appstore, const gchar * dbus_name, const gchar * dbus_object) @@ -1083,6 +1224,7 @@ application_service_appstore_approver_add (ApplicationServiceAppstore * appstore ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE (appstore); Approver * approver = g_new0(Approver, 1); + approver->destroy_by_proxy = FALSE; GError * error = NULL; approver->proxy = dbus_g_proxy_new_for_name_owner(priv->bus, @@ -1097,6 +1239,20 @@ application_service_appstore_approver_add (ApplicationServiceAppstore * appstore return; } + g_signal_connect(G_OBJECT(approver->proxy), "destroy", G_CALLBACK(approver_destroyed), appstore); + + dbus_g_proxy_add_signal(approver->proxy, + "ReviseJudgement", + G_TYPE_BOOLEAN, + G_TYPE_STRING, + DBUS_TYPE_G_OBJECT_PATH, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal(approver->proxy, + "ReviseJudgement", + G_CALLBACK(approver_revise_judgement), + appstore, + NULL); + priv->approvers = g_list_prepend(priv->approvers, approver); g_list_foreach(priv->applications, check_with_new_approver, approver); diff --git a/src/application-service-marshal.c b/src/application-service-marshal.c index db095ce..e109d7c 100644 --- a/src/application-service-marshal.c +++ b/src/application-service-marshal.c @@ -210,3 +210,42 @@ _application_service_marshal_VOID__STRING_STRING (GClosure *closure, data2); } +/* VOID:BOOL,STRING,OBJECT (./application-service-marshal.list:23) */ +void +_application_service_marshal_VOID__BOOLEAN_STRING_OBJECT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__BOOLEAN_STRING_OBJECT) (gpointer data1, + gboolean arg_1, + gpointer arg_2, + gpointer arg_3, + gpointer data2); + register GMarshalFunc_VOID__BOOLEAN_STRING_OBJECT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 4); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__BOOLEAN_STRING_OBJECT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_boolean (param_values + 1), + g_marshal_value_peek_string (param_values + 2), + g_marshal_value_peek_object (param_values + 3), + data2); +} + diff --git a/src/application-service-marshal.list b/src/application-service-marshal.list index f432028..2b2efa5 100644 --- a/src/application-service-marshal.list +++ b/src/application-service-marshal.list @@ -20,3 +20,4 @@ VOID: STRING, INT, STRING, STRING, STRING, STRING, STRING VOID: INT, STRING, STRING VOID: INT, STRING VOID: STRING, STRING +VOID: BOOL, STRING, OBJECT diff --git a/src/application-service-watcher.c b/src/application-service-watcher.c index 4fe3bc7..5b77620 100644 --- a/src/application-service-watcher.c +++ b/src/application-service-watcher.c @@ -34,7 +34,7 @@ static gboolean _notification_watcher_server_register_status_notifier_item (Appl static gboolean _notification_watcher_server_registered_status_notifier_items (ApplicationServiceWatcher * appwatcher, GArray ** apps); static gboolean _notification_watcher_server_protocol_version (ApplicationServiceWatcher * appwatcher, char ** version); static gboolean _notification_watcher_server_register_notification_host (ApplicationServiceWatcher * appwatcher, const gchar * host); -static gboolean _notification_watcher_server_register_notification_approver (ApplicationServiceWatcher * appwatcher, const gchar * path, const GArray * categories, DBusGMethodInvocation * method); +static gboolean _notification_watcher_server_x_ayatana_register_notification_approver (ApplicationServiceWatcher * appwatcher, const gchar * path, const GArray * categories, DBusGMethodInvocation * method); static gboolean _notification_watcher_server_is_notification_host_registered (ApplicationServiceWatcher * appwatcher, gboolean * haveHost); static void get_name_cb (DBusGProxy * proxy, guint status, GError * error, gpointer data); @@ -252,7 +252,7 @@ get_name_cb (DBusGProxy * proxy, guint status, GError * error, gpointer data) } static gboolean -_notification_watcher_server_register_notification_approver (ApplicationServiceWatcher * appwatcher, const gchar * path, const GArray * categories, DBusGMethodInvocation * method) +_notification_watcher_server_x_ayatana_register_notification_approver (ApplicationServiceWatcher * appwatcher, const gchar * path, const GArray * categories, DBusGMethodInvocation * method) { ApplicationServiceWatcherPrivate * priv = APPLICATION_SERVICE_WATCHER_GET_PRIVATE(appwatcher); diff --git a/src/notification-approver.xml b/src/notification-approver.xml index b1e69b9..4a8e39b 100644 --- a/src/notification-approver.xml +++ b/src/notification-approver.xml @@ -18,5 +18,12 @@ <arg type="b" name="approved" direction="out" /> </method> +<!-- Signals --> + <signal name="ReviseJudgement"> + <arg type="b" name="approved" direction="out" /> + <arg type="s" name="address" direction="out" /> + <arg type="o" name="path" direction="out" /> + </signal> + </interface> </node> diff --git a/src/notification-watcher.xml b/src/notification-watcher.xml index cc7882d..5f19dd2 100644 --- a/src/notification-watcher.xml +++ b/src/notification-watcher.xml @@ -22,7 +22,7 @@ <method name="IsNotificationHostRegistered"> <arg type="b" name="hasHost" direction="out" /> </method> - <method name="RegisterNotificationApprover"> + <method name="XAyatanaRegisterNotificationApprover"> <annotation name="org.freedesktop.DBus.GLib.Async" value="true" /> <!-- The path where to find the approver interface --> <arg type="o" name="path" direction="in" /> |