evolution-3.6.4/addressbook/gui/widgets/e-addressbook-reflow-adapter.c

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 }