
---
 addressbook/libedata-book/e-book-backend.c      |    9 +-
 addressbook/libedata-book/e-data-book-factory.c |  250 ++++++++++++++++++++++-
 addressbook/libedata-book/e-data-book.c         |  223 ++++-----------------
 addressbook/libedata-book/e-data-book.h         |    5 +
 4 files changed, 297 insertions(+), 190 deletions(-)

diff --git a/addressbook/libedata-book/e-book-backend.c b/addressbook/libedata-book/e-book-backend.c
index 955ccc4..730accf 100644
--- a/addressbook/libedata-book/e-book-backend.c
+++ b/addressbook/libedata-book/e-book-backend.c
@@ -24,7 +24,6 @@
 
 struct _EBookBackendPrivate {
 	ESourceRegistry *registry;
-
 	GMutex clients_mutex;
 	GList *clients;
 
@@ -1222,19 +1221,19 @@ e_book_backend_set_locale (EBookBackend *backend,
 			   GCancellable *cancellable,
 			   GError      **error)
 {
-	/* If the backend does not support locales, just happily return */
-	gboolean success = TRUE;
 
 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
 
 	g_object_ref (backend);
 
 	if (E_BOOK_BACKEND_GET_CLASS (backend)->set_locale)
-		success = (* E_BOOK_BACKEND_GET_CLASS (backend)->set_locale) (backend, locale,
+		return (* E_BOOK_BACKEND_GET_CLASS (backend)->set_locale) (backend, locale,
 									      cancellable, error);
 	g_object_unref (backend);
 
-	return success;
+	/* If the backend does not support locales, just happily return */
+
+	return TRUE;
 }
 
 /**
diff --git a/addressbook/libedata-book/e-data-book-factory.c b/addressbook/libedata-book/e-data-book-factory.c
index 9a7f8f4..f286930 100644
--- a/addressbook/libedata-book/e-data-book-factory.c
+++ b/addressbook/libedata-book/e-data-book-factory.c
@@ -33,6 +33,7 @@
 #include "e-book-backend-factory.h"
 #include "e-data-book.h"
 #include "e-data-book-factory.h"
+#include "e-dbus-localed.h"
 
 #define d(x)
 
@@ -51,6 +52,12 @@ struct _EDataBookFactoryPrivate {
 	GMutex connections_lock;
 	/* This is a hash of client addresses to GList* of EDataBooks */
 	GHashTable *connections;
+
+	/* Watching "org.freedesktop.locale1" for locale changes */
+	guint localed_watch_id;
+	EDBusLocale1 *localed_proxy;
+	GCancellable *localed_cancel;
+	gchar *locale;	
 };
 
 enum {
@@ -105,6 +112,38 @@ data_book_factory_ref_backend (EDataFactory *factory,
 	return backend;
 }
 
+static GList *
+data_book_factory_list_books (EDataBookFactory *factory)
+{
+	GList *l = NULL, *books_list = NULL;
+	GHashTableIter iter;
+	gpointer hkey, hvalue;
+	GHashTable *connections;
+	EDataBook *book;
+
+	g_mutex_lock (&factory->priv->connections_lock);
+
+	connections = factory->priv->connections;
+
+	g_hash_table_iter_init (&iter, connections);
+	while (g_hash_table_iter_next (&iter, &hkey, &hvalue)) {
+		GSList *books = hvalue;
+
+		for (l = books; l; l = l->next) {
+			book = l->data;
+			EDataBook *insert_book = g_object_ref(book);
+
+			if (g_list_find (books_list, insert_book) == NULL)
+				books_list = g_list_prepend(books_list, insert_book);
+			else
+				g_object_unref(insert_book);
+		}
+	}
+	g_mutex_unlock (&factory->priv->connections_lock);
+
+	return books_list;
+}
+
 static gchar *
 construct_book_factory_path (void)
 {
@@ -231,11 +270,19 @@ data_book_factory_open (EDataBookFactory *factory,
 		list = g_hash_table_lookup (
 			factory->priv->connections, sender);
 		list = g_list_prepend (list, book);
+
 		g_hash_table_insert (
 			factory->priv->connections,
 			g_strdup (sender), list);
 		g_mutex_unlock (&factory->priv->connections_lock);
 
+		/* Don't set the locale on a new book if we have not
+		 * yet received a notification of a locale change
+		 */
+		if (factory->priv->locale)
+			e_data_book_set_locale (book,
+						factory->priv->locale,
+						NULL, NULL);
 	} else {
 		g_free (object_path);
 		object_path = NULL;
@@ -323,6 +370,20 @@ data_book_factory_dispose (GObject *object)
 		priv->dbus_factory = NULL;
 	}
 
+	if (priv->localed_cancel) {
+		g_cancellable_cancel (priv->localed_cancel);
+		g_object_unref (priv->localed_cancel);
+		priv->localed_cancel = NULL;
+	}
+
+	if (priv->localed_proxy) {
+		g_object_unref (priv->localed_proxy);
+		priv->localed_proxy = NULL;
+	}
+
+	if (priv->localed_watch_id > 0)
+		g_bus_unwatch_name (priv->localed_watch_id);
+
 	/* Chain up to parent's finalize() method. */
 	G_OBJECT_CLASS (e_data_book_factory_parent_class)->dispose (object);
 }
@@ -340,10 +401,180 @@ data_book_factory_finalize (GObject *object)
 	g_mutex_clear (&priv->books_lock);
 	g_mutex_clear (&priv->connections_lock);
 
+	g_free(priv->locale);
+
 	/* Chain up to parent's finalize() method. */
 	G_OBJECT_CLASS (e_data_book_factory_parent_class)->finalize (object);
 }
 
+static gchar *
+data_book_factory_interpret_locale_value (const gchar *value)
+{
+	gchar *interpreted_value = NULL;
+	gchar **split;
+
+	split = g_strsplit (value, "=", 2);
+
+	if (split && split[0] && split[1])
+		interpreted_value = g_strdup (split[1]);
+
+	g_strfreev (split);
+
+	if (!interpreted_value)
+		g_warning ("Failed to interpret locale value: %s", value);
+
+	return interpreted_value;
+}
+
+static gchar *
+data_book_factory_interpret_locale (const gchar * const * locale)
+{
+	gint i;
+	gchar *interpreted_locale = NULL;
+
+	/* Prioritize LC_COLLATE and then LANG values
+	 * in the 'locale' specified by localed.
+	 *
+	 * If localed explicitly specifies no locale, then
+	 * default to checking system locale.
+	 */
+	if (locale) {
+		for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
+			if (strncmp (locale[i], "LC_COLLATE", 10) == 0)
+				interpreted_locale = 
+					data_book_factory_interpret_locale_value (locale[i]);
+		}
+
+		for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
+			if (strncmp (locale[i], "LANG", 4) == 0)
+				interpreted_locale = 
+					data_book_factory_interpret_locale_value (locale[i]);
+		}
+	}
+
+	if (!interpreted_locale) {
+		const gchar *system_locale = setlocale (LC_COLLATE, NULL);
+
+		interpreted_locale = g_strdup (system_locale);
+	}
+
+	return interpreted_locale;
+}
+
+
+static void
+data_book_factory_set_locale (EDataBookFactory *factory,
+			      const gchar *locale)
+{
+	EDataBookFactoryPrivate *priv = factory->priv;
+	GError *error = NULL;
+	GList *books, *l;
+
+	if (g_strcmp0 (priv->locale, locale) != 0) {
+
+		g_free (priv->locale);
+		priv->locale = g_strdup (locale);
+
+		books = data_book_factory_list_books (factory);
+		for (l = books; l; l = l->next) {
+
+			EDataBook *book = l->data;
+
+			if (!e_data_book_set_locale (book, locale, NULL, &error)) {
+				g_warning ("Failed to set locale on addressbook: %s",
+					   error->message);
+				g_clear_error (&error);
+			}
+		}
+		g_list_free_full (books, g_object_unref);
+	}
+}
+
+static void
+data_book_factory_locale_changed (GObject *object,
+				  GParamSpec *pspec,
+				  gpointer user_data)
+{
+	EDBusLocale1 *locale_proxy = E_DBUS_LOCALE1 (object);
+	EDataBookFactory *factory = (EDataBookFactory *)user_data;
+	const gchar * const *locale;
+	gchar *interpreted_locale;
+
+	locale = e_dbus_locale1_get_locale (locale_proxy);
+	interpreted_locale = data_book_factory_interpret_locale (locale);
+
+	data_book_factory_set_locale (factory, interpreted_locale);
+
+	g_free (interpreted_locale);
+}
+
+static void
+data_book_factory_localed_ready (GObject *source_object,
+				 GAsyncResult *res,
+				 gpointer user_data)
+{
+	EDataBookFactory *factory = (EDataBookFactory *)user_data;
+	GError *error = NULL;
+
+	factory->priv->localed_proxy = e_dbus_locale1_proxy_new_finish (res, &error);
+
+	if (factory->priv->localed_proxy == NULL) {
+		g_warning ("Error fetching localed proxy: %s", error->message);
+		g_error_free (error);
+	}
+
+	if (factory->priv->localed_cancel) {
+		g_object_unref (factory->priv->localed_cancel);
+		factory->priv->localed_cancel = NULL;
+	}
+
+	if (factory->priv->localed_proxy) {
+		g_signal_connect (factory->priv->localed_proxy, "notify::locale",
+				  G_CALLBACK (data_book_factory_locale_changed), factory);
+
+		/* Initial refresh of the locale */
+		data_book_factory_locale_changed (G_OBJECT (factory->priv->localed_proxy), NULL, factory);
+	}
+}
+
+static void
+data_book_factory_localed_appeared (GDBusConnection *connection,
+				    const gchar *name,
+				    const gchar *name_owner,
+				    gpointer user_data)
+{
+	EDataBookFactory *factory = (EDataBookFactory *)user_data;
+
+	factory->priv->localed_cancel = g_cancellable_new ();
+
+	e_dbus_locale1_proxy_new (connection,
+				  G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
+				  "org.freedesktop.locale1",
+				  "/org/freedesktop/locale1",
+				  factory->priv->localed_cancel,
+				  data_book_factory_localed_ready,
+				  factory);
+}
+
+static void
+data_book_factory_localed_vanished (GDBusConnection *connection,
+				    const gchar *name,
+				    gpointer user_data)
+{
+	EDataBookFactory *factory = (EDataBookFactory *)user_data;
+
+	if (factory->priv->localed_cancel) {
+		g_cancellable_cancel (factory->priv->localed_cancel);
+		g_object_unref (factory->priv->localed_cancel);
+		factory->priv->localed_cancel = NULL;
+	}
+
+	if (factory->priv->localed_proxy) {
+		g_object_unref (factory->priv->localed_proxy);
+		factory->priv->localed_proxy = NULL;
+	}
+}
+
 static void
 data_book_factory_bus_acquired (EDBusServer *server,
                                 GDBusConnection *connection)
@@ -380,7 +611,6 @@ data_book_factory_bus_name_lost (EDBusServer *server,
 	gchar *key;
 
 	priv = E_DATA_BOOK_FACTORY_GET_PRIVATE (server);
-
 	g_mutex_lock (&priv->connections_lock);
 
 	while (g_hash_table_lookup_extended (
@@ -427,11 +657,27 @@ data_book_factory_initable_init (GInitable *initable,
                                  GError **error)
 {
 	EDataBookFactoryPrivate *priv;
-
+	GBusType bus_type = G_BUS_TYPE_SYSTEM;
 	priv = E_DATA_BOOK_FACTORY_GET_PRIVATE (initable);
 
 	priv->registry = e_source_registry_new_sync (cancellable, error);
 
+	/* When running tests, we pretend to be the "org.freedesktop.locale1" service
+	 * on the session bus instead of the real location on the system bus.
+	 */
+	if (g_getenv ("EDS_TESTING") != NULL)
+		bus_type = G_BUS_TYPE_SESSION;
+
+	/* Watch system bus for locale change notifications */
+	priv->localed_watch_id =
+		g_bus_watch_name (bus_type,
+				  "org.freedesktop.locale1",
+				  G_BUS_NAME_WATCHER_FLAGS_NONE,
+				  data_book_factory_localed_appeared,
+				  data_book_factory_localed_vanished,
+				  initable,
+				  NULL);
+
 	return (priv->registry != NULL);
 }
 
diff --git a/addressbook/libedata-book/e-data-book.c b/addressbook/libedata-book/e-data-book.c
index a27a671..ec64f75 100644
--- a/addressbook/libedata-book/e-data-book.c
+++ b/addressbook/libedata-book/e-data-book.c
@@ -36,7 +36,7 @@
 #include "e-book-backend.h"
 #include "e-book-backend-sexp.h"
 #include "e-book-backend-factory.h"
-#include "e-dbus-localed.h"
+
 
 #define E_DATA_BOOK_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -61,9 +61,6 @@ struct _EDataBookPrivate {
 	guint32 open_opid;
 	GQueue open_queue;
 
-	guint localed_watch_id;
-	EDBusLocale1 *localed_proxy;
-	GCancellable *localed_cancel;
 };
 
 enum {
@@ -1965,156 +1962,8 @@ e_data_book_report_backend_property_changed (EDataBook *book,
 	/* Disregard anything else. */
 }
 
-static gchar *
-data_book_interpret_locale_value (const gchar *value)
-{
-	gchar *interpreted_value = NULL;
-	gchar **split;
-
-	split = g_strsplit (value, "=", 2);
-
-	if (split && split[0] && split[1])
-		interpreted_value = g_strdup (split[1]);
-
-	g_strfreev (split);
-
-	if (!interpreted_value)
-		g_warning ("Failed to interpret locale value: %s", value);
-
-	return interpreted_value;
-}
-
-static gchar *
-data_book_interpret_locale (const gchar * const * locale)
-{
-	gint i;
-	gchar *interpreted_locale = NULL;
-
-	/* Prioritize LC_COLLATE and then LANG values
-	 * in the 'locale' specified by localed.
-	 *
-	 * If localed explicitly specifies no locale, then
-	 * default to checking system locale.
-	 */
-	if (locale) {
-
-		for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
-
-			if (strncmp (locale[i], "LC_COLLATE", 10) == 0)
-				interpreted_locale = data_book_interpret_locale_value (locale[i]);
-		}
-
-		for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
-
-			if (strncmp (locale[i], "LANG", 4) == 0)
-				interpreted_locale = data_book_interpret_locale_value (locale[i]);
-		}
-	}
 
-	if (!interpreted_locale) {
-		const gchar *system_locale = setlocale (LC_COLLATE, NULL);
 
-		interpreted_locale = g_strdup (system_locale);
-	}
-
-	return interpreted_locale;
-}
-
-static void
-data_book_locale_changed (GObject *object,
-			  GParamSpec *pspec,
-			  gpointer user_data)
-{
-	EDBusLocale1 *locale_proxy = E_DBUS_LOCALE1 (object);
-	EDataBook *book = (EDataBook *)user_data;
-	EBookBackend *backend;
-
-	backend = book->priv->backend;
-
-	if (backend) {
-		const gchar * const *locale;
-		gchar *interpreted_locale;
-
-		locale = e_dbus_locale1_get_locale (locale_proxy);
-		interpreted_locale = data_book_interpret_locale (locale);
-
-		/* XXX This needs work, the whole handling should be done
-		 * by the singleton EDataBookFactory, and handle errors well
-		 */
-		e_book_backend_set_locale (backend, interpreted_locale, NULL, NULL);
-
-		e_dbus_address_book_set_locale (book->priv->dbus_interface, interpreted_locale);
-
-		g_free (interpreted_locale);
-	}
-}
-
-static void
-data_book_localed_ready (GObject *source_object,
-			 GAsyncResult *res,
-			 gpointer user_data)
-{
-	EDataBook *book = (EDataBook *)user_data;
-	GError *error = NULL;
-
-	book->priv->localed_proxy = e_dbus_locale1_proxy_new_finish (res, &error);
-
-	if (book->priv->localed_proxy == NULL) {
-		g_warning ("Error fetching localed proxy: %s", error->message);
-		g_error_free (error);
-	}
-
-	if (book->priv->localed_cancel) {
-		g_object_unref (book->priv->localed_cancel);
-		book->priv->localed_cancel = NULL;
-	}
-
-	if (book->priv->localed_proxy) {
-		g_signal_connect (book->priv->localed_proxy, "notify::locale",
-				  G_CALLBACK (data_book_locale_changed), book);
-
-		/* Initial refresh of the locale */
-		data_book_locale_changed (G_OBJECT (book->priv->localed_proxy), NULL, book);
-	}
-}
-
-static void
-data_book_localed_appeared (GDBusConnection *connection,
-			    const gchar *name,
-			    const gchar *name_owner,
-			    gpointer user_data)
-{
-	EDataBook *book = (EDataBook *)user_data;
-
-	book->priv->localed_cancel = g_cancellable_new ();
-
-	e_dbus_locale1_proxy_new (connection,
-				  G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
-				  "org.freedesktop.locale1",
-				  "/org/freedesktop/locale1",
-				  book->priv->localed_cancel,
-				  data_book_localed_ready,
-				  book);
-}
-
-static void
-data_book_localed_vanished (GDBusConnection *connection,
-			    const gchar *name,
-			    gpointer user_data)
-{
-	EDataBook *book = (EDataBook *)user_data;
-
-	if (book->priv->localed_cancel) {
-		g_cancellable_cancel (book->priv->localed_cancel);
-		g_object_unref (book->priv->localed_cancel);
-		book->priv->localed_cancel = NULL;
-	}
-
-	if (book->priv->localed_proxy) {
-		g_object_unref (book->priv->localed_proxy);
-		book->priv->localed_proxy = NULL;
-	}
-}
 
 static void
 data_book_set_backend (EDataBook *book,
@@ -2235,17 +2084,6 @@ data_book_dispose (GObject *object)
 		priv->direct_module = NULL;
 	}
 
-	if (priv->localed_cancel) {
-		g_cancellable_cancel (priv->localed_cancel);
-		g_object_unref (priv->localed_cancel);
-		priv->localed_cancel = NULL;
-	}
-
-	if (priv->localed_proxy) {
-		g_object_unref (priv->localed_proxy);
-		priv->localed_proxy = NULL;
-	}
-
 	/* Chain up to parent's dispose() metnod. */
 	G_OBJECT_CLASS (e_data_book_parent_class)->dispose (object);
 }
@@ -2280,9 +2118,6 @@ data_book_finalize (GObject *object)
 	/* This should be empty now, else we leak memory. */
 	g_warn_if_fail (g_queue_is_empty (&priv->open_queue));
 
-	if (priv->localed_watch_id > 0)
-		g_bus_unwatch_name (priv->localed_watch_id);
-
 	/* Chain up to parent's finalize() method. */
 	G_OBJECT_CLASS (e_data_book_parent_class)->finalize (object);
 }
@@ -2295,7 +2130,6 @@ data_book_initable_init (GInitable *initable,
 	EDataBook *book;
 	OperationData *op;
 	gchar *locale;
-	GBusType bus_type = G_BUS_TYPE_SYSTEM;
 
 	book = E_DATA_BOOK (initable);
 
@@ -2408,22 +2242,6 @@ data_book_initable_init (GInitable *initable,
 		e_dbus_address_book_set_locale (book->priv->dbus_interface, locale);
 		g_free (locale);
 
-		/* When running tests, we pretend to be the "org.freedesktop.locale1" service
-		 * on the session bus instead of the real location on the system bus.
-		 */
-		if (g_getenv ("EDS_LOCALED_TESTING") != NULL)
-			bus_type = G_BUS_TYPE_SESSION;
-
-		/* Watch system bus for locale change notifications */
-		book->priv->localed_watch_id =
-			g_bus_watch_name (bus_type,
-					  "org.freedesktop.locale1",
-					  G_BUS_NAME_WATCHER_FLAGS_NONE,
-					  data_book_localed_appeared,
-					  data_book_localed_vanished,
-					  book,
-					  NULL);
-
 		return g_dbus_interface_skeleton_export (
 			G_DBUS_INTERFACE_SKELETON (book->priv->dbus_interface),
 			book->priv->connection,
@@ -2705,6 +2523,45 @@ e_data_book_get_object_path (EDataBook *book)
 	return book->priv->object_path;
 }
 
+/**
+ * e_data_book_set_locale:
+ * @book: an #EDataBook 
+ * @locale: the new locale to set for this book
+ * @cancellable: (allow-none): a #GCancellable
+ * @error: (allow-none): a location to store any error which might occur
+ *
+ * Set's the locale for this addressbook, this can result in renormalization of
+ * locale sensitive data.
+ *
+ * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
+ *
+ * Since: 3.12
+ */
+gboolean
+e_data_book_set_locale (EDataBook *book,
+			const gchar *locale,
+			GCancellable *cancellable,
+			GError **error)
+{
+	EBookBackend *backend;
+	gboolean success;
+
+	g_return_val_if_fail (E_IS_DATA_BOOK (book), FALSE);
+
+	backend = e_data_book_get_backend (book);
+	success = e_book_backend_set_locale (backend,
+					     locale,
+					     cancellable,
+					     error);
+
+	if (success)
+		e_dbus_address_book_set_locale (book->priv->dbus_interface, locale);
+
+	g_object_unref (backend);
+
+	return success;
+}
+
 /*************************************************************************
  *                        Direct Read Access APIs                        *
  *************************************************************************/
diff --git a/addressbook/libedata-book/e-data-book.h b/addressbook/libedata-book/e-data-book.h
index 91b76c1..fe573d6 100644
--- a/addressbook/libedata-book/e-data-book.h
+++ b/addressbook/libedata-book/e-data-book.h
@@ -158,6 +158,11 @@ GDBusConnection *
 		e_data_book_get_connection	(EDataBook *book);
 const gchar *	e_data_book_get_object_path	(EDataBook *book);
 
+gboolean        e_data_book_set_locale          (EDataBook *book,
+						 const gchar *locale,
+						 GCancellable *cancellable,
+						 GError **error);
+
 void		e_data_book_respond_open	(EDataBook *book,
 						 guint32 opid,
 						 GError *error);
-- 
1.7.5.4

