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