evolution-3.6.4/addressbook/gui/widgets/eab-contact-formatter.c

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 
  17 #ifdef HAVE_CONFIG_H
  18 #include <config.h>
  19 #endif
  20 
  21 #include "eab-contact-formatter.h"
  22 
  23 #include "eab-gui-util.h"
  24 #include "e-util/e-util.h"
  25 #include "e-util/e-util-private.h"
  26 #include "e-util/e-html-utils.h"
  27 #include "e-util/e-icon-factory.h"
  28 #include "e-util/e-plugin-ui.h"
  29 
  30 #ifdef WITH_CONTACT_MAPS
  31 #include "widgets/misc/e-contact-map.h"
  32 #endif
  33 
  34 #include <string.h>
  35 #include <glib/gi18n.h>
  36 
  37 G_DEFINE_TYPE (
  38         EABContactFormatter,
  39         eab_contact_formatter,
  40         G_TYPE_OBJECT);
  41 
  42 #define EAB_CONTACT_FORMATTER_GET_PRIVATE(obj) \
  43         (G_TYPE_INSTANCE_GET_PRIVATE \
  44         ((obj), EAB_TYPE_CONTACT_FORMATTER, EABContactFormatterPrivate))
  45 
  46 #define TEXT_IS_RIGHT_TO_LEFT \
  47         (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL)
  48 
  49 enum {
  50 	PROP_0,
  51         PROP_DISPLAY_MODE,
  52         PROP_RENDER_MAPS,
  53 	PROP_STYLE,
  54 	PROP_STATE
  55 };
  56 
  57 struct _EABContactFormatterPrivate {
  58 
  59         EContact *contact;
  60 
  61         EABContactDisplayMode mode;
  62         gboolean render_maps;
  63 
  64 	GtkStyle *style;
  65 	GtkStateType state;
  66 };
  67 
  68 static struct {
  69 	const gchar *name;
  70 	const gchar *pretty_name;
  71 }
  72 common_location[] =
  73 {
  74 	{ "WORK",  N_ ("Work")  },
  75 	{ "HOME",  N_ ("Home")  },
  76 	{ "OTHER", N_ ("Other") }
  77 };
  78 
  79 #define IMAGE_COL_WIDTH   "20"
  80 #define CONTACT_LIST_ICON "stock_contact-list"
  81 #define AIM_ICON          "im-aim"
  82 #define GROUPWISE_ICON    "im-nov"
  83 #define ICQ_ICON          "im-icq"
  84 #define JABBER_ICON       "im-jabber"
  85 #define MSN_ICON          "im-msn"
  86 #define YAHOO_ICON        "im-yahoo"
  87 #define GADUGADU_ICON	  "im-gadugadu"
  88 #define SKYPE_ICON	  "stock_people"
  89 #define TWITTER_ICON	  "im-twitter"
  90 #define VIDEOCONF_ICON    "stock_video-conferencing"
  91 
  92 #define MAX_COMPACT_IMAGE_DIMENSION 48
  93 
  94 #define HTML_HEADER "<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n"  \
  95 "<head>\n<meta name=\"generator\" content=\"Evolution Addressbook Component\">\n" \
  96 "<link type=\"text/css\" rel=\"stylesheet\" href=\"evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview.css\">" \
  97 "<style type=\"text/css\">\n" \
  98 "  div#header { width:100%; clear: both; }\n" \
  99 "  div#columns { width: 100%; clear: both; }\n" \
 100 "  div#footer { width: 100%; clear: both; }\n" \
 101 "  div.column { width: auto; float: left; margin-right: 15px; }\n" \
 102 "  img#contact-photo { float: left; }\n" \
 103 "  div#contact-name { float: left; margin-left: 20px; }\n" \
 104 "</style>\n" \
 105 "</head>\n"
 106 
 107 static gboolean
 108 icon_available (const gchar *icon)
 109 {
 110 	GtkIconTheme *icon_theme;
 111 	GtkIconInfo *icon_info;
 112 
 113 	if (!icon)
 114 		return FALSE;
 115 
 116 	icon_theme = gtk_icon_theme_get_default ();
 117 	icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon, 16, 0);
 118 	if (icon_info != NULL)
 119 		gtk_icon_info_free (icon_info);
 120 
 121 	return icon_info != NULL;
 122 }
 123 
 124 static void
 125 render_address_link (GString *buffer,
 126                      EContact *contact,
 127                      gint map_type)
 128 {
 129 	EContactAddress *adr;
 130 	GString *link = g_string_new ("");
 131 
 132 	adr = e_contact_get (contact, map_type);
 133 	if (adr &&
 134 	    (adr->street || adr->locality || adr->region || adr->country)) {
 135 		gchar *escaped;
 136 
 137 		if (adr->street && *adr->street)
 138 			g_string_append_printf (link, "%s, ", adr->street);
 139 
 140 		if (adr->locality && *adr->locality)
 141 			g_string_append_printf (link, "%s, ", adr->locality);
 142 
 143 		if (adr->region && *adr->region)
 144 			g_string_append_printf (link, "%s, ", adr->region);
 145 
 146 		if (adr->country && *adr->country)
 147 			g_string_append_printf (link, "%s", adr->country);
 148 
 149 		escaped = g_uri_escape_string (link->str, NULL, TRUE);
 150 		g_string_assign (link, escaped);
 151 		g_free (escaped);
 152 
 153 		g_string_prepend (link, "<a href=\"http://maps.google.com?q=");
 154 		g_string_append_printf (link, "\">%s</a>", _("Open map"));
 155 	}
 156 
 157 	if (adr)
 158 		e_contact_address_free (adr);
 159 
 160 	g_string_append (buffer, link->str);
 161 	g_string_free (link, TRUE);
 162 }
 163 
 164 static void
 165 accum_address (GString *buffer,
 166                EContact *contact,
 167                const gchar *html_label,
 168                EContactField adr_field,
 169                EContactField label_field)
 170 {
 171 	EContactAddress *adr;
 172 	const gchar *label;
 173 	GString *map_link = g_string_new ("<br>");
 174 
 175 	render_address_link (map_link, contact, adr_field);
 176 
 177 	label = e_contact_get_const (contact, label_field);
 178 	if (label) {
 179 		gchar *html = e_text_to_html (label, E_TEXT_TO_HTML_CONVERT_NL);
 180 
 181 		if (TEXT_IS_RIGHT_TO_LEFT) {
 182 			g_string_append_printf (
 183 				buffer,
 184 				"<tr>"
 185 				"<td align=\"right\" valign=\"top\" nowrap>%s</td>"
 186 				"<th>%s:<br>%s</th>"
 187 				"<td valign=\"top\" width=\"" IMAGE_COL_WIDTH "\"></td>"
 188 				"</tr>",
 189 				html, html_label, map_link->str);
 190 		} else {
 191 			g_string_append_printf (
 192 				buffer,
 193 				"<tr>"
 194 				"<td width=\"" IMAGE_COL_WIDTH "\"></td>"
 195 				"<th>%s:<br>%s</th>"
 196 				"<td valign=\"top\" nowrap>%s</td>"
 197 				"</tr>",
 198 				html_label, map_link->str, html);
 199 		}
 200 
 201 		g_free (html);
 202 		g_string_free (map_link, TRUE);
 203 		return;
 204 	}
 205 
 206 	adr = e_contact_get (contact, adr_field);
 207 	if (adr &&
 208 	   (adr->po || adr->ext || adr->street || adr->locality ||
 209 	    adr->region || adr->code || adr->country)) {
 210 
 211 		if (TEXT_IS_RIGHT_TO_LEFT) {
 212 			g_string_append_printf (
 213 				buffer, "<tr><td align=\"right\" valign=\"top\" nowrap>");
 214 		} else {
 215 			g_string_append_printf (
 216 				buffer,
 217 				"<tr>"
 218 				"<td valign=\"top\" width=\"" IMAGE_COL_WIDTH "\"></td>"
 219 				"<th>%s:<br>%s</th>"
 220 				"<td valign=\"top\" nowrap>",
 221 				html_label, map_link->str);
 222 		}
 223 
 224 		if (adr->po && *adr->po)
 225 			g_string_append_printf (buffer, "%s<br>", adr->po);
 226 
 227 		if (adr->ext && *adr->ext)
 228 			g_string_append_printf (buffer, "%s<br>", adr->ext);
 229 
 230 		if (adr->street && *adr->street)
 231 			g_string_append_printf (buffer, "%s<br>", adr->street);
 232 
 233 		if (adr->locality && *adr->locality)
 234 			g_string_append_printf (buffer, "%s<br>", adr->locality);
 235 
 236 		if (adr->region && *adr->region)
 237 			g_string_append_printf (buffer, "%s<br>", adr->region);
 238 
 239 		if (adr->code && *adr->code)
 240 			g_string_append_printf (buffer, "%s<br>", adr->code);
 241 
 242 		if (adr->country && *adr->country)
 243 			g_string_append_printf (buffer, "%s<br>", adr->country);
 244 
 245 		if (TEXT_IS_RIGHT_TO_LEFT) {
 246 			g_string_append_printf (
 247 				buffer,
 248 				"</td><th%s:<br>%s</th>"
 249 				"<td width=\"" IMAGE_COL_WIDTH "\"></td>"
 250 				"</tr>", html_label, map_link->str);
 251 		} else {
 252 			g_string_append_printf (buffer, "</td></tr>");
 253 		}
 254 
 255 	}
 256 
 257 	if (adr)
 258 		e_contact_address_free (adr);
 259 
 260 	g_string_free (map_link, TRUE);
 261 }
 262 
 263 static void
 264 render_table_row (GString *buffer,
 265                   const gchar *label,
 266                   const gchar *str,
 267                   const gchar *icon,
 268                   guint html_flags)
 269 {
 270 	const gchar *icon_html;
 271 	gchar *value;
 272 
 273 	if (html_flags)
 274 		value = e_text_to_html (str, html_flags);
 275 	else
 276 		value = (gchar *) str;
 277 
 278 	if (icon && icon_available (icon)) {
 279 		icon_html = g_strdup_printf ("<img src=\"gtk-stock://%s\" width=\"16\" height=\"16\" />", icon);
 280 	} else {
 281 		icon_html = "";
 282 	}
 283 
 284 	if (TEXT_IS_RIGHT_TO_LEFT) {
 285 		g_string_append_printf (
 286 			buffer, "<tr>"
 287 			"<td valign=\"top\" align=\"right\">%s</td>"
 288 			"<th align=\"right\" valign=\"top\" width=\"100\" nowrap>:%s</th>"
 289 			"<td valign=\"top\" width=\"" IMAGE_COL_WIDTH "\">%s</td>"
 290 			"</tr>",
 291 			value, label, icon_html);
 292 	} else {
 293 		g_string_append_printf (
 294 			buffer, "<tr>"
 295 			"<td valign=\"top\" width=\"" IMAGE_COL_WIDTH "\">%s</td>"
 296 			"<th valign=\"top\" width=\"100\" nowrap>%s:</th>"
 297 			"<td valign=\"top\">%s</td>"
 298 			"</tr>",
 299 			icon_html, label, value);
 300 	}
 301 
 302 	if (html_flags)
 303 		g_free (value);
 304 }
 305 
 306 static void
 307 accum_attribute (GString *buffer,
 308                  EContact *contact,
 309                  const gchar *html_label,
 310                  EContactField field,
 311                  const gchar *icon,
 312                  guint html_flags)
 313 {
 314 	const gchar *str;
 315 
 316 	str = e_contact_get_const (contact, field);
 317 
 318 	if (str != NULL && *str != '\0')
 319 		render_table_row (buffer, html_label, str, icon, html_flags);
 320 }
 321 
 322 static void
 323 accum_time_attribute (GString *buffer,
 324                       EContact *contact,
 325                       const gchar *html_label,
 326                       EContactField field,
 327                       const gchar *icon,
 328                       guint html_flags)
 329 {
 330 	EContactDate *date;
 331 	GDate *gdate = NULL;
 332 	gchar sdate[100];
 333 
 334 	date = e_contact_get (contact, field);
 335 	if (date) {
 336 		gdate = g_date_new_dmy (
 337 			date->day,
 338 			date->month,
 339 			date->year);
 340 		g_date_strftime (sdate, 100, "%x", gdate);
 341 		g_date_free (gdate);
 342 		render_table_row (buffer, html_label, sdate, icon, html_flags);
 343 		e_contact_date_free (date);
 344 	}
 345 }
 346 
 347 static void
 348 accum_attribute_multival (GString *buffer,
 349                           EContact *contact,
 350                           const gchar *html_label,
 351                           EContactField field,
 352                           const gchar *icon,
 353                           guint html_flags)
 354 {
 355 	GList *val_list, *l;
 356 	GString *val = g_string_new ("");
 357 
 358 	val_list = e_contact_get (contact, field);
 359 
 360 	for (l = val_list; l; l = l->next) {
 361 		if (l != val_list)
 362 			g_string_append (val, "<br>");
 363 
 364 		g_string_append (val, l->data);
 365 	}
 366 
 367 	if (val->str && *val->str)
 368 		render_table_row (buffer, html_label, val->str, icon, html_flags);
 369 
 370 	g_string_free (val, TRUE);
 371 	g_list_foreach (val_list, (GFunc) g_free, NULL);
 372 	g_list_free (val_list);
 373 }
 374 
 375 static const gchar *
 376 get_email_location (EVCardAttribute *attr)
 377 {
 378 	gint i;
 379 
 380 	for (i = 0; i < G_N_ELEMENTS (common_location); i++) {
 381 		if (e_vcard_attribute_has_type (attr, common_location[i].name))
 382 			return _(common_location[i].pretty_name);
 383 	}
 384 
 385 	return _("Other");
 386 }
 387 
 388 static void
 389 render_title_block (EABContactFormatter *formatter,
 390                     GString *buffer)
 391 {
 392 	const gchar *str;
 393 	gchar *html;
 394 	EContactPhoto *photo;
 395 	EContact *contact;
 396 
 397 	contact = formatter->priv->contact;
 398 
 399 	g_string_append_printf (
 400 		buffer,
 401 		"<table border=\"0\"><tr>"
 402 		"<td %s valign=\"middle\">", TEXT_IS_RIGHT_TO_LEFT ?
 403 		"align=\"right\"" : "");
 404 
 405 	photo = e_contact_get (contact, E_CONTACT_PHOTO);
 406 	if (!photo)
 407 		photo = e_contact_get (contact, E_CONTACT_LOGO);
 408 
 409 	if (photo && photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
 410 		gchar *photo_data;
 411 		photo_data = g_base64_encode (
 412 				photo->data.inlined.data,
 413 				photo->data.inlined.length);
 414 		g_string_append_printf (
 415 			buffer, "<img border=\"1\" src=\"data:%s;base64,%s\">",
 416 			photo->data.inlined.mime_type,
 417 			photo_data);
 418 	} else if (photo && photo->type == E_CONTACT_PHOTO_TYPE_URI && photo->data.uri && *photo->data.uri) {
 419 		gboolean is_local = g_str_has_prefix (photo->data.uri, "file://");
 420 		gchar *unescaped = g_uri_unescape_string (photo->data.uri, NULL);
 421 		g_string_append_printf (
 422 			buffer, "<img border=\"1\" src=\"%s%s\">",
 423 			is_local ? "evo-" : "", unescaped);
 424 		g_free (unescaped);
 425 	}
 426 
 427 	if (photo)
 428 		e_contact_photo_free (photo);
 429 
 430 	if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
 431 		g_string_append_printf (buffer, "<img src=\"gtk-stock://%s\">", CONTACT_LIST_ICON);
 432 	}
 433 
 434 	g_string_append_printf (
 435 		buffer,
 436 		"</td><td width=\"20\"></td><td %s valign=\"top\">\n",
 437 		TEXT_IS_RIGHT_TO_LEFT ? "align=\"right\"" : "");
 438 
 439 	str = e_contact_get_const (contact, E_CONTACT_FILE_AS);
 440 	if (!str)
 441 		str = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
 442 
 443 	if (str) {
 444 		html = e_text_to_html (str, 0);
 445 		if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
 446 			g_string_append_printf (
 447 				buffer,
 448 				"<h2><a href=\"internal-mailto:0\">%s</a></h2>",
 449 				html);
 450 		} else {
 451 			g_string_append_printf (buffer, "<h2>%s</h2>", html);
 452 		}
 453 		g_free (html);
 454 	}
 455 
 456 	g_string_append (buffer, "</td></tr></table>");
 457 }
 458 
 459 static void
 460 render_contact_list_row (EABContactFormatter *formatter,
 461                          EDestination *destination,
 462                          GString *buffer)
 463 {
 464 	gchar *evolution_imagesdir;
 465 	gboolean list_collapsed = FALSE;
 466 	const gchar *textrep;
 467 	gchar *name = NULL, *email_addr = NULL;
 468 
 469 	evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
 470 
 471 	textrep = e_destination_get_textrep (destination, TRUE);
 472 	if (!eab_parse_qp_email (textrep, &name, &email_addr))
 473 		email_addr = g_strdup (textrep);
 474 
 475 	g_string_append (buffer, "<tr>");
 476 	if (e_destination_is_evolution_list (destination)) {
 477 		g_string_append_printf (
 478 			buffer,
 479 			"<td width=" IMAGE_COL_WIDTH " valign=\"top\" align=\"left\">"
 480 			"<img src=\"evo-file://%s/minus.png\" "
 481 			"id=\"%s\" "
 482 			"class=\"navigable _evo_collapse_button\">"
 483 			"</td><td width=\"100%%\" align=\"left\">%s",
 484 			evolution_imagesdir,
 485 			e_destination_get_contact_uid (destination),
 486 			name ? name : email_addr);
 487 
 488 		if (!list_collapsed) {
 489 			const GList *dest, *dests;
 490 			g_string_append_printf (
 491 				buffer,
 492 				"<br><table cellspacing=\"1\" id=\"list-%s\">",
 493 				e_destination_get_contact_uid (destination));
 494 
 495 			dests = e_destination_list_get_root_dests (destination);
 496 			for (dest = dests; dest; dest = dest->next) {
 497 				render_contact_list_row (
 498 					formatter, dest->data, buffer);
 499 			}
 500 
 501 			g_string_append (buffer, "</table>");
 502 		}
 503 
 504 		g_string_append (buffer, "</td>");
 505 
 506 	} else {
 507 		if (name && *name) {
 508 			g_string_append_printf (
 509 				buffer,
 510 				"<td colspan=\"2\">%s &lt"
 511 				"<a href=\"mailto:%s\">%s</a>&gt;"
 512 				"</td>",
 513 				name, email_addr, email_addr);
 514 		} else {
 515 			g_string_append_printf (
 516 				buffer,
 517 				"<td colspan=\"2\">"
 518 				"<a href=\"mailto:%s\">%s</a>"
 519 				"</td>",
 520 				email_addr, email_addr);
 521 		}
 522 	}
 523 
 524 	g_string_append (buffer, "</tr>");
 525 
 526 	g_free (evolution_imagesdir);
 527 	g_free (name);
 528 	g_free (email_addr);
 529 }
 530 
 531 static void
 532 render_contact_list (EABContactFormatter *formatter,
 533                      GString *buffer)
 534 {
 535 	EContact *contact;
 536 	EDestination *destination;
 537 	const GList *dest, *dests;
 538 
 539 	contact = formatter->priv->contact;
 540 
 541 	destination = e_destination_new ();
 542 	e_destination_set_contact (destination, contact, 0);
 543 	dests = e_destination_list_get_root_dests (destination);
 544 
 545 	render_title_block (formatter, buffer);
 546 
 547 	g_string_append_printf (
 548 		buffer,
 549 		"<table border=\"0\"><tr><th colspan=\"2\">%s</th></tr>"
 550 		"<tr><td with=" IMAGE_COL_WIDTH "></td><td>", _("List Members:"));
 551 
 552 	g_string_append (buffer, "<table border=\"0\" cellspacing=\"1\">");
 553 
 554 	for (dest = dests; dest; dest = dest->next)
 555 		render_contact_list_row (formatter, dest->data, buffer);
 556 
 557 	g_string_append (buffer, "</table>");
 558 	g_string_append (buffer, "</td></tr></table>");
 559 
 560 	g_object_unref (destination);
 561 }
 562 
 563 static void
 564 render_contact_column (EABContactFormatter *formatter,
 565                        GString *buffer)
 566 {
 567 	EContact *contact;
 568 	GString *accum, *email;
 569 	GList *email_list, *l, *email_attr_list, *al;
 570 	gint email_num = 0;
 571 	const gchar *nl;
 572 
 573 	contact = formatter->priv->contact;
 574 	email = g_string_new ("");
 575 	nl = "";
 576 
 577 	email_list = e_contact_get (contact, E_CONTACT_EMAIL);
 578 	email_attr_list = e_contact_get_attributes (contact, E_CONTACT_EMAIL);
 579 
 580 	for (l = email_list, al = email_attr_list; l && al; l = l->next, al = al->next) {
 581 		gchar *name = NULL, *mail = NULL;
 582 		gchar *attr_str = (gchar *) get_email_location ((EVCardAttribute *) al->data);
 583 
 584 		if (!eab_parse_qp_email (l->data, &name, &mail))
 585 			mail = e_text_to_html (l->data, 0);
 586 
 587 		g_string_append_printf (
 588 			email,
 589 			"%s%s%s<a href=\"internal-mailto:%d\">%s</a>%s "
 590 			"<span class=\"header\">(%s)</span>",
 591 			nl,
 592 			name ? name : "",
 593 			name ? " &lt;" : "",
 594 			email_num,
 595 			mail,
 596 			name ? "&gt;" : "",
 597 			attr_str ? attr_str : "");
 598 		email_num++;
 599 		nl = "<br>";
 600 
 601 		g_free (name);
 602 		g_free (mail);
 603 	}
 604 	g_list_foreach (email_list, (GFunc) g_free, NULL);
 605 	g_list_foreach (email_attr_list, (GFunc) e_vcard_attribute_free, NULL);
 606 	g_list_free (email_list);
 607 	g_list_free (email_attr_list);
 608 
 609 	accum = g_string_new ("");
 610 
 611 	if (email->len)
 612 		render_table_row (accum, _("Email"), email->str, NULL, 0);
 613 
 614 	accum_attribute (accum, contact, _("Nickname"), E_CONTACT_NICKNAME, NULL, 0);
 615 	accum_attribute_multival (accum, contact, _("AIM"), E_CONTACT_IM_AIM, AIM_ICON, 0);
 616 	accum_attribute_multival (accum, contact, _("GroupWise"), E_CONTACT_IM_GROUPWISE, GROUPWISE_ICON, 0);
 617 	accum_attribute_multival (accum, contact, _("ICQ"), E_CONTACT_IM_ICQ, ICQ_ICON, 0);
 618 	accum_attribute_multival (accum, contact, _("Jabber"), E_CONTACT_IM_JABBER, JABBER_ICON, 0);
 619 	accum_attribute_multival (accum, contact, _("MSN"), E_CONTACT_IM_MSN, MSN_ICON, 0);
 620 	accum_attribute_multival (accum, contact, _("Yahoo"), E_CONTACT_IM_YAHOO, YAHOO_ICON, 0);
 621 	accum_attribute_multival (accum, contact, _("Gadu-Gadu"), E_CONTACT_IM_GADUGADU, GADUGADU_ICON, 0);
 622 	accum_attribute_multival (accum, contact, _("Skype"), E_CONTACT_IM_SKYPE, SKYPE_ICON, 0);
 623 	accum_attribute_multival (accum, contact, _("Twitter"), E_CONTACT_IM_TWITTER, TWITTER_ICON, 0);
 624 
 625 	if (accum->len)
 626 		g_string_append_printf (
 627 			buffer,
 628 			"<div class=\"column\" id=\"contact-internet\">"
 629 			"<table border=\"0\" cellspacing=\"5\">%s</table>"
 630 			"</div>", accum->str);
 631 
 632 	g_string_free (accum, TRUE);
 633 	g_string_free (email, TRUE);
 634 }
 635 
 636 static void
 637 accum_address_map (GString *buffer,
 638                    EContact *contact,
 639                    gint map_type)
 640 {
 641         #ifdef WITH_CONTACT_MAPS
 642 
 643 	g_string_append (buffer, "<tr><td colspan=\"3\">");
 644 
 645 	if (map_type == E_CONTACT_ADDRESS_WORK) {
 646 		g_string_append (
 647 			buffer,
 648 			"<object type=\"application/x-work-map-widget\" "
 649 			"width=\"250\" height=\"250\"></object>");
 650 	} else {
 651 		g_string_append (
 652 			buffer,
 653 			"<object type=\"application/x-home-map-widget\" "
 654 			"width=\"250\" height=\"250\"></object>");
 655 	}
 656 
 657 	g_string_append (buffer, "</td></tr>");
 658 
 659         #endif
 660 }
 661 
 662 static void
 663 render_work_column (EABContactFormatter *formatter,
 664                     GString *buffer)
 665 {
 666 	EContact *contact = formatter->priv->contact;
 667 	GString *accum = g_string_new ("");
 668 
 669 	accum_attribute (accum, contact, _("Company"), E_CONTACT_ORG, NULL, 0);
 670 	accum_attribute (accum, contact, _("Department"), E_CONTACT_ORG_UNIT, NULL, 0);
 671 	accum_attribute (accum, contact, _("Profession"), E_CONTACT_ROLE, NULL, 0);
 672 	accum_attribute (accum, contact, _("Position"), E_CONTACT_TITLE, NULL, 0);
 673 	accum_attribute (accum, contact, _("Manager"), E_CONTACT_MANAGER, NULL, 0);
 674 	accum_attribute (accum, contact, _("Assistant"), E_CONTACT_ASSISTANT, NULL, 0);
 675 	accum_attribute (accum, contact, _("Video Chat"), E_CONTACT_VIDEO_URL, VIDEOCONF_ICON, E_TEXT_TO_HTML_CONVERT_URLS);
 676 	accum_attribute (accum, contact, _("Calendar"), E_CONTACT_CALENDAR_URI, NULL, E_TEXT_TO_HTML_CONVERT_URLS);
 677 	accum_attribute (accum, contact, _("Free/Busy"), E_CONTACT_FREEBUSY_URL, NULL, E_TEXT_TO_HTML_CONVERT_URLS);
 678 	accum_attribute (accum, contact, _("Phone"), E_CONTACT_PHONE_BUSINESS, NULL, 0);
 679 	accum_attribute (accum, contact, _("Fax"), E_CONTACT_PHONE_BUSINESS_FAX, NULL, 0);
 680 	accum_address   (accum, contact, _("Address"), E_CONTACT_ADDRESS_WORK, E_CONTACT_ADDRESS_LABEL_WORK);
 681 	if (formatter->priv->render_maps)
 682 		accum_address_map (accum, contact, E_CONTACT_ADDRESS_WORK);
 683 
 684 	if (accum->len > 0) {
 685 		g_string_append_printf (
 686 			buffer,
 687 			"<div class=\"column\" id=\"contact-work\">"
 688 			"<h3>%s</h3>"
 689 			"<table border=\"0\" cellspacing=\"5\">%s</table>"
 690 			"</div>", _("Work"), accum->str);
 691 	}
 692 
 693 	g_string_free (accum, TRUE);
 694 }
 695 
 696 static void
 697 render_personal_column (EABContactFormatter *formatter,
 698                         GString *buffer)
 699 {
 700 	EContact *contact = formatter->priv->contact;
 701 	GString *accum = g_string_new ("");
 702 
 703 	accum_attribute (accum, contact, _("Home Page"), E_CONTACT_HOMEPAGE_URL, NULL, E_TEXT_TO_HTML_CONVERT_URLS);
 704 	accum_attribute (accum, contact, _("Web Log"), E_CONTACT_BLOG_URL, NULL, E_TEXT_TO_HTML_CONVERT_URLS);
 705 	accum_attribute (accum, contact, _("Phone"), E_CONTACT_PHONE_HOME, NULL, 0);
 706 	accum_attribute (accum, contact, _("Mobile Phone"), E_CONTACT_PHONE_MOBILE, NULL, 0);
 707 	accum_address   (accum, contact, _("Address"), E_CONTACT_ADDRESS_HOME, E_CONTACT_ADDRESS_LABEL_HOME);
 708 	accum_time_attribute (accum, contact, _("Birthday"), E_CONTACT_BIRTH_DATE, NULL, 0);
 709 	accum_time_attribute (accum, contact, _("Anniversary"), E_CONTACT_ANNIVERSARY, NULL, 0);
 710 	accum_attribute (accum, contact, _("Spouse"), E_CONTACT_SPOUSE, NULL, 0);
 711 	if (formatter->priv->render_maps)
 712 		accum_address_map (accum, contact, E_CONTACT_ADDRESS_HOME);
 713 
 714 	if (accum->len > 0) {
 715 		g_string_append_printf (
 716 			buffer,
 717 			"<div class=\"column\" id=\"contact-personal\">"
 718 			"<h3>%s</h3>"
 719 			"<table border=\"0\" cellspacing=\"5\">%s</table>"
 720 			"</div>", _("Personal"), accum->str);
 721 	}
 722 
 723 	g_string_free (accum, TRUE);
 724 }
 725 
 726 static void
 727 render_footer (EABContactFormatter *formatter,
 728                GString *buffer)
 729 {
 730 	EContact *contact;
 731 	const gchar *str;
 732 
 733 	contact = formatter->priv->contact;
 734 
 735 	str = e_contact_get_const (contact, E_CONTACT_NOTE);
 736 	if (!str || !*str)
 737 		return;
 738 
 739 	g_string_append (
 740 		buffer,
 741 		"<div id=\"footer\"><table border=\"0\" cellspacing=\"5\">");
 742 
 743 	render_table_row (
 744 		buffer, _("Note"),
 745 		e_contact_get_const (contact, E_CONTACT_NOTE),
 746 		NULL,
 747 		E_TEXT_TO_HTML_CONVERT_ADDRESSES |
 748 		E_TEXT_TO_HTML_CONVERT_URLS |
 749 		E_TEXT_TO_HTML_CONVERT_NL);
 750 
 751 	g_string_append (buffer, "</table></div>");
 752 }
 753 
 754 static void
 755 render_contact (EABContactFormatter *formatter,
 756                 GString *buffer)
 757 {
 758 	render_title_block (formatter, buffer);
 759 
 760 	g_string_append (buffer, "<div id=\"columns\">");
 761 	render_contact_column (formatter, buffer);
 762 	render_work_column (formatter, buffer);
 763 	render_personal_column (formatter, buffer);
 764 	g_string_append (buffer, "</div>");
 765 
 766 	render_footer (formatter, buffer);
 767 }
 768 
 769 static void
 770 render_normal (EABContactFormatter *formatter,
 771                GString *buffer)
 772 {
 773 	g_string_append (buffer, HTML_HEADER);
 774 	g_string_append_printf (
 775 		buffer, "<body bgcolor=\"#%06x\" text=\"#%06x\">",
 776 		e_color_to_value (
 777 			&formatter->priv->style->base[formatter->priv->state]),
 778 		e_color_to_value (
 779 			&formatter->priv->style->text[formatter->priv->state]));
 780 
 781 	if (formatter->priv->contact) {
 782 
 783 		if (e_contact_get (formatter->priv->contact, E_CONTACT_IS_LIST))
 784 
 785 			render_contact_list (
 786 				formatter,
 787 				buffer);
 788 		else
 789 			render_contact (
 790 				formatter,
 791 				buffer);
 792 
 793 	}
 794 
 795 	g_string_append (buffer, "</body></html>\n");
 796 }
 797 
 798 static void
 799 render_compact (EABContactFormatter *formatter,
 800                 GString *buffer)
 801 {
 802 	EContact *contact = formatter->priv->contact;
 803 	const gchar *str;
 804 	gchar *html;
 805 	EContactPhoto *photo;
 806 
 807 	g_string_append (buffer, HTML_HEADER);
 808 	g_string_append (buffer, "<body>\n");
 809 
 810 	if (!contact) {
 811 		g_string_append (buffer, "</body></html>");
 812 		return;
 813 	}
 814 
 815 	g_string_append_printf (
 816 		buffer,
 817 		"<table><tr><td valign=\"top\">");
 818 
 819 	photo = e_contact_get (contact, E_CONTACT_PHOTO);
 820 
 821 	if (!photo)
 822 		photo = e_contact_get (contact, E_CONTACT_LOGO);
 823 
 824 	if (photo) {
 825 		gint calced_width = MAX_COMPACT_IMAGE_DIMENSION, calced_height = MAX_COMPACT_IMAGE_DIMENSION;
 826 		GdkPixbufLoader *loader = gdk_pixbuf_loader_new ();
 827 		GdkPixbuf *pixbuf;
 828 
 829 		/* figure out if we need to downscale the
 830 		 * image here.  we don't scale the pixbuf
 831 		 * itself, just insert width/height tags in
 832 		 * the html */
 833 		if (photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
 834 			gdk_pixbuf_loader_write (
 835 				loader, photo->data.inlined.data,
 836 				photo->data.inlined.length, NULL);
 837 		} else if (photo->type == E_CONTACT_PHOTO_TYPE_URI &&
 838 				photo->data.uri &&
 839 				g_ascii_strncasecmp (photo->data.uri, "file://", 7) == 0) {
 840 			gchar *filename, *contents = NULL;
 841 			gsize length;
 842 
 843 			filename = g_filename_from_uri (photo->data.uri, NULL, NULL);
 844 
 845 			if (filename) {
 846 				if (g_file_get_contents (filename, &contents, &length, NULL)) {
 847 					gdk_pixbuf_loader_write (loader, (const guchar *) contents, length, NULL);
 848 					g_free (contents);
 849 				}
 850 
 851 				g_free (filename);
 852 			}
 853 		}
 854 
 855 		gdk_pixbuf_loader_close (loader, NULL);
 856 		pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
 857 
 858 		if (pixbuf)
 859 			g_object_ref (pixbuf);
 860 
 861 		g_object_unref (loader);
 862 
 863 		if (pixbuf) {
 864 			gint max_dimension;
 865 
 866 			calced_width = gdk_pixbuf_get_width (pixbuf);
 867 			calced_height = gdk_pixbuf_get_height (pixbuf);
 868 
 869 			max_dimension = calced_width;
 870 
 871 			if (max_dimension < calced_height)
 872 				max_dimension = calced_height;
 873 
 874 			if (max_dimension > MAX_COMPACT_IMAGE_DIMENSION) {
 875 				calced_width *= ((gfloat) MAX_COMPACT_IMAGE_DIMENSION / max_dimension);
 876 				calced_height *= ((gfloat) MAX_COMPACT_IMAGE_DIMENSION / max_dimension);
 877 			}
 878 
 879 			g_object_unref (pixbuf);
 880 		}
 881 
 882 		if (photo->type == E_CONTACT_PHOTO_TYPE_URI &&
 883 			photo->data.uri && *photo->data.uri) {
 884 			gboolean is_local = g_str_has_prefix (photo->data.uri, "file://");
 885 			gchar *unescaped = g_uri_unescape_string (photo->data.uri, NULL);
 886 			g_string_append_printf (
 887 				buffer,
 888 				"<img width=\"%d\" height=\"%d\" src=\"%s%s\">",
 889 				calced_width, calced_height,
 890 				is_local ? "evo-" : "", unescaped);
 891 			g_free (unescaped);
 892 		} else {
 893 			gchar *photo_data;
 894 
 895 			photo_data = g_base64_encode (
 896 					photo->data.inlined.data,
 897 					photo->data.inlined.length);
 898 			g_string_append_printf (
 899 				buffer,
 900 				"<img border=\"1\" src=\"data:%s;base64,%s\" "
 901 					"width=\"%d\" height=\"%d\">",
 902 				photo->data.inlined.mime_type,
 903 				photo_data,
 904 				calced_width, calced_height);
 905 				g_free (photo_data);
 906 		}
 907 
 908 		e_contact_photo_free (photo);
 909 	}
 910 
 911 	g_string_append (buffer, "</td><td width=\"5\"></td><td valign=\"top\">\n");
 912 
 913 	str = e_contact_get_const (contact, E_CONTACT_FILE_AS);
 914 
 915 	if (str) {
 916 		html = e_text_to_html (str, 0);
 917 		g_string_append_printf (buffer, "<b>%s</b>", html);
 918 		g_free (html);
 919 	} else {
 920 		str = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
 921 
 922 		if (str) {
 923 			html = e_text_to_html (str, 0);
 924 			g_string_append_printf (buffer, "<b>%s</b>", html);
 925 			g_free (html);
 926 		}
 927 	}
 928 
 929 	g_string_append (buffer, "<hr>");
 930 
 931 	if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
 932 		GList *email_list;
 933 		GList *l;
 934 
 935 		g_string_append (
 936 			buffer,
 937 			"<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">"
 938 			"<tr><td valign=\"top\">");
 939 		g_string_append_printf (
 940 			buffer,
 941 			"<b>%s:</b>&nbsp;<td>", _ ("List Members"));
 942 
 943 		email_list = e_contact_get (contact, E_CONTACT_EMAIL);
 944 
 945 		for (l = email_list; l; l = l->next) {
 946 			if (l->data) {
 947 				html = e_text_to_html (l->data, 0);
 948 				g_string_append_printf (buffer, "%s, ", html);
 949 				g_free (html);
 950 			}
 951 		}
 952 
 953 		g_string_append (buffer, "</td></tr></table>");
 954 
 955 	} else {
 956 
 957 		gboolean comma = FALSE;
 958 		str = e_contact_get_const (contact, E_CONTACT_TITLE);
 959 
 960 		if (str) {
 961 			html = e_text_to_html (str, 0);
 962 			g_string_append_printf (buffer, "<b>%s:</b> %s<br>", _ ("Job Title"), str);
 963 			g_free (html);
 964 		}
 965 
 966 		#define print_email() {								\
 967 			html = eab_parse_qp_email_to_html (str);				\
 968 			\
 969 			if (!html)                                                              \
 970 				html = e_text_to_html (str, 0);					\
 971 			\
 972 			g_string_append_printf (buffer, "%s%s", comma ? ", " : "", html);	\
 973 			g_free (html);								\
 974 			comma = TRUE;								\
 975 		}
 976 
 977 		g_string_append_printf (buffer, "<b>%s:</b> ", _ ("Email"));
 978 		str = e_contact_get_const (contact, E_CONTACT_EMAIL_1);
 979 
 980 		if (str)
 981 			print_email ();
 982 
 983 		str = e_contact_get_const (contact, E_CONTACT_EMAIL_2);
 984 
 985 		if (str)
 986 			print_email ();
 987 
 988 		str = e_contact_get_const (contact, E_CONTACT_EMAIL_3);
 989 
 990 		if (str)
 991 			print_email ();
 992 
 993 		g_string_append (buffer, "<br>");
 994 
 995 		#undef print_email
 996 
 997 		str = e_contact_get_const (contact, E_CONTACT_HOMEPAGE_URL);
 998 
 999 		if (str) {
1000 			html = e_text_to_html (str, E_TEXT_TO_HTML_CONVERT_URLS);
1001 			g_string_append_printf (
1002 				buffer, "<b>%s:</b> %s<br>",
1003 				_ ("Home page"), html);
1004 			g_free (html);
1005 		}
1006 
1007 		str = e_contact_get_const (contact, E_CONTACT_BLOG_URL);
1008 
1009 		if (str) {
1010 			html = e_text_to_html (str, E_TEXT_TO_HTML_CONVERT_URLS);
1011 			g_string_append_printf (
1012 				buffer, "<b>%s:</b> %s<br>",
1013 				_ ("Blog"), html);
1014 		}
1015 	}
1016 
1017 	g_string_append (buffer, "</td></tr></table>\n");
1018 
1019 	g_string_append (buffer, "</body></html>\n");
1020 }
1021 
1022 static CamelStream *
1023 format_contact (EABContactFormatter *formatter,
1024                 GCancellable *cancellable)
1025 {
1026 	GString *buffer;
1027 	CamelStream *stream;
1028 
1029 	buffer = g_string_new ("");
1030 
1031 	if (formatter->priv->mode == EAB_CONTACT_DISPLAY_RENDER_NORMAL) {
1032 		render_normal (formatter, buffer);
1033 	} else {
1034 		render_compact (formatter, buffer);
1035 	}
1036 
1037 	stream = camel_stream_mem_new ();
1038 	camel_stream_write_string (stream, buffer->str, cancellable, NULL);
1039 
1040 	g_string_free (buffer, TRUE);
1041 
1042 	return stream;
1043 }
1044 
1045 static void
1046 do_start_async_formatter (GSimpleAsyncResult *result,
1047                           GObject *object,
1048                           GCancellable *cancellable)
1049 {
1050 	EABContactFormatter *formatter;
1051 	CamelStream *stream;
1052 
1053 	formatter = EAB_CONTACT_FORMATTER (object);
1054 
1055 	stream = format_contact (formatter, cancellable);
1056 
1057 	g_simple_async_result_set_op_res_gpointer (result, stream, NULL);
1058 }
1059 
1060 static void
1061 eab_contact_formatter_set_property (GObject *object,
1062                                     guint property_id,
1063                                     const GValue *value,
1064                                     GParamSpec *pspec)
1065 {
1066 	EABContactFormatter *formatter = EAB_CONTACT_FORMATTER (object);
1067 
1068 	switch (property_id) {
1069 		case PROP_DISPLAY_MODE:
1070 			eab_contact_formatter_set_display_mode (
1071 				formatter, g_value_get_int (value));
1072 			return;
1073 		case PROP_RENDER_MAPS:
1074 			eab_contact_formatter_set_render_maps (
1075 				formatter, g_value_get_boolean (value));
1076 			return;
1077 		case PROP_STYLE:
1078 			eab_contact_formatter_set_style (
1079 				formatter, g_value_get_object (value));
1080 			return;
1081 		case PROP_STATE:
1082 			eab_contact_formatter_set_state (
1083 				formatter, g_value_get_uint (value));
1084 			return;
1085 	}
1086 
1087 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1088 }
1089 
1090 static void
1091 eab_contact_formatter_get_property (GObject *object,
1092                                     guint property_id,
1093                                     GValue *value,
1094                                     GParamSpec *pspec)
1095 {
1096 	EABContactFormatter *formatter = EAB_CONTACT_FORMATTER (object);
1097 
1098 	switch (property_id) {
1099 		case PROP_DISPLAY_MODE:
1100 			g_value_set_int (
1101 				value,
1102 				eab_contact_formatter_get_display_mode (
1103 					formatter));
1104 			return;
1105 		case PROP_RENDER_MAPS:
1106 			g_value_set_boolean (
1107 				value,
1108 				eab_contact_formatter_get_render_maps (
1109 					formatter));
1110 			return;
1111 		case PROP_STYLE:
1112 			g_value_set_object (
1113 				value,
1114 				eab_contact_formatter_get_style (
1115 					formatter));
1116 			return;
1117 		case PROP_STATE:
1118 			g_value_set_uint (
1119 				value,
1120 				eab_contact_formatter_get_state (
1121 					formatter));
1122 			return;
1123 	}
1124 
1125 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1126 }
1127 
1128 static void
1129 eab_contact_formatter_dispose (GObject *object)
1130 {
1131 	EABContactFormatter *formatter;
1132 
1133 	formatter = EAB_CONTACT_FORMATTER (object);
1134 
1135 	if (formatter->priv->contact) {
1136 		g_object_unref (formatter->priv->contact);
1137 		formatter->priv->contact = NULL;
1138 	}
1139 
1140 	if (formatter->priv->style) {
1141 		g_object_unref (formatter->priv->style);
1142 		formatter->priv->style = NULL;
1143 	}
1144 
1145 	G_OBJECT_CLASS (eab_contact_formatter_parent_class)->dispose (object);
1146 }
1147 
1148 static void
1149 eab_contact_formatter_class_init (EABContactFormatterClass *class)
1150 {
1151 	GObjectClass *object_class;
1152 
1153 	g_type_class_add_private (class, sizeof (EABContactFormatterClass));
1154 
1155 	object_class = G_OBJECT_CLASS (class);
1156 	object_class->dispose = eab_contact_formatter_dispose;
1157 	object_class->set_property = eab_contact_formatter_set_property;
1158 	object_class->get_property = eab_contact_formatter_get_property;
1159 
1160 	g_object_class_install_property (
1161 		object_class,
1162 		PROP_DISPLAY_MODE,
1163 		g_param_spec_int (
1164 			"display-mode",
1165 			"",
1166 			"",
1167 			EAB_CONTACT_DISPLAY_RENDER_NORMAL,
1168 			EAB_CONTACT_DISPLAY_RENDER_COMPACT,
1169 			EAB_CONTACT_DISPLAY_RENDER_NORMAL,
1170 			G_PARAM_READWRITE));
1171 
1172 	g_object_class_install_property (
1173 		object_class,
1174 		PROP_RENDER_MAPS,
1175 		g_param_spec_boolean (
1176 			"render-maps",
1177 			"",
1178 			"",
1179 			FALSE,
1180 			G_PARAM_READWRITE));
1181 
1182 	g_object_class_install_property (
1183 		object_class,
1184 		PROP_STYLE,
1185 		g_param_spec_object (
1186 			"style",
1187 			NULL,
1188 			NULL,
1189 			GTK_TYPE_STYLE,
1190 			G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
1191 
1192 	g_object_class_install_property (
1193 		object_class,
1194 		PROP_STATE,
1195 		g_param_spec_uint (
1196 			"state",
1197 			NULL,
1198 			NULL,
1199 			0,
1200 			G_MAXUINT,
1201 			0,
1202 			G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
1203 }
1204 
1205 static void
1206 eab_contact_formatter_init (EABContactFormatter *formatter)
1207 {
1208 	formatter->priv = EAB_CONTACT_FORMATTER_GET_PRIVATE (formatter);
1209 
1210 	formatter->priv->contact = NULL;
1211 	formatter->priv->mode = EAB_CONTACT_DISPLAY_RENDER_NORMAL;
1212 	formatter->priv->render_maps = FALSE;
1213 }
1214 
1215 EABContactFormatter *
1216 eab_contact_formatter_new (EABContactDisplayMode mode,
1217                            gboolean render_maps)
1218 {
1219 	return g_object_new (
1220 		EAB_TYPE_CONTACT_FORMATTER,
1221 		"display-mode", mode,
1222 		"render-maps", render_maps,
1223 		NULL);
1224 }
1225 
1226 void
1227 eab_contact_formatter_set_display_mode (EABContactFormatter *formatter,
1228                                         EABContactDisplayMode mode)
1229 {
1230 	g_return_if_fail (EAB_IS_CONTACT_FORMATTER (formatter));
1231 
1232 	if (formatter->priv->mode == mode)
1233 		return;
1234 
1235 	formatter->priv->mode = mode;
1236 
1237 	g_object_notify (G_OBJECT (formatter), "display-mode");
1238 }
1239 
1240 EABContactDisplayMode
1241 eab_contact_formatter_get_display_mode (EABContactFormatter *formatter)
1242 {
1243 	g_return_val_if_fail (EAB_IS_CONTACT_FORMATTER (formatter),
1244 			      EAB_CONTACT_DISPLAY_RENDER_NORMAL);
1245 
1246 	return formatter->priv->mode;
1247 }
1248 
1249 void
1250 eab_contact_formatter_set_render_maps (EABContactFormatter *formatter,
1251                                        gboolean render_maps)
1252 {
1253 	g_return_if_fail (EAB_IS_CONTACT_FORMATTER (formatter));
1254 
1255 	if (formatter->priv->render_maps == render_maps)
1256 		return;
1257 
1258 	formatter->priv->render_maps = render_maps;
1259 
1260 	g_object_notify (G_OBJECT (formatter), "render-maps");
1261 }
1262 
1263 gboolean
1264 eab_contact_formatter_get_render_maps (EABContactFormatter *formatter)
1265 {
1266 	g_return_val_if_fail (EAB_IS_CONTACT_FORMATTER (formatter), FALSE);
1267 
1268 	return formatter->priv->render_maps;
1269 }
1270 
1271 void
1272 eab_contact_formatter_set_style (EABContactFormatter *formatter,
1273                                  GtkStyle *style)
1274 {
1275 	g_return_if_fail (EAB_IS_CONTACT_FORMATTER (formatter));
1276 
1277 	if (formatter->priv->style == style) {
1278 		return;
1279 	}
1280 
1281 	g_clear_object (&formatter->priv->style);
1282 
1283 	if (style != NULL) {
1284 		formatter->priv->style = g_object_ref (style);
1285 	}
1286 
1287 	g_object_notify (G_OBJECT (formatter), "style");
1288 }
1289 
1290 GtkStyle *
1291 eab_contact_formatter_get_style (EABContactFormatter *formatter)
1292 {
1293 	g_return_val_if_fail (EAB_IS_CONTACT_FORMATTER (formatter), NULL);
1294 
1295 	return formatter->priv->style;
1296 }
1297 
1298 void
1299 eab_contact_formatter_set_state (EABContactFormatter *formatter,
1300                                  GtkStateType state)
1301 {
1302 	g_return_if_fail (EAB_IS_CONTACT_FORMATTER (formatter));
1303 
1304 	if (formatter->priv->state == state)
1305 		return;
1306 
1307 	formatter->priv->state = state;
1308 
1309 	g_object_notify (G_OBJECT (formatter), "state");
1310 }
1311 
1312 GtkStateType
1313 eab_contact_formatter_get_state (EABContactFormatter *formatter)
1314 {
1315 	g_return_val_if_fail (EAB_IS_CONTACT_FORMATTER (formatter), 0);
1316 
1317 	return formatter->priv->state;
1318 }
1319 
1320 void
1321 eab_contact_formatter_format_contact_sync (EABContactFormatter *formatter,
1322                                            EContact *contact,
1323                                            CamelStream *stream,
1324                                            GCancellable *cancellable)
1325 {
1326 	CamelStream *out;
1327 
1328 	g_return_if_fail (EAB_IS_CONTACT_FORMATTER (formatter));
1329 	g_return_if_fail (E_IS_CONTACT (contact));
1330 
1331 	g_object_ref (contact);
1332 
1333 	if (formatter->priv->contact)
1334 		g_object_unref (formatter->priv->contact);
1335 
1336 	formatter->priv->contact = contact;
1337 
1338 	out = format_contact (formatter, cancellable);
1339 
1340 	g_seekable_seek (G_SEEKABLE (out), 0, G_SEEK_SET, cancellable, NULL);
1341 	camel_stream_write_to_stream (out, stream, cancellable, NULL);
1342 
1343 	g_object_unref (out);
1344 }
1345 
1346 void
1347 eab_contact_formatter_format_contact_async (EABContactFormatter *formatter,
1348                                             EContact *contact,
1349                                             GCancellable *cancellable,
1350                                             GAsyncReadyCallback callback,
1351                                             gpointer user_data)
1352 {
1353 	GSimpleAsyncResult *simple;
1354 
1355 	g_return_if_fail (EAB_IS_CONTACT_FORMATTER (formatter));
1356 	g_return_if_fail (E_IS_CONTACT (contact));
1357 	g_return_if_fail (callback != NULL);
1358 
1359 	g_object_ref (contact);
1360 	if (formatter->priv->contact)
1361 		g_object_unref (formatter->priv->contact);
1362 
1363 	formatter->priv->contact = contact;
1364 
1365 	simple = g_simple_async_result_new (
1366 		G_OBJECT (formatter), callback, user_data,
1367 		eab_contact_formatter_format_contact_async);
1368 
1369 	g_simple_async_result_set_check_cancellable (simple, cancellable);
1370 
1371 	g_simple_async_result_run_in_thread (
1372 		simple, do_start_async_formatter,
1373 		G_PRIORITY_DEFAULT, cancellable);
1374 
1375 	g_object_unref (simple);
1376 }
1377 
1378 static void
1379 collapse_contacts_list (WebKitDOMEventTarget *event_target,
1380                         WebKitDOMEvent *event,
1381                         gpointer user_data)
1382 {
1383 	WebKitDOMDocument *document;
1384 	WebKitDOMElement *list;
1385 	gchar *id, *list_id;
1386 	gchar *imagesdir, *src;
1387 	gboolean hidden;
1388 
1389 	document = user_data;
1390 	id = webkit_dom_html_element_get_id (WEBKIT_DOM_HTML_ELEMENT (event_target));
1391 
1392 	list_id = g_strconcat ("list-", id, NULL);
1393 	list = webkit_dom_document_get_element_by_id (document, list_id);
1394 	g_free (id);
1395 	g_free (list_id);
1396 
1397 	if (!list)
1398 		return;
1399 
1400 	imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
1401 	hidden = webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (list));
1402 
1403 	if (hidden) {
1404 		src = g_strdup_printf ("evo-file://%s/minus.png", imagesdir);
1405 	} else {
1406 		src = g_strdup_printf ("evo-file://%s/plus.png", imagesdir);
1407 	}
1408 
1409 	webkit_dom_html_element_set_hidden (
1410 		WEBKIT_DOM_HTML_ELEMENT (list), !hidden);
1411 	webkit_dom_html_image_element_set_src (
1412 		WEBKIT_DOM_HTML_IMAGE_ELEMENT (event_target), src);
1413 
1414 	g_free (src);
1415 	g_free (imagesdir);
1416 }
1417 
1418 void
1419 eab_contact_formatter_bind_dom (WebKitDOMDocument *document)
1420 {
1421 	WebKitDOMNodeList *nodes;
1422 	gulong i, length;
1423 
1424 	nodes = webkit_dom_document_get_elements_by_class_name (
1425 			document, "_evo_collapse_button");
1426 
1427 	length = webkit_dom_node_list_get_length (nodes);
1428 	for (i = 0; i < length; i++) {
1429 
1430 		WebKitDOMNode *node;
1431 
1432 		node = webkit_dom_node_list_item (nodes, i);
1433 		webkit_dom_event_target_add_event_listener (
1434 			WEBKIT_DOM_EVENT_TARGET (node), "click",
1435 			G_CALLBACK (collapse_contacts_list), FALSE, document);
1436 	}
1437 }