← Back to team overview

ayatana-commits team mailing list archive

[Merge] lp:~ted/indicator-application/approver-rejudge into lp:indicator-application

 

Ted Gould has proposed merging lp:~ted/indicator-application/approver-rejudge into lp:indicator-application.

Requested reviews:
  Indicator Applet Developers (indicator-applet-developers)


Allow for rejudging.
-- 
https://code.launchpad.net/~ted/indicator-application/approver-rejudge/+merge/33111
Your team ayatana-commits is subscribed to branch lp:indicator-application.
=== modified file 'src/application-service-appstore.c'
--- src/application-service-appstore.c	2010-08-11 20:55:25 +0000
+++ src/application-service-appstore.c	2010-08-19 14:33:59 +0000
@@ -67,9 +67,15 @@
 	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 @@
 	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_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 @@
 	                                  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 @@
 	}
 
 	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;
 	}
@@ -311,6 +326,7 @@
 {
 	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 +346,8 @@
 
 	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 +399,10 @@
 		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 +460,34 @@
 
 
 /* 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) {
+	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--;
+		}
+	}
+
+	if (lapp == NULL) {
 		return -1;
 	}
-
-	return g_list_position(priv->applications, applistitem);
+	
+	return count;
 }
 
 /* A simple global function for dealing with freeing the information
@@ -505,6 +539,9 @@
 	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 +555,17 @@
 	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 +580,53 @@
    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 +638,7 @@
 		}
 	}
 
-	app->status = status;
+	app->visible_state = goal_state;
 
 	return;
 }
@@ -645,7 +668,7 @@
 		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 +706,7 @@
 		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 +760,8 @@
 	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 +779,7 @@
 		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 +865,8 @@
 	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,8 +959,26 @@
 	return;
 }
 
+/* Looks for an application in the list of applications */
+static Application *
+find_application (ApplicationServiceAppstore * appstore, const gchar * address, const gchar * object)
+{
+	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, address) && !g_strcmp0(app->dbus_object, object)) {
+			return app;
+		}
+	}
+
+	return NULL;
+}
+
 /* Removes an application.  Currently only works for the apps
-   that are shown.  /TODO Need to fix that. */
+   that are shown. */
 void
 application_service_appstore_application_remove (ApplicationServiceAppstore * appstore, const gchar * dbus_name, const gchar * dbus_object)
 {
@@ -942,16 +986,11 @@
 	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 */
-		}
+	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 +1017,10 @@
 
 	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 +1073,32 @@
 	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 +1110,21 @@
 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 +1147,72 @@
 	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 +1223,7 @@
 	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 +1238,20 @@
 		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);

=== modified file 'src/application-service-marshal.list'
--- src/application-service-marshal.list	2010-08-10 14:04:41 +0000
+++ src/application-service-marshal.list	2010-08-19 14:33:59 +0000
@@ -20,3 +20,4 @@
 VOID: INT, STRING, STRING
 VOID: INT, STRING
 VOID: STRING, STRING
+VOID: BOOL, STRING, OBJECT

=== modified file 'src/application-service-watcher.c'
--- src/application-service-watcher.c	2010-07-09 20:20:13 +0000
+++ src/application-service-watcher.c	2010-08-19 14:33:59 +0000
@@ -34,7 +34,7 @@
 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 @@
 }
 
 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);
 

=== modified file 'src/notification-approver.xml'
--- src/notification-approver.xml	2010-07-01 19:29:30 +0000
+++ src/notification-approver.xml	2010-08-19 14:33:59 +0000
@@ -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>

=== modified file 'src/notification-watcher.xml'
--- src/notification-watcher.xml	2010-06-25 19:43:16 +0000
+++ src/notification-watcher.xml	2010-08-19 14:33:59 +0000
@@ -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" />


Follow ups