evolution-3.6.4/modules/calendar/e-task-shell-sidebar.c

No issues found

   1 /*
   2  * e-task-shell-sidebar.c
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) version 3.
   8  *
   9  * This program is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  16  *
  17  *
  18  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  19  *
  20  */
  21 
  22 #ifdef HAVE_CONFIG_H
  23 #include <config.h>
  24 #endif
  25 
  26 #include "e-task-shell-sidebar.h"
  27 
  28 #include <string.h>
  29 #include <glib/gi18n.h>
  30 #include <libedataserverui/libedataserverui.h>
  31 
  32 #include "libevolution-utils/e-alert-dialog.h"
  33 #include "e-util/e-util.h"
  34 #include "calendar/gui/e-task-list-selector.h"
  35 #include "calendar/gui/misc.h"
  36 
  37 #include "e-task-shell-view.h"
  38 #include "e-task-shell-backend.h"
  39 #include "e-task-shell-content.h"
  40 
  41 #define E_TASK_SHELL_SIDEBAR_GET_PRIVATE(obj) \
  42 	(G_TYPE_INSTANCE_GET_PRIVATE \
  43 	((obj), E_TYPE_TASK_SHELL_SIDEBAR, ETaskShellSidebarPrivate))
  44 
  45 struct _ETaskShellSidebarPrivate {
  46 	GtkWidget *selector;
  47 
  48 	/* UID -> Client */
  49 	GHashTable *client_table;
  50 
  51 	/* The default client is for ECalModel.  It follows the
  52 	 * sidebar's primary selection, even if the highlighted
  53 	 * source is not selected.  The tricky part is we don't
  54 	 * update the property until the client is successfully
  55 	 * opened.  So the user first highlights a source, then
  56 	 * sometime later we update our default-client property
  57 	 * which is bound by an EBinding to ECalModel. */
  58 	ECalClient *default_client;
  59 
  60 	ESource *loading_default_source_instance; /* not-reffed, only for comparison */
  61 
  62 	GCancellable *loading_default_client;
  63 	GCancellable *loading_clients;
  64 };
  65 
  66 enum {
  67 	PROP_0,
  68 	PROP_DEFAULT_CLIENT,
  69 	PROP_SELECTOR
  70 };
  71 
  72 enum {
  73 	CLIENT_ADDED,
  74 	CLIENT_REMOVED,
  75 	STATUS_MESSAGE,
  76 	LAST_SIGNAL
  77 };
  78 
  79 static guint signals[LAST_SIGNAL];
  80 
  81 G_DEFINE_DYNAMIC_TYPE (
  82 	ETaskShellSidebar,
  83 	e_task_shell_sidebar,
  84 	E_TYPE_SHELL_SIDEBAR)
  85 
  86 static void
  87 task_shell_sidebar_emit_client_added (ETaskShellSidebar *task_shell_sidebar,
  88                                       ECalClient *client)
  89 {
  90 	guint signal_id = signals[CLIENT_ADDED];
  91 
  92 	g_signal_emit (task_shell_sidebar, signal_id, 0, client);
  93 }
  94 
  95 static void
  96 task_shell_sidebar_emit_client_removed (ETaskShellSidebar *task_shell_sidebar,
  97                                         ECalClient *client)
  98 {
  99 	guint signal_id = signals[CLIENT_REMOVED];
 100 
 101 	g_signal_emit (task_shell_sidebar, signal_id, 0, client);
 102 }
 103 
 104 static void
 105 task_shell_sidebar_emit_status_message (ETaskShellSidebar *task_shell_sidebar,
 106                                         const gchar *status_message)
 107 {
 108 	guint signal_id = signals[STATUS_MESSAGE];
 109 
 110 	g_signal_emit (task_shell_sidebar, signal_id, 0, status_message, -1.0);
 111 }
 112 
 113 static void
 114 task_shell_sidebar_backend_died_cb (ETaskShellSidebar *task_shell_sidebar,
 115                                     ECalClient *client)
 116 {
 117 	EShellView *shell_view;
 118 	EShellContent *shell_content;
 119 	EShellSidebar *shell_sidebar;
 120 	GHashTable *client_table;
 121 	ESource *source;
 122 	const gchar *uid;
 123 
 124 	client_table = task_shell_sidebar->priv->client_table;
 125 
 126 	shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar);
 127 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
 128 	shell_content = e_shell_view_get_shell_content (shell_view);
 129 
 130 	source = e_client_get_source (E_CLIENT (client));
 131 	uid = e_source_get_uid (source);
 132 
 133 	g_object_ref (source);
 134 
 135 	g_hash_table_remove (client_table, uid);
 136 	task_shell_sidebar_emit_status_message (task_shell_sidebar, NULL);
 137 
 138 	e_alert_submit (
 139 		E_ALERT_SINK (shell_content),
 140 		"calendar:tasks-crashed", NULL);
 141 
 142 	g_object_unref (source);
 143 }
 144 
 145 static void
 146 task_shell_sidebar_backend_error_cb (ETaskShellSidebar *task_shell_sidebar,
 147                                      const gchar *message,
 148                                      ECalClient *client)
 149 {
 150 	EShell *shell;
 151 	EShellView *shell_view;
 152 	EShellBackend *shell_backend;
 153 	EShellContent *shell_content;
 154 	EShellSidebar *shell_sidebar;
 155 	ESourceRegistry *registry;
 156 	ESource *parent;
 157 	ESource *source;
 158 	const gchar *parent_uid;
 159 	const gchar *parent_display_name;
 160 	const gchar *source_display_name;
 161 
 162 	shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar);
 163 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
 164 	shell_backend = e_shell_view_get_shell_backend (shell_view);
 165 	shell_content = e_shell_view_get_shell_content (shell_view);
 166 
 167 	shell = e_shell_backend_get_shell (shell_backend);
 168 	registry = e_shell_get_registry (shell);
 169 
 170 	source = e_client_get_source (E_CLIENT (client));
 171 
 172 	parent_uid = e_source_get_parent (source);
 173 	parent = e_source_registry_ref_source (registry, parent_uid);
 174 	g_return_if_fail (parent != NULL);
 175 
 176 	parent_display_name = e_source_get_display_name (parent);
 177 	source_display_name = e_source_get_display_name (source);
 178 
 179 	e_alert_submit (
 180 		E_ALERT_SINK (shell_content),
 181 		"calendar:backend-error",
 182 		parent_display_name,
 183 		source_display_name,
 184 		message, NULL);
 185 
 186 	g_object_unref (parent);
 187 }
 188 
 189 static void
 190 task_shell_sidebar_retrieve_capabilies_cb (GObject *source_object,
 191                                            GAsyncResult *result,
 192                                            gpointer user_data)
 193 {
 194 	ECalClient *client = E_CAL_CLIENT (source_object);
 195 	ETaskShellSidebar *task_shell_sidebar = user_data;
 196 	gchar *capabilities = NULL;
 197 
 198 	g_return_if_fail (client != NULL);
 199 	g_return_if_fail (task_shell_sidebar != NULL);
 200 
 201 	e_client_retrieve_capabilities_finish (
 202 		E_CLIENT (client), result, &capabilities, NULL);
 203 	g_free (capabilities);
 204 
 205 	task_shell_sidebar_emit_status_message (
 206 		task_shell_sidebar, _("Loading tasks"));
 207 	task_shell_sidebar_emit_client_added (task_shell_sidebar, client);
 208 	task_shell_sidebar_emit_status_message (task_shell_sidebar, NULL);
 209 }
 210 
 211 static gboolean task_shell_sidebar_retry_open_timeout_cb (gpointer user_data);
 212 
 213 struct RetryOpenData
 214 {
 215 	EClient *client;
 216 	ETaskShellSidebar *task_shell_sidebar;
 217 	GCancellable *cancellable;
 218 };
 219 
 220 static void
 221 free_retry_open_data (gpointer data)
 222 {
 223 	struct RetryOpenData *rod = data;
 224 
 225 	if (!rod)
 226 		return;
 227 
 228 	g_object_unref (rod->client);
 229 	g_object_unref (rod->cancellable);
 230 	g_free (rod);
 231 }
 232 
 233 static void
 234 task_shell_sidebar_client_opened_cb (GObject *source_object,
 235                                      GAsyncResult *result,
 236                                      gpointer user_data)
 237 {
 238 	ECalClient *client = E_CAL_CLIENT (source_object);
 239 	ETaskShellSidebar *task_shell_sidebar = user_data;
 240 	ESource *source;
 241 	EShellView *shell_view;
 242 	EShellContent *shell_content;
 243 	EShellSidebar *shell_sidebar;
 244 	GError *error = NULL;
 245 
 246 	source = e_client_get_source (E_CLIENT (client));
 247 
 248 	e_client_open_finish (E_CLIENT (client), result, &error);
 249 
 250 	if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
 251 	    g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
 252 		g_clear_error (&error);
 253 		return;
 254 	}
 255 
 256 	if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_BUSY)) {
 257 		struct RetryOpenData *rod;
 258 
 259 		rod = g_new0 (struct RetryOpenData, 1);
 260 		rod->client = g_object_ref (client);
 261 		rod->task_shell_sidebar = task_shell_sidebar;
 262 		rod->cancellable = g_object_ref (task_shell_sidebar->priv->loading_clients);
 263 
 264 		/* postpone for 1/2 of a second, backend is busy now */
 265 		g_timeout_add_full (
 266 			G_PRIORITY_DEFAULT, 500,
 267 			task_shell_sidebar_retry_open_timeout_cb,
 268 			rod, free_retry_open_data);
 269 
 270 		g_clear_error (&error);
 271 		return;
 272 	}
 273 
 274 	shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar);
 275 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
 276 	shell_content = e_shell_view_get_shell_content (shell_view);
 277 
 278 	/* Handle errors. */
 279 	switch ((error && error->domain == E_CLIENT_ERROR) ? error->code : -1) {
 280 		case -1:
 281 			break;
 282 
 283 		case E_CLIENT_ERROR_BUSY:
 284 			g_warning (
 285 				"%s: Cannot open '%s', it's busy (%s)",
 286 				G_STRFUNC, e_source_get_display_name (source),
 287 				error->message);
 288 			g_clear_error (&error);
 289 			return;
 290 
 291 		case E_CLIENT_ERROR_REPOSITORY_OFFLINE:
 292 			e_alert_submit (
 293 				E_ALERT_SINK (shell_content),
 294 				"calendar:prompt-no-contents-offline-tasks",
 295 				NULL);
 296 			/* fall through */
 297 
 298 		default:
 299 			if (error->code != E_CLIENT_ERROR_REPOSITORY_OFFLINE) {
 300 				e_alert_submit (
 301 					E_ALERT_SINK (shell_content),
 302 					"calendar:failed-open-tasks",
 303 					error->message, NULL);
 304 			}
 305 
 306 			e_task_shell_sidebar_remove_source (
 307 				task_shell_sidebar,
 308 				e_client_get_source (E_CLIENT (client)));
 309 			g_clear_error (&error);
 310 			return;
 311 	}
 312 
 313 	g_clear_error (&error);
 314 
 315 	/* to have them ready for later use */
 316 	e_client_retrieve_capabilities (
 317 		E_CLIENT (client), NULL,
 318 		task_shell_sidebar_retrieve_capabilies_cb,
 319 		task_shell_sidebar);
 320 }
 321 
 322 static gboolean
 323 task_shell_sidebar_retry_open_timeout_cb (gpointer user_data)
 324 {
 325 	struct RetryOpenData *rod = user_data;
 326 
 327 	g_return_val_if_fail (rod != NULL, FALSE);
 328 	g_return_val_if_fail (rod->client != NULL, FALSE);
 329 	g_return_val_if_fail (rod->task_shell_sidebar != NULL, FALSE);
 330 	g_return_val_if_fail (rod->cancellable != NULL, FALSE);
 331 
 332 	if (g_cancellable_is_cancelled (rod->cancellable))
 333 		return FALSE;
 334 
 335 	e_client_open (
 336 		rod->client, FALSE,
 337 		rod->task_shell_sidebar->priv->loading_clients,
 338 		task_shell_sidebar_client_opened_cb,
 339 		rod->task_shell_sidebar);
 340 
 341 	return FALSE;
 342 }
 343 
 344 static void
 345 task_shell_sidebar_default_loaded_cb (GObject *source_object,
 346                                       GAsyncResult *result,
 347                                       gpointer user_data)
 348 {
 349 	ESource *source = E_SOURCE (source_object);
 350 	EShellSidebar *shell_sidebar = user_data;
 351 	ETaskShellSidebarPrivate *priv;
 352 	EShellContent *shell_content;
 353 	EShellView *shell_view;
 354 	ETaskShellContent *task_shell_content;
 355 	ECalModel *model;
 356 	EClient *client = NULL;
 357 	GError *error = NULL;
 358 
 359 	priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (shell_sidebar);
 360 
 361 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
 362 	shell_content = e_shell_view_get_shell_content (shell_view);
 363 	task_shell_content = E_TASK_SHELL_CONTENT (shell_content);
 364 	model = e_task_shell_content_get_task_model (task_shell_content);
 365 
 366 	e_client_utils_open_new_finish (source, result, &client, &error);
 367 
 368 	if (priv->loading_default_client) {
 369 		g_object_unref (priv->loading_default_client);
 370 		priv->loading_default_client = NULL;
 371 	}
 372 
 373 	if (source == priv->loading_default_source_instance)
 374 		priv->loading_default_source_instance = NULL;
 375 
 376 	/* Ignore cancellations. */
 377 	if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
 378 	    g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
 379 		g_warn_if_fail (client == NULL);
 380 		g_error_free (error);
 381 		goto exit;
 382 
 383 	} else if (error != NULL) {
 384 		g_warn_if_fail (client == NULL);
 385 		e_alert_submit (
 386 			E_ALERT_SINK (shell_content),
 387 			"calendar:failed-open-tasks",
 388 			error->message, NULL);
 389 		g_error_free (error);
 390 		goto exit;
 391 	}
 392 
 393 	g_return_if_fail (E_IS_CAL_CLIENT (client));
 394 
 395 	if (priv->default_client != NULL)
 396 		g_object_unref (priv->default_client);
 397 
 398 	priv->default_client = E_CAL_CLIENT (client);
 399 
 400 	e_cal_client_set_default_timezone (
 401 		priv->default_client, e_cal_model_get_timezone (model));
 402 
 403 	g_object_notify (G_OBJECT (shell_sidebar), "default-client");
 404 
 405  exit:
 406 	g_object_unref (shell_sidebar);
 407 }
 408 
 409 static void
 410 task_shell_sidebar_set_default (ETaskShellSidebar *task_shell_sidebar,
 411                                 ESource *source)
 412 {
 413 	ETaskShellSidebarPrivate *priv;
 414 	EShellSidebar *shell_sidebar;
 415 	ECalClient *client;
 416 	const gchar *uid;
 417 
 418 	priv = task_shell_sidebar->priv;
 419 
 420 	/* already loading that source as default source */
 421 	if (source == priv->loading_default_source_instance)
 422 		return;
 423 
 424 	/* FIXME Sidebar should not be accessing the EShellContent.
 425 	 *       This probably needs to be moved to ETaskShellView. */
 426 	shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar);
 427 
 428 	/* Cancel any unfinished previous request. */
 429 	if (priv->loading_default_client != NULL) {
 430 		g_cancellable_cancel (priv->loading_default_client);
 431 		g_object_unref (priv->loading_default_client);
 432 		priv->loading_default_client = NULL;
 433 	}
 434 
 435 	uid = e_source_get_uid (source);
 436 	client = g_hash_table_lookup (priv->client_table, uid);
 437 
 438 	/* If we already have an open connection for
 439 	 * this UID, we can finish immediately. */
 440 	if (client != NULL) {
 441 		if (priv->default_client != NULL)
 442 			g_object_unref (priv->default_client);
 443 		priv->default_client = g_object_ref (client);
 444 		g_object_notify (G_OBJECT (shell_sidebar), "default-client");
 445 		return;
 446 	}
 447 
 448 	/* it's only for pointer comparison, no need to ref it */
 449 	priv->loading_default_source_instance = source;
 450 	priv->loading_default_client = g_cancellable_new ();
 451 
 452 	e_client_utils_open_new (
 453 		source, E_CLIENT_SOURCE_TYPE_TASKS,
 454 		FALSE, priv->loading_default_client,
 455 		task_shell_sidebar_default_loaded_cb,
 456 		g_object_ref (shell_sidebar));
 457 }
 458 
 459 static void
 460 task_shell_sidebar_row_changed_cb (ETaskShellSidebar *task_shell_sidebar,
 461                                    GtkTreePath *tree_path,
 462                                    GtkTreeIter *tree_iter,
 463                                    GtkTreeModel *tree_model)
 464 {
 465 	ESourceSelector *selector;
 466 	ESource *source;
 467 
 468 	selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
 469 	source = e_source_selector_ref_source_by_path (selector, tree_path);
 470 
 471 	/* XXX This signal gets emitted a lot while the model is being
 472 	 *     rebuilt, during which time we won't get a valid ESource.
 473 	 *     ESourceSelector should probably block this signal while
 474 	 *     rebuilding the model, but we'll be forgiving and not
 475 	 *     emit a warning. */
 476 	if (source == NULL)
 477 		return;
 478 
 479 	if (e_source_selector_source_is_selected (selector, source))
 480 		e_task_shell_sidebar_add_source (task_shell_sidebar, source);
 481 	else
 482 		e_task_shell_sidebar_remove_source (task_shell_sidebar, source);
 483 
 484 	g_object_unref (source);
 485 }
 486 
 487 static void
 488 task_shell_sidebar_primary_selection_changed_cb (ETaskShellSidebar *task_shell_sidebar,
 489                                                  ESourceSelector *selector)
 490 {
 491 	ESource *source;
 492 
 493 	source = e_source_selector_ref_primary_selection (selector);
 494 	if (source == NULL)
 495 		return;
 496 
 497 	task_shell_sidebar_set_default (task_shell_sidebar, source);
 498 
 499 	g_object_unref (source);
 500 }
 501 
 502 static void
 503 task_shell_sidebar_restore_state_cb (EShellWindow *shell_window,
 504                                      EShellView *shell_view,
 505                                      EShellSidebar *shell_sidebar)
 506 {
 507 	ETaskShellSidebarPrivate *priv;
 508 	EShell *shell;
 509 	EShellBackend *shell_backend;
 510 	EShellSettings *shell_settings;
 511 	ESourceRegistry *registry;
 512 	ESourceSelector *selector;
 513 	GtkTreeModel *model;
 514 
 515 	priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (shell_sidebar);
 516 
 517 	shell = e_shell_window_get_shell (shell_window);
 518 	shell_settings = e_shell_get_shell_settings (shell);
 519 
 520 	shell_backend = e_shell_view_get_shell_backend (shell_view);
 521 	g_return_if_fail (E_IS_TASK_SHELL_BACKEND (shell_backend));
 522 
 523 	selector = E_SOURCE_SELECTOR (priv->selector);
 524 	model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
 525 
 526 	registry = e_shell_get_registry (shell);
 527 
 528 	g_signal_connect_swapped (
 529 		model, "row-changed",
 530 		G_CALLBACK (task_shell_sidebar_row_changed_cb),
 531 		shell_sidebar);
 532 
 533 	g_signal_connect_swapped (
 534 		selector, "primary-selection-changed",
 535 		G_CALLBACK (task_shell_sidebar_primary_selection_changed_cb),
 536 		shell_sidebar);
 537 
 538 	g_object_bind_property_full (
 539 		shell_settings, "cal-primary-task-list",
 540 		selector, "primary-selection",
 541 		G_BINDING_BIDIRECTIONAL |
 542 		G_BINDING_SYNC_CREATE,
 543 		(GBindingTransformFunc) e_binding_transform_uid_to_source,
 544 		(GBindingTransformFunc) e_binding_transform_source_to_uid,
 545 		g_object_ref (registry),
 546 		(GDestroyNotify) g_object_unref);
 547 }
 548 
 549 static void
 550 task_shell_sidebar_get_property (GObject *object,
 551                                  guint property_id,
 552                                  GValue *value,
 553                                  GParamSpec *pspec)
 554 {
 555 	switch (property_id) {
 556 		case PROP_DEFAULT_CLIENT:
 557 			g_value_set_object (
 558 				value,
 559 				e_task_shell_sidebar_get_default_client (
 560 				E_TASK_SHELL_SIDEBAR (object)));
 561 			return;
 562 
 563 		case PROP_SELECTOR:
 564 			g_value_set_object (
 565 				value,
 566 				e_task_shell_sidebar_get_selector (
 567 				E_TASK_SHELL_SIDEBAR (object)));
 568 			return;
 569 	}
 570 
 571 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 572 }
 573 
 574 static void
 575 task_shell_sidebar_dispose (GObject *object)
 576 {
 577 	ETaskShellSidebarPrivate *priv;
 578 
 579 	priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (object);
 580 
 581 	if (priv->selector != NULL) {
 582 		g_object_unref (priv->selector);
 583 		priv->selector = NULL;
 584 	}
 585 
 586 	if (priv->default_client != NULL) {
 587 		g_object_unref (priv->default_client);
 588 		priv->default_client = NULL;
 589 	}
 590 
 591 	if (priv->loading_default_client != NULL) {
 592 		g_cancellable_cancel (priv->loading_default_client);
 593 		g_object_unref (priv->loading_default_client);
 594 		priv->loading_default_client = NULL;
 595 	}
 596 
 597 	if (priv->loading_clients != NULL) {
 598 		g_cancellable_cancel (priv->loading_clients);
 599 		g_object_unref (priv->loading_clients);
 600 		priv->loading_clients = NULL;
 601 	}
 602 
 603 	g_hash_table_remove_all (priv->client_table);
 604 
 605 	/* Chain up to parent's dispose() method. */
 606 	G_OBJECT_CLASS (e_task_shell_sidebar_parent_class)->dispose (object);
 607 }
 608 
 609 static void
 610 task_shell_sidebar_finalize (GObject *object)
 611 {
 612 	ETaskShellSidebarPrivate *priv;
 613 
 614 	priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (object);
 615 
 616 	g_hash_table_destroy (priv->client_table);
 617 
 618 	/* Chain up to parent's finalize() method. */
 619 	G_OBJECT_CLASS (e_task_shell_sidebar_parent_class)->finalize (object);
 620 }
 621 
 622 static void
 623 task_shell_sidebar_constructed (GObject *object)
 624 {
 625 	ETaskShellSidebarPrivate *priv;
 626 	EShell *shell;
 627 	EShellView *shell_view;
 628 	EShellWindow *shell_window;
 629 	EShellSidebar *shell_sidebar;
 630 	ESourceRegistry *registry;
 631 	GtkContainer *container;
 632 	GtkWidget *widget;
 633 	AtkObject *a11y;
 634 
 635 	priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (object);
 636 
 637 	/* Chain up to parent's constructed() method. */
 638 	G_OBJECT_CLASS (e_task_shell_sidebar_parent_class)->constructed (object);
 639 
 640 	shell_sidebar = E_SHELL_SIDEBAR (object);
 641 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
 642 	shell_window = e_shell_view_get_shell_window (shell_view);
 643 	shell = e_shell_window_get_shell (shell_window);
 644 
 645 	container = GTK_CONTAINER (shell_sidebar);
 646 
 647 	widget = gtk_scrolled_window_new (NULL, NULL);
 648 	gtk_scrolled_window_set_policy (
 649 		GTK_SCROLLED_WINDOW (widget),
 650 		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 651 	gtk_scrolled_window_set_shadow_type (
 652 		GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
 653 	gtk_container_add (container, widget);
 654 	gtk_widget_show (widget);
 655 
 656 	container = GTK_CONTAINER (widget);
 657 
 658 	registry = e_shell_get_registry (shell);
 659 	widget = e_task_list_selector_new (registry);
 660 	e_source_selector_set_select_new (E_SOURCE_SELECTOR (widget), TRUE);
 661 	gtk_container_add (container, widget);
 662 	a11y = gtk_widget_get_accessible (widget);
 663 	atk_object_set_name (a11y, _("Task List Selector"));
 664 	priv->selector = g_object_ref (widget);
 665 	gtk_widget_show (widget);
 666 
 667 	/* Restore widget state from the last session once
 668 	 * the shell view is fully initialized and visible. */
 669 	g_signal_connect (
 670 		shell_window, "shell-view-created::tasks",
 671 		G_CALLBACK (task_shell_sidebar_restore_state_cb),
 672 		shell_sidebar);
 673 }
 674 
 675 static guint32
 676 task_shell_sidebar_check_state (EShellSidebar *shell_sidebar)
 677 {
 678 	ETaskShellSidebar *task_shell_sidebar;
 679 	ESourceSelector *selector;
 680 	ESourceRegistry *registry;
 681 	ESource *source;
 682 	gboolean is_writable = FALSE;
 683 	gboolean is_removable = FALSE;
 684 	gboolean is_remote_creatable = FALSE;
 685 	gboolean is_remote_deletable = FALSE;
 686 	gboolean in_collection = FALSE;
 687 	gboolean refresh_supported = FALSE;
 688 	gboolean has_primary_source = FALSE;
 689 	guint32 state = 0;
 690 
 691 	task_shell_sidebar = E_TASK_SHELL_SIDEBAR (shell_sidebar);
 692 	selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
 693 	source = e_source_selector_ref_primary_selection (selector);
 694 	registry = e_source_selector_get_registry (selector);
 695 
 696 	if (source != NULL) {
 697 		EClient *client;
 698 		ESource *collection;
 699 		const gchar *uid;
 700 
 701 		has_primary_source = TRUE;
 702 		is_writable = e_source_get_writable (source);
 703 		is_removable = e_source_get_removable (source);
 704 		is_remote_creatable = e_source_get_remote_creatable (source);
 705 		is_remote_deletable = e_source_get_remote_deletable (source);
 706 
 707 		collection = e_source_registry_find_extension (
 708 			registry, source, E_SOURCE_EXTENSION_COLLECTION);
 709 		if (collection != NULL) {
 710 			in_collection = TRUE;
 711 			g_object_unref (collection);
 712 		}
 713 
 714 		uid = e_source_get_uid (source);
 715 		client = g_hash_table_lookup (
 716 			task_shell_sidebar->priv->client_table, uid);
 717 		refresh_supported =
 718 			client != NULL &&
 719 			e_client_check_refresh_supported (client);
 720 
 721 		g_object_unref (source);
 722 	}
 723 
 724 	if (has_primary_source)
 725 		state |= E_TASK_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE;
 726 	if (is_writable)
 727 		state |= E_TASK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_WRITABLE;
 728 	if (is_removable)
 729 		state |= E_TASK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOVABLE;
 730 	if (is_remote_creatable)
 731 		state |= E_TASK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOTE_CREATABLE;
 732 	if (is_remote_deletable)
 733 		state |= E_TASK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOTE_DELETABLE;
 734 	if (in_collection)
 735 		state |= E_TASK_SHELL_SIDEBAR_PRIMARY_SOURCE_IN_COLLECTION;
 736 	if (refresh_supported)
 737 		state |= E_TASK_SHELL_SIDEBAR_SOURCE_SUPPORTS_REFRESH;
 738 
 739 	return state;
 740 }
 741 
 742 static void
 743 task_shell_sidebar_client_removed (ETaskShellSidebar *task_shell_sidebar,
 744                                    ECalClient *client)
 745 {
 746 	ESourceSelector *selector;
 747 	GHashTable *client_table;
 748 	ESource *source;
 749 	const gchar *uid;
 750 
 751 	client_table = task_shell_sidebar->priv->client_table;
 752 	selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
 753 
 754 	g_signal_handlers_disconnect_matched (
 755 		client, G_SIGNAL_MATCH_DATA, 0, 0,
 756 		NULL, NULL, task_shell_sidebar);
 757 
 758 	source = e_client_get_source (E_CLIENT (client));
 759 	uid = e_source_get_uid (source);
 760 
 761 	g_hash_table_remove (client_table, uid);
 762 	e_source_selector_unselect_source (selector, source);
 763 
 764 	task_shell_sidebar_emit_status_message (task_shell_sidebar, NULL);
 765 }
 766 
 767 static void
 768 e_task_shell_sidebar_class_init (ETaskShellSidebarClass *class)
 769 {
 770 	GObjectClass *object_class;
 771 	EShellSidebarClass *shell_sidebar_class;
 772 
 773 	g_type_class_add_private (class, sizeof (ETaskShellSidebarPrivate));
 774 
 775 	object_class = G_OBJECT_CLASS (class);
 776 	object_class->get_property = task_shell_sidebar_get_property;
 777 	object_class->dispose = task_shell_sidebar_dispose;
 778 	object_class->finalize = task_shell_sidebar_finalize;
 779 	object_class->constructed = task_shell_sidebar_constructed;
 780 
 781 	shell_sidebar_class = E_SHELL_SIDEBAR_CLASS (class);
 782 	shell_sidebar_class->check_state = task_shell_sidebar_check_state;
 783 
 784 	class->client_removed = task_shell_sidebar_client_removed;
 785 
 786 	g_object_class_install_property (
 787 		object_class,
 788 		PROP_DEFAULT_CLIENT,
 789 		g_param_spec_object (
 790 			"default-client",
 791 			"Default Task ECalClient",
 792 			"Default client for task operations",
 793 			E_TYPE_CAL_CLIENT,
 794 			G_PARAM_READABLE));
 795 
 796 	g_object_class_install_property (
 797 		object_class,
 798 		PROP_SELECTOR,
 799 		g_param_spec_object (
 800 			"selector",
 801 			"Source Selector Widget",
 802 			"This widget displays groups of task lists",
 803 			E_TYPE_SOURCE_SELECTOR,
 804 			G_PARAM_READABLE));
 805 
 806 	signals[CLIENT_ADDED] = g_signal_new (
 807 		"client-added",
 808 		G_OBJECT_CLASS_TYPE (object_class),
 809 		G_SIGNAL_RUN_LAST,
 810 		G_STRUCT_OFFSET (ETaskShellSidebarClass, client_added),
 811 		NULL, NULL,
 812 		g_cclosure_marshal_VOID__OBJECT,
 813 		G_TYPE_NONE, 1,
 814 		E_TYPE_CAL_CLIENT);
 815 
 816 	signals[CLIENT_REMOVED] = g_signal_new (
 817 		"client-removed",
 818 		G_OBJECT_CLASS_TYPE (object_class),
 819 		G_SIGNAL_RUN_LAST,
 820 		G_STRUCT_OFFSET (ETaskShellSidebarClass, client_removed),
 821 		NULL, NULL,
 822 		g_cclosure_marshal_VOID__OBJECT,
 823 		G_TYPE_NONE, 1,
 824 		E_TYPE_CAL_CLIENT);
 825 
 826 	signals[STATUS_MESSAGE] = g_signal_new (
 827 		"status-message",
 828 		G_OBJECT_CLASS_TYPE (object_class),
 829 		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 830 		G_STRUCT_OFFSET (ETaskShellSidebarClass, status_message),
 831 		NULL, NULL,
 832 		e_marshal_VOID__STRING_DOUBLE,
 833 		G_TYPE_NONE, 2,
 834 		G_TYPE_STRING,
 835 		G_TYPE_DOUBLE);
 836 }
 837 
 838 static void
 839 e_task_shell_sidebar_class_finalize (ETaskShellSidebarClass *class)
 840 {
 841 }
 842 
 843 static void
 844 e_task_shell_sidebar_init (ETaskShellSidebar *task_shell_sidebar)
 845 {
 846 	GHashTable *client_table;
 847 
 848 	client_table = g_hash_table_new_full (
 849 		g_str_hash, g_str_equal,
 850 		(GDestroyNotify) g_free,
 851 		(GDestroyNotify) g_object_unref);
 852 
 853 	task_shell_sidebar->priv =
 854 		E_TASK_SHELL_SIDEBAR_GET_PRIVATE (task_shell_sidebar);
 855 
 856 	task_shell_sidebar->priv->client_table = client_table;
 857 	task_shell_sidebar->priv->loading_clients = g_cancellable_new ();
 858 
 859 	/* Postpone widget construction until we have a shell view. */
 860 }
 861 
 862 void
 863 e_task_shell_sidebar_type_register (GTypeModule *type_module)
 864 {
 865 	/* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
 866 	 *     function, so we have to wrap it with a public function in
 867 	 *     order to register types from a separate compilation unit. */
 868 	e_task_shell_sidebar_register_type (type_module);
 869 }
 870 
 871 GtkWidget *
 872 e_task_shell_sidebar_new (EShellView *shell_view)
 873 {
 874 	g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
 875 
 876 	return g_object_new (
 877 		E_TYPE_TASK_SHELL_SIDEBAR,
 878 		"shell-view", shell_view, NULL);
 879 }
 880 
 881 GList *
 882 e_task_shell_sidebar_get_clients (ETaskShellSidebar *task_shell_sidebar)
 883 {
 884 	GHashTable *client_table;
 885 
 886 	g_return_val_if_fail (
 887 		E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar), NULL);
 888 
 889 	client_table = task_shell_sidebar->priv->client_table;
 890 
 891 	return g_hash_table_get_values (client_table);
 892 }
 893 
 894 ECalClient *
 895 e_task_shell_sidebar_get_default_client (ETaskShellSidebar *task_shell_sidebar)
 896 {
 897 	g_return_val_if_fail (
 898 		E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar), NULL);
 899 
 900 	return task_shell_sidebar->priv->default_client;
 901 }
 902 
 903 ESourceSelector *
 904 e_task_shell_sidebar_get_selector (ETaskShellSidebar *task_shell_sidebar)
 905 {
 906 	g_return_val_if_fail (
 907 		E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar), NULL);
 908 
 909 	return E_SOURCE_SELECTOR (task_shell_sidebar->priv->selector);
 910 }
 911 
 912 void
 913 e_task_shell_sidebar_add_source (ETaskShellSidebar *task_shell_sidebar,
 914                                  ESource *source)
 915 {
 916 	EShellView *shell_view;
 917 	EShellContent *shell_content;
 918 	EShellSidebar *shell_sidebar;
 919 	ETaskShellContent *task_shell_content;
 920 	ECalClientSourceType source_type;
 921 	ESourceSelector *selector;
 922 	GHashTable *client_table;
 923 	ECalModel *model;
 924 	ECalClient *default_client;
 925 	ECalClient *client;
 926 	icaltimezone *timezone;
 927 	const gchar *display_name;
 928 	const gchar *uid;
 929 	gchar *message;
 930 
 931 	g_return_if_fail (E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar));
 932 	g_return_if_fail (E_IS_SOURCE (source));
 933 
 934 	source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
 935 	client_table = task_shell_sidebar->priv->client_table;
 936 	default_client = task_shell_sidebar->priv->default_client;
 937 	selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
 938 
 939 	uid = e_source_get_uid (source);
 940 	client = g_hash_table_lookup (client_table, uid);
 941 
 942 	if (client != NULL)
 943 		return;
 944 
 945 	if (default_client != NULL) {
 946 		ESource *default_source;
 947 		const gchar *default_uid;
 948 
 949 		default_source = e_client_get_source (E_CLIENT (default_client));
 950 		default_uid = e_source_get_uid (default_source);
 951 
 952 		if (g_strcmp0 (uid, default_uid) == 0)
 953 			client = g_object_ref (default_client);
 954 	}
 955 
 956 	if (client == NULL)
 957 		client = e_cal_client_new (source, source_type, NULL);
 958 
 959 	g_return_if_fail (client != NULL);
 960 
 961 	g_signal_connect_swapped (
 962 		client, "backend-died",
 963 		G_CALLBACK (task_shell_sidebar_backend_died_cb),
 964 		task_shell_sidebar);
 965 
 966 	g_signal_connect_swapped (
 967 		client, "backend-error",
 968 		G_CALLBACK (task_shell_sidebar_backend_error_cb),
 969 		task_shell_sidebar);
 970 
 971 	g_hash_table_insert (client_table, g_strdup (uid), client);
 972 	e_source_selector_select_source (selector, source);
 973 
 974 	display_name = e_source_get_display_name (source);
 975 	message = g_strdup_printf (_("Opening task list '%s'"), display_name);
 976 	task_shell_sidebar_emit_status_message (task_shell_sidebar, message);
 977 	g_free (message);
 978 
 979 	/* FIXME Sidebar should not be accessing the EShellContent.
 980 	 *       This probably needs to be moved to ETaskShellView. */
 981 	shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar);
 982 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
 983 	shell_content = e_shell_view_get_shell_content (shell_view);
 984 
 985 	task_shell_content = E_TASK_SHELL_CONTENT (shell_content);
 986 	model = e_task_shell_content_get_task_model (task_shell_content);
 987 	timezone = e_cal_model_get_timezone (model);
 988 
 989 	e_cal_client_set_default_timezone (client, timezone);
 990 
 991 	e_client_open (
 992 		E_CLIENT (client), FALSE,
 993 		task_shell_sidebar->priv->loading_clients,
 994 		task_shell_sidebar_client_opened_cb, task_shell_sidebar);
 995 }
 996 
 997 void
 998 e_task_shell_sidebar_remove_source (ETaskShellSidebar *task_shell_sidebar,
 999                                     ESource *source)
1000 {
1001 	GHashTable *client_table;
1002 	ECalClient *client;
1003 	const gchar *uid;
1004 
1005 	g_return_if_fail (E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar));
1006 	g_return_if_fail (E_IS_SOURCE (source));
1007 
1008 	client_table = task_shell_sidebar->priv->client_table;
1009 
1010 	uid = e_source_get_uid (source);
1011 	client = g_hash_table_lookup (client_table, uid);
1012 
1013 	if (client == NULL)
1014 		return;
1015 
1016 	task_shell_sidebar_emit_client_removed (task_shell_sidebar, client);
1017 }