[evolution-data-server] Bug #655190 - Sluggish performance interacting with calendar/tasks
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Bug #655190 - Sluggish performance interacting with calendar/tasks
- Date: Tue, 2 Aug 2011 13:12:39 +0000 (UTC)
commit 549a80da23e1c6879ced6a277c547fb94a2a7e11
Author: Milan Crha <mcrha redhat com>
Date: Tue Aug 2 15:12:14 2011 +0200
Bug #655190 - Sluggish performance interacting with calendar/tasks
addressbook/libebook/e-book-client.c | 92 ++++-
addressbook/libedata-book/e-book-backend.c | 30 ++
addressbook/libedata-book/e-book-backend.h | 1 +
addressbook/libedata-book/e-data-book.c | 19 +
addressbook/libedata-book/e-data-book.h | 1 +
addressbook/libegdbus/e-gdbus-book.c | 32 +-
addressbook/libegdbus/e-gdbus-book.h | 2 +
calendar/libecal/e-cal-client.c | 682 +++++++++++++++++++++++-----
calendar/libecal/e-cal-client.h | 6 +-
calendar/libedata-cal/e-cal-backend.c | 30 ++
calendar/libedata-cal/e-cal-backend.h | 1 +
calendar/libedata-cal/e-data-cal.c | 19 +
calendar/libedata-cal/e-data-cal.h | 1 +
calendar/libegdbus/e-gdbus-cal.c | 52 +--
calendar/libegdbus/e-gdbus-cal.h | 2 +
configure.ac | 10 +-
libedataserver/e-client-private.h | 7 +
libedataserver/e-client.c | 126 +++++
libedataserver/e-client.h | 1 +
libedataserver/e-gdbus-marshallers.list | 1 +
libedataserver/e-gdbus-templates.c | 34 ++-
libedataserver/e-gdbus-templates.h | 3 +
libedataserverui/e-client-utils.c | 59 +++-
23 files changed, 1033 insertions(+), 178 deletions(-)
---
diff --git a/addressbook/libebook/e-book-client.c b/addressbook/libebook/e-book-client.c
index 4899a58..a2fd03f 100644
--- a/addressbook/libebook/e-book-client.c
+++ b/addressbook/libebook/e-book-client.c
@@ -392,6 +392,25 @@ opened_cb (EGdbusBook *object, const gchar * const *error_strv, EBookClient *cli
g_error_free (error);
}
+static void
+backend_property_changed_cb (EGdbusBook *object, const gchar * const *name_value_strv, EBookClient *client)
+{
+ gchar *prop_name = NULL, *prop_value = NULL;
+
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (E_IS_BOOK_CLIENT (client));
+ g_return_if_fail (name_value_strv != NULL);
+ g_return_if_fail (e_gdbus_templates_decode_two_strings (name_value_strv, &prop_name, &prop_value));
+ g_return_if_fail (prop_name != NULL);
+ g_return_if_fail (*prop_name);
+ g_return_if_fail (prop_value != NULL);
+
+ e_client_emit_backend_property_changed (E_CLIENT (client), prop_name, prop_value);
+
+ g_free (prop_name);
+ g_free (prop_value);
+}
+
/**
* e_book_client_new:
* @source: An #ESource pointer
@@ -496,6 +515,7 @@ e_book_client_new (ESource *source, GError **error)
g_signal_connect (client->priv->gdbus_book, "online", G_CALLBACK (online_cb), client);
g_signal_connect (client->priv->gdbus_book, "auth-required", G_CALLBACK (auth_required_cb), client);
g_signal_connect (client->priv->gdbus_book, "opened", G_CALLBACK (opened_cb), client);
+ g_signal_connect (client->priv->gdbus_book, "backend-property-changed", G_CALLBACK (backend_property_changed_cb), client);
return client;
}
@@ -884,24 +904,76 @@ e_book_client_is_self (EContact *contact)
return is_self;
}
+static gboolean
+book_client_get_backend_property_from_cache_finish (EClient *client, GAsyncResult *result, gchar **prop_value, GError **error)
+{
+ GSimpleAsyncResult *simple;
+ GError *local_error = NULL;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
+ g_return_val_if_fail (result != NULL, FALSE);
+ g_return_val_if_fail (prop_value != NULL, FALSE);
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (client), book_client_get_backend_property_from_cache_finish), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, &local_error)) {
+ e_client_unwrap_dbus_error (client, local_error, error);
+ return FALSE;
+ }
+
+ *prop_value = g_strdup (g_simple_async_result_get_op_res_gpointer (simple));
+
+ return *prop_value != NULL;
+}
+
static void
book_client_get_backend_property (EClient *client, const gchar *prop_name, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
- e_client_proxy_call_string (client, prop_name, cancellable, callback, user_data, book_client_get_backend_property,
+ gchar *prop_value;
+
+ prop_value = e_client_get_backend_property_from_cache (client, prop_name);
+ if (prop_value) {
+ e_client_finish_async_without_dbus (client, cancellable, callback, user_data, book_client_get_backend_property_from_cache_finish, prop_value, g_free);
+ } else {
+ e_client_proxy_call_string_with_res_op_data (client, prop_name, cancellable, callback, user_data, book_client_get_backend_property, prop_name,
e_gdbus_book_call_get_backend_property,
NULL, NULL, e_gdbus_book_call_get_backend_property_finish, NULL, NULL);
+ }
}
static gboolean
book_client_get_backend_property_finish (EClient *client, GAsyncResult *result, gchar **prop_value, GError **error)
{
- return e_client_proxy_call_finish_string (client, result, prop_value, error, book_client_get_backend_property);
+ gchar *str = NULL;
+ gboolean res;
+
+ g_return_val_if_fail (prop_value != NULL, FALSE);
+
+ if (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (result)) == book_client_get_backend_property_from_cache_finish) {
+ res = book_client_get_backend_property_from_cache_finish (client, result, &str, error);
+ } else {
+ res = e_client_proxy_call_finish_string (client, result, &str, error, book_client_get_backend_property);
+ if (res && str) {
+ const gchar *prop_name = g_object_get_data (G_OBJECT (result), "res-op-data");
+
+ if (prop_name && *prop_name)
+ e_client_update_backend_property_cache (client, prop_name, str);
+ }
+ }
+
+ *prop_value = str;
+
+ return res;
}
static gboolean
book_client_get_backend_property_sync (EClient *client, const gchar *prop_name, gchar **prop_value, GCancellable *cancellable, GError **error)
{
EBookClient *book_client;
+ gchar *prop_val;
+ gboolean res;
g_return_val_if_fail (client != NULL, FALSE);
g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
@@ -915,7 +987,21 @@ book_client_get_backend_property_sync (EClient *client, const gchar *prop_name,
return FALSE;
}
- return e_client_proxy_call_sync_string__string (client, prop_name, prop_value, cancellable, error, e_gdbus_book_call_get_backend_property_sync);
+ prop_val = e_client_get_backend_property_from_cache (client, prop_name);
+ if (prop_val) {
+ g_return_val_if_fail (prop_value != NULL, FALSE);
+
+ *prop_value = prop_val;
+
+ return TRUE;
+ }
+
+ res = e_client_proxy_call_sync_string__string (client, prop_name, prop_value, cancellable, error, e_gdbus_book_call_get_backend_property_sync);
+
+ if (res && prop_value)
+ e_client_update_backend_property_cache (client, prop_name, *prop_value);
+
+ return res;
}
static void
diff --git a/addressbook/libedata-book/e-book-backend.c b/addressbook/libedata-book/e-book-backend.c
index 49d7359..d4b345e 100644
--- a/addressbook/libedata-book/e-book-backend.c
+++ b/addressbook/libedata-book/e-book-backend.c
@@ -1270,6 +1270,36 @@ e_book_backend_notify_opened (EBookBackend *backend, GError *error)
}
/**
+ * e_book_backend_notify_property_changed:
+ * @backend: an #EBookBackend
+ * @prop_name: property name, which changed
+ * @prop_value: new property value
+ *
+ * Notifies clients about property value change.
+ **/
+void
+e_book_backend_notify_property_changed (EBookBackend *backend, const gchar *prop_name, const gchar *prop_value)
+{
+ EBookBackendPrivate *priv;
+ GSList *clients;
+
+ g_return_if_fail (backend != NULL);
+ g_return_if_fail (E_IS_BOOK_BACKEND (backend));
+ g_return_if_fail (backend->priv != NULL);
+ g_return_if_fail (prop_name != NULL);
+ g_return_if_fail (*prop_name != '\0');
+ g_return_if_fail (prop_value != NULL);
+
+ priv = backend->priv;
+ g_mutex_lock (priv->clients_mutex);
+
+ for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
+ e_data_book_report_backend_property_changed (E_DATA_BOOK (clients->data), prop_name, prop_value);
+
+ g_mutex_unlock (priv->clients_mutex);
+}
+
+/**
* e_book_backend_respond_opened:
* @backend: an #EBookBackend
* @book: an #EDataBook
diff --git a/addressbook/libedata-book/e-book-backend.h b/addressbook/libedata-book/e-book-backend.h
index a9f81d3..f034730 100644
--- a/addressbook/libedata-book/e-book-backend.h
+++ b/addressbook/libedata-book/e-book-backend.h
@@ -130,6 +130,7 @@ void e_book_backend_notify_readonly (EBookBackend *backend, gboolean is_readonl
void e_book_backend_notify_online (EBookBackend *backend, gboolean is_online);
void e_book_backend_notify_auth_required (EBookBackend *backend, gboolean is_self, const ECredentials *credentials);
void e_book_backend_notify_opened (EBookBackend *backend, GError *error);
+void e_book_backend_notify_property_changed (EBookBackend *backend, const gchar *prop_name, const gchar *prop_value);
void e_book_backend_sync (EBookBackend *backend);
diff --git a/addressbook/libedata-book/e-data-book.c b/addressbook/libedata-book/e-data-book.c
index 26c10ab..5c44a06 100644
--- a/addressbook/libedata-book/e-data-book.c
+++ b/addressbook/libedata-book/e-data-book.c
@@ -1047,6 +1047,25 @@ e_data_book_report_opened (EDataBook *book, const GError *error)
g_strfreev (strv_error);
}
+/* Notifies client about certain property value change */
+void
+e_data_book_report_backend_property_changed (EDataBook *book, const gchar *prop_name, const gchar *prop_value)
+{
+ gchar **strv;
+
+ g_return_if_fail (book != NULL);
+ g_return_if_fail (prop_name != NULL);
+ g_return_if_fail (*prop_name != '\0');
+ g_return_if_fail (prop_value != NULL);
+
+ strv = e_gdbus_templates_encode_two_strings (prop_name, prop_value);
+ g_return_if_fail (strv != NULL);
+
+ e_gdbus_book_emit_backend_property_changed (book->priv->gdbus_object, (const gchar * const *) strv);
+
+ g_strfreev (strv);
+}
+
/**
* e_data_book_register_gdbus_object:
*
diff --git a/addressbook/libedata-book/e-data-book.h b/addressbook/libedata-book/e-data-book.h
index 5185eba..f875d5c 100644
--- a/addressbook/libedata-book/e-data-book.h
+++ b/addressbook/libedata-book/e-data-book.h
@@ -149,6 +149,7 @@ void e_data_book_report_readonly (EDataBook *book, gboolean readonly);
void e_data_book_report_online (EDataBook *book, gboolean is_online);
void e_data_book_report_auth_required (EDataBook *book, const ECredentials *credentials);
void e_data_book_report_opened (EDataBook *book, const GError *error);
+void e_data_book_report_backend_property_changed (EDataBook *book, const gchar *prop_name, const gchar *prop_value);
gchar * e_data_book_string_slist_to_comma_string (const GSList *strings);
diff --git a/addressbook/libegdbus/e-gdbus-book.c b/addressbook/libegdbus/e-gdbus-book.c
index b115be2..1096692 100644
--- a/addressbook/libegdbus/e-gdbus-book.c
+++ b/addressbook/libegdbus/e-gdbus-book.c
@@ -40,6 +40,7 @@ enum
__ONLINE_SIGNAL,
__AUTH_REQUIRED_SIGNAL,
__OPENED_SIGNAL,
+ __BACKEND_PROPERTY_CHANGED_SIGNAL,
__OPEN_METHOD,
__OPEN_DONE_SIGNAL,
__REMOVE_METHOD,
@@ -118,6 +119,7 @@ E_DECLARE_GDBUS_SIGNAL_EMISSION_HOOK_BOOLEAN (GDBUS_BOOK_INTERFACE_NAME, readonl
E_DECLARE_GDBUS_SIGNAL_EMISSION_HOOK_BOOLEAN (GDBUS_BOOK_INTERFACE_NAME, online)
E_DECLARE_GDBUS_SIGNAL_EMISSION_HOOK_STRV (GDBUS_BOOK_INTERFACE_NAME, auth_required)
E_DECLARE_GDBUS_SIGNAL_EMISSION_HOOK_STRV (GDBUS_BOOK_INTERFACE_NAME, opened)
+E_DECLARE_GDBUS_SIGNAL_EMISSION_HOOK_STRV (GDBUS_BOOK_INTERFACE_NAME, backend_property_changed)
E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_BOOK_INTERFACE_NAME, open)
E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_BOOK_INTERFACE_NAME, remove)
@@ -147,6 +149,7 @@ e_gdbus_book_default_init (EGdbusBookIface *iface)
E_INIT_GDBUS_SIGNAL_BOOLEAN (EGdbusBookIface, "online", online, __ONLINE_SIGNAL)
E_INIT_GDBUS_SIGNAL_STRV (EGdbusBookIface, "auth_required", auth_required, __AUTH_REQUIRED_SIGNAL)
E_INIT_GDBUS_SIGNAL_STRV (EGdbusBookIface, "opened", opened, __OPENED_SIGNAL)
+ E_INIT_GDBUS_SIGNAL_STRV (EGdbusBookIface, "backend_property_changed", backend_property_changed, __BACKEND_PROPERTY_CHANGED_SIGNAL)
/* GObject signals definitions for D-Bus methods: */
E_INIT_GDBUS_METHOD_ASYNC_BOOLEAN__VOID (EGdbusBookIface, "open", open, __OPEN_METHOD, __OPEN_DONE_SIGNAL)
@@ -371,31 +374,14 @@ e_gdbus_book_call_get_backend_property_sync (GDBusProxy *proxy, const gchar *in_
gchar **
e_gdbus_book_encode_set_backend_property (const gchar *in_prop_name, const gchar *in_prop_value)
{
- gchar **strv;
-
- strv = g_new0 (gchar *, 3);
- strv[0] = e_util_utf8_make_valid (in_prop_name ? in_prop_name : "");
- strv[1] = e_util_utf8_make_valid (in_prop_value ? in_prop_value : "");
- strv[2] = NULL;
-
- return strv;
+ return e_gdbus_templates_encode_two_strings (in_prop_name, in_prop_value);
}
/* free out_prop_name and out_prop_value with g_free() */
gboolean
e_gdbus_book_decode_set_backend_property (const gchar * const *in_strv, gchar **out_prop_name, gchar **out_prop_value)
{
- g_return_val_if_fail (in_strv != NULL, FALSE);
- g_return_val_if_fail (in_strv[0] != NULL, FALSE);
- g_return_val_if_fail (in_strv[1] != NULL, FALSE);
- g_return_val_if_fail (in_strv[2] == NULL, FALSE);
- g_return_val_if_fail (out_prop_name != NULL, FALSE);
- g_return_val_if_fail (out_prop_value != NULL, FALSE);
-
- *out_prop_name = g_strdup (in_strv[0]);
- *out_prop_value = g_strdup (in_strv[1]);
-
- return TRUE;
+ return e_gdbus_templates_decode_two_strings (in_strv, out_prop_name, out_prop_value);
}
void
@@ -570,11 +556,18 @@ e_gdbus_book_emit_opened (EGdbusBook *object, const gchar * const *arg_error)
g_signal_emit (object, signals[__OPENED_SIGNAL], 0, arg_error);
}
+void
+e_gdbus_book_emit_backend_property_changed (EGdbusBook *object, const gchar * const *arg_name_value)
+{
+ g_signal_emit (object, signals[__BACKEND_PROPERTY_CHANGED_SIGNAL], 0, arg_name_value);
+}
+
E_DECLARE_GDBUS_NOTIFY_SIGNAL_1 (book, backend_error, message, "s")
E_DECLARE_GDBUS_NOTIFY_SIGNAL_1 (book, readonly, is_readonly, "b")
E_DECLARE_GDBUS_NOTIFY_SIGNAL_1 (book, online, is_online, "b")
E_DECLARE_GDBUS_NOTIFY_SIGNAL_1 (book, auth_required, credentials, "as")
E_DECLARE_GDBUS_NOTIFY_SIGNAL_1 (book, opened, error, "as")
+E_DECLARE_GDBUS_NOTIFY_SIGNAL_1 (book, backend_property_changed, name_value, "as")
E_DECLARE_GDBUS_ASYNC_METHOD_1 (book, open, only_if_exists, "b")
E_DECLARE_GDBUS_ASYNC_METHOD_0 (book, remove)
@@ -622,6 +615,7 @@ static const GDBusSignalInfo * const e_gdbus_book_signal_info_pointers[] =
&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, online),
&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, auth_required),
&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, opened),
+ &E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, backend_property_changed),
&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, open_done),
&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, remove_done),
diff --git a/addressbook/libegdbus/e-gdbus-book.h b/addressbook/libegdbus/e-gdbus-book.h
index 4fa97da..aea23ef 100644
--- a/addressbook/libegdbus/e-gdbus-book.h
+++ b/addressbook/libegdbus/e-gdbus-book.h
@@ -117,6 +117,7 @@ struct _EGdbusBookIface
void (*online) (EGdbusBook *object, gboolean arg_is_online);
void (*auth_required) (EGdbusBook *object, const gchar * const *arg_credentials);
void (*opened) (EGdbusBook *object, const gchar * const *arg_error);
+ void (*backend_property_changed) (EGdbusBook *object, const gchar * const *arg_name_value);
/* Signal handlers for handling D-Bus method calls: */
gboolean (*handle_open) (EGdbusBook *object, GDBusMethodInvocation *invocation, gboolean in_only_if_exists);
@@ -268,6 +269,7 @@ void e_gdbus_book_emit_readonly (EGdbusBook *object, gboolean arg_is_readonly);
void e_gdbus_book_emit_online (EGdbusBook *object, gboolean arg_is_online);
void e_gdbus_book_emit_auth_required (EGdbusBook *object, const gchar * const *arg_credentials);
void e_gdbus_book_emit_opened (EGdbusBook *object, const gchar * const *arg_error);
+void e_gdbus_book_emit_backend_property_changed (EGdbusBook *object, const gchar * const *arg_name_value);
G_END_DECLS
diff --git a/calendar/libecal/e-cal-client.c b/calendar/libecal/e-cal-client.c
index 64be096..7d956b3 100644
--- a/calendar/libecal/e-cal-client.c
+++ b/calendar/libecal/e-cal-client.c
@@ -479,6 +479,25 @@ free_busy_data_cb (EGdbusCal *object, const gchar * const *free_busy_strv, ECalC
e_client_util_free_object_slist (ecalcomps);
}
+static void
+backend_property_changed_cb (EGdbusCal *object, const gchar * const *name_value_strv, ECalClient *client)
+{
+ gchar *prop_name = NULL, *prop_value = NULL;
+
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (E_IS_CAL_CLIENT (client));
+ g_return_if_fail (name_value_strv != NULL);
+ g_return_if_fail (e_gdbus_templates_decode_two_strings (name_value_strv, &prop_name, &prop_value));
+ g_return_if_fail (prop_name != NULL);
+ g_return_if_fail (*prop_name);
+ g_return_if_fail (prop_value != NULL);
+
+ e_client_emit_backend_property_changed (E_CLIENT (client), prop_name, prop_value);
+
+ g_free (prop_name);
+ g_free (prop_value);
+}
+
static EDataCalObjType
convert_type (ECalClientSourceType type)
{
@@ -613,6 +632,7 @@ e_cal_client_new (ESource *source, ECalClientSourceType source_type, GError **er
g_signal_connect (client->priv->gdbus_cal, "auth-required", G_CALLBACK (auth_required_cb), client);
g_signal_connect (client->priv->gdbus_cal, "opened", G_CALLBACK (opened_cb), client);
g_signal_connect (client->priv->gdbus_cal, "free-busy-data", G_CALLBACK (free_busy_data_cb), client);
+ g_signal_connect (client->priv->gdbus_cal, "backend-property-changed", G_CALLBACK (backend_property_changed_cb), client);
return client;
}
@@ -903,7 +923,9 @@ e_cal_client_get_local_attachment_store (ECalClient *client)
if (client->priv->cache_dir || !client->priv->gdbus_cal)
return client->priv->cache_dir;
- e_gdbus_cal_call_get_backend_property_sync (client->priv->gdbus_cal, CLIENT_BACKEND_PROPERTY_CACHE_DIR, &cache_dir, NULL, &error);
+ cache_dir = e_client_get_backend_property_from_cache (E_CLIENT (client), CLIENT_BACKEND_PROPERTY_CACHE_DIR);
+ if (!cache_dir)
+ e_gdbus_cal_call_get_backend_property_sync (client->priv->gdbus_cal, CLIENT_BACKEND_PROPERTY_CACHE_DIR, &cache_dir, NULL, &error);
if (error == NULL) {
client->priv->cache_dir = cache_dir;
@@ -1252,6 +1274,8 @@ process_detached_instances (GSList *instances, GSList *detached_instances)
ECalComponentRange recur_id, instance_recur_id;
processed = FALSE;
+ recur_id.type = E_CAL_COMPONENT_RANGE_SINGLE;
+ instance_recur_id.type = E_CAL_COMPONENT_RANGE_SINGLE;
cid = dl->data;
e_cal_component_get_uid (cid->comp, &uid);
@@ -1343,63 +1367,18 @@ process_detached_instances (GSList *instances, GSList *detached_instances)
}
static void
-generate_instances (ECalClient *client, time_t start, time_t end, const gchar *uid,
+generate_instances (ECalClient *client, time_t start, time_t end, GSList *objects, GCancellable *cancellable,
ECalRecurInstanceFn cb, gpointer cb_data)
{
- GSList *objects = NULL;
GSList *instances, *detached_instances = NULL;
GSList *l;
- gchar *query;
- gchar *iso_start, *iso_end;
ECalClientPrivate *priv;
priv = client->priv;
- /* Generate objects */
- if (uid && *uid) {
- GError *error = NULL;
- gint tries = 0;
-
- try_again:
- if (!e_cal_client_get_objects_for_uid_sync (client, uid, &objects, NULL, &error)) {
- if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_BUSY) && tries >= 10) {
- tries++;
- g_usleep (500);
- g_clear_error (&error);
-
- goto try_again;
- }
-
- unwrap_dbus_error (error, &error);
- g_message ("Failed to get recurrence objects for uid %s \n", error ? error->message : "Unknown error");
- g_clear_error (&error);
- return;
- }
- } else {
- iso_start = isodate_from_time_t (start);
- if (!iso_start)
- return;
-
- iso_end = isodate_from_time_t (end);
- if (!iso_end) {
- g_free (iso_start);
- return;
- }
-
- query = g_strdup_printf ("(occur-in-time-range? (make-time \"%s\") (make-time \"%s\"))",
- iso_start, iso_end);
- g_free (iso_start);
- g_free (iso_end);
- if (!e_cal_client_get_object_list_as_comps_sync (client, query, &objects, NULL, NULL)) {
- g_free (query);
- return;
- }
- g_free (query);
- }
-
instances = NULL;
- for (l = objects; l; l = l->next) {
+ for (l = objects; l && !g_cancellable_is_cancelled (cancellable); l = l->next) {
ECalComponent *comp;
icaltimezone *default_zone;
@@ -1416,7 +1395,7 @@ generate_instances (ECalClient *client, time_t start, time_t end, const gchar *u
/* keep the detached instances apart */
ci = g_new0 (struct comp_instance, 1);
- ci->comp = comp;
+ ci->comp = g_object_ref (comp);
e_cal_component_get_dtstart (comp, &dtstart);
e_cal_component_get_dtend (comp, &dtend);
@@ -1469,7 +1448,7 @@ generate_instances (ECalClient *client, time_t start, time_t end, const gchar *u
/* Get the start timezone */
e_cal_component_get_dtstart (comp, &datetime);
if (datetime.tzid)
- e_cal_client_get_timezone_sync (client, datetime.tzid, &start_zone, NULL, NULL);
+ e_cal_client_get_timezone_sync (client, datetime.tzid, &start_zone, cancellable, NULL);
else
start_zone = NULL;
e_cal_component_free_datetime (&datetime);
@@ -1483,18 +1462,20 @@ generate_instances (ECalClient *client, time_t start, time_t end, const gchar *u
default_zone);
g_free (instances_hold);
- g_object_unref (comp);
}
}
+ g_slist_foreach (objects, (GFunc) g_object_unref, NULL);
g_slist_free (objects);
/* Generate instances and spew them out */
- instances = g_slist_sort (instances, compare_comp_instance);
- instances = process_detached_instances (instances, detached_instances);
+ if (!g_cancellable_is_cancelled (cancellable)) {
+ instances = g_slist_sort (instances, compare_comp_instance);
+ instances = process_detached_instances (instances, detached_instances);
+ }
- for (l = instances; l; l = l->next) {
+ for (l = instances; l && !g_cancellable_is_cancelled (cancellable); l = l->next) {
struct comp_instance *ci;
gboolean result;
@@ -1529,11 +1510,305 @@ generate_instances (ECalClient *client, time_t start, time_t end, const gchar *u
g_slist_free (detached_instances);
}
+static GSList *
+get_objects_sync (ECalClient *client, time_t start, time_t end, const gchar *uid)
+{
+ GSList *objects = NULL;
+
+ /* Generate objects */
+ if (uid && *uid) {
+ GError *error = NULL;
+ gint tries = 0;
+
+ try_again:
+ if (!e_cal_client_get_objects_for_uid_sync (client, uid, &objects, NULL, &error)) {
+ if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_BUSY) && tries >= 10) {
+ tries++;
+ g_usleep (500);
+ g_clear_error (&error);
+
+ goto try_again;
+ }
+
+ unwrap_dbus_error (error, &error);
+ g_message ("Failed to get recurrence objects for uid %s \n", error ? error->message : "Unknown error");
+ g_clear_error (&error);
+ return NULL;
+ }
+ } else {
+ gchar *iso_start, *iso_end;
+ gchar *query;
+
+ iso_start = isodate_from_time_t (start);
+ if (!iso_start)
+ return NULL;
+
+ iso_end = isodate_from_time_t (end);
+ if (!iso_end) {
+ g_free (iso_start);
+ return NULL;
+ }
+
+ query = g_strdup_printf ("(occur-in-time-range? (make-time \"%s\") (make-time \"%s\"))",
+ iso_start, iso_end);
+ g_free (iso_start);
+ g_free (iso_end);
+ if (!e_cal_client_get_object_list_as_comps_sync (client, query, &objects, NULL, NULL)) {
+ g_free (query);
+ return NULL;
+ }
+ g_free (query);
+ }
+
+ return objects;
+}
+
+struct get_objects_async_data
+{
+ GCancellable *cancellable;
+ ECalClient *client;
+ time_t start;
+ time_t end;
+ ECalRecurInstanceFn cb;
+ gpointer cb_data;
+ GDestroyNotify destroy_cb_data;
+ gchar *uid;
+ gchar *query;
+ guint tries;
+ void (* ready_cb) (struct get_objects_async_data *goad, GSList *objects);
+ icaltimezone *start_zone;
+ ECalComponent *comp;
+};
+
+static void
+free_get_objects_async_data (struct get_objects_async_data *goad)
+{
+ if (!goad)
+ return;
+
+ if (goad->cancellable) {
+ g_cancellable_cancel (goad->cancellable);
+ g_object_unref (goad->cancellable);
+ }
+ if (goad->destroy_cb_data)
+ goad->destroy_cb_data (goad->cb_data);
+ if (goad->client)
+ g_object_unref (goad->client);
+ if (goad->comp)
+ g_object_unref (goad->comp);
+ g_free (goad->query);
+ g_free (goad->uid);
+ g_free (goad);
+}
+
+static gboolean repeat_get_objects_for_uid_timeout_cb (gpointer user_data);
+
+static void
+got_objects_for_uid_cb (GObject *source_object, GAsyncResult *result, gpointer user_data)
+{
+ struct get_objects_async_data *goad = user_data;
+ GSList *objects = NULL;
+ GError *error = NULL;
+
+ g_return_if_fail (source_object != NULL);
+ g_return_if_fail (result != NULL);
+ g_return_if_fail (goad != NULL);
+ g_return_if_fail (goad->client == E_CAL_CLIENT (source_object));
+
+ if (!e_cal_client_get_objects_for_uid_finish (goad->client, result, &objects, &error)) {
+ if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ free_get_objects_async_data (goad);
+ g_clear_error (&error);
+ return;
+ }
+
+ if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_BUSY) && goad->tries < 10) {
+ goad->tries++;
+ g_timeout_add (250, repeat_get_objects_for_uid_timeout_cb, goad);
+ g_clear_error (&error);
+ return;
+ }
+
+ g_clear_error (&error);
+ objects = NULL;
+ }
+
+ g_return_if_fail (goad->ready_cb != NULL);
+
+ /* takes care of the objects and goad */
+ goad->ready_cb (goad, objects);
+}
+
+static gboolean
+repeat_get_objects_for_uid_timeout_cb (gpointer user_data)
+{
+ struct get_objects_async_data *goad = user_data;
+
+ g_return_val_if_fail (goad != NULL, FALSE);
+
+ e_cal_client_get_objects_for_uid (goad->client, goad->uid, goad->cancellable, got_objects_for_uid_cb, goad);
+
+ return FALSE;
+}
+
+static gboolean repeat_get_object_list_as_comps_timeout_cb (gpointer user_data);
+
+static void
+got_object_list_as_comps_cb (GObject *source_object, GAsyncResult *result, gpointer user_data)
+{
+ struct get_objects_async_data *goad = user_data;
+ GSList *objects = NULL;
+ GError *error = NULL;
+
+ g_return_if_fail (source_object != NULL);
+ g_return_if_fail (result != NULL);
+ g_return_if_fail (goad != NULL);
+ g_return_if_fail (goad->client == E_CAL_CLIENT (source_object));
+
+ if (!e_cal_client_get_object_list_as_comps_finish (goad->client, result, &objects, &error)) {
+ if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ free_get_objects_async_data (goad);
+ g_clear_error (&error);
+ return;
+ }
+
+ if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_BUSY) && goad->tries < 10) {
+ goad->tries++;
+ g_timeout_add (250, repeat_get_object_list_as_comps_timeout_cb, goad);
+ g_clear_error (&error);
+ return;
+ }
+
+ g_clear_error (&error);
+ objects = NULL;
+ }
+
+ g_return_if_fail (goad->ready_cb != NULL);
+
+ /* takes care of the objects and goad */
+ goad->ready_cb (goad, objects);
+}
+
+static gboolean
+repeat_get_object_list_as_comps_timeout_cb (gpointer user_data)
+{
+ struct get_objects_async_data *goad = user_data;
+
+ g_return_val_if_fail (goad != NULL, FALSE);
+
+ e_cal_client_get_object_list_as_comps (goad->client, goad->query, goad->cancellable, got_object_list_as_comps_cb, goad);
+
+ return FALSE;
+}
+
+/* ready_cb may take care of both arguments, goad and objects; objects can be also NULL */
+static void
+get_objects_async (void (* ready_cb)(struct get_objects_async_data *goad, GSList *objects), struct get_objects_async_data *goad)
+{
+ g_return_if_fail (ready_cb != NULL);
+ g_return_if_fail (goad != NULL);
+
+ goad->ready_cb = ready_cb;
+
+ if (goad->uid && *goad->uid) {
+ e_cal_client_get_objects_for_uid (goad->client, goad->uid, goad->cancellable, got_objects_for_uid_cb, goad);
+ } else {
+ gchar *iso_start, *iso_end;
+
+ iso_start = isodate_from_time_t (goad->start);
+ if (!iso_start) {
+ free_get_objects_async_data (goad);
+ return;
+ }
+
+ iso_end = isodate_from_time_t (goad->end);
+ if (!iso_end) {
+ g_free (iso_start);
+ free_get_objects_async_data (goad);
+ return;
+ }
+
+ goad->query = g_strdup_printf ("(occur-in-time-range? (make-time \"%s\") (make-time \"%s\"))", iso_start, iso_end);
+
+ g_free (iso_start);
+ g_free (iso_end);
+
+ e_cal_client_get_object_list_as_comps (goad->client, goad->query, goad->cancellable, got_object_list_as_comps_cb, goad);
+ }
+}
+
+static void
+generate_instances_got_objects_cb (struct get_objects_async_data *goad, GSList *objects)
+{
+ g_return_if_fail (goad != NULL);
+
+ /* generate_instaces () frees 'objects' slist */
+ if (objects)
+ generate_instances (goad->client, goad->start, goad->end, objects, goad->cancellable, goad->cb, goad->cb_data);
+
+ free_get_objects_async_data (goad);
+}
+
/**
* e_cal_client_generate_instances:
* @client: A calendar client.
* @start: Start time for query.
* @end: End time for query.
+ * @cancellable: a #GCancellable; can be %NULL
+ * @cb: Callback for each generated instance.
+ * @cb_data: Closure data for the callback.
+ * @destroy_cb_data: Function to call when the processing is done, to free @cb_data; can be %NULL.
+ *
+ * Does a combination of #e_cal_client_get_object_list () and
+ * #e_cal_client_recur_generate_instances(). Unlike #e_cal_client_generate_instances_sync (),
+ * this returns immediately and the @cb callback is called asynchronously.
+ *
+ * The callback function should do a g_object_ref() of the calendar component
+ * it gets passed if it intends to keep it around, since it will be unref'ed
+ * as soon as the callback returns.
+ *
+ * Since: 3.2
+ **/
+void
+e_cal_client_generate_instances (ECalClient *client, time_t start, time_t end, GCancellable *cancellable, ECalRecurInstanceFn cb, gpointer cb_data, GDestroyNotify destroy_cb_data)
+{
+ struct get_objects_async_data *goad;
+ GCancellable *use_cancellable;
+
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (E_IS_CAL_CLIENT (client));
+ g_return_if_fail (e_client_is_opened (E_CLIENT (client)));
+
+ g_return_if_fail (start >= 0);
+ g_return_if_fail (end >= 0);
+ g_return_if_fail (cb != NULL);
+
+ use_cancellable = cancellable;
+ if (!use_cancellable)
+ use_cancellable = g_cancellable_new ();
+
+ goad = g_new0 (struct get_objects_async_data, 1);
+ goad->cancellable = g_object_ref (use_cancellable);
+ goad->client = g_object_ref (client);
+ goad->start = start;
+ goad->end = end;
+ goad->cb = cb;
+ goad->cb_data = cb_data;
+ goad->destroy_cb_data = destroy_cb_data;
+
+ get_objects_async (generate_instances_got_objects_cb, goad);
+
+ if (use_cancellable != cancellable)
+ g_object_unref (use_cancellable);
+}
+
+/**
+ * e_cal_client_generate_instances_sync:
+ * @client: A calendar client.
+ * @start: Start time for query.
+ * @end: End time for query.
* @cb: Callback for each generated instance.
* @cb_data: Closure data for the callback.
*
@@ -1543,10 +1818,14 @@ generate_instances (ECalClient *client, time_t start, time_t end, const gchar *u
* The callback function should do a g_object_ref() of the calendar component
* it gets passed if it intends to keep it around, since it will be unref'ed
* as soon as the callback returns.
+ *
+ * Since: 3.2
**/
void
-e_cal_client_generate_instances (ECalClient *client, time_t start, time_t end, ECalRecurInstanceFn cb, gpointer cb_data)
+e_cal_client_generate_instances_sync (ECalClient *client, time_t start, time_t end, ECalRecurInstanceFn cb, gpointer cb_data)
{
+ GSList *objects = NULL;
+
g_return_if_fail (client != NULL);
g_return_if_fail (E_IS_CAL_CLIENT (client));
g_return_if_fail (e_client_is_opened (E_CLIENT (client)));
@@ -1555,7 +1834,76 @@ e_cal_client_generate_instances (ECalClient *client, time_t start, time_t end, E
g_return_if_fail (end >= 0);
g_return_if_fail (cb != NULL);
- generate_instances (client, start, end, NULL, cb, cb_data);
+ objects = get_objects_sync (client, start, end, NULL);
+ if (!objects)
+ return;
+
+ /* generate_instaces frees 'objects' slist */
+ generate_instances (client, start, end, objects, NULL, cb, cb_data);
+}
+
+/* also frees 'instances' GSList */
+static void
+process_instances (ECalComponent *comp, GSList *instances, ECalRecurInstanceFn cb, gpointer cb_data)
+{
+ gchar *rid;
+ gboolean result;
+
+ g_return_if_fail (comp != NULL);
+ g_return_if_fail (cb != NULL);
+
+ rid = e_cal_component_get_recurid_as_string (comp);
+
+ /* now only return back the instances for the given object */
+ result = TRUE;
+ while (instances != NULL) {
+ struct comp_instance *ci;
+ gchar *instance_rid = NULL;
+
+ ci = instances->data;
+
+ if (result) {
+ instance_rid = e_cal_component_get_recurid_as_string (ci->comp);
+
+ if (rid && *rid) {
+ if (instance_rid && *instance_rid && strcmp (rid, instance_rid) == 0)
+ result = (* cb) (ci->comp, ci->start, ci->end, cb_data);
+ } else
+ result = (* cb) (ci->comp, ci->start, ci->end, cb_data);
+ }
+
+ /* remove instance from list */
+ instances = g_slist_remove (instances, ci);
+ g_object_unref (ci->comp);
+ g_free (ci);
+ g_free (instance_rid);
+ }
+
+ /* clean up */
+ g_free (rid);
+}
+
+static void
+generate_instances_for_object_got_objects_cb (struct get_objects_async_data *goad, GSList *objects)
+{
+ struct instances_info *instances_hold;
+ GSList *instances = NULL;
+
+ g_return_if_fail (goad != NULL);
+
+ instances_hold = g_new0 (struct instances_info, 1);
+ instances_hold->instances = &instances;
+ instances_hold->start_zone = goad->start_zone;
+
+ /* generate all instances in the given time range */
+ generate_instances (goad->client, goad->start, goad->end, objects, goad->cancellable, add_instance, instances_hold);
+
+ /* it also frees 'instances' GSList */
+ process_instances (goad->comp, *(instances_hold->instances), goad->cb, goad->cb_data);
+
+ /* clean up */
+ free_get_objects_async_data (goad);
+ g_free (instances_hold);
}
/**
@@ -1564,24 +1912,116 @@ e_cal_client_generate_instances (ECalClient *client, time_t start, time_t end, E
* @icalcomp: Object to generate instances from.
* @start: Start time for query.
* @end: End time for query.
+ * @cancellable: a #GCancellable; can be %NULL
* @cb: Callback for each generated instance.
* @cb_data: Closure data for the callback.
+ * @destroy_cb_data: Function to call when the processing is done, to free @cb_data; can be %NULL.
*
* Does a combination of #e_cal_client_get_object_list () and
* #e_cal_client_recur_generate_instances(), like #e_cal_client_generate_instances(), but
+ * for a single object. Unlike #e_cal_client_generate_instances_for_object_sync (),
+ * this returns immediately and the @cb callback is called asynchronously.
+ *
+ * The callback function should do a g_object_ref() of the calendar component
+ * it gets passed if it intends to keep it around, since it will be unref'ed
+ * as soon as the callback returns.
+ *
+ * Since: 3.2
+ **/
+void
+e_cal_client_generate_instances_for_object (ECalClient *client, icalcomponent *icalcomp, time_t start, time_t end, GCancellable *cancellable, ECalRecurInstanceFn cb, gpointer cb_data, GDestroyNotify destroy_cb_data)
+{
+ ECalComponent *comp;
+ const gchar *uid;
+ ECalComponentDateTime datetime;
+ icaltimezone *start_zone = NULL;
+ gboolean is_single_instance = FALSE;
+ struct get_objects_async_data *goad;
+ GCancellable *use_cancellable;
+
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (E_IS_CAL_CLIENT (client));
+ g_return_if_fail (e_client_is_opened (E_CLIENT (client)));
+
+ g_return_if_fail (start >= 0);
+ g_return_if_fail (end >= 0);
+ g_return_if_fail (cb != NULL);
+
+ comp = e_cal_component_new ();
+ e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
+
+ if (!e_cal_component_has_recurrences (comp))
+ is_single_instance = TRUE;
+
+ /* If the backend stores it as individual instances and does not
+ * have a master object - do not expand */
+ if (is_single_instance || e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_RECURRENCES_NO_MASTER)) {
+ /* return the same instance */
+ (* cb) (comp, icaltime_as_timet_with_zone (icalcomponent_get_dtstart (icalcomp), client->priv->default_zone),
+ icaltime_as_timet_with_zone (icalcomponent_get_dtend (icalcomp), client->priv->default_zone), cb_data);
+ g_object_unref (comp);
+
+ if (destroy_cb_data)
+ destroy_cb_data (cb_data);
+ return;
+ }
+
+ e_cal_component_get_uid (comp, &uid);
+
+ /* Get the start timezone */
+ e_cal_component_get_dtstart (comp, &datetime);
+ if (datetime.tzid)
+ e_cal_client_get_timezone_sync (client, datetime.tzid, &start_zone, NULL, NULL);
+ else
+ start_zone = NULL;
+ e_cal_component_free_datetime (&datetime);
+
+ use_cancellable = cancellable;
+ if (!use_cancellable)
+ use_cancellable = g_cancellable_new ();
+
+ goad = g_new0 (struct get_objects_async_data, 1);
+ goad->cancellable = g_object_ref (use_cancellable);
+ goad->client = g_object_ref (client);
+ goad->start = start;
+ goad->end = end;
+ goad->cb = cb;
+ goad->cb_data = cb_data;
+ goad->destroy_cb_data = destroy_cb_data;
+ goad->start_zone = start_zone;
+ goad->comp = comp;
+ goad->uid = g_strdup (uid);
+
+ get_objects_async (generate_instances_for_object_got_objects_cb, goad);
+
+ if (use_cancellable != cancellable)
+ g_object_unref (use_cancellable);
+}
+
+/**
+ * e_cal_client_generate_instances_for_object_sync:
+ * @client: A calendar client.
+ * @icalcomp: Object to generate instances from.
+ * @start: Start time for query.
+ * @end: End time for query.
+ * @cb: Callback for each generated instance.
+ * @cb_data: Closure data for the callback.
+ *
+ * Does a combination of #e_cal_client_get_object_list () and
+ * #e_cal_client_recur_generate_instances(), like #e_cal_client_generate_instances_sync(), but
* for a single object.
*
* The callback function should do a g_object_ref() of the calendar component
* it gets passed if it intends to keep it around, since it will be unref'ed
* as soon as the callback returns.
+ *
+ * Since: 3.2
**/
void
-e_cal_client_generate_instances_for_object (ECalClient *client, icalcomponent *icalcomp, time_t start, time_t end, ECalRecurInstanceFn cb, gpointer cb_data)
+e_cal_client_generate_instances_for_object_sync (ECalClient *client, icalcomponent *icalcomp, time_t start, time_t end, ECalRecurInstanceFn cb, gpointer cb_data)
{
ECalComponent *comp;
const gchar *uid;
- gchar *rid;
- gboolean result;
GSList *instances = NULL;
ECalComponentDateTime datetime;
icaltimezone *start_zone = NULL;
@@ -1606,14 +2046,13 @@ e_cal_client_generate_instances_for_object (ECalClient *client, icalcomponent *i
* have a master object - do not expand */
if (is_single_instance || e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_RECURRENCES_NO_MASTER)) {
/* return the same instance */
- result = (* cb) (comp, icaltime_as_timet_with_zone (icalcomponent_get_dtstart (icalcomp), client->priv->default_zone),
+ (* cb) (comp, icaltime_as_timet_with_zone (icalcomponent_get_dtstart (icalcomp), client->priv->default_zone),
icaltime_as_timet_with_zone (icalcomponent_get_dtend (icalcomp), client->priv->default_zone), cb_data);
g_object_unref (comp);
return;
}
e_cal_component_get_uid (comp, &uid);
- rid = e_cal_component_get_recurid_as_string (comp);
/* Get the start timezone */
e_cal_component_get_dtstart (comp, &datetime);
@@ -1628,38 +2067,14 @@ e_cal_client_generate_instances_for_object (ECalClient *client, icalcomponent *i
instances_hold->start_zone = start_zone;
/* generate all instances in the given time range */
- generate_instances (client, start, end, uid, add_instance, instances_hold);
+ generate_instances (client, start, end, get_objects_sync (client, start, end, uid), NULL, add_instance, instances_hold);
- instances = *(instances_hold->instances);
- /* now only return back the instances for the given object */
- result = TRUE;
- while (instances != NULL) {
- struct comp_instance *ci;
- gchar *instance_rid = NULL;
-
- ci = instances->data;
-
- if (result) {
- instance_rid = e_cal_component_get_recurid_as_string (ci->comp);
-
- if (rid && *rid) {
- if (instance_rid && *instance_rid && strcmp (rid, instance_rid) == 0)
- result = (* cb) (ci->comp, ci->start, ci->end, cb_data);
- } else
- result = (* cb) (ci->comp, ci->start, ci->end, cb_data);
- }
-
- /* remove instance from list */
- instances = g_slist_remove (instances, ci);
- g_object_unref (ci->comp);
- g_free (ci);
- g_free (instance_rid);
- }
+ /* it also frees 'instances' GSList */
+ process_instances (comp, *(instances_hold->instances), cb, cb_data);
/* clean up */
g_object_unref (comp);
g_free (instances_hold);
- g_free (rid);
}
typedef struct _ForeachTZIDCallbackData ForeachTZIDCallbackData;
@@ -1789,24 +2204,76 @@ e_cal_client_get_component_as_string (ECalClient *client, icalcomponent *icalcom
return obj_string;
}
+static gboolean
+cal_client_get_backend_property_from_cache_finish (EClient *client, GAsyncResult *result, gchar **prop_value, GError **error)
+{
+ GSimpleAsyncResult *simple;
+ GError *local_error = NULL;
+
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
+ g_return_val_if_fail (result != NULL, FALSE);
+ g_return_val_if_fail (prop_value != NULL, FALSE);
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (client), cal_client_get_backend_property_from_cache_finish), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, &local_error)) {
+ e_client_unwrap_dbus_error (client, local_error, error);
+ return FALSE;
+ }
+
+ *prop_value = g_strdup (g_simple_async_result_get_op_res_gpointer (simple));
+
+ return *prop_value != NULL;
+}
+
static void
cal_client_get_backend_property (EClient *client, const gchar *prop_name, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
- e_client_proxy_call_string (client, prop_name, cancellable, callback, user_data, cal_client_get_backend_property,
+ gchar *prop_value;
+
+ prop_value = e_client_get_backend_property_from_cache (client, prop_name);
+ if (prop_value) {
+ e_client_finish_async_without_dbus (client, cancellable, callback, user_data, cal_client_get_backend_property_from_cache_finish, prop_value, g_free);
+ } else {
+ e_client_proxy_call_string_with_res_op_data (client, prop_name, cancellable, callback, user_data, cal_client_get_backend_property, prop_name,
e_gdbus_cal_call_get_backend_property,
NULL, NULL, e_gdbus_cal_call_get_backend_property_finish, NULL, NULL);
+ }
}
static gboolean
cal_client_get_backend_property_finish (EClient *client, GAsyncResult *result, gchar **prop_value, GError **error)
{
- return e_client_proxy_call_finish_string (client, result, prop_value, error, cal_client_get_backend_property);
+ gchar *str = NULL;
+ gboolean res;
+
+ g_return_val_if_fail (prop_value != NULL, FALSE);
+
+ if (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (result)) == cal_client_get_backend_property_from_cache_finish) {
+ res = cal_client_get_backend_property_from_cache_finish (client, result, &str, error);
+ } else {
+ res = e_client_proxy_call_finish_string (client, result, &str, error, cal_client_get_backend_property);
+ if (res && str) {
+ const gchar *prop_name = g_object_get_data (G_OBJECT (result), "res-op-data");
+
+ if (prop_name && *prop_name)
+ e_client_update_backend_property_cache (client, prop_name, str);
+ }
+ }
+
+ *prop_value = str;
+
+ return res;
}
static gboolean
cal_client_get_backend_property_sync (EClient *client, const gchar *prop_name, gchar **prop_value, GCancellable *cancellable, GError **error)
{
ECalClient *cal_client;
+ gchar *prop_val;
+ gboolean res;
g_return_val_if_fail (client != NULL, FALSE);
g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
@@ -1820,7 +2287,21 @@ cal_client_get_backend_property_sync (EClient *client, const gchar *prop_name, g
return FALSE;
}
- return e_client_proxy_call_sync_string__string (client, prop_name, prop_value, cancellable, error, e_gdbus_cal_call_get_backend_property_sync);
+ prop_val = e_client_get_backend_property_from_cache (client, prop_name);
+ if (prop_val) {
+ g_return_val_if_fail (prop_value != NULL, FALSE);
+
+ *prop_value = prop_val;
+
+ return TRUE;
+ }
+
+ res = e_client_proxy_call_sync_string__string (client, prop_name, prop_value, cancellable, error, e_gdbus_cal_call_get_backend_property_sync);
+
+ if (res && prop_value)
+ e_client_update_backend_property_cache (client, prop_name, *prop_value);
+
+ return res;
}
static void
@@ -3810,24 +4291,7 @@ e_cal_client_get_timezone (ECalClient *client, const gchar *tzid, GCancellable *
zone = cal_client_get_timezone_from_cache (client, tzid);
if (zone) {
- GCancellable *use_cancellable = cancellable;
- guint32 opid;
-
- if (!use_cancellable)
- use_cancellable = g_cancellable_new ();
-
- opid = e_client_register_op (E_CLIENT (client), use_cancellable);
- if (opid) {
- GSimpleAsyncResult *simple;
-
- simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, cal_client_get_timezone_from_cache);
- g_simple_async_result_set_op_res_gpointer (simple, zone, NULL);
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
- }
-
- if (use_cancellable != cancellable)
- g_object_unref (use_cancellable);
+ e_client_finish_async_without_dbus (E_CLIENT (client), cancellable, callback, user_data, cal_client_get_timezone_from_cache, zone, NULL);
} else {
e_client_proxy_call_string (E_CLIENT (client), e_util_ensure_gdbus_string (tzid, &gdbus_tzid), cancellable, callback, user_data, e_cal_client_get_timezone,
e_gdbus_cal_call_get_timezone,
diff --git a/calendar/libecal/e-cal-client.h b/calendar/libecal/e-cal-client.h
index 5010b5e..e04491a 100644
--- a/calendar/libecal/e-cal-client.h
+++ b/calendar/libecal/e-cal-client.h
@@ -116,8 +116,10 @@ void e_cal_client_free_icalcomp_slist (GSList *icalcomps);
void e_cal_client_free_ecalcomp_slist (GSList *ecalcomps);
icaltimezone * e_cal_client_resolve_tzid_cb (const gchar *tzid, gpointer data);
-void e_cal_client_generate_instances (ECalClient *client, time_t start, time_t end, ECalRecurInstanceFn cb, gpointer cb_data);
-void e_cal_client_generate_instances_for_object (ECalClient *client, icalcomponent *icalcomp, time_t start, time_t end, ECalRecurInstanceFn cb, gpointer cb_data);
+void e_cal_client_generate_instances (ECalClient *client, time_t start, time_t end, GCancellable *cancellable, ECalRecurInstanceFn cb, gpointer cb_data, GDestroyNotify destroy_cb_data);
+void e_cal_client_generate_instances_sync (ECalClient *client, time_t start, time_t end, ECalRecurInstanceFn cb, gpointer cb_data);
+void e_cal_client_generate_instances_for_object (ECalClient *client, icalcomponent *icalcomp, time_t start, time_t end, GCancellable *cancellable, ECalRecurInstanceFn cb, gpointer cb_data, GDestroyNotify destroy_cb_data);
+void e_cal_client_generate_instances_for_object_sync (ECalClient *client, icalcomponent *icalcomp, time_t start, time_t end, ECalRecurInstanceFn cb, gpointer cb_data);
gchar * e_cal_client_get_component_as_string (ECalClient *client, icalcomponent *icalcomp);
/* Calendar methods */
diff --git a/calendar/libedata-cal/e-cal-backend.c b/calendar/libedata-cal/e-cal-backend.c
index 277a6f1..d79e4e2 100644
--- a/calendar/libedata-cal/e-cal-backend.c
+++ b/calendar/libedata-cal/e-cal-backend.c
@@ -1784,6 +1784,36 @@ e_cal_backend_notify_opened (ECalBackend *backend, GError *error)
}
/**
+ * e_cal_backend_notify_property_changed:
+ * @backend: an #ECalBackend
+ * @prop_name: property name, which changed
+ * @prop_value: new property value
+ *
+ * Notifies client about property value change.
+ **/
+void
+e_cal_backend_notify_property_changed (ECalBackend *backend, const gchar *prop_name, const gchar *prop_value)
+{
+ ECalBackendPrivate *priv;
+ GSList *clients;
+
+ g_return_if_fail (backend != NULL);
+ g_return_if_fail (E_IS_CAL_BACKEND (backend));
+ g_return_if_fail (backend->priv != NULL);
+ g_return_if_fail (prop_name != NULL);
+ g_return_if_fail (*prop_name != '\0');
+ g_return_if_fail (prop_value != NULL);
+
+ priv = backend->priv;
+ g_mutex_lock (priv->clients_mutex);
+
+ for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
+ e_data_cal_report_backend_property_changed (E_DATA_CAL (clients->data), prop_name, prop_value);
+
+ g_mutex_unlock (priv->clients_mutex);
+}
+
+/**
* e_cal_backend_respond_opened:
* @backend: an #ECalBackend
* @cal: an #EDataCal
diff --git a/calendar/libedata-cal/e-cal-backend.h b/calendar/libedata-cal/e-cal-backend.h
index 360c4e4..840b092 100644
--- a/calendar/libedata-cal/e-cal-backend.h
+++ b/calendar/libedata-cal/e-cal-backend.h
@@ -159,6 +159,7 @@ void e_cal_backend_notify_readonly (ECalBackend *backend, gboolean is_readonly
void e_cal_backend_notify_online (ECalBackend *backend, gboolean is_online);
void e_cal_backend_notify_auth_required (ECalBackend *backend, gboolean is_self, const ECredentials *credentials);
void e_cal_backend_notify_opened (ECalBackend *backend, GError *error);
+void e_cal_backend_notify_property_changed (ECalBackend *backend, const gchar *prop_name, const gchar *prop_value);
void e_cal_backend_empty_cache (ECalBackend *backend, struct _ECalBackendCache *cache);
diff --git a/calendar/libedata-cal/e-data-cal.c b/calendar/libedata-cal/e-data-cal.c
index c895fc5..6eeaa96 100644
--- a/calendar/libedata-cal/e-data-cal.c
+++ b/calendar/libedata-cal/e-data-cal.c
@@ -1409,6 +1409,25 @@ e_data_cal_report_free_busy_data (EDataCal *cal, const GSList *freebusy)
g_strfreev (strv_freebusy);
}
+/* Notifies client about certain property value change */
+void
+e_data_cal_report_backend_property_changed (EDataCal *cal, const gchar *prop_name, const gchar *prop_value)
+{
+ gchar **strv;
+
+ g_return_if_fail (cal != NULL);
+ g_return_if_fail (prop_name != NULL);
+ g_return_if_fail (*prop_name != '\0');
+ g_return_if_fail (prop_value != NULL);
+
+ strv = e_gdbus_templates_encode_two_strings (prop_name, prop_value);
+ g_return_if_fail (strv != NULL);
+
+ e_gdbus_cal_emit_backend_property_changed (cal->priv->gdbus_object, (const gchar * const *) strv);
+
+ g_strfreev (strv);
+}
+
/* Instance init */
static void
e_data_cal_init (EDataCal *ecal)
diff --git a/calendar/libedata-cal/e-data-cal.h b/calendar/libedata-cal/e-data-cal.h
index c202f11..cb97e01 100644
--- a/calendar/libedata-cal/e-data-cal.h
+++ b/calendar/libedata-cal/e-data-cal.h
@@ -154,6 +154,7 @@ void e_data_cal_report_online (EDataCal *cal, gboolean is_online);
void e_data_cal_report_auth_required (EDataCal *cal, const ECredentials *credentials);
void e_data_cal_report_opened (EDataCal *cal, const GError *error);
void e_data_cal_report_free_busy_data (EDataCal *cal, const GSList *freebusy);
+void e_data_cal_report_backend_property_changed (EDataCal *cal, const gchar *prop_name, const gchar *prop_value);
G_END_DECLS
diff --git a/calendar/libegdbus/e-gdbus-cal.c b/calendar/libegdbus/e-gdbus-cal.c
index b213cd2..9e747c3 100644
--- a/calendar/libegdbus/e-gdbus-cal.c
+++ b/calendar/libegdbus/e-gdbus-cal.c
@@ -42,6 +42,7 @@ enum
__AUTH_REQUIRED_SIGNAL,
__OPENED_SIGNAL,
__FREE_BUSY_DATA_SIGNAL,
+ __BACKEND_PROPERTY_CHANGED_SIGNAL,
__OPEN_METHOD,
__OPEN_DONE_SIGNAL,
__REMOVE_METHOD,
@@ -133,6 +134,7 @@ E_DECLARE_GDBUS_SIGNAL_EMISSION_HOOK_BOOLEAN (GDBUS_CAL_INTERFACE_NAME, online)
E_DECLARE_GDBUS_SIGNAL_EMISSION_HOOK_STRV (GDBUS_CAL_INTERFACE_NAME, auth_required)
E_DECLARE_GDBUS_SIGNAL_EMISSION_HOOK_STRV (GDBUS_CAL_INTERFACE_NAME, opened)
E_DECLARE_GDBUS_SIGNAL_EMISSION_HOOK_STRV (GDBUS_CAL_INTERFACE_NAME, free_busy_data)
+E_DECLARE_GDBUS_SIGNAL_EMISSION_HOOK_STRV (GDBUS_CAL_INTERFACE_NAME, backend_property_changed)
E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_CAL_INTERFACE_NAME, open)
E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_CAL_INTERFACE_NAME, remove)
@@ -169,6 +171,7 @@ e_gdbus_cal_default_init (EGdbusCalIface *iface)
E_INIT_GDBUS_SIGNAL_STRV (EGdbusCalIface, "auth_required", auth_required, __AUTH_REQUIRED_SIGNAL)
E_INIT_GDBUS_SIGNAL_STRV (EGdbusCalIface, "opened", opened, __OPENED_SIGNAL)
E_INIT_GDBUS_SIGNAL_STRV (EGdbusCalIface, "free_busy_data", free_busy_data, __FREE_BUSY_DATA_SIGNAL)
+ E_INIT_GDBUS_SIGNAL_STRV (EGdbusCalIface, "backend_property_changed", backend_property_changed, __BACKEND_PROPERTY_CHANGED_SIGNAL)
/* GObject signals definitions for D-Bus methods: */
E_INIT_GDBUS_METHOD_ASYNC_BOOLEAN__VOID (EGdbusCalIface, "open", open, __OPEN_METHOD, __OPEN_DONE_SIGNAL)
@@ -195,35 +198,6 @@ e_gdbus_cal_default_init (EGdbusCalIface *iface)
E_INIT_GDBUS_METHOD_VOID (EGdbusCalIface, "close", close, __CLOSE_METHOD)
}
-static gchar **
-encode_string_string (const gchar *str1, const gchar *str2)
-{
- gchar **strv;
-
- strv = g_new0 (gchar *, 3);
- strv[0] = e_util_utf8_make_valid (str1 ? str1 : "");
- strv[1] = e_util_utf8_make_valid (str2 ? str2 : "");
- strv[2] = NULL;
-
- return strv;
-}
-
-static gboolean
-decode_string_string (const gchar * const *in_strv, gchar **out_str1, gchar **out_str2)
-{
- g_return_val_if_fail (in_strv != NULL, FALSE);
- g_return_val_if_fail (in_strv[0] != NULL, FALSE);
- g_return_val_if_fail (in_strv[1] != NULL, FALSE);
- g_return_val_if_fail (in_strv[2] == NULL, FALSE);
- g_return_val_if_fail (out_str1 != NULL, FALSE);
- g_return_val_if_fail (out_str2 != NULL, FALSE);
-
- *out_str1 = g_strdup (in_strv[0]);
- *out_str2 = g_strdup (in_strv[1]);
-
- return TRUE;
-}
-
void
e_gdbus_cal_call_open (GDBusProxy *proxy, gboolean in_only_if_exists, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
@@ -308,14 +282,14 @@ e_gdbus_cal_call_get_backend_property_sync (GDBusProxy *proxy, const gchar *in_p
gchar **
e_gdbus_cal_encode_set_backend_property (const gchar *in_prop_name, const gchar *in_prop_value)
{
- return encode_string_string (in_prop_name, in_prop_value);
+ return e_gdbus_templates_encode_two_strings (in_prop_name, in_prop_value);
}
/* free out_prop_name and out_prop_value with g_free() */
gboolean
e_gdbus_cal_decode_set_backend_property (const gchar * const *in_strv, gchar **out_prop_name, gchar **out_prop_value)
{
- return decode_string_string (in_strv, out_prop_name, out_prop_value);
+ return e_gdbus_templates_decode_two_strings (in_strv, out_prop_name, out_prop_value);
}
void
@@ -342,14 +316,14 @@ e_gdbus_cal_call_set_backend_property_sync (GDBusProxy *proxy, const gchar * con
gchar **
e_gdbus_cal_encode_get_object (const gchar *in_uid, const gchar *in_rid)
{
- return encode_string_string (in_uid, in_rid);
+ return e_gdbus_templates_encode_two_strings (in_uid, in_rid);
}
/* free out_uid and out_rid with g_free() */
gboolean
e_gdbus_cal_decode_get_object (const gchar * const *in_strv, gchar **out_uid, gchar **out_rid)
{
- return decode_string_string (in_strv, out_uid, out_rid);
+ return e_gdbus_templates_decode_two_strings (in_strv, out_uid, out_rid);
}
void
@@ -676,14 +650,14 @@ e_gdbus_cal_call_send_objects_sync (GDBusProxy *proxy, const gchar *in_calobj, g
gchar **
e_gdbus_cal_encode_get_attachment_uris (const gchar *in_uid, const gchar *in_rid)
{
- return encode_string_string (in_uid, in_rid);
+ return e_gdbus_templates_encode_two_strings (in_uid, in_rid);
}
/* free out_uid and out_rid with g_free() */
gboolean
e_gdbus_cal_decode_get_attachment_uris (const gchar * const *in_strv, gchar **out_uid, gchar **out_rid)
{
- return decode_string_string (in_strv, out_uid, out_rid);
+ return e_gdbus_templates_decode_two_strings (in_strv, out_uid, out_rid);
}
void
@@ -965,12 +939,19 @@ e_gdbus_cal_emit_free_busy_data (EGdbusCal *object, const gchar * const *arg_fre
g_signal_emit (object, signals[__FREE_BUSY_DATA_SIGNAL], 0, arg_free_busy);
}
+void
+e_gdbus_cal_emit_backend_property_changed (EGdbusCal *object, const gchar * const *arg_name_value)
+{
+ g_signal_emit (object, signals[__BACKEND_PROPERTY_CHANGED_SIGNAL], 0, arg_name_value);
+}
+
E_DECLARE_GDBUS_NOTIFY_SIGNAL_1 (cal, backend_error, message, "s")
E_DECLARE_GDBUS_NOTIFY_SIGNAL_1 (cal, readonly, is_readonly, "b")
E_DECLARE_GDBUS_NOTIFY_SIGNAL_1 (cal, online, is_online, "b")
E_DECLARE_GDBUS_NOTIFY_SIGNAL_1 (cal, auth_required, credentials, "as")
E_DECLARE_GDBUS_NOTIFY_SIGNAL_1 (cal, opened, error, "as")
E_DECLARE_GDBUS_NOTIFY_SIGNAL_1 (cal, free_busy_data, free_busy_data, "as")
+E_DECLARE_GDBUS_NOTIFY_SIGNAL_1 (cal, backend_property_changed, name_value, "as")
E_DECLARE_GDBUS_ASYNC_METHOD_1 (cal, open, only_if_exists, "b")
E_DECLARE_GDBUS_ASYNC_METHOD_0 (cal, remove)
@@ -1031,6 +1012,7 @@ static const GDBusSignalInfo * const e_gdbus_cal_signal_info_pointers[] =
&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, auth_required),
&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, opened),
&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, free_busy_data),
+ &E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, backend_property_changed),
&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, open_done),
&E_DECLARED_GDBUS_SIGNAL_INFO_NAME (cal, remove_done),
diff --git a/calendar/libegdbus/e-gdbus-cal.h b/calendar/libegdbus/e-gdbus-cal.h
index 21e2c7d..695d13b 100644
--- a/calendar/libegdbus/e-gdbus-cal.h
+++ b/calendar/libegdbus/e-gdbus-cal.h
@@ -109,6 +109,7 @@ struct _EGdbusCalIface
void (*auth_required) (EGdbusCal *object, const gchar * const *arg_credentials);
void (*opened) (EGdbusCal *object, const gchar * const *arg_error);
void (*free_busy_data) (EGdbusCal *object, const gchar * const *arg_free_busy);
+ void (*backend_property_changed) (EGdbusCal *object, const gchar * const *arg_name_value);
/* Signal handlers for handling D-Bus method calls: */
gboolean (*handle_open) (EGdbusCal *object, GDBusMethodInvocation *invocation, gboolean in_only_if_exists);
@@ -327,6 +328,7 @@ void e_gdbus_cal_emit_online (EGdbusCal *object, gint arg_is_online);
void e_gdbus_cal_emit_auth_required (EGdbusCal *object, const gchar * const *arg_credentials);
void e_gdbus_cal_emit_opened (EGdbusCal *object, const gchar * const *arg_error);
void e_gdbus_cal_emit_free_busy_data (EGdbusCal *object, const gchar * const *arg_free_busy);
+void e_gdbus_cal_emit_backend_property_changed (EGdbusCal *object, const gchar * const *arg_name_value);
G_END_DECLS
diff --git a/configure.ac b/configure.ac
index 8952ba5..0773f6f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -77,7 +77,7 @@ AC_SUBST(CALENDAR_DBUS_SERVICE_NAME)
dnl ******************************
dnl Libtool versioning
dnl ******************************
-LIBEDATASERVER_CURRENT=14
+LIBEDATASERVER_CURRENT=15
LIBEDATASERVER_REVISION=0
LIBEDATASERVER_AGE=0
@@ -85,19 +85,19 @@ LIBEDATASERVERUI_CURRENT=1
LIBEDATASERVERUI_REVISION=0
LIBEDATASERVERUI_AGE=0
-LIBECAL_CURRENT=11
+LIBECAL_CURRENT=12
LIBECAL_REVISION=2
LIBECAL_AGE=2
-LIBEDATACAL_CURRENT=12
+LIBEDATACAL_CURRENT=13
LIBEDATACAL_REVISION=0
LIBEDATACAL_AGE=0
-LIBEDATABOOK_CURRENT=10
+LIBEDATABOOK_CURRENT=11
LIBEDATABOOK_REVISION=0
LIBEDATABOOK_AGE=0
-LIBEBOOK_CURRENT=14
+LIBEBOOK_CURRENT=15
LIBEBOOK_REVISION=1
LIBEBOOK_AGE=3
diff --git a/libedataserver/e-client-private.h b/libedataserver/e-client-private.h
index 59b451d..23736d1 100644
--- a/libedataserver/e-client-private.h
+++ b/libedataserver/e-client-private.h
@@ -42,12 +42,17 @@ gboolean e_client_emit_authenticate (EClient *client, ECredentials *credentials)
void e_client_emit_opened (EClient *client, const GError *error);
void e_client_emit_backend_error (EClient *client, const gchar *error_msg);
void e_client_emit_backend_died (EClient *client);
+void e_client_emit_backend_property_changed (EClient *client, const gchar *prop_name, const gchar *prop_value);
+
+void e_client_update_backend_property_cache (EClient *client, const gchar *prop_name, const gchar *prop_value);
+gchar * e_client_get_backend_property_from_cache (EClient *client, const gchar *prop_name);
ESource * e_client_util_get_system_source (ESourceList *source_list);
gboolean e_client_util_set_default (ESourceList *source_list, ESource *source);
ESource * e_client_util_get_source_for_uri (ESourceList *source_list, const gchar *uri);
/* protected functions simplifying sync/async calls */
+void e_client_finish_async_without_dbus (EClient *client, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, gpointer soure_tag, gpointer op_res, GDestroyNotify destroy_op_res);
GDBusProxy * e_client_get_dbus_proxy (EClient *client);
void e_client_proxy_return_async_error (EClient *client, const GError *error, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag);
@@ -83,6 +88,8 @@ void e_client_proxy_call_string (EClient *client, const gchar *in_string, GCanc
void e_client_proxy_call_strv (EClient *client, const gchar * const *in_strv, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, void (*func) (GDBusProxy *proxy, const gchar * const * in_strv, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data), EClientProxyFinishVoidFunc finish_void, EClientProxyFinishBooleanFunc finish_boolean, EClientProxyFinishStringFunc finish_string, EClientProxyFinishStrvFunc finish_strv, EClientProxyFinishUintFunc finish_uint);
void e_client_proxy_call_uint (EClient *client, guint in_uint, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, void (*func) (GDBusProxy *proxy, guint in_uint, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data), EClientProxyFinishVoidFunc finish_void, EClientProxyFinishBooleanFunc finish_boolean, EClientProxyFinishStringFunc finish_string, EClientProxyFinishStrvFunc finish_strv, EClientProxyFinishUintFunc finish_uint);
+void e_client_proxy_call_string_with_res_op_data (EClient *client, const gchar *in_string, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, const gchar *res_op_data, void (*func) (GDBusProxy *proxy, const gchar * in_string, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data), EClientProxyFinishVoidFunc finish_void, EClientProxyFinishBooleanFunc finish_boolean, EClientProxyFinishStringFunc finish_string, EClientProxyFinishStrvFunc finish_strv, EClientProxyFinishUintFunc finish_uint);
+
gboolean e_client_proxy_call_finish_void (EClient *client, GAsyncResult *result, GError **error, gpointer source_tag);
gboolean e_client_proxy_call_finish_boolean (EClient *client, GAsyncResult *result, gboolean *out_boolean, GError **error, gpointer source_tag);
gboolean e_client_proxy_call_finish_string (EClient *client, GAsyncResult *result, gchar **out_string, GError **error, gpointer source_tag);
diff --git a/libedataserver/e-client.c b/libedataserver/e-client.c
index 7a5ec29..f7fe39b 100644
--- a/libedataserver/e-client.c
+++ b/libedataserver/e-client.c
@@ -43,6 +43,8 @@ struct _EClientPrivate
gboolean capabilities_retrieved;
GSList *capabilities;
+ GHashTable *backend_property_cache;
+
GStaticRecMutex ops_mutex;
guint32 last_opid;
GHashTable *ops; /* opid to GCancellable */
@@ -62,6 +64,7 @@ enum {
OPENED,
BACKEND_ERROR,
BACKEND_DIED,
+ BACKEND_PROPERTY_CHANGED,
LAST_SIGNAL
};
@@ -178,6 +181,7 @@ e_client_init (EClient *client)
client->priv->readonly = TRUE;
g_static_rec_mutex_init (&client->priv->prop_mutex);
+ client->priv->backend_property_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
g_static_rec_mutex_init (&client->priv->ops_mutex);
client->priv->last_opid = 0;
@@ -229,6 +233,11 @@ client_finalize (GObject *object)
priv->capabilities = NULL;
}
+ if (priv->backend_property_cache) {
+ g_hash_table_destroy (priv->backend_property_cache);
+ priv->backend_property_cache = NULL;
+ }
+
if (priv->ops) {
g_hash_table_destroy (priv->ops);
priv->ops = NULL;
@@ -386,6 +395,15 @@ e_client_class_init (EClientClass *klass)
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
+
+ signals[BACKEND_PROPERTY_CHANGED] = g_signal_new (
+ "backend-property-changed",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EClientClass, backend_property_changed),
+ NULL, NULL,
+ e_gdbus_marshallers_VOID__STRING_STRING,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
}
static void
@@ -925,6 +943,60 @@ e_client_emit_backend_died (EClient *client)
g_signal_emit (client, signals[BACKEND_DIED], 0);
}
+void
+e_client_emit_backend_property_changed (EClient *client, const gchar *prop_name, const gchar *prop_value)
+{
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (E_IS_CLIENT (client));
+ g_return_if_fail (client->priv != NULL);
+ g_return_if_fail (prop_name != NULL);
+ g_return_if_fail (*prop_name);
+ g_return_if_fail (prop_value != NULL);
+
+ e_client_update_backend_property_cache (client, prop_name, prop_value);
+
+ g_signal_emit (client, signals[BACKEND_PROPERTY_CHANGED], 0, prop_name, prop_value);
+}
+
+void
+e_client_update_backend_property_cache (EClient *client, const gchar *prop_name, const gchar *prop_value)
+{
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (E_IS_CLIENT (client));
+ g_return_if_fail (client->priv != NULL);
+ g_return_if_fail (prop_name != NULL);
+ g_return_if_fail (*prop_name);
+ g_return_if_fail (prop_value != NULL);
+
+ g_static_rec_mutex_lock (&client->priv->prop_mutex);
+
+ if (client->priv->backend_property_cache)
+ g_hash_table_insert (client->priv->backend_property_cache, g_strdup (prop_name), g_strdup (prop_value));
+
+ g_static_rec_mutex_unlock (&client->priv->prop_mutex);
+}
+
+gchar *
+e_client_get_backend_property_from_cache (EClient *client, const gchar *prop_name)
+{
+ gchar *prop_value = NULL;
+
+ g_return_val_if_fail (client != NULL, NULL);
+ g_return_val_if_fail (E_IS_CLIENT (client), NULL);
+ g_return_val_if_fail (client->priv != NULL, NULL);
+ g_return_val_if_fail (prop_name != NULL, NULL);
+ g_return_val_if_fail (*prop_name, NULL);
+
+ g_static_rec_mutex_lock (&client->priv->prop_mutex);
+
+ if (client->priv->backend_property_cache)
+ prop_value = g_strdup (g_hash_table_lookup (client->priv->backend_property_cache, prop_name));
+
+ g_static_rec_mutex_unlock (&client->priv->prop_mutex);
+
+ return prop_value;
+}
+
/**
* e_client_retrieve_capabilities:
* @client: an #EClient
@@ -1842,6 +1914,34 @@ e_client_util_get_source_for_uri (ESourceList *source_list, const gchar *uri)
return source;
}
+void
+e_client_finish_async_without_dbus (EClient *client, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, gpointer op_res, GDestroyNotify destroy_op_res)
+{
+ GCancellable *use_cancellable;
+ GSimpleAsyncResult *simple;
+ guint32 opid;
+
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (E_IS_CLIENT (client));
+ g_return_if_fail (callback != NULL);
+ g_return_if_fail (source_tag != NULL);
+
+ use_cancellable = cancellable;
+ if (!use_cancellable)
+ use_cancellable = g_cancellable_new ();
+
+ opid = e_client_register_op (client, use_cancellable);
+ g_return_if_fail (opid > 0);
+
+ simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data, source_tag);
+ g_simple_async_result_set_op_res_gpointer (simple, op_res, destroy_op_res);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+
+ if (use_cancellable != cancellable)
+ g_object_unref (use_cancellable);
+}
+
GDBusProxy *
e_client_get_dbus_proxy (EClient *client)
{
@@ -1971,6 +2071,7 @@ typedef struct _EClientAsyncOpData
guint32 opid;
gpointer source_tag;
+ gchar *res_op_data; /* optional string to set on a GAsyncResult object as "res-op-data" user data */
GAsyncReadyCallback callback;
gpointer user_data;
@@ -2005,6 +2106,7 @@ async_data_free (EClientAsyncOpData *async_data)
g_strfreev (async_data->out.val_strv);
g_object_unref (async_data->client);
+ g_free (async_data->res_op_data);
g_free (async_data);
}
@@ -2043,6 +2145,9 @@ finish_async_op (EClientAsyncOpData *async_data, const GError *error, gboolean i
simple = g_simple_async_result_new (G_OBJECT (async_data->client), async_data->callback, async_data->user_data, async_data->source_tag);
g_simple_async_result_set_op_res_gpointer (simple, async_data, (GDestroyNotify) async_data_free);
+ if (async_data->res_op_data)
+ g_object_set_data_full (G_OBJECT (simple), "res-op-data", g_strdup (async_data->res_op_data), g_free);
+
if (error != NULL)
g_simple_async_result_set_from_error (simple, error);
@@ -2246,6 +2351,27 @@ e_client_proxy_call_string (EClient *client, const gchar *in_string, GCancellabl
}
void
+e_client_proxy_call_string_with_res_op_data (EClient *client, const gchar *in_string, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, const gchar *res_op_data, void (*func) (GDBusProxy *proxy, const gchar * in_string, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data), EClientProxyFinishVoidFunc finish_void, EClientProxyFinishBooleanFunc finish_boolean, EClientProxyFinishStringFunc finish_string, EClientProxyFinishStrvFunc finish_strv, EClientProxyFinishUintFunc finish_uint)
+{
+ EClientAsyncOpData *async_data;
+ GDBusProxy *proxy = NULL;
+
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (E_IS_CLIENT (client));
+ g_return_if_fail (callback != NULL);
+ g_return_if_fail (source_tag != NULL);
+ e_client_return_async_if_fail (func != NULL, client, callback, user_data, source_tag);
+ e_client_return_async_if_fail (in_string != NULL, client, callback, user_data, source_tag);
+
+ async_data = prepare_async_data (client, cancellable, callback, user_data, source_tag, FALSE, finish_void, finish_boolean, finish_string, finish_strv, finish_uint, &proxy, &cancellable);
+ e_client_return_async_if_fail (async_data != NULL, client, callback, user_data, source_tag);
+
+ async_data->res_op_data = g_strdup (res_op_data);
+
+ func (proxy, in_string, cancellable, async_result_ready_cb, async_data);
+}
+
+void
e_client_proxy_call_strv (EClient *client, const gchar * const *in_strv, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, void (*func) (GDBusProxy *proxy, const gchar * const * in_strv, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data), EClientProxyFinishVoidFunc finish_void, EClientProxyFinishBooleanFunc finish_boolean, EClientProxyFinishStringFunc finish_string, EClientProxyFinishStrvFunc finish_strv, EClientProxyFinishUintFunc finish_uint)
{
EClientAsyncOpData *async_data;
diff --git a/libedataserver/e-client.h b/libedataserver/e-client.h
index de78aa3..d17599e 100644
--- a/libedataserver/e-client.h
+++ b/libedataserver/e-client.h
@@ -122,6 +122,7 @@ struct _EClientClass {
void (* opened) (EClient *client, const GError *error);
void (* backend_error) (EClient *client, const gchar *error_msg);
void (* backend_died) (EClient *client);
+ void (* backend_property_changed) (EClient *client, const gchar *prop_name, const gchar *prop_value);
};
GType e_client_get_type (void);
diff --git a/libedataserver/e-gdbus-marshallers.list b/libedataserver/e-gdbus-marshallers.list
index 9bf81c4..cce982a 100644
--- a/libedataserver/e-gdbus-marshallers.list
+++ b/libedataserver/e-gdbus-marshallers.list
@@ -8,6 +8,7 @@ VOID:UINT,BOXED
VOID:UINT,BOXED,STRING
VOID:UINT,BOXED,BOXED
VOID:UINT,STRING
+VOID:STRING,STRING
VOID:BOXED
VOID:STRING
diff --git a/libedataserver/e-gdbus-templates.c b/libedataserver/e-gdbus-templates.c
index 1494222..b5c6ec2 100644
--- a/libedataserver/e-gdbus-templates.c
+++ b/libedataserver/e-gdbus-templates.c
@@ -918,7 +918,8 @@ gdbus_proxy_async_method_done (guint e_gdbus_type, gconstpointer out_value, EGdb
op_data = g_hash_table_lookup (pending_ops, GUINT_TO_POINTER (arg_opid));
if (!op_data) {
- g_debug ("%s: Operation %d gone before got done signal for it", G_STRFUNC, arg_opid);
+ /* it happens for cancelled operations, thus rather than track cancelled ops disable the debug warning */
+ /* g_debug ("%s: Operation %d gone before got done signal for it", G_STRFUNC, arg_opid); */
return;
}
@@ -1728,3 +1729,34 @@ e_gdbus_templates_decode_error (const gchar * const *in_strv, GError **out_error
return TRUE;
}
+
+/* free returned pointer with g_strfreev() */
+gchar **
+e_gdbus_templates_encode_two_strings (const gchar *in_str1, const gchar *in_str2)
+{
+ gchar **strv;
+
+ strv = g_new0 (gchar *, 3);
+ strv[0] = e_util_utf8_make_valid (in_str1 ? in_str1 : "");
+ strv[1] = e_util_utf8_make_valid (in_str2 ? in_str2 : "");
+ strv[2] = NULL;
+
+ return strv;
+}
+
+/* free *out_str1 and *out_str2 with g_free() */
+gboolean
+e_gdbus_templates_decode_two_strings (const gchar * const *in_strv, gchar **out_str1, gchar **out_str2)
+{
+ g_return_val_if_fail (in_strv != NULL, FALSE);
+ g_return_val_if_fail (in_strv[0] != NULL, FALSE);
+ g_return_val_if_fail (in_strv[1] != NULL, FALSE);
+ g_return_val_if_fail (in_strv[2] == NULL, FALSE);
+ g_return_val_if_fail (out_str1 != NULL, FALSE);
+ g_return_val_if_fail (out_str2 != NULL, FALSE);
+
+ *out_str1 = g_strdup (in_strv[0]);
+ *out_str2 = g_strdup (in_strv[1]);
+
+ return TRUE;
+}
diff --git a/libedataserver/e-gdbus-templates.h b/libedataserver/e-gdbus-templates.h
index b6e68ef..c9bc205 100644
--- a/libedataserver/e-gdbus-templates.h
+++ b/libedataserver/e-gdbus-templates.h
@@ -745,6 +745,9 @@ gboolean e_gdbus_proxy_method_call_sync_strv__string (const gchar *method_name,
gchar ** e_gdbus_templates_encode_error (const GError *in_error);
gboolean e_gdbus_templates_decode_error (const gchar * const *in_strv, GError **out_error);
+gchar ** e_gdbus_templates_encode_two_strings (const gchar *in_str1, const gchar *in_str2);
+gboolean e_gdbus_templates_decode_two_strings (const gchar * const *in_strv, gchar **out_str1, gchar **out_str2);
+
G_END_DECLS
#endif /* E_GDBUS_TEMPLATES_H */
diff --git a/libedataserverui/e-client-utils.c b/libedataserverui/e-client-utils.c
index 48334dd..422947c 100644
--- a/libedataserverui/e-client-utils.c
+++ b/libedataserverui/e-client-utils.c
@@ -277,6 +277,7 @@ typedef struct _EClientUtilsAsyncOpData
GError *opened_cb_error;
guint retry_open_id;
gboolean only_if_exists;
+ guint pending_properties_count;
} EClientUtilsAsyncOpData;
static void
@@ -354,19 +355,26 @@ return_async_error (const GError *error, GAsyncReadyCallback async_cb, gpointer
}
static void
-client_utils_capabilities_retrieved_cb (GObject *source_object, GAsyncResult *result, gpointer user_data)
+client_utils_get_backend_property_cb (GObject *source_object, GAsyncResult *result, gpointer user_data)
{
EClient *client = E_CLIENT (source_object);
EClientUtilsAsyncOpData *async_data = user_data;
- gchar *capabilities = NULL;
GSimpleAsyncResult *simple;
g_return_if_fail (async_data != NULL);
g_return_if_fail (async_data->client != NULL);
g_return_if_fail (async_data->client == client);
- e_client_retrieve_capabilities_finish (client, result, &capabilities, NULL);
- g_free (capabilities);
+ if (result) {
+ gchar *prop_value = NULL;
+
+ if (e_client_get_backend_property_finish (client, result, &prop_value, NULL))
+ g_free (prop_value);
+
+ async_data->pending_properties_count--;
+ if (async_data->pending_properties_count)
+ return;
+ }
/* keep the initial auth_handler connected directly, thus it will be able
to answer any later authentication requests, for reconnection, for example
@@ -384,6 +392,49 @@ client_utils_capabilities_retrieved_cb (GObject *source_object, GAsyncResult *re
}
static void
+client_utils_capabilities_retrieved_cb (GObject *source_object, GAsyncResult *result, gpointer user_data)
+{
+ EClient *client = E_CLIENT (source_object);
+ EClientUtilsAsyncOpData *async_data = user_data;
+ gchar *capabilities = NULL;
+ gboolean caps_res;
+
+ g_return_if_fail (async_data != NULL);
+ g_return_if_fail (async_data->client != NULL);
+ g_return_if_fail (async_data->client == client);
+
+ caps_res = e_client_retrieve_capabilities_finish (client, result, &capabilities, NULL);
+ g_free (capabilities);
+
+ if (caps_res) {
+ async_data->pending_properties_count = 1;
+
+ /* precache backend properties */
+ if (E_IS_CAL_CLIENT (client)) {
+ async_data->pending_properties_count += 3;
+
+ e_client_get_backend_property (async_data->client, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
+ e_client_get_backend_property (async_data->client, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
+ e_client_get_backend_property (async_data->client, CAL_BACKEND_PROPERTY_DEFAULT_OBJECT, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
+ } else if (E_IS_BOOK_CLIENT (client)) {
+ async_data->pending_properties_count += 3;
+
+ e_client_get_backend_property (async_data->client, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
+ e_client_get_backend_property (async_data->client, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
+ e_client_get_backend_property (async_data->client, BOOK_BACKEND_PROPERTY_SUPPORTED_AUTH_METHODS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
+ } else {
+ g_warn_if_reached ();
+ client_utils_get_backend_property_cb (source_object, NULL, async_data);
+ return;
+ }
+
+ e_client_get_backend_property (async_data->client, CLIENT_BACKEND_PROPERTY_CACHE_DIR, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
+ } else {
+ client_utils_get_backend_property_cb (source_object, NULL, async_data);
+ }
+}
+
+static void
client_utils_open_new_done (EClientUtilsAsyncOpData *async_data)
{
g_return_if_fail (async_data != NULL);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]