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