1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
9 * for more details.
10 *
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
13 *
14 *
15 * Authors:
16 *
17 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
18 *
19 */
20
21 #include "evolution-config.h"
22
23 #include <gtk/gtk.h>
24 #include <glib/gi18n.h>
25
26 #include <libebook/libebook.h>
27
28 #include "e-addressbook-model.h"
29 #include "e-addressbook-table-adapter.h"
30 #include "eab-book-util.h"
31 #include "eab-contact-merging.h"
32 #include "eab-gui-util.h"
33 #include <libxml/tree.h>
34 #include <libxml/parser.h>
35 #include <libxml/xmlmemory.h>
36
37 #define E_ADDRESSBOOK_TABLE_ADAPTER_GET_PRIVATE(obj) \
38 (G_TYPE_INSTANCE_GET_PRIVATE \
39 ((obj), E_TYPE_ADDRESSBOOK_TABLE_ADAPTER, EAddressbookTableAdapterPrivate))
40
41 struct _EAddressbookTableAdapterPrivate {
42 EAddressbookModel *model;
43
44 gint create_contact_id, remove_contact_id, modify_contact_id, model_changed_id;
45
46 GHashTable *emails;
47 };
48
49 /* Forward Declarations */
50 static void e_addressbook_table_adapter_table_model_init
51 (ETableModelInterface *iface);
52
G_DEFINE_TYPE_WITH_CODE(EAddressbookTableAdapter,e_addressbook_table_adapter,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (E_TYPE_TABLE_MODEL,e_addressbook_table_adapter_table_model_init))53 G_DEFINE_TYPE_WITH_CODE (
54 EAddressbookTableAdapter,
55 e_addressbook_table_adapter,
56 G_TYPE_OBJECT,
57 G_IMPLEMENT_INTERFACE (
58 E_TYPE_TABLE_MODEL,
59 e_addressbook_table_adapter_table_model_init))
60
61 static void
62 unlink_model (EAddressbookTableAdapter *adapter)
63 {
64 EAddressbookTableAdapterPrivate *priv = adapter->priv;
65
66 g_signal_handler_disconnect (priv->model, priv->create_contact_id);
67 g_signal_handler_disconnect (priv->model, priv->remove_contact_id);
68 g_signal_handler_disconnect (priv->model, priv->modify_contact_id);
69 g_signal_handler_disconnect (priv->model, priv->model_changed_id);
70
71 priv->create_contact_id = 0;
72 priv->remove_contact_id = 0;
73 priv->modify_contact_id = 0;
74 priv->model_changed_id = 0;
75
76 g_object_unref (priv->model);
77
78 priv->model = NULL;
79 }
80
81 static void
addressbook_finalize(GObject * object)82 addressbook_finalize (GObject *object)
83 {
84 EAddressbookTableAdapter *adapter;
85
86 adapter = E_ADDRESSBOOK_TABLE_ADAPTER (object);
87
88 unlink_model (adapter);
89
90 g_hash_table_destroy (adapter->priv->emails);
91
92 /* Chain up to parent's finalize() method. */
93 G_OBJECT_CLASS (e_addressbook_table_adapter_parent_class)->finalize (object);
94 }
95
96 /* This function returns the number of columns in our ETableModel. */
97 static gint
addressbook_col_count(ETableModel * etc)98 addressbook_col_count (ETableModel *etc)
99 {
100 return E_CONTACT_FIELD_LAST;
101 }
102
103 /* This function returns the number of rows in our ETableModel. */
104 static gint
addressbook_row_count(ETableModel * etc)105 addressbook_row_count (ETableModel *etc)
106 {
107 EAddressbookTableAdapter *adapter = E_ADDRESSBOOK_TABLE_ADAPTER (etc);
108 EAddressbookTableAdapterPrivate *priv = adapter->priv;
109
110 return e_addressbook_model_contact_count (priv->model);
111 }
112
113 static void
addressbook_append_row(ETableModel * etm,ETableModel * source,gint row)114 addressbook_append_row (ETableModel *etm,
115 ETableModel *source,
116 gint row)
117 {
118 EAddressbookTableAdapter *adapter = E_ADDRESSBOOK_TABLE_ADAPTER (etm);
119 EAddressbookTableAdapterPrivate *priv = adapter->priv;
120 EClientCache *client_cache;
121 ESourceRegistry *registry;
122 EBookClient *book_client;
123 EContact *contact;
124 gint col;
125
126 contact = e_contact_new ();
127
128 for (col = 1; col < E_CONTACT_LAST_SIMPLE_STRING; col++) {
129 gconstpointer val = e_table_model_value_at (source, col, row);
130 e_contact_set (contact, col, (gpointer) val);
131 }
132
133 client_cache =
134 e_addressbook_model_get_client_cache (priv->model);
135 book_client = e_addressbook_model_get_client (priv->model);
136
137 registry = e_client_cache_ref_registry (client_cache);
138
139 eab_merging_book_add_contact (
140 registry, book_client, contact, NULL, NULL);
141
142 g_object_unref (registry);
143
144 g_object_unref (contact);
145 }
146
147 /* This function returns the value at a particular point in our ETableModel. */
148 static gpointer
addressbook_value_at(ETableModel * etc,gint col,gint row)149 addressbook_value_at (ETableModel *etc,
150 gint col,
151 gint row)
152 {
153 EAddressbookTableAdapter *adapter = E_ADDRESSBOOK_TABLE_ADAPTER (etc);
154 EAddressbookTableAdapterPrivate *priv = adapter->priv;
155 EContact *contact;
156 const gchar *value;
157
158 if (col >= E_CONTACT_FIELD_LAST)
159 return NULL;
160
161 if (row >= e_addressbook_model_contact_count (priv->model))
162 return NULL;
163
164 contact = e_addressbook_model_contact_at (priv->model, row);
165 if (col == E_CONTACT_BIRTH_DATE ||
166 col == E_CONTACT_ANNIVERSARY) {
167 EContactDate *date;
168
169 date = e_contact_get (contact, col);
170 if (date) {
171 gint int_dt;
172
173 int_dt = 10000 * date->year + 100 * date->month + date->day;
174
175 e_contact_date_free (date);
176
177 return GINT_TO_POINTER (int_dt);
178 } else {
179 return GINT_TO_POINTER (-1);
180 }
181 }
182
183 value = e_contact_get_const (contact, col);
184
185 if (value && *value && (col == E_CONTACT_EMAIL_1 ||
186 col == E_CONTACT_EMAIL_2 || col == E_CONTACT_EMAIL_3)) {
187 gchar *val = g_hash_table_lookup (priv->emails, value);
188
189 if (val) {
190 /* we have this already cached, so use value from the cache */
191 value = val;
192 } else {
193 gchar *name = NULL, *mail = NULL;
194
195 if (eab_parse_qp_email (value, &name, &mail))
196 val = g_strdup_printf ("%s <%s>", name, mail);
197 else
198 val = g_strdup (value);
199
200 g_free (name);
201 g_free (mail);
202
203 g_hash_table_insert (priv->emails, g_strdup (value), val);
204 value = val;
205 }
206 }
207
208 return g_strdup (value ? value : "");
209 }
210
211 /* This function sets the value at a particular point in our ETableModel. */
212 static void
contact_modified_cb(EBookClient * book_client,const GError * error,gpointer user_data)213 contact_modified_cb (EBookClient *book_client,
214 const GError *error,
215 gpointer user_data)
216 {
217 if (error != NULL)
218 eab_error_dialog (
219 NULL, NULL, _("Error modifying card"), error);
220 }
221
222 static void
addressbook_set_value_at(ETableModel * etc,gint col,gint row,gconstpointer val)223 addressbook_set_value_at (ETableModel *etc,
224 gint col,
225 gint row,
226 gconstpointer val)
227 {
228 EAddressbookTableAdapter *adapter = E_ADDRESSBOOK_TABLE_ADAPTER (etc);
229 EAddressbookTableAdapterPrivate *priv = adapter->priv;
230
231 if (e_addressbook_model_get_editable (priv->model)) {
232 EClientCache *client_cache;
233 ESourceRegistry *registry;
234 EBookClient *book_client;
235 EContact *contact;
236
237 if (col >= E_CONTACT_FIELD_LAST ||
238 col == E_CONTACT_BIRTH_DATE ||
239 col == E_CONTACT_ANNIVERSARY)
240 return;
241
242 if (row >= e_addressbook_model_contact_count (priv->model))
243 return;
244
245 contact = e_addressbook_model_get_contact (priv->model, row);
246 if (!contact)
247 return;
248
249 e_table_model_pre_change (etc);
250
251 if (col == E_CONTACT_EMAIL_1 ||
252 col == E_CONTACT_EMAIL_2 ||
253 col == E_CONTACT_EMAIL_3) {
254 const gchar *old_value = e_contact_get_const (contact, col);
255
256 /* remove old value from cache and use new one */
257 if (old_value && *old_value)
258 g_hash_table_remove (priv->emails, old_value);
259 }
260
261 client_cache =
262 e_addressbook_model_get_client_cache (priv->model);
263 book_client = e_addressbook_model_get_client (priv->model);
264
265 registry = e_client_cache_ref_registry (client_cache);
266
267 e_contact_set (contact, col, (gpointer) val);
268 eab_merging_book_modify_contact (
269 registry, book_client,
270 contact, contact_modified_cb, etc);
271
272 g_object_unref (registry);
273
274 g_object_unref (contact);
275
276 /* XXX Do we need this? Shouldn't the commit_contact
277 * generate a changed signal? */
278 e_table_model_cell_changed (etc, col, row);
279 }
280 }
281
282 /* This function returns whether a particular cell is editable. */
283 static gboolean
addressbook_is_cell_editable(ETableModel * etc,gint col,gint row)284 addressbook_is_cell_editable (ETableModel *etc,
285 gint col,
286 gint row)
287 {
288 return FALSE;
289 }
290
291 /* This function duplicates the value passed to it. */
292 static gpointer
addressbook_duplicate_value(ETableModel * etc,gint col,gconstpointer value)293 addressbook_duplicate_value (ETableModel *etc,
294 gint col,
295 gconstpointer value)
296 {
297 if (col == E_CONTACT_BIRTH_DATE ||
298 col == E_CONTACT_ANNIVERSARY)
299 return GINT_TO_POINTER (GPOINTER_TO_INT (value));
300
301 return g_strdup (value);
302 }
303
304 /* This function frees the value passed to it. */
305 static void
addressbook_free_value(ETableModel * etc,gint col,gpointer value)306 addressbook_free_value (ETableModel *etc,
307 gint col,
308 gpointer value)
309 {
310 if (col != E_CONTACT_BIRTH_DATE &&
311 col != E_CONTACT_ANNIVERSARY)
312 g_free (value);
313 }
314
315 static gpointer
addressbook_initialize_value(ETableModel * etc,gint col)316 addressbook_initialize_value (ETableModel *etc,
317 gint col)
318 {
319 if (col == E_CONTACT_BIRTH_DATE ||
320 col == E_CONTACT_ANNIVERSARY)
321 return GINT_TO_POINTER (-1);
322
323 return g_strdup ("");
324 }
325
326 static gboolean
addressbook_value_is_empty(ETableModel * etc,gint col,gconstpointer value)327 addressbook_value_is_empty (ETableModel *etc,
328 gint col,
329 gconstpointer value)
330 {
331 if (col == E_CONTACT_BIRTH_DATE ||
332 col == E_CONTACT_ANNIVERSARY)
333 return GPOINTER_TO_INT (value) <= 0;
334
335 return !(value && *(gchar *) value);
336 }
337
338 static gchar *
addressbook_value_to_string(ETableModel * etc,gint col,gconstpointer value)339 addressbook_value_to_string (ETableModel *etc,
340 gint col,
341 gconstpointer value)
342 {
343 if (col == E_CONTACT_BIRTH_DATE ||
344 col == E_CONTACT_ANNIVERSARY) {
345 gint int_dt = GPOINTER_TO_INT (value);
346
347 if (int_dt <= 0)
348 return g_strdup ("");
349
350 return g_strdup_printf ("%04d-%02d-%02d", int_dt / 10000, (int_dt / 100) % 100, int_dt % 100);
351 }
352
353 return g_strdup (value);
354 }
355
356 static void
e_addressbook_table_adapter_class_init(EAddressbookTableAdapterClass * class)357 e_addressbook_table_adapter_class_init (EAddressbookTableAdapterClass *class)
358 {
359 GObjectClass *object_class;
360
361 g_type_class_add_private (
362 class, sizeof (EAddressbookTableAdapterPrivate));
363
364 object_class = G_OBJECT_CLASS (class);
365 object_class->finalize = addressbook_finalize;
366 }
367
368 static void
e_addressbook_table_adapter_table_model_init(ETableModelInterface * iface)369 e_addressbook_table_adapter_table_model_init (ETableModelInterface *iface)
370 {
371 iface->column_count = addressbook_col_count;
372 iface->row_count = addressbook_row_count;
373 iface->append_row = addressbook_append_row;
374
375 iface->value_at = addressbook_value_at;
376 iface->set_value_at = addressbook_set_value_at;
377 iface->is_cell_editable = addressbook_is_cell_editable;
378
379 iface->duplicate_value = addressbook_duplicate_value;
380 iface->free_value = addressbook_free_value;
381 iface->initialize_value = addressbook_initialize_value;
382 iface->value_is_empty = addressbook_value_is_empty;
383 iface->value_to_string = addressbook_value_to_string;
384 }
385
386 static void
e_addressbook_table_adapter_init(EAddressbookTableAdapter * adapter)387 e_addressbook_table_adapter_init (EAddressbookTableAdapter *adapter)
388 {
389 adapter->priv = E_ADDRESSBOOK_TABLE_ADAPTER_GET_PRIVATE (adapter);
390 }
391
392 static void
create_contact(EAddressbookModel * model,gint index,gint count,EAddressbookTableAdapter * adapter)393 create_contact (EAddressbookModel *model,
394 gint index,
395 gint count,
396 EAddressbookTableAdapter *adapter)
397 {
398 e_table_model_pre_change (E_TABLE_MODEL (adapter));
399 e_table_model_rows_inserted (E_TABLE_MODEL (adapter), index, count);
400 }
401
402 static void
remove_contacts(EAddressbookModel * model,gpointer data,EAddressbookTableAdapter * adapter)403 remove_contacts (EAddressbookModel *model,
404 gpointer data,
405 EAddressbookTableAdapter *adapter)
406 {
407 GArray *indices = (GArray *) data;
408 gint count = indices->len;
409
410 /* clear whole cache */
411 g_hash_table_remove_all (adapter->priv->emails);
412
413 e_table_model_pre_change (E_TABLE_MODEL (adapter));
414 if (count == 1)
415 e_table_model_rows_deleted (
416 E_TABLE_MODEL (adapter),
417 g_array_index (indices, gint, 0), 1);
418 else
419 e_table_model_changed (E_TABLE_MODEL (adapter));
420 }
421
422 static void
modify_contact(EAddressbookModel * model,gint index,EAddressbookTableAdapter * adapter)423 modify_contact (EAddressbookModel *model,
424 gint index,
425 EAddressbookTableAdapter *adapter)
426 {
427 /* clear whole cache */
428 g_hash_table_remove_all (adapter->priv->emails);
429
430 e_table_model_pre_change (E_TABLE_MODEL (adapter));
431 e_table_model_row_changed (E_TABLE_MODEL (adapter), index);
432 }
433
434 static void
model_changed(EAddressbookModel * model,EAddressbookTableAdapter * adapter)435 model_changed (EAddressbookModel *model,
436 EAddressbookTableAdapter *adapter)
437 {
438 /* clear whole cache */
439 g_hash_table_remove_all (adapter->priv->emails);
440
441 e_table_model_pre_change (E_TABLE_MODEL (adapter));
442 e_table_model_changed (E_TABLE_MODEL (adapter));
443 }
444
445 void
e_addressbook_table_adapter_construct(EAddressbookTableAdapter * adapter,EAddressbookModel * model)446 e_addressbook_table_adapter_construct (EAddressbookTableAdapter *adapter,
447 EAddressbookModel *model)
448 {
449 EAddressbookTableAdapterPrivate *priv = adapter->priv;
450
451 priv->model = model;
452 g_object_ref (priv->model);
453
454 priv->create_contact_id = g_signal_connect (
455 priv->model, "contact_added",
456 G_CALLBACK (create_contact), adapter);
457
458 priv->remove_contact_id = g_signal_connect (
459 priv->model, "contacts_removed",
460 G_CALLBACK (remove_contacts), adapter);
461
462 priv->modify_contact_id = g_signal_connect (
463 priv->model, "contact_changed",
464 G_CALLBACK (modify_contact), adapter);
465
466 priv->model_changed_id = g_signal_connect (
467 priv->model, "model_changed",
468 G_CALLBACK (model_changed), adapter);
469
470 priv->emails = g_hash_table_new_full (
471 g_str_hash, g_str_equal,
472 (GDestroyNotify) g_free,
473 (GDestroyNotify) g_free);
474 }
475
476 ETableModel *
e_addressbook_table_adapter_new(EAddressbookModel * model)477 e_addressbook_table_adapter_new (EAddressbookModel *model)
478 {
479 EAddressbookTableAdapter *et;
480
481 et = g_object_new (E_TYPE_ADDRESSBOOK_TABLE_ADAPTER, NULL);
482
483 e_addressbook_table_adapter_construct (et, model);
484
485 return E_TABLE_MODEL (et);
486 }
487