gobjectification: 947b2f5f: Save accounts from the account-manager, ...

sadrul at pidgin.im sadrul at pidgin.im
Sun Jun 22 14:42:00 EDT 2008


-----------------------------------------------------------------
Revision: 947b2f5f15d0fd8c782d89718d5c5e7d1b96cabd
Ancestor: 25a7956a58e2d73e89d4e5fb686847e93f8c993d
Author: sadrul at pidgin.im
Date: 2008-06-22T16:59:06
Branch: im.pidgin.gobjectification
URL: http://d.pidgin.im/viewmtn/revision/info/947b2f5f15d0fd8c782d89718d5c5e7d1b96cabd

Modified files:
        libpurple/account.c libpurple/account.h
        libpurple/accountmanager.c libpurple/accountmanager.h

ChangeLog: 

Save accounts from the account-manager, and a 'settings-changed' detailed
signal for account settings.
Currently I am using the same signal for ui-settings too. Do we want a
new signal for that, or do we want to merge the two kind of settings?

-------------- next part --------------
============================================================
--- libpurple/account.c	193239c73aed0dc6179cc31f751654278eade88e
+++ libpurple/account.c	d292477a5b8f3c09a4ba8e0d7947d853c3a49979
@@ -83,17 +83,16 @@ static PurpleAccountUiOps *account_ui_op
 
 static PurpleAccountUiOps *account_ui_ops = NULL;
 
-static guint    save_timer = 0;
-static gboolean accounts_loaded = FALSE;
-
 static GList *handles = NULL;
 
 static void set_current_error(PurpleAccount *account,
 	PurpleConnectionErrorInfo *new_err);
 
-/*********************************************************************
- * Writing to disk                                                   *
- *********************************************************************/
+static void
+schedule_accounts_save(void)
+{
+#warning Remove this when it's no longer needed
+}
 
 static void
 setting_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
@@ -356,129 +355,7 @@ current_error_to_xmlnode(PurpleConnectio
 	return node;
 }
 
-static xmlnode *
-account_to_xmlnode(PurpleAccount *account)
-{
-	PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
-
-	xmlnode *node, *child;
-	const char *tmp;
-	PurplePresence *presence;
-	PurpleProxyInfo *proxy_info;
-
-	node = xmlnode_new("account");
-
-	child = xmlnode_new_child(node, "protocol");
-	xmlnode_insert_data(child, purple_account_get_protocol_id(account), -1);
-
-	child = xmlnode_new_child(node, "name");
-	xmlnode_insert_data(child, purple_account_get_username(account), -1);
-
-	if (purple_account_get_remember_password(account) &&
-		((tmp = purple_account_get_password(account)) != NULL))
-	{
-		child = xmlnode_new_child(node, "password");
-		xmlnode_insert_data(child, tmp, -1);
-	}
-
-	if ((tmp = purple_account_get_alias(account)) != NULL)
-	{
-		child = xmlnode_new_child(node, "alias");
-		xmlnode_insert_data(child, tmp, -1);
-	}
-
-	if ((presence = purple_account_get_presence(account)) != NULL)
-	{
-		child = statuses_to_xmlnode(presence);
-		xmlnode_insert_child(node, child);
-	}
-
-	if ((tmp = purple_account_get_user_info(account)) != NULL)
-	{
-		/* TODO: Do we need to call purple_str_strip_char(tmp, '\r') here? */
-		child = xmlnode_new_child(node, "userinfo");
-		xmlnode_insert_data(child, tmp, -1);
-	}
-
-	if (g_hash_table_size(account->settings) > 0)
-	{
-		child = xmlnode_new_child(node, "settings");
-		g_hash_table_foreach(account->settings, setting_to_xmlnode, child);
-	}
-
-	if (g_hash_table_size(account->ui_settings) > 0)
-	{
-		g_hash_table_foreach(account->ui_settings, ui_setting_to_xmlnode, node);
-	}
-
-	if ((proxy_info = purple_account_get_proxy_info(account)) != NULL)
-	{
-		child = proxy_settings_to_xmlnode(proxy_info);
-		xmlnode_insert_child(node, child);
-	}
-
-	child = current_error_to_xmlnode(priv->current_error);
-	xmlnode_insert_child(node, child);
-
-	return node;
-}
-
-static xmlnode *
-accounts_to_xmlnode(void)
-{
-	xmlnode *node, *child;
-	GList *cur;
-
-	node = xmlnode_new("account");
-	xmlnode_set_attrib(node, "version", "1.0");
-
-	for (cur = purple_accounts_get_all(); cur != NULL; cur = cur->next)
-	{
-		child = account_to_xmlnode(cur->data);
-		xmlnode_insert_child(node, child);
-	}
-
-	return node;
-}
-
 static void
-sync_accounts(void)
-{
-	xmlnode *node;
-	char *data;
-
-	if (!accounts_loaded)
-	{
-		purple_debug_error("account", "Attempted to save accounts before "
-						 "they were read!\n");
-		return;
-	}
-
-	node = accounts_to_xmlnode();
-	data = xmlnode_to_formatted_str(node, NULL);
-	purple_util_write_data_to_file("accounts.xml", data, -1);
-	g_free(data);
-	xmlnode_free(node);
-}
-
-static gboolean
-save_cb(gpointer data)
-{
-	sync_accounts();
-	save_timer = 0;
-	return FALSE;
-}
-
-static void
-schedule_accounts_save(void)
-{
-#warning Saving should really be moved to the account manager
-	if (save_timer == 0)
-		save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
-}
-
-
-static void
 delete_setting(void *data)
 {
 	PurpleAccountSetting *setting = (PurpleAccountSetting *)data;
@@ -523,6 +400,7 @@ enum
 enum
 {
 	SIG_SETTING_INFO,
+	SIG_SETTINGS_CHANGED,
 	SIG_LAST
 };
 static guint signals[SIG_LAST] = { 0, };
@@ -629,6 +507,12 @@ purple_account_finalize(GObject *object)
 	/* Make sure we disconnect first */
 	purple_account_set_connection(account, NULL);
 
+	/* Clearing the error ensures that account-error-changed is emitted,
+	 * which is the end of the guarantee that the the error's pointer is
+	 * valid.
+	 */
+	purple_account_clear_current_error(account);
+
 	for (l = purple_get_conversations(); l != NULL; l = l->next) {
 		PurpleConversation *conv = (PurpleConversation *)l->data;
 
@@ -719,7 +603,12 @@ static void purple_account_class_init(Pu
 			);
 
 	/* Setup signals */
-#warning TODO: Setup signals
+	signals[SIG_SETTINGS_CHANGED] =
+		g_signal_new("settings-changed", G_OBJECT_CLASS_TYPE(klass),
+				G_SIGNAL_ACTION | G_SIGNAL_DETAILED, 0, NULL, NULL,
+				g_cclosure_marshal_VOID__VOID,
+				G_TYPE_NONE, 0);
+#warning TODO: Setup more signals
 }
 
 GType purple_account_get_gtype(void)
@@ -1270,8 +1159,6 @@ purple_account_set_username(PurpleAccoun
 	account->username = g_strdup(username);
 	g_object_notify(G_OBJECT(account), PROP_USERNAME_S);
 
-	schedule_accounts_save();
-
 	/* if the name changes, we should re-write the buddy list
 	 * to disk with the new name */
 	purple_blist_schedule_save();
@@ -1288,8 +1175,6 @@ purple_account_set_password(PurpleAccoun
 	g_free(account->password);
 	account->password = g_strdup(password);
 	g_object_notify(G_OBJECT(account), PROP_PASSWORD_S);
-
-	schedule_accounts_save();
 }
 
 void
@@ -1311,7 +1196,6 @@ purple_account_set_alias(PurpleAccount *
 	purple_signal_emit(purple_accounts_get_handle(), "account-alias-changed",
 					 account, old);
 #endif
-	schedule_accounts_save();
 }
 
 void
@@ -1420,8 +1304,6 @@ purple_account_set_enabled(PurpleAccount
 		purple_account_connect(account);
 	else if (!value && !purple_account_is_disconnected(account))
 		purple_account_disconnect(account);
-
-	schedule_accounts_save();
 }
 
 void
@@ -1511,9 +1393,43 @@ purple_account_clear_settings(PurpleAcco
 	g_hash_table_destroy(account->settings);
 
 	account->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
-											  g_free, delete_setting);
+			g_free, delete_setting);
 }
 
+static gboolean
+account_setting_value_changed(PurpleAccount *account, const char *name,
+		GType type, ...)
+{
+	va_list args;
+	PurpleAccountSetting *setting;
+	gboolean changed = TRUE;
+
+	setting = g_hash_table_lookup(account->settings, name);
+	if (!setting)
+		return TRUE;  /* This is a new setting */
+
+	va_start(args, type);
+	switch (type) {
+		case G_TYPE_STRING: {
+			const char *string = va_arg(args, const char *);
+			changed = (g_utf8_collate(string, setting->value.string) != 0);
+			break;
+		}
+		case G_TYPE_INT: {
+			int value = va_arg(args, int);
+			changed = (value != setting->value.integer);
+			break;
+		}
+		case G_TYPE_BOOLEAN: {
+			gboolean value = va_arg(args, gboolean);
+			changed = (value != setting->value.boolean);
+			break;
+		}
+	}
+	va_end(args);
+	return changed;
+}
+
 void
 purple_account_set_int(PurpleAccount *account, const char *name, int value)
 {
@@ -1522,14 +1438,17 @@ purple_account_set_int(PurpleAccount *ac
 	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
 	g_return_if_fail(name    != NULL);
 
+	if (!account_setting_value_changed(account, name, G_TYPE_INT, value))
+		return;
+
 	setting = g_new0(PurpleAccountSetting, 1);
 
 	setting->type          = PURPLE_PREF_INT;
 	setting->value.integer = value;
 
 	g_hash_table_insert(account->settings, g_strdup(name), setting);
-
-	schedule_accounts_save();
+	g_signal_emit(G_OBJECT(account), signals[SIG_SETTINGS_CHANGED],
+			g_quark_from_string(name));
 }
 
 void
@@ -1541,6 +1460,9 @@ purple_account_set_string(PurpleAccount 
 	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
 	g_return_if_fail(name    != NULL);
 
+	if (!account_setting_value_changed(account, name, G_TYPE_STRING, value))
+		return;
+
 	setting = g_new0(PurpleAccountSetting, 1);
 
 	setting->type         = PURPLE_PREF_STRING;
@@ -1548,7 +1470,8 @@ purple_account_set_string(PurpleAccount 
 
 	g_hash_table_insert(account->settings, g_strdup(name), setting);
 
-	schedule_accounts_save();
+	g_signal_emit(G_OBJECT(account), signals[SIG_SETTINGS_CHANGED],
+			g_quark_from_string(name));
 }
 
 void
@@ -1559,6 +1482,9 @@ purple_account_set_bool(PurpleAccount *a
 	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
 	g_return_if_fail(name    != NULL);
 
+	if (!account_setting_value_changed(account, name, G_TYPE_BOOLEAN, value))
+		return;
+
 	setting = g_new0(PurpleAccountSetting, 1);
 
 	setting->type       = PURPLE_PREF_BOOLEAN;
@@ -1566,7 +1492,8 @@ purple_account_set_bool(PurpleAccount *a
 
 	g_hash_table_insert(account->settings, g_strdup(name), setting);
 
-	schedule_accounts_save();
+	g_signal_emit(G_OBJECT(account), signals[SIG_SETTINGS_CHANGED],
+			g_quark_from_string(name));
 }
 
 static GHashTable *
@@ -1585,17 +1512,57 @@ get_ui_settings_table(PurpleAccount *acc
 	return table;
 }
 
+static gboolean
+account_ui_setting_value_changed(PurpleAccount *account, const char *ui,
+		const char *name, GType type, ...)
+{
+	va_list args;
+	PurpleAccountSetting *setting;
+	gboolean changed = TRUE;
+	GHashTable *table;
+
+	table = get_ui_settings_table(account, ui);
+	setting = table ? g_hash_table_lookup(table, name) : NULL;
+	if (!setting)
+		return TRUE;  /* This is a new setting */
+
+	va_start(args, type);
+	switch (type) {
+		case G_TYPE_STRING: {
+			const char *string = va_arg(args, const char *);
+			changed = (g_utf8_collate(string, setting->value.string) != 0);
+			break;
+		}
+		case G_TYPE_INT: {
+			int value = va_arg(args, int);
+			changed = (value != setting->value.integer);
+			break;
+		}
+		case G_TYPE_BOOLEAN: {
+			gboolean value = va_arg(args, gboolean);
+			changed = (value != setting->value.boolean);
+			break;
+		}
+	}
+	va_end(args);
+	return changed;
+}
+
 void
 purple_account_set_ui_int(PurpleAccount *account, const char *ui,
 						const char *name, int value)
 {
 	PurpleAccountSetting *setting;
 	GHashTable *table;
+	char *uiname;
 
 	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
 	g_return_if_fail(ui      != NULL);
 	g_return_if_fail(name    != NULL);
 
+	if (!account_ui_setting_value_changed(account, ui, name, G_TYPE_INT, value))
+		return;
+
 	setting = g_new0(PurpleAccountSetting, 1);
 
 	setting->type          = PURPLE_PREF_INT;
@@ -1606,7 +1573,11 @@ purple_account_set_ui_int(PurpleAccount 
 
 	g_hash_table_insert(table, g_strdup(name), setting);
 
-	schedule_accounts_save();
+	/* XXX: Or do want a seperate ui-settings-changed signal? */
+	uiname = g_strconcat("ui:", name, NULL);
+	g_signal_emit(G_OBJECT(account), signals[SIG_SETTINGS_CHANGED],
+			g_quark_from_string(uiname));
+	g_free(uiname);
 }
 
 void
@@ -1615,11 +1586,15 @@ purple_account_set_ui_string(PurpleAccou
 {
 	PurpleAccountSetting *setting;
 	GHashTable *table;
+	char *uiname;
 
 	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
 	g_return_if_fail(ui      != NULL);
 	g_return_if_fail(name    != NULL);
 
+	if (!account_ui_setting_value_changed(account, ui, name, G_TYPE_STRING, value))
+		return;
+
 	setting = g_new0(PurpleAccountSetting, 1);
 
 	setting->type         = PURPLE_PREF_STRING;
@@ -1630,7 +1605,11 @@ purple_account_set_ui_string(PurpleAccou
 
 	g_hash_table_insert(table, g_strdup(name), setting);
 
-	schedule_accounts_save();
+	/* XXX: Or do want a seperate ui-settings-changed signal? */
+	uiname = g_strconcat("ui:", name, NULL);
+	g_signal_emit(G_OBJECT(account), signals[SIG_SETTINGS_CHANGED],
+			g_quark_from_string(uiname));
+	g_free(uiname);
 }
 
 void
@@ -1639,11 +1618,15 @@ purple_account_set_ui_bool(PurpleAccount
 {
 	PurpleAccountSetting *setting;
 	GHashTable *table;
+	char *uiname;
 
 	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
 	g_return_if_fail(ui      != NULL);
 	g_return_if_fail(name    != NULL);
 
+	if (!account_ui_setting_value_changed(account, ui, name, G_TYPE_BOOLEAN, value))
+		return;
+
 	setting = g_new0(PurpleAccountSetting, 1);
 
 	setting->type       = PURPLE_PREF_BOOLEAN;
@@ -1659,7 +1642,11 @@ purple_account_set_ui_bool(PurpleAccount
 		purple_account_set_enabled(account, value);
 	}
 
-	schedule_accounts_save();
+	/* XXX: Or do want a seperate ui-settings-changed signal? */
+	uiname = g_strconcat("ui:", name, NULL);
+	g_signal_emit(G_OBJECT(account), signals[SIG_SETTINGS_CHANGED],
+			g_quark_from_string(uiname));
+	g_free(uiname);
 }
 
 static PurpleConnectionState
@@ -2260,14 +2247,81 @@ purple_account_clear_current_error(Purpl
 	set_current_error(account, NULL);
 }
 
+xmlnode * purple_account_to_xmlnode(PurpleAccount *account)
+{
+	PurpleAccountPrivate *priv;
+	xmlnode *node, *child;
+	const char *tmp;
+	PurplePresence *presence;
+	PurpleProxyInfo *proxy_info;
+
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+	priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+	node = xmlnode_new("account");
+
+	child = xmlnode_new_child(node, "protocol");
+	xmlnode_insert_data(child, purple_account_get_protocol_id(account), -1);
+
+	child = xmlnode_new_child(node, "name");
+	xmlnode_insert_data(child, purple_account_get_username(account), -1);
+
+	if (purple_account_get_remember_password(account) &&
+		((tmp = purple_account_get_password(account)) != NULL))
+	{
+		child = xmlnode_new_child(node, "password");
+		xmlnode_insert_data(child, tmp, -1);
+	}
+
+	if ((tmp = purple_account_get_alias(account)) != NULL)
+	{
+		child = xmlnode_new_child(node, "alias");
+		xmlnode_insert_data(child, tmp, -1);
+	}
+
+	if ((presence = purple_account_get_presence(account)) != NULL)
+	{
+		child = statuses_to_xmlnode(presence);
+		xmlnode_insert_child(node, child);
+	}
+
+	if ((tmp = purple_account_get_user_info(account)) != NULL)
+	{
+		/* TODO: Do we need to call purple_str_strip_char(tmp, '\r') here? */
+		child = xmlnode_new_child(node, "userinfo");
+		xmlnode_insert_data(child, tmp, -1);
+	}
+
+	if (g_hash_table_size(account->settings) > 0)
+	{
+		child = xmlnode_new_child(node, "settings");
+		g_hash_table_foreach(account->settings, setting_to_xmlnode, child);
+	}
+
+	if (g_hash_table_size(account->ui_settings) > 0)
+	{
+		g_hash_table_foreach(account->ui_settings, ui_setting_to_xmlnode, node);
+	}
+
+	if ((proxy_info = purple_account_get_proxy_info(account)) != NULL)
+	{
+		child = proxy_settings_to_xmlnode(proxy_info);
+		xmlnode_insert_child(node, child);
+	}
+
+	child = current_error_to_xmlnode(priv->current_error);
+	xmlnode_insert_child(node, child);
+
+	return node;
+}
+
 void
 purple_accounts_add(PurpleAccount *account)
 {
 	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
 
 	purple_account_manager_add_account(purple_account_manager_get(), account);
-
-	schedule_accounts_save();
 }
 
 void
@@ -2276,15 +2330,6 @@ purple_accounts_remove(PurpleAccount *ac
 	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
 
 	purple_account_manager_remove_account(purple_account_manager_get(), account);
-
-	schedule_accounts_save();
-
-#warning TODO: This should be moved to the account destructor
-	/* Clearing the error ensures that account-error-changed is emitted,
-	 * which is the end of the guarantee that the the error's pointer is
-	 * valid.
-	 */
-	purple_account_clear_current_error(account);
 }
 
 void
@@ -2362,8 +2407,6 @@ purple_accounts_reorder(PurpleAccount *a
 {
 	purple_account_manager_reorder_account(purple_account_manager_get(),
 			account, new_index);
-
-	schedule_accounts_save();
 }
 
 GList *
@@ -2533,19 +2576,12 @@ purple_accounts_init(void)
 	purple_signal_connect(conn_handle, "connection-error", handle,
 	                      PURPLE_CALLBACK(connection_error_cb), NULL);
 #endif
-	accounts_loaded = TRUE;
 }
 
 void
 purple_accounts_uninit(void)
 {
 	gpointer handle = purple_accounts_get_handle();
-	if (save_timer != 0)
-	{
-		purple_timeout_remove(save_timer);
-		save_timer = 0;
-		sync_accounts();
-	}
 
 	purple_signals_disconnect_by_handle(handle);
 	purple_signals_unregister_by_instance(handle);
============================================================
--- libpurple/account.h	bbbb68c958453100855fd9768340bda3daec27ec
+++ libpurple/account.h	56a7dd8c64e208f2cb70b2ec9b44ed2976f5cbb1
@@ -56,6 +56,7 @@ typedef void (*PurpleAccountUnregistrati
 #include "proxy.h"
 #include "prpl.h"
 #include "status.h"
+#include "xmlnode.h"
 
 /**
  * Account request types.
@@ -930,6 +931,15 @@ void purple_account_clear_current_error(
  */
 void purple_account_clear_current_error(PurpleAccount *account);
 
+/**
+ * Get an XML description of an account.
+ *
+ * @param account  The account
+ *
+ * @return  The XML description of the account.
+ */
+xmlnode * purple_account_to_xmlnode(PurpleAccount *account);
+
 /*@}*/
 
 /**************************************************************************/
============================================================
--- libpurple/accountmanager.c	ee48caf65b8e5217d359fe387e49ac981feccb1d
+++ libpurple/accountmanager.c	d76d501c5c7b99794ca0b12af2d205bf4b3e5905
@@ -29,6 +29,9 @@
 
 #include <string.h>
 
+static void schedule_accounts_save(PurpleAccountManager *manager);
+static void sync_accounts(PurpleAccountManager *manager);
+
 /******************************************************************************
  * PurpleAccountManager API
  *****************************************************************************/
@@ -44,6 +47,7 @@ struct _PurpleAccountManagerPrivate
 {
 	GList *accounts;
 	gboolean accounts_loaded;
+	int save_timer;
 };
 
 #define PURPLE_ACCOUNT_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_ACCOUNT_MANAGER, PurpleAccountManagerPrivate))
@@ -60,19 +64,37 @@ static void
 }
 
 static void
+purple_account_manager_dispose(GObject *object)
+{
+	PurpleAccountManager *manager = PURPLE_ACCOUNT_MANAGER(object);
+	sync_accounts(manager);
+}
+
+static void
+purple_account_manager_finalize(GObject *object)
+{
+	PurpleAccountManagerPrivate *priv = PURPLE_ACCOUNT_MANAGER_GET_PRIVATE(object);
+	g_list_free(priv->accounts);
+}
+
+static void
 purple_account_manager_class_init(PurpleAccountManagerClass *klass)
 {
-	G_OBJECT_CLASS(klass)->constructor = purple_account_manager_constructor;
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	obj_class->constructor = purple_account_manager_constructor;
+	obj_class->dispose = purple_account_manager_dispose;
+	obj_class->finalize = purple_account_manager_finalize;
+
 	account_manager_signals[ACCOUNT_ADDED] =
 		g_signal_new("account-added",
 				G_OBJECT_CLASS_TYPE(klass),
 				G_SIGNAL_RUN_FIRST,
 				G_STRUCT_OFFSET(PurpleAccountManagerClass, account_added),
 				NULL, NULL,
-#warning FIXME: Change this to __OBJECT when PurpleAccount is a GObject
-				purple_smarshal_VOID__POINTER,
+				purple_smarshal_VOID__OBJECT,
 				G_TYPE_NONE,
-				1, G_TYPE_POINTER);//PURPLE_TYPE_ACCOUNT);
+				1, PURPLE_TYPE_ACCOUNT);
 
 	account_manager_signals[ACCOUNT_REMOVED] =
 		g_signal_new("account-removed",
@@ -80,10 +102,9 @@ purple_account_manager_class_init(Purple
 				G_SIGNAL_RUN_FIRST,
 				G_STRUCT_OFFSET(PurpleAccountManagerClass, account_removed),
 				NULL, NULL,
-#warning FIXME: Change this to __OBJECT when PurpleAccount is a GObject
-				purple_smarshal_VOID__POINTER,
+				purple_smarshal_VOID__OBJECT,
 				G_TYPE_NONE,
-				1, G_TYPE_POINTER);//PURPLE_TYPE_ACCOUNT);
+				1, PURPLE_TYPE_ACCOUNT);
 
 	g_type_class_add_private(klass, sizeof(PurpleAccountManagerPrivate));
 }
@@ -111,6 +132,10 @@ void purple_account_manager_add_account(
 
 	manager->priv->accounts = g_list_append(manager->priv->accounts, account);
 	g_signal_emit(manager, account_manager_signals[ACCOUNT_ADDED], 0, account);
+
+	/* Make sure we save the accounts when something changes */
+	g_signal_connect_swapped(G_OBJECT(account), "notify", G_CALLBACK(schedule_accounts_save), manager);
+	g_signal_connect_swapped(G_OBJECT(account), "settings-changed", G_CALLBACK(schedule_accounts_save), manager);
 }
 
 void purple_account_manager_remove_account(PurpleAccountManager *manager, PurpleAccount *account)
@@ -120,6 +145,9 @@ void purple_account_manager_remove_accou
 
 	manager->priv->accounts = g_list_remove(manager->priv->accounts, account);
 	g_signal_emit(manager, account_manager_signals[ACCOUNT_REMOVED], 0, account);
+
+	g_signal_handlers_disconnect_by_func(G_OBJECT(account),
+			G_CALLBACK(schedule_accounts_save), manager);
 }
 
 void purple_account_manager_reorder_account(PurpleAccountManager *manager, PurpleAccount *account, int new_index)
@@ -151,6 +179,7 @@ void purple_account_manager_reorder_acco
 	/* Insert it where it should go. */
 	accounts = g_list_insert(accounts, account, new_index);
 	manager->priv->accounts = accounts;
+	schedule_accounts_save(manager);
 }
 
 GList *purple_account_manager_get_all_accounts(PurpleAccountManager *manager)
@@ -587,3 +616,67 @@ void purple_account_manager_load_account
 	_purple_buddy_icons_account_loaded_cb();
 }
 
+/*********************************************************************
+ * Writing to disk                                                   *
+ *********************************************************************/
+
+static xmlnode *
+accounts_to_xmlnode(PurpleAccountManager *manager)
+{
+	xmlnode *node, *child;
+	GList *cur;
+
+	node = xmlnode_new("account");
+	xmlnode_set_attrib(node, "version", "1.0");
+
+	for (cur = purple_account_manager_get_all_accounts(manager);
+			cur != NULL; cur = cur->next)
+	{
+		child = purple_account_to_xmlnode(PURPLE_ACCOUNT(cur->data));
+		xmlnode_insert_child(node, child);
+	}
+
+	return node;
+}
+
+static void
+sync_accounts(PurpleAccountManager *manager)
+{
+	xmlnode *node;
+	char *data;
+	PurpleAccountManagerPrivate *priv = PURPLE_ACCOUNT_MANAGER_GET_PRIVATE(manager);
+
+	if (priv->save_timer == 0)
+		return;
+
+	if (!priv->accounts_loaded) {
+		purple_debug_error("account", "Attempted to save accounts before "
+						 "they were read!\n");
+		return;
+	}
+
+	node = accounts_to_xmlnode(manager);
+	data = xmlnode_to_formatted_str(node, NULL);
+	purple_util_write_data_to_file("accounts.xml", data, -1);
+	g_free(data);
+	xmlnode_free(node);
+
+	g_source_remove(priv->save_timer);
+	priv->save_timer = 0;
+}
+
+static gboolean
+save_cb(gpointer data)
+{
+	sync_accounts(PURPLE_ACCOUNT_MANAGER(data));
+	return FALSE;
+}
+
+static void
+schedule_accounts_save(PurpleAccountManager *manager)
+{
+	PurpleAccountManagerPrivate *priv = PURPLE_ACCOUNT_MANAGER_GET_PRIVATE(manager);
+	if (priv->save_timer == 0)
+		priv->save_timer = purple_timeout_add_seconds(5, save_cb, manager);
+}
+
============================================================
--- libpurple/accountmanager.h	8b6b2c2be2d057d196431dfceca52d1b658d6e1a
+++ libpurple/accountmanager.h	1a182fae0515e7350897843d9ebaf20f7795e080
@@ -67,7 +67,7 @@ G_BEGIN_DECLS
 /**************************************************************************/
 /*@{*/
 
-GType purple_account_manager_get_gtype(void);
+GType purple_account_manager_get_type(void);
 PurpleAccountManager *purple_account_manager_get(void);
 
 void purple_account_manager_add_account(PurpleAccountManager *manager, PurpleAccount *account);


More information about the Commits mailing list