# HG changeset patch
# Node ID 56e3c432c30b
# Parent  8a8e85e9e114
diff --git a/ChangeLog.API b/ChangeLog.API
--- a/ChangeLog.API
+++ b/ChangeLog.API
@@ -499,6 +499,7 @@
 		* purple_conversation_get_remote_smileys
 		* purple_conversation_get_smiley
 		* purple_conversation_is_logging
+		* purple_conversation_set_account
 		* purple_conversations_add. Use purple_conversation_manager_register
 		  instead.
 		* purple_conversations_remove. Use
diff --git a/libpurple/account.c b/libpurple/account.c
--- a/libpurple/account.c
+++ b/libpurple/account.c
@@ -807,26 +807,12 @@
 static void
 purple_account_finalize(GObject *object)
 {
-	GList *l;
 	PurpleAccount *account = PURPLE_ACCOUNT(object);
-	PurpleConversationManager *manager = NULL;
 
 	purple_debug_info("account", "Destroying account %p", account);
 
 	purple_account_free_notify_settings(account);
 
-	manager = purple_conversation_manager_get_default();
-	l = purple_conversation_manager_get_all(manager);
-	while(l != NULL) {
-		PurpleConversation *conv = PURPLE_CONVERSATION(l->data);
-
-		if (purple_conversation_get_account(conv) == account) {
-			purple_conversation_set_account(conv, NULL);
-		}
-
-		l = g_list_delete_link(l, l);
-	}
-
 	purple_account_set_status_types(account, NULL);
 
 	g_clear_object(&account->proxy_info);
diff --git a/libpurple/purpleconversation.h b/libpurple/purpleconversation.h
--- a/libpurple/purpleconversation.h
+++ b/libpurple/purpleconversation.h
@@ -216,18 +216,6 @@
 void purple_conversation_set_conversation_type(PurpleConversation *conversation, PurpleConversationType type);
 
 /**
- * purple_conversation_set_account:
- * @conv: The conversation.
- * @account: The purple_account.
- *
- * Sets the specified conversation's purple_account.
- *
- * This purple_account represents the user using purple, not the person the user
- * is having a conversation/chat/flame with.
- */
-void purple_conversation_set_account(PurpleConversation *conv, PurpleAccount *account);
-
-/**
  * purple_conversation_get_account:
  * @conv: The conversation.
  *
@@ -649,6 +637,50 @@
 void purple_conversation_set_creator(PurpleConversation *conversation, PurpleContactInfo *creator);
 
 /**
+ * purple_conversation_get_online:
+ * @conversation: The instance.
+ *
+ * Gets whether or not @conversation is online.
+ *
+ * A conversation is considered offline when messages cannot be sent or
+ * received.
+ *
+ * Returns: %TRUE if messages can be sent and received from @conversation.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_conversation_get_online(PurpleConversation *conversation);
+
+/**
+ * purple_conversation_set_online:
+ * @conversation: The instance.
+ * @online: Whether or not the conversation is online.
+ *
+ * Sets whether or not the conversation is online, or able to send and receive
+ * messages.
+ *
+ * Since: 3.0.0
+ */
+void purple_conversation_set_online(PurpleConversation *conversation, gboolean online);
+
+/**
+ * purple_conversation_get_federated:
+ * @conversation: The instance.
+ *
+ * Gets whether or not @conversation is federated.
+ *
+ * Depending on the underlying protocol, conversations can be federated, which
+ * means they can be running on a different server. When a protocol creates a
+ * federated conversation, it takes the responsibility of managing the
+ * [property@Conversation:online] state of the conversation.
+ *
+ * Returns: %TRUE if @conversation is federated otherwise %FALSE.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_conversation_get_federated(PurpleConversation *conversation);
+
+/**
  * purple_conversation_get_tags:
  * @conversation: The instance.
  *
diff --git a/libpurple/purpleconversation.c b/libpurple/purpleconversation.c
--- a/libpurple/purpleconversation.c
+++ b/libpurple/purpleconversation.c
@@ -52,6 +52,8 @@
 	gboolean favorite;
 	GDateTime *created_on;
 	PurpleContactInfo *creator;
+	gboolean online;
+	gboolean federated;
 	PurpleTags *tags;
 	GListStore *members;
 
@@ -75,6 +77,8 @@
 	PROP_FAVORITE,
 	PROP_CREATED_ON,
 	PROP_CREATOR,
+	PROP_ONLINE,
+	PROP_FEDERATED,
 	PROP_TAGS,
 	PROP_MEMBERS,
 	PROP_MESSAGES,
@@ -92,6 +96,10 @@
 G_DEFINE_TYPE_WITH_PRIVATE(PurpleConversation, purple_conversation,
                            G_TYPE_OBJECT);
 
+static void purple_conversation_account_connected_cb(GObject *obj,
+                                                     GParamSpec *pspec,
+                                                     gpointer data);
+
 /**************************************************************************
  * Helpers
  **************************************************************************/
@@ -111,6 +119,45 @@
 	}
 }
 
+static void
+purple_conversation_set_account(PurpleConversation *conv,
+                                PurpleAccount *account)
+{
+	PurpleConversationPrivate *priv = NULL;
+
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
+	priv = purple_conversation_get_instance_private(conv);
+
+	if(g_set_object(&priv->account, account)) {
+		if(PURPLE_IS_ACCOUNT(priv->account)) {
+			g_signal_connect_object(account, "notify::connected",
+			                        G_CALLBACK(purple_conversation_account_connected_cb),
+			                        conv, 0);
+		}
+
+		purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_ACCOUNT);
+
+		g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_ACCOUNT]);
+	}
+}
+
+static void
+purple_conversation_set_federated(PurpleConversation *conversation,
+                                  gboolean federated)
+{
+	PurpleConversationPrivate *priv = NULL;
+
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
+
+	priv = purple_conversation_get_instance_private(conversation);
+	if(priv->federated != federated) {
+		priv->federated = federated;
+
+		g_object_notify_by_pspec(G_OBJECT(conversation),
+		                         properties[PROP_FEDERATED]);
+	}
+}
+
 static gboolean
 purple_conversation_check_member_equal(gconstpointer a, gconstpointer b) {
 	PurpleConversationMember *member_a = (PurpleConversationMember *)a;
@@ -262,6 +309,33 @@
 }
 
 /**************************************************************************
+ * Callbacks
+ **************************************************************************/
+static void
+purple_conversation_account_connected_cb(GObject *obj,
+                                         G_GNUC_UNUSED GParamSpec *pspec,
+                                         gpointer data)
+{
+	PurpleConversation *conversation = data;
+	PurpleConversationPrivate *priv = NULL;
+	gboolean connected = purple_account_is_connected(PURPLE_ACCOUNT(obj));
+
+	priv = purple_conversation_get_instance_private(conversation);
+
+	if(priv->federated) {
+		/* If the account changed to connected and the conversation is
+		 * federated we do nothing. But if the account went offline, we can
+		 * safely set the conversation to offline.
+		 */
+		if(!connected) {
+			purple_conversation_set_online(conversation, FALSE);
+		}
+	} else {
+		purple_conversation_set_online(conversation, connected);
+	}
+}
+
+/**************************************************************************
  * GObject Implementation
  **************************************************************************/
 static void
@@ -325,6 +399,13 @@
 		case PROP_CREATOR:
 			purple_conversation_set_creator(conv, g_value_get_object(value));
 			break;
+		case PROP_ONLINE:
+			purple_conversation_set_online(conv, g_value_get_boolean(value));
+			break;
+		case PROP_FEDERATED:
+			purple_conversation_set_federated(conv,
+			                                  g_value_get_boolean(value));
+			break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
 			break;
@@ -389,6 +470,13 @@
 		case PROP_CREATOR:
 			g_value_set_object(value, purple_conversation_get_creator(conv));
 			break;
+		case PROP_ONLINE:
+			g_value_set_boolean(value, purple_conversation_get_online(conv));
+			break;
+		case PROP_FEDERATED:
+			g_value_set_boolean(value,
+			                    purple_conversation_get_federated(conv));
+			break;
 		case PROP_TAGS:
 			g_value_set_object(value, purple_conversation_get_tags(conv));
 			break;
@@ -550,7 +638,7 @@
 		"account", "Account",
 		"The account for the conversation.",
 		PURPLE_TYPE_ACCOUNT,
-		G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
 
 	properties[PROP_NAME] = g_param_spec_string(
 		"name", "Name",
@@ -711,6 +799,50 @@
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
+	 * PurpleConversation:online:
+	 *
+	 * Whether or not the conversation is able to send and receive messages.
+	 *
+	 * This is typically tied to whether or not the account that this
+	 * conversation belongs is online or not.
+	 *
+	 * However, if a protocol supports federated conversation, it is possible
+	 * for a conversation to be offline if the server it is on is currently
+	 * unreachable.
+	 *
+	 * See Also: [property@Conversation:federated].
+	 *
+	 * Since: 3.0.0
+	 */
+	properties[PROP_ONLINE] = g_param_spec_boolean(
+		"online", "online",
+		"Whether or not the conversation can send and receive messages.",
+		TRUE,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleConversation:federated:
+	 *
+	 * Whether or this conversation is federated.
+	 *
+	 * This should only be set by protocols that support federated
+	 * conversations.
+	 *
+	 * When this is %TRUE the [property@Conversation:online] property will not
+	 * be automatically set to match the [property@Account:connected] property
+	 * of the account that this conversation belongs to. It is the
+	 * responsibility of the protocol to manage the online property in this
+	 * case.
+	 *
+	 * Since: 3.0.0
+	 */
+	properties[PROP_FEDERATED] = g_param_spec_boolean(
+		"federated", "federated",
+		"Whether or not this conversation is federated.",
+		FALSE,
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+	/**
 	 * PurpleConversation:tags:
 	 *
 	 * [class@Tags] for the conversation.
@@ -917,22 +1049,6 @@
 	}
 }
 
-void
-purple_conversation_set_account(PurpleConversation *conv,
-                                PurpleAccount *account)
-{
-	PurpleConversationPrivate *priv = NULL;
-
-	g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
-	priv = purple_conversation_get_instance_private(conv);
-
-	if(g_set_object(&priv->account, account)) {
-		g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_ACCOUNT]);
-
-		purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_ACCOUNT);
-	}
-}
-
 PurpleAccount *
 purple_conversation_get_account(PurpleConversation *conv) {
 	PurpleConversationPrivate *priv = NULL;
@@ -1582,6 +1698,45 @@
 	}
 }
 
+gboolean
+purple_conversation_get_online(PurpleConversation *conversation) {
+	PurpleConversationPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+	priv = purple_conversation_get_instance_private(conversation);
+
+	return priv->online;
+}
+
+void
+purple_conversation_set_online(PurpleConversation *conversation,
+                               gboolean online)
+{
+	PurpleConversationPrivate *priv = NULL;
+
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
+
+	priv = purple_conversation_get_instance_private(conversation);
+	if(priv->online != online) {
+		priv->online = online;
+
+		g_object_notify_by_pspec(G_OBJECT(conversation),
+		                         properties[PROP_ONLINE]);
+	}
+}
+
+gboolean
+purple_conversation_get_federated(PurpleConversation *conversation) {
+	PurpleConversationPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+	priv = purple_conversation_get_instance_private(conversation);
+
+	return priv->federated;
+}
+
 PurpleTags *
 purple_conversation_get_tags(PurpleConversation *conversation) {
 	PurpleConversationPrivate *priv = NULL;
