/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * Copyright (C) 2006 OpenedHand Ltd * Copyright (C) 2009 Intel Corporation * * This library is free software; you can redistribute it and/or modify it under * the terms of version 2.1 of the GNU Lesser General Public License as * published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Author: Ross Burton */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "e-book-client.h" #include "e-book-client-view.h" #include "e-book-client-view-private.h" #include "e-book-marshal.h" #include "e-gdbus-book-view.h" #define E_BOOK_CLIENT_VIEW_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_BOOK_CLIENT_VIEW, EBookClientViewPrivate)) G_DEFINE_TYPE (EBookClientView, e_book_client_view, G_TYPE_OBJECT); struct _EBookClientViewPrivate { GDBusProxy *gdbus_bookview; EBookClient *client; gboolean running; }; enum { OBJECTS_ADDED, OBJECTS_MODIFIED, OBJECTS_REMOVED, PROGRESS, COMPLETE, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; static void objects_added_cb (EGdbusBookView *object, const gchar * const *vcards, EBookClientView *view) { const gchar * const *p; GSList *contacts = NULL; if (!view->priv->running) return; /* array contains both UID and vcard */ for (p = vcards; p[0] && p[1]; p += 2) { contacts = g_slist_prepend (contacts, e_contact_new_from_vcard_with_uid (p[0], p[1])); } contacts = g_slist_reverse (contacts); g_signal_emit (view, signals[OBJECTS_ADDED], 0, contacts); e_util_free_object_slist (contacts); } static void objects_modified_cb (EGdbusBookView *object, const gchar * const *vcards, EBookClientView *view) { const gchar * const *p; GSList *contacts = NULL; if (!view->priv->running) return; /* array contains both UID and vcard */ for (p = vcards; p[0] && p[1]; p += 2) { contacts = g_slist_prepend (contacts, e_contact_new_from_vcard_with_uid (p[0], p[1])); } contacts = g_slist_reverse (contacts); g_signal_emit (view, signals[OBJECTS_MODIFIED], 0, contacts); e_util_free_object_slist (contacts); } static void objects_removed_cb (EGdbusBookView *object, const gchar * const *ids, EBookClientView *view) { const gchar * const *p; GSList *list = NULL; if (!view->priv->running) return; for (p = ids; *p; p++) { list = g_slist_prepend (list, (gchar *) *p); } list = g_slist_reverse (list); g_signal_emit (view, signals[OBJECTS_REMOVED], 0, list); /* No need to free the values, our caller will */ g_slist_free (list); } static void progress_cb (EGdbusBookView *object, guint percent, const gchar *message, EBookClientView *view) { if (!view->priv->running) return; g_signal_emit (view, signals[PROGRESS], 0, percent, message); } static void complete_cb (EGdbusBookView *object, const gchar * const *in_error_strv, EBookClientView *view) { GError *error = NULL; if (!view->priv->running) return; g_return_if_fail (e_gdbus_templates_decode_error (in_error_strv, &error)); g_signal_emit (view, signals[COMPLETE], 0, error); if (error) g_error_free (error); } /* * _e_book_client_view_new: * @book_client: an #EBookClient * @gdbus_bookview: The #EGdbusBookView to get signals from * * Creates a new #EBookClientView based on #EBookClient and listening to @gdbus_bookview. * This is a private function, applications should call e_book_client_get_view() or * e_book_client_get_view_sync(). * * Returns: A new #EBookClientView. **/ EBookClientView * _e_book_client_view_new (EBookClient *book_client, EGdbusBookView *gdbus_bookview) { EBookClientView *view; EBookClientViewPrivate *priv; view = g_object_new (E_TYPE_BOOK_CLIENT_VIEW, NULL); priv = view->priv; priv->client = g_object_ref (book_client); /* Take ownership of the gdbus_bookview object */ priv->gdbus_bookview = g_object_ref (G_DBUS_PROXY (gdbus_bookview)); g_object_add_weak_pointer (G_OBJECT (gdbus_bookview), (gpointer) &priv->gdbus_bookview); g_signal_connect (priv->gdbus_bookview, "objects-added", G_CALLBACK (objects_added_cb), view); g_signal_connect (priv->gdbus_bookview, "objects-modified", G_CALLBACK (objects_modified_cb), view); g_signal_connect (priv->gdbus_bookview, "objects-removed", G_CALLBACK (objects_removed_cb), view); g_signal_connect (priv->gdbus_bookview, "progress", G_CALLBACK (progress_cb), view); g_signal_connect (priv->gdbus_bookview, "complete", G_CALLBACK (complete_cb), view); return view; } /** * e_book_client_view_get_client: * @view: an #EBookClientView * * Returns the #EBookClient that this book view is monitoring. * * Returns: (transfer none): an #EBookClient. **/ EBookClient * e_book_client_view_get_client (EBookClientView *view) { g_return_val_if_fail (E_IS_BOOK_CLIENT_VIEW (view), NULL); return view->priv->client; } /** * e_book_client_view_start: * @error: A #GError * @view: an #EBookClientView * * Tells @view to start processing events. */ void e_book_client_view_start (EBookClientView *view, GError **error) { EBookClientViewPrivate *priv; g_return_if_fail (E_IS_BOOK_CLIENT_VIEW (view)); priv = view->priv; if (priv->gdbus_bookview) { GError *local_error = NULL; priv->running = TRUE; if (!e_gdbus_book_view_call_start_sync (priv->gdbus_bookview, NULL, &local_error)) priv->running = FALSE; e_client_unwrap_dbus_error (E_CLIENT (priv->client), local_error, error); } else { /* do not translate this string, it should ideally never happen */ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR, "Cannot start view, D-Bus proxy gone"); } } /** * e_book_client_view_stop: * @view: an #EBookClientView * @error: A #GError * * Tells @view to stop processing events. **/ void e_book_client_view_stop (EBookClientView *view, GError **error) { EBookClientViewPrivate *priv; g_return_if_fail (E_IS_BOOK_CLIENT_VIEW (view)); priv = view->priv; priv->running = FALSE; if (priv->gdbus_bookview) { GError *local_error = NULL; e_gdbus_book_view_call_stop_sync (priv->gdbus_bookview, NULL, &local_error); e_client_unwrap_dbus_error (E_CLIENT (priv->client), local_error, error); } else { /* do not translate this string, it should ideally never happen */ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR, "Cannot stop view, D-Bus proxy gone"); } } /** * e_book_client_view_set_flags: * @view: an #EBookClientView * @flags: the #EBookClientViewFlags for @view. * @error: a return location for a #GError, or %NULL. * * Sets the @flags which control the behaviour of @view. * * Since: 3.4 */ void e_book_client_view_set_flags (EBookClientView *view, EBookClientViewFlags flags, GError **error) { EBookClientViewPrivate *priv; g_return_if_fail (E_IS_BOOK_CLIENT_VIEW (view)); priv = view->priv; if (priv->gdbus_bookview) { GError *local_error = NULL; e_gdbus_book_view_call_set_flags_sync (priv->gdbus_bookview, flags, NULL, &local_error); e_client_unwrap_dbus_error (E_CLIENT (priv->client), local_error, error); } else { g_set_error_literal ( error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR, "Cannot set flags on view, D-Bus proxy gone"); } } /** * e_book_client_view_set_fields_of_interest: * @view: An #EBookClientView object * @fields_of_interest: (element-type utf8): List of field names in which the client is interested * @error: A #GError * * Client can instruct server to which fields it is interested in only, thus * the server can return less data over the wire. The server can still return * complete objects, this is just a hint to it that the listed fields will * be used only. The UID field is returned always. Initial views has no fields * of interest and using %NULL for @fields_of_interest will unset any previous * changes. * * Some backends can use summary information of its cache to create artifical * objects, which will omit stored object parsing. If this cannot be done then * it will simply return object as is stored in the cache. **/ void e_book_client_view_set_fields_of_interest (EBookClientView *view, const GSList *fields_of_interest, GError **error) { EBookClientViewPrivate *priv; g_return_if_fail (E_IS_BOOK_CLIENT_VIEW (view)); priv = view->priv; if (priv->gdbus_bookview) { GError *local_error = NULL; gchar **strv; strv = e_client_util_slist_to_strv (fields_of_interest); e_gdbus_book_view_call_set_fields_of_interest_sync (priv->gdbus_bookview, (const gchar * const *) strv, NULL, &local_error); g_strfreev (strv); e_client_unwrap_dbus_error (E_CLIENT (priv->client), local_error, error); } else { /* do not translate this string, it should ideally never happen */ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR, "Cannot set fields of interest, D-Bus proxy gone"); } } static void e_book_client_view_init (EBookClientView *view) { view->priv = E_BOOK_CLIENT_VIEW_GET_PRIVATE (view); view->priv->gdbus_bookview = NULL; view->priv->client = NULL; view->priv->running = FALSE; } static void book_client_view_dispose (GObject *object) { EBookClientView *view = E_BOOK_CLIENT_VIEW (object); if (view->priv->gdbus_bookview) { GError *error = NULL; g_signal_handlers_disconnect_matched (view->priv->gdbus_bookview, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, view); e_gdbus_book_view_call_dispose_sync (G_DBUS_PROXY (view->priv->gdbus_bookview), NULL, &error); g_object_unref (view->priv->gdbus_bookview); view->priv->gdbus_bookview = NULL; if (error) { g_warning ("Failed to dispose book view: %s", error->message); g_error_free (error); } } if (view->priv->client) { g_object_unref (view->priv->client); view->priv->client = NULL; } /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_book_client_view_parent_class)->dispose (object); } static void e_book_client_view_class_init (EBookClientViewClass *class) { GObjectClass *object_class; g_type_class_add_private (class, sizeof (EBookClientViewPrivate)); object_class = G_OBJECT_CLASS (class); object_class->dispose = book_client_view_dispose; signals[OBJECTS_ADDED] = g_signal_new ( "objects-added", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EBookClientViewClass, objects_added), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); signals[OBJECTS_MODIFIED] = g_signal_new ( "objects-modified", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EBookClientViewClass, objects_modified), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); signals[OBJECTS_REMOVED] = g_signal_new ( "objects-removed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EBookClientViewClass, objects_removed), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); signals[PROGRESS] = g_signal_new ( "progress", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EBookClientViewClass, progress), NULL, NULL, e_gdbus_marshallers_VOID__UINT_STRING, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING); signals[COMPLETE] = g_signal_new ( "complete", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EBookClientViewClass, complete), NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, G_TYPE_ERROR); }