On Fri, Aug 5, 2011 at 12:45 PM, Patrick Ohly <[email protected]> wrote:
> On Fr, 2011-08-05 at 11:03 +0300, Dumez, Christophe wrote:
>> +    if (!e_book_client_get_view_sync(m_addressbook.get(), sexp,
>> &view, NULL, gerror)) {
>> +        gerror.throwError( "getting the view" );
>> +    }
>> +    EBookClientViewCXX viewWrapper = EBookClientViewCXX::steal(view);
>
> Would it make sense to extend SE_GOBJECT_TYPE = EBookClientViewCXX such
> that it supports the same initialization as GListCXX by simply passing
> it to GNOME functions?
>
> Like this:
>
> EBookClientViewCXX view;
> if (!e_book_client_get_view_sync(m_addressbook.get(), sexp, view, NULL,
> gerror)) {
>        gerror.throwError( "getting the view" );
> }
>
> Implementing it won't be as easy as for GListCXX, because
> EBookClientViewCXX is a boost::shared_pointer. Just wondering... ;-)

Good idea, I implemented that and fixed the code.

> +    EvolutionContactAsyncList async_list(view);
> +
> +         if (!async_list.m_error.isNull()) {
> +                   async_list.m_error.throwError("watching view");
> +    }
> +
> +    GSList *nextItem = async_list.m_contacts;
> +    GListCXX<EContact, GSList, GObjectDestructor<EContact> > 
> listptr(nextItem);
>
> Indention problem, but my main question is: how is ownership of the list
> elements handled here? Isn't there a double unref, once inside
> async_list.m_contacts and once in listptr?

I fixed the indentation problem and there was indeed a double-unref
that is fixed now.

> From GLibSupport.h:
>
> +template<class T> void GObjectDestructor(T *obj);
>
> Does that really work? Doesn't the template definition have to be
> visible here for it to be used?

Well, it compiled at least. I moved the implementation to the header
file to be safe.

> +/**
> + * Wraps a C char array and takes care of freeing the memory.
> + */
> +class PlainCStr : public boost::shared_ptr<char>
> +{
> +    public:
> +        PlainCStr() {}
> +        PlainCStr(char *str) : boost::shared_ptr<char>(str, free) {}
> +        PlainCStr(const PlainCStr &other) :
> boost::shared_ptr<char>(other) {}
> +        operator const char *() const { return &**this; }
> +        const char *c_str() const { return &**this; }
> +};
>
> Note that free() != g_free(). This PlainCStr is used for strings
> allocated by GNOME, isn't it? Then we need a second PlainGStr which uses
> g_free().

Replaced char / free() by gchar / g_free() and renamed to PlainGStr.

> Other than that the patch looks good. Does it pass the
> Client::Source::eds_contact tests?

Yes it does:
$ CLIENT_TEST_SOURCES=eds_contact CLIENT_TEST_EVOLUTION_PREFIX=Test_
client-test Client::Source::eds_contact
Client::Source::eds_contact::testOpen okay
Client::Source::eds_contact::testIterateTwice okay
Client::Source::eds_contact::testSimpleInsert okay
Client::Source::eds_contact::testLocalDeleteAll okay
Client::Source::eds_contact::testComplexInsert okay
Client::Source::eds_contact::testLocalUpdate okay
Client::Source::eds_contact::testChanges okay
Client::Source::eds_contact::testImport okay
Client::Source::eds_contact::testImportDelete okay
Client::Source::eds_contact::testManyChanges okay

OK (10)


Remark: I also had to extend SE_GOBJECT_TYPE() macro to support a
"base" parameter as certain GObjects such as EBookQuery have their own
ref/unref functions. SE_GOBJECT_TYPE(_x) still behaves the same way
but it now calls internally the new SE_GOBJECT_BASED_TYPE(_x, base)
macro, using GObject as base.

Kr,

-- 
Dr. Christophe Dumez
Linux Software Engineer
Intel Finland Oy - Open Source Technology Center
From 4ad5d728e2bd655226636f3d8a0d8592fcc08aed Mon Sep 17 00:00:00 2001
From: Christophe Dumez <[email protected]>
Date: Fri, 5 Aug 2011 10:50:16 +0300
Subject: [PATCH] Support for the new EBookClient API in EDS 3.2

SyncEvolution EDS backend currently uses the EBook API which is
deprecated as of EDS v3.2 and replaced by the new EBookClient API.
This patch brings support for this new EBookClient API (when EDS
3.2 is detected at compile time).

The new code based on the EBookClient API enables a few
optimizations, especially the partial fetching of contacts to
avoid useless DBus trafic. This results in better contact change
tracking by fetching only the UID/REV attributes for each contact.

Note: The new code uses the new GLibSupport header instead of the
deprecated SmartPointer (eptr).
---
 src/backends/evolution/EvolutionContactSource.cpp |  273 +++++++++++++++++++--
 src/backends/evolution/EvolutionContactSource.h   |   12 +
 src/backends/evolution/EvolutionSyncSource.h      |   28 ++
 src/syncevo/GLibSupport.h                         |   73 +++++-
 src/syncevo/eds_abi_wrapper.h                     |    9 +
 5 files changed, 368 insertions(+), 27 deletions(-)

diff --git a/src/backends/evolution/EvolutionContactSource.cpp b/src/backends/evolution/EvolutionContactSource.cpp
index 28fd082..4117319 100644
--- a/src/backends/evolution/EvolutionContactSource.cpp
+++ b/src/backends/evolution/EvolutionContactSource.cpp
@@ -41,25 +41,8 @@ using namespace std;
 #include <boost/foreach.hpp>
 
 #include <syncevo/declarations.h>
-SE_BEGIN_CXX
-
-class unrefEBookChanges {
- public:
-    /** free list of EBookChange instances */
-    static void unref(GList *pointer) {
-        if (pointer) {
-            GList *next = pointer;
-            do {
-                EBookChange *ebc = (EBookChange *)next->data;
-                g_object_unref(ebc->contact);
-                g_free(next->data);
-                next = next->next;
-            } while (next);
-            g_list_free(pointer);
-        }
-    }
-};
 
+SE_BEGIN_CXX
 
 const EvolutionContactSource::extensions EvolutionContactSource::m_vcardExtensions;
 const EvolutionContactSource::unique EvolutionContactSource::m_uniqueProperties;
@@ -78,7 +61,11 @@ EvolutionSyncSource::Databases EvolutionContactSource::getDatabases()
 {
     ESourceList *sources = NULL;
 
+#ifdef USE_EBOOK_CLIENT
+    if (!e_book_client_get_sources(&sources, NULL)) {
+#else
     if (!e_book_get_addressbooks(&sources, NULL)) {
+#endif
         SyncContext::throwError("unable to access address books");
     }
 
@@ -120,6 +107,22 @@ EvolutionSyncSource::Databases EvolutionContactSource::getDatabases()
 
     // No results? Try system address book (workaround for embedded Evolution Dataserver).
     if (!result.size()) {
+#ifdef USE_EBOOK_CLIENT
+        EBookClientCXX book;
+        const char *name;
+
+        name = "<<system>>";
+        book = EBookClientCXX::steal(e_book_client_new_system (NULL));
+        if (!book) {
+            name = "<<default>>";
+            book = EBookClientCXX::steal(e_book_client_new_default (NULL));
+        }
+
+        if (book) {
+            const char *uri = e_client_get_uri (E_CLIENT ((EBookClient*)book));
+            result.push_back(Database(name, uri, true));
+        }
+#else
         eptr<EBook, GObject> book;
         GError *gerror = NULL;
         const char *name;
@@ -137,6 +140,7 @@ EvolutionSyncSource::Databases EvolutionContactSource::getDatabases()
             const char *uri = e_book_get_uri (book);
             result.push_back(Database(name, uri, true));
         }
+#endif
     } else {
         //  the first DB found is the default
         result[0].m_isDefault = true;
@@ -145,8 +149,92 @@ EvolutionSyncSource::Databases EvolutionContactSource::getDatabases()
     return result;
 }
 
+#ifdef USE_EBOOK_CLIENT
+static void
+handle_error_cb (EClient */*client*/, const gchar *error_msg, gpointer user_data)
+{
+    EvolutionContactSource *that = static_cast<EvolutionContactSource *>(user_data);
+    SE_LOG_ERROR(that, NULL, error_msg);
+}
+
+static gboolean
+handle_authentication_cb (EClient */*client*/, ECredentials *credentials, gpointer user_data)
+{
+    EvolutionContactSource *that = static_cast<EvolutionContactSource *>(user_data);
+    std::string user = that->getUser();
+    std::string passwd = that->getPassword();
+
+    if (!user.empty() || !passwd.empty()) {
+        e_credentials_set (credentials, E_CREDENTIALS_KEY_USERNAME, user.c_str());
+        e_credentials_set (credentials, E_CREDENTIALS_KEY_PASSWORD, passwd.c_str());
+        return true;
+    } else {
+        return false;
+    }
+}
+#endif
+
 void EvolutionContactSource::open()
 {
+#ifdef USE_EBOOK_CLIENT
+    ESourceList *sources;
+
+    if (!e_book_client_get_sources(&sources, NULL)) {
+        throwError("unable to access address books");
+    }
+    
+    string id = getDatabaseID();
+    ESource *source = findSource(sources, id);
+    bool onlyIfExists = true;
+    bool created = false;
+    GErrorCXX gerror;
+    if (!source) {
+        // might have been special "<<system>>" or "<<default>>", try that and
+        // creating address book from file:// URI before giving up
+        if (id.empty() || id == "<<system>>") {
+            m_addressbook = EBookClientCXX::steal(e_book_client_new_system (gerror));
+        } else if (id.empty() || id == "<<default>>") {
+            m_addressbook = EBookClientCXX::steal(e_book_client_new_default (gerror));
+        } else if (boost::starts_with(id, "file://")) {
+            m_addressbook = EBookClientCXX::steal(e_book_client_new_from_uri(id.c_str(), gerror));
+        } else {
+            throwError(string(getName()) + ": no such address book: '" + id + "'");
+        }
+        created = true;
+        onlyIfExists = false;
+    } else {
+        m_addressbook = EBookClientCXX::steal(e_book_client_new( source, gerror ));
+    }
+
+    if (!gerror.isNull()) {
+        gerror.throwError("create addressbook");
+    }
+
+    // Listen for errors
+    g_signal_connect (m_addressbook, "backend-error", G_CALLBACK (handle_error_cb), this); 
+
+    // Handle authentication requests from the backend
+    g_signal_connect (m_addressbook, "authenticate", G_CALLBACK (handle_authentication_cb), this);
+ 
+    // Open the address book
+    if (!e_client_open_sync( E_CLIENT ((EBookClient*)m_addressbook), onlyIfExists, NULL, gerror) ) {
+        if (created) {
+            // opening newly created address books often fails, try again once more
+            gerror.clear();
+            sleep(5);
+            if (!e_client_open_sync( E_CLIENT ((EBookClient*)m_addressbook), onlyIfExists, NULL, gerror)) {
+                gerror.throwError("opening address book");
+            }
+        } else {
+            gerror.throwError("opening address book");
+        }
+    }
+
+    g_signal_connect_after(m_addressbook,
+                           "backend-died",
+                           G_CALLBACK(SyncContext::fatalError),
+                           (void *)"Evolution Data Server has died unexpectedly, contacts no longer available.");
+#else
     ESourceList *sources;
     if (!e_book_get_addressbooks(&sources, NULL)) {
         throwError("unable to access address books");
@@ -223,6 +311,7 @@ void EvolutionContactSource::open()
                            "backend-died",
                            G_CALLBACK(SyncContext::fatalError),
                            (void *)"Evolution Data Server has died unexpectedly, contacts no longer available.");
+#endif
 }
 
 bool EvolutionContactSource::isEmpty()
@@ -234,8 +323,88 @@ bool EvolutionContactSource::isEmpty()
     return revisions.empty();
 }
 
+#ifdef USE_EBOOK_CLIENT
+struct EvolutionContactAsyncList {
+    EvolutionContactAsyncList(EBookClientView *view): m_view(view)
+    {
+        // Listen for view signals
+        g_signal_connect(view, "objects-added", G_CALLBACK(contactsAdded), this);
+        g_signal_connect(view, "complete", G_CALLBACK(completed), this);
+
+        // Start the view
+        e_book_client_view_start (view, m_error);
+        if (!m_error.isNull())
+          return;
+
+        // Async -> Sync
+        m_loop.run();
+        e_book_client_view_stop (view, NULL);
+    }
+ 
+    static void contactsAdded(EBookClientView *ebookview,
+                              const GSList *contacts,
+                              gpointer user_data) {
+        EvolutionContactAsyncList *that = (EvolutionContactAsyncList *)user_data;
+        const GSList *elem;
+        for (elem = contacts; elem; elem = elem->next) {
+            EContact *contact = E_CONTACT(elem->data);
+            g_object_ref (contact);
+            that->m_contacts.push_back(contact);
+        }
+    }
+ 
+    static void completed(EBookClientView *ebookview,
+                          const GError *error,
+                          gpointer user_data) {
+        EvolutionContactAsyncList *that = (EvolutionContactAsyncList *)user_data;
+        that->m_error = error;
+        that->m_loop.quit();
+    }
+ 
+    // View watched
+    EBookClientView *m_view;
+    // List of contacts in the view
+    GListCXX<EContact, GSList, GObjectDestructor<EContact> >  m_contacts;
+    // Possible error while watching the view
+    GErrorCXX m_error;
+    // Event loop for Async -> Sync
+    EvolutionAsync m_loop;
+};
+
+#endif
+
 void EvolutionContactSource::listAllItems(RevisionMap_t &revisions)
 {
+#ifdef USE_EBOOK_CLIENT
+    GErrorCXX gerror;
+    EBookClientView *view;
+
+    EBookQueryCXX allItemsQuery = EBookQueryCXX::steal(e_book_query_any_field_contains(""));
+    PlainGStr sexp(e_book_query_to_string (allItemsQuery));
+    
+    if (!e_book_client_get_view_sync(m_addressbook, sexp, &view, NULL, gerror)) {
+        gerror.throwError( "getting the view" );
+    }
+    EBookClientViewCXX viewWrapper = EBookClientViewCXX::steal(view);
+
+    // Optimization: set fields_of_interest (UID / REV)
+    GListCXX<const char, GSList> interesting_field_list;
+    interesting_field_list.push_back(e_contact_field_name (E_CONTACT_UID));
+    interesting_field_list.push_back(e_contact_field_name (E_CONTACT_REV));
+    e_book_client_view_set_fields_of_interest (view, interesting_field_list, gerror);
+    if (!gerror.isNull()) {
+        SE_LOG_ERROR(this, NULL, "e_book_client_view_set_fields_of_interest: %s", (const char*)gerror);
+        gerror.clear();
+    }
+
+    EvolutionContactAsyncList async_list(view);
+
+    if (!async_list.m_error.isNull()) {
+        async_list.m_error.throwError("watching view");
+    }
+
+    GSList *nextItem = async_list.m_contacts;
+#else
     GError *gerror = NULL;
     eptr<EBookQuery> allItemsQuery(e_book_query_any_field_contains(""), "query");
     GList *nextItem;
@@ -243,6 +412,7 @@ void EvolutionContactSource::listAllItems(RevisionMap_t &revisions)
         throwError( "reading all items", gerror );
     }
     eptr<GList> listptr(nextItem);
+#endif
     while (nextItem) {
         EContact *contact = E_CONTACT(nextItem->data);
         if (!contact) {
@@ -274,6 +444,16 @@ void EvolutionContactSource::close()
 string EvolutionContactSource::getRevision(const string &luid)
 {
     EContact *contact;
+#ifdef USE_EBOOK_CLIENT
+    GErrorCXX gerror;
+    if (!e_book_client_get_contact_sync(m_addressbook,
+                                        luid.c_str(),
+                                        &contact,
+                                        NULL,
+                                        gerror)) {
+        gerror.throwError(string("reading contact ") + luid);
+    }
+#else
     GError *gerror = NULL;
     if (!e_book_get_contact(m_addressbook,
                             luid.c_str(),
@@ -282,6 +462,7 @@ string EvolutionContactSource::getRevision(const string &luid)
         throwError(string("reading contact ") + luid,
                    gerror);
     }
+#endif
     eptr<EContact, GObject> contactptr(contact, "contact");
     const char *rev = (const char *)e_contact_get_const(contact,
                                                         E_CONTACT_REV);
@@ -294,6 +475,16 @@ string EvolutionContactSource::getRevision(const string &luid)
 void EvolutionContactSource::readItem(const string &luid, std::string &item, bool raw)
 {
     EContact *contact;
+#ifdef USE_EBOOK_CLIENT
+    GErrorCXX gerror;
+    if (!e_book_client_get_contact_sync(m_addressbook,
+                                        luid.c_str(),
+                                        &contact,
+                                        NULL,
+                                        gerror)) {
+        gerror.throwError(string("reading contact ") + luid);
+    }
+#else
     GError *gerror = NULL;
     if (!e_book_get_contact(m_addressbook,
                             luid.c_str(),
@@ -302,6 +493,7 @@ void EvolutionContactSource::readItem(const string &luid, std::string &item, boo
         throwError(string("reading contact ") + luid,
                    gerror);
     }
+#endif
     eptr<EContact, GObject> contactptr(contact, "contact");
     eptr<char> vcardstr(e_vcard_to_string(&contactptr->parent,
                                           EVC_FORMAT_VCARD_30));
@@ -317,11 +509,29 @@ EvolutionContactSource::insertItem(const string &uid, const std::string &item, b
 {
     eptr<EContact, GObject> contact(e_contact_new_from_vcard(item.c_str()));
     if (contact) {
-        GError *gerror = NULL;
         e_contact_set(contact, E_CONTACT_UID,
                       uid.empty() ?
                       NULL :
                       const_cast<char *>(uid.c_str()));
+#ifdef USE_EBOOK_CLIENT
+        GErrorCXX gerror;
+        if (uid.empty()) {
+            gchar* newuid;
+            if (!e_book_client_add_contact_sync(m_addressbook, contact, &newuid, NULL, gerror)) {
+                gerror.throwError("add new contact");
+            }
+            PlainGStr newuidWrapper(newuid);
+            string newrev = getRevision(newuid);
+            return InsertItemResult(newuid, newrev, false);
+        } else {
+            if (!e_book_client_modify_contact_sync(m_addressbook, contact, NULL, gerror)) {
+                gerror.throwError("modify existing contact: "+ uid);
+            }
+            string newrev = getRevision(uid);
+            return InsertItemResult(uid, newrev, false);
+        }
+#else
+        GError *gerror = NULL;
         if (uid.empty() ?
             e_book_add_contact(m_addressbook, contact, &gerror) :
             e_book_commit_contact(m_addressbook, contact, &gerror)) {
@@ -337,6 +547,7 @@ EvolutionContactSource::insertItem(const string &uid, const std::string &item, b
                        string("updating contact ") + uid,
                        gerror);
         }
+#endif
     } else {
         throwError(string("failure parsing vcard " ) + item);
     }
@@ -346,6 +557,18 @@ EvolutionContactSource::insertItem(const string &uid, const std::string &item, b
 
 void EvolutionContactSource::removeItem(const string &uid)
 {
+#ifdef USE_EBOOK_CLIENT
+    GErrorCXX gerror;
+    if (!e_book_client_remove_contact_by_uid_sync(m_addressbook, uid.c_str(), NULL, gerror)) {
+        if (gerror->domain == E_BOOK_CLIENT_ERROR &&
+            gerror->code == E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND) {
+            SE_LOG_DEBUG(this, NULL, "%s: request to delete non-existant contact ignored",
+                         uid.c_str());
+        } else {
+            gerror.throwError( string( "deleting contact " ) + uid );
+        }
+    }
+#else
     GError *gerror = NULL;
     if (!e_book_remove_contact(m_addressbook, uid.c_str(), &gerror)) {
         if (gerror->domain == E_BOOK_ERROR &&
@@ -358,12 +581,23 @@ void EvolutionContactSource::removeItem(const string &uid)
                         gerror );
         }
     }
+#endif
 }
 
 std::string EvolutionContactSource::getDescription(const string &luid)
 {
     try {
         EContact *contact;
+#ifdef USE_EBOOK_CLIENT
+        GErrorCXX gerror;          
+        if (!e_book_client_get_contact_sync(m_addressbook,
+                                        luid.c_str(),
+                                        &contact,
+                                        NULL,
+                                        gerror)) {
+            gerror.throwError(string("reading contact ") + luid);
+        }
+#else
         GError *gerror = NULL;
         if (!e_book_get_contact(m_addressbook,
                                 luid.c_str(),
@@ -372,6 +606,7 @@ std::string EvolutionContactSource::getDescription(const string &luid)
             throwError(string("reading contact ") + luid,
                        gerror);
         }
+#endif
         eptr<EContact, GObject> contactptr(contact, "contact");
         const char *name = (const char *)e_contact_get_const(contact, E_CONTACT_FULL_NAME);
         if (name) {
diff --git a/src/backends/evolution/EvolutionContactSource.h b/src/backends/evolution/EvolutionContactSource.h
index 7d7b5ac..271935e 100644
--- a/src/backends/evolution/EvolutionContactSource.h
+++ b/src/backends/evolution/EvolutionContactSource.h
@@ -24,6 +24,7 @@
 #include "config.h"
 #include "EvolutionSyncSource.h"
 #include <syncevo/SmartPtr.h>
+#include <syncevo/GLibSupport.h>
 
 #include <boost/noncopyable.hpp>
 
@@ -32,6 +33,13 @@
 #include <set>
 
 #include <syncevo/declarations.h>
+
+#ifdef USE_EBOOK_CLIENT
+SE_GOBJECT_TYPE(EBookClient)
+SE_GOBJECT_TYPE(EBookClientView)
+SE_GOBJECT_BASED_TYPE(EBookQuery, EBookQuery)
+#endif
+
 SE_BEGIN_CXX
 
 /**
@@ -84,7 +92,11 @@ class EvolutionContactSource : public EvolutionSyncSource,
     std::string getRevision(const std::string &uid);
 
     /** valid after open(): the address book that this source references */
+#ifdef USE_EBOOK_CLIENT
+    EBookClientCXX m_addressbook;
+#else
     eptr<EBook, GObject> m_addressbook;
+#endif
 
     /** the format of vcards that new items are expected to have */
     const EVCardFormat m_vcardFormat;
diff --git a/src/backends/evolution/EvolutionSyncSource.h b/src/backends/evolution/EvolutionSyncSource.h
index 64ed70c..475260f 100644
--- a/src/backends/evolution/EvolutionSyncSource.h
+++ b/src/backends/evolution/EvolutionSyncSource.h
@@ -25,6 +25,10 @@
 #include <syncevo/eds_abi_wrapper.h>
 
 #include <syncevo/declarations.h>
+#include <syncevo/GLibSupport.h>
+
+SE_GOBJECT_TYPE(GMainLoop)
+
 SE_BEGIN_CXX
 
 
@@ -85,6 +89,30 @@ class EvolutionSyncSource : public TrackingSyncSource
 #endif
 };
 
+/**
+ * Utility class which hides the mechanisms needed to handle events
+ * during asynchronous calls.
+ */
+class EvolutionAsync {
+    public:
+    EvolutionAsync()
+    {
+        m_loop = GMainLoopCXX::steal(g_main_loop_new(NULL, FALSE));
+    }
+     
+    /** start processing events */
+    void run() {
+        g_main_loop_run(m_loop.get());
+    }
+ 
+    /** stop processing events, to be called inside run() by callback */
+    void quit() {
+        g_main_loop_quit(m_loop.get());
+    }
+ 
+    private:
+    GMainLoopCXX m_loop;
+};
 
 SE_END_CXX
 #endif // INCL_EVOLUTIONSYNCSOURCE
diff --git a/src/syncevo/GLibSupport.h b/src/syncevo/GLibSupport.h
index e050cb4..745fd5a 100644
--- a/src/syncevo/GLibSupport.h
+++ b/src/syncevo/GLibSupport.h
@@ -25,6 +25,7 @@
 #endif
 
 #include <syncevo/util.h>
+#include <syncevo/eds_abi_wrapper.h>
 
 #ifdef HAVE_GLIB
 # include <glib-object.h>
@@ -33,6 +34,7 @@
 typedef void *GMainLoop;
 #endif
 
+#include <boost/shared_ptr.hpp>
 #include <boost/intrusive_ptr.hpp>
 #include <boost/utility.hpp>
 #include <boost/foreach.hpp>
@@ -68,6 +70,21 @@ GLibSelectResult GLibSelect(GMainLoop *loop, int fd, int direction, Timespec *ti
 
 #ifdef HAVE_GLIB
 
+SE_END_CXX
+/** static functions for handling (un)referencing of GObjects */
+class GObjectRef {
+    public:
+        // Regular GObject
+        static void ref(GObject *obj) { g_object_ref(obj); }
+        static void unref(GObject *obj) { g_object_unref(obj); }
+#ifdef ENABLE_EBOOK
+        // EBookQuery
+        static void ref(EBookQuery *query) { e_book_query_ref(query); }
+        static void unref(EBookQuery *query) { e_book_query_unref(query); }
+#endif
+};
+SE_BEGIN_CXX
+
 /**
  * Defines a shared pointer for a GObject-based type, with intrusive
  * reference counting. Use *outside* of SyncEvolution namespace
@@ -76,7 +93,7 @@ GLibSelectResult GLibSelect(GMainLoop *loop, int fd, int direction, Timespec *ti
  * *inside* the SyncEvolution namespace.
  *
  * Example:
- * SE_GOBJECT_TYPE(GFile);
+ * SE_GOBJECT_TYPE(GFile)
  * SE_BEGIN_CXX
  * {
  *   // reference normally increased during construction,
@@ -86,21 +103,32 @@ GLibSelectResult GLibSelect(GMainLoop *loop, int fd, int direction, Timespec *ti
  *   // file freed here as filecxx gets destroyed
  * }
  * SE_END_CXX
+ *
+ * If the GObject-based type uses custom ref/unref functions, then use 
+ * SE_GOBJECT_BASED_TYPE(_x, _base) macro instead and indicate the base.
+ * Make sure corresponding ref()/unref() functions are defined in the
+ * GObjectRef class.
+ *
+ * Example:
+ * SE_GOBJECT_BASED_TYPE(EBookQuery, EBookQuery)
  */
-#define SE_GOBJECT_TYPE(_x) \
-    void inline intrusive_ptr_add_ref(_x *ptr) { g_object_ref(ptr); } \
-    void inline intrusive_ptr_release(_x *ptr) { g_object_unref(ptr); } \
+#define SE_GOBJECT_BASED_TYPE(_x, _base) \
+    void inline intrusive_ptr_add_ref(_x *ptr) { GObjectRef::ref((_base *)ptr); } \
+    void inline intrusive_ptr_release(_x *ptr) { GObjectRef::unref((_base *)ptr); } \
     SE_BEGIN_CXX \
     class _x ## CXX : public boost::intrusive_ptr<_x> { \
     public: \
          _x ## CXX(_x *ptr, bool add_ref = true) : boost::intrusive_ptr<_x>(ptr, add_ref) {} \
          _x ## CXX() {} \
          _x ## CXX(const _x ## CXX &other) : boost::intrusive_ptr<_x>(other) {} \
+         operator _x * () { return boost::intrusive_ptr<_x>::get(); } \
 \
          static  _x ## CXX steal(_x *ptr) { return _x ## CXX(ptr, false); } \
     }; \
     SE_END_CXX \
 
+#define SE_GOBJECT_TYPE(_x) SE_GOBJECT_BASED_TYPE(_x, GObject)
+
 SE_END_CXX
 
 SE_GOBJECT_TYPE(GFile)
@@ -157,10 +185,23 @@ struct GErrorCXX {
         }
         return *this;
     }
+    GErrorCXX &operator =(const GError* err) {
+       if (m_gerror) {
+           g_clear_error(&m_gerror);
+       }
+       m_gerror = g_error_copy(err);
+       return *this;
+    }
+
+    /** For convenient access to GError members (message, domain, ...) */
+    GError*	operator-> () const { return m_gerror; }
 
     /** error description, with fallback if not set (not expected, so not localized) */
     operator const char * () { return m_gerror ? m_gerror->message : "<<no error>>"; }
 
+    /** Check if a gError was set */
+    bool isNull() const { return m_gerror == NULL; }
+
     /** clear error */
     ~GErrorCXX() { g_clear_error(&m_gerror); }
 
@@ -183,6 +224,7 @@ struct GErrorCXX {
 };
 
 template<class T> void NoopDestructor(T *) {}
+template<class T> void GObjectDestructor(T *obj) { g_object_unref(obj); }
 
 /**
  * Wraps a G[S]List of pointers to a specific type.
@@ -202,11 +244,11 @@ template< class T, class L, void (*D)(T*) = NoopDestructor<T> > struct GListCXX
     static void listFree(GSList *l) { g_slist_free(l); }
     static void listFree(GList *l) { g_list_free(l); }
 
-    static GSList *listPrepend(GSList *list, T *entry) { return g_slist_prepend(list, static_cast<gpointer>(entry)); }
-    static GList *listPrepend(GList *list, T *entry) { return g_list_prepend(list, static_cast<gpointer>(entry)); }
+    static GSList *listPrepend(GSList *list, T *entry) { return g_slist_prepend(list, (gpointer)entry); }
+    static GList *listPrepend(GList *list, T *entry) { return g_list_prepend(list, (gpointer)entry); }
 
-    static GSList *listAppend(GSList *list, T *entry) { return g_slist_append(list, static_cast<gpointer>(entry)); }
-    static GList *listAppend(GList *list, T *entry) { return g_list_append(list, static_cast<gpointer>(entry)); }
+    static GSList *listAppend(GSList *list, T *entry) { return g_slist_append(list, (gpointer)entry); }
+    static GList *listAppend(GList *list, T *entry) { return g_list_append(list, (gpointer)entry); }
 
  public:
     typedef T * value_type;
@@ -214,6 +256,8 @@ template< class T, class L, void (*D)(T*) = NoopDestructor<T> > struct GListCXX
     /** empty error, NULL pointer */
     GListCXX() : m_list(NULL) {}
 
+    GListCXX(L* l) : m_list(l) {}
+
     /** free list */
     ~GListCXX() { clear(); }
 
@@ -289,6 +333,19 @@ template< class T, class L, void (*D)(T*) = NoopDestructor<T> > struct GListCXX
     void push_front(T *entry) { m_list = listPrepend(m_list, entry); }
 };
 
+/**
+ * Wraps a C gchar array and takes care of freeing the memory.
+ */
+class PlainGStr : public boost::shared_ptr<gchar>
+{
+    public:
+        PlainGStr() {}
+        PlainGStr(gchar *str) : boost::shared_ptr<char>(str, g_free) {}
+        PlainGStr(const PlainGStr &other) : boost::shared_ptr<gchar>(other) {}    
+        operator const gchar *() const { return &**this; }
+        const gchar *c_str() const { return &**this; }
+};
+
 
 #endif
 
diff --git a/src/syncevo/eds_abi_wrapper.h b/src/syncevo/eds_abi_wrapper.h
index 69d56b7..451b0bf 100644
--- a/src/syncevo/eds_abi_wrapper.h
+++ b/src/syncevo/eds_abi_wrapper.h
@@ -52,11 +52,20 @@
 
 #ifdef HAVE_EDS
 #include <glib-object.h>
+#include <libedataserver/eds-version.h>
 #include <libedataserver/e-source.h>
 #include <libedataserver/e-source-list.h>
+#if EDS_CHECK_VERSION(3,1,0)
+#define USE_EBOOK_CLIENT 1
+#endif
 #ifdef ENABLE_EBOOK
+#ifdef USE_EBOOK_CLIENT
+#include <libebook/e-book-client.h>
+#else
 #include <libebook/e-book.h>
+#endif
 #include <libebook/e-vcard.h>
+#include <libebook/e-book-query.h>
 #endif
 #ifdef ENABLE_ECAL
 # define HANDLE_LIBICAL_MEMORY 1
-- 
1.7.6

_______________________________________________
SyncEvolution mailing list
[email protected]
http://lists.syncevolution.org/listinfo/syncevolution

Reply via email to