evolution-3.6.4/modules/itip-formatter/itip-view.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  * Authors:
  17  *		JP Rosevear <jpr@novell.com>
  18  *
  19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  20  *
  21  */
  22 
  23 #ifdef HAVE_CONFIG_H
  24 #include <config.h>
  25 #endif
  26 
  27 #include <string.h>
  28 #include <glib/gi18n.h>
  29 #include <libedataserverui/libedataserverui.h>
  30 #include <libedataserver/libedataserver.h>
  31 
  32 #include <e-util/e-util.h>
  33 #include <e-util/e-unicode.h>
  34 #include <calendar/gui/itip-utils.h>
  35 #include <webkit/webkitdom.h>
  36 
  37 #include <libevolution-utils/e-alert-dialog.h>
  38 #include <e-util/e-mktemp.h>
  39 
  40 #include <shell/e-shell.h>
  41 #include <shell/e-shell-utils.h>
  42 
  43 #include <libemail-utils/mail-mt.h>
  44 
  45 #include <libemail-engine/mail-folder-cache.h>
  46 #include <libemail-engine/mail-tools.h>
  47 
  48 #include <mail/em-config.h>
  49 #include <mail/em-utils.h>
  50 #include <em-format/e-mail-formatter-utils.h>
  51 
  52 #include <calendar/gui/itip-utils.h>
  53 
  54 #include "e-conflict-search-selector.h"
  55 #include "e-source-conflict-search.h"
  56 #include "itip-view.h"
  57 #include "e-mail-part-itip.h"
  58 
  59 #define d(x)
  60 
  61 #define MEETING_ICON "stock_new-meeting"
  62 
  63 #define ITIP_VIEW_GET_PRIVATE(obj) \
  64 	(G_TYPE_INSTANCE_GET_PRIVATE \
  65 	((obj), ITIP_TYPE_VIEW, ItipViewPrivate))
  66 
  67 G_DEFINE_TYPE (ItipView, itip_view, G_TYPE_OBJECT)
  68 
  69 typedef struct  {
  70 	ItipViewInfoItemType type;
  71 	gchar *message;
  72 
  73 	guint id;
  74 } ItipViewInfoItem;
  75 
  76 struct _ItipViewPrivate {
  77 	ESourceRegistry *registry;
  78 	gulong source_added_id;
  79 	gulong source_removed_id;
  80 	gchar *extension_name;
  81 
  82 	ItipViewMode mode;
  83 	ECalClientSourceType type;
  84 
  85         gchar *sender;
  86 	gchar *organizer;
  87 	gchar *organizer_sentby;
  88 	gchar *delegator;
  89 	gchar *attendee;
  90 	gchar *attendee_sentby;
  91 	gchar *proxy;
  92 
  93 	gchar *summary;
  94 
  95 	gchar *location;
  96         gchar *status;
  97 	gchar *comment;
  98 
  99 	struct tm *start_tm;
 100 	gint start_tm_is_date : 1;
 101         gchar *start_label;
 102         const gchar *start_header;
 103 
 104 	struct tm *end_tm;
 105 	gint end_tm_is_date : 1;
 106         gchar *end_label;
 107         const gchar *end_header;
 108 
 109 	GSList *upper_info_items;
 110 	GSList *lower_info_items;
 111 
 112 	guint next_info_item_id;
 113 
 114 	gchar *description;
 115 
 116 	gint buttons_sensitive : 1;
 117 
 118         gboolean is_recur_set;
 119 
 120 	gint needs_decline : 1;
 121 
 122         WebKitDOMDocument *dom_document;
 123         EMailPartItip *itip_part;
 124 
 125         gchar *error;
 126 };
 127 
 128 #define TEXT_ROW_SENDER "text_row_sender"
 129 #define TABLE_ROW_SUMMARY "table_row_summary"
 130 #define TABLE_ROW_LOCATION "table_row_location"
 131 #define TABLE_ROW_START_DATE "table_row_start_time"
 132 #define TABLE_ROW_END_DATE "table_row_end_time"
 133 #define TABLE_ROW_STATUS "table_row_status"
 134 #define TABLE_ROW_COMMENT "table_row_comment"
 135 #define TABLE_ROW_DESCRIPTION "table_row_description"
 136 #define TABLE_ROW_RSVP_COMMENT "table_row_rsvp_comment"
 137 #define TABLE_ROW_ESCB "table_row_escb"
 138 #define TABLE_ROW_BUTTONS "table_row_buttons"
 139 #define TABLE_ROW_ESCB_LABEL "table_row_escb_label"
 140 
 141 #define TABLE_BUTTONS "table_buttons"
 142 
 143 #define SELECT_ESOURCE "select_esource"
 144 #define TEXTAREA_RSVP_COMMENT "textarea_rsvp_comment"
 145 
 146 #define CHECKBOX_RSVP "checkbox_rsvp"
 147 #define CHECKBOX_RECUR "checkbox_recur"
 148 #define CHECKBOX_UPDATE "checkbox_update"
 149 #define CHECKBOX_FREE_TIME "checkbox_free_time"
 150 #define CHECKBOX_KEEP_ALARM "checkbox_keep_alarm"
 151 #define CHECKBOX_INHERIT_ALARM "checkbox_inherit_alarm"
 152 
 153 #define BUTTON_OPEN_CALENDAR "button_open_calendar"
 154 #define BUTTON_DECLINE "button_decline"
 155 #define BUTTON_DECLINE_ALL "button_decline_all"
 156 #define BUTTON_ACCEPT "button_accept"
 157 #define BUTTON_ACCEPT_ALL "button_accept_all"
 158 #define BUTTON_TENTATIVE "button_tentative"
 159 #define BUTTON_TENTATIVE_ALL "button_tentative_all"
 160 #define BUTTON_SEND_INFORMATION "button_send_information"
 161 #define BUTTON_UPDATE "button_update"
 162 #define BUTTON_UPDATE_ATTENDEE_STATUS "button_update_attendee_status"
 163 #define BUTTON_SAVE "button_save"
 164 
 165 #define TABLE_UPPER_ITIP_INFO "table_upper_itip_info"
 166 #define TABLE_LOWER_ITIP_INFO "table_lower_itip_info"
 167 
 168 #define DIV_ITIP_CONTENT "div_itip_content"
 169 #define DIV_ITIP_ERROR "div_itip_error"
 170 
 171 enum {
 172 	PROP_0,
 173 	PROP_EXTENSION_NAME,
 174 	PROP_REGISTRY
 175 };
 176 
 177 enum {
 178 	SOURCE_SELECTED,
 179 	RESPONSE,
 180 	LAST_SIGNAL
 181 };
 182 
 183 static guint signals[LAST_SIGNAL] = { 0 };
 184 
 185 static void
 186 format_date_and_time_x (struct tm *date_tm,
 187                         struct tm *current_tm,
 188                         gboolean use_24_hour_format,
 189                         gboolean show_midnight,
 190                         gboolean show_zero_seconds,
 191                         gboolean is_date,
 192                         gchar *buffer,
 193                         gint buffer_size)
 194 {
 195 	gchar *format;
 196 	struct tm tomorrow_tm, week_tm;
 197 
 198 	/* Calculate a normalized "tomorrow" */
 199 	tomorrow_tm = *current_tm;
 200 	/* Don't need this if date is in the past. Also, year assumption won't fail. */
 201 	if (date_tm->tm_year >= current_tm->tm_year && tomorrow_tm.tm_mday == time_days_in_month (current_tm->tm_year + 1900, current_tm->tm_mon)) {
 202 		tomorrow_tm.tm_mday = 1;
 203 		if (tomorrow_tm.tm_mon == 11) {
 204 			tomorrow_tm.tm_mon = 1;
 205 			tomorrow_tm.tm_year++;
 206 		} else {
 207 			tomorrow_tm.tm_mon++;
 208 		}
 209 	} else {
 210 		tomorrow_tm.tm_mday++;
 211 	}
 212 
 213 	/* Calculate a normalized "next seven days" */
 214 	week_tm = *current_tm;
 215 	/* Don't need this if date is in the past. Also, year assumption won't fail. */
 216 	if (date_tm->tm_year >= current_tm->tm_year && week_tm.tm_mday + 6 > time_days_in_month (date_tm->tm_year + 1900, date_tm->tm_mon)) {
 217 		week_tm.tm_mday = (week_tm.tm_mday + 6) % time_days_in_month (date_tm->tm_year + 1900, date_tm->tm_mon);
 218 		if (week_tm.tm_mon == 11) {
 219 			week_tm.tm_mon = 1;
 220 			week_tm.tm_year++;
 221 		} else {
 222 			week_tm.tm_mon++;
 223 		}
 224 	} else {
 225 		week_tm.tm_mday += 6;
 226 	}
 227 
 228 	/* Today */
 229 	if (date_tm->tm_mday == current_tm->tm_mday &&
 230 	    date_tm->tm_mon == current_tm->tm_mon &&
 231 	    date_tm->tm_year == current_tm->tm_year) {
 232 		if (is_date || (!show_midnight && date_tm->tm_hour == 0
 233 		    && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
 234 			/* strftime format of a weekday and a date. */
 235 			format = _("Today");
 236 		} else if (use_24_hour_format) {
 237 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 238 				/* strftime format of a time,
 239 				 * in 24-hour format, without seconds. */
 240 				format = _("Today %H:%M");
 241 			else
 242 				/* strftime format of a time,
 243 				 * in 24-hour format. */
 244 				format = _("Today %H:%M:%S");
 245 		} else {
 246 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 247 				/* strftime format of a time,
 248 				 * in 12-hour format, without seconds. */
 249 				format = _("Today %l:%M %p");
 250 			else
 251 				/* strftime format of a time,
 252 				 * in 12-hour format. */
 253 				format = _("Today %l:%M:%S %p");
 254 		}
 255 
 256 	/* Tomorrow */
 257 	} else if (date_tm->tm_mday == tomorrow_tm.tm_mday &&
 258 		   date_tm->tm_mon == tomorrow_tm.tm_mon &&
 259 		   date_tm->tm_year == tomorrow_tm.tm_year) {
 260 		if (is_date || (!show_midnight && date_tm->tm_hour == 0
 261 		    && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
 262 			/* strftime format of a weekday and a date. */
 263 			format = _("Tomorrow");
 264 		} else if (use_24_hour_format) {
 265 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 266 				/* strftime format of a time,
 267 				 * in 24-hour format, without seconds. */
 268 				format = _("Tomorrow %H:%M");
 269 			else
 270 				/* strftime format of a time,
 271 				 * in 24-hour format. */
 272 				format = _("Tomorrow %H:%M:%S");
 273 		} else {
 274 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 275 				/* strftime format of a time,
 276 				 * in 12-hour format, without seconds. */
 277 				format = _("Tomorrow %l:%M %p");
 278 			else
 279 				/* strftime format of a time,
 280 				 * in 12-hour format. */
 281 				format = _("Tomorrow %l:%M:%S %p");
 282 		}
 283 
 284 	/* Within 6 days */
 285 	} else if ((date_tm->tm_year >= current_tm->tm_year &&
 286 		    date_tm->tm_mon >= current_tm->tm_mon &&
 287 		    date_tm->tm_mday >= current_tm->tm_mday) &&
 288 
 289 		   (date_tm->tm_year < week_tm.tm_year ||
 290 
 291 		   (date_tm->tm_year == week_tm.tm_year &&
 292 		    date_tm->tm_mon < week_tm.tm_mon) ||
 293 
 294 		   (date_tm->tm_year == week_tm.tm_year &&
 295 		    date_tm->tm_mon == week_tm.tm_mon &&
 296 		    date_tm->tm_mday < week_tm.tm_mday))) {
 297 		if (is_date || (!show_midnight && date_tm->tm_hour == 0
 298 		    && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
 299 			/* strftime format of a weekday. */
 300 			format = _("%A");
 301 		} else if (use_24_hour_format) {
 302 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 303 				/* strftime format of a weekday and a
 304 				 * time, in 24-hour format, without seconds. */
 305 				format = _("%A %H:%M");
 306 			else
 307 				/* strftime format of a weekday and a
 308 				 * time, in 24-hour format. */
 309 				format = _("%A %H:%M:%S");
 310 		} else {
 311 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 312 				/* strftime format of a weekday and a
 313 				 * time, in 12-hour format, without seconds. */
 314 				format = _("%A %l:%M %p");
 315 			else
 316 				/* strftime format of a weekday and a
 317 				 * time, in 12-hour format. */
 318 				format = _("%A %l:%M:%S %p");
 319 		}
 320 
 321 	/* This Year */
 322 	} else if (date_tm->tm_year == current_tm->tm_year) {
 323 		if (is_date || (!show_midnight && date_tm->tm_hour == 0
 324 		    && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
 325 			/* strftime format of a weekday and a date
 326 			 * without a year. */
 327 			format = _("%A, %B %e");
 328 		} else if (use_24_hour_format) {
 329 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 330 				/* strftime format of a weekday, a date
 331 				 * without a year and a time,
 332 				 * in 24-hour format, without seconds. */
 333 				format = _("%A, %B %e %H:%M");
 334 			else
 335 				/* strftime format of a weekday, a date without a year
 336 				 * and a time, in 24-hour format. */
 337 				format = _("%A, %B %e %H:%M:%S");
 338 		} else {
 339 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 340 				/* strftime format of a weekday, a date without a year
 341 				 * and a time, in 12-hour format, without seconds. */
 342 				format = _("%A, %B %e %l:%M %p");
 343 			else
 344 				/* strftime format of a weekday, a date without a year
 345 				 * and a time, in 12-hour format. */
 346 				format = _("%A, %B %e %l:%M:%S %p");
 347 		}
 348 	} else {
 349 		if (is_date || (!show_midnight && date_tm->tm_hour == 0
 350 		    && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
 351 			/* strftime format of a weekday and a date. */
 352 			format = _("%A, %B %e, %Y");
 353 		} else if (use_24_hour_format) {
 354 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 355 				/* strftime format of a weekday, a date and a
 356 				 * time, in 24-hour format, without seconds. */
 357 				format = _("%A, %B %e, %Y %H:%M");
 358 			else
 359 				/* strftime format of a weekday, a date and a
 360 				 * time, in 24-hour format. */
 361 				format = _("%A, %B %e, %Y %H:%M:%S");
 362 		} else {
 363 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 364 				/* strftime format of a weekday, a date and a
 365 				 * time, in 12-hour format, without seconds. */
 366 				format = _("%A, %B %e, %Y %l:%M %p");
 367 			else
 368 				/* strftime format of a weekday, a date and a
 369 				 * time, in 12-hour format. */
 370 				format = _("%A, %B %e, %Y %l:%M:%S %p");
 371 		}
 372 	}
 373 
 374 	/* strftime returns 0 if the string doesn't fit, and leaves the buffer
 375 	 * undefined, so we set it to the empty string in that case. */
 376 	if (e_utf8_strftime_fix_am_pm (buffer, buffer_size, format, date_tm) == 0)
 377 		buffer[0] = '\0';
 378 }
 379 
 380 static gchar *
 381 dupe_first_bold (const gchar *format,
 382                  const gchar *first,
 383                  const gchar *second)
 384 {
 385 	gchar *f, *s, *res;
 386 
 387 	f = g_markup_printf_escaped ("<b>%s</b>", first ? first : "");
 388 	s = g_markup_escape_text (second ? second : "", -1);
 389 
 390 	res = g_strdup_printf (format, f, s);
 391 
 392 	g_free (f);
 393 	g_free (s);
 394 
 395 	return res;
 396 }
 397 
 398 static gchar *
 399 set_calendar_sender_text (ItipView *view)
 400 {
 401 	ItipViewPrivate *priv;
 402 	const gchar *organizer, *attendee;
 403 	gchar *sender = NULL;
 404 	gchar *on_behalf_of = NULL;
 405 
 406 	priv = view->priv;
 407 
 408 	organizer = priv->organizer ? priv->organizer : _("An unknown person");
 409 	attendee = priv->attendee ? priv->attendee : _("An unknown person");
 410 
 411 	/* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
 412 	if (priv->organizer && priv->proxy)
 413 		on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
 414 	else if (priv->attendee && priv->proxy)
 415 		on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);
 416 
 417 	switch (priv->mode) {
 418 	case ITIP_VIEW_MODE_PUBLISH:
 419 		if (priv->organizer_sentby)
 420 			sender = dupe_first_bold (_("%s through %s has published the following meeting information:"), organizer, priv->organizer_sentby);
 421 		else
 422 			sender = dupe_first_bold (_("%s has published the following meeting information:"), organizer, NULL);
 423 		break;
 424 	case ITIP_VIEW_MODE_REQUEST:
 425 		/* FIXME is the delegator stuff handled correctly here? */
 426 		if (priv->delegator) {
 427 			sender = dupe_first_bold (_("%s has delegated the following meeting to you:"), priv->delegator, NULL);
 428 		} else {
 429 			if (priv->organizer_sentby)
 430 				sender = dupe_first_bold (_("%s through %s requests your presence at the following meeting:"), organizer, priv->organizer_sentby);
 431 			else
 432 				sender = dupe_first_bold (_("%s requests your presence at the following meeting:"), organizer, NULL);
 433 		}
 434 		break;
 435 	case ITIP_VIEW_MODE_ADD:
 436 		/* FIXME What text for this? */
 437 		if (priv->organizer_sentby)
 438 			sender = dupe_first_bold (_("%s through %s wishes to add to an existing meeting:"), organizer, priv->organizer_sentby);
 439 		else
 440 			sender = dupe_first_bold (_("%s wishes to add to an existing meeting:"), organizer, NULL);
 441 		break;
 442 	case ITIP_VIEW_MODE_REFRESH:
 443 		if (priv->attendee_sentby)
 444 			sender = dupe_first_bold (_("%s through %s wishes to receive the latest information for the following meeting:"), attendee, priv->attendee_sentby);
 445 		else
 446 			sender = dupe_first_bold (_("%s wishes to receive the latest information for the following meeting:"), attendee, NULL);
 447 		break;
 448 	case ITIP_VIEW_MODE_REPLY:
 449 		if (priv->attendee_sentby)
 450 			sender = dupe_first_bold (_("%s through %s has sent back the following meeting response:"), attendee, priv->attendee_sentby);
 451 		else
 452 			sender = dupe_first_bold (_("%s has sent back the following meeting response:"), attendee, NULL);
 453 		break;
 454 	case ITIP_VIEW_MODE_CANCEL:
 455 		if (priv->organizer_sentby)
 456 			sender = dupe_first_bold (_("%s through %s has canceled the following meeting:"), organizer, priv->organizer_sentby);
 457 		else
 458 			sender = dupe_first_bold (_("%s has canceled the following meeting:"), organizer, NULL);
 459 		break;
 460 	case ITIP_VIEW_MODE_COUNTER:
 461 		if (priv->attendee_sentby)
 462 			sender = dupe_first_bold (_("%s through %s has proposed the following meeting changes."), attendee, priv->attendee_sentby);
 463 		else
 464 			sender = dupe_first_bold (_("%s has proposed the following meeting changes:"), attendee, NULL);
 465 		break;
 466 	case ITIP_VIEW_MODE_DECLINECOUNTER:
 467 		if (priv->organizer_sentby)
 468 			sender = dupe_first_bold (_("%s through %s has declined the following meeting changes:"), organizer, priv->organizer_sentby);
 469 		else
 470 			sender = dupe_first_bold (_("%s has declined the following meeting changes:"), organizer, NULL);
 471 		break;
 472 	default:
 473 		break;
 474 	}
 475 
 476 	if (sender && on_behalf_of) {
 477 		gchar *tmp;
 478 		tmp = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
 479 		g_free (sender);
 480 		sender = tmp;
 481 	}
 482 
 483 	g_free (on_behalf_of);
 484 
 485 	return sender;
 486 }
 487 
 488 static gchar *
 489 set_tasklist_sender_text (ItipView *view)
 490 {
 491 	ItipViewPrivate *priv;
 492 	const gchar *organizer, *attendee;
 493 	gchar *sender = NULL;
 494 	gchar *on_behalf_of = NULL;
 495 
 496 	priv = view->priv;
 497 
 498 	organizer = priv->organizer ? priv->organizer : _("An unknown person");
 499 	attendee = priv->attendee ? priv->attendee : _("An unknown person");
 500 
 501 	/* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
 502 	if (priv->organizer && priv->proxy)
 503 		on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
 504 	else if (priv->attendee && priv->proxy)
 505 		on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);
 506 
 507 	switch (priv->mode) {
 508 	case ITIP_VIEW_MODE_PUBLISH:
 509 		if (priv->organizer_sentby)
 510 			sender = dupe_first_bold (_("%s through %s has published the following task:"), organizer, priv->organizer_sentby);
 511 		else
 512 			sender = dupe_first_bold (_("%s has published the following task:"), organizer, NULL);
 513 		break;
 514 	case ITIP_VIEW_MODE_REQUEST:
 515 		/* FIXME is the delegator stuff handled correctly here? */
 516 		if (priv->delegator) {
 517 			sender = dupe_first_bold (_("%s requests the assignment of %s to the following task:"), organizer, priv->delegator);
 518 		} else {
 519 			if (priv->organizer_sentby)
 520 				sender = dupe_first_bold (_("%s through %s has assigned you a task:"), organizer, priv->organizer_sentby);
 521 			else
 522 				sender = dupe_first_bold (_("%s has assigned you a task:"), organizer, NULL);
 523 		}
 524 		break;
 525 	case ITIP_VIEW_MODE_ADD:
 526 		/* FIXME What text for this? */
 527 		if (priv->organizer_sentby)
 528 			sender = dupe_first_bold (_("%s through %s wishes to add to an existing task:"), organizer, priv->organizer_sentby);
 529 		else
 530 			sender = dupe_first_bold (_("%s wishes to add to an existing task:"), organizer, NULL);
 531 		break;
 532 	case ITIP_VIEW_MODE_REFRESH:
 533 		if (priv->attendee_sentby)
 534 			sender = dupe_first_bold (_("%s through %s wishes to receive the latest information for the following assigned task:"), attendee, priv->attendee_sentby);
 535 		else
 536 			sender = dupe_first_bold (_("%s wishes to receive the latest information for the following assigned task:"), attendee, NULL);
 537 		break;
 538 	case ITIP_VIEW_MODE_REPLY:
 539 		if (priv->attendee_sentby)
 540 			sender = dupe_first_bold (_("%s through %s has sent back the following assigned task response:"), attendee, priv->attendee_sentby);
 541 		else
 542 			sender = dupe_first_bold (_("%s has sent back the following assigned task response:"), attendee, NULL);
 543 		break;
 544 	case ITIP_VIEW_MODE_CANCEL:
 545 		if (priv->organizer_sentby)
 546 			sender = dupe_first_bold (_("%s through %s has canceled the following assigned task:"), organizer, priv->organizer_sentby);
 547 		else
 548 			sender = dupe_first_bold (_("%s has canceled the following assigned task:"), organizer, NULL);
 549 		break;
 550 	case ITIP_VIEW_MODE_COUNTER:
 551 		if (priv->attendee_sentby)
 552 			sender = dupe_first_bold (_("%s through %s has proposed the following task assignment changes:"), attendee, priv->attendee_sentby);
 553 		else
 554 			sender = dupe_first_bold (_("%s has proposed the following task assignment changes:"), attendee, NULL);
 555 		break;
 556 	case ITIP_VIEW_MODE_DECLINECOUNTER:
 557 		if (priv->organizer_sentby)
 558 			sender = dupe_first_bold (_("%s through %s has declined the following assigned task:"), organizer, priv->organizer_sentby);
 559 		else
 560 			sender = dupe_first_bold (_("%s has declined the following assigned task:"), organizer, NULL);
 561 		break;
 562 	default:
 563 		break;
 564 	}
 565 
 566 	if (sender && on_behalf_of) {
 567 		gchar *tmp;
 568 		tmp = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
 569 		g_free (sender);
 570 		sender = tmp;
 571 	}
 572 
 573 	g_free (on_behalf_of);
 574 
 575 	return sender;
 576 }
 577 
 578 static gchar *
 579 set_journal_sender_text (ItipView *view)
 580 {
 581 	ItipViewPrivate *priv;
 582 	const gchar *organizer;
 583 	gchar *sender = NULL;
 584 	gchar *on_behalf_of = NULL;
 585 
 586 	priv = view->priv;
 587 
 588 	organizer = priv->organizer ? priv->organizer : _("An unknown person");
 589 
 590 	/* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
 591 	if (priv->organizer && priv->proxy)
 592 		on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
 593 	else if (priv->attendee && priv->proxy)
 594 		on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);
 595 
 596 	switch (priv->mode) {
 597 	case ITIP_VIEW_MODE_PUBLISH:
 598 		if (priv->organizer_sentby)
 599 			sender = dupe_first_bold (_("%s through %s has published the following memo:"), organizer, priv->organizer_sentby);
 600 		else
 601 			sender = dupe_first_bold (_("%s has published the following memo:"), organizer, NULL);
 602 		break;
 603 	case ITIP_VIEW_MODE_ADD:
 604 		/* FIXME What text for this? */
 605 		if (priv->organizer_sentby)
 606 			sender = dupe_first_bold (_("%s through %s wishes to add to an existing memo:"), organizer, priv->organizer_sentby);
 607 		else
 608 			sender = dupe_first_bold (_("%s wishes to add to an existing memo:"), organizer, NULL);
 609 		break;
 610 	case ITIP_VIEW_MODE_CANCEL:
 611 		if (priv->organizer_sentby)
 612 			sender = dupe_first_bold (_("%s through %s has canceled the following shared memo:"), organizer, priv->organizer_sentby);
 613 		else
 614 			sender = dupe_first_bold (_("%s has canceled the following shared memo:"), organizer, NULL);
 615 		break;
 616 	default:
 617 		break;
 618 	}
 619 
 620 	if (sender && on_behalf_of)
 621 		sender = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
 622 
 623 	g_free (on_behalf_of);
 624 
 625 	return sender;
 626 }
 627 
 628 static void
 629 set_sender_text (ItipView *view)
 630 {
 631 	ItipViewPrivate *priv;
 632 	priv = view->priv;
 633 
 634 	if (priv->sender)
 635 		g_free (priv->sender);
 636 
 637 	switch (priv->type) {
 638 	case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
 639 		priv->sender = set_calendar_sender_text (view);
 640 		break;
 641 	case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
 642 		priv->sender = set_tasklist_sender_text (view);
 643 		break;
 644 	case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
 645 		priv->sender = set_journal_sender_text (view);
 646 		break;
 647 	default:
 648 		priv->sender = NULL;
 649 		break;
 650 	}
 651 
 652 	if (priv->sender && priv->dom_document) {
 653 		WebKitDOMElement *div;
 654 
 655 		div = webkit_dom_document_get_element_by_id (
 656 			priv->dom_document, TEXT_ROW_SENDER);
 657 		webkit_dom_html_element_set_inner_html (
 658 			WEBKIT_DOM_HTML_ELEMENT (div), priv->sender, NULL);
 659 	}
 660 }
 661 
 662 static void
 663 update_start_end_times (ItipView *view)
 664 {
 665 	ItipViewPrivate *priv;
 666 	WebKitDOMElement *row, *col;
 667 	gchar buffer[256];
 668 	time_t now;
 669 	struct tm *now_tm;
 670 
 671 	priv = view->priv;
 672 
 673 	now = time (NULL);
 674 	now_tm = localtime (&now);
 675 
 676 	if (priv->start_label)
 677 		g_free (priv->start_label);
 678 	if (priv->end_label)
 679 		g_free (priv->end_label);
 680 
 681 	#define is_same(_member) (priv->start_tm->_member == priv->end_tm->_member)
 682 	if (priv->start_tm && priv->end_tm && priv->start_tm_is_date && priv->end_tm_is_date
 683 	    && is_same (tm_mday) && is_same (tm_mon) && is_same (tm_year)) {
 684 		/* it's an all day event in one particular day */
 685 		format_date_and_time_x (priv->start_tm, now_tm, FALSE, TRUE, FALSE, priv->start_tm_is_date, buffer, 256);
 686 		priv->start_label = g_strdup (buffer);
 687 		priv->start_header = _("All day:");
 688 		priv->end_header = NULL;
 689 		priv->end_label = NULL;
 690 	} else {
 691 		if (priv->start_tm) {
 692 			format_date_and_time_x (priv->start_tm, now_tm, FALSE, TRUE, FALSE, priv->start_tm_is_date, buffer, 256);
 693 			priv->start_header = priv->start_tm_is_date ? _("Start day:") : _("Start time:");
 694 			priv->start_label = g_strdup (buffer);
 695 		} else {
 696 			priv->start_header = NULL;
 697 			priv->start_label = NULL;
 698 		}
 699 
 700 		if (priv->end_tm) {
 701 			format_date_and_time_x (priv->end_tm, now_tm, FALSE, TRUE, FALSE, priv->end_tm_is_date, buffer, 256);
 702 			priv->end_header = priv->end_tm_is_date ? _("End day:") : _("End time:");
 703 			priv->end_label = g_strdup (buffer);
 704 		} else {
 705 			priv->end_header = NULL;
 706 			priv->end_label = NULL;
 707 		}
 708 	}
 709 	#undef is_same
 710 
 711 	if (priv->dom_document) {
 712 		row = webkit_dom_document_get_element_by_id (
 713 			priv->dom_document, TABLE_ROW_START_DATE);
 714 		if (priv->start_header && priv->start_label) {
 715 			webkit_dom_html_element_set_hidden (
 716 				WEBKIT_DOM_HTML_ELEMENT (row), FALSE);
 717 
 718 			col = webkit_dom_element_get_first_element_child (row);
 719 			webkit_dom_html_element_set_inner_html (
 720 				WEBKIT_DOM_HTML_ELEMENT (col), priv->start_header, NULL);
 721 
 722 			col = webkit_dom_element_get_last_element_child (row);
 723 			webkit_dom_html_element_set_inner_html (
 724 				WEBKIT_DOM_HTML_ELEMENT (col), priv->start_label, NULL);
 725 		} else {
 726 			webkit_dom_html_element_set_hidden (
 727 				WEBKIT_DOM_HTML_ELEMENT (row), TRUE);
 728 		}
 729 
 730 		row = webkit_dom_document_get_element_by_id (
 731 			priv->dom_document, TABLE_ROW_END_DATE);
 732 		if (priv->end_header && priv->end_label) {
 733 			webkit_dom_html_element_set_hidden (
 734 				WEBKIT_DOM_HTML_ELEMENT (row), FALSE);
 735 
 736 			col = webkit_dom_element_get_first_element_child (row);
 737 			webkit_dom_html_element_set_inner_html (
 738 				WEBKIT_DOM_HTML_ELEMENT (col), priv->end_header, NULL);
 739 
 740 			col = webkit_dom_element_get_last_element_child (row);
 741 			webkit_dom_html_element_set_inner_html (
 742 				WEBKIT_DOM_HTML_ELEMENT (col), priv->end_label, NULL);
 743 		} else {
 744 			webkit_dom_html_element_set_hidden (
 745 				WEBKIT_DOM_HTML_ELEMENT (row), TRUE);
 746 		}
 747 	}
 748 }
 749 
 750 static void
 751 button_clicked_cb (WebKitDOMElement *element,
 752                    WebKitDOMEvent *event,
 753                    gpointer data)
 754 {
 755 	ItipViewResponse response;
 756 	gchar *responseStr;
 757 
 758 	responseStr = webkit_dom_html_button_element_get_value (
 759 		WEBKIT_DOM_HTML_BUTTON_ELEMENT (element));
 760 
 761 	response = atoi (responseStr);
 762 
 763 	g_signal_emit (data, signals[RESPONSE], 0, response);
 764 }
 765 
 766 static void
 767 rsvp_toggled_cb (WebKitDOMHTMLInputElement *input,
 768                  WebKitDOMEvent *event,
 769                  gpointer data)
 770 {
 771 	WebKitDOMElement *el;
 772 
 773 	ItipView *view = data;
 774 	gboolean rsvp;
 775 
 776 	rsvp = webkit_dom_html_input_element_get_checked (input);
 777 
 778 	el = webkit_dom_document_get_element_by_id (
 779 		view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
 780 	webkit_dom_html_text_area_element_set_disabled (
 781 		WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp);
 782 }
 783 
 784 static void
 785 recur_toggled_cb (WebKitDOMHTMLInputElement *input,
 786                   WebKitDOMEvent *event,
 787                   gpointer data)
 788 {
 789 	ItipView *view = data;
 790 
 791 	itip_view_set_mode (view, view->priv->mode);
 792 }
 793 
 794 /*
 795   alarm_check_toggled_cb
 796   check1 was changed, so make the second available based on state of the first check.
 797 */
 798 static void
 799 alarm_check_toggled_cb (WebKitDOMHTMLInputElement *check1,
 800                         WebKitDOMEvent *event,
 801                         ItipView *view)
 802 {
 803 	WebKitDOMElement *check2;
 804 	gchar *id = webkit_dom_html_element_get_id (WEBKIT_DOM_HTML_ELEMENT (check1));
 805 
 806 	if (g_strcmp0 (id, CHECKBOX_INHERIT_ALARM)) {
 807 		check2 = webkit_dom_document_get_element_by_id (
 808 			view->priv->dom_document, CHECKBOX_KEEP_ALARM);
 809 	} else {
 810 		check2 = webkit_dom_document_get_element_by_id (
 811 			view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
 812 	}
 813 
 814 	g_free (id);
 815 
 816 	webkit_dom_html_input_element_set_disabled (
 817 		WEBKIT_DOM_HTML_INPUT_ELEMENT (check2),
 818 		(webkit_dom_html_element_get_hidden (
 819 				WEBKIT_DOM_HTML_ELEMENT (check1)) &&
 820 			webkit_dom_html_input_element_get_checked (check1)));
 821 }
 822 
 823 static void
 824 source_changed_cb (WebKitDOMElement *select,
 825                    WebKitDOMEvent *event,
 826                    ItipView *view)
 827 {
 828 	ESource *source;
 829 
 830 	source = itip_view_ref_source (view);
 831 
 832 	d (printf ("Source changed to '%s'\n", e_source_get_display_name (source)));
 833 	g_signal_emit (view, signals[SOURCE_SELECTED], 0, source);
 834 
 835 	g_object_unref (source);
 836 }
 837 
 838 static void
 839 append_checkbox_table_row (GString *buffer,
 840                            const gchar *name,
 841                            const gchar *label)
 842 {
 843 	gchar *access_key, *html_label;
 844 
 845 	html_label = e_mail_formatter_parse_html_mnemonics (label, &access_key);
 846 
 847 	g_string_append_printf (
 848 		buffer,
 849 		"<tr id=\"table_row_%s\" hidden=\"\"><td colspan=\"2\">"
 850 		"<input type=\"checkbox\" name=\"%s\" id=\"%s\" value=\"%s\" >"
 851 		"<label for=\"%s\" accesskey=\"%s\">%s</label>"
 852 		"</td></tr>\n",
 853 		name, name, name, name, name,
 854 		access_key ? access_key : "", html_label);
 855 
 856 	g_free (html_label);
 857 
 858 	if (access_key)
 859 		g_free (access_key);
 860 }
 861 
 862 static void
 863 append_text_table_row (GString *buffer,
 864                        const gchar *id,
 865                        const gchar *label,
 866                        const gchar *value)
 867 {
 868 	if (label && *label) {
 869 
 870 		g_string_append_printf (
 871 			buffer,
 872 			"<tr id=\"%s\" %s><th>%s</th><td>%s</td></tr>\n",
 873 			id, (value && *value) ? "" : "hidden=\"\"", label, value ? value : "");
 874 
 875 	} else {
 876 
 877 		g_string_append_printf (
 878 			buffer,
 879 			"<tr id=\"%s\"%s><td colspan=\"2\">%s</td></tr>\n",
 880 			id, g_strcmp0 (id, TABLE_ROW_SUMMARY) == 0 ? "" : " hidden=\"\"", value ? value : "");
 881 
 882 	}
 883 }
 884 
 885 static void
 886 append_text_table_row_nonempty (GString *buffer,
 887                                 const gchar *id,
 888                                 const gchar *label,
 889                                 const gchar *value)
 890 {
 891 	if (!value || !*value)
 892 		return;
 893 
 894 	append_text_table_row (buffer, id, label, value);
 895 }
 896 
 897 static void
 898 append_info_item_row (ItipView *view,
 899                       const gchar *table_id,
 900                       ItipViewInfoItem *item)
 901 {
 902 	WebKitDOMElement *table;
 903 	WebKitDOMHTMLElement *row, *cell;
 904 	const gchar *icon_name;
 905 	gchar *id;
 906 
 907 	table = webkit_dom_document_get_element_by_id (
 908 		view->priv->dom_document, table_id);
 909 	row = webkit_dom_html_table_element_insert_row (
 910 		WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);
 911 
 912 	id = g_strdup_printf ("%s_row_%d", table_id, item->id);
 913 	webkit_dom_html_element_set_id (row, id);
 914 	g_free (id);
 915 
 916 	switch (item->type) {
 917 		case ITIP_VIEW_INFO_ITEM_TYPE_INFO:
 918 			icon_name = GTK_STOCK_DIALOG_INFO;
 919 			break;
 920 		case ITIP_VIEW_INFO_ITEM_TYPE_WARNING:
 921 			icon_name = GTK_STOCK_DIALOG_WARNING;
 922 			break;
 923 		case ITIP_VIEW_INFO_ITEM_TYPE_ERROR:
 924 			icon_name = GTK_STOCK_DIALOG_ERROR;
 925 			break;
 926 		case ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS:
 927 			icon_name = GTK_STOCK_FIND;
 928 			break;
 929 		case ITIP_VIEW_INFO_ITEM_TYPE_NONE:
 930 			default:
 931 			icon_name = NULL;
 932 	}
 933 
 934 	cell = webkit_dom_html_table_row_element_insert_cell (
 935 		(WebKitDOMHTMLTableRowElement *) row, -1, NULL);
 936 
 937 	if (icon_name) {
 938 		WebKitDOMElement *image;
 939 		gchar *icon_uri;
 940 
 941 		image = webkit_dom_document_create_element (
 942 			view->priv->dom_document, "IMG", NULL);
 943 
 944 		icon_uri = g_strdup_printf ("gtk-stock://%s", icon_name);
 945 		webkit_dom_html_image_element_set_src (
 946 			WEBKIT_DOM_HTML_IMAGE_ELEMENT (image), icon_uri);
 947 		g_free (icon_uri);
 948 
 949 		webkit_dom_node_append_child (
 950 			WEBKIT_DOM_NODE (cell),
 951 			WEBKIT_DOM_NODE (image),
 952 			NULL);
 953 	}
 954 
 955 	cell = webkit_dom_html_table_row_element_insert_cell (
 956 		(WebKitDOMHTMLTableRowElement *) row, -1, NULL);
 957 
 958 	webkit_dom_html_element_set_inner_html (cell, item->message, NULL);
 959 
 960 	d (printf ("Added row %s_row_%d ('%s')\n", table_id, item->id, item->message));
 961 }
 962 
 963 static void
 964 remove_info_item_row (ItipView *view,
 965                       const gchar *table_id,
 966                       guint id)
 967 {
 968 	WebKitDOMElement *row;
 969 	gchar *row_id;
 970 
 971 	row_id = g_strdup_printf ("%s_row_%d", table_id, id);
 972 	row = webkit_dom_document_get_element_by_id (
 973 		view->priv->dom_document, row_id);
 974 	g_free (row_id);
 975 
 976 	webkit_dom_node_remove_child (
 977 		webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)),
 978 		WEBKIT_DOM_NODE (row),
 979 		NULL);
 980 
 981 	d (printf ("Removed row %s_row_%d\n", table_id, id));
 982 }
 983 
 984 static void
 985 buttons_table_write_button (GString *buffer,
 986                             const gchar *name,
 987                             const gchar *label,
 988                             const gchar *icon,
 989                             ItipViewResponse response)
 990 {
 991 	gchar *access_key, *html_label;
 992 
 993 	html_label = e_mail_formatter_parse_html_mnemonics (label, &access_key);
 994 
 995 	g_string_append_printf (
 996 		buffer,
 997 		"<td><button type=\"button\" name=\"%s\" value=\"%d\" id=\"%s\" accesskey=\"%s\" hidden disabled>"
 998 		"<div><img src=\"gtk-stock://%s?size=%d\"> <span>%s</span></div>"
 999 		"</button></td>\n",
1000 		name, response, name, access_key ? access_key : "" , icon,
1001 		GTK_ICON_SIZE_BUTTON, html_label);
1002 
1003 	g_free (html_label);
1004 
1005 	if (access_key)
1006 		g_free (access_key);
1007 }
1008 
1009 static void
1010 append_buttons_table (GString *buffer)
1011 {
1012 	g_string_append (
1013 		buffer,
1014 		"<table class=\"itip buttons\" border=\"0\" "
1015 		"id=\"" TABLE_BUTTONS "\" cellspacing=\"6\" "
1016 		"cellpadding=\"0\" >"
1017 		"<tr id=\"" TABLE_ROW_BUTTONS "\">");
1018 
1019         /* Everything gets the open button */
1020 	buttons_table_write_button (
1021 		buffer, BUTTON_OPEN_CALENDAR, _("_Open Calendar"),
1022 		GTK_STOCK_JUMP_TO, ITIP_VIEW_RESPONSE_OPEN);
1023 	buttons_table_write_button (
1024 		buffer, BUTTON_DECLINE_ALL, _("_Decline all"),
1025 		GTK_STOCK_CANCEL, ITIP_VIEW_RESPONSE_DECLINE);
1026 	buttons_table_write_button (
1027 		buffer, BUTTON_DECLINE, _("_Decline"),
1028 		GTK_STOCK_CANCEL, ITIP_VIEW_RESPONSE_DECLINE);
1029 	buttons_table_write_button (
1030 		buffer, BUTTON_TENTATIVE_ALL, _("_Tentative all"),
1031 		GTK_STOCK_DIALOG_QUESTION, ITIP_VIEW_RESPONSE_TENTATIVE);
1032 	buttons_table_write_button (
1033 		buffer, BUTTON_TENTATIVE, _("_Tentative"),
1034 		GTK_STOCK_DIALOG_QUESTION, ITIP_VIEW_RESPONSE_TENTATIVE);
1035 	buttons_table_write_button (
1036 		buffer, BUTTON_ACCEPT_ALL, _("A_ccept all"),
1037 		GTK_STOCK_APPLY, ITIP_VIEW_RESPONSE_ACCEPT);
1038 	buttons_table_write_button (
1039 		buffer, BUTTON_ACCEPT, _("A_ccept"),
1040 		GTK_STOCK_APPLY, ITIP_VIEW_RESPONSE_ACCEPT);
1041 	buttons_table_write_button (
1042 		buffer, BUTTON_SEND_INFORMATION, _("_Send Information"),
1043 		GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_REFRESH);
1044 	buttons_table_write_button (
1045 		buffer, BUTTON_UPDATE_ATTENDEE_STATUS, _("_Update Attendee Status"),
1046 		GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_UPDATE);
1047 	buttons_table_write_button (
1048 		buffer, BUTTON_UPDATE,  _("_Update"),
1049 		GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_CANCEL);
1050 
1051 	g_string_append (buffer, "</tr></table>");
1052 }
1053 
1054 static void
1055 itip_view_rebuild_source_list (ItipView *view)
1056 {
1057 	ESourceRegistry *registry;
1058 	WebKitDOMElement *select;
1059 	GList *list, *link;
1060 	const gchar *extension_name;
1061 	GHashTable *groups;
1062 
1063 	d (printf ("Assigning a new source list!\n"));
1064 
1065 	if (!view->priv->dom_document)
1066 		return;
1067 
1068 	registry = itip_view_get_registry (view);
1069 	extension_name = itip_view_get_extension_name (view);
1070 
1071 	select = webkit_dom_document_get_element_by_id (
1072 		view->priv->dom_document, SELECT_ESOURCE);
1073 
1074 	while (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (select))) {
1075 		webkit_dom_node_remove_child (
1076 			WEBKIT_DOM_NODE (select),
1077 			webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (select)),
1078 			NULL);
1079 	}
1080 
1081 	if (extension_name == NULL)
1082 		return;
1083 
1084 	list = e_source_registry_list_sources (registry, extension_name);
1085 	groups = g_hash_table_new_full (
1086 			g_str_hash, g_str_equal,
1087 			(GDestroyNotify) g_free, NULL);
1088 	for (link = list; link != NULL; link = g_list_next (link)) {
1089 		ESource *source = E_SOURCE (link->data);
1090 		ESource *parent;
1091 		WebKitDOMElement *option;
1092 		WebKitDOMHTMLOptGroupElement *optgroup;
1093 
1094 		parent = e_source_registry_ref_source (
1095 				registry, e_source_get_parent (source));
1096 
1097 		optgroup = g_hash_table_lookup (groups, e_source_get_uid (parent));
1098 		if (!optgroup) {
1099 			optgroup = WEBKIT_DOM_HTML_OPT_GROUP_ELEMENT (
1100 					webkit_dom_document_create_element (
1101 						view->priv->dom_document,
1102 						"OPTGROUP", NULL));
1103 			webkit_dom_html_opt_group_element_set_label (
1104 				optgroup, e_source_get_display_name (parent));
1105 			g_hash_table_insert (
1106 				groups, g_strdup (e_source_get_uid (parent)), optgroup);
1107 		}
1108 		g_object_unref (parent);
1109 
1110 		option = webkit_dom_document_create_element (
1111 			view->priv->dom_document, "OPTION", NULL);
1112 		webkit_dom_html_option_element_set_value (
1113 			WEBKIT_DOM_HTML_OPTION_ELEMENT (option),
1114 			e_source_get_uid (source));
1115 		webkit_dom_html_option_element_set_label (
1116 			WEBKIT_DOM_HTML_OPTION_ELEMENT (option),
1117 			e_source_get_display_name (source));
1118 		webkit_dom_html_element_set_inner_html (
1119 			WEBKIT_DOM_HTML_ELEMENT (option),
1120 			e_source_get_display_name (source), NULL);
1121 
1122 		/* See https://bugzilla.gnome.org/show_bug.cgi?id=681400
1123 		 * FIXME: This can be removed once we require WebKitGtk 1.10+ */
1124 		#if WEBKIT_CHECK_VERSION (1, 9, 6)
1125 			webkit_dom_element_set_class_name (
1126 				WEBKIT_DOM_ELEMENT (option), "calendar");
1127 		#else
1128 			webkit_dom_html_element_set_class_name (
1129 				WEBKIT_DOM_HTML_ELEMENT (option), "calendar");
1130 		#endif
1131 
1132 		if (!e_source_get_writable (source)) {
1133 			webkit_dom_html_option_element_set_disabled (
1134 				WEBKIT_DOM_HTML_OPTION_ELEMENT (option), TRUE);
1135 		}
1136 
1137 		webkit_dom_node_append_child (
1138 			WEBKIT_DOM_NODE (optgroup),
1139 			WEBKIT_DOM_NODE (option),
1140 			NULL);
1141 	}
1142 
1143 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
1144 
1145 	list = g_hash_table_get_values (groups);
1146 	for (link = list; link != NULL; link = g_list_next (link)) {
1147 		WebKitDOMNode *optgroup = link->data;
1148 
1149 		webkit_dom_node_append_child (
1150 			WEBKIT_DOM_NODE (select), optgroup, NULL);
1151 	}
1152 	g_list_free (list);
1153 	g_hash_table_destroy (groups);
1154 
1155 	source_changed_cb (select, NULL, view);
1156 }
1157 
1158 static void
1159 itip_view_source_added_cb (ESourceRegistry *registry,
1160                            ESource *source,
1161                            ItipView *view)
1162 {
1163 	const gchar *extension_name;
1164 
1165 	extension_name = itip_view_get_extension_name (view);
1166 
1167 	/* If we don't have an extension name set
1168 	 * yet then disregard the signal emission. */
1169 	if (extension_name == NULL)
1170 		return;
1171 
1172 	if (e_source_has_extension (source, extension_name))
1173 		itip_view_rebuild_source_list (view);
1174 }
1175 
1176 static void
1177 itip_view_source_removed_cb (ESourceRegistry *registry,
1178                              ESource *source,
1179                              ItipView *view)
1180 {
1181 	const gchar *extension_name;
1182 
1183 	extension_name = itip_view_get_extension_name (view);
1184 
1185 	/* If we don't have an extension name set
1186 	 * yet then disregard the signal emission. */
1187 	if (extension_name == NULL)
1188 		return;
1189 
1190 	if (e_source_has_extension (source, extension_name))
1191 		itip_view_rebuild_source_list (view);
1192 }
1193 
1194 static void
1195 itip_view_set_registry (ItipView *view,
1196                         ESourceRegistry *registry)
1197 {
1198 	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
1199 	g_return_if_fail (view->priv->registry == NULL);
1200 
1201 	view->priv->registry = g_object_ref (registry);
1202 }
1203 
1204 static void
1205 itip_view_set_property (GObject *object,
1206                         guint property_id,
1207                         const GValue *value,
1208                         GParamSpec *pspec)
1209 {
1210 	switch (property_id) {
1211 		case PROP_EXTENSION_NAME:
1212 			itip_view_set_extension_name (
1213 				ITIP_VIEW (object),
1214 				g_value_get_string (value));
1215 			return;
1216 
1217 		case PROP_REGISTRY:
1218 			itip_view_set_registry (
1219 				ITIP_VIEW (object),
1220 				g_value_get_object (value));
1221 			return;
1222 	}
1223 
1224 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1225 }
1226 
1227 static void
1228 itip_view_get_property (GObject *object,
1229                         guint property_id,
1230                         GValue *value,
1231                         GParamSpec *pspec)
1232 {
1233 	switch (property_id) {
1234 		case PROP_EXTENSION_NAME:
1235 			g_value_set_string (
1236 				value, itip_view_get_extension_name (
1237 				ITIP_VIEW (object)));
1238 			return;
1239 
1240 		case PROP_REGISTRY:
1241 			g_value_set_object (
1242 				value, itip_view_get_registry (
1243 				ITIP_VIEW (object)));
1244 			return;
1245 	}
1246 
1247 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1248 }
1249 
1250 static void
1251 itip_view_dispose (GObject *object)
1252 {
1253 	ItipViewPrivate *priv;
1254 
1255 	priv = ITIP_VIEW_GET_PRIVATE (object);
1256 
1257 	if (priv->registry != NULL) {
1258 		g_signal_handler_disconnect (
1259 			priv->registry, priv->source_added_id);
1260 		g_signal_handler_disconnect (
1261 			priv->registry, priv->source_removed_id);
1262 		g_object_unref (priv->registry);
1263 		priv->registry = NULL;
1264 	}
1265 
1266 	/* Chain up to parent's dispose() method. */
1267 	G_OBJECT_CLASS (itip_view_parent_class)->dispose (object);
1268 }
1269 
1270 static void
1271 itip_view_finalize (GObject *object)
1272 {
1273 	ItipViewPrivate *priv;
1274 	GSList *iter;
1275 
1276 	priv = ITIP_VIEW_GET_PRIVATE (object);
1277 
1278 	d (printf ("Itip view finalized!\n"));
1279 
1280 	g_clear_object (&priv->dom_document);
1281 	g_free (priv->extension_name);
1282 	g_free (priv->sender);
1283 	g_free (priv->organizer);
1284 	g_free (priv->organizer_sentby);
1285 	g_free (priv->delegator);
1286 	g_free (priv->attendee);
1287 	g_free (priv->attendee_sentby);
1288 	g_free (priv->proxy);
1289 	g_free (priv->summary);
1290 	g_free (priv->location);
1291 	g_free (priv->status);
1292 	g_free (priv->comment);
1293 	g_free (priv->start_tm);
1294 	g_free (priv->start_label);
1295 	g_free (priv->end_tm);
1296 	g_free (priv->end_label);
1297 	g_free (priv->description);
1298 	g_free (priv->error);
1299 
1300 	for (iter = priv->lower_info_items; iter; iter = iter->next) {
1301 		ItipViewInfoItem *item = iter->data;
1302 		g_free (item->message);
1303 		g_free (item);
1304 	}
1305 
1306 	g_slist_free (priv->lower_info_items);
1307 
1308 	for (iter = priv->upper_info_items; iter; iter = iter->next) {
1309 		ItipViewInfoItem *item = iter->data;
1310 		g_free (item->message);
1311 		g_free (item);
1312 	}
1313 
1314 	g_slist_free (priv->upper_info_items);
1315 
1316 	/* Chain up to parent's finalize() method. */
1317 	G_OBJECT_CLASS (itip_view_parent_class)->finalize (object);
1318 }
1319 
1320 static void
1321 itip_view_constructed (GObject *object)
1322 {
1323 	ItipView *view;
1324 	ESourceRegistry *registry;
1325 
1326 	view = ITIP_VIEW (object);
1327 	registry = itip_view_get_registry (view);
1328 
1329 	view->priv->source_added_id = g_signal_connect (
1330 		registry, "source-added",
1331 		G_CALLBACK (itip_view_source_added_cb), view);
1332 
1333 	view->priv->source_removed_id = g_signal_connect (
1334 		registry, "source-removed",
1335 		G_CALLBACK (itip_view_source_removed_cb), view);
1336 
1337 	/* Chain up to parent's constructed() method. */
1338 	G_OBJECT_CLASS (itip_view_parent_class)->constructed (object);
1339 }
1340 
1341 static void
1342 itip_view_class_init (ItipViewClass *class)
1343 {
1344 	GObjectClass *object_class;
1345 
1346 	g_type_class_add_private (class, sizeof (ItipViewPrivate));
1347 
1348 	object_class = G_OBJECT_CLASS (class);
1349 	object_class->set_property = itip_view_set_property;
1350 	object_class->get_property = itip_view_get_property;
1351 	object_class->dispose = itip_view_dispose;
1352 	object_class->finalize = itip_view_finalize;
1353 	object_class->constructed = itip_view_constructed;
1354 
1355 	g_object_class_install_property (
1356 		object_class,
1357 		PROP_REGISTRY,
1358 		g_param_spec_string (
1359 			"extension-name",
1360 			"Extension Name",
1361 			"Show only data sources with this extension",
1362 			NULL,
1363 			G_PARAM_READWRITE));
1364 
1365 	g_object_class_install_property (
1366 		object_class,
1367 		PROP_REGISTRY,
1368 		g_param_spec_object (
1369 			"registry",
1370 			"Registry",
1371 			"Data source registry",
1372 			E_TYPE_SOURCE_REGISTRY,
1373 			G_PARAM_READWRITE |
1374 			G_PARAM_CONSTRUCT_ONLY));
1375 
1376 	signals[SOURCE_SELECTED] = g_signal_new (
1377 		"source_selected",
1378 		G_TYPE_FROM_CLASS (class),
1379 		G_SIGNAL_RUN_LAST,
1380 		G_STRUCT_OFFSET (ItipViewClass, source_selected),
1381 		NULL, NULL,
1382 		g_cclosure_marshal_VOID__OBJECT,
1383 		G_TYPE_NONE, 1,
1384 		E_TYPE_SOURCE);
1385 
1386 	signals[RESPONSE] = g_signal_new (
1387 		"response",
1388 		G_TYPE_FROM_CLASS (class),
1389 		G_SIGNAL_RUN_LAST,
1390 		G_STRUCT_OFFSET (ItipViewClass, response),
1391 		NULL, NULL,
1392 		g_cclosure_marshal_VOID__INT,
1393 		G_TYPE_NONE, 1,
1394 		G_TYPE_INT);
1395 }
1396 
1397 EMailPartItip *
1398 itip_view_get_mail_part (ItipView *view)
1399 {
1400 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1401 
1402 	return view->priv->itip_part;
1403 }
1404 
1405 ESourceRegistry *
1406 itip_view_get_registry (ItipView *view)
1407 {
1408 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1409 
1410 	return view->priv->registry;
1411 }
1412 
1413 const gchar *
1414 itip_view_get_extension_name (ItipView *view)
1415 {
1416 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1417 
1418 	return view->priv->extension_name;
1419 }
1420 
1421 void
1422 itip_view_set_extension_name (ItipView *view,
1423                               const gchar *extension_name)
1424 {
1425 	g_return_if_fail (ITIP_IS_VIEW (view));
1426 
1427 	/* Avoid unnecessary rebuilds. */
1428 	if (g_strcmp0 (extension_name, view->priv->extension_name) == 0)
1429 		return;
1430 
1431 	g_free (view->priv->extension_name);
1432 	view->priv->extension_name = g_strdup (extension_name);
1433 
1434 	g_object_notify (G_OBJECT (view), "extension-name");
1435 
1436 	itip_view_rebuild_source_list (view);
1437 }
1438 
1439 void
1440 itip_view_write (EMailFormatter *formatter,
1441                  GString *buffer)
1442 {
1443 	gchar *header = e_mail_formatter_get_html_header (formatter);
1444 	g_string_append (buffer, header);
1445 	g_free (header);
1446 
1447 	g_string_append_printf (
1448 		buffer,
1449 		"<img src=\"gtk-stock://%s?size=%d\" class=\"itip icon\" />\n",
1450 			MEETING_ICON, GTK_ICON_SIZE_BUTTON);
1451 
1452 	g_string_append (
1453 		buffer,
1454 		"<div class=\"itip content\" id=\"" DIV_ITIP_CONTENT "\">\n");
1455 
1456         /* The first section listing the sender */
1457         /* FIXME What to do if the send and organizer do not match */
1458 	g_string_append (
1459 		buffer,
1460 		"<div id=\"" TEXT_ROW_SENDER "\" class=\"itip sender\"></div>\n");
1461 
1462 	g_string_append (buffer, "<hr>\n");
1463 
1464         /* Elementary event information */
1465 	g_string_append (
1466 		buffer,
1467 		"<table class=\"itip table\" border=\"0\" "
1468 		"cellspacing=\"5\" cellpadding=\"0\">\n");
1469 
1470 	append_text_table_row (buffer, TABLE_ROW_SUMMARY, NULL, NULL);
1471 	append_text_table_row (buffer, TABLE_ROW_LOCATION, _("Location:"), NULL);
1472 	append_text_table_row (buffer, TABLE_ROW_START_DATE, _("Start time:"), NULL);
1473 	append_text_table_row (buffer, TABLE_ROW_END_DATE, _("End time:"), NULL);
1474 	append_text_table_row (buffer, TABLE_ROW_STATUS, _("Status:"), NULL);
1475 	append_text_table_row (buffer, TABLE_ROW_COMMENT, _("Comment:"), NULL);
1476 
1477 	g_string_append (buffer, "</table>\n");
1478 
1479 	/* Upper Info items */
1480 	g_string_append (
1481 		buffer,
1482 		"<table class=\"itip info\" id=\"" TABLE_UPPER_ITIP_INFO "\" border=\"0\" "
1483 		"cellspacing=\"5\" cellpadding=\"0\">");
1484 
1485         /* Description */
1486 	g_string_append (
1487 		buffer,
1488 		"<div id=\"" TABLE_ROW_DESCRIPTION "\" class=\"itip description\" hidden=\"\"></div>\n");
1489 
1490 	g_string_append (buffer, "<hr>\n");
1491 
1492 	/* Lower Info items */
1493 	g_string_append (
1494 		buffer,
1495 		"<table class=\"itip info\" id=\"" TABLE_LOWER_ITIP_INFO "\" border=\"0\" "
1496 		"cellspacing=\"5\" cellpadding=\"0\">");
1497 
1498 	g_string_append (
1499 		buffer,
1500 		"<table class=\"itip table\" border=\"0\" "
1501 		"cellspacing=\"5\" cellpadding=\"0\">\n");
1502 
1503 	g_string_append (
1504 		buffer,
1505 		"<tr id=\"" TABLE_ROW_ESCB "\" hidden=\"\""">"
1506 		"<th><label id=\"" TABLE_ROW_ESCB_LABEL "\" for=\"" SELECT_ESOURCE "\"></label></th>"
1507 		"<td><select name=\"" SELECT_ESOURCE "\" id=\"" SELECT_ESOURCE "\"></select></td>"
1508 		"</tr>\n");
1509 
1510 	/* RSVP area */
1511 	append_checkbox_table_row (buffer, CHECKBOX_RSVP, _("Send reply to sender"));
1512 
1513         /* Comments */
1514 	g_string_append_printf (
1515 		buffer,
1516 		"<tr id=\"" TABLE_ROW_RSVP_COMMENT "\" hidden=\"\">"
1517 		"<th>%s</th>"
1518 		"<td><textarea name=\"" TEXTAREA_RSVP_COMMENT "\" "
1519 		"id=\"" TEXTAREA_RSVP_COMMENT "\" "
1520 		"rows=\"3\" cols=\"40\" disabled=\"\">"
1521 		"</textarea></td>\n"
1522 		"</tr>\n",
1523 		_("Comment:"));
1524 
1525         /* Updates */
1526 	append_checkbox_table_row (buffer, CHECKBOX_UPDATE, _("Send _updates to attendees"));
1527 
1528         /* The recurrence check button */
1529 	append_checkbox_table_row (buffer, CHECKBOX_RECUR, _("_Apply to all instances"));
1530 	append_checkbox_table_row (buffer, CHECKBOX_FREE_TIME, _("Show time as _free"));
1531 	append_checkbox_table_row (buffer, CHECKBOX_KEEP_ALARM, _("_Preserve my reminder"));
1532 	append_checkbox_table_row (buffer, CHECKBOX_INHERIT_ALARM, _("_Inherit reminder"));
1533 
1534 	g_string_append (buffer, "</table>\n");
1535 
1536         /* Buttons table */
1537 	append_buttons_table (buffer);
1538 
1539         /* <div class="itip content" > */
1540 	g_string_append (buffer, "</div>\n");
1541 
1542 	g_string_append (buffer, "<div class=\"itip error\" id=\"" DIV_ITIP_ERROR "\"></div>");
1543 
1544 	g_string_append (buffer, "</body></html>");
1545 }
1546 
1547 void
1548 itip_view_write_for_printing (ItipView *view,
1549                               GString *buffer)
1550 {
1551 	if (view->priv->error && *view->priv->error) {
1552 		g_string_append (buffer, view->priv->error);
1553 		return;
1554 	}
1555 
1556 	g_string_append (
1557 		buffer,
1558 		"<div class=\"itip print_content\" id=\"" DIV_ITIP_CONTENT "\">\n");
1559 
1560         /* The first section listing the sender */
1561 	if (view->priv->sender && *view->priv->sender) {
1562 		/* FIXME What to do if the send and organizer do not match */
1563 		g_string_append_printf (
1564 			buffer,
1565 			"<div id=\"" TEXT_ROW_SENDER "\" class=\"itip sender\">%s</div>\n",
1566 			view->priv->sender);
1567 
1568 		g_string_append (buffer, "<hr>\n");
1569 	}
1570 
1571         /* Elementary event information */
1572 	g_string_append (
1573 		buffer,
1574 		"<table class=\"itip table\" border=\"0\" "
1575 		"cellspacing=\"5\" cellpadding=\"0\">\n");
1576 
1577 	append_text_table_row_nonempty (
1578 		buffer, TABLE_ROW_SUMMARY,
1579 		NULL, view->priv->summary);
1580 	append_text_table_row_nonempty (
1581 		buffer, TABLE_ROW_LOCATION,
1582 		_("Location:"), view->priv->location);
1583 	append_text_table_row_nonempty (
1584 		buffer, TABLE_ROW_START_DATE,
1585 		view->priv->start_header, view->priv->start_label);
1586 	append_text_table_row_nonempty (
1587 		buffer, TABLE_ROW_END_DATE,
1588 		view->priv->end_header, view->priv->end_label);
1589 	append_text_table_row_nonempty (
1590 		buffer, TABLE_ROW_STATUS,
1591 		_("Status:"), view->priv->status);
1592 	append_text_table_row_nonempty (
1593 		buffer, TABLE_ROW_COMMENT,
1594 		_("Comment:"), view->priv->comment);
1595 
1596 	g_string_append (buffer, "</table><br>\n");
1597 
1598         /* Description */
1599 	if (view->priv->description && *view->priv->description) {
1600 		g_string_append_printf (
1601 			buffer,
1602 			"<div id=\"" TABLE_ROW_DESCRIPTION "\" "
1603 			"class=\"itip description\" %s>%s</div>\n",
1604 			view->priv->description ? "" : "hidden=\"\"", view->priv->description);
1605 
1606 		g_string_append (buffer, "</div>");
1607 	}
1608 }
1609 
1610 void
1611 itip_view_create_dom_bindings (ItipView *view,
1612                                WebKitDOMElement *element)
1613 {
1614 	WebKitDOMElement *el;
1615 	WebKitDOMDocument *doc;
1616 
1617 	doc = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
1618 	view->priv->dom_document = g_object_ref (doc);
1619 
1620 	el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RECUR);
1621 	if (el) {
1622 		webkit_dom_event_target_add_event_listener (
1623 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1624 			G_CALLBACK (recur_toggled_cb), FALSE, view);
1625 	}
1626 
1627 	el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RSVP);
1628 	if (el) {
1629 		webkit_dom_event_target_add_event_listener (
1630 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1631 			G_CALLBACK (rsvp_toggled_cb), FALSE, view);
1632 	}
1633 
1634 	el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_INHERIT_ALARM);
1635 	if (el) {
1636 		webkit_dom_event_target_add_event_listener (
1637 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1638 			G_CALLBACK (alarm_check_toggled_cb), FALSE, view);
1639 	}
1640 
1641 	el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_KEEP_ALARM);
1642 	if (el) {
1643 		webkit_dom_event_target_add_event_listener (
1644 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1645 			G_CALLBACK (alarm_check_toggled_cb), FALSE, view);
1646 	}
1647 
1648 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_OPEN_CALENDAR);
1649 	if (el) {
1650 		webkit_dom_event_target_add_event_listener (
1651 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1652 			G_CALLBACK (button_clicked_cb), FALSE, view);
1653 	}
1654 
1655 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT);
1656 	if (el) {
1657 		webkit_dom_event_target_add_event_listener (
1658 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1659 			G_CALLBACK (button_clicked_cb), FALSE, view);
1660 	}
1661 
1662 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT_ALL);
1663 	if (el) {
1664 		webkit_dom_event_target_add_event_listener (
1665 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1666 			G_CALLBACK (button_clicked_cb), FALSE, view);
1667 	}
1668 
1669 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE);
1670 	if (el) {
1671 		webkit_dom_event_target_add_event_listener (
1672 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1673 			G_CALLBACK (button_clicked_cb), FALSE, view);
1674 	}
1675 
1676 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE_ALL);
1677 	if (el) {
1678 		webkit_dom_event_target_add_event_listener (
1679 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1680 			G_CALLBACK (button_clicked_cb), FALSE, view);
1681 	}
1682 
1683 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE);
1684 	if (el) {
1685 		webkit_dom_event_target_add_event_listener (
1686 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1687 			G_CALLBACK (button_clicked_cb), FALSE, view);
1688 	}
1689 
1690 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE_ALL);
1691 	if (el) {
1692 		webkit_dom_event_target_add_event_listener (
1693 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1694 			G_CALLBACK (button_clicked_cb), FALSE, view);
1695 	}
1696 
1697 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE);
1698 	if (el) {
1699 		webkit_dom_event_target_add_event_listener (
1700 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1701 			G_CALLBACK (button_clicked_cb), FALSE, view);
1702 	}
1703 
1704 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE_ATTENDEE_STATUS);
1705 	if (el) {
1706 		webkit_dom_event_target_add_event_listener (
1707 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1708 			G_CALLBACK (button_clicked_cb), FALSE, view);
1709 	}
1710 
1711 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_SEND_INFORMATION);
1712 	if (el) {
1713 		webkit_dom_event_target_add_event_listener (
1714 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1715 			G_CALLBACK (button_clicked_cb), FALSE, view);
1716 	}
1717 
1718 	el = webkit_dom_document_get_element_by_id (doc, SELECT_ESOURCE);
1719 	if (el) {
1720 		webkit_dom_event_target_add_event_listener (
1721 			WEBKIT_DOM_EVENT_TARGET (el), "change",
1722 			G_CALLBACK (source_changed_cb), FALSE, view);
1723 	}
1724 }
1725 
1726 static void
1727 itip_view_init (ItipView *view)
1728 {
1729 	view->priv = ITIP_VIEW_GET_PRIVATE (view);
1730 
1731 }
1732 
1733 ItipView *
1734 itip_view_new (EMailPartItip *puri,
1735                ESourceRegistry *registry)
1736 {
1737 	ItipView *view;
1738 
1739 	view = ITIP_VIEW (g_object_new (
1740 		ITIP_TYPE_VIEW,
1741 		"registry", registry,
1742 		NULL));
1743 	view->priv->itip_part = puri;
1744 
1745 	return view;
1746 }
1747 
1748 static void
1749 show_button (ItipView *view,
1750              const gchar *id)
1751 {
1752 	WebKitDOMElement *button;
1753 
1754 	button = webkit_dom_document_get_element_by_id (
1755 		view->priv->dom_document, id);
1756 	webkit_dom_html_element_set_hidden (
1757 		WEBKIT_DOM_HTML_ELEMENT (button), FALSE);
1758 }
1759 
1760 void
1761 itip_view_set_mode (ItipView *view,
1762                     ItipViewMode mode)
1763 {
1764 	WebKitDOMElement *row, *cell;
1765 	WebKitDOMElement *button;
1766 
1767 	g_return_if_fail (ITIP_IS_VIEW (view));
1768 
1769 	view->priv->mode = mode;
1770 
1771 	set_sender_text (view);
1772 
1773 	if (!view->priv->dom_document)
1774 		return;
1775 
1776 	row = webkit_dom_document_get_element_by_id (
1777 		view->priv->dom_document, TABLE_ROW_BUTTONS);
1778 	cell = webkit_dom_element_get_first_element_child (row);
1779 	do {
1780 		button = webkit_dom_element_get_first_element_child (cell);
1781 		webkit_dom_html_element_set_hidden (
1782 			WEBKIT_DOM_HTML_ELEMENT (button), TRUE);
1783 	} while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL);
1784 
1785 	view->priv->is_recur_set = itip_view_get_recur_check_state (view);
1786 
1787         /* Always visible */
1788 	show_button (view, BUTTON_OPEN_CALENDAR);
1789 
1790 	switch (mode) {
1791 	case ITIP_VIEW_MODE_PUBLISH:
1792 		if (view->priv->needs_decline) {
1793 			show_button (view, BUTTON_DECLINE);
1794 		}
1795 		show_button (view, BUTTON_ACCEPT);
1796 		break;
1797 	case ITIP_VIEW_MODE_REQUEST:
1798 		show_button (view, view->priv->is_recur_set ? BUTTON_DECLINE_ALL : BUTTON_DECLINE);
1799 		show_button (view, view->priv->is_recur_set ? BUTTON_TENTATIVE_ALL : BUTTON_TENTATIVE);
1800 		show_button (view, view->priv->is_recur_set ? BUTTON_ACCEPT_ALL : BUTTON_ACCEPT);
1801 		break;
1802 	case ITIP_VIEW_MODE_ADD:
1803 		if (view->priv->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
1804 			show_button (view, BUTTON_DECLINE);
1805 			show_button (view, BUTTON_TENTATIVE);
1806 		}
1807 		show_button (view, BUTTON_ACCEPT);
1808 		break;
1809 	case ITIP_VIEW_MODE_REFRESH:
1810 		show_button (view, BUTTON_SEND_INFORMATION);
1811 		break;
1812 	case ITIP_VIEW_MODE_REPLY:
1813 		show_button (view, BUTTON_UPDATE_ATTENDEE_STATUS);
1814 		break;
1815 	case ITIP_VIEW_MODE_CANCEL:
1816 		show_button (view, BUTTON_UPDATE);
1817 		break;
1818 	case ITIP_VIEW_MODE_COUNTER:
1819 	case ITIP_VIEW_MODE_DECLINECOUNTER:
1820 		show_button (view, BUTTON_DECLINE);
1821 		show_button (view, BUTTON_TENTATIVE);
1822 		show_button (view, BUTTON_ACCEPT);
1823 		break;
1824 	default:
1825 		break;
1826 	}
1827 }
1828 
1829 ItipViewMode
1830 itip_view_get_mode (ItipView *view)
1831 {
1832 	g_return_val_if_fail (ITIP_IS_VIEW (view), ITIP_VIEW_MODE_NONE);
1833 
1834 	return view->priv->mode;
1835 }
1836 
1837 void
1838 itip_view_set_item_type (ItipView *view,
1839                          ECalClientSourceType type)
1840 {
1841 	WebKitDOMElement *label;
1842 	const gchar *header;
1843 	gchar *access_key, *html_label;
1844 
1845 	g_return_if_fail (ITIP_IS_VIEW (view));
1846 
1847 	view->priv->type = type;
1848 
1849 	if (!view->priv->dom_document)
1850 		return;
1851 
1852 	label = webkit_dom_document_get_element_by_id (
1853 		view->priv->dom_document, TABLE_ROW_ESCB_LABEL);
1854 
1855 	switch (view->priv->type) {
1856 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
1857 			header = _("_Calendar:");
1858 			break;
1859 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
1860 			header = _("_Tasks:");
1861 			break;
1862 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
1863 			header = _("_Memos:");
1864 			break;
1865 		default:
1866 			header = NULL;
1867 			break;
1868 	}
1869 
1870 	if (!header) {
1871 		set_sender_text (view);
1872 		return;
1873 	}
1874 
1875 	html_label = e_mail_formatter_parse_html_mnemonics (header, &access_key);
1876 
1877 	webkit_dom_html_element_set_access_key (
1878 		WEBKIT_DOM_HTML_ELEMENT (label), access_key);
1879 	webkit_dom_html_element_set_inner_html (
1880 		WEBKIT_DOM_HTML_ELEMENT (label), html_label, NULL);
1881 
1882 	g_free (html_label);
1883 
1884 	if (access_key)
1885 		g_free (access_key);
1886 
1887 	set_sender_text (view);
1888 }
1889 
1890 ECalClientSourceType
1891 itip_view_get_item_type (ItipView *view)
1892 {
1893 	g_return_val_if_fail (ITIP_IS_VIEW (view), ITIP_VIEW_MODE_NONE);
1894 
1895 	return view->priv->type;
1896 }
1897 
1898 void
1899 itip_view_set_organizer (ItipView *view,
1900                          const gchar *organizer)
1901 {
1902 	g_return_if_fail (ITIP_IS_VIEW (view));
1903 
1904 	if (view->priv->organizer)
1905 		g_free (view->priv->organizer);
1906 
1907 	view->priv->organizer = e_utf8_ensure_valid (organizer);
1908 
1909 	set_sender_text (view);
1910 }
1911 
1912 const gchar *
1913 itip_view_get_organizer (ItipView *view)
1914 {
1915 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1916 
1917 	return view->priv->organizer;
1918 }
1919 
1920 void
1921 itip_view_set_organizer_sentby (ItipView *view,
1922                                 const gchar *sentby)
1923 {
1924 	g_return_if_fail (ITIP_IS_VIEW (view));
1925 
1926 	if (view->priv->organizer_sentby)
1927 		g_free (view->priv->organizer_sentby);
1928 
1929 	view->priv->organizer_sentby = e_utf8_ensure_valid (sentby);
1930 
1931 	set_sender_text (view);
1932 }
1933 
1934 const gchar *
1935 itip_view_get_organizer_sentby (ItipView *view)
1936 {
1937 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1938 
1939 	return view->priv->organizer_sentby;
1940 }
1941 
1942 void
1943 itip_view_set_attendee (ItipView *view,
1944                         const gchar *attendee)
1945 {
1946 	g_return_if_fail (ITIP_IS_VIEW (view));
1947 
1948 	if (view->priv->attendee)
1949 		g_free (view->priv->attendee);
1950 
1951 	view->priv->attendee = e_utf8_ensure_valid (attendee);
1952 
1953 	set_sender_text (view);
1954 }
1955 
1956 const gchar *
1957 itip_view_get_attendee (ItipView *view)
1958 {
1959 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1960 
1961 	return view->priv->attendee;
1962 }
1963 
1964 void
1965 itip_view_set_attendee_sentby (ItipView *view,
1966                                const gchar *sentby)
1967 {
1968 	g_return_if_fail (ITIP_IS_VIEW (view));
1969 
1970 	if (view->priv->attendee_sentby)
1971 		g_free (view->priv->attendee_sentby);
1972 
1973 	view->priv->attendee_sentby = e_utf8_ensure_valid (sentby);
1974 
1975 	set_sender_text (view);
1976 }
1977 
1978 const gchar *
1979 itip_view_get_attendee_sentby (ItipView *view)
1980 {
1981 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1982 
1983 	return view->priv->attendee_sentby;
1984 }
1985 
1986 void
1987 itip_view_set_proxy (ItipView *view,
1988                      const gchar *proxy)
1989 {
1990 	g_return_if_fail (ITIP_IS_VIEW (view));
1991 
1992 	if (view->priv->proxy)
1993 		g_free (view->priv->proxy);
1994 
1995 	view->priv->proxy = e_utf8_ensure_valid (proxy);
1996 
1997 	set_sender_text (view);
1998 }
1999 
2000 const gchar *
2001 itip_view_get_proxy (ItipView *view)
2002 {
2003 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2004 
2005 	return view->priv->proxy;
2006 }
2007 
2008 void
2009 itip_view_set_delegator (ItipView *view,
2010                          const gchar *delegator)
2011 {
2012 	g_return_if_fail (ITIP_IS_VIEW (view));
2013 
2014 	if (view->priv->delegator)
2015 		g_free (view->priv->delegator);
2016 
2017 	view->priv->delegator = e_utf8_ensure_valid (delegator);
2018 
2019 	set_sender_text (view);
2020 }
2021 
2022 const gchar *
2023 itip_view_get_delegator (ItipView *view)
2024 {
2025 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2026 
2027 	return view->priv->delegator;
2028 }
2029 
2030 void
2031 itip_view_set_summary (ItipView *view,
2032                        const gchar *summary)
2033 {
2034 	WebKitDOMElement *row, *col;
2035 
2036 	g_return_if_fail (ITIP_IS_VIEW (view));
2037 
2038 	if (view->priv->summary)
2039 		g_free (view->priv->summary);
2040 
2041 	view->priv->summary = summary ? g_strstrip (e_utf8_ensure_valid (summary)) : NULL;
2042 
2043 	if (!view->priv->dom_document)
2044 		return;
2045 
2046 	row = webkit_dom_document_get_element_by_id (
2047 		view->priv->dom_document, TABLE_ROW_SUMMARY);
2048 	webkit_dom_html_element_set_hidden (
2049 		WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->summary == NULL));
2050 
2051 	col = webkit_dom_element_get_last_element_child (row);
2052 	webkit_dom_html_element_set_inner_html (
2053 		WEBKIT_DOM_HTML_ELEMENT (col),
2054 		view->priv->summary ? view->priv->summary : "",
2055 		NULL);
2056 }
2057 
2058 const gchar *
2059 itip_view_get_summary (ItipView *view)
2060 {
2061 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2062 
2063 	return view->priv->summary;
2064 }
2065 
2066 void
2067 itip_view_set_location (ItipView *view,
2068                         const gchar *location)
2069 {
2070 	WebKitDOMElement *row, *col;
2071 
2072 	g_return_if_fail (ITIP_IS_VIEW (view));
2073 
2074 	if (view->priv->location)
2075 		g_free (view->priv->location);
2076 
2077 	view->priv->location = location ? g_strstrip (e_utf8_ensure_valid (location)) : NULL;
2078 
2079 	if (!view->priv->dom_document)
2080 		return;
2081 
2082 	row = webkit_dom_document_get_element_by_id (
2083 		view->priv->dom_document, TABLE_ROW_LOCATION);
2084 	webkit_dom_html_element_set_hidden (
2085 		WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->location == NULL));
2086 
2087 	col = webkit_dom_element_get_last_element_child (row);
2088 	webkit_dom_html_element_set_inner_html (
2089 		WEBKIT_DOM_HTML_ELEMENT (col),
2090 		view->priv->location ? view->priv->location : "",
2091 		NULL);
2092 }
2093 
2094 const gchar *
2095 itip_view_get_location (ItipView *view)
2096 {
2097 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2098 
2099 	return view->priv->location;
2100 }
2101 
2102 void
2103 itip_view_set_status (ItipView *view,
2104                       const gchar *status)
2105 {
2106 	WebKitDOMElement *row, *col;
2107 
2108 	g_return_if_fail (ITIP_IS_VIEW (view));
2109 
2110 	if (view->priv->status)
2111 		g_free (view->priv->status);
2112 
2113 	view->priv->status = status ? g_strstrip (e_utf8_ensure_valid (status)) : NULL;
2114 
2115 	if (!view->priv->dom_document)
2116 		return;
2117 
2118 	row = webkit_dom_document_get_element_by_id (
2119 		view->priv->dom_document, TABLE_ROW_STATUS);
2120 	webkit_dom_html_element_set_hidden (
2121 		WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->status == NULL));
2122 
2123 	col = webkit_dom_element_get_last_element_child (row);
2124 	webkit_dom_html_element_set_inner_html (
2125 		WEBKIT_DOM_HTML_ELEMENT (col),
2126 		view->priv->status ? view->priv->status : "",
2127 		NULL);
2128 }
2129 
2130 const gchar *
2131 itip_view_get_status (ItipView *view)
2132 {
2133 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2134 
2135 	return view->priv->status;
2136 }
2137 
2138 void
2139 itip_view_set_comment (ItipView *view,
2140                        const gchar *comment)
2141 {
2142 	WebKitDOMElement *row, *col;
2143 
2144 	g_return_if_fail (ITIP_IS_VIEW (view));
2145 
2146 	if (view->priv->comment)
2147 		g_free (view->priv->comment);
2148 
2149 	view->priv->comment = comment ? g_strstrip (e_utf8_ensure_valid (comment)) : NULL;
2150 
2151 	if (!view->priv->dom_document)
2152 		return;
2153 
2154 	row = webkit_dom_document_get_element_by_id (
2155 		view->priv->dom_document, TABLE_ROW_COMMENT);
2156 	webkit_dom_html_element_set_hidden (
2157 		WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->comment == NULL));
2158 
2159 	col = webkit_dom_element_get_last_element_child (row);
2160 	webkit_dom_html_element_set_inner_html (
2161 		WEBKIT_DOM_HTML_ELEMENT (col),
2162 		view->priv->comment ? view->priv->comment : "",
2163 		NULL);
2164 }
2165 
2166 const gchar *
2167 itip_view_get_comment (ItipView *view)
2168 {
2169 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2170 
2171 	return view->priv->comment;
2172 }
2173 
2174 void
2175 itip_view_set_description (ItipView *view,
2176                            const gchar *description)
2177 {
2178 	WebKitDOMElement *div;
2179 
2180 	g_return_if_fail (ITIP_IS_VIEW (view));
2181 
2182 	if (view->priv->description)
2183 		g_free (view->priv->description);
2184 
2185 	view->priv->description = description ? g_strstrip (e_utf8_ensure_valid (description)) : NULL;
2186 
2187 	if (!view->priv->dom_document)
2188 		return;
2189 
2190 	div = webkit_dom_document_get_element_by_id (
2191 		view->priv->dom_document, TABLE_ROW_DESCRIPTION);
2192 	webkit_dom_html_element_set_hidden (
2193 		WEBKIT_DOM_HTML_ELEMENT (div), (view->priv->description == NULL));
2194 
2195 	webkit_dom_html_element_set_inner_html (
2196 		WEBKIT_DOM_HTML_ELEMENT (div),
2197 		view->priv->description ? view->priv->description : "",
2198 		NULL);
2199 }
2200 
2201 const gchar *
2202 itip_view_get_description (ItipView *view)
2203 {
2204 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2205 
2206 	return view->priv->description;
2207 }
2208 
2209 void
2210 itip_view_set_start (ItipView *view,
2211                      struct tm *start,
2212                      gboolean is_date)
2213 {
2214 	ItipViewPrivate *priv;
2215 
2216 	g_return_if_fail (ITIP_IS_VIEW (view));
2217 
2218 	priv = view->priv;
2219 
2220 	if (priv->start_tm && !start) {
2221 		g_free (priv->start_tm);
2222 		priv->start_tm = NULL;
2223 	} else if (start) {
2224 		if (!priv->start_tm)
2225 			priv->start_tm = g_new0 (struct tm, 1);
2226 
2227 		*priv->start_tm = *start;
2228 	}
2229 
2230 	priv->start_tm_is_date = is_date && start;
2231 
2232 	update_start_end_times (view);
2233 }
2234 
2235 const struct tm *
2236 itip_view_get_start (ItipView *view,
2237                      gboolean *is_date)
2238 {
2239 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2240 
2241 	if (is_date)
2242 		*is_date = view->priv->start_tm_is_date;
2243 
2244 	return view->priv->start_tm;
2245 }
2246 
2247 void
2248 itip_view_set_end (ItipView *view,
2249                    struct tm *end,
2250                    gboolean is_date)
2251 {
2252 	ItipViewPrivate *priv;
2253 
2254 	g_return_if_fail (ITIP_IS_VIEW (view));
2255 
2256 	priv = view->priv;
2257 
2258 	if (priv->end_tm && !end) {
2259 		g_free (priv->end_tm);
2260 		priv->end_tm = NULL;
2261 	} else if (end) {
2262 		if (!priv->end_tm)
2263 			priv->end_tm = g_new0 (struct tm, 1);
2264 
2265 		*priv->end_tm = *end;
2266 	}
2267 
2268 	priv->end_tm_is_date = is_date && end;
2269 
2270 	update_start_end_times (view);
2271 }
2272 
2273 const struct tm *
2274 itip_view_get_end (ItipView *view,
2275                    gboolean *is_date)
2276 {
2277 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2278 
2279 	if (is_date)
2280 		*is_date = view->priv->end_tm_is_date;
2281 
2282 	return view->priv->end_tm;
2283 }
2284 
2285 guint
2286 itip_view_add_upper_info_item (ItipView *view,
2287                                ItipViewInfoItemType type,
2288                                const gchar *message)
2289 {
2290 	ItipViewPrivate *priv;
2291 	ItipViewInfoItem *item;
2292 
2293 	g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
2294 
2295 	priv = view->priv;
2296 
2297 	item = g_new0 (ItipViewInfoItem, 1);
2298 
2299 	item->type = type;
2300 	item->message = e_utf8_ensure_valid (message);
2301 	item->id = priv->next_info_item_id++;
2302 
2303 	priv->upper_info_items = g_slist_append (priv->upper_info_items, item);
2304 
2305 	if (!view->priv->dom_document)
2306 		return item->id;
2307 
2308 	append_info_item_row (view, TABLE_UPPER_ITIP_INFO, item);
2309 
2310 	return item->id;
2311 }
2312 
2313 guint
2314 itip_view_add_upper_info_item_printf (ItipView *view,
2315                                       ItipViewInfoItemType type,
2316                                       const gchar *format,
2317                                       ...)
2318 {
2319 	va_list args;
2320 	gchar *message;
2321 	guint id;
2322 
2323 	g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
2324 
2325 	va_start (args, format);
2326 	message = g_strdup_vprintf (format, args);
2327 	va_end (args);
2328 
2329 	id = itip_view_add_upper_info_item (view, type, message);
2330 	g_free (message);
2331 
2332 	return id;
2333 }
2334 
2335 void
2336 itip_view_remove_upper_info_item (ItipView *view,
2337                                   guint id)
2338 {
2339 	ItipViewPrivate *priv;
2340 	GSList *l;
2341 
2342 	g_return_if_fail (ITIP_IS_VIEW (view));
2343 
2344 	priv = view->priv;
2345 
2346 	for (l = priv->upper_info_items; l; l = l->next) {
2347 		ItipViewInfoItem *item = l->data;
2348 
2349 		if (item->id == id) {
2350 			priv->upper_info_items = g_slist_remove (priv->upper_info_items, item);
2351 
2352 			g_free (item->message);
2353 			g_free (item);
2354 
2355 			if (!view->priv->dom_document)
2356 				remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, id);
2357 
2358 			return;
2359 		}
2360 	}
2361 }
2362 
2363 void
2364 itip_view_clear_upper_info_items (ItipView *view)
2365 {
2366 	ItipViewPrivate *priv;
2367 	GSList *l;
2368 
2369 	g_return_if_fail (ITIP_IS_VIEW (view));
2370 
2371 	priv = view->priv;
2372 
2373 	for (l = priv->upper_info_items; l; l = l->next) {
2374 		ItipViewInfoItem *item = l->data;
2375 
2376 		if (view->priv->dom_document)
2377 			remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, item->id);
2378 
2379 		g_free (item->message);
2380 		g_free (item);
2381 	}
2382 
2383 	g_slist_free (priv->upper_info_items);
2384 	priv->upper_info_items = NULL;
2385 }
2386 
2387 guint
2388 itip_view_add_lower_info_item (ItipView *view,
2389                                ItipViewInfoItemType type,
2390                                const gchar *message)
2391 {
2392 	ItipViewPrivate *priv;
2393 	ItipViewInfoItem *item;
2394 
2395 	g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
2396 
2397 	priv = view->priv;
2398 
2399 	item = g_new0 (ItipViewInfoItem, 1);
2400 
2401 	item->type = type;
2402 	item->message = e_utf8_ensure_valid (message);
2403 	item->id = priv->next_info_item_id++;
2404 
2405 	priv->lower_info_items = g_slist_append (priv->lower_info_items, item);
2406 
2407 	if (!view->priv->dom_document)
2408 		return item->id;
2409 
2410 	append_info_item_row (view, TABLE_LOWER_ITIP_INFO, item);
2411 
2412 	return item->id;
2413 }
2414 
2415 guint
2416 itip_view_add_lower_info_item_printf (ItipView *view,
2417                                       ItipViewInfoItemType type,
2418                                       const gchar *format,
2419                                       ...)
2420 {
2421 	va_list args;
2422 	gchar *message;
2423 	guint id;
2424 
2425 	g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
2426 
2427 	va_start (args, format);
2428 	message = g_strdup_vprintf (format, args);
2429 	va_end (args);
2430 
2431 	id = itip_view_add_lower_info_item (view, type, message);
2432 	g_free (message);
2433 
2434 	return id;
2435 }
2436 
2437 void
2438 itip_view_remove_lower_info_item (ItipView *view,
2439                                   guint id)
2440 {
2441 	ItipViewPrivate *priv;
2442 	GSList *l;
2443 
2444 	g_return_if_fail (ITIP_IS_VIEW (view));
2445 
2446 	priv = view->priv;
2447 
2448 	for (l = priv->lower_info_items; l; l = l->next) {
2449 		ItipViewInfoItem *item = l->data;
2450 
2451 		if (item->id == id) {
2452 			priv->lower_info_items = g_slist_remove (priv->lower_info_items, item);
2453 
2454 			g_free (item->message);
2455 			g_free (item);
2456 
2457 			if (view->priv->dom_document)
2458 				remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, id);
2459 
2460 			return;
2461 		}
2462 	}
2463 }
2464 
2465 void
2466 itip_view_clear_lower_info_items (ItipView *view)
2467 {
2468 	ItipViewPrivate *priv;
2469 	GSList *l;
2470 
2471 	g_return_if_fail (ITIP_IS_VIEW (view));
2472 
2473 	priv = view->priv;
2474 
2475 	for (l = priv->lower_info_items; l; l = l->next) {
2476 		ItipViewInfoItem *item = l->data;
2477 
2478 		if (view->priv->dom_document)
2479 			remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, item->id);
2480 
2481 		g_free (item->message);
2482 		g_free (item);
2483 	}
2484 
2485 	g_slist_free (priv->lower_info_items);
2486 	priv->lower_info_items = NULL;
2487 }
2488 
2489 void
2490 itip_view_set_source (ItipView *view,
2491                       ESource *source)
2492 {
2493 	WebKitDOMElement *select;
2494 	WebKitDOMElement *row;
2495 	ESource *selected_source;
2496 	gulong i, len;
2497 
2498 	g_return_if_fail (ITIP_IS_VIEW (view));
2499 
2500 	d (printf ("Settings default source '%s'\n", e_source_get_display_name (source)));
2501 
2502 	if (!view->priv->dom_document)
2503 		return;
2504 
2505 	row = webkit_dom_document_get_element_by_id (
2506 		view->priv->dom_document, TABLE_ROW_ESCB);
2507 	webkit_dom_html_element_set_hidden (
2508 		WEBKIT_DOM_HTML_ELEMENT (row), (source == NULL));
2509 	if (source == NULL)
2510 		return;
2511 
2512 	select = webkit_dom_document_get_element_by_id (
2513 		view->priv->dom_document, SELECT_ESOURCE);
2514 
2515         /* <select> does not emit 'change' event when already selected
2516 	 * <option> is re-selected, but we need to notify itip formatter,
2517 	 * so that it would make all the buttons sensitive */
2518 	selected_source = itip_view_ref_source (view);
2519 	if (source == selected_source) {
2520 		source_changed_cb (select, NULL, view);
2521 		return;
2522 	}
2523 
2524 	if (selected_source != NULL)
2525 		g_object_unref (selected_source);
2526 
2527 	if (webkit_dom_html_select_element_get_disabled (
2528 			WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) {
2529 		webkit_dom_html_select_element_set_disabled (
2530 			WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE);
2531 	}
2532 
2533 	len = webkit_dom_html_select_element_get_length (
2534 		WEBKIT_DOM_HTML_SELECT_ELEMENT (select));
2535 	for (i = 0; i < len; i++) {
2536 
2537 		WebKitDOMNode *node;
2538 		WebKitDOMHTMLOptionElement *option;
2539 		gchar *value;
2540 
2541 		node = webkit_dom_html_select_element_item (
2542 			WEBKIT_DOM_HTML_SELECT_ELEMENT (select), i);
2543 		option = WEBKIT_DOM_HTML_OPTION_ELEMENT (node);
2544 
2545 		value = webkit_dom_html_option_element_get_value (option);
2546 		if (g_strcmp0 (value, e_source_get_uid (source)) == 0) {
2547 			webkit_dom_html_option_element_set_selected (
2548 				option, TRUE);
2549 
2550 			g_free (value);
2551 			break;
2552 		}
2553 
2554 		g_free (value);
2555 	}
2556 
2557 	source_changed_cb (select, NULL, view);
2558 }
2559 
2560 ESource *
2561 itip_view_ref_source (ItipView *view)
2562 {
2563 	ESourceRegistry *registry;
2564 	WebKitDOMElement *select;
2565 	gchar *uid;
2566 	ESource *source;
2567 	gboolean disable = FALSE;
2568 
2569 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2570 
2571 	if (!view->priv->dom_document)
2572 		return NULL;
2573 
2574 	select = webkit_dom_document_get_element_by_id (
2575 		view->priv->dom_document, SELECT_ESOURCE);
2576 	if (webkit_dom_html_select_element_get_disabled (
2577 			WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) {
2578 		webkit_dom_html_select_element_set_disabled (
2579 		WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE);
2580 		disable = TRUE;
2581 	}
2582 
2583 	uid = webkit_dom_html_select_element_get_value (
2584 		WEBKIT_DOM_HTML_SELECT_ELEMENT (select));
2585 
2586 	registry = itip_view_get_registry (view);
2587 	source = e_source_registry_ref_source (registry, uid);
2588 
2589 	g_free (uid);
2590 
2591 	if (disable) {
2592 		webkit_dom_html_select_element_set_disabled (
2593 			WEBKIT_DOM_HTML_SELECT_ELEMENT (select), TRUE);
2594 	}
2595 
2596 	return source;
2597 }
2598 
2599 void
2600 itip_view_set_rsvp (ItipView *view,
2601                     gboolean rsvp)
2602 {
2603 	WebKitDOMElement *el;
2604 
2605 	g_return_if_fail (ITIP_IS_VIEW (view));
2606 
2607 	if (!view->priv->dom_document)
2608 		return;
2609 
2610 	el = webkit_dom_document_get_element_by_id (
2611 		view->priv->dom_document, CHECKBOX_RSVP);
2612 	webkit_dom_html_input_element_set_checked (
2613 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), rsvp);
2614 
2615 	el = webkit_dom_document_get_element_by_id (
2616 		view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
2617 	webkit_dom_html_text_area_element_set_disabled (
2618 		WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp);
2619 }
2620 
2621 gboolean
2622 itip_view_get_rsvp (ItipView *view)
2623 {
2624 	WebKitDOMElement *el;
2625 
2626 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2627 
2628 	if (!view->priv->dom_document)
2629 		return FALSE;
2630 
2631 	el = webkit_dom_document_get_element_by_id (
2632 		view->priv->dom_document, CHECKBOX_RSVP);
2633 	return webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
2634 }
2635 
2636 void
2637 itip_view_set_show_rsvp_check (ItipView *view,
2638                                gboolean show)
2639 {
2640 	WebKitDOMElement *label;
2641 	WebKitDOMElement *el;
2642 
2643 	g_return_if_fail (ITIP_IS_VIEW (view));
2644 
2645 	if (!view->priv->dom_document)
2646 		return;
2647 
2648 	el = webkit_dom_document_get_element_by_id (
2649 		view->priv->dom_document, "table_row_" CHECKBOX_RSVP);
2650 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2651 
2652 	el = webkit_dom_document_get_element_by_id (
2653 		view->priv->dom_document, CHECKBOX_RSVP);
2654 	label = webkit_dom_element_get_next_element_sibling (el);
2655 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2656 
2657 	if (!show) {
2658 		webkit_dom_html_input_element_set_checked (
2659 			WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
2660 	}
2661 
2662 	el = webkit_dom_document_get_element_by_id (
2663 		view->priv->dom_document, TABLE_ROW_RSVP_COMMENT);
2664 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2665 }
2666 
2667 gboolean
2668 itip_view_get_show_rsvp_check (ItipView *view)
2669 {
2670 	WebKitDOMElement *el;
2671 
2672 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2673 
2674 	if (!view->priv->dom_document)
2675 		return FALSE;
2676 
2677 	el = webkit_dom_document_get_element_by_id (
2678 		view->priv->dom_document, CHECKBOX_RSVP);
2679 	return !webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el));
2680 }
2681 
2682 void
2683 itip_view_set_update (ItipView *view,
2684                       gboolean update)
2685 {
2686 	WebKitDOMElement *el;
2687 
2688 	g_return_if_fail (ITIP_IS_VIEW (view));
2689 
2690 	if (!view->priv->dom_document)
2691 		return;
2692 
2693 	el = webkit_dom_document_get_element_by_id (
2694 		view->priv->dom_document, CHECKBOX_UPDATE);
2695 
2696 	webkit_dom_html_input_element_set_checked (
2697 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), update);
2698 }
2699 
2700 gboolean
2701 itip_view_get_update (ItipView *view)
2702 {
2703 	WebKitDOMElement *el;
2704 
2705 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2706 
2707 	if (!view->priv->dom_document)
2708 		return FALSE;
2709 
2710 	el = webkit_dom_document_get_element_by_id (
2711 		view->priv->dom_document, CHECKBOX_UPDATE);
2712 	return webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
2713 }
2714 
2715 void
2716 itip_view_set_show_update_check (ItipView *view,
2717                                  gboolean show)
2718 {
2719 	WebKitDOMElement *label;
2720 	WebKitDOMElement *el;
2721 
2722 	g_return_if_fail (ITIP_IS_VIEW (view));
2723 
2724 	if (!view->priv->dom_document)
2725 		return;
2726 
2727 	el = webkit_dom_document_get_element_by_id (
2728 		view->priv->dom_document, "table_row_" CHECKBOX_UPDATE);
2729 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2730 
2731 	el = webkit_dom_document_get_element_by_id (
2732 		view->priv->dom_document, CHECKBOX_UPDATE);
2733 	label = webkit_dom_element_get_next_element_sibling (el);
2734 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2735 
2736 	if (!show) {
2737 		webkit_dom_html_input_element_set_checked (
2738 			WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
2739 	}
2740 }
2741 
2742 gboolean
2743 itip_view_get_show_update_check (ItipView *view)
2744 {
2745 	WebKitDOMElement *el;
2746 
2747 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2748 
2749 	if (!view->priv->dom_document)
2750 		return FALSE;
2751 
2752 	el = webkit_dom_document_get_element_by_id (
2753 		view->priv->dom_document, CHECKBOX_UPDATE);
2754 	return !webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el));
2755 }
2756 
2757 void
2758 itip_view_set_rsvp_comment (ItipView *view,
2759                             const gchar *comment)
2760 {
2761 	WebKitDOMElement *el;
2762 
2763 	g_return_if_fail (ITIP_IS_VIEW (view));
2764 
2765 	if (!view->priv->dom_document)
2766 		return;
2767 
2768 	el = webkit_dom_document_get_element_by_id (
2769 		view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
2770 	webkit_dom_html_element_set_hidden (
2771 		WEBKIT_DOM_HTML_ELEMENT (el), (comment == NULL));
2772 
2773 	if (comment) {
2774 		webkit_dom_html_text_area_element_set_value (
2775 			WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), comment);
2776 	}
2777 }
2778 
2779 gchar *
2780 itip_view_get_rsvp_comment (ItipView *view)
2781 {
2782 	WebKitDOMElement *el;
2783 
2784 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2785 
2786 	if (!view->priv->dom_document)
2787 		return NULL;
2788 
2789 	el = webkit_dom_document_get_element_by_id (
2790 		view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
2791 
2792 	if (webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el))) {
2793 		return NULL;
2794 	}
2795 
2796 	return webkit_dom_html_text_area_element_get_value (
2797 		WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el));
2798 }
2799 
2800 void
2801 itip_view_set_needs_decline (ItipView *view,
2802                              gboolean needs_decline)
2803 {
2804 	g_return_if_fail (ITIP_IS_VIEW (view));
2805 
2806 	view->priv->needs_decline = needs_decline;
2807 }
2808 
2809 void
2810 itip_view_set_buttons_sensitive (ItipView *view,
2811                                  gboolean sensitive)
2812 {
2813 	WebKitDOMElement *el, *cell;
2814 
2815 	g_return_if_fail (ITIP_IS_VIEW (view));
2816 
2817 	d (printf ("Settings buttons %s\n", sensitive ? "sensitive" : "insensitive"));
2818 
2819 	view->priv->buttons_sensitive = sensitive;
2820 
2821 	if (!view->priv->dom_document)
2822 		return;
2823 
2824 	el = webkit_dom_document_get_element_by_id (
2825 		view->priv->dom_document, CHECKBOX_UPDATE);
2826 	webkit_dom_html_input_element_set_disabled (
2827 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2828 
2829 	el = webkit_dom_document_get_element_by_id (
2830 		view->priv->dom_document, CHECKBOX_RECUR);
2831 	webkit_dom_html_input_element_set_disabled (
2832 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2833 
2834 	el = webkit_dom_document_get_element_by_id (
2835 		view->priv->dom_document, CHECKBOX_FREE_TIME);
2836 	webkit_dom_html_input_element_set_disabled (
2837 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2838 
2839 	el = webkit_dom_document_get_element_by_id (
2840 		view->priv->dom_document, CHECKBOX_KEEP_ALARM);
2841 	webkit_dom_html_input_element_set_disabled (
2842 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2843 
2844 	el = webkit_dom_document_get_element_by_id (
2845 		view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
2846 	webkit_dom_html_input_element_set_disabled (
2847 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2848 
2849 	el = webkit_dom_document_get_element_by_id (
2850 		view->priv->dom_document, CHECKBOX_RSVP);
2851 	webkit_dom_html_input_element_set_disabled (
2852 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2853 
2854 	el = webkit_dom_document_get_element_by_id (
2855 		view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
2856 	webkit_dom_html_text_area_element_set_disabled (
2857 		WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !sensitive);
2858 
2859 	el = webkit_dom_document_get_element_by_id (
2860 		view->priv->dom_document, TABLE_ROW_BUTTONS);
2861 	cell = webkit_dom_element_get_first_element_child (el);
2862 	do {
2863 		WebKitDOMElement *btn;
2864 		btn = webkit_dom_element_get_first_element_child (cell);
2865 		if (!webkit_dom_html_element_get_hidden (
2866 			WEBKIT_DOM_HTML_ELEMENT (btn))) {
2867 			webkit_dom_html_button_element_set_disabled (
2868 				WEBKIT_DOM_HTML_BUTTON_ELEMENT (btn), !sensitive);
2869 		}
2870 	} while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL);
2871 }
2872 
2873 gboolean
2874 itip_view_get_buttons_sensitive (ItipView *view)
2875 {
2876 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2877 
2878 	return view->priv->buttons_sensitive;
2879 }
2880 
2881 gboolean
2882 itip_view_get_recur_check_state (ItipView *view)
2883 {
2884 	WebKitDOMElement *el;
2885 
2886 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2887 
2888 	if (!view->priv->dom_document)
2889 		return FALSE;
2890 
2891 	el = webkit_dom_document_get_element_by_id (
2892 		view->priv->dom_document, CHECKBOX_RECUR);
2893 	return webkit_dom_html_input_element_get_checked (
2894 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
2895 }
2896 
2897 void
2898 itip_view_set_show_recur_check (ItipView *view,
2899                                 gboolean show)
2900 {
2901 	WebKitDOMElement *label;
2902 	WebKitDOMElement *el;
2903 
2904 	g_return_if_fail (ITIP_IS_VIEW (view));
2905 
2906 	if (!view->priv->dom_document)
2907 		return;
2908 
2909 	el = webkit_dom_document_get_element_by_id (
2910 		view->priv->dom_document, "table_row_" CHECKBOX_RECUR);
2911 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2912 
2913 	el = webkit_dom_document_get_element_by_id (
2914 		view->priv->dom_document, CHECKBOX_RECUR);
2915 	label = webkit_dom_element_get_next_element_sibling (el);
2916 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2917 
2918 	if (!show) {
2919 		webkit_dom_html_input_element_set_checked (
2920 			WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
2921 	}
2922 
2923         /* and update state of the second check */
2924 	alarm_check_toggled_cb (
2925 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
2926 		NULL, view);
2927 }
2928 
2929 void
2930 itip_view_set_show_free_time_check (ItipView *view,
2931                                     gboolean show)
2932 {
2933 	WebKitDOMElement *label;
2934 	WebKitDOMElement *el;
2935 
2936 	g_return_if_fail (ITIP_IS_VIEW (view));
2937 
2938 	if (!view->priv->dom_document)
2939 		return;
2940 
2941 	el = webkit_dom_document_get_element_by_id (
2942 		view->priv->dom_document, "table_row_" CHECKBOX_FREE_TIME);
2943 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2944 
2945 	el = webkit_dom_document_get_element_by_id (
2946 		view->priv->dom_document, CHECKBOX_FREE_TIME);
2947 	label = webkit_dom_element_get_next_element_sibling (el);
2948 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2949 
2950 	if (!show) {
2951 		webkit_dom_html_input_element_set_checked (
2952 			WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
2953 	}
2954 
2955         /* and update state of the second check */
2956 	alarm_check_toggled_cb (
2957 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
2958 		NULL, view);
2959 }
2960 
2961 gboolean
2962 itip_view_get_free_time_check_state (ItipView *view)
2963 {
2964 	WebKitDOMElement *el;
2965 
2966 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2967 
2968 	if (!view->priv->dom_document)
2969 		return FALSE;
2970 
2971 	el = webkit_dom_document_get_element_by_id (
2972 		view->priv->dom_document, CHECKBOX_FREE_TIME);
2973 	return webkit_dom_html_input_element_get_checked (
2974 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
2975 }
2976 
2977 void
2978 itip_view_set_show_keep_alarm_check (ItipView *view,
2979                                      gboolean show)
2980 {
2981 	WebKitDOMElement *label;
2982 	WebKitDOMElement *el;
2983 
2984 	g_return_if_fail (ITIP_IS_VIEW (view));
2985 
2986 	if (!view->priv->dom_document)
2987 		return;
2988 
2989 	el = webkit_dom_document_get_element_by_id (
2990 		view->priv->dom_document, "table_row_" CHECKBOX_KEEP_ALARM);
2991 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2992 
2993 	el = webkit_dom_document_get_element_by_id (
2994 		view->priv->dom_document, CHECKBOX_KEEP_ALARM);
2995 	label = webkit_dom_element_get_next_element_sibling (el);
2996 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2997 
2998 	if (!show) {
2999 		webkit_dom_html_input_element_set_checked (
3000 			WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
3001 	}
3002 
3003         /* and update state of the second check */
3004 	alarm_check_toggled_cb (
3005 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
3006 		NULL, view);
3007 }
3008 
3009 gboolean
3010 itip_view_get_keep_alarm_check_state (ItipView *view)
3011 {
3012 	WebKitDOMElement *el;
3013 
3014 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
3015 
3016 	if (!view->priv->dom_document)
3017 		return FALSE;
3018 
3019 	el = webkit_dom_document_get_element_by_id (
3020 		view->priv->dom_document, CHECKBOX_KEEP_ALARM);
3021 	return webkit_dom_html_input_element_get_checked (
3022 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
3023 }
3024 
3025 void
3026 itip_view_set_show_inherit_alarm_check (ItipView *view,
3027                                         gboolean show)
3028 {
3029 	WebKitDOMElement *label;
3030 	WebKitDOMElement *el;
3031 
3032 	g_return_if_fail (ITIP_IS_VIEW (view));
3033 
3034 	if (!view->priv->dom_document)
3035 		return;
3036 
3037 	el = webkit_dom_document_get_element_by_id (
3038 		view->priv->dom_document, "table_row_" CHECKBOX_INHERIT_ALARM);
3039 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
3040 
3041 	el = webkit_dom_document_get_element_by_id (
3042 		view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
3043 	label = webkit_dom_element_get_next_element_sibling (el);
3044 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
3045 
3046 	if (!show) {
3047 		webkit_dom_html_input_element_set_checked (
3048 			WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
3049 	}
3050 
3051 	/* and update state of the second check */
3052 	alarm_check_toggled_cb (
3053 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
3054 		NULL, view);
3055 }
3056 
3057 gboolean
3058 itip_view_get_inherit_alarm_check_state (ItipView *view)
3059 {
3060 	WebKitDOMElement *el;
3061 
3062 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
3063 
3064 	if (!view->priv->dom_document)
3065 		return FALSE;
3066 
3067 	el = webkit_dom_document_get_element_by_id (
3068 		view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
3069 	return webkit_dom_html_input_element_get_checked (
3070 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
3071 }
3072 
3073 void
3074 itip_view_set_error (ItipView *view,
3075                      const gchar *error_html,
3076                      gboolean show_save_btn)
3077 {
3078 	WebKitDOMElement *content, *error;
3079 	GString *str;
3080 
3081 	g_return_if_fail (ITIP_IS_VIEW (view));
3082 	g_return_if_fail (error_html);
3083 
3084 	str = g_string_new (error_html);
3085 
3086 	if (show_save_btn) {
3087 		g_string_append (
3088 			str,
3089 			"<table border=\"0\" width=\"100%\">"
3090 			"<tr width=\"100%\" id=\"" TABLE_ROW_BUTTONS "\">");
3091 
3092 		buttons_table_write_button (
3093 			str, BUTTON_SAVE, _("_Save"),
3094 			GTK_STOCK_SAVE, ITIP_VIEW_RESPONSE_SAVE);
3095 
3096 		g_string_append (str, "</tr></table>");
3097 	}
3098 
3099 	view->priv->error = str->str;
3100 	g_string_free (str, FALSE);
3101 
3102 	if (!view->priv->dom_document)
3103 		return;
3104 
3105 	content = webkit_dom_document_get_element_by_id (
3106 		view->priv->dom_document, DIV_ITIP_CONTENT);
3107 	webkit_dom_html_element_set_hidden (
3108 		WEBKIT_DOM_HTML_ELEMENT (content), TRUE);
3109 
3110 	error = webkit_dom_document_get_element_by_id (
3111 		view->priv->dom_document, DIV_ITIP_ERROR);
3112 	webkit_dom_html_element_set_hidden (
3113 		WEBKIT_DOM_HTML_ELEMENT (error), FALSE);
3114 
3115 	webkit_dom_html_element_set_inner_html (
3116 		WEBKIT_DOM_HTML_ELEMENT (error), view->priv->error, NULL);
3117 
3118 	if (show_save_btn) {
3119 		WebKitDOMElement *el;
3120 
3121 		show_button (view, BUTTON_SAVE);
3122 
3123 		el = webkit_dom_document_get_element_by_id (
3124 			view->priv->dom_document, BUTTON_SAVE);
3125 		webkit_dom_html_button_element_set_disabled (
3126 			WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), FALSE);
3127 		webkit_dom_event_target_add_event_listener (
3128 			WEBKIT_DOM_EVENT_TARGET (el), "click",
3129 			G_CALLBACK (button_clicked_cb), FALSE, view);
3130 	}
3131 }
3132 
3133 /******************************************************************************/
3134 
3135 typedef struct {
3136 	EMailPartItip *puri;
3137         ItipView *view;
3138 	GCancellable *itip_cancellable;
3139 	GCancellable *cancellable;
3140 	gulong cancelled_id;
3141 	gboolean keep_alarm_check;
3142 	GHashTable *conflicts;
3143 
3144 	gchar *uid;
3145 	gchar *rid;
3146 
3147 	gchar *sexp;
3148 
3149 	gint count;
3150 } FormatItipFindData;
3151 
3152 static gboolean check_is_instance (icalcomponent *icalcomp);
3153 
3154 static icalproperty *
3155 find_attendee (icalcomponent *ical_comp,
3156                const gchar *address)
3157 {
3158 	icalproperty *prop;
3159 
3160 	if (address == NULL)
3161 		return NULL;
3162 
3163 	for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
3164 	     prop != NULL;
3165 	     prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
3166 		gchar *attendee;
3167 		gchar *text;
3168 
3169 		attendee = icalproperty_get_value_as_string_r (prop);
3170 
3171 		 if (!attendee)
3172 			continue;
3173 
3174 		text = g_strdup (itip_strip_mailto (attendee));
3175 		text = g_strstrip (text);
3176 		if (text && !g_ascii_strcasecmp (address, text)) {
3177 			g_free (text);
3178 			g_free (attendee);
3179 			break;
3180 		}
3181 		g_free (text);
3182 		g_free (attendee);
3183 	}
3184 
3185 	return prop;
3186 }
3187 
3188 static icalproperty *
3189 find_attendee_if_sentby (icalcomponent *ical_comp,
3190                          const gchar *address)
3191 {
3192 	icalproperty *prop;
3193 
3194 	if (address == NULL)
3195 		return NULL;
3196 
3197 	for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
3198 	     prop != NULL;
3199 	     prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
3200 		icalparameter *param;
3201 		const gchar *attendee_sentby;
3202 		gchar *text;
3203 
3204 		param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
3205 		if (!param)
3206 			continue;
3207 
3208 		attendee_sentby = icalparameter_get_sentby (param);
3209 
3210 		if (!attendee_sentby)
3211 			continue;
3212 
3213 		text = g_strdup (itip_strip_mailto (attendee_sentby));
3214 		text = g_strstrip (text);
3215 		if (text && !g_ascii_strcasecmp (address, text)) {
3216 			g_free (text);
3217 			break;
3218 		}
3219 		g_free (text);
3220 	}
3221 
3222 	return prop;
3223 }
3224 
3225 static void
3226 find_to_address (EMailPartItip *itip_part,
3227                  icalcomponent *ical_comp,
3228                  icalparameter_partstat *status)
3229 {
3230 	ESourceRegistry *registry;
3231 	ESourceMailIdentity *extension;
3232 	GList *list, *link;
3233 	const gchar *extension_name;
3234 
3235 	registry = itip_part->registry;
3236 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
3237 
3238 	if (itip_part->to_address != NULL)
3239 		return;
3240 
3241 	if (itip_part->msg != NULL && itip_part->folder != NULL) {
3242 		ESource *source;
3243 
3244 		source = em_utils_guess_mail_identity (
3245 			registry, itip_part->msg, itip_part->folder, itip_part->uid);
3246 
3247 		if (source != NULL) {
3248 			extension = e_source_get_extension (source, extension_name);
3249 
3250 			itip_part->to_address = e_source_mail_identity_dup_address (extension);
3251 
3252 			g_object_unref (source);
3253 		}
3254 	}
3255 
3256 	if (itip_part->to_address != NULL)
3257 		return;
3258 
3259 	/* Look through the list of attendees to find the user's address */
3260 	list = e_source_registry_list_sources (registry, extension_name);
3261 
3262 	for (link = list; link != NULL; link = g_list_next (link)) {
3263 		ESource *source = E_SOURCE (link->data);
3264 		icalproperty *prop = NULL;
3265 		icalparameter *param;
3266 		const gchar *address;
3267 		gchar *text;
3268 
3269 		if (!e_source_get_enabled (source))
3270 			continue;
3271 
3272 		extension = e_source_get_extension (source, extension_name);
3273 		address = e_source_mail_identity_get_address (extension);
3274 
3275 		prop = find_attendee (ical_comp, address);
3276 		if (prop == NULL)
3277 			continue;
3278 
3279 		param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
3280 		if (param != NULL)
3281 			itip_part->to_name = g_strdup (icalparameter_get_cn (param));
3282 
3283 		text = icalproperty_get_value_as_string_r (prop);
3284 
3285 		itip_part->to_address = g_strdup (itip_strip_mailto (text));
3286 		g_free (text);
3287 		g_strstrip (itip_part->to_address);
3288 
3289 		itip_part->my_address = g_strdup (address);
3290 
3291 		param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
3292 		if (param != NULL &&
3293 		    icalparameter_get_rsvp (param) == ICAL_RSVP_FALSE)
3294 			itip_part->no_reply_wanted = TRUE;
3295 
3296 		if (status) {
3297 			param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
3298 			*status = param ? icalparameter_get_partstat (param) : ICAL_PARTSTAT_NEEDSACTION;
3299 		}
3300 
3301 		break;
3302 	}
3303 
3304 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
3305 
3306 	if (itip_part->to_address != NULL)
3307 		return;
3308 
3309 	/* If the user's address was not found in the attendee's list,
3310 	 * then the user might be responding on behalf of his/her delegator.
3311 	 * In this case, we would want to go through the SENT-BY fields of
3312 	 * the attendees to find the user's address.
3313  	 *
3314  *
3315 	 * Note: This functionality could have been (easily) implemented
3316 	 * in the previous loop, but it would hurt the performance for all
3317 	 * providers in general. Hence, we choose to iterate through the
3318 	 * accounts list again.
3319  	 */
3320 
3321 	list = e_source_registry_list_sources (registry, extension_name);
3322 
3323 	for (link = list; link != NULL; link = g_list_next (link)) {
3324 		ESource *source = E_SOURCE (link->data);
3325 		icalproperty *prop = NULL;
3326 		icalparameter *param;
3327 		const gchar *address;
3328 		gchar *text;
3329 
3330 		if (!e_source_get_enabled (source))
3331 			continue;
3332 
3333 		extension = e_source_get_extension (source, extension_name);
3334 		address = e_source_mail_identity_get_address (extension);
3335 
3336 		prop = find_attendee_if_sentby (ical_comp, address);
3337 		if (prop == NULL)
3338 			continue;
3339 
3340 		param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
3341 		if (param != NULL)
3342 			itip_part->to_name = g_strdup (icalparameter_get_cn (param));
3343 
3344 		text = icalproperty_get_value_as_string_r (prop);
3345 
3346 		itip_part->to_address = g_strdup (itip_strip_mailto (text));
3347 		g_free (text);
3348 		g_strstrip (itip_part->to_address);
3349 
3350 		itip_part->my_address = g_strdup (address);
3351 
3352 		param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
3353 		if (param != NULL &&
3354 		    ICAL_RSVP_FALSE == icalparameter_get_rsvp (param))
3355 			itip_part->no_reply_wanted = TRUE;
3356 
3357 		if (status) {
3358 			param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
3359 			*status = param ? icalparameter_get_partstat (param) : ICAL_PARTSTAT_NEEDSACTION;
3360 		}
3361 
3362 		break;
3363 	}
3364 
3365 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
3366 }
3367 
3368 static void
3369 find_from_address (EMailPartItip *pitip,
3370                    icalcomponent *ical_comp)
3371 {
3372 	GList *list, *link;
3373 	icalproperty *prop;
3374 	gchar *organizer;
3375 	icalparameter *param;
3376 	const gchar *extension_name;
3377 	const gchar *organizer_sentby;
3378 	gchar *organizer_clean = NULL;
3379 	gchar *organizer_sentby_clean = NULL;
3380 
3381 	prop = icalcomponent_get_first_property (ical_comp, ICAL_ORGANIZER_PROPERTY);
3382 
3383 	if (!prop)
3384 		return;
3385 
3386 	organizer = icalproperty_get_value_as_string_r (prop);
3387 	if (organizer) {
3388 		organizer_clean = g_strdup (itip_strip_mailto (organizer));
3389 		organizer_clean = g_strstrip (organizer_clean);
3390 		g_free (organizer);
3391 	}
3392 
3393 	param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
3394 	if (param) {
3395 		organizer_sentby = icalparameter_get_sentby (param);
3396 		if (organizer_sentby) {
3397 			organizer_sentby_clean = g_strdup (itip_strip_mailto (organizer_sentby));
3398 			organizer_sentby_clean = g_strstrip (organizer_sentby_clean);
3399 		}
3400 	}
3401 
3402 	if (!(organizer_sentby_clean || organizer_clean))
3403 		return;
3404 
3405 	pitip->from_address = g_strdup (organizer_clean);
3406 
3407 	param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
3408 	if (param)
3409 		pitip->from_name = g_strdup (icalparameter_get_cn (param));
3410 
3411 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
3412 	list = e_source_registry_list_sources (pitip->registry, extension_name);
3413 
3414 	for (link = list; link != NULL; link = g_list_next (link)) {
3415 		ESource *source = E_SOURCE (link->data);
3416 		ESourceMailIdentity *extension;
3417 		const gchar *address;
3418 
3419 		if (!e_source_get_enabled (source))
3420 			continue;
3421 
3422 		extension = e_source_get_extension (source, extension_name);
3423 		address = e_source_mail_identity_get_address (extension);
3424 
3425 		if (address == NULL)
3426 			continue;
3427 
3428 		if ((organizer_clean && !g_ascii_strcasecmp (organizer_clean, address))
3429 		    || (organizer_sentby_clean && !g_ascii_strcasecmp (organizer_sentby_clean, address))) {
3430 			pitip->my_address = g_strdup (address);
3431 
3432 			break;
3433 		}
3434 	}
3435 
3436 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
3437 
3438 	g_free (organizer_sentby_clean);
3439 	g_free (organizer_clean);
3440 }
3441 
3442 static ECalComponent *
3443 get_real_item (EMailPartItip *pitip)
3444 {
3445 	ECalComponent *comp = NULL;
3446 	ESource *source;
3447 
3448 	source = e_client_get_source (E_CLIENT (pitip->current_client));
3449 	if (source)
3450 		comp = g_hash_table_lookup (pitip->real_comps, e_source_get_uid (source));
3451 
3452 	if (!comp) {
3453 		return NULL;
3454 	}
3455 
3456 	return e_cal_component_clone (comp);
3457 }
3458 
3459 static void
3460 adjust_item (EMailPartItip *pitip,
3461              ECalComponent *comp)
3462 {
3463 	ECalComponent *real_comp;
3464 
3465 	real_comp = get_real_item (pitip);
3466 	if (real_comp != NULL) {
3467 		ECalComponentText text;
3468 		const gchar *string;
3469 		GSList *l;
3470 
3471 		e_cal_component_get_summary (real_comp, &text);
3472 		e_cal_component_set_summary (comp, &text);
3473 		e_cal_component_get_location (real_comp, &string);
3474 		e_cal_component_set_location (comp, string);
3475 		e_cal_component_get_description_list (real_comp, &l);
3476 		e_cal_component_set_description_list (comp, l);
3477 		e_cal_component_free_text_list (l);
3478 
3479 		g_object_unref (real_comp);
3480 	} else {
3481 		ECalComponentText text = {_("Unknown"), NULL};
3482 
3483 		e_cal_component_set_summary (comp, &text);
3484 	}
3485 }
3486 
3487 static void
3488 set_buttons_sensitive (EMailPartItip *pitip,
3489                        ItipView *view)
3490 {
3491 	gboolean read_only = TRUE;
3492 
3493 	if (pitip->current_client)
3494 		read_only = e_client_is_readonly (E_CLIENT (pitip->current_client));
3495 
3496 	itip_view_set_buttons_sensitive (view, pitip->current_client != NULL && !read_only);
3497 }
3498 
3499 static void
3500 add_failed_to_load_msg (ItipView *view,
3501                         ESource *source,
3502                         const GError *error)
3503 {
3504 	gchar *msg;
3505 
3506 	g_return_if_fail (view != NULL);
3507 	g_return_if_fail (source != NULL);
3508 	g_return_if_fail (error != NULL);
3509 
3510 	/* Translators: The first '%s' is replaced with a calendar name,
3511 	 * the second '%s' with an error message */
3512 	msg = g_strdup_printf (_("Failed to load the calendar '%s' (%s)"), e_source_get_display_name (source), error->message);
3513 
3514 	itip_view_add_lower_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING, msg);
3515 
3516 	g_free (msg);
3517 }
3518 
3519 static void
3520 cal_opened_cb (GObject *source_object,
3521                GAsyncResult *result,
3522                gpointer user_data)
3523 {
3524 	ESource *source = E_SOURCE (source_object);
3525 	ItipView *view = user_data;
3526 	EMailPartItip *pitip = itip_view_get_mail_part (view);
3527 	ECalClientSourceType source_type;
3528 	EClient *client = NULL;
3529 	ECalClient *cal_client;
3530 	const gchar *uid;
3531 	GError *error = NULL;
3532 
3533 	e_client_utils_open_new_finish (source, result, &client, &error);
3534 
3535 	/* Ignore cancellations. */
3536 	if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
3537 	    g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
3538 		g_warn_if_fail (client == NULL);
3539 		g_error_free (error);
3540 		return;
3541 
3542 	} else if (error != NULL) {
3543 		g_warn_if_fail (client == NULL);
3544 		add_failed_to_load_msg (view, source, error);
3545 		g_error_free (error);
3546 		return;
3547 	}
3548 
3549 	g_return_if_fail (E_IS_CAL_CLIENT (client));
3550 
3551 	cal_client = E_CAL_CLIENT (client);
3552 	g_return_if_fail (cal_client != NULL);
3553 
3554 	uid = e_source_get_uid (source);
3555 	source_type = e_cal_client_get_source_type (cal_client);
3556 	g_hash_table_insert (
3557 		pitip->clients[source_type], g_strdup (uid), cal_client);
3558 
3559 	if (e_cal_client_check_recurrences_no_master (cal_client)) {
3560 		icalcomponent *icalcomp;
3561 		gboolean show_recur_check;
3562 
3563 		icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
3564 
3565 		show_recur_check = check_is_instance (icalcomp);
3566 		itip_view_set_show_recur_check (view, show_recur_check);
3567 	}
3568 
3569 	if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
3570 		gboolean needs_decline;
3571 
3572 		needs_decline = e_client_check_capability (
3573 			E_CLIENT (client),
3574 			CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING);
3575 		itip_view_set_needs_decline (view, needs_decline);
3576 		itip_view_set_mode (view, ITIP_VIEW_MODE_PUBLISH);
3577 	}
3578 
3579 	pitip->current_client = cal_client;
3580 
3581 	set_buttons_sensitive (pitip, view);
3582 }
3583 
3584 static void
3585 start_calendar_server (EMailPartItip *pitip,
3586                        ItipView *view,
3587                        ESource *source,
3588                        ECalClientSourceType type,
3589                        GAsyncReadyCallback func,
3590                        gpointer data)
3591 {
3592 	ECalClient *client;
3593 
3594 	g_return_if_fail (source != NULL);
3595 
3596 	client = g_hash_table_lookup (pitip->clients[type], e_source_get_uid (source));
3597 	if (client) {
3598 		pitip->current_client = client;
3599 
3600 		itip_view_remove_lower_info_item (view, pitip->progress_info_id);
3601 		pitip->progress_info_id = 0;
3602 
3603 		set_buttons_sensitive (pitip, view);
3604 
3605 		return;
3606 	}
3607 
3608 	e_client_utils_open_new (
3609 		source,
3610 		type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS ? E_CLIENT_SOURCE_TYPE_EVENTS :
3611 		type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS ? E_CLIENT_SOURCE_TYPE_MEMOS :
3612 		type == E_CAL_CLIENT_SOURCE_TYPE_TASKS ? E_CLIENT_SOURCE_TYPE_TASKS : E_CLIENT_SOURCE_TYPE_LAST,
3613 		TRUE, pitip->cancellable,
3614 		func, data);
3615 }
3616 
3617 static void
3618 start_calendar_server_by_uid (EMailPartItip *pitip,
3619                               ItipView *view,
3620                               const gchar *uid,
3621                               ECalClientSourceType type)
3622 {
3623 	ESource *source;
3624 
3625 	itip_view_set_buttons_sensitive (view, FALSE);
3626 
3627 	source = e_source_registry_ref_source (pitip->registry, uid);
3628 	if (source != NULL) {
3629 		start_calendar_server (
3630 			pitip, view, source, type, cal_opened_cb, view);
3631 		g_object_unref (source);
3632 	}
3633 }
3634 
3635 static void
3636 source_selected_cb (ItipView *view,
3637                     ESource *source,
3638                     gpointer data)
3639 {
3640 	EMailPartItip *pitip = data;
3641 
3642 	itip_view_set_buttons_sensitive (view, FALSE);
3643 
3644 	g_return_if_fail (source != NULL);
3645 
3646 	start_calendar_server (pitip, view, source, pitip->type, cal_opened_cb, view);
3647 }
3648 
3649 static void
3650 find_cal_update_ui (FormatItipFindData *fd,
3651                     ECalClient *cal_client)
3652 {
3653 	EMailPartItip *pitip;
3654 	ItipView *view;
3655 	ESource *source;
3656 
3657 	g_return_if_fail (fd != NULL);
3658 
3659 	pitip = fd->puri;
3660 	view = fd->view;
3661 
3662 	/* UI part gone */
3663 	if (g_cancellable_is_cancelled (fd->cancellable))
3664 		return;
3665 
3666 	source = cal_client ? e_client_get_source (E_CLIENT (cal_client)) : NULL;
3667 
3668 	if (cal_client && g_hash_table_lookup (fd->conflicts, cal_client)) {
3669 		itip_view_add_upper_info_item_printf (
3670 			view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
3671 			_("An appointment in the calendar "
3672 			"'%s' conflicts with this meeting"),
3673 			e_source_get_display_name (source));
3674 	}
3675 
3676 	/* search for a master object if the detached object doesn't exist in the calendar */
3677 	if (pitip->current_client && pitip->current_client == cal_client) {
3678 		gboolean rsvp_enabled = FALSE;
3679 
3680 		itip_view_set_show_keep_alarm_check (view, fd->keep_alarm_check);
3681 
3682 		pitip->current_client = cal_client;
3683 
3684 		/* Provide extra info, since its not in the component */
3685 		/* FIXME Check sequence number of meeting? */
3686 		/* FIXME Do we need to adjust elsewhere for the delegated calendar item? */
3687 		/* FIXME Need to update the fields in the view now */
3688 		if (pitip->method == ICAL_METHOD_REPLY || pitip->method == ICAL_METHOD_REFRESH)
3689 			adjust_item (pitip, pitip->comp);
3690 
3691 		/* We clear everything because we don't really care
3692 		 * about any other info/warnings now we found an
3693 		 * existing versions */
3694 		itip_view_clear_lower_info_items (view);
3695 		pitip->progress_info_id = 0;
3696 
3697 		/* FIXME Check read only state of calendar? */
3698 		itip_view_add_lower_info_item_printf (
3699 			view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
3700 			_("Found the appointment in the calendar '%s'"), e_source_get_display_name (source));
3701 
3702 		/*
3703 		 * Only allow replies if backend doesn't do that automatically.
3704 		 * Only enable it for forwarded invitiations (PUBLISH) or direct
3705 		 * invitiations (REQUEST), but not replies (REPLY).
3706 		 * Replies only make sense for events with an organizer.
3707 		 */
3708 		if ((!pitip->current_client || !e_cal_client_check_save_schedules (pitip->current_client)) &&
3709 		    (pitip->method == ICAL_METHOD_PUBLISH || pitip->method ==  ICAL_METHOD_REQUEST) &&
3710 		    pitip->has_organizer) {
3711 			rsvp_enabled = TRUE;
3712 		}
3713 		itip_view_set_show_rsvp_check (view, rsvp_enabled);
3714 
3715 		/* default is chosen in extract_itip_data() based on content of the VEVENT */
3716 		itip_view_set_rsvp (view, !pitip->no_reply_wanted);
3717 
3718 		set_buttons_sensitive (pitip, view);
3719 
3720 		g_cancellable_cancel (fd->cancellable);
3721 	} else if (!pitip->current_client)
3722 		itip_view_set_show_keep_alarm_check (view, FALSE);
3723 
3724 	if (pitip->current_client && pitip->current_client == cal_client) {
3725 		if (e_cal_client_check_recurrences_no_master (pitip->current_client)) {
3726 			icalcomponent *icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
3727 
3728 			if (check_is_instance (icalcomp))
3729 				itip_view_set_show_recur_check (view, TRUE);
3730 			else
3731 				itip_view_set_show_recur_check (view, FALSE);
3732 		}
3733 
3734 		if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
3735 			/* TODO The static capability should be made generic to convey that the calendar contains unaccepted items */
3736 			if (e_client_check_capability (E_CLIENT (pitip->current_client), CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING))
3737 				itip_view_set_needs_decline (view, TRUE);
3738 			else
3739 				itip_view_set_needs_decline (view, FALSE);
3740 
3741 			itip_view_set_mode (view, ITIP_VIEW_MODE_PUBLISH);
3742 		}
3743 	}
3744 }
3745 
3746 static void
3747 decrease_find_data (FormatItipFindData *fd)
3748 {
3749 	g_return_if_fail (fd != NULL);
3750 
3751 	fd->count--;
3752 	d (printf ("Decreasing itip formatter search count to %d\n", fd->count));
3753 
3754 	if (fd->count == 0 && !g_cancellable_is_cancelled (fd->cancellable)) {
3755 		gboolean rsvp_enabled = FALSE;
3756 		EMailPartItip *pitip = fd->puri;
3757 		ItipView *view = fd->view;
3758 
3759 		itip_view_remove_lower_info_item (view, pitip->progress_info_id);
3760 		pitip->progress_info_id = 0;
3761 
3762 		/*
3763 		 * Only allow replies if backend doesn't do that automatically.
3764 		 * Only enable it for forwarded invitiations (PUBLISH) or direct
3765 		 * invitiations (REQUEST), but not replies (REPLY).
3766 		 * Replies only make sense for events with an organizer.
3767 		 */
3768 		if ((!pitip->current_client || !e_cal_client_check_save_schedules (pitip->current_client)) &&
3769 		    (pitip->method == ICAL_METHOD_PUBLISH || pitip->method ==  ICAL_METHOD_REQUEST) &&
3770 		    pitip->has_organizer) {
3771 			rsvp_enabled = TRUE;
3772 		}
3773 		itip_view_set_show_rsvp_check (view, rsvp_enabled);
3774 
3775 		/* default is chosen in extract_itip_data() based on content of the VEVENT */
3776 		itip_view_set_rsvp (view, !pitip->no_reply_wanted);
3777 
3778 		if ((pitip->method == ICAL_METHOD_PUBLISH || pitip->method ==  ICAL_METHOD_REQUEST)
3779 		    && !pitip->current_client) {
3780 			/* Reuse already declared one or rename? */
3781 			ESource *source = NULL;
3782 			const gchar *extension_name;
3783 
3784 			switch (pitip->type) {
3785 				case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
3786 					extension_name = E_SOURCE_EXTENSION_CALENDAR;
3787 					break;
3788 				case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
3789 					extension_name = E_SOURCE_EXTENSION_TASK_LIST;
3790 					break;
3791 				case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
3792 					extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
3793 					break;
3794 				default:
3795 					g_return_if_reached ();
3796 			}
3797 
3798 			source = e_source_registry_ref_default_for_extension_name (
3799 				pitip->registry, extension_name);
3800 
3801 			itip_view_set_extension_name (view, extension_name);
3802 
3803 			g_signal_connect (
3804 				view, "source_selected",
3805 				G_CALLBACK (source_selected_cb), pitip);
3806 
3807 			if (source != NULL) {
3808 				itip_view_set_source (view, source);
3809 				g_object_unref (source);
3810 
3811  				/* FIXME Shouldn't the buttons be sensitized here? */
3812 			} else {
3813 				itip_view_add_lower_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR, _("Unable to find any calendars"));
3814 				itip_view_set_buttons_sensitive (view, FALSE);
3815 			}
3816 		} else if (!pitip->current_client) {
3817 			switch (pitip->type) {
3818 			case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
3819 				itip_view_add_lower_info_item_printf (
3820 					view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
3821 					_("Unable to find this meeting in any calendar"));
3822 				break;
3823 			case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
3824 				itip_view_add_lower_info_item_printf (
3825 					view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
3826 					_("Unable to find this task in any task list"));
3827 				break;
3828 			case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
3829 				itip_view_add_lower_info_item_printf (
3830 					view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
3831 					_("Unable to find this memo in any memo list"));
3832 				break;
3833 			default:
3834 				g_assert_not_reached ();
3835 				break;
3836 			}
3837 		}
3838 	}
3839 
3840 	if (fd->count == 0) {
3841 		g_hash_table_destroy (fd->conflicts);
3842 		g_cancellable_disconnect (fd->itip_cancellable, fd->cancelled_id);
3843 		g_object_unref (fd->cancellable);
3844 		g_object_unref (fd->itip_cancellable);
3845 		g_object_unref (fd->view);
3846 		g_free (fd->uid);
3847 		g_free (fd->rid);
3848 		if (fd->sexp)
3849 			g_free (fd->sexp);
3850 		g_free (fd);
3851 	}
3852 }
3853 
3854 static void
3855 get_object_without_rid_ready_cb (GObject *source_object,
3856                                  GAsyncResult *result,
3857                                  gpointer user_data)
3858 {
3859 	ECalClient *cal_client = E_CAL_CLIENT (source_object);
3860 	FormatItipFindData *fd = user_data;
3861 	icalcomponent *icalcomp = NULL;
3862 	GError *error = NULL;
3863 
3864 	if (!e_cal_client_get_object_finish (cal_client, result, &icalcomp, &error))
3865 		icalcomp = NULL;
3866 
3867 	if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
3868 	    g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
3869 	    g_cancellable_is_cancelled (fd->cancellable)) {
3870 		g_clear_error (&error);
3871 		find_cal_update_ui (fd, cal_client);
3872 		decrease_find_data (fd);
3873 		return;
3874 	}
3875 
3876 	g_clear_error (&error);
3877 
3878 	if (icalcomp) {
3879 		ECalComponent *comp;
3880 
3881 		fd->puri->current_client = cal_client;
3882 		fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method ==  ICAL_METHOD_REQUEST) &&
3883 			(icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) ||
3884 			icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) ||
3885 			icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) ||
3886 			icalcomponent_get_first_component (icalcomp, ICAL_XPROCEDUREALARM_COMPONENT) ||
3887 			icalcomponent_get_first_component (icalcomp, ICAL_XEMAILALARM_COMPONENT));
3888 
3889 		comp = e_cal_component_new_from_icalcomponent (icalcomp);
3890 		if (comp) {
3891 			ESource *source = e_client_get_source (E_CLIENT (cal_client));
3892 
3893 			g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), comp);
3894 		}
3895 
3896 		find_cal_update_ui (fd, cal_client);
3897 		decrease_find_data (fd);
3898 		return;
3899 	}
3900 
3901 	find_cal_update_ui (fd, cal_client);
3902 	decrease_find_data (fd);
3903 }
3904 
3905 static void
3906 get_object_with_rid_ready_cb (GObject *source_object,
3907                               GAsyncResult *result,
3908                               gpointer user_data)
3909 {
3910 	ECalClient *cal_client = E_CAL_CLIENT (source_object);
3911 	FormatItipFindData *fd = user_data;
3912 	icalcomponent *icalcomp = NULL;
3913 	GError *error = NULL;
3914 
3915 	if (!e_cal_client_get_object_finish (cal_client, result, &icalcomp, &error))
3916 		icalcomp = NULL;
3917 
3918 	if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
3919 	    g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
3920 	    g_cancellable_is_cancelled (fd->cancellable)) {
3921 		g_clear_error (&error);
3922 		find_cal_update_ui (fd, cal_client);
3923 		decrease_find_data (fd);
3924 		return;
3925 	}
3926 
3927 	g_clear_error (&error);
3928 
3929 	if (icalcomp) {
3930 		ECalComponent *comp;
3931 
3932 		fd->puri->current_client = cal_client;
3933 		fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method ==  ICAL_METHOD_REQUEST) &&
3934 			(icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) ||
3935 			icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) ||
3936 			icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) ||
3937 			icalcomponent_get_first_component (icalcomp, ICAL_XPROCEDUREALARM_COMPONENT) ||
3938 			icalcomponent_get_first_component (icalcomp, ICAL_XEMAILALARM_COMPONENT));
3939 
3940 		comp = e_cal_component_new_from_icalcomponent (icalcomp);
3941 		if (comp) {
3942 			ESource *source = e_client_get_source (E_CLIENT (cal_client));
3943 
3944 			g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), comp);
3945 		}
3946 
3947 		find_cal_update_ui (fd, cal_client);
3948 		decrease_find_data (fd);
3949 		return;
3950 	}
3951 
3952 	if (fd->rid && *fd->rid) {
3953 		e_cal_client_get_object (cal_client, fd->uid, NULL, fd->cancellable, get_object_without_rid_ready_cb, fd);
3954 		return;
3955 	}
3956 
3957 	find_cal_update_ui (fd, cal_client);
3958 	decrease_find_data (fd);
3959 }
3960 
3961 static void
3962 get_object_list_ready_cb (GObject *source_object,
3963                           GAsyncResult *result,
3964                           gpointer user_data)
3965 {
3966 	ECalClient *cal_client = E_CAL_CLIENT (source_object);
3967 	FormatItipFindData *fd = user_data;
3968 	GSList *objects = NULL;
3969 	GError *error = NULL;
3970 
3971 	if (!e_cal_client_get_object_list_finish (cal_client, result, &objects, &error))
3972 		objects = NULL;
3973 
3974 	if (g_cancellable_is_cancelled (fd->cancellable)) {
3975 		g_clear_error (&error);
3976 		decrease_find_data (fd);
3977 		return;
3978 	}
3979 
3980 	if (error) {
3981 		if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
3982 		    g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
3983 			g_error_free (error);
3984 			decrease_find_data (fd);
3985 			return;
3986 		}
3987 
3988 		g_error_free (error);
3989 	} else {
3990 		g_hash_table_insert (fd->conflicts, cal_client, GINT_TO_POINTER (g_slist_length (objects)));
3991 		e_cal_client_free_icalcomp_slist (objects);
3992 	}
3993 
3994 	e_cal_client_get_object (cal_client, fd->uid, fd->rid, fd->cancellable, get_object_with_rid_ready_cb, fd);
3995 }
3996 
3997 static void
3998 find_cal_opened_cb (GObject *source_object,
3999                     GAsyncResult *result,
4000                     gpointer user_data)
4001 {
4002 	ESource *source = E_SOURCE (source_object);
4003 	FormatItipFindData *fd = user_data;
4004 	EMailPartItip *pitip = fd->puri;
4005 	ItipView *view = fd->view;
4006 	ECalClientSourceType source_type;
4007 	EClient *client = NULL;
4008 	ECalClient *cal_client;
4009 	gboolean search_for_conflicts = FALSE;
4010 	const gchar *extension_name;
4011 	const gchar *uid;
4012 	GError *error = NULL;
4013 
4014 	e_client_utils_open_new_finish (source, result, &client, &error);
4015 
4016 	/* Ignore cancellations. */
4017 	if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
4018 	    g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
4019 		g_warn_if_fail (client == NULL);
4020 		decrease_find_data (fd);
4021 		g_error_free (error);
4022 		return;
4023 	}
4024 
4025 	if (g_cancellable_is_cancelled (fd->cancellable)) {
4026 		g_clear_error (&error);
4027 		decrease_find_data (fd);
4028 		return;
4029 	}
4030 
4031 	if (error) {
4032 		/* FIXME Do we really want to warn here?  If we fail
4033 		 * to find the item, this won't be cleared but the
4034 		 * selector might be shown */
4035 		g_warn_if_fail (client == NULL);
4036 		add_failed_to_load_msg (view, source, error);
4037 		decrease_find_data (fd);
4038 		g_error_free (error);
4039 		return;
4040 	}
4041 
4042 	g_return_if_fail (E_IS_CAL_CLIENT (client));
4043 
4044 	/* Do not process read-only calendars */
4045 	if (e_client_is_readonly (client)) {
4046 		g_object_unref (client);
4047 		decrease_find_data (fd);
4048 		return;
4049 	}
4050 
4051 	cal_client = E_CAL_CLIENT (client);
4052 	source_type = e_cal_client_get_source_type (cal_client);
4053 
4054 	uid = e_source_get_uid (source);
4055 	g_hash_table_insert (
4056 		pitip->clients[source_type], g_strdup (uid), cal_client);
4057 
4058 	extension_name = E_SOURCE_EXTENSION_CONFLICT_SEARCH;
4059 	if (e_source_has_extension (source, extension_name)) {
4060 		ESourceConflictSearch *extension;
4061 
4062 		extension = e_source_get_extension (source, extension_name);
4063 		search_for_conflicts =
4064 			(pitip->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) &&
4065 			e_source_conflict_search_get_include_me (extension);
4066 	}
4067 
4068  	/* Check for conflicts */
4069  	/* If the query fails, we'll just ignore it */
4070  	/* FIXME What happens for recurring conflicts? */
4071 	if (search_for_conflicts) {
4072 		e_cal_client_get_object_list (
4073 			cal_client, fd->sexp,
4074 			fd->cancellable,
4075 			get_object_list_ready_cb, fd);
4076 		return;
4077 	}
4078 
4079 	if (!pitip->current_client) {
4080 		e_cal_client_get_object (
4081 			cal_client, fd->uid, fd->rid,
4082 			fd->cancellable,
4083 			get_object_with_rid_ready_cb, fd);
4084 		return;
4085 	}
4086 
4087 	decrease_find_data (fd);
4088 }
4089 
4090 static void
4091 itip_cancellable_cancelled (GCancellable *itip_cancellable,
4092                             GCancellable *fd_cancellable)
4093 {
4094 	g_cancellable_cancel (fd_cancellable);
4095 }
4096 
4097 static void
4098 find_server (EMailPartItip *pitip,
4099              ItipView *view,
4100              ECalComponent *comp)
4101 {
4102 	FormatItipFindData *fd = NULL;
4103 	const gchar *uid;
4104 	gchar *rid = NULL;
4105 	CamelStore *parent_store;
4106 	ESourceRegistry *registry;
4107 	ESource *current_source = NULL;
4108 	GList *list, *link;
4109 	GList *conflict_list = NULL;
4110 	const gchar *extension_name;
4111 	const gchar *store_uid;
4112 
4113 	g_return_if_fail (pitip->folder != NULL);
4114 
4115 	switch (pitip->type) {
4116 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
4117 			extension_name = E_SOURCE_EXTENSION_CALENDAR;
4118 			break;
4119 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
4120 			extension_name = E_SOURCE_EXTENSION_TASK_LIST;
4121 			break;
4122 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
4123 			extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
4124 			break;
4125 		default:
4126 			g_return_if_reached ();
4127 	}
4128 
4129 	registry = pitip->registry;
4130 	list = e_source_registry_list_sources (registry, extension_name);
4131 
4132 	e_cal_component_get_uid (comp, &uid);
4133 	rid = e_cal_component_get_recurid_as_string (comp);
4134 
4135 	/* XXX Not sure what this was trying to do,
4136 	 *     but it propbably doesn't work anymore.
4137 	 *     Some comments would have been helpful. */
4138 	parent_store = camel_folder_get_parent_store (pitip->folder);
4139 
4140 	store_uid = camel_service_get_uid (CAMEL_SERVICE (parent_store));
4141 
4142 	itip_view_set_buttons_sensitive (view, FALSE);
4143 
4144 	for (link = list; link != NULL; link = g_list_next (link)) {
4145 		ESource *source = E_SOURCE (link->data);
4146 		gboolean search_for_conflicts = FALSE;
4147 		const gchar *source_uid;
4148 
4149 		extension_name = E_SOURCE_EXTENSION_CONFLICT_SEARCH;
4150 		if (e_source_has_extension (source, extension_name)) {
4151 			ESourceConflictSearch *extension;
4152 
4153 			extension =
4154 				e_source_get_extension (source, extension_name);
4155 			search_for_conflicts =
4156 				e_source_conflict_search_get_include_me (extension);
4157 		}
4158 
4159 		if (search_for_conflicts)
4160 			conflict_list = g_list_prepend (
4161 				conflict_list, g_object_ref (source));
4162 
4163 		if (current_source != NULL)
4164 			continue;
4165 
4166 		source_uid = e_source_get_uid (source);
4167 		if (g_strcmp0 (source_uid, store_uid) == 0) {
4168 			current_source = source;
4169 			conflict_list = g_list_prepend (
4170 				conflict_list, g_object_ref (source));
4171 
4172 			continue;
4173 		}
4174 	}
4175 
4176 	if (current_source) {
4177 		link = conflict_list;
4178 
4179 		pitip->progress_info_id = itip_view_add_lower_info_item (
4180 			view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
4181 			_("Opening the calendar. Please wait..."));
4182 	} else {
4183 		link = list;
4184 		pitip->progress_info_id = itip_view_add_lower_info_item (
4185 			view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
4186 			_("Searching for an existing version of this appointment"));
4187 	}
4188 
4189 	for (; link != NULL; link = g_list_next (link)) {
4190 		ESource *source = E_SOURCE (link->data);
4191 
4192 		if (!fd) {
4193 			gchar *start = NULL, *end = NULL;
4194 
4195 			fd = g_new0 (FormatItipFindData, 1);
4196 			fd->puri = pitip;
4197 			fd->view = g_object_ref (view);
4198 			fd->itip_cancellable = g_object_ref (pitip->cancellable);
4199 			fd->cancellable = g_cancellable_new ();
4200 			fd->cancelled_id = g_cancellable_connect (
4201 				fd->itip_cancellable,
4202 				G_CALLBACK (itip_cancellable_cancelled), fd->cancellable, NULL);
4203 			fd->conflicts = g_hash_table_new (g_direct_hash, g_direct_equal);
4204 			fd->uid = g_strdup (uid);
4205 			fd->rid = rid;
4206 			/* avoid free this at the end */
4207 			rid = NULL;
4208 
4209 			if (pitip->start_time && pitip->end_time) {
4210 				start = isodate_from_time_t (pitip->start_time);
4211 				end = isodate_from_time_t (pitip->end_time);
4212 
4213 				fd->sexp = g_strdup_printf (
4214 					"(and (occur-in-time-range? "
4215 					"(make-time \"%s\") "
4216 					"(make-time \"%s\")) "
4217 					"(not (uid? \"%s\")))",
4218 					start, end,
4219 					icalcomponent_get_uid (pitip->ical_comp));
4220 			}
4221 
4222 			g_free (start);
4223 			g_free (end);
4224 		}
4225 		fd->count++;
4226 		d (printf ("Increasing itip formatter search count to %d\n", fd->count));
4227 
4228 		if (current_source == source)
4229 			start_calendar_server (
4230 				pitip, view, source, pitip->type,
4231 				find_cal_opened_cb, fd);
4232 		else
4233 			start_calendar_server (
4234 				pitip, view, source, pitip->type,
4235 				find_cal_opened_cb, fd);
4236 	}
4237 
4238 	g_list_free_full (conflict_list, (GDestroyNotify) g_object_unref);
4239 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
4240 
4241 	g_free (rid);
4242 }
4243 
4244 static gboolean
4245 change_status (ESourceRegistry *registry,
4246                icalcomponent *ical_comp,
4247                const gchar *address,
4248                icalparameter_partstat status)
4249 {
4250 	icalproperty *prop;
4251 
4252 	prop = find_attendee (ical_comp, address);
4253 	if (prop) {
4254 		icalparameter *param;
4255 
4256 		icalproperty_remove_parameter (prop, ICAL_PARTSTAT_PARAMETER);
4257 		param = icalparameter_new_partstat (status);
4258 		icalproperty_add_parameter (prop, param);
4259 	} else {
4260 		icalparameter *param;