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 }