diff options
author | Tristan Van Berkom <tristanvb@openismus.com> | 2013-05-23 15:24:09 +0900 |
---|---|---|
committer | Tristan Van Berkom <tristanvb@openismus.com> | 2013-10-23 20:39:39 +0200 |
commit | 7df46dce9a0165f82d4d5c8eb1f0ebeb9fad70ee (patch) | |
tree | d4f8dba3d0727d8a70143e64ddde94f60969ea93 | |
parent | e0cc4b07270348023104192db66545e104d94562 (diff) | |
download | evolution-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.c | 383 |
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); |