evolution-3.6.4/calendar/gui/dialogs/task-page.c

Location Tool Test ID Function Issue
task-page.c:2111:33 clang-analyzer Dereference of null pointer (loaded from variable 'backend_address')
task-page.c:2111:33 clang-analyzer Dereference of null pointer (loaded from variable 'backend_address')
   1 /*
   2  * Evolution calendar - Main page of the task editor dialog
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) version 3.
   8  *
   9  * This program is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  16  *
  17  *
  18  * Authors:
  19  *		Federico Mena-Quintero <federico@ximian.com>
  20  *		Miguel de Icaza <miguel@ximian.com>
  21  *      Seth Alves <alves@hungry.com>
  22  *      JP Rosevear <jpr@ximian.com>
  23  *
  24  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  25  *
  26  */
  27 
  28 #ifdef HAVE_CONFIG_H
  29 #include <config.h>
  30 #endif
  31 
  32 #include <string.h>
  33 #include <gtk/gtk.h>
  34 #include <glib/gi18n.h>
  35 #include <gdk/gdkkeysyms.h>
  36 
  37 #include <libedataserverui/libedataserverui.h>
  38 
  39 #include <misc/e-dateedit.h>
  40 #include <misc/e-spell-entry.h>
  41 #include <misc/e-buffer-tagger.h>
  42 
  43 #include <e-util/e-categories-config.h>
  44 #include <e-util/e-dialog-utils.h>
  45 #include <e-util/e-util-private.h>
  46 #include <e-util/e-util.h>
  47 
  48 #include "../e-meeting-attendee.h"
  49 #include "../e-meeting-list-view.h"
  50 #include "../e-meeting-store.h"
  51 #include "../e-timezone-entry.h"
  52 
  53 #include "comp-editor.h"
  54 #include "comp-editor-util.h"
  55 #include "e-send-options-utils.h"
  56 #include "task-page.h"
  57 
  58 #define TASK_PAGE_GET_PRIVATE(obj) \
  59 	(G_TYPE_INSTANCE_GET_PRIVATE \
  60 	((obj), TYPE_TASK_PAGE, TaskPagePrivate))
  61 
  62 struct _TaskPagePrivate {
  63 	GtkBuilder *builder;
  64 
  65 	/* Widgets from the UI file */
  66 	GtkWidget *main;
  67 
  68 	gchar **address_strings;
  69 	gchar *fallback_address;
  70 	EMeetingAttendee *ia;
  71 	gchar *user_add;
  72 	ECalComponent *comp;
  73 
  74 	/* For meeting/event */
  75 	GtkWidget *calendar_label;
  76 	GtkWidget *org_cal_label;
  77 	GtkWidget *attendee_box;
  78 
  79 	/* Lists of attendees */
  80 	GPtrArray *deleted_attendees;
  81 
  82 	/* Generic informative messages placeholder */
  83 	GtkWidget *info_hbox;
  84 	GtkWidget *info_icon;
  85 	GtkWidget *info_string;
  86 
  87 	GtkWidget *summary;
  88 	GtkWidget *summary_label;
  89 
  90 	GtkWidget *due_date;
  91 	GtkWidget *start_date;
  92 	GtkWidget *timezone;
  93 	GtkWidget *timezone_label;
  94 
  95 	GtkWidget *description;
  96 
  97 	GtkWidget *categories_btn;
  98 	GtkWidget *categories;
  99 
 100 	GtkWidget *source_combo_box;
 101 
 102 	/* Meeting related items */
 103 	GtkWidget *list_box;
 104 	GtkWidget *organizer_table;
 105 	GtkWidget *organizer;
 106 	GtkWidget *add;
 107 	GtkWidget *remove;
 108 	GtkWidget *edit;
 109 	GtkWidget *invite;
 110 	GtkWidget *attendees_label;
 111 
 112 	/* ListView stuff */
 113 	ECalClient *client;
 114 	EMeetingStore *meeting_store;
 115 	EMeetingListView *list_view;
 116 	gint row;
 117 
 118 	/* For handling who the organizer is */
 119 	gboolean user_org;
 120 	gboolean existing;
 121 
 122 	gboolean sendoptions_shown;
 123 	gboolean is_assignment;
 124 
 125 	ESendOptionsDialog *sod;
 126 
 127 	GCancellable *open_cancellable;
 128 };
 129 
 130 static const gint classification_map[] = {
 131 	E_CAL_COMPONENT_CLASS_PUBLIC,
 132 	E_CAL_COMPONENT_CLASS_PRIVATE,
 133 	E_CAL_COMPONENT_CLASS_CONFIDENTIAL,
 134 	-1
 135 };
 136 
 137 static gboolean task_page_fill_timezones (CompEditorPage *page, GHashTable *timezones);
 138 static void task_page_select_organizer (TaskPage *tpage, const gchar *backend_address);
 139 static void set_subscriber_info_string (TaskPage *tpage, const gchar *backend_address);
 140 
 141 G_DEFINE_TYPE (TaskPage, task_page, TYPE_COMP_EDITOR_PAGE)
 142 
 143 static gboolean
 144 get_current_identity (TaskPage *page,
 145                       gchar **name,
 146                       gchar **mailto)
 147 {
 148 	EShell *shell;
 149 	CompEditor *editor;
 150 	ESourceRegistry *registry;
 151 	GList *list, *iter;
 152 	GtkWidget *entry;
 153 	const gchar *extension_name;
 154 	const gchar *text;
 155 	gboolean match = FALSE;
 156 
 157 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
 158 	shell = comp_editor_get_shell (editor);
 159 
 160 	entry = gtk_bin_get_child (GTK_BIN (page->priv->organizer));
 161 	text = gtk_entry_get_text (GTK_ENTRY (entry));
 162 
 163 	if (text == NULL || *text == '\0')
 164 		return FALSE;
 165 
 166 	registry = e_shell_get_registry (shell);
 167 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
 168 
 169 	list = e_source_registry_list_sources (registry, extension_name);
 170 
 171 	for (iter = list; !match && iter != NULL; iter = g_list_next (iter)) {
 172 		ESource *source = E_SOURCE (iter->data);
 173 		ESourceMailIdentity *extension;
 174 		const gchar *id_name;
 175 		const gchar *id_address;
 176 		gchar *identity;
 177 
 178 		extension = e_source_get_extension (source, extension_name);
 179 
 180 		id_name = e_source_mail_identity_get_name (extension);
 181 		id_address = e_source_mail_identity_get_address (extension);
 182 
 183 		if (id_name == NULL || id_address == NULL)
 184 			continue;
 185 
 186 		identity = g_strdup_printf ("%s <%s>", id_name, id_address);
 187 		match = (g_ascii_strcasecmp (text, identity) == 0);
 188 		g_free (identity);
 189 
 190 		if (match && name != NULL)
 191 			*name = g_strdup (id_name);
 192 
 193 		if (match && mailto != NULL)
 194 			*mailto = g_strdup_printf ("MAILTO:%s", id_address);
 195 	}
 196 
 197 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
 198 
 199 	return match;
 200 }
 201 
 202 /* Fills the widgets with default values */
 203 static void
 204 clear_widgets (TaskPage *tpage)
 205 {
 206 	TaskPagePrivate *priv = tpage->priv;
 207 	CompEditor *editor;
 208 
 209 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage));
 210 
 211 	/* Summary, description */
 212 	gtk_entry_set_text (GTK_ENTRY (priv->summary), "");
 213 	gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description)), "", 0);
 214 	e_buffer_tagger_update_tags (GTK_TEXT_VIEW (priv->description));
 215 
 216 	/* Start, due times - both set to None */
 217 	e_date_edit_set_time (E_DATE_EDIT (priv->start_date), -1);
 218 	e_date_edit_set_time (E_DATE_EDIT (priv->due_date), -1);
 219 
 220 	/* Classification */
 221 	comp_editor_set_classification (editor, E_CAL_COMPONENT_CLASS_PUBLIC);
 222 
 223 	/* Categories */
 224 	gtk_entry_set_text (GTK_ENTRY (priv->categories), "");
 225 }
 226 
 227 static gboolean
 228 date_in_past (TaskPage *tpage,
 229               EDateEdit *date)
 230 {
 231 	struct icaltimetype tt = icaltime_null_time ();
 232 
 233 	if (!e_date_edit_get_date (date, &tt.year, &tt.month, &tt.day))
 234 		return FALSE;
 235 
 236 	if (e_date_edit_get_time_of_day (date, &tt.hour, &tt.minute))
 237 		tt.zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY (tpage->priv->timezone));
 238 	else
 239 		tt.is_date = TRUE;
 240 
 241 	return comp_editor_test_time_in_the_past (tt);
 242 }
 243 
 244 /* returns whether changed info text */
 245 static gboolean
 246 check_starts_in_the_past (TaskPage *tpage)
 247 {
 248 	TaskPagePrivate *priv;
 249 	gboolean start_in_past, due_in_past;
 250 
 251 	if ((comp_editor_get_flags (comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage))) & COMP_EDITOR_NEW_ITEM) == 0)
 252 		return FALSE;
 253 
 254 	priv = tpage->priv;
 255 	start_in_past = date_in_past (tpage, E_DATE_EDIT (priv->start_date));
 256 	due_in_past = date_in_past (tpage, E_DATE_EDIT (priv->due_date));
 257 
 258 	if (start_in_past || due_in_past) {
 259 		gchar *tmp = g_strconcat (
 260 			"<b>", start_in_past ? _("Task's start date is in the past") : "",
 261 			start_in_past && due_in_past ? "\n" : "", due_in_past ? _("Task's due date is in the past") : "", "</b>", NULL);
 262 		task_page_set_info_string (tpage, GTK_STOCK_DIALOG_WARNING, tmp);
 263 		g_free (tmp);
 264 	} else {
 265 		task_page_set_info_string (tpage, NULL, NULL);
 266 	}
 267 
 268 	return TRUE;
 269 }
 270 
 271 static void
 272 sensitize_widgets (TaskPage *tpage)
 273 {
 274 	TaskPagePrivate *priv = tpage->priv;
 275 	CompEditor *editor;
 276 	CompEditorFlags flags;
 277 	ECalClient *client;
 278 	GtkActionGroup *action_group;
 279 	GtkAction *action;
 280 	gboolean read_only, sens = TRUE, sensitize;
 281 
 282 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage));
 283 	client = comp_editor_get_client (editor);
 284 	flags = comp_editor_get_flags (editor);
 285 
 286 	read_only = e_client_is_readonly (E_CLIENT (client));
 287 
 288 	if (flags & COMP_EDITOR_IS_ASSIGNED)
 289 		sens = flags & COMP_EDITOR_USER_ORG;
 290 
 291 	sensitize = (!read_only && sens);
 292 
 293 	if (read_only) {
 294 		gchar *tmp = g_strconcat ("<b>", _("Task cannot be edited, because the selected task list is read only"), "</b>", NULL);
 295 		task_page_set_info_string (tpage, GTK_STOCK_DIALOG_INFO, tmp);
 296 		g_free (tmp);
 297 	} else if (!sens) {
 298 		gchar *tmp = g_strconcat ("<b>", _("Task cannot be fully edited, because you are not the organizer"), "</b>", NULL);
 299 		task_page_set_info_string (tpage, GTK_STOCK_DIALOG_INFO, tmp);
 300 		g_free (tmp);
 301 	} else if ((flags & COMP_EDITOR_IS_ASSIGNED) != 0 && e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT)) {
 302 		gchar *tmp = g_strconcat ("<b>", _("Task cannot be edited, because the selected task list does not support assigned tasks"), "</b>", NULL);
 303 		task_page_set_info_string (tpage, GTK_STOCK_DIALOG_INFO, tmp);
 304 		g_free (tmp);
 305 		sens = FALSE;
 306 		read_only = TRUE;
 307 	} else if (!check_starts_in_the_past (tpage)) {
 308 		task_page_set_info_string (tpage, NULL, NULL);
 309 	}
 310 
 311 	/* The list of organizers is set to be non-editable. Otherwise any
 312 	 * change in the displayed list causes an 'Account not found' error.
 313 	 */
 314 	gtk_editable_set_editable (GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (priv->organizer))), FALSE);
 315 
 316 	gtk_editable_set_editable (GTK_EDITABLE (priv->summary), !read_only);
 317 	gtk_widget_set_sensitive (priv->due_date, !read_only);
 318 	gtk_widget_set_sensitive (priv->start_date, !read_only);
 319 	gtk_widget_set_sensitive (priv->timezone, !read_only);
 320 	gtk_widget_set_sensitive (priv->description, !read_only);
 321 	gtk_widget_set_sensitive (priv->categories_btn, !read_only);
 322 	gtk_editable_set_editable (GTK_EDITABLE (priv->categories), !read_only);
 323 
 324 	gtk_widget_set_sensitive (priv->organizer, !read_only);
 325 	gtk_widget_set_sensitive (priv->add, (!read_only &&  sens));
 326 	gtk_widget_set_sensitive (priv->edit, (!read_only && sens));
 327 	e_meeting_list_view_set_editable (priv->list_view, (!read_only && sens));
 328 	gtk_widget_set_sensitive (priv->remove, (!read_only &&  sens));
 329 	gtk_widget_set_sensitive (priv->invite, (!read_only &&  sens));
 330 	gtk_widget_set_sensitive (GTK_WIDGET (priv->list_view), !read_only);
 331 
 332 	action_group = comp_editor_get_action_group (editor, "editable");
 333 	gtk_action_group_set_sensitive (action_group, !read_only);
 334 
 335 	action_group = comp_editor_get_action_group (editor, "individual");
 336 	gtk_action_group_set_sensitive (action_group, sensitize);
 337 
 338 	action = comp_editor_get_action (editor, "send-options");
 339 	gtk_action_set_sensitive (action, sensitize);
 340 
 341 	if (!priv->is_assignment) {
 342 		gtk_widget_hide (priv->calendar_label);
 343 		gtk_widget_hide (priv->list_box);
 344 		gtk_widget_hide (priv->attendee_box);
 345 		gtk_widget_hide (priv->organizer);
 346 		gtk_widget_hide (priv->invite);
 347 		gtk_label_set_text_with_mnemonic (GTK_LABEL (priv->org_cal_label), _("_List:"));
 348 		gtk_label_set_mnemonic_widget (GTK_LABEL (priv->org_cal_label), priv->source_combo_box);
 349 	} else {
 350 		gtk_widget_show (priv->invite);
 351 		gtk_widget_show (priv->calendar_label);
 352 		gtk_widget_show (priv->list_box);
 353 		gtk_widget_show (priv->attendee_box);
 354 		gtk_widget_show (priv->organizer);
 355 		gtk_label_set_text_with_mnemonic (GTK_LABEL (priv->org_cal_label), _("Organi_zer:"));
 356 		gtk_label_set_mnemonic_widget (GTK_LABEL (priv->org_cal_label), priv->organizer);
 357 	}
 358 }
 359 
 360 static void
 361 set_attendees (ECalComponent *comp,
 362                const GPtrArray *attendees)
 363 {
 364 	GSList *comp_attendees = NULL, *l;
 365 	gint i;
 366 
 367 	for (i = 0; i < attendees->len; i++) {
 368 		EMeetingAttendee *ia = g_ptr_array_index (attendees, i);
 369 		ECalComponentAttendee *ca;
 370 
 371 		ca = e_meeting_attendee_as_e_cal_component_attendee (ia);
 372 
 373 		comp_attendees = g_slist_prepend (comp_attendees, ca);
 374 
 375 	}
 376 	comp_attendees = g_slist_reverse (comp_attendees);
 377 
 378 	e_cal_component_set_attendee_list (comp, comp_attendees);
 379 
 380 	for (l = comp_attendees; l != NULL; l = l->next)
 381 		g_free (l->data);
 382 	g_slist_free (comp_attendees);
 383 }
 384 
 385 static void
 386 organizer_changed_cb (GtkEntry *entry,
 387                       TaskPage *tpage)
 388 {
 389 	gchar *name;
 390 	gchar *mailto;
 391 
 392 	g_return_if_fail (GTK_IS_ENTRY (entry));
 393 	g_return_if_fail (IS_TASK_PAGE (tpage));
 394 
 395 	if (!tpage->priv->ia)
 396 		return;
 397 
 398 	if (!get_current_identity (tpage, &name, &mailto))
 399 		return;
 400 
 401 	/* XXX EMeetingAttendee takes ownership of the strings. */
 402 	e_meeting_attendee_set_cn (tpage->priv->ia, name);
 403 	e_meeting_attendee_set_address (tpage->priv->ia, mailto);
 404 }
 405 
 406 static void
 407 task_page_dispose (GObject *object)
 408 {
 409 	TaskPagePrivate *priv;
 410 
 411 	priv = TASK_PAGE_GET_PRIVATE (object);
 412 
 413 	if (priv->open_cancellable) {
 414 		g_cancellable_cancel (priv->open_cancellable);
 415 		g_object_unref (priv->open_cancellable);
 416 		priv->open_cancellable = NULL;
 417 	}
 418 
 419 	if (priv->main != NULL) {
 420 		g_object_unref (priv->main);
 421 		priv->main = NULL;
 422 	}
 423 
 424 	if (priv->builder != NULL) {
 425 		g_object_unref (priv->builder);
 426 		priv->builder = NULL;
 427 	}
 428 
 429 	if (priv->sod != NULL) {
 430 		g_object_unref (priv->sod);
 431 		priv->sod = NULL;
 432 	}
 433 
 434 	if (priv->comp != NULL) {
 435 		g_object_unref (priv->comp);
 436 		priv->comp = NULL;
 437 	}
 438 
 439 	/* Chain up to parent's dispose() method. */
 440 	G_OBJECT_CLASS (task_page_parent_class)->dispose (object);
 441 }
 442 
 443 static void
 444 task_page_finalize (GObject *object)
 445 {
 446 	TaskPagePrivate *priv;
 447 
 448 	priv = TASK_PAGE_GET_PRIVATE (object);
 449 
 450 	g_strfreev (priv->address_strings);
 451 	g_free (priv->fallback_address);
 452 
 453 	g_ptr_array_foreach (
 454 		priv->deleted_attendees, (GFunc) g_object_unref, NULL);
 455 	g_ptr_array_free (priv->deleted_attendees, TRUE);
 456 
 457 	/* Chain up to parent's finalize() method. */
 458 	G_OBJECT_CLASS (task_page_parent_class)->finalize (object);
 459 }
 460 
 461 static GtkWidget *
 462 task_page_get_widget (CompEditorPage *page)
 463 {
 464 	TaskPage *tpage;
 465 	TaskPagePrivate *priv;
 466 
 467 	tpage = TASK_PAGE (page);
 468 	priv = tpage->priv;
 469 
 470 	return priv->main;
 471 }
 472 
 473 static void
 474 task_page_focus_main_widget (CompEditorPage *page)
 475 {
 476 	TaskPage *tpage;
 477 	TaskPagePrivate *priv;
 478 
 479 	tpage = TASK_PAGE (page);
 480 	priv = tpage->priv;
 481 
 482 	gtk_widget_grab_focus (priv->summary);
 483 }
 484 
 485 static gboolean
 486 task_page_fill_widgets (CompEditorPage *page,
 487                         ECalComponent *comp)
 488 {
 489 	TaskPage *tpage;
 490 	TaskPagePrivate *priv;
 491 	ECalComponentText text;
 492 	ECalComponentDateTime d;
 493 	ECalComponentClassification cl;
 494 	ESourceRegistry *registry;
 495 	CompEditor *editor;
 496 	CompEditorFlags flags;
 497 	GtkAction *action;
 498 	ECalClient *client;
 499 	EShell *shell;
 500 	GSList *l;
 501 	icalcomponent *icalcomp;
 502 	const gchar *categories, *uid;
 503 	icaltimezone *zone, *default_zone;
 504 	gchar *backend_addr = NULL;
 505 	gboolean active;
 506 
 507 	tpage = TASK_PAGE (page);
 508 	priv = tpage->priv;
 509 
 510 	editor = comp_editor_page_get_editor (page);
 511 	client = comp_editor_get_client (editor);
 512 	flags = comp_editor_get_flags (editor);
 513 	shell = comp_editor_get_shell (editor);
 514 
 515 	registry = e_shell_get_registry (shell);
 516 
 517 	/* Clean out old data */
 518 	if (priv->comp != NULL)
 519 		g_object_unref (priv->comp);
 520 	priv->comp = NULL;
 521 
 522 	g_ptr_array_foreach (
 523 		priv->deleted_attendees, (GFunc) g_object_unref, NULL);
 524 	g_ptr_array_set_size (priv->deleted_attendees, 0);
 525 
 526 	/* Component for cancellation */
 527 	priv->comp = e_cal_component_clone (comp);
 528 	comp_editor_copy_new_attendees (priv->comp, comp);
 529 
 530 	/* Clean the screen */
 531 	clear_widgets (tpage);
 532 
 533 	priv->user_add = itip_get_comp_attendee (
 534 		registry, comp, client);
 535 
 536         /* Summary, description(s) */
 537 	e_cal_component_get_summary (comp, &text);
 538 	if (text.value != NULL)
 539 		gtk_entry_set_text (GTK_ENTRY (priv->summary), text.value);
 540 	else
 541 		gtk_entry_set_text (GTK_ENTRY (priv->summary), "");
 542 
 543 	e_cal_component_get_description_list (comp, &l);
 544 	if (l && l->data) {
 545 		ECalComponentText *dtext;
 546 
 547 		dtext = l->data;
 548 		gtk_text_buffer_set_text (
 549 			gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description)),
 550 			dtext->value ? dtext->value : "", -1);
 551 	} else {
 552 		gtk_text_buffer_set_text (
 553 			gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description)),
 554 			"", 0);
 555 	}
 556 	e_cal_component_free_text_list (l);
 557 	e_buffer_tagger_update_tags (GTK_TEXT_VIEW (priv->description));
 558 
 559 	default_zone = comp_editor_get_timezone (editor);
 560 
 561 	/* Due Date. */
 562 	e_cal_component_get_due (comp, &d);
 563 	zone = NULL;
 564 	if (d.value) {
 565 		struct icaltimetype *due_tt = d.value;
 566 		e_date_edit_set_date (
 567 			E_DATE_EDIT (priv->due_date),
 568 			due_tt->year, due_tt->month,
 569 			due_tt->day);
 570 		e_date_edit_set_time_of_day (E_DATE_EDIT (priv->due_date), -1, -1);
 571 	} else {
 572 		e_date_edit_set_time (E_DATE_EDIT (priv->due_date), -1);
 573 
 574 		/* If no time is set, we use the default timezone, so the
 575 		 * user usually doesn't have to set this when they set the
 576 		 * date. */
 577 		zone = NULL;
 578 	}
 579 
 580 	/* Note that if we are creating a new task, the timezones may not be
 581 	 * on the server, so we try to get the builtin timezone with the TZID
 582 	 * first. */
 583 	if (!zone && d.tzid) {
 584 		GError *error = NULL;
 585 
 586 		e_cal_client_get_timezone_sync (
 587 			client, d.tzid, &zone, NULL, &error);
 588 
 589 		if (error != NULL) {
 590 			/* FIXME: Handle error better. */
 591 			g_warning (
 592 				"Couldn't get timezone '%s' from server: %s",
 593 				d.tzid ? d.tzid : "", error->message);
 594 			g_error_free (error);
 595 		}
 596 	}
 597 
 598 	e_timezone_entry_set_timezone (
 599 		E_TIMEZONE_ENTRY (priv->timezone),
 600 		zone ? zone : default_zone);
 601 
 602 	action = comp_editor_get_action (editor, "view-time-zone");
 603 	active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
 604 	task_page_set_show_timezone (tpage, active);
 605 
 606 	if (!(flags & COMP_EDITOR_NEW_ITEM) && !zone) {
 607 		GtkAction *action;
 608 
 609 		task_page_set_show_timezone (tpage, FALSE);
 610 		action = comp_editor_get_action (editor, "view-time-zone");
 611 		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
 612 	}
 613 
 614 	e_cal_component_free_datetime (&d);
 615 
 616 	/* Start Date. */
 617 	e_cal_component_get_dtstart (comp, &d);
 618 	zone = NULL;
 619 	if (d.value) {
 620 		struct icaltimetype *start_tt = d.value;
 621 		e_date_edit_set_date (
 622 			E_DATE_EDIT (priv->start_date),
 623 			start_tt->year, start_tt->month,
 624 			start_tt->day);
 625 		e_date_edit_set_time_of_day (E_DATE_EDIT (priv->start_date), -1, -1);
 626 	} else {
 627 		e_date_edit_set_time (E_DATE_EDIT (priv->start_date), -1);
 628 	}
 629 
 630 	e_cal_component_free_datetime (&d);
 631 
 632 	/* Classification. */
 633 	e_cal_component_get_classification (comp, &cl);
 634 	comp_editor_set_classification (editor, cl);
 635 
 636 	e_cal_component_get_uid (comp, &uid);
 637 	if (e_cal_client_get_object_sync (client, uid, NULL, &icalcomp, NULL, NULL)) {
 638 		icalcomponent_free (icalcomp);
 639 		task_page_hide_options (tpage);
 640 	}
 641 
 642 	/* Categories */
 643 	e_cal_component_get_categories (comp, &categories);
 644 	if (categories != NULL)
 645 		gtk_entry_set_text (GTK_ENTRY (priv->categories), categories);
 646 	else
 647 		gtk_entry_set_text (GTK_ENTRY (priv->categories), "");
 648 
 649 	/* Source */
 650 	e_source_combo_box_set_active (
 651 		E_SOURCE_COMBO_BOX (priv->source_combo_box),
 652 		e_client_get_source (E_CLIENT (client)));
 653 
 654 	e_client_get_backend_property_sync (E_CLIENT (client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &backend_addr, NULL, NULL);
 655 	set_subscriber_info_string (tpage, backend_addr);
 656 
 657 	if (priv->is_assignment) {
 658 		ECalComponentOrganizer organizer;
 659 		gchar *name = NULL;
 660 		gchar *mailto = NULL;
 661 
 662 		priv->user_add = itip_get_comp_attendee (
 663 			registry, comp, client);
 664 
 665 		/* Organizer strings */
 666 		task_page_select_organizer (tpage, backend_addr);
 667 
 668 		/* If there is an existing organizer show it properly */
 669 		if (e_cal_component_has_organizer (comp)) {
 670 			e_cal_component_get_organizer (comp, &organizer);
 671 			if (organizer.value != NULL) {
 672 				const gchar *strip = itip_strip_mailto (organizer.value);
 673 				gchar *string;
 674 
 675 				if (itip_organizer_is_user (registry, comp, client) ||
 676 				    itip_sentby_is_user (registry, comp, client)) {
 677 					if (e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS))
 678 						priv->user_org = TRUE;
 679 				} else {
 680 					if (e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS))
 681 						gtk_widget_set_sensitive (priv->invite, FALSE);
 682 					gtk_widget_set_sensitive (priv->add, FALSE);
 683 					gtk_widget_set_sensitive (priv->edit, FALSE);
 684 					gtk_widget_set_sensitive (priv->remove, FALSE);
 685 					priv->user_org = FALSE;
 686 				}
 687 
 688 				if (e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_NO_ORGANIZER) && (flags & COMP_EDITOR_DELEGATE))
 689 					string = g_strdup (priv->user_add);
 690 				else if (organizer.cn != NULL)
 691 					string = g_strdup_printf ("%s <%s>", organizer.cn, strip);
 692 				else
 693 					string = g_strdup (strip);
 694 
 695 				g_signal_handlers_block_by_func (gtk_bin_get_child (GTK_BIN (priv->organizer)), organizer_changed_cb, tpage);
 696 
 697 				if (!priv->user_org) {
 698 					GtkComboBox *combo_box;
 699 					GtkListStore *list_store;
 700 					GtkTreeModel *model;
 701 					GtkTreeIter iter;
 702 
 703 					combo_box = GTK_COMBO_BOX (priv->organizer);
 704 					model = gtk_combo_box_get_model (combo_box);
 705 					list_store = GTK_LIST_STORE (model);
 706 
 707 					gtk_list_store_clear (list_store);
 708 					gtk_list_store_append (list_store, &iter);
 709 					gtk_list_store_set (list_store, &iter, 0, string, -1);
 710 					gtk_combo_box_set_active (combo_box, 0);
 711 					gtk_editable_set_editable (GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (priv->organizer))), FALSE);
 712 				} else {
 713 					gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->organizer))), string);
 714 				}
 715 
 716 				g_signal_handlers_unblock_by_func (gtk_bin_get_child (GTK_BIN (priv->organizer)), organizer_changed_cb, tpage);
 717 
 718 				g_free (string);
 719 				priv->existing = TRUE;
 720 			}
 721 		} else if (get_current_identity (tpage, &name, &mailto)) {
 722 			EMeetingAttendee *attendee;
 723 			gchar *backend_mailto = NULL;
 724 
 725 			if (backend_addr != NULL && *backend_addr != '\0') {
 726 				backend_mailto = g_strdup_printf (
 727 					"MAILTO:%s", backend_addr);
 728 				if (g_ascii_strcasecmp (backend_mailto, mailto) == 0) {
 729 					g_free (backend_mailto);
 730 					backend_mailto = NULL;
 731 				}
 732 			}
 733 
 734 			attendee =
 735 				e_meeting_store_add_attendee_with_defaults (
 736 				priv->meeting_store);
 737 			priv->ia = g_object_ref (attendee);
 738 
 739 			if (backend_mailto == NULL) {
 740 				e_meeting_attendee_set_cn (attendee, name);
 741 				e_meeting_attendee_set_address (attendee, mailto);
 742 				name = mailto = NULL;
 743 			} else {
 744 				e_meeting_attendee_set_address (attendee, backend_mailto);
 745 				e_meeting_attendee_set_sentby (attendee, mailto);
 746 				backend_mailto = mailto = NULL;
 747 			}
 748 
 749 			if (client && e_cal_client_check_organizer_must_accept (client))
 750 				e_meeting_attendee_set_status (
 751 					attendee, ICAL_PARTSTAT_NEEDSACTION);
 752 			else
 753 				e_meeting_attendee_set_status (
 754 					attendee, ICAL_PARTSTAT_ACCEPTED);
 755 
 756 			e_meeting_list_view_add_attendee_to_name_selector (
 757 				E_MEETING_LIST_VIEW (priv->list_view), attendee);
 758 
 759 			g_free (backend_mailto);
 760 		}
 761 
 762 		g_free (mailto);
 763 		g_free (name);
 764 	}
 765 
 766 	g_free (backend_addr);
 767 
 768 	sensitize_widgets (tpage);
 769 
 770 	return TRUE;
 771 }
 772 
 773 static gboolean
 774 task_page_fill_component (CompEditorPage *page,
 775                           ECalComponent *comp)
 776 {
 777 	TaskPage *tpage;
 778 	TaskPagePrivate *priv;
 779 	ECalComponentClassification classification;
 780 	ECalComponentDateTime date;
 781 	CompEditor *editor;
 782 	CompEditorFlags flags;
 783 	ECalClient *client;
 784 	struct icaltimetype start_tt, due_tt;
 785 	gchar *cat, *str;
 786 	gboolean start_date_set, due_date_set;
 787 	GtkTextBuffer *text_buffer;
 788 	GtkTextIter text_iter_start, text_iter_end;
 789 
 790 	tpage = TASK_PAGE (page);
 791 	priv = tpage->priv;
 792 	text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description));
 793 
 794 	editor = comp_editor_page_get_editor (page);
 795 	client = comp_editor_get_client (editor);
 796 	flags = comp_editor_get_flags (editor);
 797 
 798 	/* Summary. */
 799 
 800 	str = gtk_editable_get_chars (GTK_EDITABLE (priv->summary), 0, -1);
 801 	if (str == NULL || *str == '\0')
 802 		e_cal_component_set_summary (comp, NULL);
 803 	else {
 804 		ECalComponentText text;
 805 
 806 		text.value = str;
 807 		text.altrep = NULL;
 808 
 809 		e_cal_component_set_summary (comp, &text);
 810 	}
 811 
 812 	g_free (str);
 813 
 814 	/* Description */
 815 
 816 	gtk_text_buffer_get_start_iter (text_buffer, &text_iter_start);
 817 	gtk_text_buffer_get_end_iter   (text_buffer, &text_iter_end);
 818 	str = gtk_text_buffer_get_text (text_buffer, &text_iter_start, &text_iter_end, FALSE);
 819 
 820 	if (!str || strlen (str) == 0)
 821 		e_cal_component_set_description_list (comp, NULL);
 822 	else {
 823 		GSList l;
 824 		ECalComponentText text;
 825 
 826 		text.value = str;
 827 		text.altrep = NULL;
 828 		l.data = &text;
 829 		l.next = NULL;
 830 
 831 		e_cal_component_set_description_list (comp, &l);
 832 	}
 833 
 834 	g_free (str);
 835 
 836 	/* Dates */
 837 
 838 	due_tt = icaltime_null_time ();
 839 
 840 	date.value = &due_tt;
 841 	date.tzid = NULL;
 842 
 843 	/* Due Date. */
 844 	if (!e_date_edit_date_is_valid (E_DATE_EDIT (priv->due_date))) {
 845 		comp_editor_page_display_validation_error (page, _("Due date is wrong"), priv->due_date);
 846 		return FALSE;
 847 	}
 848 
 849 	due_date_set = e_date_edit_get_date (
 850 		E_DATE_EDIT (priv->due_date),
 851 		&due_tt.year,
 852 		&due_tt.month,
 853 		&due_tt.day);
 854 	if (due_date_set) {
 855 		due_tt.is_date = TRUE;
 856 		date.tzid = NULL;
 857 		e_cal_component_set_due (comp, &date);
 858 	} else {
 859 		e_cal_component_set_due (comp, NULL);
 860 	}
 861 
 862 	/* Start Date. */
 863 	if (!e_date_edit_date_is_valid (E_DATE_EDIT (priv->start_date))) {
 864 		comp_editor_page_display_validation_error (page, _("Start date is wrong"), priv->start_date);
 865 		return FALSE;
 866 	}
 867 
 868 	start_tt = icaltime_null_time ();
 869 	date.value = &start_tt;
 870 	start_date_set = e_date_edit_get_date (
 871 		E_DATE_EDIT (priv->start_date),
 872 		&start_tt.year,
 873 		&start_tt.month,
 874 		&start_tt.day);
 875 	if (start_date_set) {
 876 		start_tt.is_date = TRUE;
 877 		date.tzid = NULL;
 878 		e_cal_component_set_dtstart (comp, &date);
 879 	} else {
 880 		e_cal_component_set_dtstart (comp, NULL);
 881 	}
 882 
 883 	/* Classification. */
 884 	classification = comp_editor_get_classification (editor);
 885 	e_cal_component_set_classification (comp, classification);
 886 
 887 	/* send options */
 888 	if (priv->sendoptions_shown && priv->sod) {
 889 		icaltimezone *zone = comp_editor_get_timezone (editor);
 890 		e_send_options_utils_fill_component (priv->sod, comp, zone);
 891 	}
 892 
 893 	/* Categories */
 894 	cat = gtk_editable_get_chars (GTK_EDITABLE (priv->categories), 0, -1);
 895 	str = comp_editor_strip_categories (cat);
 896 	g_free (cat);
 897 
 898 	e_cal_component_set_categories (comp, str);
 899 
 900 	if (str)
 901 		g_free (str);
 902 
 903 	if (priv->is_assignment) {
 904 		ECalComponentOrganizer organizer = {NULL, NULL, NULL, NULL};
 905 
 906 		if (!priv->existing) {
 907 			gchar *backend_addr = NULL;
 908 			gchar *backend_mailto = NULL;
 909 			gchar *name;
 910 			gchar *mailto;
 911 
 912 			e_client_get_backend_property_sync (E_CLIENT (client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &backend_addr, NULL, NULL);
 913 
 914 			/* Find the identity for the organizer or sentby field */
 915 			if (!get_current_identity (tpage, &name, &mailto)) {
 916 				e_notice (
 917 					priv->main, GTK_MESSAGE_ERROR,
 918 					_("An organizer is required."));
 919 				return FALSE;
 920 			}
 921 
 922 			/* Prefer the backend address if we have one. */
 923 			if (backend_addr != NULL && *backend_addr != '\0') {
 924 				backend_mailto = g_strdup_printf (
 925 					"MAILTO:%s", backend_addr);
 926 				if (g_ascii_strcasecmp (backend_mailto, mailto) == 0) {
 927 					g_free (backend_mailto);
 928 					backend_mailto = NULL;
 929 				}
 930 			}
 931 
 932 			if (backend_mailto == NULL) {
 933 				organizer.cn = name;
 934 				organizer.value = mailto;
 935 				name = mailto = NULL;
 936 			} else {
 937 				organizer.value = backend_mailto;
 938 				organizer.sentby = mailto;
 939 				backend_mailto = mailto = NULL;
 940 			}
 941 
 942 			e_cal_component_set_organizer (comp, &organizer);
 943 
 944 			g_free (backend_addr);
 945 			g_free (backend_mailto);
 946 			g_free (name);
 947 			g_free (mailto);
 948 		}
 949 
 950 		if (e_meeting_store_count_actual_attendees (priv->meeting_store) < 1) {
 951 			e_notice (
 952 				priv->main, GTK_MESSAGE_ERROR,
 953 				_("At least one attendee is required."));
 954 			return FALSE;
 955 		}
 956 
 957 		if (flags & COMP_EDITOR_DELEGATE) {
 958 			GSList *attendee_list, *l;
 959 			gint i;
 960 			const GPtrArray *attendees = e_meeting_store_get_attendees (priv->meeting_store);
 961 
 962 			e_cal_component_get_attendee_list (priv->comp, &attendee_list);
 963 
 964 			for (i = 0; i < attendees->len; i++) {
 965 				EMeetingAttendee *ia = g_ptr_array_index (attendees, i);
 966 				ECalComponentAttendee *ca;
 967 
 968 				/* Remove the duplicate user from the component if present */
 969 				if (e_meeting_attendee_is_set_delfrom (ia) || e_meeting_attendee_is_set_delto (ia)) {
 970 					for (l = attendee_list; l; l = l->next) {
 971 						ECalComponentAttendee *a = l->data;
 972 
 973 						if (g_str_equal (a->value, e_meeting_attendee_get_address (ia))) {
 974 							attendee_list = g_slist_remove (attendee_list, l->data);
 975 							break;
 976 						}
 977 					}
 978 				}
 979 
 980 				ca = e_meeting_attendee_as_e_cal_component_attendee (ia);
 981 
 982 				attendee_list = g_slist_append (attendee_list, ca);
 983 			}
 984 			e_cal_component_set_attendee_list (comp, attendee_list);
 985 			e_cal_component_free_attendee_list (attendee_list);
 986 		} else
 987 			set_attendees (comp, e_meeting_store_get_attendees (priv->meeting_store));
 988 	}
 989 
 990 	return TRUE;
 991 }
 992 
 993 static gboolean
 994 task_page_fill_timezones (CompEditorPage *page,
 995                           GHashTable *timezones)
 996 {
 997 	TaskPage *tpage;
 998 	TaskPagePrivate *priv;
 999 	icaltimezone *zone;
1000 
1001 	tpage = TASK_PAGE (page);
1002 	priv = tpage->priv;
1003 
1004 	/* add start date timezone */
1005 	zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY (priv->timezone));
1006 	if (zone) {
1007 		if (!g_hash_table_lookup (timezones, icaltimezone_get_tzid (zone)))
1008 			g_hash_table_insert (timezones, (gpointer) icaltimezone_get_tzid (zone), zone);
1009 	}
1010 
1011 	return TRUE;
1012 }
1013 
1014 static void
1015 task_page_add_attendee (CompEditorPage *page,
1016                         EMeetingAttendee *attendee)
1017 {
1018 	CompEditor *editor;
1019 	TaskPagePrivate *priv;
1020 
1021 	priv = TASK_PAGE_GET_PRIVATE (page);
1022 	editor = comp_editor_page_get_editor (page);
1023 
1024 	if ((comp_editor_get_flags (editor) & COMP_EDITOR_DELEGATE) != 0) {
1025 		gchar *delfrom;
1026 
1027 		/* EMeetingAttendee takes ownership of the string. */
1028 		delfrom = g_strdup_printf ("MAILTO:%s", priv->user_add);
1029 		e_meeting_attendee_set_delfrom (attendee, delfrom);
1030 	}
1031 
1032 	e_meeting_store_add_attendee (priv->meeting_store, attendee);
1033 	e_meeting_list_view_add_attendee_to_name_selector (
1034 		E_MEETING_LIST_VIEW (priv->list_view), attendee);
1035 }
1036 
1037 static void
1038 task_page_class_init (TaskPageClass *class)
1039 {
1040 	GObjectClass *object_class;
1041 	CompEditorPageClass *editor_page_class;
1042 
1043 	g_type_class_add_private (class, sizeof (TaskPagePrivate));
1044 
1045 	object_class = G_OBJECT_CLASS (class);
1046 	object_class->dispose = task_page_dispose;
1047 	object_class->finalize = task_page_finalize;
1048 
1049 	editor_page_class = COMP_EDITOR_PAGE_CLASS (class);
1050 	editor_page_class->get_widget = task_page_get_widget;
1051 	editor_page_class->focus_main_widget = task_page_focus_main_widget;
1052 	editor_page_class->fill_widgets = task_page_fill_widgets;
1053 	editor_page_class->fill_component = task_page_fill_component;
1054 	editor_page_class->fill_timezones = task_page_fill_timezones;
1055 	editor_page_class->add_attendee = task_page_add_attendee;
1056 }
1057 
1058 static void
1059 task_page_init (TaskPage *tpage)
1060 {
1061 	tpage->priv = TASK_PAGE_GET_PRIVATE (tpage);
1062 	tpage->priv->deleted_attendees = g_ptr_array_new ();
1063 	tpage->priv->open_cancellable = NULL;
1064 }
1065 
1066 void
1067 task_page_set_view_role (TaskPage *page,
1068                          gboolean state)
1069 {
1070 	TaskPagePrivate *priv = page->priv;
1071 
1072 	e_meeting_list_view_column_set_visible (priv->list_view, E_MEETING_STORE_ROLE_COL, state);
1073 }
1074 
1075 void
1076 task_page_set_view_status (TaskPage *page,
1077                            gboolean state)
1078 {
1079 	TaskPagePrivate *priv = page->priv;
1080 
1081 	e_meeting_list_view_column_set_visible (priv->list_view, E_MEETING_STORE_STATUS_COL, state);
1082 }
1083 
1084 void
1085 task_page_set_view_type (TaskPage *page,
1086                          gboolean state)
1087 {
1088 	TaskPagePrivate *priv = page->priv;
1089 
1090 	e_meeting_list_view_column_set_visible (priv->list_view, E_MEETING_STORE_TYPE_COL, state);
1091 }
1092 
1093 void
1094 task_page_set_view_rsvp (TaskPage *page,
1095                          gboolean state)
1096 {
1097 	TaskPagePrivate *priv = page->priv;
1098 
1099 	e_meeting_list_view_column_set_visible (priv->list_view, E_MEETING_STORE_RSVP_COL, state);
1100 }
1101 
1102 void
1103 task_page_hide_options (TaskPage *page)
1104 {
1105 	CompEditor *editor;
1106 	GtkAction *action;
1107 
1108 	g_return_if_fail (IS_TASK_PAGE (page));
1109 
1110 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
1111 	action = comp_editor_get_action (editor, "send-options");
1112 	gtk_action_set_visible (action, FALSE);
1113 }
1114 
1115 void
1116 task_page_show_options (TaskPage *page)
1117 {
1118 	CompEditor *editor;
1119 	GtkAction *action;
1120 
1121 	g_return_if_fail (IS_TASK_PAGE (page));
1122 
1123 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
1124 	action = comp_editor_get_action (editor, "send-options");
1125 	gtk_action_set_visible (action, TRUE);
1126 }
1127 
1128 void
1129 task_page_set_assignment (TaskPage *page,
1130                           gboolean set)
1131 {
1132 	g_return_if_fail (IS_TASK_PAGE (page));
1133 
1134 	page->priv->is_assignment = set;
1135 	sensitize_widgets (page);
1136 }
1137 
1138 static void
1139 add_clicked_cb (GtkButton *btn,
1140                 TaskPage *page)
1141 {
1142 	EMeetingAttendee *attendee;
1143 	CompEditor *editor;
1144 	CompEditorFlags flags;
1145 
1146 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
1147 	flags = comp_editor_get_flags (editor);
1148 
1149 	attendee = e_meeting_store_add_attendee_with_defaults (page->priv->meeting_store);
1150 
1151 	if (flags & COMP_EDITOR_DELEGATE) {
1152 		e_meeting_attendee_set_delfrom (attendee, g_strdup_printf ("MAILTO:%s", page->priv->user_add));
1153 	}
1154 
1155 	e_meeting_list_view_edit (page->priv->list_view, attendee);
1156 }
1157 
1158 static void edit_clicked_cb (GtkButton *btn, TaskPage *tpage)
1159 {
1160 	TaskPagePrivate *priv;
1161 	GtkTreePath *path = NULL;
1162 	GtkTreeViewColumn *focus_col;
1163 
1164 	priv = tpage->priv;
1165 
1166 	gtk_tree_view_get_cursor (GTK_TREE_VIEW (priv->list_view), &path, NULL);
1167 	g_return_if_fail (path != NULL);
1168 
1169 	gtk_tree_view_get_cursor (GTK_TREE_VIEW (priv->list_view), &path, &focus_col);
1170 	gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->list_view), path, focus_col, TRUE);
1171 	gtk_tree_path_free (path);
1172 }
1173 
1174 static gboolean
1175 existing_attendee (EMeetingAttendee *ia,
1176                    ECalComponent *comp)
1177 {
1178 	GSList *attendees, *l;
1179 	const gchar *ia_address;
1180 	const gchar *ia_sentby = NULL;
1181 
1182 	ia_address = itip_strip_mailto (e_meeting_attendee_get_address (ia));
1183 	if (!ia_address)
1184 		return FALSE;
1185 
1186 	if (e_meeting_attendee_is_set_sentby (ia))
1187 		ia_sentby = itip_strip_mailto (e_meeting_attendee_get_sentby (ia));
1188 
1189 	e_cal_component_get_attendee_list (comp, &attendees);
1190 
1191 	for (l = attendees; l; l = l->next) {
1192 		ECalComponentAttendee *attendee = l->data;
1193 		const gchar *address;
1194 		const gchar *sentby = NULL;
1195 
1196 		address = itip_strip_mailto (attendee->value);
1197 		if (attendee->sentby)
1198 			sentby = itip_strip_mailto (attendee->sentby);
1199 
1200 		if ((address && !g_ascii_strcasecmp (ia_address, address)) || (sentby && ia_sentby && !g_ascii_strcasecmp (ia_sentby, sentby))) {
1201 			e_cal_component_free_attendee_list (attendees);
1202 			return TRUE;
1203 		}
1204 	}
1205 
1206 	e_cal_component_free_attendee_list (attendees);
1207 
1208 	return FALSE;
1209 }
1210 
1211 static void
1212 remove_attendee (TaskPage *page,
1213                  EMeetingAttendee *ia)
1214 {
1215 	TaskPagePrivate *priv = page->priv;
1216 	CompEditor *editor;
1217 	CompEditorFlags flags;
1218 	gint pos = 0;
1219 
1220 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
1221 	flags = comp_editor_get_flags (editor);
1222 
1223 	/* If the user deletes the organizer attendee explicitly,
1224 	 * assume they no longer want the organizer showing up */
1225 	if (ia == priv->ia) {
1226 		g_object_unref (priv->ia);
1227 		priv->ia = NULL;
1228 	}
1229 
1230 	/* If this was a delegatee, no longer delegate */
1231 	if (e_meeting_attendee_is_set_delfrom (ia)) {
1232 		EMeetingAttendee *ib;
1233 
1234 		ib = e_meeting_store_find_attendee (priv->meeting_store, e_meeting_attendee_get_delfrom (ia), &pos);
1235 		if (ib != NULL) {
1236 			e_meeting_attendee_set_delto (ib, NULL);
1237 
1238 			if (!(flags & COMP_EDITOR_DELEGATE))
1239 				e_meeting_attendee_set_edit_level (ib,  E_MEETING_ATTENDEE_EDIT_FULL);
1240 		}
1241 	}
1242 
1243 	/* Handle deleting all attendees in the delegation chain */
1244 	while (ia != NULL) {
1245 		EMeetingAttendee *ib = NULL;
1246 
1247 		if (existing_attendee (ia, priv->comp) && !comp_editor_have_in_new_attendees (priv->comp, ia)) {
1248 			g_object_ref (ia);
1249 			g_ptr_array_add (priv->deleted_attendees, ia);
1250 		}
1251 
1252 		if (e_meeting_attendee_get_delto (ia) != NULL)
1253 			ib = e_meeting_store_find_attendee (priv->meeting_store, e_meeting_attendee_get_delto (ia), NULL);
1254 
1255 		comp_editor_manage_new_attendees (priv->comp, ia, FALSE);
1256 		e_meeting_list_view_remove_attendee_from_name_selector (priv->list_view, ia);
1257 		e_meeting_store_remove_attendee (priv->meeting_store, ia);
1258 
1259 		ia = ib;
1260 	}
1261 
1262 	sensitize_widgets (page);
1263 }
1264 
1265 static void
1266 remove_clicked_cb (GtkButton *btn,
1267                    TaskPage *page)
1268 {
1269 	TaskPagePrivate *priv;
1270 	EMeetingAttendee *ia;
1271 	GtkTreeSelection *selection;
1272 	GList *paths = NULL, *tmp;
1273 	GtkTreeIter iter;
1274 	GtkTreePath *path = NULL;
1275 	GtkTreeModel *model = NULL;
1276 	gboolean valid_iter;
1277 	gchar *address;
1278 
1279 	priv = page->priv;
1280 
1281 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view));
1282 	model = GTK_TREE_MODEL (priv->meeting_store);
1283 	if (!(paths = gtk_tree_selection_get_selected_rows (selection, &model))) {
1284 		g_warning ("Could not get a selection to delete.");
1285 		return;
1286 	}
1287 	paths = g_list_reverse (paths);
1288 
1289 	for (tmp = paths; tmp; tmp = tmp->next) {
1290 		path = tmp->data;
1291 
1292 		gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->meeting_store), &iter, path);
1293 
1294 		gtk_tree_model_get (GTK_TREE_MODEL (priv->meeting_store), &iter, E_MEETING_STORE_ADDRESS_COL, &address, -1);
1295 		ia = e_meeting_store_find_attendee (priv->meeting_store, address, NULL);
1296 		g_free (address);
1297 		if (!ia) {
1298 			g_warning ("Cannot delete attendee\n");
1299 			continue;
1300 		} else if (e_meeting_attendee_get_edit_level (ia) != E_MEETING_ATTENDEE_EDIT_FULL) {
1301 			g_warning ("Not enough rights to delete attendee: %s\n", e_meeting_attendee_get_address (ia));
1302 			continue;
1303 		}
1304 
1305 		remove_attendee (page, ia);
1306 	}
1307 
1308 	/* Select closest item after removal */
1309 	valid_iter = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->meeting_store), &iter, path);
1310 	if (!valid_iter) {
1311 		gtk_tree_path_prev (path);
1312 		valid_iter = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->meeting_store), &iter, path);
1313 	}
1314 
1315 	if (valid_iter) {
1316 		gtk_tree_selection_unselect_all (selection);
1317 		gtk_tree_selection_select_iter (selection, &iter);
1318 	}
1319 
1320 	g_list_foreach (paths, (GFunc) gtk_tree_path_free, NULL);
1321 	g_list_free (paths);
1322 }
1323 
1324 static void
1325 invite_cb (GtkWidget *widget,
1326            TaskPage *page)
1327 {
1328 	e_meeting_list_view_invite_others_dialog (page->priv->list_view);
1329 }
1330 
1331 static void
1332 attendee_added_cb (EMeetingListView *emlv,
1333                    EMeetingAttendee *ia,
1334                    TaskPage *page)
1335 {
1336 	TaskPagePrivate *priv = page->priv;
1337 	CompEditor *editor;
1338 	CompEditorFlags flags;
1339 	ECalClient *client;
1340 
1341 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
1342 	client = comp_editor_get_client (editor);
1343 	flags = comp_editor_get_flags (editor);
1344 
1345 	if (!(flags & COMP_EDITOR_DELEGATE)) {
1346 		comp_editor_manage_new_attendees (priv->comp, ia, TRUE);
1347 		return;
1348 	}
1349 
1350 	/* do not remove here, it did EMeetingListView already */
1351 	e_meeting_attendee_set_delfrom (ia, g_strdup_printf ("MAILTO:%s", priv->user_add ? priv->user_add : ""));
1352 
1353 	if (!e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY)) {
1354 		EMeetingAttendee *delegator;
1355 
1356 		gtk_widget_set_sensitive (priv->invite, FALSE);
1357 		gtk_widget_set_sensitive (priv->add, FALSE);
1358 		gtk_widget_set_sensitive (priv->edit, FALSE);
1359 
1360 		delegator = e_meeting_store_find_attendee (priv->meeting_store, priv->user_add, NULL);
1361 		g_return_if_fail (delegator != NULL);
1362 
1363 		e_meeting_attendee_set_delto (delegator, g_strdup (e_meeting_attendee_get_address (ia)));
1364 	}
1365 }
1366 
1367 static gboolean
1368 list_view_event (EMeetingListView *list_view,
1369                  GdkEvent *event,
1370                  TaskPage *page)
1371 {
1372 	TaskPagePrivate *priv= page->priv;
1373 	CompEditor *editor;
1374 	CompEditorFlags flags;
1375 
1376 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
1377 	flags = comp_editor_get_flags (editor);
1378 
1379 	if (event->type == GDK_2BUTTON_PRESS && flags & COMP_EDITOR_USER_ORG) {
1380 		EMeetingAttendee *attendee;
1381 
1382 		attendee = e_meeting_store_add_attendee_with_defaults (priv->meeting_store);
1383 
1384 		if (flags & COMP_EDITOR_DELEGATE) {
1385 			e_meeting_attendee_set_delfrom (attendee, g_strdup_printf ("MAILTO:%s", page->priv->user_add));
1386 		}
1387 
1388 		e_meeting_list_view_edit (page->priv->list_view, attendee);
1389 		return TRUE;
1390 	}
1391 
1392 	return FALSE;
1393 }
1394 
1395 static gboolean
1396 list_key_press (EMeetingListView *list_view,
1397                 GdkEventKey *event,
1398                 TaskPage *page)
1399 {
1400 	if (event->keyval == GDK_KEY_Delete) {
1401 
1402 		remove_clicked_cb (NULL, page);
1403 
1404 		return TRUE;
1405 	} else if (event->keyval == GDK_KEY_Insert) {
1406 		add_clicked_cb (NULL, page);
1407 
1408 		return TRUE;
1409 	}
1410 
1411 	return FALSE;
1412 }
1413 
1414 void
1415 task_page_set_show_timezone (TaskPage *page,
1416                              gboolean state)
1417 {
1418 	if (state) {
1419 		gtk_widget_show_all (page->priv->timezone);
1420 		gtk_widget_show (page->priv->timezone_label);
1421 	} else {
1422 		gtk_widget_hide (page->priv->timezone);
1423 		gtk_widget_hide (page->priv->timezone_label);
1424 	}
1425 
1426 }
1427 
1428 void
1429 task_page_set_show_categories (TaskPage *page,
1430                                gboolean state)
1431 {
1432 	if (state) {
1433 		gtk_widget_show (page->priv->categories_btn);
1434 		gtk_widget_show (page->priv->categories);
1435 	} else {
1436 		gtk_widget_hide (page->priv->categories_btn);
1437 		gtk_widget_hide (page->priv->categories);
1438 	}
1439 }
1440 
1441 /*If the msg has some value set, the icon should always be set */
1442 void
1443 task_page_set_info_string (TaskPage *tpage,
1444                            const gchar *icon,
1445                            const gchar *msg)
1446 {
1447 	TaskPagePrivate *priv;
1448 
1449 	priv = tpage->priv;
1450 
1451 	gtk_image_set_from_stock (GTK_IMAGE (priv->info_icon), icon, GTK_ICON_SIZE_BUTTON);
1452 	gtk_label_set_markup (GTK_LABEL (priv->info_string), msg);
1453 
1454 	if (msg && icon)
1455 		gtk_widget_show (priv->info_hbox);
1456 	else
1457 		gtk_widget_hide (priv->info_hbox);
1458 }
1459 
1460 /* Gets the widgets from the XML file and returns if they are all available. */
1461 static gboolean
1462 get_widgets (TaskPage *tpage)
1463 {
1464 	EShell *shell;
1465 	ESourceRegistry *registry;
1466 	CompEditor *editor;
1467 	CompEditorPage *page = COMP_EDITOR_PAGE (tpage);
1468 	GtkEntryCompletion *completion;
1469 	TaskPagePrivate *priv;
1470 	GSList *accel_groups;
1471 	GtkWidget *toplevel;
1472 	GtkWidget *parent;
1473 	GtkWidget *sw;
1474 	GtkTreeSelection *selection;
1475 
1476 	priv = tpage->priv;
1477 
1478 	editor = comp_editor_page_get_editor (page);
1479 	shell = comp_editor_get_shell (editor);
1480 	registry = e_shell_get_registry (shell);
1481 
1482 	priv->main = e_builder_get_widget (priv->builder, "task-page");
1483 	if (!priv->main)
1484 		return FALSE;
1485 
1486 	/* Get the GtkAccelGroup from the toplevel window, so we can install
1487 	 * it when the notebook page is mapped. */
1488 	toplevel = gtk_widget_get_toplevel (priv->main);
1489 	accel_groups = gtk_accel_groups_from_object (G_OBJECT (toplevel));
1490 	if (accel_groups)
1491 		page->accel_group = g_object_ref (accel_groups->data);
1492 
1493 	g_object_ref (priv->main);
1494 	parent = gtk_widget_get_parent (priv->main);
1495 	gtk_container_remove (GTK_CONTAINER (parent), priv->main);
1496 
1497 	priv->info_hbox = e_builder_get_widget (priv->builder, "generic-info");
1498 	priv->info_icon = e_builder_get_widget (priv->builder, "generic-info-image");
1499 	priv->info_string = e_builder_get_widget (priv->builder, "generic-info-msgs");
1500 
1501 	priv->summary = e_builder_get_widget (priv->builder, "summary");
1502 	priv->summary_label = e_builder_get_widget (priv->builder, "summary-label");
1503 
1504 	/* Glade's visibility flag doesn't seem to work for custom widgets */
1505 	priv->due_date = e_builder_get_widget (priv->builder, "due-date");
1506 	gtk_widget_show (priv->due_date);
1507 	priv->start_date = e_builder_get_widget (priv->builder, "start-date");
1508 	gtk_widget_show (priv->start_date);
1509 
1510 	priv->timezone = e_builder_get_widget (priv->builder, "timezone");
1511 	priv->timezone_label = e_builder_get_widget (priv->builder, "timezone-label");
1512 	priv->attendees_label = e_builder_get_widget (priv->builder, "attendees-label");
1513 	priv->description = e_builder_get_widget (priv->builder, "description");
1514 	priv->categories_btn = e_builder_get_widget (priv->builder, "categories-button");
1515 	priv->categories = e_builder_get_widget (priv->builder, "categories");
1516 
1517 	priv->organizer = e_builder_get_widget (priv->builder, "organizer");
1518 	gtk_list_store_clear (GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (priv->organizer))));
1519 	gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (priv->organizer), 0);
1520 
1521 	priv->invite = e_builder_get_widget (priv->builder, "invite");
1522 	priv->add = e_builder_get_widget (priv->builder, "add-attendee");
1523 	priv->edit = e_builder_get_widget (priv->builder, "edit-attendee");
1524 	priv->remove = e_builder_get_widget (priv->builder, "remove-attendee");
1525 	priv->list_box = e_builder_get_widget (priv->builder, "list-box");
1526 	priv->calendar_label = e_builder_get_widget (priv->builder, "group-label");
1527 	priv->attendee_box = e_builder_get_widget (priv->builder, "attendee-box");
1528 	priv->org_cal_label = e_builder_get_widget (priv->builder, "org-task-label");
1529 
1530 	priv->list_view = e_meeting_list_view_new (priv->meeting_store);
1531 
1532 	selection = gtk_tree_view_get_selection ((GtkTreeView *) priv->list_view);
1533 	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
1534 	gtk_widget_show (GTK_WIDGET (priv->list_view));
1535 
1536 	sw = gtk_scrolled_window_new (NULL, NULL);
1537 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1538 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
1539 	gtk_widget_show (sw);
1540 	gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (priv->list_view));
1541 	gtk_box_pack_start (GTK_BOX (priv->list_box), sw, TRUE, TRUE, 0);
1542 
1543 	priv->source_combo_box = e_builder_get_widget (priv->builder, "source");
1544 	e_source_combo_box_set_registry (
1545 		E_SOURCE_COMBO_BOX (priv->source_combo_box), registry);
1546 
1547 	gtk_label_set_mnemonic_widget (GTK_LABEL (priv->calendar_label), priv->source_combo_box);
1548 
1549 	completion = e_category_completion_new ();
1550 	gtk_entry_set_completion (GTK_ENTRY (priv->categories), completion);
1551 	g_object_unref (completion);
1552 
1553 	if (priv->summary) {
1554 		EShell *shell;
1555 		EShellSettings *shell_settings;
1556 		CompEditor *editor;
1557 
1558 		editor = comp_editor_page_get_editor (page);
1559 		shell = comp_editor_get_shell (editor);
1560 		shell_settings = e_shell_get_shell_settings (shell);
1561 
1562 		g_object_bind_property (
1563 			shell_settings, "composer-inline-spelling",
1564 			priv->summary, "checking-enabled",
1565 			G_BINDING_SYNC_CREATE);
1566 	}
1567 
1568 	return (priv->summary
1569 		&& priv->summary_label
1570 		&& priv->due_date
1571 		&& priv->start_date
1572 		&& priv->timezone
1573 		&& priv->description
1574 		&& priv->categories_btn
1575 		&& priv->categories
1576 		&& priv->organizer
1577 		);
1578 }
1579 
1580 static void
1581 summary_changed_cb (GtkEntry *entry,
1582                     CompEditorPage *page)
1583 {
1584 	CompEditor *editor;
1585 	const gchar *text;
1586 
1587 	if (comp_editor_page_get_updating (page))
1588 		return;
1589 
1590 	editor = comp_editor_page_get_editor (page);
1591 	text = gtk_entry_get_text (entry);
1592 	comp_editor_set_summary (editor, text);
1593 }
1594 
1595 /* Callback used when the start or due date widgets change.  We notify the
1596  * other pages in the task editor, so they can update any labels.
1597  */
1598 static void
1599 date_changed_cb (EDateEdit *dedit,
1600                  TaskPage *tpage)
1601 {
1602 	TaskPagePrivate *priv = tpage->priv;
1603 	CompEditorPageDates dates;
1604 	gboolean date_set;
1605 	ECalComponentDateTime start_dt, due_dt;
1606 	struct icaltimetype start_tt = icaltime_null_time ();
1607 	struct icaltimetype due_tt = icaltime_null_time ();
1608 
1609 	if (comp_editor_page_get_updating (COMP_EDITOR_PAGE (tpage)))
1610 		return;
1611 
1612 	date_set = e_date_edit_get_date (
1613 		E_DATE_EDIT (priv->start_date),
1614 		&start_tt.year,
1615 		&start_tt.month,
1616 		&start_tt.day);
1617 	if (date_set) {
1618 		start_tt.is_date = TRUE;
1619 		start_dt.tzid = NULL;
1620 	} else {
1621 		start_tt = icaltime_null_time ();
1622 		start_dt.tzid = NULL;
1623 	}
1624 
1625 	date_set = e_date_edit_get_date (
1626 		E_DATE_EDIT (priv->due_date),
1627 		&due_tt.year,
1628 		&due_tt.month,
1629 		&due_tt.day);
1630 	if (date_set) {
1631 		due_tt.is_date = TRUE;
1632 		due_dt.tzid = NULL;
1633 	} else {
1634 		due_tt = icaltime_null_time ();
1635 		due_dt.tzid = NULL;
1636 	}
1637 
1638 	start_dt.value = &start_tt;
1639 	dates.start = &start_dt;
1640 	dates.end = NULL;
1641 	due_dt.value = &due_tt;
1642 	dates.due = &due_dt;
1643 	dates.complete = NULL;
1644 
1645 	/* Notify upstream */
1646 	comp_editor_page_notify_dates_changed (COMP_EDITOR_PAGE (tpage),
1647 					       &dates);
1648 
1649 	check_starts_in_the_past (tpage);
1650 }
1651 
1652 static void
1653 timezone_changed_cb (EDateEdit *dedit,
1654                      TaskPage *tpage)
1655 {
1656 	date_changed_cb ((EDateEdit *) tpage->priv->start_date, tpage);
1657 	date_changed_cb ((EDateEdit *) tpage->priv->due_date, tpage);
1658 }
1659 
1660 /* Callback used when the categories button is clicked; we must bring up the
1661  * category list dialog.
1662  */
1663 static void
1664 categories_clicked_cb (GtkWidget *button,
1665                        TaskPage *tpage)
1666 {
1667 	GtkEntry *entry;
1668 
1669 	entry = GTK_ENTRY (tpage->priv->categories);
1670 	e_categories_config_open_dialog_for_entry (entry);
1671 }
1672 
1673 static gboolean
1674 check_start_before_end (struct icaltimetype *start_tt,
1675                         icaltimezone *start_zone,
1676                         struct icaltimetype *end_tt,
1677                         icaltimezone *end_zone,
1678                         gboolean adjust_end_time,
1679                         gboolean adjust_by_hour)
1680 {
1681 	struct icaltimetype end_tt_copy;
1682 	gint cmp;
1683 
1684 	/* Convert the end time to the same timezone as the start time. */
1685 	end_tt_copy = *end_tt;
1686 	icaltimezone_convert_time (&end_tt_copy, end_zone, start_zone);
1687 
1688 	/* Now check if the start time is after the end time. If it is,
1689 	 * we need to modify one of the times. */
1690 	cmp = icaltime_compare (*start_tt, end_tt_copy);
1691 	if (cmp > 0) {
1692 		if (adjust_end_time) {
1693 			/* Modify the end time, to be the start + 1 hour/day. */
1694 			*end_tt = *start_tt;
1695 			icaltime_adjust (end_tt, 0, adjust_by_hour ? 1 : 24, 0, 0);
1696 			icaltimezone_convert_time (
1697 				end_tt, start_zone,
1698 				end_zone);
1699 		} else {
1700 			/* Modify the start time, to be the end - 1 hour/day. */
1701 			*start_tt = *end_tt;
1702 			icaltime_adjust (start_tt, 0, adjust_by_hour ? -1 : -24, 0, 0);
1703 			icaltimezone_convert_time (
1704 				start_tt, end_zone,
1705 				start_zone);
1706 		}
1707 		return TRUE;
1708 	}
1709 
1710 	return FALSE;
1711 }
1712 
1713 /*
1714  * This is called whenever the start or due dates.
1715  * It makes sure that the start date < end date. It also emits the notification
1716  * signals so the other event editor pages update their labels etc.
1717  *
1718  * If adjust_end_time is TRUE, if the start time < end time it will adjust
1719  * the end time. If FALSE it will adjust the start time. If the user sets the
1720  * start or end time, the other time is adjusted to make it valid.
1721  *
1722  * Time part of the value is changed only when both edits have time set,
1723  * otherwise times will differ one hour.
1724  */
1725 static void
1726 times_updated (TaskPage *tpage,
1727                gboolean adjust_end_time)
1728 {
1729 	TaskPagePrivate *priv;
1730 	struct icaltimetype start_tt = icaltime_null_time ();
1731 	struct icaltimetype end_tt = icaltime_null_time ();
1732 	gboolean date_set;
1733 	gboolean set_start_date = FALSE, set_end_date = FALSE;
1734 	icaltimezone *zone;
1735 
1736 	priv = tpage->priv;
1737 
1738 	if (comp_editor_page_get_updating (COMP_EDITOR_PAGE (tpage)))
1739 		return;
1740 
1741 	date_set = e_date_edit_get_date (
1742 		E_DATE_EDIT (priv->start_date),
1743 		&start_tt.year,
1744 		&start_tt.month,
1745 		&start_tt.day);
1746 	if (!date_set)
1747 		return;
1748 
1749 	date_set = e_date_edit_get_date (
1750 		E_DATE_EDIT (priv->due_date),
1751 		&end_tt.year,
1752 		&end_tt.month,
1753 		&end_tt.day);
1754 	if (!date_set)
1755 		return;
1756 
1757 	zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY (priv->timezone));
1758 
1759 	if (check_start_before_end (&start_tt, zone,
1760 				    &end_tt, zone,
1761 				    adjust_end_time,
1762 				    FALSE)) {
1763 		if (adjust_end_time)
1764 			set_end_date = TRUE;
1765 		else
1766 			set_start_date = TRUE;
1767 	}
1768 
1769 	if (set_start_date) {
1770 		g_signal_handlers_block_matched (priv->start_date, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, tpage);
1771 		e_date_edit_set_date (E_DATE_EDIT (priv->start_date), start_tt.year, start_tt.month, start_tt.day);
1772 		g_signal_handlers_unblock_matched (priv->start_date, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, tpage);
1773 	}
1774 
1775 	if (set_end_date) {
1776 		g_signal_handlers_block_matched (priv->due_date, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, tpage);
1777 		e_date_edit_set_date (E_DATE_EDIT (priv->due_date), end_tt.year, end_tt.month, end_tt.day);
1778 		g_signal_handlers_unblock_matched (priv->due_date, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, tpage);
1779 	}
1780 
1781 	/* Notify upstream */
1782 	date_changed_cb ((EDateEdit *) priv->start_date, tpage);
1783 	date_changed_cb ((EDateEdit *) priv->due_date, tpage);
1784 }
1785 
1786 static void
1787 start_date_changed_cb (TaskPage *tpage)
1788 {
1789 	times_updated (tpage, TRUE);
1790 }
1791 
1792 static void
1793 due_date_changed_cb (TaskPage *tpage)
1794 {
1795 	times_updated (tpage, FALSE);
1796 }
1797 
1798 static void
1799 tpage_client_opened_cb (GObject *source_object,
1800                         GAsyncResult *result,
1801                         gpointer user_data)
1802 {
1803 	ESource *source = E_SOURCE (source_object);
1804 	EClient *client = NULL;
1805 	TaskPage *tpage = user_data;
1806 	TaskPagePrivate *priv;
1807 	CompEditor *editor;
1808 	GError *error = NULL;
1809 
1810 	if (!e_client_utils_open_new_finish (source, result, &client, &error)) {
1811 		if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
1812 		    g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
1813 			g_clear_error (&error);
1814 			return;
1815 		}
1816 	}
1817 
1818 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage));
1819 	priv = tpage->priv;
1820 	if (error) {
1821 		GtkWidget *dialog;
1822 		ECalClient *old_client;
1823 
1824 		old_client = comp_editor_get_client (editor);
1825 
1826 		e_source_combo_box_set_active (
1827 			E_SOURCE_COMBO_BOX (priv->source_combo_box),
1828 			e_client_get_source (E_CLIENT (old_client)));
1829 
1830 		dialog = gtk_message_dialog_new (
1831 			NULL, GTK_DIALOG_MODAL,
1832 			GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
1833 			_("Unable to open tasks in '%s': %s"),
1834 			e_source_get_display_name (source),
1835 			error->message);
1836 		gtk_dialog_run (GTK_DIALOG (dialog));
1837 		gtk_widget_destroy (dialog);
1838 
1839 		g_clear_error (&error);
1840 	} else {
1841 		icaltimezone *zone;
1842 		ECalClient *cal_client = E_CAL_CLIENT (client);
1843 
1844 		g_return_if_fail (cal_client != NULL);
1845 
1846 		zone = comp_editor_get_timezone (editor);
1847 		e_cal_client_set_default_timezone (cal_client, zone);
1848 
1849 		comp_editor_set_client (editor, cal_client);
1850 		comp_editor_page_changed (COMP_EDITOR_PAGE (tpage));
1851 		if (e_client_check_capability (client, CAL_STATIC_CAPABILITY_REQ_SEND_OPTIONS) && priv->is_assignment)
1852 			task_page_show_options (tpage);
1853 		else
1854 			task_page_hide_options (tpage);
1855 
1856 		if (client) {
1857 			gchar *backend_addr = NULL;
1858 
1859 			e_client_get_backend_property_sync (client, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &backend_addr, NULL, NULL);
1860 
1861 			if (priv->is_assignment)
1862 				task_page_select_organizer (tpage, backend_addr);
1863 
1864 			set_subscriber_info_string (tpage, backend_addr);
1865 			g_free (backend_addr);
1866 		}
1867 
1868 		sensitize_widgets (tpage);
1869 	}
1870 }
1871 
1872 static void
1873 source_changed_cb (ESourceComboBox *source_combo_box,
1874                    TaskPage *tpage)
1875 {
1876 	TaskPagePrivate *priv = tpage->priv;
1877 	ESource *source;
1878 
1879 	if (comp_editor_page_get_updating (COMP_EDITOR_PAGE (tpage)))
1880 		return;
1881 
1882 	source = e_source_combo_box_ref_active (source_combo_box);
1883 	g_return_if_fail (source != NULL);
1884 
1885 	if (priv->open_cancellable) {
1886 		g_cancellable_cancel (priv->open_cancellable);
1887 		g_object_unref (priv->open_cancellable);
1888 	}
1889 	priv->open_cancellable = g_cancellable_new ();
1890 
1891 	e_client_utils_open_new (
1892 		source, E_CLIENT_SOURCE_TYPE_TASKS,
1893 		FALSE, priv->open_cancellable,
1894 		tpage_client_opened_cb, tpage);
1895 
1896 	g_object_unref (source);
1897 }
1898 
1899 static void
1900 set_subscriber_info_string (TaskPage *tpage,
1901                             const gchar *backend_address)
1902 {
1903 	if (!check_starts_in_the_past (tpage))
1904 		task_page_set_info_string (tpage, NULL, NULL);
1905 }
1906 
1907 void
1908 task_page_send_options_clicked_cb (TaskPage *tpage)
1909 {
1910 	TaskPagePrivate *priv = tpage->priv;
1911 	CompEditor *editor;
1912 	GtkWidget *toplevel;
1913 	ESource *source;
1914 	ECalClient *client;
1915 
1916 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage));
1917 	client = comp_editor_get_client (editor);
1918 
1919 	if (!priv->sod) {
1920 		priv->sod = e_send_options_dialog_new ();
1921 		priv->sod->data->initialized = TRUE;
1922 		source = e_source_combo_box_ref_active (
1923 			E_SOURCE_COMBO_BOX (priv->source_combo_box));
1924 		e_send_options_utils_set_default_data (
1925 			priv->sod, source, "task");
1926 		g_object_unref (source);
1927 	}
1928 
1929 	if (e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_NO_GEN_OPTIONS)) {
1930 		e_send_options_set_need_general_options (priv->sod, FALSE);
1931 	}
1932 
1933 	toplevel = gtk_widget_get_toplevel (priv->main);
1934 	e_send_options_dialog_run (priv->sod, toplevel, E_ITEM_TASK);
1935 }
1936 
1937 /* Hooks the widget signals */
1938 static gboolean
1939 init_widgets (TaskPage *tpage)
1940 {
1941 	CompEditor *editor;
1942 	TaskPagePrivate *priv;
1943 	GtkAction *action;
1944 	GtkTextBuffer *text_buffer;
1945 	icaltimezone *zone;
1946 	gboolean active;
1947 
1948 	priv = tpage->priv;
1949 
1950 	editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage));
1951 
1952 	/* Make sure the EDateEdit widgets use our timezones to get the
1953 	 * current time. */
1954 	e_date_edit_set_get_time_callback (
1955 		E_DATE_EDIT (priv->start_date),
1956 		(EDateEditGetTimeCallback) comp_editor_get_current_time,
1957 		g_object_ref (editor),
1958 		(GDestroyNotify) g_object_unref);
1959 	e_date_edit_set_get_time_callback (
1960 		E_DATE_EDIT (priv->due_date),
1961 		(EDateEditGetTimeCallback) comp_editor_get_current_time,
1962 		g_object_ref (editor),
1963 		(GDestroyNotify) g_object_unref);
1964 
1965 	/* Generic informative messages */
1966 	gtk_widget_hide (priv->info_hbox);
1967 
1968 	/* Summary */
1969 	g_signal_connect (
1970 		priv->summary, "changed",
1971 		G_CALLBACK (summary_changed_cb), tpage);
1972 
1973 	/* Description */
1974 	text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description));
1975 
1976 	gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (priv->description), GTK_WRAP_WORD);
1977 
1978 	e_buffer_tagger_connect (GTK_TEXT_VIEW (priv->description));
1979 
1980 	/* Dates */
1981 	g_signal_connect (
1982 		priv->start_date, "changed",
1983 		G_CALLBACK (date_changed_cb), tpage);
1984 	g_signal_connect (
1985 		priv->due_date, "changed",
1986 		G_CALLBACK (date_changed_cb), tpage);
1987 
1988 	/* time zone changed */
1989 	g_signal_connect (
1990 		priv->timezone, "changed",
1991 		G_CALLBACK (timezone_changed_cb), tpage);
1992 
1993 	/* Categories button */
1994 	g_signal_connect (
1995 		priv->categories_btn, "clicked",
1996 		G_CALLBACK (categories_clicked_cb), tpage);
1997 
1998 	/* Source selector */
1999 	g_signal_connect (
2000 		priv->source_combo_box, "changed",
2001 		G_CALLBACK (source_changed_cb), tpage);
2002 
2003 	/* Connect the default signal handler to use to make sure the "changed"
2004 	 * field gets set whenever a field is changed. */
2005 
2006 	/* Belongs to priv->description */
2007 	g_signal_connect_swapped (
2008 		text_buffer, "changed",
2009 		G_CALLBACK (comp_editor_page_changed), tpage);
2010 	g_signal_connect_swapped (
2011 		priv->summary, "changed",
2012 		G_CALLBACK (comp_editor_page_changed), tpage);
2013 	g_signal_connect_swapped (
2014 		priv->start_date, "changed",
2015 		G_CALLBACK (start_date_changed_cb), tpage);
2016 	g_signal_connect_swapped (
2017 		priv->start_date, "changed",
2018 		G_CALLBACK (comp_editor_page_changed), tpage);
2019 	g_signal_connect_swapped (
2020 		priv->due_date, "changed",
2021 		G_CALLBACK (due_date_changed_cb), tpage);
2022 	g_signal_connect_swapped (
2023 		priv->due_date, "changed",
2024 		G_CALLBACK (comp_editor_page_changed), tpage);
2025 	g_signal_connect_swapped (
2026 		priv->timezone, "changed",
2027 		G_CALLBACK (comp_editor_page_changed), tpage);
2028 	g_signal_connect_swapped (
2029 		priv->categories, "changed",
2030 		G_CALLBACK (comp_editor_page_changed), tpage);
2031 
2032 	g_signal_connect (
2033 		priv->list_view, "event",
2034 		G_CALLBACK (list_view_event), tpage);
2035 	g_signal_connect (
2036 		priv->list_view, "key_press_event",
2037 		G_CALLBACK (list_key_press), tpage);
2038 
2039 	/* Add attendee button */
2040 	g_signal_connect (
2041 		priv->add, "clicked",
2042 		G_CALLBACK (add_clicked_cb), tpage);
2043 
2044 	/* Edit attendee button */
2045 	g_signal_connect (
2046 		priv->edit, "clicked",
2047 		G_CALLBACK (edit_clicked_cb), tpage);
2048 
2049 	/* Remove attendee button */
2050 	g_signal_connect (
2051 		priv->remove, "clicked",
2052 		G_CALLBACK (remove_clicked_cb), tpage);
2053 
2054 	/* Contacts button */
2055 	g_signal_connect (
2056 		priv->invite, "clicked",
2057 		G_CALLBACK (invite_cb), tpage);
2058 
2059 	/* Meeting List View */
2060 	g_signal_connect (
2061 		priv->list_view, "attendee_added",
2062 		G_CALLBACK (attendee_added_cb), tpage);
2063 
2064 	/* Set the default timezone, so the timezone entry may be hidden. */
2065 	zone = comp_editor_get_timezone (editor);
2066 	e_timezone_entry_set_default_timezone (E_TIMEZONE_ENTRY (priv->timezone), zone);
2067 
2068 	action = comp_editor_get_action (editor, "view-time-zone");
2069 	active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2070 	task_page_set_show_timezone (tpage, active);
2071 
2072 	e_meeting_list_view_column_set_visible (
2073 		priv->list_view, E_MEETING_STORE_ATTENDEE_COL, TRUE);
2074 
2075 	action = comp_editor_get_action (editor, "view-role");
2076 	active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2077 	e_meeting_list_view_column_set_visible (
2078 		priv->list_view, E_MEETING_STORE_ROLE_COL, active);
2079 
2080 	action = comp_editor_get_action (editor, "view-rsvp");
2081 	active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2082 	e_meeting_list_view_column_set_visible (
2083 		priv->list_view, E_MEETING_STORE_RSVP_COL, active);
2084 
2085 	action = comp_editor_get_action (editor, "view-status");
2086 	active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2087 	e_meeting_list_view_column_set_visible (
2088 		priv->list_view, E_MEETING_STORE_STATUS_COL, active);
2089 
2090 	action = comp_editor_get_action (editor, "view-type");
2091 	active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2092 	e_meeting_list_view_column_set_visible (
2093 		priv->list_view, E_MEETING_STORE_TYPE_COL, active);
2094 
2095 	action = comp_editor_get_action (editor, "view-categories");
2096 	active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2097 	task_page_set_show_categories (tpage, active);
2098 
2099 	return TRUE;
2100 }
2101 
2102 static void
2103 task_page_select_organizer (TaskPage *tpage,
2104                             const gchar *backend_address)
2105 {
2106 	TaskPagePrivate *priv = tpage->priv;
2107 	const gchar *default_address;
2108 	gint ii;
2109 
2110 	/* Treat an empty backend address as NULL. */
2111 	if (backend_address == NULL && *backend_address == '\0')
Dereference of null pointer (loaded from variable 'backend_address')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Dereference of null pointer (loaded from variable 'backend_address')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

2112 backend_address = NULL; 2113 2114 default_address = priv->fallback_address; 2115 2116 if (backend_address != NULL) { 2117 for (ii = 0; priv->address_strings[ii] != NULL; ii++) { 2118 if (g_strrstr (priv->address_strings[ii], backend_address) != NULL) { 2119 default_address = priv->address_strings[ii]; 2120 break; 2121 } 2122 } 2123 } 2124 2125 if (default_address != NULL) { 2126 if (!priv->comp || !e_cal_component_has_organizer (priv->comp)) { 2127 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->organizer))); 2128 2129 g_signal_handlers_block_by_func (entry, organizer_changed_cb, tpage); 2130 gtk_entry_set_text (entry, default_address); 2131 g_signal_handlers_unblock_by_func (entry, organizer_changed_cb, tpage); 2132 } 2133 } else 2134 g_warning ("No potential organizers!"); 2135 } 2136 2137 /** 2138 * task_page_construct: 2139 * @tpage: An task page. 2140 * 2141 * Constructs an task page by loading its Glade data. 2142 * 2143 * Return value: The same object as @tpage, or NULL if the widgets could not be 2144 * created. 2145 **/ 2146 TaskPage * 2147 task_page_construct (TaskPage *tpage, 2148 EMeetingStore *meeting_store, 2149 ECalClient *client) 2150 { 2151 EShell *shell; 2152 CompEditor *editor; 2153 ESourceRegistry *registry; 2154 TaskPagePrivate *priv; 2155 GtkComboBox *combo_box; 2156 GtkListStore *list_store; 2157 GtkTreeModel *model; 2158 GtkTreeIter iter; 2159 gint ii; 2160 2161 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage)); 2162 shell = comp_editor_get_shell (editor); 2163 2164 priv = tpage->priv; 2165 priv->meeting_store = g_object_ref (meeting_store); 2166 priv->client = client; 2167 2168 /* Make sure our custom widget classes are registered with 2169 * GType before we load the GtkBuilder definition file. */ 2170 E_TYPE_DATE_EDIT; 2171 E_TYPE_TIMEZONE_ENTRY; 2172 E_TYPE_SOURCE_COMBO_BOX; 2173 E_TYPE_SPELL_ENTRY; 2174 2175 priv->builder = gtk_builder_new (); 2176 e_load_ui_builder_definition (priv->builder, "task-page.ui"); 2177 2178 if (!get_widgets (tpage)) { 2179 g_message ( 2180 "task_page_construct(): " 2181 "Could not find all widgets in the XML file!"); 2182 return NULL; 2183 } 2184 2185 combo_box = GTK_COMBO_BOX (priv->organizer); 2186 model = gtk_combo_box_get_model (combo_box); 2187 list_store = GTK_LIST_STORE (model); 2188 2189 registry = e_shell_get_registry (shell); 2190 priv->address_strings = itip_get_user_identities (registry); 2191 priv->fallback_address = itip_get_fallback_identity (registry); 2192 2193 /* FIXME Could we just use a GtkComboBoxText? */ 2194 for (ii = 0; priv->address_strings[ii] != NULL; ii++) { 2195 gtk_list_store_append (list_store, &iter); 2196 gtk_list_store_set ( 2197 list_store, &iter, 2198 0, priv->address_strings[ii], -1); 2199 } 2200 2201 gtk_combo_box_set_active (combo_box, 0); 2202 2203 g_signal_connect ( 2204 gtk_bin_get_child (GTK_BIN (priv->organizer)), "changed", 2205 G_CALLBACK (organizer_changed_cb), tpage); 2206 2207 if (!init_widgets (tpage)) { 2208 g_message ( 2209 "task_page_construct(): " 2210 "Could not initialize the widgets!"); 2211 return NULL; 2212 } 2213 2214 return tpage; 2215 } 2216 2217 /** 2218 * task_page_new: 2219 * 2220 * Creates a new task page. 2221 * 2222 * Return value: A newly-created task page, or NULL if the page could 2223 * not be created. 2224 **/ 2225 TaskPage * 2226 task_page_new (EMeetingStore *model, 2227 CompEditor *editor) 2228 { 2229 TaskPage *tpage; 2230 ECalClient *client; 2231 2232 tpage = g_object_new (TYPE_TASK_PAGE, "editor", editor, NULL); 2233 client = comp_editor_get_client (editor); 2234 if (!task_page_construct (tpage, model, client)) { 2235 g_object_unref (tpage); 2236 g_return_val_if_reached (NULL); 2237 } 2238 2239 return tpage; 2240 } 2241 2242 ECalComponent * 2243 task_page_get_cancel_comp (TaskPage *page) 2244 { 2245 TaskPagePrivate *priv; 2246 2247 g_return_val_if_fail (page != NULL, NULL); 2248 g_return_val_if_fail (IS_TASK_PAGE (page), NULL); 2249 2250 priv = page->priv; 2251 2252 if (priv->deleted_attendees->len == 0) 2253 return NULL; 2254 2255 set_attendees (priv->comp, priv->deleted_attendees); 2256 2257 return e_cal_component_clone (priv->comp); 2258 }