No issues found
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with the program; if not, see <http://www.gnu.org/licenses/>
15 *
16 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <string.h>
24
25 #include <glib/gi18n.h>
26 #include "e-addressbook-reflow-adapter.h"
27 #include "e-addressbook-model.h"
28 #include "eab-gui-util.h"
29
30 #include "e-minicard.h"
31 #include <e-util/e-util.h>
32 #include "addressbook/printing/e-contact-print.h"
33
34 #define E_ADDRESSBOOK_REFLOW_ADAPTER_GET_PRIVATE(obj) \
35 (G_TYPE_INSTANCE_GET_PRIVATE \
36 ((obj), E_TYPE_ADDRESSBOOK_REFLOW_ADAPTER, EAddressbookReflowAdapterPrivate))
37
38 struct _EAddressbookReflowAdapterPrivate {
39 EAddressbookModel *model;
40
41 gboolean loading;
42
43 gint create_contact_id, remove_contact_id, modify_contact_id, model_changed_id;
44 gint search_started_id, search_result_id;
45 };
46
47 #define d(x)
48
49 enum {
50 PROP_0,
51 PROP_CLIENT,
52 PROP_QUERY,
53 PROP_EDITABLE,
54 PROP_MODEL
55 };
56
57 enum {
58 DRAG_BEGIN,
59 OPEN_CONTACT,
60 LAST_SIGNAL
61 };
62
63 static guint signals[LAST_SIGNAL] = {0, };
64
65 G_DEFINE_TYPE (
66 EAddressbookReflowAdapter,
67 e_addressbook_reflow_adapter,
68 E_TYPE_REFLOW_MODEL)
69
70 static void
71 unlink_model (EAddressbookReflowAdapter *adapter)
72 {
73 EAddressbookReflowAdapterPrivate *priv = adapter->priv;
74
75 if (priv->model && priv->create_contact_id)
76 g_signal_handler_disconnect (
77 priv->model,
78 priv->create_contact_id);
79 if (priv->model && priv->remove_contact_id)
80 g_signal_handler_disconnect (
81 priv->model,
82 priv->remove_contact_id);
83 if (priv->model && priv->modify_contact_id)
84 g_signal_handler_disconnect (
85 priv->model,
86 priv->modify_contact_id);
87 if (priv->model && priv->model_changed_id)
88 g_signal_handler_disconnect (
89 priv->model,
90 priv->model_changed_id);
91 if (priv->model && priv->search_started_id)
92 g_signal_handler_disconnect (
93 priv->model,
94 priv->search_started_id);
95 if (priv->model && priv->search_result_id)
96 g_signal_handler_disconnect (
97 priv->model,
98 priv->search_result_id);
99
100 priv->create_contact_id = 0;
101 priv->remove_contact_id = 0;
102 priv->modify_contact_id = 0;
103 priv->model_changed_id = 0;
104 priv->search_started_id = 0;
105 priv->search_result_id = 0;
106
107 if (priv->model)
108 g_object_unref (priv->model);
109
110 priv->model = NULL;
111 }
112
113 static gint
114 text_height (PangoLayout *layout,
115 const gchar *text)
116 {
117 gint height;
118
119 pango_layout_set_text (layout, text, -1);
120
121 pango_layout_get_pixel_size (layout, NULL, &height);
122
123 return height;
124 }
125
126 static void
127 addressbook_dispose (GObject *object)
128 {
129 EAddressbookReflowAdapter *adapter = E_ADDRESSBOOK_REFLOW_ADAPTER (object);
130
131 unlink_model (adapter);
132 }
133
134 static void
135 addressbook_set_width (EReflowModel *erm,
136 gint width)
137 {
138 }
139
140 /* This function returns the number of items in our EReflowModel. */
141 static gint
142 addressbook_count (EReflowModel *erm)
143 {
144 EAddressbookReflowAdapter *adapter = E_ADDRESSBOOK_REFLOW_ADAPTER (erm);
145 EAddressbookReflowAdapterPrivate *priv = adapter->priv;
146
147 return e_addressbook_model_contact_count (priv->model);
148 }
149
150 /* This function returns the height of the minicontact in question */
151 static gint
152 addressbook_height (EReflowModel *erm,
153 gint i,
154 GnomeCanvasGroup *parent)
155 {
156 EAddressbookReflowAdapter *adapter = E_ADDRESSBOOK_REFLOW_ADAPTER (erm);
157 EAddressbookReflowAdapterPrivate *priv = adapter->priv;
158 EContactField field;
159 gint count = 0;
160 gchar *string;
161 EContact *contact = (EContact *) e_addressbook_model_contact_at (priv->model, i);
162 PangoLayout *layout;
163 gint height;
164
165 layout = gtk_widget_create_pango_layout (
166 GTK_WIDGET (GNOME_CANVAS_ITEM (parent)->canvas), "");
167
168 string = e_contact_get (contact, E_CONTACT_FILE_AS);
169 height = text_height (layout, string ? string : "") + 10.0;
170 g_free (string);
171
172 for (field = E_CONTACT_FULL_NAME;
173 field != E_CONTACT_LAST_SIMPLE_STRING && count < 5; field++) {
174
175 if (field == E_CONTACT_FAMILY_NAME || field == E_CONTACT_GIVEN_NAME)
176 continue;
177
178 string = e_contact_get (contact, field);
179 if (string && *string) {
180 gint this_height;
181 gint field_text_height;
182
183 this_height = text_height (layout, e_contact_pretty_name (field));
184
185 field_text_height = text_height (layout, string);
186 if (this_height < field_text_height)
187 this_height = field_text_height;
188
189 this_height += 3;
190
191 height += this_height;
192 count++;
193 }
194 g_free (string);
195 }
196 height += 2;
197
198 g_object_unref (layout);
199
200 return height;
201 }
202
203 static GHashTable *
204 addressbook_create_cmp_cache (EReflowModel *erm)
205 {
206 EAddressbookReflowAdapter *adapter = E_ADDRESSBOOK_REFLOW_ADAPTER (erm);
207 EAddressbookReflowAdapterPrivate *priv = adapter->priv;
208 GHashTable *cmp_cache;
209 gint ii, count;
210
211 count = e_reflow_model_count (erm);
212
213 if (priv->loading || count <= 0)
214 return NULL;
215
216 cmp_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
217
218 for (ii = 0; ii < count; ii++) {
219 EContact *contact;
220
221 contact = (EContact *)
222 e_addressbook_model_contact_at (priv->model, ii);
223 if (contact != NULL) {
224 const gchar *file_as;
225
226 file_as = e_contact_get_const (
227 contact, E_CONTACT_FILE_AS);
228 if (file_as != NULL)
229 g_hash_table_insert (
230 cmp_cache, GINT_TO_POINTER (ii),
231 g_utf8_collate_key (file_as, -1));
232 }
233 }
234
235 return cmp_cache;
236 }
237
238 static gint
239 addressbook_compare (EReflowModel *erm,
240 gint n1,
241 gint n2,
242 GHashTable *cmp_cache)
243 {
244 EAddressbookReflowAdapter *adapter = E_ADDRESSBOOK_REFLOW_ADAPTER (erm);
245 EAddressbookReflowAdapterPrivate *priv = adapter->priv;
246 EContact *contact1, *contact2;
247
248 if (priv->loading) {
249 return n1 - n2;
250 }
251 else {
252 contact1 = (EContact *) e_addressbook_model_contact_at (priv->model, n1);
253 contact2 = (EContact *) e_addressbook_model_contact_at (priv->model, n2);
254
255 if (contact1 && contact2) {
256 const gchar *file_as1, *file_as2;
257 const gchar *uid1, *uid2;
258
259 if (cmp_cache) {
260 file_as1 = g_hash_table_lookup (cmp_cache, GINT_TO_POINTER (n1));
261 file_as2 = g_hash_table_lookup (cmp_cache, GINT_TO_POINTER (n2));
262 if (file_as1 && file_as2)
263 return strcmp (file_as1, file_as2);
264 } else {
265 file_as1 = e_contact_get_const (contact1, E_CONTACT_FILE_AS);
266 file_as2 = e_contact_get_const (contact2, E_CONTACT_FILE_AS);
267 if (file_as1 && file_as2)
268 return g_utf8_collate (file_as1, file_as2);
269 }
270 if (file_as1)
271 return -1;
272 if (file_as2)
273 return 1;
274 uid1 = e_contact_get_const (contact1, E_CONTACT_UID);
275 uid2 = e_contact_get_const (contact2, E_CONTACT_UID);
276 if (uid1 && uid2)
277 return strcmp (uid1, uid2);
278 if (uid1)
279 return -1;
280 if (uid2)
281 return 1;
282 }
283 if (contact1)
284 return -1;
285 if (contact2)
286 return 1;
287 return 0;
288 }
289 }
290
291 static gint
292 adapter_drag_begin (EMinicard *card,
293 GdkEvent *event,
294 EAddressbookReflowAdapter *adapter)
295 {
296 gint ret_val = 0;
297
298 g_signal_emit (
299 adapter,
300 signals[DRAG_BEGIN], 0,
301 event, &ret_val);
302
303 return ret_val;
304 }
305
306 static void
307 adapter_open_contact (EMinicard *card,
308 EContact *contact,
309 EAddressbookReflowAdapter *adapter)
310 {
311 g_signal_emit (adapter, signals[OPEN_CONTACT], 0, contact);
312 }
313
314 static GnomeCanvasItem *
315 addressbook_incarnate (EReflowModel *erm,
316 gint i,
317 GnomeCanvasGroup *parent)
318 {
319 EAddressbookReflowAdapter *adapter = E_ADDRESSBOOK_REFLOW_ADAPTER (erm);
320 EAddressbookReflowAdapterPrivate *priv = adapter->priv;
321 GnomeCanvasItem *item;
322
323 item = gnome_canvas_item_new (
324 parent, e_minicard_get_type (),
325 "contact", e_addressbook_model_contact_at (priv->model, i),
326 "editable", e_addressbook_model_get_editable (priv->model),
327 NULL);
328
329 g_signal_connect (
330 item, "drag_begin",
331 G_CALLBACK (adapter_drag_begin), adapter);
332
333 g_signal_connect (
334 item, "open-contact",
335 G_CALLBACK (adapter_open_contact), adapter);
336
337 return item;
338 }
339
340 static void
341 addressbook_reincarnate (EReflowModel *erm,
342 gint i,
343 GnomeCanvasItem *item)
344 {
345 EAddressbookReflowAdapter *adapter = E_ADDRESSBOOK_REFLOW_ADAPTER (erm);
346 EAddressbookReflowAdapterPrivate *priv = adapter->priv;
347
348 gnome_canvas_item_set (
349 item,
350 "contact", e_addressbook_model_contact_at (priv->model, i),
351 NULL);
352 }
353
354 static void
355 create_contact (EAddressbookModel *model,
356 gint index,
357 gint count,
358 EAddressbookReflowAdapter *adapter)
359 {
360 e_reflow_model_items_inserted (
361 E_REFLOW_MODEL (adapter),
362 index,
363 count);
364 }
365
366 static void
367 remove_contacts (EAddressbookModel *model,
368 gpointer data,
369 EAddressbookReflowAdapter *adapter)
370 {
371 GArray *indices = (GArray *) data;
372 gint count = indices->len;
373
374 if (count == 1)
375 e_reflow_model_item_removed (
376 E_REFLOW_MODEL (adapter),
377 g_array_index (indices, gint, 0));
378 else
379 e_reflow_model_changed (E_REFLOW_MODEL (adapter));
380
381 }
382
383 static void
384 modify_contact (EAddressbookModel *model,
385 gint index,
386 EAddressbookReflowAdapter *adapter)
387 {
388 e_reflow_model_item_changed (E_REFLOW_MODEL (adapter), index);
389 }
390
391 static void
392 model_changed (EAddressbookModel *model,
393 EAddressbookReflowAdapter *adapter)
394 {
395 e_reflow_model_changed (E_REFLOW_MODEL (adapter));
396 }
397
398 static void
399 search_started (EAddressbookModel *model,
400 EAddressbookReflowAdapter *adapter)
401 {
402 EAddressbookReflowAdapterPrivate *priv = adapter->priv;
403
404 priv->loading = TRUE;
405 }
406
407 static void
408 search_result (EAddressbookModel *model,
409 const GError *error,
410 EAddressbookReflowAdapter *adapter)
411 {
412 EAddressbookReflowAdapterPrivate *priv = adapter->priv;
413
414 priv->loading = FALSE;
415
416 e_reflow_model_comparison_changed (E_REFLOW_MODEL (adapter));
417 }
418
419 static void
420 addressbook_set_property (GObject *object,
421 guint property_id,
422 const GValue *value,
423 GParamSpec *pspec)
424 {
425 EAddressbookReflowAdapter *adapter = E_ADDRESSBOOK_REFLOW_ADAPTER (object);
426 EAddressbookReflowAdapterPrivate *priv = adapter->priv;
427
428 switch (property_id) {
429 case PROP_CLIENT:
430 g_object_set (
431 priv->model,
432 "client", g_value_get_object (value),
433 NULL);
434 break;
435 case PROP_QUERY:
436 g_object_set (
437 priv->model,
438 "query", g_value_get_string (value),
439 NULL);
440 break;
441 case PROP_EDITABLE:
442 g_object_set (
443 priv->model,
444 "editable", g_value_get_boolean (value),
445 NULL);
446 break;
447 default:
448 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
449 break;
450 }
451 }
452
453 static void
454 addressbook_get_property (GObject *object,
455 guint property_id,
456 GValue *value,
457 GParamSpec *pspec)
458 {
459 EAddressbookReflowAdapter *adapter = E_ADDRESSBOOK_REFLOW_ADAPTER (object);
460 EAddressbookReflowAdapterPrivate *priv = adapter->priv;
461
462 switch (property_id) {
463 case PROP_CLIENT: {
464 g_object_get_property (
465 G_OBJECT (priv->model),
466 "client", value);
467 break;
468 }
469 case PROP_QUERY: {
470 g_object_get_property (
471 G_OBJECT (priv->model),
472 "query", value);
473 break;
474 }
475 case PROP_EDITABLE: {
476 g_object_get_property (
477 G_OBJECT (priv->model),
478 "editable", value);
479 break;
480 }
481 case PROP_MODEL:
482 g_value_set_object (value, priv->model);
483 break;
484 default:
485 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
486 break;
487 }
488 }
489
490 static void
491 e_addressbook_reflow_adapter_class_init (EAddressbookReflowAdapterClass *class)
492 {
493 GObjectClass *object_class;
494 EReflowModelClass *reflow_model_class;
495
496 g_type_class_add_private (
497 class, sizeof (EAddressbookReflowAdapterPrivate));
498
499 object_class = G_OBJECT_CLASS (class);
500 object_class->set_property = addressbook_set_property;
501 object_class->get_property = addressbook_get_property;
502 object_class->dispose = addressbook_dispose;
503
504 reflow_model_class = E_REFLOW_MODEL_CLASS (class);
505 reflow_model_class->set_width = addressbook_set_width;
506 reflow_model_class->count = addressbook_count;
507 reflow_model_class->height = addressbook_height;
508 reflow_model_class->create_cmp_cache = addressbook_create_cmp_cache;
509 reflow_model_class->compare = addressbook_compare;
510 reflow_model_class->incarnate = addressbook_incarnate;
511 reflow_model_class->reincarnate = addressbook_reincarnate;
512
513 g_object_class_install_property (
514 object_class,
515 PROP_CLIENT,
516 g_param_spec_object (
517 "client",
518 "EBookClient",
519 NULL,
520 E_TYPE_BOOK_CLIENT,
521 G_PARAM_READWRITE));
522
523 g_object_class_install_property (
524 object_class,
525 PROP_QUERY,
526 g_param_spec_string (
527 "query",
528 "Query",
529 NULL,
530 NULL,
531 G_PARAM_READWRITE));
532
533 g_object_class_install_property (
534 object_class,
535 PROP_EDITABLE,
536 g_param_spec_boolean (
537 "editable",
538 "Editable",
539 NULL,
540 FALSE,
541 G_PARAM_READWRITE));
542
543 g_object_class_install_property (
544 object_class,
545 PROP_MODEL,
546 g_param_spec_object (
547 "model",
548 "Model",
549 NULL,
550 E_TYPE_ADDRESSBOOK_MODEL,
551 G_PARAM_READABLE));
552
553 signals[DRAG_BEGIN] = g_signal_new (
554 "drag_begin",
555 G_OBJECT_CLASS_TYPE (object_class),
556 G_SIGNAL_RUN_LAST,
557 G_STRUCT_OFFSET (EAddressbookReflowAdapterClass, drag_begin),
558 NULL, NULL,
559 e_marshal_INT__POINTER,
560 G_TYPE_INT, 1,
561 G_TYPE_POINTER);
562
563 signals[OPEN_CONTACT] = g_signal_new (
564 "open-contact",
565 G_OBJECT_CLASS_TYPE (object_class),
566 G_SIGNAL_RUN_LAST,
567 G_STRUCT_OFFSET (EAddressbookReflowAdapterClass, open_contact),
568 NULL, NULL,
569 g_cclosure_marshal_VOID__OBJECT,
570 G_TYPE_NONE, 1,
571 E_TYPE_CONTACT);
572 }
573
574 static void
575 e_addressbook_reflow_adapter_init (EAddressbookReflowAdapter *adapter)
576 {
577 adapter->priv = E_ADDRESSBOOK_REFLOW_ADAPTER_GET_PRIVATE (adapter);
578 }
579
580 void
581 e_addressbook_reflow_adapter_construct (EAddressbookReflowAdapter *adapter,
582 EAddressbookModel *model)
583 {
584 EAddressbookReflowAdapterPrivate *priv = adapter->priv;
585
586 priv->model = g_object_ref (model);
587
588 priv->create_contact_id = g_signal_connect (
589 priv->model, "contact_added",
590 G_CALLBACK (create_contact), adapter);
591
592 priv->remove_contact_id = g_signal_connect (
593 priv->model, "contacts_removed",
594 G_CALLBACK (remove_contacts), adapter);
595
596 priv->modify_contact_id = g_signal_connect (
597 priv->model, "contact_changed",
598 G_CALLBACK (modify_contact), adapter);
599
600 priv->model_changed_id = g_signal_connect (
601 priv->model, "model_changed",
602 G_CALLBACK (model_changed), adapter);
603
604 priv->search_started_id = g_signal_connect (
605 priv->model, "search_started",
606 G_CALLBACK (search_started), adapter);
607
608 priv->search_result_id = g_signal_connect (
609 priv->model, "search_result",
610 G_CALLBACK (search_result), adapter);
611 }
612
613 EReflowModel *
614 e_addressbook_reflow_adapter_new (EAddressbookModel *model)
615 {
616 EAddressbookReflowAdapter *et;
617
618 et = g_object_new (E_TYPE_ADDRESSBOOK_REFLOW_ADAPTER, NULL);
619
620 e_addressbook_reflow_adapter_construct (et, model);
621
622 return E_REFLOW_MODEL (et);
623 }
624
625 EContact *
626 e_addressbook_reflow_adapter_get_contact (EAddressbookReflowAdapter *adapter,
627 gint index)
628 {
629 EAddressbookReflowAdapterPrivate *priv = adapter->priv;
630
631 return e_addressbook_model_get_contact (priv->model, index);
632 }