evolution-3.6.4/calendar/gui/e-meeting-list-view.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-meeting-list-view.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-meeting-list-view.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   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  *		Mike Kestner  <mkestner@ximian.com>
  18  *	    JP Rosevear  <jpr@ximian.com>
  19  *
  20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  21  *
  22  */
  23 
  24 #ifdef HAVE_CONFIG_H
  25 #include <config.h>
  26 #endif
  27 
  28 #include <string.h>
  29 #include <gtk/gtk.h>
  30 #include <glib/gi18n.h>
  31 #include <libecal/libecal.h>
  32 #include <libebook/libebook.h>
  33 #include <libedataserverui/libedataserverui.h>
  34 
  35 #include "calendar-config.h"
  36 #include "e-meeting-list-view.h"
  37 #include "itip-utils.h"
  38 #include <shell/e-shell.h>
  39 #include "e-select-names-renderer.h"
  40 
  41 #define E_MEETING_LIST_VIEW_GET_PRIVATE(obj) \
  42 	(G_TYPE_INSTANCE_GET_PRIVATE \
  43 	((obj), E_TYPE_MEETING_LIST_VIEW, EMeetingListViewPrivate))
  44 
  45 struct _EMeetingListViewPrivate {
  46 	EMeetingStore *store;
  47 
  48 	ENameSelector *name_selector;
  49 
  50 	GHashTable *renderers;
  51 };
  52 
  53 #define BUF_SIZE 1024
  54 
  55 /* Signal IDs */
  56 enum {
  57 	ATTENDEE_ADDED,
  58 	LAST_SIGNAL
  59 };
  60 
  61 static guint e_meeting_list_view_signals[LAST_SIGNAL] = { 0 };
  62 
  63 static void name_selector_dialog_close_cb (ENameSelectorDialog *dialog, gint response, gpointer data);
  64 
  65 static const gchar *sections[] = {N_("Chair Persons"),
  66 				  N_("Required Participants"),
  67 				  N_("Optional Participants"),
  68 				  N_("Resources"),
  69 				  NULL};
  70 
  71 static icalparameter_role roles[] = {ICAL_ROLE_CHAIR,
  72 				     ICAL_ROLE_REQPARTICIPANT,
  73 				     ICAL_ROLE_OPTPARTICIPANT,
  74 				     ICAL_ROLE_NONPARTICIPANT,
  75 				     ICAL_ROLE_NONE};
  76 
  77 G_DEFINE_TYPE (EMeetingListView, e_meeting_list_view, GTK_TYPE_TREE_VIEW)
  78 
  79 static void
  80 e_meeting_list_view_finalize (GObject *object)
  81 {
  82 	EMeetingListViewPrivate *priv;
  83 
  84 	priv = E_MEETING_LIST_VIEW_GET_PRIVATE (object);
  85 
  86 	if (priv->name_selector) {
  87 		e_name_selector_cancel_loading (priv->name_selector);
  88 		g_object_unref (priv->name_selector);
  89 		priv->name_selector = NULL;
  90 	}
  91 
  92 	if (priv->renderers) {
  93 		g_hash_table_destroy (priv->renderers);
  94 		priv->renderers = NULL;
  95 	}
  96 
  97 	/* Chain up to parent's finalize() method. */
  98 	G_OBJECT_CLASS (e_meeting_list_view_parent_class)->finalize (object);
  99 }
 100 
 101 static void
 102 e_meeting_list_view_class_init (EMeetingListViewClass *class)
 103 {
 104 	GObjectClass *object_class;
 105 
 106 	g_type_class_add_private (class, sizeof (EMeetingListViewPrivate));
 107 
 108 	object_class = G_OBJECT_CLASS (class);
 109 	object_class->finalize = e_meeting_list_view_finalize;
 110 
 111 	e_meeting_list_view_signals[ATTENDEE_ADDED] = g_signal_new (
 112 		"attendee_added",
 113 		G_TYPE_FROM_CLASS (class),
 114 		G_SIGNAL_RUN_LAST,
 115 		G_STRUCT_OFFSET (EMeetingListViewClass, attendee_added),
 116 		NULL, NULL,
 117 		g_cclosure_marshal_VOID__POINTER,
 118 		G_TYPE_NONE, 1,
 119 		G_TYPE_POINTER);
 120 }
 121 
 122 static void
 123 add_section (ENameSelector *name_selector,
 124              const gchar *name)
 125 {
 126 	ENameSelectorModel *name_selector_model;
 127 
 128 	name_selector_model = e_name_selector_peek_model (name_selector);
 129 	e_name_selector_model_add_section (name_selector_model, name, gettext (name), NULL);
 130 }
 131 
 132 static void
 133 meeting_list_view_realize_cb (EMeetingListView *view)
 134 {
 135 	g_return_if_fail (view != NULL);
 136 	g_return_if_fail (view->priv != NULL);
 137 
 138 	g_signal_handlers_disconnect_by_func (view, meeting_list_view_realize_cb, NULL);
 139 
 140 	e_name_selector_load_books (view->priv->name_selector);
 141 }
 142 
 143 static void
 144 e_meeting_list_view_init (EMeetingListView *view)
 145 {
 146 	ENameSelectorDialog *name_selector_dialog;
 147 	ESourceRegistry *registry;
 148 	EShell *shell;
 149 	gint i;
 150 
 151 	view->priv = E_MEETING_LIST_VIEW_GET_PRIVATE (view);
 152 
 153 	view->priv->renderers = g_hash_table_new (g_direct_hash, g_int_equal);
 154 
 155 	/* FIXME Refactor this so we don't need e_shell_get_default(). */
 156 	shell = e_shell_get_default ();
 157 	registry = e_shell_get_registry (shell);
 158 
 159 	view->priv->name_selector = e_name_selector_new (registry);
 160 
 161 	for (i = 0; sections[i]; i++)
 162 		add_section (view->priv->name_selector, sections[i]);
 163 
 164 	name_selector_dialog =
 165 		e_name_selector_peek_dialog (view->priv->name_selector);
 166 	gtk_window_set_title (GTK_WINDOW (name_selector_dialog), _("Attendees"));
 167 	g_signal_connect (
 168 		name_selector_dialog, "response",
 169 		G_CALLBACK (name_selector_dialog_close_cb), view);
 170 
 171 	/* postpone name_selector loading, do that only when really needed */
 172 	g_signal_connect (
 173 		view, "realize",
 174 		G_CALLBACK (meeting_list_view_realize_cb), NULL);
 175 }
 176 
 177 static GList *
 178 get_type_strings (void)
 179 {
 180 	GList *strings = NULL;
 181 
 182 	strings = g_list_append (strings, (gchar *) _("Individual"));
 183 	strings = g_list_append (strings, (gchar *) _("Group"));
 184 	strings = g_list_append (strings, (gchar *) _("Resource"));
 185 	strings = g_list_append (strings, (gchar *) _("Room"));
 186 	strings = g_list_append (strings, (gchar *) _("Unknown"));
 187 
 188 	return strings;
 189 }
 190 
 191 static GList *
 192 get_role_strings (void)
 193 {
 194 	GList *strings = NULL;
 195 
 196 	strings = g_list_append (strings, (gchar *) _("Chair"));
 197 	strings = g_list_append (strings, (gchar *) _("Required Participant"));
 198 	strings = g_list_append (strings, (gchar *) _("Optional Participant"));
 199 	strings = g_list_append (strings, (gchar *) _("Non-Participant"));
 200 	strings = g_list_append (strings, (gchar *) _("Unknown"));
 201 
 202 	return strings;
 203 }
 204 
 205 static GList *
 206 get_rsvp_strings (void)
 207 {
 208 	GList *strings = NULL;
 209 
 210 	strings = g_list_append (strings, (gchar *) _("Yes"));
 211 	strings = g_list_append (strings, (gchar *) _("No"));
 212 
 213 	return strings;
 214 }
 215 
 216 static GList *
 217 get_status_strings (void)
 218 {
 219 	GList *strings = NULL;
 220 
 221 	strings = g_list_append (strings, (gchar *) _("Needs Action"));
 222 	strings = g_list_append (strings, (gchar *) _("Accepted"));
 223 	strings = g_list_append (strings, (gchar *) _("Declined"));
 224 	strings = g_list_append (strings, (gchar *) _("Tentative"));
 225 	strings = g_list_append (strings, (gchar *) _("Delegated"));
 226 
 227 	return strings;
 228 }
 229 
 230 static void
 231 value_edited (GtkTreeView *view,
 232               gint col,
 233               const gchar *path,
 234               const gchar *text)
 235 {
 236 	EMeetingStore *model = E_MEETING_STORE (gtk_tree_view_get_model (view));
 237 	GtkTreePath *treepath = gtk_tree_path_new_from_string (path);
 238 	gint row = gtk_tree_path_get_indices (treepath)[0];
 239 
 240 	e_meeting_store_set_value (model, row, col, text);
 241 	gtk_tree_path_free (treepath);
 242 }
 243 
 244 static guint
 245 get_index_from_role (icalparameter_role role)
 246 {
 247 	switch (role)	{
 248 		case ICAL_ROLE_CHAIR:
 249 			return 0;
 250 		case ICAL_ROLE_REQPARTICIPANT:
 251 			return 1;
 252 		case ICAL_ROLE_OPTPARTICIPANT:
 253 			return 2;
 254 		case ICAL_ROLE_NONPARTICIPANT:
 255 			return 3;
 256 		default:
 257 			return 1;
 258 	}
 259 }
 260 
 261 void
 262 e_meeting_list_view_add_attendee_to_name_selector (EMeetingListView *view,
 263                                                    EMeetingAttendee *ma)
 264 {
 265 	EDestinationStore *destination_store;
 266 	ENameSelectorModel *name_selector_model;
 267 	EDestination *des;
 268 	EMeetingListViewPrivate *priv;
 269 	guint i = 1;
 270 
 271 	priv = view->priv;
 272 
 273 	name_selector_model = e_name_selector_peek_model (priv->name_selector);
 274 	i = get_index_from_role (e_meeting_attendee_get_role (ma));
 275 	e_name_selector_model_peek_section (
 276 		name_selector_model, sections[i],
 277 		NULL, &destination_store);
 278 	des = e_destination_new ();
 279 	e_destination_set_email (des, itip_strip_mailto (e_meeting_attendee_get_address (ma)));
 280 	e_destination_set_name (des, e_meeting_attendee_get_cn (ma));
 281 	e_destination_store_append_destination (destination_store, des);
 282 	g_object_unref (des);
 283 }
 284 
 285 void
 286 e_meeting_list_view_remove_attendee_from_name_selector (EMeetingListView *view,
 287                                                         EMeetingAttendee *ma)
 288 {
 289 	GList             *destinations, *l;
 290 	EDestinationStore *destination_store;
 291 	ENameSelectorModel *name_selector_model;
 292 	const gchar *madd = NULL;
 293 	EMeetingListViewPrivate *priv;
 294 	guint i = 1;
 295 
 296 	priv = view->priv;
 297 
 298 	name_selector_model = e_name_selector_peek_model (priv->name_selector);
 299 	i = get_index_from_role (e_meeting_attendee_get_role (ma));
 300 	e_name_selector_model_peek_section (
 301 		name_selector_model, sections[i],
 302 		NULL, &destination_store);
 303 	destinations = e_destination_store_list_destinations (destination_store);
 304 	madd = itip_strip_mailto (e_meeting_attendee_get_address (ma));
 305 
 306 	for (l = destinations; l; l = g_list_next (l)) {
 307 		const gchar *attendee = NULL;
 308 		EDestination *des = l->data;
 309 
 310 		if (e_destination_is_evolution_list (des)) {
 311 			GList *l, *dl;
 312 
 313 			dl = (GList *) e_destination_list_get_dests (des);
 314 
 315 			for (l = dl; l; l = l->next) {
 316 				attendee = e_destination_get_email (l->data);
 317 				if (madd && attendee && g_str_equal (madd, attendee)) {
 318 					g_object_unref (l->data);
 319 					l = g_list_remove (l, l->data);
 320 					break;
 321 				}
 322 			}
 323 		} else {
 324 			attendee = e_destination_get_email (des);
 325 			if (madd && attendee && g_str_equal (madd, attendee)) {
 326 				e_destination_store_remove_destination (destination_store, des);
 327 			}
 328 		}
 329 	}
 330 
 331 	g_list_free (destinations);
 332 }
 333 
 334 void
 335 e_meeting_list_view_remove_all_attendees_from_name_selector (EMeetingListView *view)
 336 {
 337 	ENameSelectorModel *name_selector_model;
 338 	EMeetingListViewPrivate *priv;
 339 	guint i;
 340 
 341 	priv = view->priv;
 342 
 343 	name_selector_model = e_name_selector_peek_model (priv->name_selector);
 344 
 345 	for (i = 0; sections[i] != NULL; i++) {
 346 		EDestinationStore *destination_store = NULL;
 347 		GList             *destinations = NULL, *l = NULL;
 348 
 349 		e_name_selector_model_peek_section (
 350 			name_selector_model, sections[i],
 351 			NULL, &destination_store);
 352 		if (!destination_store) {
 353 			g_warning ("destination store is NULL\n");
 354 			continue;
 355 		}
 356 
 357 		destinations = e_destination_store_list_destinations (destination_store);
 358 		for (l = destinations; l; l = g_list_next (l)) {
 359 			EDestination *des = l->data;
 360 
 361 			if (e_destination_is_evolution_list (des)) {
 362 				GList *m, *dl;
 363 
 364 				dl = (GList *) e_destination_list_get_dests (des);
 365 
 366 				for (m = dl; m; m = m->next) {
 367 					g_object_unref (m->data);
 368 					m = g_list_remove (m, l->data);
 369 				}
 370 			} else {
 371 				e_destination_store_remove_destination (destination_store, des);
 372 			}
 373 		}
 374 		g_list_free (destinations);
 375 	}
 376 }
 377 
 378 static void
 379 attendee_edited_cb (GtkCellRenderer *renderer,
 380                     const gchar *path,
 381                     GList *addresses,
 382                     GList *names,
 383                     GtkTreeView *view)
 384 {
 385 	EMeetingStore *model = E_MEETING_STORE (gtk_tree_view_get_model (view));
 386 	GtkTreePath *treepath = gtk_tree_path_new_from_string (path);
 387 	gint row = gtk_tree_path_get_indices (treepath)[0];
 388 	EMeetingAttendee *existing_attendee;
 389 
 390 	existing_attendee = e_meeting_store_find_attendee_at_row (model, row);
 391 
 392 	if (g_list_length (addresses) > 1) {
 393 		EMeetingAttendee *attendee;
 394 		GList *l, *m;
 395 		gboolean can_remove = TRUE;
 396 
 397 		for (l = addresses, m = names; l && m; l = l->next, m = m->next) {
 398 			gchar *name = m->data, *email = l->data;
 399 
 400 			if (!((name && *name) || (email && *email)))
 401 					continue;
 402 
 403 			attendee = e_meeting_store_find_attendee (model, email, NULL);
 404 			if (attendee != NULL) {
 405 				if (attendee == existing_attendee)
 406 					can_remove = FALSE;
 407 				continue;
 408 			}
 409 
 410 			attendee = e_meeting_store_add_attendee_with_defaults (model);
 411 			e_meeting_attendee_set_address (attendee, g_strdup_printf ("MAILTO:%s", (gchar *) l->data));
 412 			e_meeting_attendee_set_cn (attendee, g_strdup (m->data));
 413 			if (existing_attendee) {
 414 				/* FIXME Should we copy anything else? */
 415 				e_meeting_attendee_set_cutype (attendee, e_meeting_attendee_get_cutype (existing_attendee));
 416 				e_meeting_attendee_set_role (attendee, e_meeting_attendee_get_role (existing_attendee));
 417 				e_meeting_attendee_set_rsvp (attendee, e_meeting_attendee_get_rsvp (existing_attendee));
 418 				e_meeting_attendee_set_status (attendee, ICAL_PARTSTAT_NEEDSACTION);
 419 				e_meeting_attendee_set_delfrom (attendee, (gchar *) e_meeting_attendee_get_delfrom (existing_attendee));
 420 			}
 421 			e_meeting_list_view_add_attendee_to_name_selector (E_MEETING_LIST_VIEW (view), attendee);
 422 			g_signal_emit_by_name (view, "attendee_added", (gpointer) attendee);
 423 		}
 424 
 425 		if (existing_attendee && can_remove) {
 426 			e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), existing_attendee);
 427 			e_meeting_store_remove_attendee (model, existing_attendee);
 428 		}
 429 	} else if (g_list_length (addresses) == 1) {
 430 		gchar *name = names->data, *email = addresses->data;
 431 		gint existing_row;
 432 
 433 		if (!((name && *name) || (email && *email)) || ((e_meeting_store_find_attendee (model, email, &existing_row) != NULL) && existing_row != row)) {
 434 			if (existing_attendee) {
 435 				e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), existing_attendee);
 436 				e_meeting_store_remove_attendee (model, existing_attendee);
 437 			}
 438 		} else {
 439 			gboolean address_changed = FALSE;
 440 			EMeetingAttendee *attendee;
 441 
 442 			if (existing_attendee) {
 443 				const gchar *addr = e_meeting_attendee_get_address (existing_attendee);
 444 
 445 				if (addr && g_ascii_strncasecmp (addr, "MAILTO:", 7) == 0)
 446 					addr += 7;
 447 
 448 				address_changed = addr && g_ascii_strcasecmp (addr, email) != 0;
 449 
 450 				e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), existing_attendee);
 451 				attendee = existing_attendee;
 452 			} else {
 453 				attendee = e_meeting_store_add_attendee_with_defaults (model);
 454 			}
 455 
 456 			value_edited (view, E_MEETING_STORE_ADDRESS_COL, path, email);
 457 			value_edited (view, E_MEETING_STORE_CN_COL, path, name);
 458 
 459 			e_meeting_attendee_set_address (attendee, g_strdup_printf ("MAILTO:%s", email));
 460 			e_meeting_attendee_set_cn (attendee, g_strdup (name));
 461 			e_meeting_attendee_set_role (attendee, ICAL_ROLE_REQPARTICIPANT);
 462 			e_meeting_list_view_add_attendee_to_name_selector (E_MEETING_LIST_VIEW (view), attendee);
 463 
 464 			if (address_changed)
 465 				e_meeting_attendee_set_status (attendee, ICAL_PARTSTAT_NEEDSACTION);
 466 
 467 			g_signal_emit_by_name (view, "attendee_added", (gpointer) attendee);
 468 		}
 469 	} else if (existing_attendee) {
 470 		const gchar *address = e_meeting_attendee_get_address (existing_attendee);
 471 
 472 		if (!(address && *address)) {
 473 			e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), existing_attendee);
 474 			e_meeting_store_remove_attendee (model, existing_attendee);
 475 		}
 476 	}
 477 
 478 	gtk_tree_path_free (treepath);
 479 }
 480 
 481 static void
 482 attendee_editing_canceled_cb (GtkCellRenderer *renderer,
 483                               GtkTreeView *view)
 484 {
 485 	EMeetingStore *model = E_MEETING_STORE (gtk_tree_view_get_model (view));
 486 	GtkTreePath *path;
 487 	EMeetingAttendee *existing_attendee;
 488 	gint row;
 489 
 490 	/* This is for newly added attendees when the editing is cancelled */
 491 	gtk_tree_view_get_cursor (view, &path, NULL);
 492 	if (!path)
 493 		return;
 494 
 495 	row = gtk_tree_path_get_indices (path)[0];
 496 	existing_attendee = e_meeting_store_find_attendee_at_row (model, row);
 497 	if (existing_attendee) {
 498 		if (!e_meeting_attendee_is_set_cn (existing_attendee) && !e_meeting_attendee_is_set_address (existing_attendee))
 499 			e_meeting_store_remove_attendee (model, existing_attendee);
 500 	}
 501 
 502 	gtk_tree_path_free (path);
 503 }
 504 
 505 static void
 506 type_edited_cb (GtkCellRenderer *renderer,
 507                 const gchar *path,
 508                 const gchar *text,
 509                 GtkTreeView *view)
 510 {
 511 	value_edited (view, E_MEETING_STORE_TYPE_COL, path, text);
 512 }
 513 
 514 static void
 515 role_edited_cb (GtkCellRenderer *renderer,
 516                 const gchar *path,
 517                 const gchar *text,
 518                 GtkTreeView *view)
 519 {
 520 	/* This is a little more complex than the other callbacks because
 521 	 * we also need to update the "Required Participants" dialog. */
 522 
 523 	EMeetingStore *model = E_MEETING_STORE (gtk_tree_view_get_model (view));
 524 	GtkTreePath *treepath = gtk_tree_path_new_from_string (path);
 525 	gint row = gtk_tree_path_get_indices (treepath)[0];
 526 	EMeetingAttendee *attendee;
 527 
 528 	attendee = e_meeting_store_find_attendee_at_row (model, row);
 529 	e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), attendee);
 530 	e_meeting_store_set_value (model, row, E_MEETING_STORE_ROLE_COL, text);
 531 	e_meeting_list_view_add_attendee_to_name_selector (E_MEETING_LIST_VIEW (view), attendee);
 532 
 533 	gtk_tree_path_free (treepath);
 534 }
 535 
 536 static void
 537 rsvp_edited_cb (GtkCellRenderer *renderer,
 538                 const gchar *path,
 539                 const gchar *text,
 540                 GtkTreeView *view)
 541 {
 542 	value_edited (view, E_MEETING_STORE_RSVP_COL, path, text);
 543 }
 544 
 545 static void
 546 status_edited_cb (GtkCellRenderer *renderer,
 547                   const gchar *path,
 548                   const gchar *text,
 549                   GtkTreeView *view)
 550 {
 551 	value_edited (view, E_MEETING_STORE_STATUS_COL, path, text);
 552 }
 553 
 554 static void
 555 ense_update (GtkWidget *w,
 556              gpointer data1,
 557              gpointer user_data)
 558 {
 559 	gtk_cell_editable_editing_done ((GtkCellEditable *) w);
 560 }
 561 
 562 static void
 563 editing_started_cb (GtkCellRenderer *renderer,
 564                     GtkCellEditable *editable,
 565                     gchar *path,
 566                     gpointer user_data)
 567 {
 568 	g_signal_connect (
 569 		editable, "updated",
 570 		G_CALLBACK (ense_update), NULL);
 571 }
 572 
 573 static GtkCellRenderer *
 574 create_combo_cell_renderer (GList *strings)
 575 {
 576 	GList *li;
 577 	GtkTreeIter iter;
 578 	GtkListStore *store;
 579 	GtkCellRenderer *renderer;
 580 
 581 	store = gtk_list_store_new (1, G_TYPE_STRING);
 582 	for (li = strings; li; li = li->next) {
 583 		const gchar *str = li->data;
 584 
 585 		gtk_list_store_append (store, &iter);
 586 		gtk_list_store_set (store, &iter, 0, str, -1);
 587 	}
 588 
 589 	renderer = gtk_cell_renderer_combo_new ();
 590 
 591 	g_object_set (
 592 		G_OBJECT (renderer),
 593 		"has-entry", FALSE,
 594 		"editable", TRUE,
 595 		"model", GTK_TREE_MODEL (store),
 596 		"text-column", 0,
 597 		NULL);
 598 
 599 	g_object_unref (store);
 600 	g_list_free (strings);
 601 
 602 	return renderer;
 603 }
 604 
 605 static void
 606 build_table (EMeetingListView *lview)
 607 {
 608 	GtkCellRenderer *renderer;
 609 	GtkTreeView *view = GTK_TREE_VIEW (lview);
 610 	EMeetingListViewPrivate *priv;
 611 	GHashTable *edit_table;
 612 	GtkTreeViewColumn *col;
 613 	gint pos;
 614 
 615 	priv = lview->priv;
 616 	edit_table = priv->renderers;
 617 	gtk_tree_view_set_headers_visible (view, TRUE);
 618 	gtk_tree_view_set_rules_hint (view, TRUE);
 619 
 620 	renderer = e_select_names_renderer_new ();
 621 	g_object_set (renderer, "editable", TRUE, NULL);
 622 	/* The extra space is just a hack to occupy more space for Attendee */
 623 	pos = gtk_tree_view_insert_column_with_attributes (
 624 		view, -1, _("Attendee                          "), renderer,
 625 		"text", E_MEETING_STORE_ATTENDEE_COL,
 626 		"name", E_MEETING_STORE_CN_COL,
 627 		"email", E_MEETING_STORE_ADDRESS_COL,
 628 		"underline", E_MEETING_STORE_ATTENDEE_UNDERLINE_COL,
 629 		NULL);
 630 	col = gtk_tree_view_get_column (view, pos -1);
 631 	gtk_tree_view_column_set_resizable (col, TRUE);
 632 	gtk_tree_view_column_set_reorderable (col, TRUE);
 633 	gtk_tree_view_column_set_expand (col, TRUE);
 634 	g_object_set (col, "min-width", 50, NULL);
 635 	g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_ATTENDEE_COL));
 636 	g_signal_connect (
 637 		renderer, "cell_edited",
 638 		G_CALLBACK (attendee_edited_cb), view);
 639 	g_signal_connect (
 640 		renderer, "editing-canceled",
 641 		G_CALLBACK (attendee_editing_canceled_cb), view);
 642 	g_signal_connect (
 643 		renderer, "editing-started",
 644 		G_CALLBACK (editing_started_cb), view);
 645 
 646 	g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_ATTENDEE_COL), renderer);
 647 
 648 	renderer = create_combo_cell_renderer (get_type_strings ());
 649 	pos = gtk_tree_view_insert_column_with_attributes (
 650 		view, -1, _("Type"), renderer,
 651 		"text", E_MEETING_STORE_TYPE_COL,
 652 		NULL);
 653 	col = gtk_tree_view_get_column (view, pos -1);
 654 	gtk_tree_view_column_set_resizable (col, TRUE);
 655 	gtk_tree_view_column_set_reorderable (col, TRUE);
 656 	g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_TYPE_COL));
 657 	g_signal_connect (
 658 		renderer, "edited",
 659 		G_CALLBACK (type_edited_cb), view);
 660 	g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_TYPE_COL), renderer);
 661 
 662 	renderer = create_combo_cell_renderer (get_role_strings ());
 663 	pos = gtk_tree_view_insert_column_with_attributes (
 664 		view, -1, _("Role"), renderer,
 665 		"text", E_MEETING_STORE_ROLE_COL,
 666 		NULL);
 667 	col = gtk_tree_view_get_column (view, pos -1);
 668 	gtk_tree_view_column_set_resizable (col, TRUE);
 669 	gtk_tree_view_column_set_reorderable (col, TRUE);
 670 	g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_ROLE_COL));
 671 	g_signal_connect (
 672 		renderer, "edited",
 673 		G_CALLBACK (role_edited_cb), view);
 674 	g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_ROLE_COL), renderer);
 675 
 676 	renderer = create_combo_cell_renderer (get_rsvp_strings ());
 677 	/* To translators: RSVP means "please reply" */
 678 	pos = gtk_tree_view_insert_column_with_attributes (
 679 		view, -1, _("RSVP"), renderer,
 680 		"text", E_MEETING_STORE_RSVP_COL,
 681 		NULL);
 682 	col = gtk_tree_view_get_column (view, pos -1);
 683 	gtk_tree_view_column_set_resizable (col, TRUE);
 684 	gtk_tree_view_column_set_reorderable (col, TRUE);
 685 	g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_RSVP_COL));
 686 	g_signal_connect (
 687 		renderer, "edited",
 688 		G_CALLBACK (rsvp_edited_cb), view);
 689 	g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_RSVP_COL), renderer);
 690 
 691 	renderer = create_combo_cell_renderer (get_status_strings ());
 692 	pos = gtk_tree_view_insert_column_with_attributes (
 693 		view, -1, _("Status"), renderer,
 694 		"text", E_MEETING_STORE_STATUS_COL,
 695 		NULL);
 696 	col = gtk_tree_view_get_column (view, pos -1);
 697 	gtk_tree_view_column_set_resizable (col, TRUE);
 698 	gtk_tree_view_column_set_reorderable (col, TRUE);
 699 	g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_STATUS_COL));
 700 	g_signal_connect (
 701 		renderer, "edited",
 702 		G_CALLBACK (status_edited_cb), view);
 703 	g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_STATUS_COL), renderer);
 704 
 705 	priv->renderers = edit_table;
 706 }
 707 
 708 static void
 709 change_edit_cols_for_user (gpointer key,
 710                            gpointer value,
 711                            gpointer user_data)
 712 {
 713 	GtkCellRenderer *renderer = (GtkCellRenderer *) value;
 714 	gint key_val = GPOINTER_TO_INT (key);
 715 
 716 	switch (key_val) {
 717 		case E_MEETING_STORE_ATTENDEE_COL:
 718 			g_object_set (renderer, "editable", FALSE, NULL);
 719 			break;
 720 		case E_MEETING_STORE_ROLE_COL:
 721 			g_object_set (renderer, "editable", FALSE, NULL);
 722 			break;
 723 		case E_MEETING_STORE_TYPE_COL:
 724 			g_object_set (renderer, "editable", FALSE, NULL);
 725 			break;
 726 		case E_MEETING_STORE_RSVP_COL:
 727 			g_object_set (renderer, "editable", TRUE, NULL);
 728 			break;
 729 		case E_MEETING_STORE_STATUS_COL:
 730 			g_object_set (renderer, "editable", TRUE, NULL);
 731 			break;
 732 	}
 733 }
 734 
 735 static void
 736 change_edit_cols_for_organizer (gpointer key,
 737                                 gpointer value,
 738                                 gpointer user_data)
 739 {
 740 	GtkCellRenderer *renderer = (GtkCellRenderer *) value;
 741 	guint edit_level = GPOINTER_TO_INT (user_data);
 742 
 743 	g_object_set (renderer, "editable", GINT_TO_POINTER (edit_level), NULL);
 744 }
 745 
 746 static void
 747 row_activated_cb (GtkTreeSelection *selection,
 748                   EMeetingListView *view)
 749 {
 750 	EMeetingAttendee *existing_attendee;
 751 	EMeetingListViewPrivate *priv;
 752 	gint row;
 753 	EMeetingAttendeeEditLevel el;
 754 	gint  edit_level;
 755 	GtkTreeModel *model;
 756 	GtkTreePath *path = NULL;
 757 	GList *paths = NULL;
 758 
 759 	priv = view->priv;
 760 
 761 	if (!(paths = gtk_tree_selection_get_selected_rows (selection, &model)))
 762 		return;
 763 	if (g_list_length (paths) > 1)
 764 		return;
 765 	path = g_list_nth_data (paths, 0);
 766 	if (!path)
 767 		return;
 768 
 769 	row = gtk_tree_path_get_indices (path)[0];
 770 	existing_attendee = e_meeting_store_find_attendee_at_row (priv->store, row);
 771 	el = e_meeting_attendee_get_edit_level (existing_attendee);
 772 
 773 	switch (el) {
 774 		case E_MEETING_ATTENDEE_EDIT_NONE:
 775 			edit_level = FALSE;
 776 			g_hash_table_foreach (
 777 				priv->renderers,
 778 				change_edit_cols_for_organizer,
 779 				GINT_TO_POINTER (edit_level));
 780 			break;
 781 
 782 		case E_MEETING_ATTENDEE_EDIT_FULL:
 783 			edit_level = TRUE;
 784 			g_hash_table_foreach (
 785 				priv->renderers,
 786 				change_edit_cols_for_organizer,
 787 				GINT_TO_POINTER (edit_level));
 788 			break;
 789 
 790 		case E_MEETING_ATTENDEE_EDIT_STATUS:
 791 			edit_level = FALSE;
 792 			g_hash_table_foreach (
 793 				priv->renderers,
 794 				change_edit_cols_for_user,
 795 				GINT_TO_POINTER (edit_level));
 796 			break;
 797 	}
 798 }
 799 
 800 EMeetingListView *
 801 e_meeting_list_view_new (EMeetingStore *store)
 802 {
 803 	EMeetingListView *view = g_object_new (E_TYPE_MEETING_LIST_VIEW, NULL);
 804 	GtkTreeSelection *selection;
 805 
 806 	if (view) {
 807 		view->priv->store = store;
 808 		gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL (store));
 809 		build_table (view);
 810 	}
 811 
 812 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
 813 	g_signal_connect (
 814 		selection, "changed",
 815 		G_CALLBACK (row_activated_cb), view);
 816 
 817 	return view;
 818 }
 819 
 820 void
 821 e_meeting_list_view_column_set_visible (EMeetingListView *view,
 822                                         EMeetingStoreColumns column,
 823                                         gboolean visible)
 824 {
 825 	GList *cols, *l;
 826 
 827 	cols = gtk_tree_view_get_columns (GTK_TREE_VIEW (view));
 828 
 829 	for (l = cols; l; l = l->next) {
 830 		GtkTreeViewColumn *col = (GtkTreeViewColumn *) l->data;
 831 		EMeetingStoreColumns store_colum = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (col), "mtg-store-col"));
 832 
 833 		if (store_colum == column) {
 834 			gtk_tree_view_column_set_visible (col, visible);
 835 			break;
 836 		}
 837 	}
 838 }
 839 
 840 void
 841 e_meeting_list_view_edit (EMeetingListView *emlv,
 842                           EMeetingAttendee *attendee)
 843 {
 844 	EMeetingListViewPrivate *priv;
 845 	GtkTreePath *path;
 846 	GtkTreeViewColumn *focus_col;
 847 
 848 	priv = emlv->priv;
 849 
 850 	g_return_if_fail (emlv != NULL);
 851 	g_return_if_fail (E_IS_MEETING_LIST_VIEW (emlv));
 852 	g_return_if_fail (attendee != NULL);
 853 
 854 	path = e_meeting_store_find_attendee_path (priv->store, attendee);
 855 	focus_col = gtk_tree_view_get_column (GTK_TREE_VIEW (emlv), 0);
 856 
 857 	if (path) {
 858 		gtk_tree_view_set_cursor (GTK_TREE_VIEW (emlv), path, focus_col, TRUE);
 859 
 860 		gtk_tree_path_free (path);
 861 	}
 862 }
 863 
 864 static void
 865 process_section (EMeetingListView *view,
 866                  GList *destinations,
 867                  icalparameter_role role,
 868                  GSList **la)
 869 {
 870 	EMeetingListViewPrivate *priv;
 871 	GList *l;
 872 
 873 	priv = view->priv;
 874 	for (l = destinations; l; l = g_list_next (l)) {
 875 		EDestination *destination = l->data, *des = NULL;
 876 		const GList *list_dests = NULL, *l;
 877 		GList card_dest;
 878 
 879 		if (e_destination_is_evolution_list (destination)) {
 880 			list_dests = e_destination_list_get_dests (destination);
 881 		} else {
 882 			EContact *contact = e_destination_get_contact (destination);
 883 			/* check if the contact is contact list which is not expanded yet */
 884 			/* we expand it by getting the list again from the server forming the query */
 885 			if (contact && e_contact_get (contact , E_CONTACT_IS_LIST)) {
 886 				EBookClient *book_client = NULL;
 887 				ENameSelectorDialog *dialog;
 888 				ENameSelectorModel *model;
 889 				EContactStore *c_store;
 890 				GSList *clients, *l;
 891 				gchar *uid = e_contact_get (contact, E_CONTACT_BOOK_UID);
 892 
 893 				dialog = e_name_selector_peek_dialog (view->priv->name_selector);
 894 				model = e_name_selector_dialog_peek_model (dialog);
 895 				c_store = e_name_selector_model_peek_contact_store (model);
 896 				clients = e_contact_store_get_clients (c_store);
 897 
 898 				for (l = clients; l; l = l->next) {
 899 					EBookClient *b = l->data;
 900 					ESource *source;
 901 
 902 					source = e_client_get_source (E_CLIENT (b));
 903 
 904 					if (g_strcmp0 (uid, e_source_get_uid (source)) == 0) {
 905 						book_client = b;
 906 						break;
 907 					}
 908 				}
 909 
 910 				if (book_client) {
 911 					GSList *contacts;
 912 					EContact *n_con = NULL;
 913 					gchar *query;
 914 
 915 					query = g_strdup_printf (
 916 						"(is \"full_name\" \"%s\")",
 917 						(gchar *) e_contact_get (contact, E_CONTACT_FULL_NAME));
 918 
 919 					if (!e_book_client_get_contacts_sync (book_client, query, &contacts, NULL, NULL)) {
 920 						g_warning ("Could not get contact from the book \n");
 921 						g_free (query);
 922 						g_slist_free (clients);
 923 						return;
 924 					} else {
 925 						des = e_destination_new ();
 926 						n_con = contacts->data;
 927 
 928 						e_destination_set_contact (des, n_con, 0);
 929 						e_destination_set_client (des, book_client);
 930 						list_dests = e_destination_list_get_dests (des);
 931 
 932 						g_slist_foreach (contacts, (GFunc) g_object_unref, NULL);
 933 						g_slist_free (contacts);
 934 					}
 935 
 936 					g_free (query);
 937 				}
 938 				g_slist_free (clients);
 939 			} else {
 940 				card_dest.next = NULL;
 941 				card_dest.prev = NULL;
 942 				card_dest.data = destination;
 943 				list_dests = &card_dest;
 944 			}
 945 		}
 946 
 947 		for (l = list_dests; l; l = l->next) {
 948 			EDestination *dest = l->data;
 949 			EContact *contact;
 950 			const gchar *name, *attendee = NULL;
 951 			gchar *fburi = NULL;
 952 
 953 			name = e_destination_get_name (dest);
 954 			attendee = e_destination_get_email (dest);
 955 
 956 			if (attendee == NULL || *attendee == '\0')
 957 				continue;
 958 
 959 			contact = e_destination_get_contact (dest);
 960 			if (contact)
 961 				fburi = e_contact_get (contact, E_CONTACT_FREEBUSY_URL);
 962 
 963 			if (e_meeting_store_find_attendee (priv->store, attendee, NULL) == NULL) {
 964 				EMeetingAttendee *ia = e_meeting_store_add_attendee_with_defaults (priv->store);
 965 
 966 				e_meeting_attendee_set_address (ia, g_strdup_printf ("MAILTO:%s", attendee));
 967 				e_meeting_attendee_set_role (ia, role);
 968 				if (role == ICAL_ROLE_NONPARTICIPANT)
 969 					e_meeting_attendee_set_cutype (ia, ICAL_CUTYPE_RESOURCE);
 970 				e_meeting_attendee_set_cn (ia, g_strdup (name));
 971 
 972 				if (fburi)
 973 					e_meeting_attendee_set_fburi (ia, fburi);
 974 			} else {
 975 				if (g_slist_length (*la) == 1) {
 976 					g_slist_free (*la);
 977 					*la = NULL;
 978 				} else
 979 					*la = g_slist_remove_link (*la, g_slist_find_custom (*la, attendee, (GCompareFunc)g_ascii_strcasecmp));
 980 			}
 981 		}
 982 
 983 		if (des) {
 984 			g_object_unref (des);
 985 			des = NULL;
 986 		}
 987 
 988 	}
 989 }
 990 
 991 static void
 992 add_to_list (gpointer data,
 993              gpointer u_data)
 994 {
 995 	GSList **user_data = u_data;
 996 
 997 	*user_data = g_slist_append (*user_data, (gpointer)itip_strip_mailto (e_meeting_attendee_get_address (data)));
 998 }
 999 
1000 static void
1001 name_selector_dialog_close_cb (ENameSelectorDialog *dialog,
1002                                gint response,
1003                                gpointer data)
1004 {
1005 	EMeetingListView   *view = E_MEETING_LIST_VIEW (data);
1006 	ENameSelectorModel *name_selector_model;
1007 	EMeetingStore *store;
1008 	const GPtrArray *attendees;
1009 	gint i;
1010 	GSList		  *la = NULL, *l;
1011 
1012 	name_selector_model = e_name_selector_peek_model (view->priv->name_selector);
1013 	store = E_MEETING_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
1014 	attendees = e_meeting_store_get_attendees (store);
1015 
1016 	/* get all the email ids of the attendees */
1017 	g_ptr_array_foreach ((GPtrArray *) attendees, (GFunc) add_to_list, &la);
1018 
1019 	for (i = 0; sections[i] != NULL; i++) {
1020 		EDestinationStore *destination_store;
1021 		GList             *destinations;
1022 
1023 		e_name_selector_model_peek_section (
1024 			name_selector_model, sections[i],
1025 			NULL, &destination_store);
1026 		if (!destination_store) {
1027 			g_warning ("destination store is NULL\n");
1028 			continue;
1029 		}
1030 
1031 		destinations = e_destination_store_list_destinations (destination_store);
1032 		process_section (view, destinations, roles[i], &la);
1033 		g_list_free (destinations);
1034 	}
1035 
1036 	/* remove the deleted attendees from name selector */
1037 	for (l = la; l != NULL; l = l->next) {
1038 		EMeetingAttendee *ma = NULL;
1039 		const gchar *email = l->data;
1040 		gint i;
1041 
1042 		ma = e_meeting_store_find_attendee (store, email, &i);
1043 
1044 		if (ma) {
1045 			if (e_meeting_attendee_get_edit_level (ma) != E_MEETING_ATTENDEE_EDIT_FULL)
1046 				g_warning ("Not enough rights to delete attendee: %s\n", e_meeting_attendee_get_address (ma));
1047 			else
1048 				e_meeting_store_remove_attendee (store, ma);
1049 		}
1050 	}
1051 
1052 	g_slist_free (la);
1053 	gtk_widget_hide (GTK_WIDGET (dialog));
1054 }
1055 
1056 void
1057 e_meeting_list_view_invite_others_dialog (EMeetingListView *view)
1058 {
1059 	e_name_selector_show_dialog (
1060 		view->priv->name_selector,
1061 		GTK_WIDGET (view));
1062 }
1063 
1064 void
1065 e_meeting_list_view_set_editable (EMeetingListView *lview,
1066                                   gboolean set)
1067 {
1068 	EMeetingListViewPrivate *priv = lview->priv;
1069 
1070 	gint edit_level = set;
1071 
1072 	g_hash_table_foreach (priv->renderers, change_edit_cols_for_organizer, GINT_TO_POINTER (edit_level));
1073 }
1074 
1075 ENameSelector *
1076 e_meeting_list_view_get_name_selector (EMeetingListView *lview)
1077 {
1078 	EMeetingListViewPrivate *priv;
1079 
1080 	g_return_val_if_fail (lview != NULL, NULL);
1081 	g_return_val_if_fail (E_IS_MEETING_LIST_VIEW (lview), NULL);
1082 
1083 	priv = lview->priv;
1084 
1085 	return priv->name_selector;
1086 }
1087 
1088 void
1089 e_meeting_list_view_set_name_selector (EMeetingListView *lview,
1090                                        ENameSelector *name_selector)
1091 {
1092 	EMeetingListViewPrivate *priv;
1093 
1094 	g_return_if_fail (lview != NULL);
1095 	g_return_if_fail (E_IS_MEETING_LIST_VIEW (lview));
1096 
1097 	priv = lview->priv;
1098 
1099 	if (priv->name_selector) {
1100 		g_object_unref (priv->name_selector);
1101 		priv->name_selector = NULL;
1102 	}
1103 
1104 	priv->name_selector = g_object_ref (name_selector);
1105 }