commit b31e9dfc3949727b9be2727b813b52c060a9174f
Author: Vitaly Minko <vitaly.minko@gmail.com>
Date:   Fri Oct 29 20:32:30 2010 +0400

    Make AccountChooser filters asynchronous to let them handle newly connected accounts properly (#633481).

diff --git a/libempathy-gtk/empathy-account-chooser.c b/libempathy-gtk/empathy-account-chooser.c
index 5e261eb..f077827 100644
--- a/libempathy-gtk/empathy-account-chooser.c
+++ b/libempathy-gtk/empathy-account-chooser.c
@@ -74,6 +74,12 @@ typedef struct {
 	gboolean               set;
 } SetAccountData;
 
+typedef struct {
+	EmpathyAccountChooser *chooser;
+	TpAccount             *account;
+	GtkTreeIter           *iter;
+} FilterResultCallbackData;
+
 /* Distinguishes between store entries which are actually accounts, and special
  * items like the "All" entry and the separator below it, so they can be sorted
  * correctly. Higher-numbered entries will sort earlier.
@@ -724,33 +730,26 @@ account_chooser_account_remove_foreach (TpAccount             *account,
 }
 
 static void
-account_chooser_update_iter (EmpathyAccountChooser *chooser,
-			     GtkTreeIter           *iter)
+account_chooser_filter_ready_cb (gboolean is_enabled,
+				 gpointer data)
 {
+	FilterResultCallbackData  *fr_data = data;
+	EmpathyAccountChooser     *chooser;
 	EmpathyAccountChooserPriv *priv;
+	TpAccount                 *account;
+	GtkTreeIter               *iter;
 	GtkListStore              *store;
 	GtkComboBox               *combobox;
-	TpAccount                 *account;
 	const gchar               *icon_name;
-	gboolean                   is_enabled = TRUE;
 
+	chooser = fr_data->chooser;
 	priv = GET_PRIV (chooser);
-
+	account = fr_data->account;
+	iter = fr_data->iter;
 	combobox = GTK_COMBO_BOX (chooser);
 	store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox));
 
-	gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
-			    COL_ACCOUNT_POINTER, &account,
-			    -1);
-
-	/* Skip rows without account associated */
-	if (account == NULL)
-		return;
-
 	icon_name = tp_account_get_icon_name (account);
-	if (priv->filter) {
-		is_enabled = priv->filter (account, priv->filter_data);
-	}
 
 	gtk_list_store_set (store, iter,
 			    COL_ACCOUNT_IMAGE, icon_name,
@@ -766,6 +765,43 @@ account_chooser_update_iter (EmpathyAccountChooser *chooser,
 	}
 
 	g_object_unref (account);
+	g_free (iter);
+	g_slice_free (FilterResultCallbackData, fr_data);
+}
+
+static void
+account_chooser_update_iter (EmpathyAccountChooser *chooser,
+			     GtkTreeIter           *iter)
+{
+	EmpathyAccountChooserPriv *priv;
+	GtkListStore              *store;
+	GtkComboBox               *combobox;
+	TpAccount                 *account;
+	FilterResultCallbackData  *data;
+
+	priv = GET_PRIV (chooser);
+
+	combobox = GTK_COMBO_BOX (chooser);
+	store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox));
+
+	gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+			    COL_ACCOUNT_POINTER, &account,
+			    -1);
+
+	/* Skip rows without account associated */
+	if (account == NULL)
+		return;
+
+	data = g_slice_new0 (FilterResultCallbackData);
+	data->chooser = chooser;
+	data->account = account;
+	data->iter = g_memdup (iter, sizeof (GtkTreeIter));
+
+	if (priv->filter)
+		priv->filter (account, account_chooser_filter_ready_cb,
+			      (gpointer) data, priv->filter_data);
+	else
+		account_chooser_filter_ready_cb (TRUE, (gpointer) data);
 }
 
 static void
@@ -879,19 +915,26 @@ empathy_account_chooser_set_filter (EmpathyAccountChooser           *chooser,
 /**
  * empathy_account_chooser_filter_is_connected:
  * @account: a #TpAccount
+ * @callback: an #EmpathyAccountChooserFilterResultCallback accepting the result
+ * @callback_data: data passed to the @callback
  * @user_data: user data or %NULL
  *
  * A useful #EmpathyAccountChooserFilterFunc that one could pass into
  * empathy_account_chooser_set_filter() and only show connected accounts.
  *
- * Return value: Whether @account is connected
+ * Returns (via the callback) TRUE is @account is connected
  */
-gboolean
-empathy_account_chooser_filter_is_connected (TpAccount *account,
-					     gpointer   user_data)
+void
+empathy_account_chooser_filter_is_connected (
+	TpAccount                                 *account,
+	EmpathyAccountChooserFilterResultCallback  callback,
+	gpointer                                   callback_data,
+	gpointer                                   user_data)
 {
-	return (tp_account_get_connection_status (account, NULL)
-	    == TP_CONNECTION_STATUS_CONNECTED);
+	gboolean is_connected =
+		tp_account_get_connection_status (account, NULL)
+		== TP_CONNECTION_STATUS_CONNECTED;
+	callback (is_connected, callback_data);
 }
 
 gboolean
diff --git a/libempathy-gtk/empathy-account-chooser.h b/libempathy-gtk/empathy-account-chooser.h
index f80d700..dfd372c 100644
--- a/libempathy-gtk/empathy-account-chooser.h
+++ b/libempathy-gtk/empathy-account-chooser.h
@@ -38,8 +38,26 @@ G_BEGIN_DECLS
 #define EMPATHY_IS_ACCOUNT_CHOOSER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_ACCOUNT_CHOOSER))
 #define EMPATHY_ACCOUNT_CHOOSER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_ACCOUNT_CHOOSER, EmpathyAccountChooserClass))
 
-typedef gboolean (* EmpathyAccountChooserFilterFunc) (TpAccount *account,
-						      gpointer   user_data);
+/**
+ * EmpathyAccountChooserFilterResultCallback:
+ * @is_enabled: indicated whether the account should be enabled
+ * @user_data: user data passed to the callback
+ */
+typedef void (* EmpathyAccountChooserFilterResultCallback) (gboolean is_enabled,
+							    gpointer user_data);
+
+/**
+ * EmpathyAccountChooserFilterFunc:
+ * @account: a #TpAccount
+ * @callback: an #EmpathyAccountChooserFilterResultCallback accepting the result
+ * @callback_data: data passed to the @callback
+ * @user_data: user data passed to the callback
+ */
+typedef void (* EmpathyAccountChooserFilterFunc) (
+	TpAccount                                 *account,
+	EmpathyAccountChooserFilterResultCallback  callback,
+	gpointer                                   callback_data,
+	gpointer                                   user_data);
 
 
 typedef struct _EmpathyAccountChooser      EmpathyAccountChooser;
@@ -69,7 +87,9 @@ void           empathy_account_chooser_set_has_all_option (EmpathyAccountChooser
 void           empathy_account_chooser_set_filter         (EmpathyAccountChooser *chooser,
 							   EmpathyAccountChooserFilterFunc filter,
 							   gpointer               user_data);
-gboolean       empathy_account_chooser_filter_is_connected (TpAccount            *account,
+void           empathy_account_chooser_filter_is_connected (TpAccount             *account,
+							   EmpathyAccountChooserFilterResultCallback callback,
+							   gpointer               callback_data,
 							   gpointer               user_data);
 gboolean       empathy_account_chooser_is_ready (EmpathyAccountChooser *chooser);
 
diff --git a/libempathy-gtk/empathy-contact-dialogs.c b/libempathy-gtk/empathy-contact-dialogs.c
index 9189d41..7aba1fd 100644
--- a/libempathy-gtk/empathy-contact-dialogs.c
+++ b/libempathy-gtk/empathy-contact-dialogs.c
@@ -331,24 +331,28 @@ empathy_contact_personal_dialog_show (GtkWindow *parent)
  *  New contact dialog
  */
 
-static gboolean
-can_add_contact_to_account (TpAccount *account,
-			    gpointer   user_data)
+static void
+can_add_contact_to_account (TpAccount                                 *account,
+			    EmpathyAccountChooserFilterResultCallback  callback,
+			    gpointer                                   callback_data,
+			    gpointer                                   user_data)
 {
 	EmpathyContactManager *contact_manager;
 	TpConnection          *connection;
 	gboolean               result;
 
 	connection = tp_account_get_connection (account);
-	if (connection == NULL)
-		return FALSE;
+	if (connection == NULL) {
+		callback (FALSE, callback_data);
+		return;
+	}
 
 	contact_manager = empathy_contact_manager_dup_singleton ();
 	result = empathy_contact_manager_get_flags_for_connection (
 		contact_manager, connection) & EMPATHY_CONTACT_LIST_CAN_ADD;
 	g_object_unref (contact_manager);
 
-	return result;
+	callback (result, callback_data);
 }
 
 static void
diff --git a/libempathy-gtk/empathy-contact-selector-dialog.c b/libempathy-gtk/empathy-contact-selector-dialog.c
index 62b9027..3ed7f30 100644
--- a/libempathy-gtk/empathy-contact-selector-dialog.c
+++ b/libempathy-gtk/empathy-contact-selector-dialog.c
@@ -243,8 +243,10 @@ entry_activate_cb (GtkEntry *entry,
   gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
 }
 
-static gboolean
+static void
 account_chooser_filter (TpAccount *account,
+    EmpathyAccountChooserFilterResultCallback callback,
+    gpointer callback_data,
     gpointer user_data)
 {
   EmpathyContactSelectorDialog *self = user_data;
@@ -252,9 +254,13 @@ account_chooser_filter (TpAccount *account,
       EMPATHY_CONTACT_SELECTOR_DIALOG_GET_CLASS (self);
 
   if (class->account_filter == NULL)
-    return empathy_account_chooser_filter_is_connected (account, user_data);
+    {
+      empathy_account_chooser_filter_is_connected (
+          account,callback, callback_data, user_data);
+      return;
+    }
 
-  return class->account_filter (self, account);
+  class->account_filter (self, callback, callback_data, account);
 }
 
 static gboolean
diff --git a/libempathy-gtk/empathy-contact-selector-dialog.h b/libempathy-gtk/empathy-contact-selector-dialog.h
index 402f1c7..27d689c 100644
--- a/libempathy-gtk/empathy-contact-selector-dialog.h
+++ b/libempathy-gtk/empathy-contact-selector-dialog.h
@@ -29,6 +29,8 @@
 #include <telepathy-glib/connection.h>
 #include <telepathy-glib/account.h>
 
+#include "empathy-account-chooser.h"
+
 G_BEGIN_DECLS
 
 typedef struct _EmpathyContactSelectorDialog EmpathyContactSelectorDialog;
@@ -38,7 +40,9 @@ typedef struct _EmpathyContactSelectorDialogClass \
 struct _EmpathyContactSelectorDialogClass {
   GtkDialogClass parent_class;
 
-  gboolean (*account_filter) (EmpathyContactSelectorDialog *self,
+  void (*account_filter) (EmpathyContactSelectorDialog *self,
+      EmpathyAccountChooserFilterResultCallback callback,
+      gpointer callback_data,
       TpAccount *account);
   gboolean (*contact_filter) (EmpathyContactSelectorDialog *self,
       const char *id);
diff --git a/libempathy-gtk/empathy-individual-dialogs.c b/libempathy-gtk/empathy-individual-dialogs.c
index 85ed7d1..2c98010 100644
--- a/libempathy-gtk/empathy-individual-dialogs.c
+++ b/libempathy-gtk/empathy-individual-dialogs.c
@@ -43,8 +43,10 @@ static GtkWidget *new_individual_dialog = NULL;
  *  New contact dialog
  */
 
-static gboolean
+static void
 can_add_contact_to_account (TpAccount *account,
+    EmpathyAccountChooserFilterResultCallback callback,
+    gpointer callback_data,
     gpointer user_data)
 {
   EmpathyIndividualManager *individual_manager;
@@ -53,13 +55,16 @@ can_add_contact_to_account (TpAccount *account,
 
   connection = tp_account_get_connection (account);
   if (connection == NULL)
-    return FALSE;
+    {
+      callback (FALSE, callback_data);
+      return;
+    }
 
   individual_manager = empathy_individual_manager_dup_singleton ();
   result = empathy_connection_can_add_personas (connection);
   g_object_unref (individual_manager);
 
-  return result;
+  callback (result, callback_data);
 }
 
 static void
diff --git a/libempathy-gtk/empathy-new-call-dialog.c b/libempathy-gtk/empathy-new-call-dialog.c
index 3d51283..74dbe4f 100644
--- a/libempathy-gtk/empathy-new-call-dialog.c
+++ b/libempathy-gtk/empathy-new-call-dialog.c
@@ -50,6 +50,11 @@ G_DEFINE_TYPE(EmpathyNewCallDialog, empathy_new_call_dialog,
 
 typedef struct _EmpathyNewCallDialogPriv EmpathyNewCallDialogPriv;
 
+typedef struct {
+  EmpathyAccountChooserFilterResultCallback callback;
+  gpointer                                  user_data;
+} FilterCallbackData;
+
 struct _EmpathyNewCallDialogPriv {
   GtkWidget *check_video;
 };
@@ -136,36 +141,57 @@ out:
   gtk_widget_destroy (GTK_WIDGET (dialog));
 }
 
-static gboolean
+static void
+find_requestable_channel_classes_cd (GList    *classes,
+				     gpointer  user_data)
+{
+  FilterCallbackData *data = user_data;
+
+  if (classes == NULL)
+    data->callback (FALSE, data->user_data);
+  else
+    data->callback (TRUE, data->user_data);
+
+  g_slice_free (FilterCallbackData, data);
+}
+
+static void
 empathy_new_call_dialog_account_filter (EmpathyContactSelectorDialog *dialog,
+    EmpathyAccountChooserFilterResultCallback callback,
+    gpointer callback_data,
     TpAccount *account)
 {
   TpConnection *connection;
   EmpathyDispatcher *dispatcher;
-  GList *classes;
+  FilterCallbackData *cb_data;
 
   if (tp_account_get_connection_status (account, NULL) !=
       TP_CONNECTION_STATUS_CONNECTED)
-    return FALSE;
+    {
+      callback (FALSE, callback_data);
+      return;
+    }
 
   /* check if CM supports calls */
   connection = tp_account_get_connection (account);
   if (connection == NULL)
-    return FALSE;
+    {
+      callback (FALSE, callback_data);
+      return;
+    }
 
   dispatcher = empathy_dispatcher_dup_singleton ();
 
-  classes = empathy_dispatcher_find_requestable_channel_classes
-    (dispatcher, connection, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
-     TP_HANDLE_TYPE_CONTACT, NULL);
+  cb_data = g_slice_new0 (FilterCallbackData);
+  cb_data->callback = callback;
+  cb_data->user_data = callback_data;
+  empathy_dispatcher_find_requestable_channel_classes_async (
+      dispatcher, connection,
+      TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, TP_HANDLE_TYPE_CONTACT,
+      find_requestable_channel_classes_cd, cb_data,
+      NULL);
 
   g_object_unref (dispatcher);
-
-  if (classes == NULL)
-    return FALSE;
-
-  g_list_free (classes);
-  return TRUE;
 }
 
 static GObject *
diff --git a/libempathy-gtk/empathy-new-message-dialog.c b/libempathy-gtk/empathy-new-message-dialog.c
index a174814..f2f1be5 100644
--- a/libempathy-gtk/empathy-new-message-dialog.c
+++ b/libempathy-gtk/empathy-new-message-dialog.c
@@ -42,6 +42,11 @@
 #include "empathy-new-message-dialog.h"
 #include "empathy-account-chooser.h"
 
+typedef struct {
+  EmpathyAccountChooserFilterResultCallback callback;
+  gpointer                                  user_data;
+} FilterCallbackData;
+
 static EmpathyNewMessageDialog *dialog_singleton = NULL;
 
 G_DEFINE_TYPE(EmpathyNewMessageDialog, empathy_new_message_dialog,
@@ -77,36 +82,57 @@ out:
   gtk_widget_destroy (GTK_WIDGET (dialog));
 }
 
-static gboolean
+static void
+find_requestable_channel_classes_cd (GList    *classes,
+				     gpointer  user_data)
+{
+  FilterCallbackData *data = user_data;
+
+  if (classes == NULL)
+    data->callback (FALSE, data->user_data);
+  else
+    data->callback (TRUE, data->user_data);
+
+  g_slice_free (FilterCallbackData, data);
+}
+
+static void
 empathy_new_message_account_filter (EmpathyContactSelectorDialog *dialog,
+    EmpathyAccountChooserFilterResultCallback callback,
+    gpointer callback_data,
     TpAccount *account)
 {
   TpConnection *connection;
   EmpathyDispatcher *dispatcher;
-  GList *classes;
+  FilterCallbackData *cb_data;
 
   if (tp_account_get_connection_status (account, NULL) !=
       TP_CONNECTION_STATUS_CONNECTED)
-    return FALSE;
+    {
+      callback (FALSE, callback_data);
+      return;
+    }
 
   /* check if CM supports 1-1 text chat */
   connection = tp_account_get_connection (account);
   if (connection == NULL)
-    return FALSE;
+    {
+      callback (FALSE, callback_data);
+      return;
+    }
 
   dispatcher = empathy_dispatcher_dup_singleton ();
 
-  classes = empathy_dispatcher_find_requestable_channel_classes
-    (dispatcher, connection, TP_IFACE_CHANNEL_TYPE_TEXT,
-     TP_HANDLE_TYPE_CONTACT, NULL);
+  cb_data = g_slice_new0 (FilterCallbackData);
+  cb_data->callback = callback;
+  cb_data->user_data = callback_data;
+  empathy_dispatcher_find_requestable_channel_classes_async (
+      dispatcher, connection,
+      TP_IFACE_CHANNEL_TYPE_TEXT, TP_HANDLE_TYPE_CONTACT,
+      find_requestable_channel_classes_cd, cb_data,
+      NULL);
 
   g_object_unref (dispatcher);
-
-  if (classes == NULL)
-    return FALSE;
-
-  g_list_free (classes);
-  return TRUE;
 }
 
 static GObject *
diff --git a/src/empathy-new-chatroom-dialog.c b/src/empathy-new-chatroom-dialog.c
index 3808daa..88a00c2 100644
--- a/src/empathy-new-chatroom-dialog.c
+++ b/src/empathy-new-chatroom-dialog.c
@@ -25,7 +25,6 @@
 #include <config.h>
 
 #include <string.h>
-#include <stdio.h>
 
 #include <gtk/gtk.h>
 #include <glib.h>
@@ -74,6 +73,11 @@ typedef struct {
 	GtkWidget         *viewport_error;
 } EmpathyNewChatroomDialog;
 
+typedef struct {
+  EmpathyAccountChooserFilterResultCallback callback;
+  gpointer                                  user_data;
+} FilterCallbackData;
+
 enum {
 	COL_NEED_PASSWORD,
 	COL_INVITE_ONLY,
@@ -133,46 +137,69 @@ static void	new_chatroom_dialog_button_close_error_clicked_cb   (GtkButton
 
 static EmpathyNewChatroomDialog *dialog_p = NULL;
 
+
+static void
+find_requestable_channel_classes_cd (GList    *classes,
+				     gpointer  user_data)
+{
+	FilterCallbackData *data = user_data;
+
+	if (classes == NULL)
+		data->callback (FALSE, data->user_data);
+	else
+		data->callback (TRUE, data->user_data);
+
+	g_slice_free (FilterCallbackData, data);
+}
+
 /**
  * empathy_account_chooser_filter_supports_multichat:
  * @account: a #TpAccount
+ * @callback: an #EmpathyAccountChooserFilterResultCallback accepting the result
+ * @callback_data: data passed to the @callback
  * @user_data: user data or %NULL
  *
  * An #EmpathyAccountChooserFilterFunc that returns accounts that both
  * support multiuser text chat and are connected.
  *
- * Return value: TRUE if @account both supports muc and is connected
+ * Returns (via the callback) TRUE if @account both supports muc and is connected
  */
-static gboolean
-empathy_account_chooser_filter_supports_multichat (TpAccount *account,
-						   gpointer   user_data)
+static void
+empathy_account_chooser_filter_supports_multichat (
+	TpAccount                                 *account,
+	EmpathyAccountChooserFilterResultCallback  callback,
+	gpointer                                   callback_data,
+	gpointer                                   user_data)
 {
-	TpConnection *connection;
-	EmpathyDispatcher *dispatcher;
-	GList *classes;
+	TpConnection       *connection;
+	EmpathyDispatcher  *dispatcher;
+	FilterCallbackData *cb_data;
 
 	if (tp_account_get_connection_status (account, NULL) !=
-	    TP_CONNECTION_STATUS_CONNECTED)
-	return FALSE;
+	    TP_CONNECTION_STATUS_CONNECTED) {
+		callback (FALSE, callback_data);
+		return;
+	}
 
 	/* check if CM supports multiuser text chat */
 	connection = tp_account_get_connection (account);
-	if (connection == NULL)
-		return FALSE;
+	if (connection == NULL) {
+		callback (FALSE, callback_data);
+		return;
+	}
 
 	dispatcher = empathy_dispatcher_dup_singleton ();
 
-	classes = empathy_dispatcher_find_requestable_channel_classes
-		(dispatcher, connection, TP_IFACE_CHANNEL_TYPE_TEXT,
-		TP_HANDLE_TYPE_ROOM, NULL);
+	cb_data = g_slice_new0 (FilterCallbackData);
+	cb_data->callback = callback;
+	cb_data->user_data = callback_data;
+	empathy_dispatcher_find_requestable_channel_classes_async (
+		dispatcher, connection,
+		TP_IFACE_CHANNEL_TYPE_TEXT, TP_HANDLE_TYPE_ROOM,
+		find_requestable_channel_classes_cd, cb_data,
+		NULL);
 
 	g_object_unref (dispatcher);
-
-	if (classes == NULL)
-		return FALSE;
-
-	g_list_free (classes);
-	return TRUE;
 }
 
 void
diff --git a/tests/interactive/test-empathy-account-chooser.c b/tests/interactive/test-empathy-account-chooser.c
index d2ce7cc..7622a62 100644
--- a/tests/interactive/test-empathy-account-chooser.c
+++ b/tests/interactive/test-empathy-account-chooser.c
@@ -5,12 +5,14 @@
 #include <libempathy-gtk/empathy-ui-utils.h>
 #include <libempathy-gtk/empathy-account-chooser.h>
 
-static gboolean
+static void
 filter_func (TpAccount *account,
+    EmpathyAccountChooserFilterResultCallback callback,
+    gpointer callback_data,
     gpointer user_data)
 {
   g_assert (TP_IS_ACCOUNT (account));
-  return TRUE;
+  callback (TRUE, callback_data);
 }
 
 int
