3 * Copyright (C) 2001-2009 Jim Evins <evins@snaught.com>.
5 * Copyright (C) 2005 Austin Henry <ahenry@users.sourceforge.net>
7 * Copyright (C) 2007 Peter Cherriman <glabels-devel2712@bubieyehyeh.me.uk>
9 * This file is part of gLabels.
11 * gLabels is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * gLabels is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with gLabels. If not, see <http://www.gnu.org/licenses/>.
30 #include "merge-evolution.h"
32 #include <libebook/libebook.h>
33 #include <glib/gi18n.h>
37 #include <libglabels.h>
41 #define DEFAULT_QUERY "(exists \"full_name\")"
44 /*===========================================*/
46 /*===========================================*/
48 struct _glMergeEvolutionPrivate {
52 GList *fields; /* the fields supported by the addressbook */
65 /*===========================================*/
67 /*===========================================*/
69 /*===========================================*/
70 /* Local function prototypes */
71 /*===========================================*/
73 static void gl_merge_evolution_finalize (GObject *object);
75 static void gl_merge_evolution_set_property (GObject *object,
80 static void gl_merge_evolution_get_property (GObject *object,
85 static GList *gl_merge_evolution_get_key_list (const glMerge *merge);
86 static gchar *gl_merge_evolution_get_primary_key (const glMerge *merge);
87 static void gl_merge_evolution_open (glMerge *merge);
88 static void gl_merge_evolution_close (glMerge *merge);
89 static glMergeRecord *gl_merge_evolution_get_record (glMerge *merge);
90 static void gl_merge_evolution_copy (glMerge *dst_merge,
91 const glMerge *src_merge);
93 /* utility function prototypes go here */
94 static void free_field_list (GList *fields);
97 /*****************************************************************************/
98 /* Boilerplate object stuff. */
99 /*****************************************************************************/
100 G_DEFINE_TYPE (glMergeEvolution, gl_merge_evolution, GL_TYPE_MERGE)
103 gl_merge_evolution_class_init (glMergeEvolutionClass *class)
105 GObjectClass *object_class = G_OBJECT_CLASS (class);
106 glMergeClass *merge_class = GL_MERGE_CLASS (class);
108 gl_debug (DEBUG_MERGE, "START");
110 gl_merge_evolution_parent_class = g_type_class_peek_parent (class);
112 object_class->set_property = gl_merge_evolution_set_property;
113 object_class->get_property = gl_merge_evolution_get_property;
115 g_object_class_install_property
118 g_param_spec_string ("query", NULL,
119 "Query used to select records from the addressbook",
120 "(exists \"full_name\")",
121 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
123 object_class->finalize = gl_merge_evolution_finalize;
125 merge_class->get_key_list = gl_merge_evolution_get_key_list;
126 merge_class->get_primary_key = gl_merge_evolution_get_primary_key;
127 merge_class->open = gl_merge_evolution_open;
128 merge_class->close = gl_merge_evolution_close;
129 merge_class->get_record = gl_merge_evolution_get_record;
130 merge_class->copy = gl_merge_evolution_copy;
132 gl_debug (DEBUG_MERGE, "END");
137 gl_merge_evolution_init (glMergeEvolution *merge_evolution)
139 gl_debug (DEBUG_MERGE, "START");
141 merge_evolution->priv = g_new0 (glMergeEvolutionPrivate, 1);
142 merge_evolution->priv->query = g_strdup(DEFAULT_QUERY);
144 gl_debug (DEBUG_MERGE, "END");
149 gl_merge_evolution_finalize (GObject *object)
151 glMergeEvolution *merge_evolution = GL_MERGE_EVOLUTION (object);
153 gl_debug (DEBUG_MERGE, "START");
155 g_return_if_fail (object && GL_IS_MERGE_EVOLUTION (object));
157 free_field_list(merge_evolution->priv->fields);
158 g_free (merge_evolution->priv->query);
159 g_free (merge_evolution->priv);
161 G_OBJECT_CLASS (gl_merge_evolution_parent_class)->finalize (object);
163 gl_debug (DEBUG_MERGE, "END");
167 /*--------------------------------------------------------------------------*/
169 /*--------------------------------------------------------------------------*/
171 gl_merge_evolution_set_property (GObject *object,
176 glMergeEvolution *merge_evolution;
178 merge_evolution = GL_MERGE_EVOLUTION (object);
183 g_free (merge_evolution->priv->query);
184 merge_evolution->priv->query = g_value_dup_string (value);
185 gl_debug (DEBUG_MERGE, "ARG \"query\" = \"%s\"",
186 merge_evolution->priv->query);
190 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
198 /*--------------------------------------------------------------------------*/
200 /*--------------------------------------------------------------------------*/
202 gl_merge_evolution_get_property (GObject *object,
207 glMergeEvolution *merge_evolution;
209 merge_evolution = GL_MERGE_EVOLUTION (object);
214 g_value_set_string (value, merge_evolution->priv->query);
218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
226 /*--------------------------------------------------------------------------*/
228 /*--------------------------------------------------------------------------*/
230 gl_merge_evolution_get_key_list (const glMerge *merge)
232 glMergeEvolution *merge_evolution;
233 GList *key_list = NULL;
236 gl_debug (DEBUG_MERGE, "BEGIN");
238 merge_evolution = GL_MERGE_EVOLUTION (merge);
240 /* for the previously retrieved supported fileds, go through them and find
241 * their pretty names */
242 for (iter = merge_evolution->priv->fields;
244 iter = g_list_next(iter))
246 key_list = g_list_prepend (key_list,
247 g_strdup (e_contact_pretty_name (*(EContactField *)iter->data)));
250 key_list = g_list_reverse (key_list);
252 gl_debug (DEBUG_MERGE, "END");
258 /*--------------------------------------------------------------------------*/
259 /* Get "primary" key. */
260 /*--------------------------------------------------------------------------*/
262 gl_merge_evolution_get_primary_key (const glMerge *merge)
264 return g_strdup (e_contact_pretty_name(E_CONTACT_FILE_AS));
268 /* Sort compare function for sorting contacts by file-as element
269 * by Peter Cherriman (PJC)
270 * called by GList* g_list_sort(GList *list, sort_contact_by_file_as);
272 static gint sort_contact_by_file_as(gconstpointer *a, gconstpointer *b)
275 * Returns : negative value if a < b; zero if a = b; positive value if a > b
278 /* Check and cast a and b to EContact */
279 EContact *contact_a = E_CONTACT(a);
280 EContact *contact_b = E_CONTACT(b);
282 /* Extract file_as for each contact and compare... */
283 gchar *a_file_as = e_contact_get (contact_a, E_CONTACT_FILE_AS);
284 gchar *b_file_as = e_contact_get (contact_b, E_CONTACT_FILE_AS);
285 gint res = lgl_str_utf8_casecmp(a_file_as, b_file_as);
287 gl_debug(DEBUG_MERGE, "Sort comparing contacts '%s' and '%s' = %d", a_file_as, b_file_as, res);
289 /* free file_as strings created earlier.... */
297 /*--------------------------------------------------------------------------*/
298 /* Open merge source. */
299 /*--------------------------------------------------------------------------*/
301 gl_open_system_addressbook (GError **error)
303 ESourceRegistry *registry;
307 registry = e_source_registry_new_sync (NULL, error);
311 source = e_source_registry_ref_builtin_address_book (registry);
313 g_object_unref (registry);
317 book = e_book_new (source, error);
319 g_object_unref (source);
320 g_object_unref (registry);
326 gl_merge_evolution_open (glMerge *merge)
328 glMergeEvolution *merge_evolution;
331 GList *fields, *iter;
332 EContactField *field_id;
333 GError *error = NULL;
335 gl_debug (DEBUG_MERGE, "BEGIN");
337 merge_evolution = GL_MERGE_EVOLUTION (merge);
339 query = e_book_query_from_string(merge_evolution->priv->query);
341 g_warning ("Couldn't construct query");
345 merge_evolution->priv->book = gl_open_system_addressbook(&error);
346 if (!merge_evolution->priv->book) {
347 g_warning ("Couldn't open addressbook.");
350 g_warning ("gl_open_system_addressbook: %s", error->message);
351 g_error_free (error);
353 e_book_query_unref(query);
357 if (!e_book_open(merge_evolution->priv->book, FALSE, &error)) {
358 g_warning ("Couldn't open addressbook.");
361 g_warning ("e_book_open: %s", error->message);
362 g_error_free (error);
364 e_book_query_unref(query);
365 g_object_unref(merge_evolution->priv->book);
366 merge_evolution->priv->book = NULL;
370 /* fetch the list of fields supported by this address book */
371 status = e_book_get_supported_fields(merge_evolution->priv->book, &fields, &error);
372 if (status == FALSE) {
373 g_warning ("Couldn't list available fields.");
376 g_warning ("e_book_get_supported_fields: %s", error->message);
377 g_error_free (error);
379 e_book_query_unref(query);
380 g_object_unref(merge_evolution->priv->book);
381 merge_evolution->priv->book = NULL;
385 /* generate a list of field_ids, and put that into private->fields */
386 for (iter = fields; iter != NULL; iter = g_list_next(iter)) {
387 field_id = g_new(EContactField, 1);
388 *field_id = e_contact_field_id(iter->data);
390 /* above this value, the data aren't strings anymore */
391 if (*field_id > E_CONTACT_LAST_SIMPLE_STRING) {
396 merge_evolution->priv->fields =
397 g_list_prepend(merge_evolution->priv->fields, field_id);
399 free_field_list(fields); /* don't need the list of names anymore */
401 gl_debug(DEBUG_MERGE, "Field list length: %d", g_list_length(merge_evolution->priv->fields));
403 /* finally retrieve the contacts */
404 status = e_book_get_contacts (merge_evolution->priv->book,
406 &merge_evolution->priv->contacts,
408 if (status == FALSE) {
409 g_warning ("Couldn't get contacts.");
412 g_warning ("e_book_get_contacts: %s", error->message);
413 g_error_free (error);
415 e_book_query_unref(query);
416 free_field_list(merge_evolution->priv->fields);
417 g_object_unref(merge_evolution->priv->book);
418 merge_evolution->priv->book = NULL;
423 e_book_query_unref(query);
425 /* Sort contacts using file-as element....
426 * by Peter Cherriman (PJC)
428 gl_debug (DEBUG_MERGE, "Starting sort");
429 merge_evolution->priv->contacts = g_list_sort(merge_evolution->priv->contacts, (GCompareFunc)sort_contact_by_file_as);
430 gl_debug (DEBUG_MERGE, "Ended sort");
432 gl_debug (DEBUG_MERGE, "END");
438 /*--------------------------------------------------------------------------*/
439 /* Close merge source. */
440 /*--------------------------------------------------------------------------*/
442 gl_merge_evolution_close (glMerge *merge)
444 glMergeEvolution *merge_evolution;
447 merge_evolution = GL_MERGE_EVOLUTION (merge);
449 /* unref all of the objects created in _open */
450 g_object_unref(merge_evolution->priv->book);
451 merge_evolution->priv->book = NULL;
453 for (iter = merge_evolution->priv->contacts;
455 iter = g_list_next(iter))
457 EContact *contact = E_CONTACT (iter->data);
459 g_object_unref(contact);
461 g_list_free(merge_evolution->priv->contacts);
462 merge_evolution->priv->contacts = NULL;
466 /*--------------------------------------------------------------------------*/
467 /* Get next record from merge source, NULL if no records left (i.e EOF) */
468 /*--------------------------------------------------------------------------*/
469 static glMergeRecord *
470 gl_merge_evolution_get_record (glMerge *merge)
472 glMergeEvolution *merge_evolution;
473 glMergeRecord *record;
475 EContactField field_id;
480 merge_evolution = GL_MERGE_EVOLUTION (merge);
482 head = merge_evolution->priv->contacts;
484 return NULL; /* past the last record */
486 contact = E_CONTACT(head->data);
488 record = g_new0 (glMergeRecord, 1);
489 record->select_flag = TRUE;
491 /* Take the interesting fields one by one from the contact, and put them
492 * into the glMergeRecord structure. When done, free up the resources for
495 /* iterate through the supported fields, and add them to the list */
496 for (iter = merge_evolution->priv->fields;
498 iter = g_list_next(iter))
501 field_id = *(EContactField *)iter->data;
502 value = g_strdup (e_contact_get_const (contact, field_id));
505 field = g_new0 (glMergeField, 1);
506 field->key = g_strdup (e_contact_pretty_name (field_id));
507 field->value = value;
508 record->field_list = g_list_prepend (record->field_list, field);
512 record->field_list = g_list_reverse (record->field_list);
514 /* do a destructive read */
515 g_object_unref (contact);
516 merge_evolution->priv->contacts =
517 g_list_remove_link (merge_evolution->priv->contacts, head);
518 g_list_free_1 (head);
524 /*---------------------------------------------------------------------------*/
525 /* Copy merge_evolution specific fields. */
526 /*---------------------------------------------------------------------------*/
528 gl_merge_evolution_copy (glMerge *dst_merge,
529 const glMerge *src_merge)
531 GList *src_iter, *dst_iter;
533 glMergeEvolution *dst_merge_evolution;
534 glMergeEvolution *src_merge_evolution;
536 gl_debug (DEBUG_MERGE, "BEGIN");
538 dst_merge_evolution = GL_MERGE_EVOLUTION (dst_merge);
539 src_merge_evolution = GL_MERGE_EVOLUTION (src_merge);
541 dst_merge_evolution->priv->query = g_strdup(src_merge_evolution->priv->query);
543 dst_merge_evolution->priv->fields = g_list_copy(src_merge_evolution->priv->fields);
544 for (src_iter = src_merge_evolution->priv->fields,
545 dst_iter = dst_merge_evolution->priv->fields;
546 src_iter != NULL && dst_iter != NULL;
547 src_iter = g_list_next(src_iter), dst_iter = g_list_next(dst_iter))
549 dst_iter->data = g_new(EContactField, 1);
550 if (src_iter->data) { /* this better not be null, but... */
551 memcpy(dst_iter->data, src_iter->data, sizeof(EContactField));
555 /* I don't know that there's a good way to do a deep copy of the various
556 * libebook structures/objects, so I'm just going to leave them out. They
557 * are all regenerated on gl_merge_evolution_open, anyway */
559 gl_debug (DEBUG_MERGE, "END");
563 /*---------------------------------------------------------------------------*/
564 /* Free the list of supported fields */
565 /*---------------------------------------------------------------------------*/
567 free_field_list (GList *fields)
571 for (iter = fields; iter != NULL; iter = g_list_next(iter))
582 #endif /* HAVE_LIBEBOOK */
587 * Local Variables: -- emacs
589 * c-basic-offset: 8 -- emacs
590 * tab-width: 8 -- emacs
591 * indent-tabs-mode: nil -- emacs