1087 lines
53 KiB
C++
1087 lines
53 KiB
C++
/*
|
|
* Copyright (C) 2012 Intel Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) version 3.
|
|
*
|
|
* 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 Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*/
|
|
|
|
/**
|
|
* The D-Bus IPC binding for folks.h. Maps FolksIndividual to and
|
|
* from the D-Bus dict described in pim-manager-api.txt.
|
|
*/
|
|
|
|
#include "individual-traits.h"
|
|
#include "persona-details.h"
|
|
#include "folks.h"
|
|
|
|
#include <boost/typeof/typeof.hpp>
|
|
|
|
SE_GLIB_TYPE(GDateTime, g_date_time)
|
|
SE_GOBJECT_TYPE(GTimeZone)
|
|
SE_GOBJECT_TYPE(GObject)
|
|
|
|
SE_BEGIN_CXX
|
|
|
|
typedef GValueDynTypedCXX<GDateTime *, g_date_time_get_type> GValueDateTimeCXX;
|
|
|
|
static const char * const CONTACT_HASH_FULL_NAME = "full-name";
|
|
static const char * const CONTACT_HASH_NICKNAME = "nickname";
|
|
static const char * const CONTACT_HASH_STRUCTURED_NAME = "structured-name";
|
|
static const char * const CONTACT_HASH_STRUCTURED_NAME_FAMILY = "family";
|
|
static const char * const CONTACT_HASH_STRUCTURED_NAME_GIVEN = "given";
|
|
static const char * const CONTACT_HASH_STRUCTURED_NAME_ADDITIONAL = "additional";
|
|
static const char * const CONTACT_HASH_STRUCTURED_NAME_PREFIXES = "prefixes";
|
|
static const char * const CONTACT_HASH_STRUCTURED_NAME_SUFFIXES = "suffixes";
|
|
static const char * const CONTACT_HASH_ALIAS = "alias";
|
|
static const char * const CONTACT_HASH_PHOTO = "photo";
|
|
static const char * const CONTACT_HASH_BIRTHDAY = "birthday";
|
|
static const char * const CONTACT_HASH_LOCATION = "location";
|
|
static const char * const CONTACT_HASH_EMAILS = "emails";
|
|
static const char * const CONTACT_HASH_PHONES = "phones";
|
|
static const char * const CONTACT_HASH_URLS = "urls";
|
|
static const char * const CONTACT_HASH_NOTES = "notes";
|
|
static const char * const CONTACT_HASH_ADDRESSES = "addresses";
|
|
static const char * const CONTACT_HASH_ADDRESSES_PO_BOX = "po-box";
|
|
static const char * const CONTACT_HASH_ADDRESSES_EXTENSION = "extension";
|
|
static const char * const CONTACT_HASH_ADDRESSES_STREET = "street";
|
|
static const char * const CONTACT_HASH_ADDRESSES_LOCALITY = "locality";
|
|
static const char * const CONTACT_HASH_ADDRESSES_REGION = "region";
|
|
static const char * const CONTACT_HASH_ADDRESSES_POSTAL_CODE = "postal-code";
|
|
static const char * const CONTACT_HASH_ADDRESSES_COUNTRY = "country";
|
|
static const char * const CONTACT_HASH_ROLES = "roles";
|
|
static const char * const CONTACT_HASH_ROLES_ORGANISATION = "organisation";
|
|
static const char * const CONTACT_HASH_ROLES_TITLE = "title";
|
|
static const char * const CONTACT_HASH_ROLES_ROLE = "role";
|
|
static const char * const CONTACT_HASH_GROUPS = "groups";
|
|
static const char * const CONTACT_HASH_SOURCE = "source";
|
|
static const char * const CONTACT_HASH_ID = "id";
|
|
|
|
static const char * const INDIVIDUAL_DICT = "a{sv}";
|
|
static const char * const INDIVIDUAL_DICT_ENTRY = "{sv}";
|
|
|
|
/**
|
|
* Checks whether a certain value is the default value and thus
|
|
* can be skipped when converting to D-Bus.
|
|
*
|
|
* class B provides additional type information, which is necessary
|
|
* to check a GeeSet containing FolksRoleFieldDetails differently
|
|
* than other GeeSets.
|
|
*/
|
|
template <class B> struct IsNonDefault
|
|
{
|
|
template<class V> static bool check(V value)
|
|
{
|
|
// Default version uses normal C/C++ rules, for example pointer non-NULL.
|
|
// For bool and integer, the value will only be sent if true or non-zero.
|
|
return value;
|
|
}
|
|
|
|
static bool check(const gchar *value)
|
|
{
|
|
// Don't send empty strings.
|
|
return value && value[0];
|
|
}
|
|
|
|
static bool check(GeeSet *value)
|
|
{
|
|
// Don't send empty sets.
|
|
return value && gee_collection_get_size(GEE_COLLECTION(value));
|
|
}
|
|
};
|
|
|
|
template <> struct IsNonDefault< GeeCollCXX<FolksRoleFieldDetails *> >
|
|
{
|
|
static bool check(GeeSet *value)
|
|
{
|
|
// Don't send empty set and set which contains only empty roles.
|
|
if (value) {
|
|
BOOST_FOREACH (FolksRoleFieldDetails *value, GeeCollCXX<FolksRoleFieldDetails *>(value, ADD_REF)) {
|
|
FolksRole *role = static_cast<FolksRole *>(const_cast<gpointer>((folks_abstract_field_details_get_value(FOLKS_ABSTRACT_FIELD_DETAILS(value)))));
|
|
if (IsNonDefault<const gchar *>::check(folks_role_get_organisation_name(role)) ||
|
|
IsNonDefault<const gchar *>::check(folks_role_get_title(role)) ||
|
|
IsNonDefault<const gchar *>::check(folks_role_get_role(role))) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Adds a dict entry to the builder, with 'key' as string key and the
|
|
* result of 'get()' as value.
|
|
*/
|
|
template <class O, class V> void SerializeFolks(GDBusCXX::builder_type &builder,
|
|
O *obj,
|
|
V (*get)(O *),
|
|
const char *key)
|
|
{
|
|
if (!obj) {
|
|
SE_THROW("casting to base class failed");
|
|
}
|
|
V value = get(obj);
|
|
|
|
if (IsNonDefault<V>::check(value)) {
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE(INDIVIDUAL_DICT_ENTRY)); // dict entry
|
|
GDBusCXX::dbus_traits<std::string>::append(builder, key);
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE("v")); // variant
|
|
GDBusCXX::dbus_traits<V>::append(builder, value);
|
|
g_variant_builder_close(&builder); // variant
|
|
g_variant_builder_close(&builder); // dict entry
|
|
}
|
|
}
|
|
|
|
template <class O, class V, class B> void SerializeFolks(GDBusCXX::builder_type &builder,
|
|
O *obj,
|
|
V (*get)(O *),
|
|
B *, // dummy parameter, determines base type
|
|
const char *key)
|
|
{
|
|
if (!obj) {
|
|
SE_THROW("casting to base class failed");
|
|
}
|
|
V value = get(obj);
|
|
B coll(value, ADD_REF);
|
|
|
|
if (IsNonDefault<B>::check(value)) {
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE(INDIVIDUAL_DICT_ENTRY)); // dict entry
|
|
GDBusCXX::dbus_traits<std::string>::append(builder, key);
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE("v")); // variant
|
|
GDBusCXX::dbus_traits<B>::append(builder, coll);
|
|
g_variant_builder_close(&builder); // variant
|
|
g_variant_builder_close(&builder); // dict entry
|
|
}
|
|
}
|
|
|
|
SE_END_CXX
|
|
|
|
namespace GDBusCXX {
|
|
|
|
template <> struct dbus_traits<FolksStructuredName *> {
|
|
static void append(builder_type &builder, FolksStructuredName *value) {
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE(INDIVIDUAL_DICT)); // dict
|
|
SyncEvo::SerializeFolks(builder, value, folks_structured_name_get_family_name, CONTACT_HASH_STRUCTURED_NAME_FAMILY);
|
|
SyncEvo::SerializeFolks(builder, value, folks_structured_name_get_given_name, CONTACT_HASH_STRUCTURED_NAME_GIVEN);
|
|
SyncEvo::SerializeFolks(builder, value, folks_structured_name_get_additional_names, CONTACT_HASH_STRUCTURED_NAME_ADDITIONAL);
|
|
SyncEvo::SerializeFolks(builder, value, folks_structured_name_get_prefixes, CONTACT_HASH_STRUCTURED_NAME_PREFIXES);
|
|
SyncEvo::SerializeFolks(builder, value, folks_structured_name_get_suffixes, CONTACT_HASH_STRUCTURED_NAME_SUFFIXES);
|
|
g_variant_builder_close(&builder); // dict
|
|
}
|
|
};
|
|
|
|
/** Path to icon (the expected case) or uninitialized string (not set or not a file). */
|
|
static InitStateString ExtractFilePath(GLoadableIcon *value)
|
|
{
|
|
if (value &&
|
|
G_IS_FILE_ICON(value)) {
|
|
GFileIcon *fileIcon = G_FILE_ICON(value);
|
|
GFile *file = g_file_icon_get_file(fileIcon);
|
|
if (file) {
|
|
PlainGStr uri(g_file_get_uri(file));
|
|
// Have a path.
|
|
return InitStateString(uri.get(), true);
|
|
}
|
|
}
|
|
// No path.
|
|
return InitStateString();
|
|
}
|
|
|
|
template <> struct dbus_traits<GLoadableIcon *> {
|
|
static void append(builder_type &builder, GLoadableIcon *value) {
|
|
InitStateString path = ExtractFilePath(value);
|
|
// EDS is expected to only work with URIs for the PHOTO
|
|
// property, therefore we shouldn't get here without a valid
|
|
// path. Either way, we need to store something.
|
|
GDBusCXX::dbus_traits<const char *>::append(builder, path.c_str());
|
|
}
|
|
};
|
|
|
|
template <> struct dbus_traits<FolksPostalAddress *> {
|
|
static void append(builder_type &builder, FolksPostalAddress *value) {
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE(INDIVIDUAL_DICT)); // dict
|
|
SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_po_box, CONTACT_HASH_ADDRESSES_PO_BOX);
|
|
SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_extension, CONTACT_HASH_ADDRESSES_EXTENSION);
|
|
SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_street, CONTACT_HASH_ADDRESSES_STREET);
|
|
SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_locality, CONTACT_HASH_ADDRESSES_LOCALITY);
|
|
SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_region, CONTACT_HASH_ADDRESSES_REGION);
|
|
SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_postal_code, CONTACT_HASH_ADDRESSES_POSTAL_CODE);
|
|
SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_country, CONTACT_HASH_ADDRESSES_COUNTRY);
|
|
|
|
// Not used by EDS. The tracker backend in folks was able to provide such a uid.
|
|
// SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_address_format, CONTACT_HASH_ADDRESSES_FORMAT);
|
|
// SyncEvo::SerializeFolks(builder, value, folks_postal_address_get_uid, "uid");
|
|
g_variant_builder_close(&builder); // dict
|
|
}
|
|
};
|
|
|
|
template <> struct dbus_traits<FolksRole *> {
|
|
static void append(builder_type &builder, FolksRole *value) {
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE(INDIVIDUAL_DICT)); // dict
|
|
// Other parts of ORG are not currently supported by libfolks.
|
|
SyncEvo::SerializeFolks(builder, value, folks_role_get_organisation_name, CONTACT_HASH_ROLES_ORGANISATION);
|
|
SyncEvo::SerializeFolks(builder, value, folks_role_get_title, CONTACT_HASH_ROLES_TITLE);
|
|
SyncEvo::SerializeFolks(builder, value, folks_role_get_role, CONTACT_HASH_ROLES_ROLE);
|
|
// Not used:
|
|
// SyncEvo::SerializeFolks(builder, value, folks_role_get_uid, "uid");
|
|
g_variant_builder_close(&builder); // dict
|
|
}
|
|
};
|
|
|
|
// TODO: put into global header file
|
|
static const char * const MANAGER_PREFIX = "pim-manager-";
|
|
|
|
template <> struct dbus_traits<FolksPersona *> {
|
|
static void append(builder_type &builder, FolksPersona *value) {
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE("(ss)")); // pair of peer ID and local ID
|
|
const gchar *uid = folks_persona_get_uid(value);
|
|
if (uid) {
|
|
gchar *backend, *storeID, *personaID;
|
|
folks_persona_split_uid(uid, &backend, &storeID, &personaID);
|
|
PlainGStr tmp1(backend), tmp2(storeID), tmp3(personaID);
|
|
if (boost::starts_with(storeID, MANAGER_PREFIX)) {
|
|
dbus_traits<const char *>::append(builder, storeID + strlen(MANAGER_PREFIX));
|
|
} else {
|
|
// Must be the system address book.
|
|
dbus_traits<const char *>::append(builder, "");
|
|
}
|
|
dbus_traits<const char *>::append(builder, personaID);
|
|
} else {
|
|
dbus_traits<const char *>::append(builder, "");
|
|
dbus_traits<const char *>::append(builder, "");
|
|
}
|
|
g_variant_builder_close(&builder); // pair
|
|
}
|
|
};
|
|
|
|
// Only use this with FolksAbstractFieldDetails instances where
|
|
// the value is a string.
|
|
template <> struct dbus_traits<FolksAbstractFieldDetails *> {
|
|
static void append(builder_type &builder, FolksAbstractFieldDetails *value) {
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE("(sas)")); // pair of string and string list
|
|
gconstpointer v = folks_abstract_field_details_get_value(value);
|
|
dbus_traits<const char *>::append(builder, v ? (const char *)v : "");
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE("as"));
|
|
GeeMultiMap *map = folks_abstract_field_details_get_parameters(value);
|
|
if (map) {
|
|
GeeCollCXX<const char *> coll(gee_multi_map_get(map, FOLKS_ABSTRACT_FIELD_DETAILS_PARAM_TYPE), TRANSFER_REF);
|
|
BOOST_FOREACH (const char *type, coll) {
|
|
dbus_traits<const char *>::append(builder, type);
|
|
}
|
|
}
|
|
g_variant_builder_close(&builder); // string list
|
|
g_variant_builder_close(&builder); // pair
|
|
}
|
|
};
|
|
|
|
template <> struct dbus_traits<FolksPostalAddressFieldDetails *> {
|
|
static void append(builder_type &builder, FolksPostalAddressFieldDetails *value) {
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE(StringPrintf("(%sas)", INDIVIDUAL_DICT).c_str())); // pair of dict and string list
|
|
FolksAbstractFieldDetails *fieldDetails = FOLKS_ABSTRACT_FIELD_DETAILS(value);
|
|
gconstpointer v = folks_abstract_field_details_get_value(fieldDetails);
|
|
dbus_traits<FolksPostalAddress *>::append(builder, static_cast<FolksPostalAddress *>(const_cast<gpointer>(v)));
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE("as"));
|
|
GeeMultiMap *map = folks_abstract_field_details_get_parameters(fieldDetails);
|
|
if (map) {
|
|
GeeCollCXX<const char *> coll(gee_multi_map_get(map, "type"), TRANSFER_REF);
|
|
BOOST_FOREACH (const char *type, coll) {
|
|
dbus_traits<const char *>::append(builder, type);
|
|
}
|
|
}
|
|
g_variant_builder_close(&builder); // string list
|
|
g_variant_builder_close(&builder); // pair
|
|
}
|
|
};
|
|
|
|
template <> struct dbus_traits<FolksNoteFieldDetails *> {
|
|
static void append(builder_type &builder, FolksNoteFieldDetails *value) {
|
|
FolksAbstractFieldDetails *fieldDetails = FOLKS_ABSTRACT_FIELD_DETAILS(value);
|
|
gconstpointer v = folks_abstract_field_details_get_value(fieldDetails);
|
|
dbus_traits<const char *>::append(builder, static_cast<const char *>(v)); // plain string
|
|
// Ignore parameters. LANGUAGE is hardly ever set.
|
|
}
|
|
};
|
|
|
|
template <> struct dbus_traits<FolksRoleFieldDetails *> {
|
|
static void append(builder_type &builder, FolksRoleFieldDetails *value) {
|
|
FolksAbstractFieldDetails *fieldDetails = FOLKS_ABSTRACT_FIELD_DETAILS(value);
|
|
gconstpointer v = folks_abstract_field_details_get_value(fieldDetails);
|
|
dbus_traits<FolksRole *>::append(builder, static_cast<FolksRole *>(const_cast<gpointer>((v))));
|
|
// Ignore parameters. LANGUAGE is hardly ever set.
|
|
}
|
|
};
|
|
|
|
// Used for types like V = FolksEmailFieldDetails *
|
|
template <class V> struct dbus_traits< GeeCollCXX<V> > {
|
|
static void append(builder_type &builder, const GeeCollCXX<V> &collection) {
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE("av")); // array of variants
|
|
BOOST_FOREACH (V value, collection) {
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE("v")); // variant
|
|
dbus_traits<V>::append(builder, value);
|
|
g_variant_builder_close(&builder); // variant
|
|
}
|
|
g_variant_builder_close(&builder); // array of variants
|
|
}
|
|
};
|
|
|
|
template <> struct dbus_traits<GDateTime *> {
|
|
static void append(builder_type &builder, GDateTime *value) {
|
|
// Extract local date from UTC date + time + UTC offset.
|
|
//
|
|
// The libfolks EDS backend does date + 00:00 in local time,
|
|
// then converts to UTC. We need to hard-code the stripping
|
|
// of the time. Folks should make it easier to extract the date, see
|
|
// https://bugzilla.gnome.org/show_bug.cgi?id=684905
|
|
//
|
|
// TODO: if folks doesn't get fixed, then we should cache the
|
|
// GTimeZone local = g_time_zone_new_local()
|
|
// and use that throughout the runtime of the process, like
|
|
// folks-eds does.
|
|
GDateTimeCXX local(g_date_time_to_local(value), TRANSFER_REF);
|
|
gint year, month, day;
|
|
g_date_time_get_ymd(local.get(), &year, &month, &day);
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE("(iii)")); // tuple with year, month, day
|
|
GDBusCXX::dbus_traits<int>::append(builder, year);
|
|
GDBusCXX::dbus_traits<int>::append(builder, month);
|
|
GDBusCXX::dbus_traits<int>::append(builder, day);
|
|
g_variant_builder_close(&builder); // tuple
|
|
}
|
|
};
|
|
|
|
template <> struct dbus_traits<FolksLocation *> {
|
|
static void append(builder_type &builder, FolksLocation *value) {
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE("(dd)")); // tuple with latitude + longitude
|
|
GDBusCXX::dbus_traits<double>::append(builder, value->latitude);
|
|
GDBusCXX::dbus_traits<double>::append(builder, value->longitude);
|
|
g_variant_builder_close(&builder); // tuple
|
|
}
|
|
};
|
|
|
|
} // namespace GDBusCXX
|
|
|
|
SE_BEGIN_CXX
|
|
|
|
static guint FolksAbstractFieldDetailsHash(gconstpointer v, void *d)
|
|
{
|
|
return folks_abstract_field_details_hash((FolksAbstractFieldDetails *)v);
|
|
}
|
|
static gboolean FolksAbstractFieldDetailsEqual(gconstpointer a, gconstpointer b, void *d)
|
|
{
|
|
return folks_abstract_field_details_equal((FolksAbstractFieldDetails *)a,
|
|
(FolksAbstractFieldDetails *)b);
|
|
}
|
|
|
|
/**
|
|
* Copy from D-Bus into a type derived from FolksAbstractFieldDetails,
|
|
* including type flags.
|
|
*/
|
|
static void DBus2AbstractField(GDBusCXX::ExtractArgs &context,
|
|
GVariantIter &valueIter,
|
|
PersonaDetails &details,
|
|
GType detailType,
|
|
FolksPersonaDetail detail,
|
|
FolksAbstractFieldDetails *(*fieldNew)(const gchar *, GeeMultiMap *))
|
|
{
|
|
// type of emails, urls, etc.
|
|
typedef std::vector< std::pair< std::string, std::vector<std::string> > > Details_t;
|
|
|
|
Details_t value;
|
|
GDBusCXX::dbus_traits<BOOST_TYPEOF(value)>::get(context, valueIter, value);
|
|
GeeHashSetCXX set(gee_hash_set_new(detailType,
|
|
g_object_ref,
|
|
g_object_unref,
|
|
FolksAbstractFieldDetailsHash, NULL, NULL,
|
|
FolksAbstractFieldDetailsEqual, NULL, NULL),
|
|
TRANSFER_REF);
|
|
BOOST_FOREACH (const Details_t::value_type &entry, value) {
|
|
const Details_t::value_type::first_type &val = entry.first;
|
|
const Details_t::value_type::second_type &flags = entry.second;
|
|
FolksAbstractFieldDetailsCXX field(fieldNew(val.c_str(), NULL), TRANSFER_REF);
|
|
BOOST_FOREACH (const std::string &type, flags) {
|
|
folks_abstract_field_details_add_parameter(field.get(),
|
|
FOLKS_ABSTRACT_FIELD_DETAILS_PARAM_TYPE,
|
|
type.c_str());
|
|
}
|
|
gee_collection_add(GEE_COLLECTION(set.get()),
|
|
field.get());
|
|
}
|
|
g_hash_table_insert(details.get(),
|
|
const_cast<gchar *>(folks_persona_store_detail_key(detail)),
|
|
new GValueObjectCXX(set.get()));
|
|
}
|
|
|
|
/**
|
|
* Copy from D-Bus into a type derived from FolksAbstractFieldDetails,
|
|
* excluding type flags.
|
|
*/
|
|
static void DBus2SimpleAbstractField(GDBusCXX::ExtractArgs &context,
|
|
GVariantIter &valueIter,
|
|
PersonaDetails &details,
|
|
GType detailType,
|
|
FolksPersonaDetail detail,
|
|
FolksAbstractFieldDetails *(*fieldNew)(const gchar *, GeeMultiMap *))
|
|
{
|
|
// type of notes
|
|
typedef std::vector<std::string> Details_t;
|
|
|
|
Details_t value;
|
|
GDBusCXX::dbus_traits<BOOST_TYPEOF(value)>::get(context, valueIter, value);
|
|
GeeHashSetCXX set(gee_hash_set_new(detailType,
|
|
g_object_ref,
|
|
g_object_unref,
|
|
FolksAbstractFieldDetailsHash, NULL, NULL,
|
|
FolksAbstractFieldDetailsEqual, NULL, NULL),
|
|
TRANSFER_REF);
|
|
BOOST_FOREACH (const std::string &val, value) {
|
|
FolksAbstractFieldDetailsCXX field(fieldNew(val.c_str(), NULL), TRANSFER_REF);
|
|
gee_collection_add(GEE_COLLECTION(set.get()),
|
|
field.get());
|
|
}
|
|
g_hash_table_insert(details.get(),
|
|
const_cast<gchar *>(folks_persona_store_detail_key(detail)),
|
|
new GValueObjectCXX(set.get()));
|
|
}
|
|
|
|
/**
|
|
* Copy from D-Bus into FolksRoleFieldDetails.
|
|
*/
|
|
static void DBus2Role(GDBusCXX::ExtractArgs &context,
|
|
GVariantIter &valueIter,
|
|
PersonaDetails &details)
|
|
{
|
|
// type of role
|
|
typedef std::vector<StringMap> Details_t;
|
|
|
|
Details_t value;
|
|
GDBusCXX::dbus_traits<BOOST_TYPEOF(value)>::get(context, valueIter, value);
|
|
GeeHashSetCXX set(gee_hash_set_new(FOLKS_TYPE_ROLE_FIELD_DETAILS,
|
|
g_object_ref,
|
|
g_object_unref,
|
|
FolksAbstractFieldDetailsHash, NULL, NULL,
|
|
FolksAbstractFieldDetailsEqual, NULL, NULL),
|
|
TRANSFER_REF);
|
|
BOOST_FOREACH (const StringMap &entry, value) {
|
|
FolksRoleCXX role(folks_role_new(NULL, NULL, NULL),
|
|
TRANSFER_REF);
|
|
BOOST_FOREACH (const StringPair &aspect, entry) {
|
|
const std::string &k = aspect.first;
|
|
const std::string &v = aspect.second;
|
|
if (k == CONTACT_HASH_ROLES_ORGANISATION) {
|
|
folks_role_set_organisation_name(role, v.c_str());
|
|
} else if (k == CONTACT_HASH_ROLES_TITLE) {
|
|
folks_role_set_title(role, v.c_str());
|
|
} else if (k == CONTACT_HASH_ROLES_ROLE) {
|
|
folks_role_set_role(role, v.c_str());
|
|
}
|
|
}
|
|
FolksRoleFieldDetailsCXX field(folks_role_field_details_new(role.get(), NULL),
|
|
TRANSFER_REF);
|
|
gee_collection_add(GEE_COLLECTION(set.get()),
|
|
field.get());
|
|
}
|
|
g_hash_table_insert(details.get(),
|
|
const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_ROLES)),
|
|
new GValueObjectCXX(set.get()));
|
|
}
|
|
|
|
/**
|
|
* Copy from D-Bus into a set of strings.
|
|
*/
|
|
static void DBus2Groups(GDBusCXX::ExtractArgs &context,
|
|
GVariantIter &valueIter,
|
|
PersonaDetails &details)
|
|
{
|
|
std::list<std::string> value;
|
|
GDBusCXX::dbus_traits<BOOST_TYPEOF(value)>::get(context, valueIter, value);
|
|
GeeHashSetCXX set(gee_hash_set_new(G_TYPE_STRING, (GBoxedCopyFunc)g_strdup, g_free, NULL, NULL, NULL, NULL, NULL, NULL), TRANSFER_REF);
|
|
BOOST_FOREACH(const std::string &entry, value) {
|
|
gee_collection_add(GEE_COLLECTION(set.get()),
|
|
entry.c_str());
|
|
}
|
|
g_hash_table_insert(details.get(),
|
|
const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_GROUPS)),
|
|
new GValueObjectCXX(set.get()));
|
|
}
|
|
|
|
/**
|
|
* Copy from D-Bus into a FolksAddressFieldDetails.
|
|
*/
|
|
static void DBus2Addr(GDBusCXX::ExtractArgs &context,
|
|
GVariantIter &valueIter,
|
|
PersonaDetails &details)
|
|
{
|
|
// type of CONTACT_HASH_ADDRESSES
|
|
typedef std::vector< std::pair< StringMap, std::vector<std::string> > > Details_t;
|
|
|
|
Details_t value;
|
|
GDBusCXX::dbus_traits<BOOST_TYPEOF(value)>::get(context, valueIter, value);
|
|
GeeHashSetCXX set(gee_hash_set_new(FOLKS_TYPE_POSTAL_ADDRESS_FIELD_DETAILS,
|
|
g_object_ref,
|
|
g_object_unref,
|
|
FolksAbstractFieldDetailsHash, NULL, NULL,
|
|
FolksAbstractFieldDetailsEqual, NULL, NULL),
|
|
TRANSFER_REF);
|
|
BOOST_FOREACH (const Details_t::value_type &entry, value) {
|
|
const StringMap &fields = entry.first;
|
|
const std::vector<std::string> &flags = entry.second;
|
|
FolksPostalAddressCXX address(folks_postal_address_new(GetWithDef(fields, CONTACT_HASH_ADDRESSES_PO_BOX).c_str(),
|
|
GetWithDef(fields, CONTACT_HASH_ADDRESSES_EXTENSION).c_str(),
|
|
GetWithDef(fields, CONTACT_HASH_ADDRESSES_STREET).c_str(),
|
|
GetWithDef(fields, CONTACT_HASH_ADDRESSES_LOCALITY).c_str(),
|
|
GetWithDef(fields, CONTACT_HASH_ADDRESSES_REGION).c_str(),
|
|
GetWithDef(fields, CONTACT_HASH_ADDRESSES_POSTAL_CODE).c_str(),
|
|
GetWithDef(fields, CONTACT_HASH_ADDRESSES_COUNTRY).c_str(),
|
|
NULL /* address format */,
|
|
NULL /* uid */),
|
|
TRANSFER_REF);
|
|
FolksAbstractFieldDetailsCXX field(FOLKS_ABSTRACT_FIELD_DETAILS(folks_postal_address_field_details_new(address.get(), NULL)),
|
|
TRANSFER_REF);
|
|
BOOST_FOREACH (const std::string &type, flags) {
|
|
folks_abstract_field_details_add_parameter(field.get(),
|
|
FOLKS_ABSTRACT_FIELD_DETAILS_PARAM_TYPE,
|
|
type.c_str());
|
|
}
|
|
gee_collection_add(GEE_COLLECTION(set.get()),
|
|
field.get());
|
|
}
|
|
g_hash_table_insert(details.get(),
|
|
const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_POSTAL_ADDRESSES)),
|
|
new GValueObjectCXX(set.get()));
|
|
}
|
|
|
|
static FolksAbstractFieldDetails *my_folks_note_field_details_new_wrapper(const gchar *value, GeeMultiMap *parameters) { return FOLKS_ABSTRACT_FIELD_DETAILS(folks_note_field_details_new(value, parameters, NULL)); }
|
|
|
|
void DBus2PersonaDetails(GDBusCXX::ExtractArgs &context,
|
|
GDBusCXX::reader_type &iter,
|
|
PersonaDetails &details)
|
|
{
|
|
GDBusCXX::GVariantCXX var(g_variant_iter_next_value(&iter));
|
|
if (var == NULL || !g_variant_type_is_subtype_of(g_variant_get_type(var), G_VARIANT_TYPE_ARRAY)) {
|
|
SE_THROW("D-Bus contact hash: unexpected content");
|
|
}
|
|
|
|
GVariantIter contIter;
|
|
GDBusCXX::GVariantCXX child;
|
|
g_variant_iter_init(&contIter, var); // array
|
|
while((child = g_variant_iter_next_value(&contIter)) != NULL) {
|
|
GVariantIter childIter;
|
|
g_variant_iter_init(&childIter, child); // dict entry
|
|
std::string key;
|
|
GDBusCXX::dbus_traits<std::string>::get(context, childIter, key);
|
|
GDBusCXX::GVariantCXX valueVarient(g_variant_iter_next_value(&childIter));
|
|
if (valueVarient == NULL || !g_variant_type_equal(g_variant_get_type(valueVarient), G_VARIANT_TYPE_VARIANT)) {
|
|
SE_THROW("D-Bus contact hash entry: value must be variant");
|
|
}
|
|
|
|
GVariantIter valueIter;
|
|
g_variant_iter_init(&valueIter, valueVarient);
|
|
if (key == CONTACT_HASH_FULL_NAME) {
|
|
std::string value;
|
|
GDBusCXX::dbus_traits<std::string>::get(context, valueIter, value);
|
|
g_hash_table_insert(details.get(),
|
|
const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_FULL_NAME)),
|
|
new GValueStringCXX(value.c_str()));
|
|
} else if (key == CONTACT_HASH_STRUCTURED_NAME) {
|
|
StringMap value;
|
|
GDBusCXX::dbus_traits<StringMap>::get(context, valueIter, value);
|
|
g_hash_table_insert(details.get(),
|
|
const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_STRUCTURED_NAME)),
|
|
new GValueObjectCXX(folks_structured_name_new(GetWithDef(value, CONTACT_HASH_STRUCTURED_NAME_FAMILY).c_str(),
|
|
GetWithDef(value, CONTACT_HASH_STRUCTURED_NAME_GIVEN).c_str(),
|
|
GetWithDef(value, CONTACT_HASH_STRUCTURED_NAME_ADDITIONAL).c_str(),
|
|
GetWithDef(value, CONTACT_HASH_STRUCTURED_NAME_PREFIXES).c_str(),
|
|
GetWithDef(value, CONTACT_HASH_STRUCTURED_NAME_SUFFIXES).c_str()),
|
|
TRANSFER_REF));
|
|
} else if (key == CONTACT_HASH_PHOTO) {
|
|
std::string value;
|
|
GDBusCXX::dbus_traits<std::string>::get(context, valueIter, value);
|
|
GFileCXX file(g_file_new_for_uri(value.c_str()),
|
|
TRANSFER_REF);
|
|
g_hash_table_insert(details.get(),
|
|
const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_AVATAR)),
|
|
new GValueObjectCXX(g_file_icon_new(file.get()), TRANSFER_REF));
|
|
} else if (key == CONTACT_HASH_BIRTHDAY) {
|
|
boost::tuple<int, int, int> value;
|
|
GDBusCXX::dbus_traits<BOOST_TYPEOF(value)>::get(context, valueIter, value);
|
|
GDateTimeCXX local(g_date_time_new_local(value.get<0>(),
|
|
value.get<1>(),
|
|
value.get<2>(),
|
|
0, 0, 0),
|
|
TRANSFER_REF);
|
|
g_hash_table_insert(details.get(),
|
|
const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_BIRTHDAY)),
|
|
new GValueDateTimeCXX(g_date_time_to_utc(local.get()), TRANSFER_REF));
|
|
} else if (key == CONTACT_HASH_LOCATION) {
|
|
boost::tuple<double, double> value;
|
|
GDBusCXX::dbus_traits<BOOST_TYPEOF(value)>::get(context, valueIter, value);
|
|
FolksLocationCXX location(folks_location_new(value.get<0>(),
|
|
value.get<1>()),
|
|
TRANSFER_REF);
|
|
g_hash_table_insert(details.get(),
|
|
const_cast<gchar *>(folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_LOCATION)),
|
|
new GValueObjectCXX(location.get()));
|
|
} else if (key == CONTACT_HASH_EMAILS) {
|
|
DBus2AbstractField(context, valueIter, details,
|
|
FOLKS_TYPE_EMAIL_FIELD_DETAILS,
|
|
FOLKS_PERSONA_DETAIL_EMAIL_ADDRESSES,
|
|
reinterpret_cast<FolksAbstractFieldDetails *(*)(const gchar *, GeeMultiMap *)>(folks_email_field_details_new));
|
|
} else if (key == CONTACT_HASH_PHONES) {
|
|
DBus2AbstractField(context, valueIter, details,
|
|
FOLKS_TYPE_PHONE_FIELD_DETAILS,
|
|
FOLKS_PERSONA_DETAIL_PHONE_NUMBERS,
|
|
reinterpret_cast<FolksAbstractFieldDetails *(*)(const gchar *, GeeMultiMap *)>(folks_phone_field_details_new));
|
|
} else if (key == CONTACT_HASH_URLS) {
|
|
DBus2AbstractField(context, valueIter, details,
|
|
FOLKS_TYPE_URL_FIELD_DETAILS,
|
|
FOLKS_PERSONA_DETAIL_URLS,
|
|
reinterpret_cast<FolksAbstractFieldDetails *(*)(const gchar *, GeeMultiMap *)>(folks_url_field_details_new));
|
|
} else if (key == CONTACT_HASH_NOTES) {
|
|
DBus2SimpleAbstractField(context, valueIter, details,
|
|
FOLKS_TYPE_NOTE_FIELD_DETAILS,
|
|
FOLKS_PERSONA_DETAIL_NOTES,
|
|
my_folks_note_field_details_new_wrapper);
|
|
} else if (key == CONTACT_HASH_ROLES) {
|
|
DBus2Role(context, valueIter, details);
|
|
} else if (key == CONTACT_HASH_GROUPS) {
|
|
DBus2Groups(context, valueIter, details);
|
|
} else if (key == CONTACT_HASH_ADDRESSES) {
|
|
DBus2Addr(context, valueIter, details);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Pending
|
|
{
|
|
Pending(const Result<void ()> &result,
|
|
FolksPersona *persona) :
|
|
m_result(result),
|
|
m_persona(persona, ADD_REF),
|
|
m_current(0)
|
|
{}
|
|
|
|
Result<void ()> m_result;
|
|
FolksPersonaCXX m_persona;
|
|
typedef boost::function<void (const GError *)> AsyncDone;
|
|
typedef boost::function<void (AsyncDone *)> Prepare;
|
|
typedef boost::tuple<Prepare,
|
|
const char *,
|
|
boost::shared_ptr<void> > Change;
|
|
typedef std::vector<Change> Changes;
|
|
Changes m_changes;
|
|
size_t m_current;
|
|
};
|
|
|
|
static bool GeeCollectionEqual(GeeCollection *a, GeeCollection *b)
|
|
{
|
|
return gee_collection_get_size(a) == gee_collection_get_size(b) &&
|
|
gee_collection_contains_all(a, b);
|
|
}
|
|
|
|
/**
|
|
* Gets called by event loop. All errors must be reported back to the caller.
|
|
*/
|
|
static void Details2PersonaStep(const GError *gerror, const boost::shared_ptr<Pending> &pending) throw ()
|
|
{
|
|
try {
|
|
if (gerror) {
|
|
GErrorCXX::throwError(SE_HERE, "modifying property", gerror);
|
|
}
|
|
size_t current = pending->m_current++;
|
|
if (current < pending->m_changes.size()) {
|
|
// send next change, as determined earlier
|
|
Pending::Change &change = pending->m_changes[current];
|
|
SE_LOG_DEBUG(NULL, "modification step %d/%d: %s",
|
|
(int)current,
|
|
(int)pending->m_changes.size(),
|
|
boost::get<1>(change));
|
|
boost::get<0>(change)(new Pending::AsyncDone(boost::bind(Details2PersonaStep, _1, pending)));
|
|
} else {
|
|
pending->m_result.done();
|
|
}
|
|
} catch (...) {
|
|
// Tell caller about specific error.
|
|
pending->m_result.failed();
|
|
}
|
|
}
|
|
|
|
void Details2Persona(const Result<void ()> &result, const PersonaDetails &details, FolksPersona *persona)
|
|
{
|
|
boost::shared_ptr<Pending> pending(new Pending(result, persona));
|
|
|
|
#define PUSH_CHANGE(_prepare) \
|
|
SE_LOG_DEBUG(NULL, "queueing new change: %s", #_prepare); \
|
|
pending->m_changes.push_back(Pending::Change(boost::bind(_prepare, \
|
|
details, \
|
|
value, \
|
|
SYNCEVO_GLIB_CALL_ASYNC_CXX(_prepare)::handleGLibResult, \
|
|
_1), \
|
|
#_prepare, \
|
|
tracker))
|
|
|
|
const GValue *gvalue;
|
|
boost::shared_ptr<void> tracker;
|
|
gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(),
|
|
folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_FULL_NAME)));
|
|
{
|
|
const gchar *value;
|
|
if (gvalue) {
|
|
value = g_value_get_string(gvalue);
|
|
tracker = boost::shared_ptr<void>(g_strdup(value), g_free);
|
|
} else {
|
|
value = NULL;
|
|
}
|
|
FolksNameDetails *details = FOLKS_NAME_DETAILS(persona);
|
|
if (g_strcmp0(value, folks_name_details_get_full_name(details))) {
|
|
PUSH_CHANGE(folks_name_details_change_full_name);
|
|
}
|
|
}
|
|
|
|
|
|
gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_STRUCTURED_NAME)));
|
|
{
|
|
FolksStructuredName *value;
|
|
if (gvalue) {
|
|
value = FOLKS_STRUCTURED_NAME(g_value_get_object(gvalue));
|
|
tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
|
|
} else {
|
|
tracker = boost::shared_ptr<void>(folks_structured_name_new("", "", "", "", ""), g_object_unref);
|
|
value = static_cast<FolksStructuredName *>(tracker.get());
|
|
}
|
|
FolksNameDetails *details = FOLKS_NAME_DETAILS(persona);
|
|
if (!folks_structured_name_equal(value, folks_name_details_get_structured_name(details))) {
|
|
PUSH_CHANGE(folks_name_details_change_structured_name);
|
|
}
|
|
}
|
|
|
|
gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_AVATAR)));
|
|
{
|
|
FolksAvatarDetails *details = FOLKS_AVATAR_DETAILS(persona);
|
|
GLoadableIcon *value;
|
|
if (gvalue) {
|
|
value = G_LOADABLE_ICON(g_value_get_object(gvalue));
|
|
tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
|
|
} else {
|
|
tracker = boost::shared_ptr<void>();
|
|
value = NULL;
|
|
}
|
|
InitStateString newPath = GDBusCXX::ExtractFilePath(value);
|
|
InitStateString oldPath = GDBusCXX::ExtractFilePath(folks_avatar_details_get_avatar(details));
|
|
if (newPath.wasSet() != oldPath.wasSet() ||
|
|
newPath != oldPath) {
|
|
PUSH_CHANGE(folks_avatar_details_change_avatar);
|
|
}
|
|
}
|
|
|
|
gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_BIRTHDAY)));
|
|
{
|
|
GDateTime *value;
|
|
if (gvalue) {
|
|
value = static_cast<GDateTime *>(g_value_get_boxed(gvalue));
|
|
tracker = boost::shared_ptr<void>(g_date_time_ref(value), g_date_time_unref);
|
|
} else {
|
|
tracker = boost::shared_ptr<void>();
|
|
value = NULL;
|
|
}
|
|
FolksBirthdayDetails *details = FOLKS_BIRTHDAY_DETAILS(persona);
|
|
GDateTime *old = folks_birthday_details_get_birthday(details);
|
|
if ((value == NULL && old != NULL) ||
|
|
(value != NULL && old == NULL) ||
|
|
!g_date_time_equal(value, old)) {
|
|
PUSH_CHANGE(folks_birthday_details_change_birthday);
|
|
}
|
|
}
|
|
|
|
gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_LOCATION)));
|
|
{
|
|
FolksLocation *value;
|
|
if (gvalue) {
|
|
value = static_cast<FolksLocation *>(g_value_get_object(gvalue));
|
|
tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
|
|
} else {
|
|
tracker = boost::shared_ptr<void>();
|
|
value = NULL;
|
|
}
|
|
FolksLocationDetails *details = FOLKS_LOCATION_DETAILS(persona);
|
|
FolksLocation *old = folks_location_details_get_location(details);
|
|
if ((value == NULL && old != NULL) ||
|
|
(value != NULL && old == NULL) ||
|
|
(value && old &&
|
|
(value->latitude != old->latitude ||
|
|
value->longitude != old->longitude))) {
|
|
PUSH_CHANGE(folks_location_details_change_location);
|
|
}
|
|
}
|
|
|
|
gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_EMAIL_ADDRESSES)));
|
|
{
|
|
GeeSet *value;
|
|
if (gvalue) {
|
|
value = GEE_SET(g_value_get_object(gvalue));
|
|
tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
|
|
} else {
|
|
tracker = boost::shared_ptr<void>(gee_hash_set_new(FOLKS_TYPE_EMAIL_FIELD_DETAILS,
|
|
g_object_ref,
|
|
g_object_unref,
|
|
FolksAbstractFieldDetailsHash, NULL, NULL,
|
|
FolksAbstractFieldDetailsEqual, NULL, NULL),
|
|
g_object_unref);
|
|
value = static_cast<GeeSet *>(tracker.get());
|
|
}
|
|
FolksEmailDetails *details = FOLKS_EMAIL_DETAILS(persona);
|
|
if (!GeeCollectionEqual(GEE_COLLECTION(value),
|
|
GEE_COLLECTION(folks_email_details_get_email_addresses(details)))) {
|
|
PUSH_CHANGE(folks_email_details_change_email_addresses);
|
|
}
|
|
}
|
|
|
|
gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_PHONE_NUMBERS)));
|
|
{
|
|
GeeSet *value;
|
|
if (gvalue) {
|
|
value = GEE_SET(g_value_get_object(gvalue));
|
|
tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
|
|
} else {
|
|
tracker = boost::shared_ptr<void>(gee_hash_set_new(FOLKS_TYPE_PHONE_FIELD_DETAILS,
|
|
g_object_ref,
|
|
g_object_unref,
|
|
FolksAbstractFieldDetailsHash, NULL, NULL,
|
|
FolksAbstractFieldDetailsEqual, NULL, NULL),
|
|
g_object_unref);
|
|
value = static_cast<GeeSet *>(tracker.get());
|
|
}
|
|
FolksPhoneDetails *details = FOLKS_PHONE_DETAILS(persona);
|
|
if (!GeeCollectionEqual(GEE_COLLECTION(value),
|
|
GEE_COLLECTION(folks_phone_details_get_phone_numbers(details)))) {
|
|
PUSH_CHANGE(folks_phone_details_change_phone_numbers);
|
|
}
|
|
}
|
|
|
|
gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_URLS)));
|
|
{
|
|
GeeSet *value;
|
|
if (gvalue) {
|
|
value = GEE_SET(g_value_get_object(gvalue));
|
|
tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
|
|
} else {
|
|
tracker = boost::shared_ptr<void>(gee_hash_set_new(FOLKS_TYPE_URL_FIELD_DETAILS,
|
|
g_object_ref,
|
|
g_object_unref,
|
|
FolksAbstractFieldDetailsHash, NULL, NULL,
|
|
FolksAbstractFieldDetailsEqual, NULL, NULL),
|
|
g_object_unref);
|
|
value = static_cast<GeeSet *>(tracker.get());
|
|
}
|
|
FolksUrlDetails *details = FOLKS_URL_DETAILS(persona);
|
|
if (!GeeCollectionEqual(GEE_COLLECTION(value),
|
|
GEE_COLLECTION(folks_url_details_get_urls(details)))) {
|
|
PUSH_CHANGE(folks_url_details_change_urls);
|
|
}
|
|
}
|
|
|
|
gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_NOTES)));
|
|
{
|
|
// At the moment the comparison fails and tiggers a write on each update?!
|
|
GeeSet *value;
|
|
if (gvalue) {
|
|
value = GEE_SET(g_value_get_object(gvalue));
|
|
tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
|
|
} else {
|
|
tracker = boost::shared_ptr<void>(gee_hash_set_new(FOLKS_TYPE_NOTE_FIELD_DETAILS,
|
|
g_object_ref,
|
|
g_object_unref,
|
|
FolksAbstractFieldDetailsHash, NULL, NULL,
|
|
FolksAbstractFieldDetailsEqual, NULL, NULL),
|
|
g_object_unref);
|
|
value = static_cast<GeeSet *>(tracker.get());
|
|
}
|
|
FolksNoteDetails *details = FOLKS_NOTE_DETAILS(persona);
|
|
if (!GeeCollectionEqual(GEE_COLLECTION(value),
|
|
GEE_COLLECTION(folks_note_details_get_notes(details)))) {
|
|
PUSH_CHANGE(folks_note_details_change_notes);
|
|
}
|
|
}
|
|
|
|
gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_ROLES)));
|
|
{
|
|
GeeSet *value;
|
|
if (gvalue) {
|
|
value = GEE_SET(g_value_get_object(gvalue));
|
|
tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
|
|
} else {
|
|
tracker = boost::shared_ptr<void>(gee_hash_set_new(FOLKS_TYPE_ROLE_FIELD_DETAILS,
|
|
g_object_ref,
|
|
g_object_unref,
|
|
FolksAbstractFieldDetailsHash, NULL, NULL,
|
|
FolksAbstractFieldDetailsEqual, NULL, NULL),
|
|
g_object_unref);
|
|
value = static_cast<GeeSet *>(tracker.get());
|
|
}
|
|
FolksRoleDetails *details = FOLKS_ROLE_DETAILS(persona);
|
|
if (!GeeCollectionEqual(GEE_COLLECTION(value),
|
|
GEE_COLLECTION(folks_role_details_get_roles(details)))) {
|
|
PUSH_CHANGE(folks_role_details_change_roles);
|
|
}
|
|
}
|
|
|
|
gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_GROUPS)));
|
|
{
|
|
GeeSet *value;
|
|
if (gvalue) {
|
|
value = GEE_SET(g_value_get_object(gvalue));
|
|
tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
|
|
} else {
|
|
tracker = boost::shared_ptr<void>(gee_hash_set_new(G_TYPE_STRING,
|
|
(GBoxedCopyFunc)g_strdup,
|
|
g_free,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL),
|
|
g_object_unref);
|
|
value = static_cast<GeeSet *>(tracker.get());
|
|
}
|
|
FolksGroupDetails *details = FOLKS_GROUP_DETAILS(persona);
|
|
if (!GeeCollectionEqual(GEE_COLLECTION(value),
|
|
GEE_COLLECTION(folks_group_details_get_groups(details)))) {
|
|
PUSH_CHANGE(folks_group_details_change_groups);
|
|
}
|
|
}
|
|
|
|
gvalue = static_cast<const GValue *>(g_hash_table_lookup(details.get(), folks_persona_store_detail_key(FOLKS_PERSONA_DETAIL_POSTAL_ADDRESSES)));
|
|
{
|
|
GeeSet *value;
|
|
if (gvalue) {
|
|
value = GEE_SET(g_value_get_object(gvalue));
|
|
tracker = boost::shared_ptr<void>(g_object_ref(value), g_object_unref);
|
|
} else {
|
|
tracker = boost::shared_ptr<void>(gee_hash_set_new(FOLKS_TYPE_POSTAL_ADDRESS_FIELD_DETAILS,
|
|
g_object_ref,
|
|
g_object_unref,
|
|
FolksAbstractFieldDetailsHash, NULL, NULL,
|
|
FolksAbstractFieldDetailsEqual, NULL, NULL),
|
|
g_object_unref);
|
|
value = static_cast<GeeSet *>(tracker.get());
|
|
}
|
|
FolksPostalAddressDetails *details = FOLKS_POSTAL_ADDRESS_DETAILS(persona);
|
|
if (!GeeCollectionEqual(GEE_COLLECTION(value),
|
|
GEE_COLLECTION(folks_postal_address_details_get_postal_addresses(details)))) {
|
|
PUSH_CHANGE(folks_postal_address_details_change_postal_addresses);
|
|
}
|
|
}
|
|
|
|
Details2PersonaStep(NULL, pending);
|
|
}
|
|
|
|
void FolksIndividual2DBus(const FolksIndividualCXX &individual, GDBusCXX::builder_type &builder)
|
|
{
|
|
g_variant_builder_open(&builder, G_VARIANT_TYPE(INDIVIDUAL_DICT)); // dict
|
|
|
|
FolksNameDetails *name = FOLKS_NAME_DETAILS(individual.get());
|
|
SerializeFolks(builder, name, folks_name_details_get_full_name,
|
|
CONTACT_HASH_FULL_NAME);
|
|
SerializeFolks(builder, name, folks_name_details_get_nickname,
|
|
CONTACT_HASH_NICKNAME);
|
|
SerializeFolks(builder, name, folks_name_details_get_structured_name,
|
|
CONTACT_HASH_STRUCTURED_NAME);
|
|
|
|
// gconstpointer folks_abstract_field_details_get_value (FolksAbstractFieldDetails* self);
|
|
|
|
FolksAliasDetails *alias = FOLKS_ALIAS_DETAILS(individual.get());
|
|
SerializeFolks(builder, alias, folks_alias_details_get_alias,
|
|
CONTACT_HASH_ALIAS);
|
|
|
|
FolksAvatarDetails *avatar = FOLKS_AVATAR_DETAILS(individual.get());
|
|
SerializeFolks(builder, avatar, folks_avatar_details_get_avatar,
|
|
CONTACT_HASH_PHOTO);
|
|
|
|
FolksBirthdayDetails *birthday = FOLKS_BIRTHDAY_DETAILS(individual.get());
|
|
SerializeFolks(builder, birthday, folks_birthday_details_get_birthday,
|
|
CONTACT_HASH_BIRTHDAY);
|
|
// const gchar* folks_birthday_details_get_calendar_event_id (FolksBirthdayDetails* self);
|
|
|
|
FolksLocationDetails *location = FOLKS_LOCATION_DETAILS(individual.get());
|
|
SerializeFolks(builder, location, folks_location_details_get_location,
|
|
CONTACT_HASH_LOCATION);
|
|
|
|
FolksEmailDetails *emails = FOLKS_EMAIL_DETAILS(individual.get());
|
|
SerializeFolks(builder, emails, folks_email_details_get_email_addresses,
|
|
(GeeCollCXX<FolksAbstractFieldDetails *>*)NULL,
|
|
CONTACT_HASH_EMAILS);
|
|
|
|
FolksPhoneDetails *phones = FOLKS_PHONE_DETAILS(individual.get());
|
|
SerializeFolks(builder, phones, folks_phone_details_get_phone_numbers,
|
|
(GeeCollCXX<FolksAbstractFieldDetails *>*)NULL,
|
|
CONTACT_HASH_PHONES);
|
|
|
|
FolksUrlDetails *urls = FOLKS_URL_DETAILS(individual.get());
|
|
SerializeFolks(builder, urls, folks_url_details_get_urls,
|
|
(GeeCollCXX<FolksAbstractFieldDetails *>*)NULL,
|
|
CONTACT_HASH_URLS);
|
|
|
|
// Doesn't work like this, folks_im_details_get_im_addresses returns
|
|
// a GeeMultiMap, not a GeeList. Not required anyway.
|
|
// FolksImDetails *im = FOLKS_IM_DETAILS(individual.get());
|
|
// SerializeFolks(builder, im, folks_im_details_get_im_addresses,
|
|
// (GeeCollCXX<FolksAbstractFieldDetails *>*)NULL, "im");
|
|
|
|
FolksNoteDetails *notes = FOLKS_NOTE_DETAILS(individual.get());
|
|
SerializeFolks(builder, notes, folks_note_details_get_notes,
|
|
(GeeCollCXX<FolksNoteFieldDetails *>*)NULL,
|
|
CONTACT_HASH_NOTES);
|
|
|
|
FolksPostalAddressDetails *postal = FOLKS_POSTAL_ADDRESS_DETAILS(individual.get());
|
|
SerializeFolks(builder, postal, folks_postal_address_details_get_postal_addresses,
|
|
(GeeCollCXX<FolksPostalAddressFieldDetails *>*)NULL,
|
|
CONTACT_HASH_ADDRESSES);
|
|
|
|
FolksRoleDetails *roles = FOLKS_ROLE_DETAILS(individual.get());
|
|
SerializeFolks(builder, roles, folks_role_details_get_roles,
|
|
(GeeCollCXX<FolksRoleFieldDetails *>*)NULL,
|
|
CONTACT_HASH_ROLES);
|
|
|
|
FolksGroupDetails *groups = FOLKS_GROUP_DETAILS(individual.get());
|
|
SerializeFolks(builder, groups, folks_group_details_get_groups,
|
|
(GeeStringCollection*)NULL,
|
|
CONTACT_HASH_GROUPS);
|
|
|
|
SerializeFolks(builder, individual.get(), folks_individual_get_personas,
|
|
(GeeCollCXX<FolksPersona *>*)NULL,
|
|
CONTACT_HASH_SOURCE);
|
|
|
|
SerializeFolks(builder, individual.get(), folks_individual_get_id,
|
|
CONTACT_HASH_ID);
|
|
|
|
#if 0
|
|
// Not exposed via D-Bus.
|
|
FolksGender folks_gender_details_get_gender (FolksGenderDetails* self);
|
|
GeeMultiMap* folks_web_service_details_get_web_service_addresses (FolksWebServiceDetails* self);
|
|
guint folks_interaction_details_get_im_interaction_count (FolksInteractionDetails* self);
|
|
GDateTime* folks_interaction_details_get_last_im_interaction_datetime (FolksInteractionDetails* self);
|
|
guint folks_interaction_details_get_call_interaction_count (FolksInteractionDetails* self);
|
|
GDateTime* folks_interaction_details_get_last_call_interaction_datetime (FolksInteractionDetails* self);
|
|
GeeSet* folks_local_id_details_get_local_ids (FolksLocalIdDetails* self);
|
|
const gchar* folks_presence_details_get_default_message_from_type (FolksPresenceType type);
|
|
FolksPresenceType folks_presence_details_get_presence_type (FolksPresenceDetails* self);
|
|
const gchar* folks_presence_details_get_presence_message (FolksPresenceDetails* self);
|
|
const gchar* folks_presence_details_get_presence_status (FolksPresenceDetails* self);
|
|
#endif
|
|
|
|
g_variant_builder_close(&builder); // dict
|
|
}
|
|
|
|
SE_END_CXX
|