summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Van Berkom <tristanvb@openismus.com>2013-05-23 15:24:09 +0900
committerTristan Van Berkom <tristanvb@openismus.com>2013-10-23 20:39:39 +0200
commit7df46dce9a0165f82d4d5c8eb1f0ebeb9fad70ee (patch)
treed4f8dba3d0727d8a70143e64ddde94f60969ea93
parente0cc4b07270348023104192db66545e104d94562 (diff)
downloadevolution-data-server-7df46dce9a0165f82d4d5c8eb1f0ebeb9fad70ee.tar.gz
EDataBook: Cursor related changes
Watch the system bus for locale notifications When org.freedesktop.locale1 is available, listen to changes in the LC_COLLATE locale and configure backends with locale changes using e_book_backend_set_locale(), notify property changes via the locale property on the addressbook D-Bus API. Also, load the backend's initially set locale as the locale property value until the org.freedesktop.locale1 D-Bus interface notifies us of a locale change on the system bus. Also handle D-Bus calls for creating cursors and export new cursors on the bus.
-rw-r--r--addressbook/libedata-book/e-data-book.c383
1 files changed, 383 insertions, 0 deletions
diff --git a/addressbook/libedata-book/e-data-book.c b/addressbook/libedata-book/e-data-book.c
index cb2e7f250..376f68dbe 100644
--- a/addressbook/libedata-book/e-data-book.c
+++ b/addressbook/libedata-book/e-data-book.c
@@ -37,6 +37,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 \
@@ -55,6 +56,10 @@ struct _EDataBookPrivate {
GMutex sender_lock;
GHashTable *sender_table;
+
+ guint localed_watch_id;
+ EDBusLocale1 *localed_proxy;
+ GCancellable *localed_cancel;
};
struct _AsyncContext {
@@ -210,6 +215,18 @@ construct_bookview_path (void)
getpid (), counter);
}
+static gchar *
+construct_bookcursor_path (void)
+{
+ static volatile gint counter = 1;
+
+ g_atomic_int_inc (&counter);
+
+ return g_strdup_printf (
+ "/org/gnome/evolution/dataserver/AddressBookCursor/%d/%d",
+ getpid (), counter);
+}
+
static void
data_book_convert_to_client_error (GError *error)
{
@@ -1031,6 +1048,173 @@ data_book_handle_get_view_cb (EDBusAddressBook *interface,
}
static gboolean
+data_book_interpret_sort_keys (const gchar * const *in_sort_keys,
+ const gchar * const *in_sort_types,
+ EContactField **out_sort_keys,
+ EBookCursorSortType **out_sort_types,
+ gint *n_fields,
+ GError **error)
+{
+ gint i, key_count = 0, type_count = 0;
+ EContactField *sort_keys;
+ EBookCursorSortType *sort_types;
+ gboolean success = TRUE;
+
+ if (!in_sort_keys || !in_sort_types) {
+ g_set_error (error,
+ E_CLIENT_ERROR,
+ E_CLIENT_ERROR_INVALID_ARG,
+ "Missing sort keys while trying to create a Cursor");
+ return FALSE;
+ }
+
+ for (i = 0; in_sort_keys[i] != NULL; i++)
+ key_count++;
+ for (i = 0; in_sort_types[i] != NULL; i++)
+ type_count++;
+
+ if (key_count != type_count) {
+ g_set_error (error,
+ E_CLIENT_ERROR,
+ E_CLIENT_ERROR_INVALID_ARG,
+ "Must specify the same amount of sort keys as sort types while creating a Cursor");
+ return FALSE;
+ }
+
+ sort_keys = g_new0 (EContactField, key_count);
+ sort_types = g_new0 (EBookCursorSortType, type_count);
+
+ for (i = 0; success && i < key_count; i++) {
+
+ sort_keys[i] = e_contact_field_id (in_sort_keys[i]);
+
+ if (sort_keys[i] == 0) {
+ g_set_error (error,
+ E_CLIENT_ERROR,
+ E_CLIENT_ERROR_INVALID_ARG,
+ "Invalid sort key '%s' specified when creating a Cursor",
+ in_sort_keys[i]);
+ success = FALSE;
+ }
+ }
+
+ for (i = 0; success && i < type_count; i++) {
+ gint enum_value = 0;
+
+ if (!e_enum_from_string (E_TYPE_BOOK_CURSOR_SORT_TYPE,
+ in_sort_types[i],
+ &enum_value)) {
+ g_set_error (error,
+ E_CLIENT_ERROR,
+ E_CLIENT_ERROR_INVALID_ARG,
+ "Invalid sort type '%s' specified when creating a Cursor",
+ in_sort_types[i]);
+ success = FALSE;
+ }
+
+ sort_types[i] = enum_value;
+ }
+
+ if (!success) {
+ g_free (sort_keys);
+ g_free (sort_types);
+ } else {
+ *out_sort_keys = sort_keys;
+ *out_sort_types = sort_types;
+ *n_fields = key_count;
+ }
+
+ return success;
+}
+
+static gboolean
+data_book_handle_get_cursor_cb (EDBusAddressBook *interface,
+ GDBusMethodInvocation *invocation,
+ const gchar *in_query,
+ const gchar * const *in_sort_keys,
+ const gchar * const *in_sort_types,
+ EDataBook *data_book)
+{
+ EBookBackend *backend;
+ EDataBookCursor *cursor;
+ GDBusConnection *connection;
+ EContactField *sort_keys = NULL;
+ EBookCursorSortType *sort_types = NULL;
+ gint n_fields = 0;
+ gchar *object_path;
+ GError *error = NULL;
+
+ backend = e_data_book_ref_backend (data_book);
+ g_return_val_if_fail (backend != NULL, FALSE);
+
+ /*
+ * Interpret arguments
+ */
+ if (!data_book_interpret_sort_keys (in_sort_keys,
+ in_sort_types,
+ &sort_keys,
+ &sort_types,
+ &n_fields,
+ &error)) {
+ g_dbus_method_invocation_take_error (invocation, error);
+ g_object_unref (backend);
+ return TRUE;
+ }
+
+ /*
+ * Create cursor
+ */
+ cursor = e_book_backend_create_cursor (backend,
+ sort_keys,
+ sort_types,
+ n_fields,
+ &error);
+ g_free (sort_keys);
+ g_free (sort_types);
+
+ if (!cursor) {
+ g_dbus_method_invocation_take_error (invocation, error);
+ g_object_unref (backend);
+ return TRUE;
+ }
+
+ /*
+ * Set the query, if any (no query is allowed)
+ */
+ if (!e_data_book_cursor_set_sexp (cursor, in_query, &error)) {
+
+ e_book_backend_delete_cursor (backend, cursor, NULL);
+ g_dbus_method_invocation_take_error (invocation, error);
+ g_object_unref (backend);
+ return TRUE;
+ }
+
+ object_path = construct_bookcursor_path ();
+ connection = g_dbus_method_invocation_get_connection (invocation);
+
+ /*
+ * Now export the object on the connection
+ */
+ if (!e_data_book_cursor_register_gdbus_object (cursor, connection, object_path, &error)) {
+ e_book_backend_delete_cursor (backend, cursor, NULL);
+ g_dbus_method_invocation_take_error (invocation, error);
+ g_object_unref (backend);
+ g_free (object_path);
+ return TRUE;
+ }
+
+ /*
+ * All is good in the hood, complete the method call
+ */
+ e_dbus_address_book_complete_get_cursor (interface,
+ invocation,
+ object_path);
+ g_free (object_path);
+ g_object_unref (backend);
+ return TRUE;
+}
+
+static gboolean
data_book_handle_close_cb (EDBusAddressBook *interface,
GDBusMethodInvocation *invocation,
EDataBook *data_book)
@@ -1459,6 +1643,156 @@ 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 = e_data_book_ref_backend (book);
+
+ if (backend) {
+ const gchar * const *locale;
+ gchar *interpreted_locale;
+
+ locale = e_dbus_locale1_get_locale (locale_proxy);
+ interpreted_locale = data_book_interpret_locale (locale);
+
+ e_book_backend_set_locale (backend, interpreted_locale);
+
+ e_dbus_address_book_set_locale (book->priv->dbus_interface, interpreted_locale);
+
+ g_free (interpreted_locale);
+ }
+
+ g_object_unref (backend);
+}
+
+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,
EBookBackend *backend)
@@ -1574,6 +1908,17 @@ 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;
+ }
+
g_hash_table_remove_all (priv->sender_table);
/* Chain up to parent's dispose() metnod. */
@@ -1597,6 +1942,9 @@ data_book_finalize (GObject *object)
priv->dbus_interface = 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_parent_class)->finalize (object);
}
@@ -1659,6 +2007,12 @@ data_book_constructed (GObject *object)
book, prop_name, prop_value);
g_free (prop_value);
+ /* Initialize the locale to the value reported by setlocale() until
+ * systemd says otherwise.
+ */
+ e_dbus_address_book_set_locale (book->priv->dbus_interface,
+ setlocale (LC_COLLATE, NULL));
+
g_object_unref (backend);
}
@@ -1669,6 +2023,8 @@ data_book_initable_init (GInitable *initable,
{
EBookBackend *backend;
EDataBook *book;
+ gchar *locale;
+ GBusType bus_type = G_BUS_TYPE_SYSTEM;
book = E_DATA_BOOK (initable);
@@ -1698,6 +2054,29 @@ data_book_initable_init (GInitable *initable,
return FALSE;
}
+ /* Fetch backend configured locale and set that as the initial
+ * value on the dbus object
+ */
+ locale = e_book_backend_dup_locale (backend);
+ 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_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,
@@ -1819,6 +2198,10 @@ e_data_book_init (EDataBook *data_book)
G_CALLBACK (data_book_handle_get_view_cb),
data_book);
g_signal_connect (
+ dbus_interface, "handle-get-cursor",
+ G_CALLBACK (data_book_handle_get_cursor_cb),
+ data_book);
+ g_signal_connect (
dbus_interface, "handle-close",
G_CALLBACK (data_book_handle_close_cb),
data_book);