evolution-3.6.4/calendar/gui/e-task-table.c

No issues found

   1 /*
   2  * This program is free software; you can redistribute it and/or
   3  * modify it under the terms of the GNU Lesser General Public
   4  * License as published by the Free Software Foundation; either
   5  * version 2 of the License, or (at your option) version 3.
   6  *
   7  * This program is distributed in the hope that it will be useful,
   8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  10  * Lesser General Public License for more details.
  11  *
  12  * You should have received a copy of the GNU Lesser General Public
  13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  14  *
  15  *
  16  * Authors:
  17  *		Damon Chaplin <damon@ximian.com>
  18  *		Rodrigo Moya <rodrigo@ximian.com>
  19  *
  20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  21  *
  22  */
  23 
  24 /*
  25  * ETaskTable - displays the ECalComponent objects in a table (an ETable).
  26  */
  27 
  28 #ifdef HAVE_CONFIG_H
  29 #include <config.h>
  30 #endif
  31 
  32 #include <sys/stat.h>
  33 #include <unistd.h>
  34 #include <glib/gi18n.h>
  35 #include <glib/gstdio.h>
  36 #include <gtk/gtk.h>
  37 #include <gdk/gdkkeysyms.h>
  38 #include <misc/e-selectable.h>
  39 #include <table/e-cell-checkbox.h>
  40 #include <table/e-cell-toggle.h>
  41 #include <table/e-cell-text.h>
  42 #include <table/e-cell-combo.h>
  43 #include <table/e-cell-date.h>
  44 #include <e-util/e-selection.h>
  45 #include <e-util/e-dialog-utils.h>
  46 #include <e-util/e-util-private.h>
  47 #include <table/e-cell-date-edit.h>
  48 #include <table/e-cell-percent.h>
  49 #include <table/e-table-sorting-utils.h>
  50 
  51 #include "calendar-config.h"
  52 #include "dialogs/delete-comp.h"
  53 #include "dialogs/delete-error.h"
  54 #include "dialogs/task-editor.h"
  55 #include "e-cal-model-tasks.h"
  56 #include "e-task-table.h"
  57 #include "e-calendar-view.h"
  58 #include "e-cell-date-edit-text.h"
  59 #include "print.h"
  60 #include <e-util/e-util-private.h>
  61 #include <e-util/e-icon-factory.h>
  62 #include "misc.h"
  63 
  64 #define E_TASK_TABLE_GET_PRIVATE(obj) \
  65 	(G_TYPE_INSTANCE_GET_PRIVATE \
  66 	((obj), E_TYPE_TASK_TABLE, ETaskTablePrivate))
  67 
  68 struct _ETaskTablePrivate {
  69 	gpointer shell_view;  /* weak pointer */
  70 	ECalModel *model;
  71 	GCancellable *completed_cancellable; /* when processing completed tasks */
  72 
  73 	GtkTargetList *copy_target_list;
  74 	GtkTargetList *paste_target_list;
  75 };
  76 
  77 enum {
  78 	PROP_0,
  79 	PROP_COPY_TARGET_LIST,
  80 	PROP_MODEL,
  81 	PROP_PASTE_TARGET_LIST,
  82 	PROP_SHELL_VIEW
  83 };
  84 
  85 enum {
  86 	OPEN_COMPONENT,
  87 	POPUP_EVENT,
  88 	STATUS_MESSAGE,
  89 	LAST_SIGNAL
  90 };
  91 
  92 static struct tm e_task_table_get_current_time (ECellDateEdit *ecde, gpointer data);
  93 
  94 static guint signals[LAST_SIGNAL];
  95 
  96 /* The icons to represent the task. */
  97 static const gchar *icon_names[] = {
  98 	"stock_task",
  99 	"stock_task-recurring",
 100 	"stock_task-assigned",
 101 	"stock_task-assigned-to"
 102 };
 103 
 104 /* Forward Declarations */
 105 static void	e_task_table_selectable_init
 106 					(ESelectableInterface *interface);
 107 
 108 G_DEFINE_TYPE_WITH_CODE (
 109 	ETaskTable,
 110 	e_task_table,
 111 	E_TYPE_TABLE,
 112 	G_IMPLEMENT_INTERFACE (
 113 		E_TYPE_SELECTABLE,
 114 		e_task_table_selectable_init))
 115 
 116 static void
 117 task_table_emit_open_component (ETaskTable *task_table,
 118                                 ECalModelComponent *comp_data)
 119 {
 120 	guint signal_id = signals[OPEN_COMPONENT];
 121 
 122 	g_signal_emit (task_table, signal_id, 0, comp_data);
 123 }
 124 
 125 static void
 126 task_table_emit_popup_event (ETaskTable *task_table,
 127                              GdkEvent *event)
 128 {
 129 	guint signal_id = signals[POPUP_EVENT];
 130 
 131 	g_signal_emit (task_table, signal_id, 0, event);
 132 }
 133 
 134 static void
 135 task_table_emit_status_message (ETaskTable *task_table,
 136                                 const gchar *message,
 137                                 gdouble percent)
 138 {
 139 	guint signal_id = signals[STATUS_MESSAGE];
 140 
 141 	g_signal_emit (task_table, signal_id, 0, message, percent);
 142 }
 143 
 144 static gint
 145 task_table_percent_compare_cb (gconstpointer a,
 146                                gconstpointer b,
 147                                gpointer cmp_cache)
 148 {
 149 	gint percent1 = GPOINTER_TO_INT (a);
 150 	gint percent2 = GPOINTER_TO_INT (b);
 151 
 152 	return (percent1 < percent2) ? -1 : (percent1 > percent2);
 153 }
 154 
 155 static gint
 156 task_table_priority_compare_cb (gconstpointer a,
 157                                 gconstpointer b,
 158                                 gpointer cmp_cache)
 159 {
 160 	gint priority1, priority2;
 161 
 162 	priority1 = e_cal_util_priority_from_string ((const gchar *) a);
 163 	priority2 = e_cal_util_priority_from_string ((const gchar *) b);
 164 
 165 	/* We change undefined priorities so they appear after 'Low'. */
 166 	if (priority1 <= 0)
 167 		priority1 = 10;
 168 	if (priority2 <= 0)
 169 		priority2 = 10;
 170 
 171 	/* We'll just use the ordering of the priority values. */
 172 	return (priority1 < priority2) ? -1 : (priority1 > priority2);
 173 }
 174 
 175 static const gchar *
 176 get_cache_str (gpointer cmp_cache,
 177                const gchar *str)
 178 {
 179 	const gchar *value;
 180 
 181 	if (!cmp_cache || !str)
 182 		return str;
 183 
 184 	value = e_table_sorting_utils_lookup_cmp_cache (cmp_cache, str);
 185 	if (!value) {
 186 		gchar *ckey;
 187 
 188 		ckey = g_utf8_collate_key (str, -1);
 189 		e_table_sorting_utils_add_to_cmp_cache (cmp_cache, (gchar *) str, ckey);
 190 		value = ckey;
 191 	}
 192 
 193 	return value;
 194 }
 195 
 196 static gboolean
 197 same_cache_string (gpointer cmp_cache,
 198                    const gchar *str_a,
 199                    const gchar *str_b)
 200 {
 201 	if (!cmp_cache)
 202 		return g_utf8_collate (str_a, str_b) == 0;
 203 
 204 	str_b = get_cache_str (cmp_cache, str_b);
 205 
 206 	g_return_val_if_fail (str_a != NULL, FALSE);
 207 	g_return_val_if_fail (str_b != NULL, FALSE);
 208 
 209 	return strcmp (str_a, str_b) == 0;
 210 }
 211 
 212 static gint
 213 task_table_status_compare_cb (gconstpointer a,
 214                               gconstpointer b,
 215                               gpointer cmp_cache)
 216 {
 217 	const gchar *string_a = a;
 218 	const gchar *string_b = b;
 219 	gint status_a = -2;
 220 	gint status_b = -2;
 221 
 222 	if (string_a == NULL || *string_a == '\0')
 223 		status_a = -1;
 224 	else {
 225 		const gchar *cache_str = get_cache_str (cmp_cache, string_a);
 226 
 227 		if (same_cache_string (cmp_cache, cache_str, _("Not Started")))
 228 			status_a = 0;
 229 		else if (same_cache_string (cmp_cache, cache_str, _("In Progress")))
 230 			status_a = 1;
 231 		else if (same_cache_string (cmp_cache, cache_str, _("Completed")))
 232 			status_a = 2;
 233 		else if (same_cache_string (cmp_cache, cache_str, _("Canceled")))
 234 			status_a = 3;
 235 	}
 236 
 237 	if (string_b == NULL || *string_b == '\0')
 238 		status_b = -1;
 239 	else {
 240 		const gchar *cache_str = get_cache_str (cmp_cache, string_b);
 241 
 242 		if (same_cache_string (cmp_cache, cache_str, _("Not Started")))
 243 			status_b = 0;
 244 		else if (same_cache_string (cmp_cache, cache_str, _("In Progress")))
 245 			status_b = 1;
 246 		else if (same_cache_string (cmp_cache, cache_str, _("Completed")))
 247 			status_b = 2;
 248 		else if (same_cache_string (cmp_cache, cache_str, _("Canceled")))
 249 			status_b = 3;
 250 	}
 251 
 252 	return (status_a < status_b) ? -1 : (status_a > status_b);
 253 }
 254 
 255 static void
 256 task_table_model_cal_view_progress_cb (ETaskTable *task_table,
 257                                        const gchar *message,
 258                                        gint progress,
 259                                        ECalClientSourceType type)
 260 {
 261 	gdouble percent = (gdouble) progress;
 262 
 263 	task_table_emit_status_message (task_table, message, percent);
 264 }
 265 
 266 static void
 267 task_table_model_cal_view_complete_cb (ETaskTable *task_table,
 268                                        const GError *error,
 269                                        ECalClientSourceType type)
 270 {
 271 	task_table_emit_status_message (task_table, NULL, -1.0);
 272 }
 273 
 274 /* Deletes all of the selected components in the table */
 275 static void
 276 delete_selected_components (ETaskTable *task_table)
 277 {
 278 	GSList *objs, *l;
 279 	const gchar *status_message;
 280 
 281 	objs = e_task_table_get_selected (task_table);
 282 
 283 	status_message = _("Deleting selected objects");
 284 	task_table_emit_status_message (task_table, status_message, -1.0);
 285 
 286 	for (l = objs; l; l = l->next) {
 287 		ECalModelComponent *comp_data = (ECalModelComponent *) l->data;
 288 		GError *error = NULL;
 289 
 290 		e_cal_client_remove_object_sync (
 291 			comp_data->client,
 292 			icalcomponent_get_uid (comp_data->icalcomp),
 293 			NULL, CALOBJ_MOD_THIS, NULL, &error);
 294 		delete_error_dialog (error, E_CAL_COMPONENT_TODO);
 295 		g_clear_error (&error);
 296 	}
 297 
 298 	task_table_emit_status_message (task_table, NULL, -1.0);
 299 
 300 	g_slist_free (objs);
 301 }
 302 
 303 static void
 304 task_table_queue_draw_cb (ECalModelTasks *tasks_model,
 305                           GParamSpec *param,
 306                           GtkWidget *task_table)
 307 {
 308 	g_return_if_fail (task_table != NULL);
 309 
 310 	gtk_widget_queue_draw (task_table);
 311 }
 312 
 313 static void
 314 task_table_set_model (ETaskTable *task_table,
 315                       ECalModel *model)
 316 {
 317 	g_return_if_fail (task_table->priv->model == NULL);
 318 
 319 	task_table->priv->model = g_object_ref (model);
 320 
 321 	g_signal_connect_swapped (
 322 		model, "cal-view-progress",
 323 		G_CALLBACK (task_table_model_cal_view_progress_cb),
 324 		task_table);
 325 
 326 	g_signal_connect_swapped (
 327 		model, "cal-view-complete",
 328 		G_CALLBACK (task_table_model_cal_view_complete_cb),
 329 		task_table);
 330 
 331 	/* redraw on drawing options change */
 332 	g_signal_connect (
 333 		model, "notify::highlight-due-today",
 334 		G_CALLBACK (task_table_queue_draw_cb),
 335 		task_table);
 336 
 337 	g_signal_connect (
 338 		model, "notify::color-due-today",
 339 		G_CALLBACK (task_table_queue_draw_cb),
 340 		task_table);
 341 
 342 	g_signal_connect (
 343 		model, "notify::highlight-overdue",
 344 		G_CALLBACK (task_table_queue_draw_cb),
 345 		task_table);
 346 
 347 	g_signal_connect (
 348 		model, "notify::color-overdue",
 349 		G_CALLBACK (task_table_queue_draw_cb),
 350 		task_table);
 351 
 352 }
 353 
 354 static void
 355 task_table_set_shell_view (ETaskTable *task_table,
 356                            EShellView *shell_view)
 357 {
 358 	g_return_if_fail (task_table->priv->shell_view == NULL);
 359 
 360 	task_table->priv->shell_view = shell_view;
 361 
 362 	g_object_add_weak_pointer (
 363 		G_OBJECT (shell_view),
 364 		&task_table->priv->shell_view);
 365 }
 366 
 367 static void
 368 task_table_set_property (GObject *object,
 369                          guint property_id,
 370                          const GValue *value,
 371                          GParamSpec *pspec)
 372 {
 373 	switch (property_id) {
 374 		case PROP_MODEL:
 375 			task_table_set_model (
 376 				E_TASK_TABLE (object),
 377 				g_value_get_object (value));
 378 			return;
 379 
 380 		case PROP_SHELL_VIEW:
 381 			task_table_set_shell_view (
 382 				E_TASK_TABLE (object),
 383 				g_value_get_object (value));
 384 			return;
 385 	}
 386 
 387 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 388 }
 389 
 390 static void
 391 task_table_get_property (GObject *object,
 392                          guint property_id,
 393                          GValue *value,
 394                          GParamSpec *pspec)
 395 {
 396 	switch (property_id) {
 397 		case PROP_COPY_TARGET_LIST:
 398 			g_value_set_boxed (
 399 				value, e_task_table_get_copy_target_list (
 400 				E_TASK_TABLE (object)));
 401 			return;
 402 
 403 		case PROP_MODEL:
 404 			g_value_set_object (
 405 				value, e_task_table_get_model (
 406 				E_TASK_TABLE (object)));
 407 			return;
 408 
 409 		case PROP_PASTE_TARGET_LIST:
 410 			g_value_set_boxed (
 411 				value, e_task_table_get_paste_target_list (
 412 				E_TASK_TABLE (object)));
 413 			return;
 414 
 415 		case PROP_SHELL_VIEW:
 416 			g_value_set_object (
 417 				value, e_task_table_get_shell_view (
 418 				E_TASK_TABLE (object)));
 419 			return;
 420 	}
 421 
 422 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 423 }
 424 
 425 static void
 426 task_table_dispose (GObject *object)
 427 {
 428 	ETaskTablePrivate *priv;
 429 
 430 	priv = E_TASK_TABLE_GET_PRIVATE (object);
 431 
 432 	if (priv->completed_cancellable) {
 433 		g_cancellable_cancel (priv->completed_cancellable);
 434 		g_object_unref (priv->completed_cancellable);
 435 		priv->completed_cancellable = NULL;
 436 	}
 437 
 438 	if (priv->shell_view != NULL) {
 439 		g_object_remove_weak_pointer (
 440 			G_OBJECT (priv->shell_view), &priv->shell_view);
 441 		priv->shell_view = NULL;
 442 	}
 443 
 444 	if (priv->model != NULL) {
 445 		g_signal_handlers_disconnect_by_func (priv->model, task_table_queue_draw_cb, object);
 446 		g_object_unref (priv->model);
 447 		priv->model = NULL;
 448 	}
 449 
 450 	if (priv->copy_target_list != NULL) {
 451 		gtk_target_list_unref (priv->copy_target_list);
 452 		priv->copy_target_list = NULL;
 453 	}
 454 
 455 	if (priv->paste_target_list != NULL) {
 456 		gtk_target_list_unref (priv->paste_target_list);
 457 		priv->paste_target_list = NULL;
 458 	}
 459 
 460 	/* Chain up to parent's dispose() method. */
 461 	G_OBJECT_CLASS (e_task_table_parent_class)->dispose (object);
 462 }
 463 
 464 static void
 465 task_table_constructed (GObject *object)
 466 {
 467 	ETaskTable *task_table;
 468 	ECalModel *model;
 469 	ECell *cell, *popup_cell;
 470 	ETableExtras *extras;
 471 	GList *strings;
 472 	AtkObject *a11y;
 473 	gchar *etspecfile;
 474 	gint percent;
 475 
 476 	task_table = E_TASK_TABLE (object);
 477 	model = e_task_table_get_model (task_table);
 478 
 479 	/* Create the header columns */
 480 
 481 	extras = e_table_extras_new ();
 482 
 483 	/*
 484 	 * Normal string fields.
 485 	 */
 486 	cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
 487 	g_object_set (
 488 		cell,
 489 		"strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
 490 		"bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
 491 		"bg_color_column", E_CAL_MODEL_FIELD_COLOR,
 492 		NULL);
 493 
 494 	e_table_extras_add_cell (extras, "calstring", cell);
 495 	g_object_unref (cell);
 496 
 497 	/*
 498 	 * Date fields.
 499 	 */
 500 	cell = e_cell_date_edit_text_new (NULL, GTK_JUSTIFY_LEFT);
 501 	g_object_set (
 502 		cell,
 503 		"strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
 504 		"bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
 505 		"bg_color_column", E_CAL_MODEL_FIELD_COLOR,
 506 		NULL);
 507 
 508 	g_object_bind_property (
 509 		model, "timezone",
 510 		cell, "timezone",
 511 		G_BINDING_BIDIRECTIONAL |
 512 		G_BINDING_SYNC_CREATE);
 513 
 514 	g_object_bind_property (
 515 		model, "use-24-hour-format",
 516 		cell, "use-24-hour-format",
 517 		G_BINDING_BIDIRECTIONAL |
 518 		G_BINDING_SYNC_CREATE);
 519 
 520 	popup_cell = e_cell_date_edit_new ();
 521 	e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
 522 	g_object_unref (cell);
 523 
 524 	g_object_bind_property (
 525 		model, "use-24-hour-format",
 526 		popup_cell, "use-24-hour-format",
 527 		G_BINDING_BIDIRECTIONAL |
 528 		G_BINDING_SYNC_CREATE);
 529 
 530 	e_table_extras_add_cell (extras, "dateedit", popup_cell);
 531 	g_object_unref (popup_cell);
 532 
 533 	task_table->dates_cell = E_CELL_DATE_EDIT (popup_cell);
 534 
 535 	e_cell_date_edit_set_get_time_callback (
 536 		E_CELL_DATE_EDIT (popup_cell),
 537 		e_task_table_get_current_time, task_table, NULL);
 538 
 539 	/*
 540 	 * Combo fields.
 541 	 */
 542 
 543 	/* Classification field. */
 544 	cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
 545 	g_object_set (
 546 		cell,
 547 		"strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
 548 		"bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
 549 		"bg_color_column", E_CAL_MODEL_FIELD_COLOR,
 550 		"editable", FALSE,
 551 		NULL);
 552 
 553 	popup_cell = e_cell_combo_new ();
 554 	e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
 555 	g_object_unref (cell);
 556 
 557 	strings = NULL;
 558 	strings = g_list_append (strings, (gchar *) _("Public"));
 559 	strings = g_list_append (strings, (gchar *) _("Private"));
 560 	strings = g_list_append (strings, (gchar *) _("Confidential"));
 561 	e_cell_combo_set_popdown_strings (
 562 		E_CELL_COMBO (popup_cell),
 563 		strings);
 564 	g_list_free (strings);
 565 
 566 	e_table_extras_add_cell (extras, "classification", popup_cell);
 567 	g_object_unref (popup_cell);
 568 
 569 	/* Priority field. */
 570 	cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
 571 	g_object_set (
 572 		cell,
 573 		"strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
 574 		"bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
 575 		"bg_color_column", E_CAL_MODEL_FIELD_COLOR,
 576 		"editable", FALSE,
 577 		NULL);
 578 
 579 	popup_cell = e_cell_combo_new ();
 580 	e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
 581 	g_object_unref (cell);
 582 
 583 	strings = NULL;
 584 	strings = g_list_append (strings, (gchar *) _("High"));
 585 	strings = g_list_append (strings, (gchar *) _("Normal"));
 586 	strings = g_list_append (strings, (gchar *) _("Low"));
 587 	strings = g_list_append (strings, (gchar *) _("Undefined"));
 588 	e_cell_combo_set_popdown_strings (
 589 		E_CELL_COMBO (popup_cell),
 590 		strings);
 591 	g_list_free (strings);
 592 
 593 	e_table_extras_add_cell (extras, "priority", popup_cell);
 594 	g_object_unref (popup_cell);
 595 
 596 	/* Percent field. */
 597 	cell = e_cell_percent_new (NULL, GTK_JUSTIFY_LEFT);
 598 	g_object_set (
 599 		cell,
 600 		"strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
 601 		"bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
 602 		"bg_color_column", E_CAL_MODEL_FIELD_COLOR,
 603 		NULL);
 604 
 605 	popup_cell = e_cell_combo_new ();
 606 	e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
 607 	g_object_unref (cell);
 608 
 609 	strings = NULL;
 610 	for (percent = 0; percent <= 100; percent += 10) {
 611 		/* Translators: "%d%%" is the percentage of a task done.
 612 		 * %d is the actual value, %% is replaced with a percent sign.
 613 		 * Result values will be 0%, 10%, 20%, ... 100%
 614 		*/
 615 		strings = g_list_append (strings, g_strdup_printf (_("%d%%"), percent));
 616 	}
 617 	e_cell_combo_set_popdown_strings (
 618 		E_CELL_COMBO (popup_cell),
 619 		strings);
 620 
 621 	g_list_foreach (strings, (GFunc) g_free, NULL);
 622 	g_list_free (strings);
 623 
 624 	e_table_extras_add_cell (extras, "percent", popup_cell);
 625 	g_object_unref (popup_cell);
 626 
 627 	/* Transparency field. */
 628 	cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
 629 	g_object_set (
 630 		cell,
 631 		"strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
 632 		"bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
 633 		"bg_color_column", E_CAL_MODEL_FIELD_COLOR,
 634 		"editable", FALSE,
 635 		NULL);
 636 
 637 	popup_cell = e_cell_combo_new ();
 638 	e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
 639 	g_object_unref (cell);
 640 
 641 	strings = NULL;
 642 	strings = g_list_append (strings, (gchar *) _("Free"));
 643 	strings = g_list_append (strings, (gchar *) _("Busy"));
 644 	e_cell_combo_set_popdown_strings (
 645 		E_CELL_COMBO (popup_cell),
 646 		strings);
 647 	g_list_free (strings);
 648 
 649 	e_table_extras_add_cell (extras, "transparency", popup_cell);
 650 	g_object_unref (popup_cell);
 651 
 652 	/* Status field. */
 653 	cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
 654 	g_object_set (
 655 		cell,
 656 		"strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
 657 		"bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
 658 		"bg_color_column", E_CAL_MODEL_FIELD_COLOR,
 659 		"editable", FALSE,
 660 		NULL);
 661 
 662 	popup_cell = e_cell_combo_new ();
 663 	e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
 664 	g_object_unref (cell);
 665 
 666 	strings = NULL;
 667 	strings = g_list_append (strings, (gchar *) _("Not Started"));
 668 	strings = g_list_append (strings, (gchar *) _("In Progress"));
 669 	strings = g_list_append (strings, (gchar *) _("Completed"));
 670 	strings = g_list_append (strings, (gchar *) _("Canceled"));
 671 	e_cell_combo_set_popdown_strings (
 672 		E_CELL_COMBO (popup_cell),
 673 		strings);
 674 	g_list_free (strings);
 675 
 676 	e_table_extras_add_cell (extras, "calstatus", popup_cell);
 677 	g_object_unref (popup_cell);
 678 
 679 	e_table_extras_add_compare (
 680 		extras, "date-compare",
 681 		e_cell_date_edit_compare_cb);
 682 	e_table_extras_add_compare (
 683 		extras, "percent-compare",
 684 		task_table_percent_compare_cb);
 685 	e_table_extras_add_compare (
 686 		extras, "priority-compare",
 687 		task_table_priority_compare_cb);
 688 	e_table_extras_add_compare (
 689 		extras, "status-compare",
 690 		task_table_status_compare_cb);
 691 
 692 	/* Create pixmaps */
 693 
 694 	cell = e_cell_toggle_new (icon_names, G_N_ELEMENTS (icon_names));
 695 	e_table_extras_add_cell (extras, "icon", cell);
 696 	g_object_unref (cell);
 697 
 698 	e_table_extras_add_icon_name (extras, "icon", "stock_task");
 699 
 700 	e_table_extras_add_icon_name (extras, "complete", "stock_check-filled");
 701 
 702 	/* set proper format component for a default 'date' cell renderer */
 703 	cell = e_table_extras_get_cell (extras, "date");
 704 	e_cell_date_set_format_component (E_CELL_DATE (cell), "calendar");
 705 
 706 	/* Create the table */
 707 
 708 	etspecfile = g_build_filename (
 709 		EVOLUTION_ETSPECDIR, "e-calendar-table.etspec", NULL);
 710 	e_table_construct_from_spec_file (
 711 		E_TABLE (task_table), E_TABLE_MODEL (model),
 712 		extras, etspecfile, NULL);
 713 	g_free (etspecfile);
 714 
 715 	gtk_widget_set_has_tooltip (GTK_WIDGET (task_table), TRUE);
 716 
 717 	g_object_unref (extras);
 718 
 719 	a11y = gtk_widget_get_accessible (GTK_WIDGET (task_table));
 720 	if (a11y)
 721 		atk_object_set_name (a11y, _("Tasks"));
 722 
 723 	/* Chain up to parent's constructed() method. */
 724 	G_OBJECT_CLASS (e_task_table_parent_class)->constructed (object);
 725 }
 726 
 727 static gboolean
 728 task_table_popup_menu (GtkWidget *widget)
 729 {
 730 	ETaskTable *task_table;
 731 
 732 	task_table = E_TASK_TABLE (widget);
 733 	task_table_emit_popup_event (task_table, NULL);
 734 
 735 	return TRUE;
 736 }
 737 
 738 static gboolean
 739 task_table_query_tooltip (GtkWidget *widget,
 740                               gint x,
 741                               gint y,
 742                               gboolean keyboard_mode,
 743                               GtkTooltip *tooltip)
 744 {
 745 	ETaskTable *task_table;
 746 	ECalModel *model;
 747 	ECalModelComponent *comp_data;
 748 	gint row = -1, col = -1;
 749 	GtkWidget *box, *l, *w;
 750 	GtkStyle *style = gtk_widget_get_default_style ();
 751 	gchar *tmp;
 752 	const gchar *str;
 753 	GString *tmp2;
 754 	gchar buff[1001];
 755 	gboolean free_text = FALSE;
 756 	gboolean use_24_hour_format;
 757 	ECalComponent *new_comp;
 758 	ECalComponentOrganizer organizer;
 759 	ECalComponentDateTime dtstart, dtdue;
 760 	icalcomponent *clone;
 761 	icaltimezone *zone, *default_zone;
 762 	GSList *desc, *p;
 763 	gint len;
 764 	ESelectionModel *esm;
 765 	struct tm tmp_tm;
 766 
 767 	if (keyboard_mode)
 768 		return FALSE;
 769 
 770 	task_table = E_TASK_TABLE (widget);
 771 
 772 	e_table_get_mouse_over_cell (E_TABLE (task_table), &row, &col);
 773 	if (row == -1)
 774 		return FALSE;
 775 
 776 	/* Respect sorting option; the 'e_table_get_mouse_over_cell'
 777 	 * returns sorted row, not the model one. */
 778 	esm = e_table_get_selection_model (E_TABLE (task_table));
 779 	if (esm && esm->sorter && e_sorter_needs_sorting (esm->sorter))
 780 		row = e_sorter_sorted_to_model (esm->sorter, row);
 781 
 782 	model = e_task_table_get_model (task_table);
 783 	comp_data = e_cal_model_get_component_at (model, row);
 784 
 785 	if (!comp_data || !comp_data->icalcomp)
 786 		return FALSE;
 787 
 788 	new_comp = e_cal_component_new ();
 789 	clone = icalcomponent_new_clone (comp_data->icalcomp);
 790 	if (!e_cal_component_set_icalcomponent (new_comp, clone)) {
 791 		g_object_unref (new_comp);
 792 		return FALSE;
 793 	}
 794 
 795 	box = gtk_vbox_new (FALSE, 0);
 796 
 797 	str = e_calendar_view_get_icalcomponent_summary (
 798 		comp_data->client, comp_data->icalcomp, &free_text);
 799 	if (!(str && *str)) {
 800 		if (free_text)
 801 			g_free ((gchar *) str);
 802 		free_text = FALSE;
 803 		str = _("* No Summary *");
 804 	}
 805 
 806 	l = gtk_label_new (NULL);
 807 	tmp = g_markup_printf_escaped ("<b>%s</b>", str);
 808 	gtk_label_set_line_wrap (GTK_LABEL (l), TRUE);
 809 	gtk_label_set_markup (GTK_LABEL (l), tmp);
 810 	gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
 811 	w = gtk_event_box_new ();
 812 
 813 	gtk_widget_modify_bg (w, GTK_STATE_NORMAL, &(style->bg[GTK_STATE_SELECTED]));
 814 	gtk_widget_modify_fg (l, GTK_STATE_NORMAL, &(style->text[GTK_STATE_SELECTED]));
 815 	gtk_container_add (GTK_CONTAINER (w), l);
 816 	gtk_box_pack_start (GTK_BOX (box), w, TRUE, TRUE, 0);
 817 	g_free (tmp);
 818 
 819 	if (free_text)
 820 		g_free ((gchar *) str);
 821 	free_text = FALSE;
 822 
 823 	w = gtk_event_box_new ();
 824 	gtk_widget_modify_bg (w, GTK_STATE_NORMAL, &(style->bg[GTK_STATE_NORMAL]));
 825 
 826 	l = gtk_vbox_new (FALSE, 0);
 827 	gtk_container_add (GTK_CONTAINER (w), l);
 828 	gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
 829 	w = l;
 830 
 831 	e_cal_component_get_organizer (new_comp, &organizer);
 832 	if (organizer.cn) {
 833 		gchar *ptr;
 834 		ptr = strchr (organizer.value, ':');
 835 
 836 		if (ptr) {
 837 			ptr++;
 838 			/* To Translators: It will display
 839 			 * "Organizer: NameOfTheUser <email@ofuser.com>" */
 840 			tmp = g_strdup_printf (_("Organizer: %s <%s>"), organizer.cn, ptr);
 841 		} else {
 842 			/* With SunOne accounts, there may be no ':' in
 843 			 * organizer.value. */
 844 			tmp = g_strdup_printf (_("Organizer: %s"), organizer.cn);
 845 		}
 846 
 847 		l = gtk_label_new (tmp);
 848 		gtk_label_set_line_wrap (GTK_LABEL (l), FALSE);
 849 		gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
 850 		gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
 851 		g_free (tmp);
 852 
 853 		gtk_widget_modify_fg (l, GTK_STATE_NORMAL, &(style->text[GTK_STATE_NORMAL]));
 854 	}
 855 
 856 	e_cal_component_get_dtstart (new_comp, &dtstart);
 857 	e_cal_component_get_due (new_comp, &dtdue);
 858 
 859 	default_zone = e_cal_model_get_timezone (model);
 860 	use_24_hour_format = e_cal_model_get_use_24_hour_format (model);
 861 
 862 	if (dtstart.tzid) {
 863 		zone = icalcomponent_get_timezone (
 864 			e_cal_component_get_icalcomponent (new_comp),
 865 			dtstart.tzid);
 866 		if (!zone)
 867 			e_cal_client_get_timezone_sync (
 868 				comp_data->client, dtstart.tzid, &zone, NULL, NULL);
 869 		if (!zone)
 870 			zone = default_zone;
 871 	} else {
 872 		zone = NULL;
 873 	}
 874 
 875 	tmp2 = g_string_new ("");
 876 
 877 	if (dtstart.value) {
 878 		buff[0] = 0;
 879 
 880 		tmp_tm = icaltimetype_to_tm_with_zone (
 881 			dtstart.value, zone, default_zone);
 882 		e_time_format_date_and_time (
 883 			&tmp_tm, use_24_hour_format,
 884 			FALSE, FALSE, buff, 1000);
 885 
 886 		if (buff[0]) {
 887 			g_string_append (tmp2, _("Start: "));
 888 			g_string_append (tmp2, buff);
 889 		}
 890 	}
 891 
 892 	if (dtdue.value) {
 893 		buff[0] = 0;
 894 
 895 		tmp_tm = icaltimetype_to_tm_with_zone (
 896 			dtdue.value, zone, default_zone);
 897 		e_time_format_date_and_time (
 898 			&tmp_tm, use_24_hour_format,
 899 			FALSE, FALSE, buff, 1000);
 900 
 901 		if (buff[0]) {
 902 			if (tmp2->len)
 903 				g_string_append (tmp2, "; ");
 904 
 905 			g_string_append (tmp2, _("Due: "));
 906 			g_string_append (tmp2, buff);
 907 		}
 908 	}
 909 
 910 	if (tmp2->len) {
 911 		l = gtk_label_new (tmp2->str);
 912 		gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
 913 		gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
 914 
 915 		gtk_widget_modify_fg (l, GTK_STATE_NORMAL, &(style->text[GTK_STATE_NORMAL]));
 916 	}
 917 
 918 	g_string_free (tmp2, TRUE);
 919 
 920 	e_cal_component_free_datetime (&dtstart);
 921 	e_cal_component_free_datetime (&dtdue);
 922 
 923 	tmp = e_cal_model_get_attendees_status_info (
 924 		model, new_comp, comp_data->client);
 925 	if (tmp) {
 926 		l = gtk_label_new (tmp);
 927 		gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
 928 		gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
 929 
 930 		g_free (tmp);
 931 		tmp = NULL;
 932 
 933 		gtk_widget_modify_fg (l, GTK_STATE_NORMAL, &(style->text[GTK_STATE_NORMAL]));
 934 	}
 935 
 936 	tmp2 = g_string_new ("");
 937 	e_cal_component_get_description_list (new_comp, &desc);
 938 	for (len = 0, p = desc; p != NULL; p = p->next) {
 939 		ECalComponentText *text = p->data;
 940 
 941 		if (text->value != NULL) {
 942 			len += strlen (text->value);
 943 			g_string_append (tmp2, text->value);
 944 			if (len > 1024) {
 945 				g_string_set_size (tmp2, 1020);
 946 				g_string_append (tmp2, "...");
 947 				break;
 948 			}
 949 		}
 950 	}
 951 	e_cal_component_free_text_list (desc);
 952 
 953 	if (tmp2->len) {
 954 		l = gtk_label_new (tmp2->str);
 955 		gtk_label_set_line_wrap (GTK_LABEL (l), TRUE);
 956 		gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
 957 		gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
 958 
 959 		gtk_widget_modify_fg (l, GTK_STATE_NORMAL, &(style->text[GTK_STATE_NORMAL]));
 960 	}
 961 
 962 	g_string_free (tmp2, TRUE);
 963 
 964 	gtk_widget_show_all (box);
 965 	gtk_tooltip_set_custom (tooltip, box);
 966 
 967 	g_object_unref (new_comp);
 968 
 969 	return TRUE;
 970 }
 971 
 972 static void
 973 task_table_double_click (ETable *table,
 974                          gint row,
 975                          gint col,
 976                          GdkEvent *event)
 977 {
 978 	ETaskTable *task_table;
 979 	ECalModel *model;
 980 	ECalModelComponent *comp_data;
 981 
 982 	task_table = E_TASK_TABLE (table);
 983 	model = e_task_table_get_model (task_table);
 984 	comp_data = e_cal_model_get_component_at (model, row);
 985 	task_table_emit_open_component (task_table, comp_data);
 986 }
 987 
 988 static gint
 989 task_table_right_click (ETable *table,
 990                         gint row,
 991                         gint col,
 992                         GdkEvent *event)
 993 {
 994 	ETaskTable *task_table;
 995 
 996 	task_table = E_TASK_TABLE (table);
 997 	task_table_emit_popup_event (task_table, event);
 998 
 999 	return TRUE;
1000 }
1001 
1002 static void
1003 task_table_update_actions (ESelectable *selectable,
1004                            EFocusTracker *focus_tracker,
1005                            GdkAtom *clipboard_targets,
1006                            gint n_clipboard_targets)
1007 {
1008 	ETaskTable *task_table;
1009 	GtkAction *action;
1010 	GtkTargetList *target_list;
1011 	GSList *list, *iter;
1012 	gboolean can_paste = FALSE;
1013 	gboolean sources_are_editable = TRUE;
1014 	gboolean sensitive;
1015 	const gchar *tooltip;
1016 	gint n_selected;
1017 	gint ii;
1018 
1019 	task_table = E_TASK_TABLE (selectable);
1020 	n_selected = e_table_selected_count (E_TABLE (task_table));
1021 
1022 	list = e_task_table_get_selected (task_table);
1023 	for (iter = list; iter != NULL && sources_are_editable; iter = iter->next) {
1024 		ECalModelComponent *comp_data = iter->data;
1025 
1026 		sources_are_editable = sources_are_editable &&
1027 			!e_client_is_readonly (E_CLIENT (comp_data->client));
1028 	}
1029 	g_slist_free (list);
1030 
1031 	target_list = e_selectable_get_paste_target_list (selectable);
1032 	for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++)
1033 		can_paste = gtk_target_list_find (
1034 			target_list, clipboard_targets[ii], NULL);
1035 
1036 	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
1037 	sensitive = (n_selected > 0) && sources_are_editable;
1038 	tooltip = _("Cut selected tasks to the clipboard");
1039 	gtk_action_set_sensitive (action, sensitive);
1040 	gtk_action_set_tooltip (action, tooltip);
1041 
1042 	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
1043 	sensitive = (n_selected > 0);
1044 	tooltip = _("Copy selected tasks to the clipboard");
1045 	gtk_action_set_sensitive (action, sensitive);
1046 	gtk_action_set_tooltip (action, tooltip);
1047 
1048 	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
1049 	sensitive = sources_are_editable && can_paste;
1050 	tooltip = _("Paste tasks from the clipboard");
1051 	gtk_action_set_sensitive (action, sensitive);
1052 	gtk_action_set_tooltip (action, tooltip);
1053 
1054 	action = e_focus_tracker_get_delete_selection_action (focus_tracker);
1055 	sensitive = (n_selected > 0) && sources_are_editable;
1056 	tooltip = _("Delete selected tasks");
1057 	gtk_action_set_sensitive (action, sensitive);
1058 	gtk_action_set_tooltip (action, tooltip);
1059 
1060 	action = e_focus_tracker_get_select_all_action (focus_tracker);
1061 	sensitive = TRUE;
1062 	tooltip = _("Select all visible tasks");
1063 	gtk_action_set_sensitive (action, sensitive);
1064 	gtk_action_set_tooltip (action, tooltip);
1065 }
1066 
1067 static void
1068 task_table_cut_clipboard (ESelectable *selectable)
1069 {
1070 	ETaskTable *task_table;
1071 
1072 	task_table = E_TASK_TABLE (selectable);
1073 
1074 	e_selectable_copy_clipboard (selectable);
1075 	delete_selected_components (task_table);
1076 }
1077 
1078 /* Helper for task_table_copy_clipboard() */
1079 static void
1080 copy_row_cb (gint model_row,
1081              gpointer data)
1082 {
1083 	ETaskTable *task_table;
1084 	ECalModelComponent *comp_data;
1085 	ECalModel *model;
1086 	gchar *comp_str;
1087 	icalcomponent *child;
1088 
1089 	task_table = E_TASK_TABLE (data);
1090 
1091 	g_return_if_fail (task_table->tmp_vcal != NULL);
1092 
1093 	model = e_task_table_get_model (task_table);
1094 	comp_data = e_cal_model_get_component_at (model, model_row);
1095 	if (!comp_data)
1096 		return;
1097 
1098 	/* Add timezones to the VCALENDAR component. */
1099 	e_cal_util_add_timezones_from_component (
1100 		task_table->tmp_vcal, comp_data->icalcomp);
1101 
1102 	/* Add the new component to the VCALENDAR component. */
1103 	comp_str = icalcomponent_as_ical_string_r (comp_data->icalcomp);
1104 	child = icalparser_parse_string (comp_str);
1105 	if (child) {
1106 		icalcomponent_add_component (
1107 			task_table->tmp_vcal,
1108 			icalcomponent_new_clone (child));
1109 		icalcomponent_free (child);
1110 	}
1111 	g_free (comp_str);
1112 }
1113 
1114 static void
1115 task_table_copy_clipboard (ESelectable *selectable)
1116 {
1117 	ETaskTable *task_table;
1118 	GtkClipboard *clipboard;
1119 	gchar *comp_str;
1120 
1121 	task_table = E_TASK_TABLE (selectable);
1122 
1123 	/* Create a temporary VCALENDAR object. */
1124 	task_table->tmp_vcal = e_cal_util_new_top_level ();
1125 
1126 	e_table_selected_row_foreach (
1127 		E_TABLE (task_table), copy_row_cb, task_table);
1128 	comp_str = icalcomponent_as_ical_string_r (task_table->tmp_vcal);
1129 
1130 	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1131 	e_clipboard_set_calendar (clipboard, comp_str, -1);
1132 	gtk_clipboard_store (clipboard);
1133 
1134 	g_free (comp_str);
1135 
1136 	icalcomponent_free (task_table->tmp_vcal);
1137 	task_table->tmp_vcal = NULL;
1138 }
1139 
1140 /* Helper for calenable_table_paste_clipboard() */
1141 static void
1142 clipboard_get_calendar_data (ETaskTable *task_table,
1143                              const gchar *text)
1144 {
1145 	icalcomponent *icalcomp;
1146 	gchar *uid;
1147 	ECalComponent *comp;
1148 	ECalModel *model;
1149 	ECalClient *client;
1150 	icalcomponent_kind kind;
1151 	const gchar *status_message;
1152 
1153 	g_return_if_fail (E_IS_TASK_TABLE (task_table));
1154 
1155 	if (!text || !*text)
1156 		return;
1157 
1158 	icalcomp = icalparser_parse_string (text);
1159 	if (!icalcomp)
1160 		return;
1161 
1162 	/* check the type of the component */
1163 	kind = icalcomponent_isa (icalcomp);
1164 	if (kind != ICAL_VCALENDAR_COMPONENT &&
1165 	    kind != ICAL_VEVENT_COMPONENT &&
1166 	    kind != ICAL_VTODO_COMPONENT &&
1167 	    kind != ICAL_VJOURNAL_COMPONENT) {
1168 		return;
1169 	}
1170 
1171 	model = e_task_table_get_model (task_table);
1172 	client = e_cal_model_get_default_client (model);
1173 
1174 	status_message = _("Updating objects");
1175 	task_table_emit_status_message (task_table, status_message, -1.0);
1176 
1177 	if (kind == ICAL_VCALENDAR_COMPONENT) {
1178 		icalcomponent_kind child_kind;
1179 		icalcomponent *subcomp;
1180 		icalcomponent *vcal_comp;
1181 
1182 		vcal_comp = icalcomp;
1183 		subcomp = icalcomponent_get_first_component (
1184 			vcal_comp, ICAL_ANY_COMPONENT);
1185 		while (subcomp) {
1186 			child_kind = icalcomponent_isa (subcomp);
1187 			if (child_kind == ICAL_VEVENT_COMPONENT ||
1188 			    child_kind == ICAL_VTODO_COMPONENT ||
1189 			    child_kind == ICAL_VJOURNAL_COMPONENT) {
1190 				ECalComponent *tmp_comp;
1191 				GError *error = NULL;
1192 
1193 				uid = e_cal_component_gen_uid ();
1194 				tmp_comp = e_cal_component_new ();
1195 				e_cal_component_set_icalcomponent (
1196 					tmp_comp,
1197 					icalcomponent_new_clone (subcomp));
1198 				e_cal_component_set_uid (tmp_comp, uid);
1199 				g_free (uid);
1200 				uid = NULL;
1201 
1202 				/* FIXME should we convert start/due/complete
1203 				 * times?  Also, need error handling. */
1204 				if (!e_cal_client_create_object_sync (client, e_cal_component_get_icalcomponent (tmp_comp), &uid, NULL, &error))
1205 					uid = NULL;
1206 
1207 				if (error != NULL) {
1208 					g_warning (
1209 						"%s: Failed to create object: %s",
1210 						G_STRFUNC, error->message);
1211 					g_error_free (error);
1212 				}
1213 
1214 				g_object_unref (tmp_comp);
1215 				g_free (uid);
1216 			}
1217 			subcomp = icalcomponent_get_next_component (
1218 				vcal_comp, ICAL_ANY_COMPONENT);
1219 		}
1220 	} else {
1221 		GError *error = NULL;
1222 
1223 		comp = e_cal_component_new ();
1224 		e_cal_component_set_icalcomponent (comp, icalcomp);
1225 		uid = e_cal_component_gen_uid ();
1226 		e_cal_component_set_uid (comp, (const gchar *) uid);
1227 		g_free (uid);
1228 		uid = NULL;
1229 
1230 		if (!e_cal_client_create_object_sync (client, e_cal_component_get_icalcomponent (comp), &uid, NULL, &error))
1231 			uid = NULL;
1232 
1233 		if (error != NULL) {
1234 			g_warning (
1235 				"%s: Failed to create object: %s",
1236 				G_STRFUNC, error->message);
1237 			g_error_free (error);
1238 		}
1239 
1240 		g_object_unref (comp);
1241 		g_free (uid);
1242 	}
1243 
1244 	task_table_emit_status_message (task_table, NULL, -1.0);
1245 }
1246 
1247 static void
1248 task_table_paste_clipboard (ESelectable *selectable)
1249 {
1250 	ETaskTable *task_table;
1251 	GtkClipboard *clipboard;
1252 	GnomeCanvasItem *item;
1253 	GnomeCanvas *table_canvas;
1254 
1255 	task_table = E_TASK_TABLE (selectable);
1256 
1257 	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1258 
1259 	table_canvas = E_TABLE (task_table)->table_canvas;
1260 	item = table_canvas->focused_item;
1261 
1262 	/* XXX Should ECellText implement GtkEditable? */
1263 
1264 	/* Paste text into a cell being edited. */
1265 	if (gtk_clipboard_wait_is_text_available (clipboard) &&
1266 		gtk_widget_has_focus (GTK_WIDGET (table_canvas)) &&
1267 		E_IS_TABLE_ITEM (item) &&
1268 		E_TABLE_ITEM (item)->editing_col >= 0 &&
1269 		E_TABLE_ITEM (item)->editing_row >= 0) {
1270 
1271 		ETableItem *etable_item = E_TABLE_ITEM (item);
1272 
1273 		e_cell_text_paste_clipboard (
1274 			etable_item->cell_views[etable_item->editing_col],
1275 			etable_item->editing_col,
1276 			etable_item->editing_row);
1277 
1278 	/* Paste iCalendar data into the table. */
1279 	} else if (e_clipboard_wait_is_calendar_available (clipboard)) {
1280 		gchar *calendar_source;
1281 
1282 		calendar_source = e_clipboard_wait_for_calendar (clipboard);
1283 		clipboard_get_calendar_data (task_table, calendar_source);
1284 		g_free (calendar_source);
1285 	}
1286 }
1287 
1288 /* Used from e_table_selected_row_foreach(); puts the selected row number in an
1289  * gint pointed to by the closure data.
1290  */
1291 static void
1292 get_selected_row_cb (gint model_row,
1293                      gpointer data)
1294 {
1295 	gint *row;
1296 
1297 	row = data;
1298 	*row = model_row;
1299 }
1300 
1301 /*
1302  * Returns the component that is selected in the table; only works if there is
1303  * one and only one selected row.
1304  */
1305 static ECalModelComponent *
1306 get_selected_comp (ETaskTable *task_table)
1307 {
1308 	ECalModel *model;
1309 	gint row;
1310 
1311 	model = e_task_table_get_model (task_table);
1312 	if (e_table_selected_count (E_TABLE (task_table)) != 1)
1313 		return NULL;
1314 
1315 	row = -1;
1316 	e_table_selected_row_foreach (
1317 		E_TABLE (task_table), get_selected_row_cb, &row);
1318 	g_return_val_if_fail (row != -1, NULL);
1319 
1320 	return e_cal_model_get_component_at (model, row);
1321 }
1322 
1323 static void
1324 add_retract_data (ECalComponent *comp,
1325                   const gchar *retract_comment)
1326 {
1327 	icalcomponent *icalcomp = NULL;
1328 	icalproperty *icalprop = NULL;
1329 
1330 	icalcomp = e_cal_component_get_icalcomponent (comp);
1331 	if (retract_comment && *retract_comment)
1332 		icalprop = icalproperty_new_x (retract_comment);
1333 	else
1334 		icalprop = icalproperty_new_x ("0");
1335 	icalproperty_set_x_name (icalprop, "X-EVOLUTION-RETRACT-COMMENT");
1336 	icalcomponent_add_property (icalcomp, icalprop);
1337 }
1338 
1339 static gboolean
1340 check_for_retract (ECalComponent *comp,
1341                    ECalClient *client)
1342 {
1343 	ECalComponentOrganizer org;
1344 	gchar *email = NULL;
1345 	const gchar *strip = NULL;
1346 	gboolean ret_val;
1347 
1348 	if (!e_cal_component_has_attendees (comp))
1349 		return FALSE;
1350 
1351 	if (!e_cal_client_check_save_schedules (client))
1352 		return FALSE;
1353 
1354 	e_cal_component_get_organizer (comp, &org);
1355 	strip = itip_strip_mailto (org.value);
1356 
1357 	ret_val = e_client_get_backend_property_sync (
1358 		E_CLIENT (client),
1359 		CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS,
1360 		&email, NULL, NULL) && email != NULL &&
1361 		g_ascii_strcasecmp (email, strip) == 0;
1362 
1363 	g_free (email);
1364 
1365 	return ret_val;
1366 }
1367 
1368 static void
1369 task_table_delete_selection (ESelectable *selectable)
1370 {
1371 	ECalModel *model;
1372 	ETaskTable *task_table;
1373 	ECalModelComponent *comp_data;
1374 	ECalComponent *comp = NULL;
1375 	gboolean delete = TRUE;
1376 	gint n_selected;
1377 	GError *error = NULL;
1378 
1379 	task_table = E_TASK_TABLE (selectable);
1380 	model = e_task_table_get_model (task_table);
1381 
1382 	n_selected = e_table_selected_count (E_TABLE (task_table));
1383 	if (n_selected <= 0)
1384 		return;
1385 
1386 	if (n_selected == 1)
1387 		comp_data = get_selected_comp (task_table);
1388 	else
1389 		comp_data = NULL;
1390 
1391 	/* FIXME: this may be something other than a TODO component */
1392 
1393 	if (comp_data) {
1394 		comp = e_cal_component_new ();
1395 		e_cal_component_set_icalcomponent (
1396 			comp, icalcomponent_new_clone (comp_data->icalcomp));
1397 	}
1398 
1399 	if ((n_selected == 1) && comp && check_for_retract (comp, comp_data->client)) {
1400 		gchar *retract_comment = NULL;
1401 		gboolean retract = FALSE;
1402 
1403 		delete = prompt_retract_dialog (
1404 			comp, &retract_comment,
1405 			GTK_WIDGET (task_table), &retract);
1406 		if (retract) {
1407 			GSList *users = NULL;
1408 			icalcomponent *icalcomp = NULL, *mod_comp = NULL;
1409 
1410 			add_retract_data (comp, retract_comment);
1411 			icalcomp = e_cal_component_get_icalcomponent (comp);
1412 			icalcomponent_set_method (icalcomp, ICAL_METHOD_CANCEL);
1413 			if (!e_cal_client_send_objects_sync (comp_data->client, icalcomp, &users, &mod_comp, NULL, &error)) {
1414 				delete_error_dialog (error, E_CAL_COMPONENT_TODO);
1415 				g_clear_error (&error);
1416 				error = NULL;
1417 			} else {
1418 
1419 				if (mod_comp)
1420 					icalcomponent_free (mod_comp);
1421 
1422 				if (users) {
1423 					g_slist_foreach (users, (GFunc) g_free, NULL);
1424 					g_slist_free (users);
1425 				}
1426 			}
1427 
1428 		}
1429 	} else if (e_cal_model_get_confirm_delete (model))
1430 		delete = delete_component_dialog (
1431 			comp, FALSE, n_selected,
1432 			E_CAL_COMPONENT_TODO,
1433 			GTK_WIDGET (task_table));
1434 
1435 	if (delete)
1436 		delete_selected_components (task_table);
1437 
1438 	/* free memory */
1439 	if (comp)
1440 		g_object_unref (comp);
1441 }
1442 
1443 static void
1444 task_table_select_all (ESelectable *selectable)
1445 {
1446 	e_table_select_all (E_TABLE (selectable));
1447 }
1448 
1449 static void
1450 e_task_table_class_init (ETaskTableClass *class)
1451 {
1452 	GObjectClass *object_class;
1453 	GtkWidgetClass *widget_class;
1454 	ETableClass *table_class;
1455 
1456 	g_type_class_add_private (class, sizeof (ETaskTablePrivate));
1457 
1458 	object_class = G_OBJECT_CLASS (class);
1459 	object_class->set_property = task_table_set_property;
1460 	object_class->get_property = task_table_get_property;
1461 	object_class->dispose = task_table_dispose;
1462 	object_class->constructed = task_table_constructed;
1463 
1464 	widget_class = GTK_WIDGET_CLASS (class);
1465 	widget_class->popup_menu = task_table_popup_menu;
1466 	widget_class->query_tooltip = task_table_query_tooltip;
1467 
1468 	table_class = E_TABLE_CLASS (class);
1469 	table_class->double_click = task_table_double_click;
1470 	table_class->right_click = task_table_right_click;
1471 
1472 	/* Inherited from ESelectableInterface */
1473 	g_object_class_override_property (
1474 		object_class,
1475 		PROP_COPY_TARGET_LIST,
1476 		"copy-target-list");
1477 
1478 	g_object_class_install_property (
1479 		object_class,
1480 		PROP_MODEL,
1481 		g_param_spec_object (
1482 			"model",
1483 			"Model",
1484 			NULL,
1485 			E_TYPE_CAL_MODEL,
1486 			G_PARAM_READWRITE |
1487 			G_PARAM_CONSTRUCT_ONLY));
1488 
1489 	/* Inherited from ESelectableInterface */
1490 	g_object_class_override_property (
1491 		object_class,
1492 		PROP_PASTE_TARGET_LIST,
1493 		"paste-target-list");
1494 
1495 	g_object_class_install_property (
1496 		object_class,
1497 		PROP_SHELL_VIEW,
1498 		g_param_spec_object (
1499 			"shell-view",
1500 			"Shell View",
1501 			NULL,
1502 			E_TYPE_SHELL_VIEW,
1503 			G_PARAM_READWRITE |
1504 			G_PARAM_CONSTRUCT_ONLY));
1505 
1506 	signals[OPEN_COMPONENT] = g_signal_new (
1507 		"open-component",
1508 		G_TYPE_FROM_CLASS (class),
1509 		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1510 		G_STRUCT_OFFSET (ETaskTableClass, open_component),
1511 		NULL, NULL,
1512 		g_cclosure_marshal_VOID__OBJECT,
1513 		G_TYPE_NONE, 1,
1514 		E_TYPE_CAL_MODEL_COMPONENT);
1515 
1516 	signals[POPUP_EVENT] = g_signal_new (
1517 		"popup-event",
1518 		G_TYPE_FROM_CLASS (class),
1519 		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1520 		G_STRUCT_OFFSET (ETaskTableClass, popup_event),
1521 		NULL, NULL,
1522 		g_cclosure_marshal_VOID__BOXED,
1523 		G_TYPE_NONE, 1,
1524 		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
1525 
1526 	signals[STATUS_MESSAGE] = g_signal_new (
1527 		"status-message",
1528 		G_TYPE_FROM_CLASS (class),
1529 		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1530 		G_STRUCT_OFFSET (ETaskTableClass, status_message),
1531 		NULL, NULL,
1532 		e_marshal_VOID__STRING_DOUBLE,
1533 		G_TYPE_NONE, 2,
1534 		G_TYPE_STRING, G_TYPE_DOUBLE);
1535 }
1536 
1537 static void
1538 e_task_table_init (ETaskTable *task_table)
1539 {
1540 	GtkTargetList *target_list;
1541 
1542 	task_table->priv = E_TASK_TABLE_GET_PRIVATE (task_table);
1543 
1544 	task_table->priv->completed_cancellable = NULL;
1545 
1546 	target_list = gtk_target_list_new (NULL, 0);
1547 	e_target_list_add_calendar_targets (target_list, 0);
1548 	task_table->priv->copy_target_list = target_list;
1549 
1550 	target_list = gtk_target_list_new (NULL, 0);
1551 	e_target_list_add_calendar_targets (target_list, 0);
1552 	task_table->priv->paste_target_list = target_list;
1553 }
1554 
1555 static void
1556 e_task_table_selectable_init (ESelectableInterface *interface)
1557 {
1558 	interface->update_actions = task_table_update_actions;
1559 	interface->cut_clipboard = task_table_cut_clipboard;
1560 	interface->copy_clipboard = task_table_copy_clipboard;
1561 	interface->paste_clipboard = task_table_paste_clipboard;
1562 	interface->delete_selection = task_table_delete_selection;
1563 	interface->select_all = task_table_select_all;
1564 }
1565 
1566 /**
1567  * e_task_table_new:
1568  * @shell_view: an #EShellView
1569  * @model: an #ECalModel for the table
1570  *
1571  * Returns a new #ETaskTable.
1572  *
1573  * Returns: a new #ETaskTable
1574  **/
1575 GtkWidget *
1576 e_task_table_new (EShellView *shell_view,
1577                   ECalModel *model)
1578 {
1579 	g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1580 	g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
1581 
1582 	return g_object_new (
1583 		E_TYPE_TASK_TABLE,
1584 		"model", model, "shell-view", shell_view, NULL);
1585 }
1586 
1587 /**
1588  * e_task_table_get_model:
1589  * @task_table: A calendar table.
1590  *
1591  * Queries the calendar data model that a calendar table is using.
1592  *
1593  * Return value: A calendar model.
1594  **/
1595 ECalModel *
1596 e_task_table_get_model (ETaskTable *task_table)
1597 {
1598 	g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL);
1599 
1600 	return task_table->priv->model;
1601 }
1602 
1603 EShellView *
1604 e_task_table_get_shell_view (ETaskTable *task_table)
1605 {
1606 	g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL);
1607 
1608 	return task_table->priv->shell_view;
1609 }
1610 
1611 struct get_selected_uids_closure {
1612 	ETaskTable *task_table;
1613 	GSList *objects;
1614 };
1615 
1616 /* Used from e_table_selected_row_foreach(), builds a list of the selected UIDs */
1617 static void
1618 add_uid_cb (gint model_row,
1619             gpointer data)
1620 {
1621 	struct get_selected_uids_closure *closure = data;
1622 	ECalModelComponent *comp_data;
1623 	ECalModel *model;
1624 
1625 	model = e_task_table_get_model (closure->task_table);
1626 	comp_data = e_cal_model_get_component_at (model, model_row);
1627 
1628 	closure->objects = g_slist_prepend (closure->objects, comp_data);
1629 }
1630 
1631 /**
1632  * e_task_table_get_selected:
1633  * @task_table:
1634  *
1635  * Get the currently selected ECalModelComponent's on the table.
1636  *
1637  * Return value: A GSList of the components, which should be
1638  * g_slist_free'd when finished with.
1639  **/
1640 GSList *
1641 e_task_table_get_selected (ETaskTable *task_table)
1642 {
1643 	struct get_selected_uids_closure closure;
1644 
1645 	closure.task_table = task_table;
1646 	closure.objects = NULL;
1647 
1648 	e_table_selected_row_foreach (
1649 		E_TABLE (task_table), add_uid_cb, &closure);
1650 
1651 	return closure.objects;
1652 }
1653 
1654 GtkTargetList *
1655 e_task_table_get_copy_target_list (ETaskTable *task_table)
1656 {
1657 	g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL);
1658 
1659 	return task_table->priv->copy_target_list;
1660 }
1661 
1662 GtkTargetList *
1663 e_task_table_get_paste_target_list (ETaskTable *task_table)
1664 {
1665 	g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL);
1666 
1667 	return task_table->priv->paste_target_list;
1668 }
1669 
1670 static void
1671 task_table_get_object_list_async (GList *clients_list,
1672                                   const gchar *sexp,
1673                                   GCancellable *cancellable,
1674                                   GAsyncReadyCallback callback,
1675                                   gpointer callback_data)
1676 {
1677 	GList *l;
1678 
1679 	for (l = clients_list; l != NULL; l = l->next) {
1680 		ECalClient *client = l->data;
1681 
1682 		e_cal_client_get_object_list (
1683 			client, sexp, cancellable,
1684 			callback, callback_data);
1685 	}
1686 }
1687 
1688 static void
1689 hide_completed_rows_ready (GObject *source_object,
1690                            GAsyncResult *result,
1691                            gpointer user_data)
1692 {
1693 	ECalModel *model = user_data;
1694 	GSList *m, *objects;
1695 	gboolean changed = FALSE;
1696 	gint pos;
1697 	GPtrArray *comp_objects;
1698 	GError *error = NULL;
1699 
1700 	e_cal_client_get_object_list_finish (
1701 		E_CAL_CLIENT (source_object), result, &objects, &error);
1702 
1703 	if (error != NULL) {
1704 		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
1705 		    !g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
1706 			ESource *source = e_client_get_source (E_CLIENT (source_object));
1707 
1708 			g_debug (
1709 				"%s: Could not get the objects from '%s': %s",
1710 				G_STRFUNC,
1711 				e_source_get_display_name (source),
1712 				error->message);
1713 		}
1714 		g_error_free (error);
1715 		return;
1716 	}
1717 
1718 	comp_objects = e_cal_model_get_object_array (model);
1719 	g_return_if_fail (comp_objects != NULL);
1720 
1721 	for (m = objects; m; m = m->next) {
1722 		ECalModelComponent *comp_data;
1723 		ECalComponentId *id;
1724 		ECalComponent *comp = e_cal_component_new ();
1725 
1726 		e_cal_component_set_icalcomponent (
1727 			comp, icalcomponent_new_clone (m->data));
1728 		id = e_cal_component_get_id (comp);
1729 
1730 		comp_data = e_cal_model_get_component_for_uid (model, id);
1731 		if (comp_data != NULL) {
1732 			e_table_model_pre_change (E_TABLE_MODEL (model));
1733 			pos = get_position_in_array (
1734 				comp_objects, comp_data);
1735 			e_table_model_row_deleted (
1736 				E_TABLE_MODEL (model), pos);
1737 			changed = TRUE;
1738 
1739 			if (g_ptr_array_remove (comp_objects, comp_data))
1740 				g_object_unref (comp_data);
1741 		}
1742 		e_cal_component_free_id (id);
1743 		g_object_unref (comp);
1744 	}
1745 
1746 	e_cal_client_free_icalcomp_slist (objects);
1747 
1748 	if (changed) {
1749 		/* To notify about changes, because in call of
1750 		 * row_deleted there are still all events. */
1751 		e_table_model_changed (E_TABLE_MODEL (model));
1752 	}
1753 }
1754 
1755 static void
1756 show_completed_rows_ready (GObject *source_object,
1757                            GAsyncResult *result,
1758                            gpointer user_data)
1759 {
1760 	ECalClient *client;
1761 	ECalModel *model = user_data;
1762 	GSList *m, *objects;
1763 	GPtrArray *comp_objects;
1764 	GError *error = NULL;
1765 
1766 	e_cal_client_get_object_list_finish (
1767 		E_CAL_CLIENT (source_object), result, &objects, &error);
1768 
1769 	if (error != NULL) {
1770 		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
1771 		    !g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
1772 			ESource *source = e_client_get_source (E_CLIENT (source_object));
1773 
1774 			g_debug (
1775 				"%s: Could not get the objects from '%s': %s",
1776 				G_STRFUNC,
1777 				e_source_get_display_name (source),
1778 				error->message);
1779 		}
1780 		g_error_free (error);
1781 		return;
1782 	}
1783 
1784 	client = E_CAL_CLIENT (source_object);
1785 	g_return_if_fail (client != NULL);
1786 
1787 	comp_objects = e_cal_model_get_object_array (model);
1788 	g_return_if_fail (comp_objects != NULL);
1789 
1790 	for (m = objects; m; m = m->next) {
1791 		ECalModelComponent *comp_data;
1792 		ECalComponentId *id;
1793 		ECalComponent *comp = e_cal_component_new ();
1794 
1795 		e_cal_component_set_icalcomponent (
1796 			comp, icalcomponent_new_clone (m->data));
1797 		id = e_cal_component_get_id (comp);
1798 
1799 		if (!(e_cal_model_get_component_for_uid (model, id))) {
1800 			e_table_model_pre_change (E_TABLE_MODEL (model));
1801 			comp_data = g_object_new (
1802 				E_TYPE_CAL_MODEL_COMPONENT, NULL);
1803 			comp_data->client = g_object_ref (client);
1804 			comp_data->icalcomp =
1805 				icalcomponent_new_clone (m->data);
1806 			e_cal_model_set_instance_times (
1807 				comp_data,
1808 				e_cal_model_get_timezone (model));
1809 			comp_data->dtstart = NULL;
1810 			comp_data->dtend = NULL;
1811 			comp_data->due = NULL;
1812 			comp_data->completed = NULL;
1813 			comp_data->color = NULL;
1814 
1815 			g_ptr_array_add (comp_objects, comp_data);
1816 			e_table_model_row_inserted (
1817 				E_TABLE_MODEL (model),
1818 				comp_objects->len - 1);
1819 		}
1820 		e_cal_component_free_id (id);
1821 		g_object_unref (comp);
1822 	}
1823 
1824 	e_cal_client_free_icalcomp_slist (objects);
1825 }
1826 
1827 /* Returns the current time, for the ECellDateEdit items.
1828  * FIXME: Should probably use the timezone of the item rather than the
1829  * current timezone, though that may be difficult to get from here. */
1830 static struct tm
1831 e_task_table_get_current_time (ECellDateEdit *ecde,
1832                                gpointer data)
1833 {
1834 	ETaskTable *task_table = data;
1835 	ECalModel *model;
1836 	icaltimezone *zone;
1837 	struct tm tmp_tm = { 0 };
1838 	struct icaltimetype tt;
1839 
1840 	/* Get the current timezone. */
1841 	model = e_task_table_get_model (task_table);
1842 	zone = e_cal_model_get_timezone (model);
1843 
1844 	tt = icaltime_from_timet_with_zone (time (NULL), FALSE, zone);
1845 
1846 	/* Now copy it to the struct tm and return it. */
1847 	tmp_tm.tm_year  = tt.year - 1900;
1848 	tmp_tm.tm_mon   = tt.month - 1;
1849 	tmp_tm.tm_mday  = tt.day;
1850 	tmp_tm.tm_hour  = tt.hour;
1851 	tmp_tm.tm_min   = tt.minute;
1852 	tmp_tm.tm_sec   = tt.second;
1853 	tmp_tm.tm_isdst = -1;
1854 
1855 	return tmp_tm;
1856 }
1857 
1858 /**
1859  * e_task_table_hide_completed_tasks:
1860  * @table: A calendar table model.
1861  * @client_list: Clients List
1862  *
1863  * Hide completed tasks.
1864  */
1865 void
1866 e_task_table_process_completed_tasks (ETaskTable *task_table,
1867                                       GList *clients_list,
1868                                       gboolean config_changed)
1869 {
1870 	ECalModel *model;
1871 	GCancellable *cancellable;
1872 	gchar *hide_sexp, *show_sexp;
1873 
1874 	if (task_table->priv->completed_cancellable) {
1875 		g_cancellable_cancel (task_table->priv->completed_cancellable);
1876 		g_object_unref (task_table->priv->completed_cancellable);
1877 	}
1878 
1879 	task_table->priv->completed_cancellable = g_cancellable_new ();
1880 	cancellable = task_table->priv->completed_cancellable;
1881 
1882 	model = e_task_table_get_model (task_table);
1883 	hide_sexp = calendar_config_get_hide_completed_tasks_sexp (TRUE);
1884 	show_sexp = calendar_config_get_hide_completed_tasks_sexp (FALSE);
1885 
1886 	/* If hide option is unchecked */
1887 	if (!(hide_sexp && show_sexp))
1888 		show_sexp = g_strdup ("(is-completed?)");
1889 
1890 	/* Delete rows from model*/
1891 	if (hide_sexp) {
1892 		task_table_get_object_list_async (
1893 			clients_list, hide_sexp, cancellable,
1894 			hide_completed_rows_ready, model);
1895 	}
1896 
1897 	/* Insert rows into model */
1898 	if (config_changed) {
1899 		task_table_get_object_list_async (
1900 			clients_list, show_sexp, cancellable,
1901 			show_completed_rows_ready, model);
1902 	}
1903 
1904 	g_free (hide_sexp);
1905 	g_free (show_sexp);
1906 }