No issues found
1 /*
2 * e-cal-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-cal-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 "widgets/misc/e-paned.h"
34
35 #include "calendar/gui/e-calendar-selector.h"
36 #include "calendar/gui/misc.h"
37
38 #include "e-cal-shell-view.h"
39 #include "e-cal-shell-backend.h"
40 #include "e-cal-shell-content.h"
41
42 #define E_CAL_SHELL_SIDEBAR_GET_PRIVATE(obj) \
43 (G_TYPE_INSTANCE_GET_PRIVATE \
44 ((obj), E_TYPE_CAL_SHELL_SIDEBAR, ECalShellSidebarPrivate))
45
46 struct _ECalShellSidebarPrivate {
47 GtkWidget *paned;
48 GtkWidget *selector;
49 GtkWidget *date_navigator;
50 GtkWidget *new_calendar_button;
51
52 /* UID -> Client */
53 GHashTable *client_table;
54
55 /* The default client is for ECalModel. It follows the
56 * sidebar's primary selection, even if the highlighted
57 * source is not selected. The tricky part is we don't
58 * update the property until the client is successfully
59 * opened. So the user first highlights a source, then
60 * sometime later we update our default-client property
61 * which is bound by an EBinding to ECalModel. */
62 ECalClient *default_client;
63
64 ESource *loading_default_source_instance; /* not-reffed, only for comparison */
65
66 GCancellable *loading_default_client;
67 GCancellable *loading_clients;
68 };
69
70 enum {
71 PROP_0,
72 PROP_DATE_NAVIGATOR,
73 PROP_DEFAULT_CLIENT,
74 PROP_SELECTOR
75 };
76
77 enum {
78 CLIENT_ADDED,
79 CLIENT_REMOVED,
80 STATUS_MESSAGE,
81 LAST_SIGNAL
82 };
83
84 static guint signals[LAST_SIGNAL];
85
86 G_DEFINE_DYNAMIC_TYPE (
87 ECalShellSidebar,
88 e_cal_shell_sidebar,
89 E_TYPE_SHELL_SIDEBAR)
90
91 static void
92 cal_shell_sidebar_emit_client_added (ECalShellSidebar *cal_shell_sidebar,
93 ECalClient *client)
94 {
95 guint signal_id = signals[CLIENT_ADDED];
96
97 g_signal_emit (cal_shell_sidebar, signal_id, 0, client);
98 }
99
100 static void
101 cal_shell_sidebar_emit_client_removed (ECalShellSidebar *cal_shell_sidebar,
102 ECalClient *client)
103 {
104 guint signal_id = signals[CLIENT_REMOVED];
105
106 g_signal_emit (cal_shell_sidebar, signal_id, 0, client);
107 }
108
109 static void
110 cal_shell_sidebar_emit_status_message (ECalShellSidebar *cal_shell_sidebar,
111 const gchar *status_message)
112 {
113 guint signal_id = signals[STATUS_MESSAGE];
114
115 g_signal_emit (cal_shell_sidebar, signal_id, 0, status_message);
116 }
117
118 static void
119 cal_shell_sidebar_backend_died_cb (ECalShellSidebar *cal_shell_sidebar,
120 ECalClient *client)
121 {
122 EShellView *shell_view;
123 EShellContent *shell_content;
124 EShellSidebar *shell_sidebar;
125 GHashTable *client_table;
126 ESource *source;
127 const gchar *uid;
128
129 client_table = cal_shell_sidebar->priv->client_table;
130
131 shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar);
132 shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
133 shell_content = e_shell_view_get_shell_content (shell_view);
134
135 source = e_client_get_source (E_CLIENT (client));
136 uid = e_source_get_uid (source);
137
138 g_object_ref (source);
139
140 g_hash_table_remove (client_table, uid);
141 cal_shell_sidebar_emit_status_message (cal_shell_sidebar, NULL);
142
143 e_alert_submit (
144 E_ALERT_SINK (shell_content),
145 "calendar:calendar-crashed", NULL);
146
147 g_object_unref (source);
148 }
149
150 static void
151 cal_shell_sidebar_backend_error_cb (ECalShellSidebar *cal_shell_sidebar,
152 const gchar *message,
153 ECalClient *client)
154 {
155 EShell *shell;
156 EShellView *shell_view;
157 EShellBackend *shell_backend;
158 EShellContent *shell_content;
159 EShellSidebar *shell_sidebar;
160 ESourceRegistry *registry;
161 ESource *parent;
162 ESource *source;
163 const gchar *parent_uid;
164 const gchar *parent_display_name;
165 const gchar *source_display_name;
166
167 shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar);
168 shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
169 shell_backend = e_shell_view_get_shell_backend (shell_view);
170 shell_content = e_shell_view_get_shell_content (shell_view);
171
172 shell = e_shell_backend_get_shell (shell_backend);
173 registry = e_shell_get_registry (shell);
174
175 source = e_client_get_source (E_CLIENT (client));
176
177 parent_uid = e_source_get_parent (source);
178 parent = e_source_registry_ref_source (registry, parent_uid);
179 g_return_if_fail (parent != NULL);
180
181 parent_display_name = e_source_get_display_name (parent);
182 source_display_name = e_source_get_display_name (source);
183
184 e_alert_submit (
185 E_ALERT_SINK (shell_content),
186 "calendar:backend-error",
187 parent_display_name,
188 source_display_name,
189 message, NULL);
190
191 g_object_unref (parent);
192 }
193
194 static void
195 cal_shell_sidebar_retrieve_capabilies_cb (GObject *source_object,
196 GAsyncResult *result,
197 gpointer user_data)
198 {
199 ECalClient *client = E_CAL_CLIENT (source_object);
200 ECalShellSidebar *cal_shell_sidebar = user_data;
201 gchar *capabilities = NULL;
202
203 g_return_if_fail (client != NULL);
204 g_return_if_fail (cal_shell_sidebar != NULL);
205
206 e_client_retrieve_capabilities_finish (
207 E_CLIENT (client), result, &capabilities, NULL);
208 g_free (capabilities);
209
210 cal_shell_sidebar_emit_status_message (
211 cal_shell_sidebar, _("Loading calendars"));
212 cal_shell_sidebar_emit_client_added (cal_shell_sidebar, client);
213 cal_shell_sidebar_emit_status_message (cal_shell_sidebar, NULL);
214 }
215
216 static gboolean cal_shell_sidebar_retry_open_timeout_cb (gpointer user_data);
217
218 struct RetryOpenData
219 {
220 EClient *client;
221 ECalShellSidebar *cal_shell_sidebar;
222 GCancellable *cancellable;
223 };
224
225 static void
226 free_retry_open_data (gpointer data)
227 {
228 struct RetryOpenData *rod = data;
229
230 if (!rod)
231 return;
232
233 g_object_unref (rod->client);
234 g_object_unref (rod->cancellable);
235 g_free (rod);
236 }
237
238 static void
239 cal_shell_sidebar_client_opened_cb (GObject *source_object,
240 GAsyncResult *result,
241 gpointer user_data)
242 {
243 ECalClient *client = E_CAL_CLIENT (source_object);
244 ECalShellSidebar *cal_shell_sidebar = user_data;
245 ESource *source;
246 EShellView *shell_view;
247 EShellContent *shell_content;
248 EShellSidebar *shell_sidebar;
249 GError *error = NULL;
250
251 source = e_client_get_source (E_CLIENT (client));
252
253 e_client_open_finish (E_CLIENT (client), result, &error);
254
255 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
256 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
257 g_clear_error (&error);
258 return;
259 }
260
261 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_BUSY)) {
262 struct RetryOpenData *rod;
263
264 rod = g_new0 (struct RetryOpenData, 1);
265 rod->client = g_object_ref (client);
266 rod->cal_shell_sidebar = cal_shell_sidebar;
267 rod->cancellable = g_object_ref (cal_shell_sidebar->priv->loading_clients);
268
269 /* postpone for 1/2 of a second, backend is busy now */
270 g_timeout_add_full (
271 G_PRIORITY_DEFAULT, 500,
272 cal_shell_sidebar_retry_open_timeout_cb,
273 rod, free_retry_open_data);
274
275 g_clear_error (&error);
276 return;
277 }
278
279 shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar);
280 shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
281 shell_content = e_shell_view_get_shell_content (shell_view);
282
283 /* Handle errors. */
284 switch ((error && error->domain == E_CLIENT_ERROR) ? error->code : -1) {
285 case -1:
286 break;
287
288 case E_CLIENT_ERROR_BUSY:
289 g_warning (
290 "%s: Cannot open '%s', it's busy (%s)",
291 G_STRFUNC, e_source_get_display_name (source),
292 error->message);
293 g_clear_error (&error);
294 return;
295
296 case E_CLIENT_ERROR_REPOSITORY_OFFLINE:
297 e_alert_submit (
298 E_ALERT_SINK (shell_content),
299 "calendar:prompt-no-contents-offline-calendar",
300 NULL);
301 /* fall through */
302
303 default:
304 if (error->code != E_CLIENT_ERROR_REPOSITORY_OFFLINE) {
305 e_alert_submit (
306 E_ALERT_SINK (shell_content),
307 "calendar:failed-open-calendar",
308 error->message, NULL);
309 }
310
311 e_cal_shell_sidebar_remove_source (
312 cal_shell_sidebar,
313 e_client_get_source (E_CLIENT (client)));
314 g_clear_error (&error);
315 return;
316 }
317
318 g_clear_error (&error);
319
320 /* to have them ready for later use */
321 e_client_retrieve_capabilities (
322 E_CLIENT (client), NULL,
323 cal_shell_sidebar_retrieve_capabilies_cb,
324 cal_shell_sidebar);
325 }
326
327 static gboolean
328 cal_shell_sidebar_retry_open_timeout_cb (gpointer user_data)
329 {
330 struct RetryOpenData *rod = user_data;
331
332 g_return_val_if_fail (rod != NULL, FALSE);
333 g_return_val_if_fail (rod->client != NULL, FALSE);
334 g_return_val_if_fail (rod->cal_shell_sidebar != NULL, FALSE);
335 g_return_val_if_fail (rod->cancellable != NULL, FALSE);
336
337 if (g_cancellable_is_cancelled (rod->cancellable))
338 return FALSE;
339
340 e_client_open (
341 rod->client, FALSE,
342 rod->cal_shell_sidebar->priv->loading_clients,
343 cal_shell_sidebar_client_opened_cb,
344 rod->cal_shell_sidebar);
345
346 return FALSE;
347 }
348
349 static void
350 cal_shell_sidebar_default_loaded_cb (GObject *source_object,
351 GAsyncResult *result,
352 gpointer user_data)
353 {
354 ESource *source = E_SOURCE (source_object);
355 EShellSidebar *shell_sidebar = user_data;
356 ECalShellSidebarPrivate *priv;
357 EShellContent *shell_content;
358 EShellView *shell_view;
359 ECalShellContent *cal_shell_content;
360 ECalModel *model;
361 EClient *client = NULL;
362 GError *error = NULL;
363
364 priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (shell_sidebar);
365
366 shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
367 shell_content = e_shell_view_get_shell_content (shell_view);
368 cal_shell_content = E_CAL_SHELL_CONTENT (shell_content);
369 model = e_cal_shell_content_get_model (cal_shell_content);
370
371 e_client_utils_open_new_finish (source, result, &client, &error);
372
373 if (priv->loading_default_client) {
374 g_object_unref (priv->loading_default_client);
375 priv->loading_default_client = NULL;
376 }
377
378 if (source == priv->loading_default_source_instance)
379 priv->loading_default_source_instance = NULL;
380
381 /* Ignore cancellations. */
382 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
383 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
384 g_warn_if_fail (client == NULL);
385 g_error_free (error);
386 goto exit;
387
388 } else if (error != NULL) {
389 g_warn_if_fail (client == NULL);
390 e_alert_submit (
391 E_ALERT_SINK (shell_content),
392 "calendar:failed-open-calendar",
393 error->message, NULL);
394 g_error_free (error);
395 goto exit;
396 }
397
398 g_return_if_fail (E_IS_CAL_CLIENT (client));
399
400 if (priv->default_client != NULL)
401 g_object_unref (priv->default_client);
402
403 priv->default_client = E_CAL_CLIENT (client);
404
405 e_cal_client_set_default_timezone (
406 priv->default_client, e_cal_model_get_timezone (model));
407
408 g_object_notify (G_OBJECT (shell_sidebar), "default-client");
409
410 exit:
411 g_object_unref (shell_sidebar);
412 }
413
414 static void
415 cal_shell_sidebar_set_default (ECalShellSidebar *cal_shell_sidebar,
416 ESource *source)
417 {
418 ECalShellSidebarPrivate *priv;
419 EShellSidebar *shell_sidebar;
420 ECalClient *client;
421 const gchar *uid;
422
423 priv = cal_shell_sidebar->priv;
424
425 /* already loading that source as default source */
426 if (source == priv->loading_default_source_instance)
427 return;
428
429 /* FIXME Sidebar should not be accessing the EShellContent.
430 * This probably needs to be moved to ECalShellView. */
431 shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar);
432
433 /* Cancel any unfinished previous request. */
434 if (priv->loading_default_client != NULL) {
435 g_cancellable_cancel (priv->loading_default_client);
436 g_object_unref (priv->loading_default_client);
437 priv->loading_default_client = NULL;
438 }
439
440 uid = e_source_get_uid (source);
441 client = g_hash_table_lookup (priv->client_table, uid);
442
443 /* If we already have an open connection for
444 * this UID, we can finish immediately. */
445 if (client != NULL) {
446 if (priv->default_client != NULL)
447 g_object_unref (priv->default_client);
448 priv->default_client = g_object_ref (client);
449 g_object_notify (G_OBJECT (shell_sidebar), "default-client");
450 return;
451 }
452
453 /* it's only for pointer comparison, no need to ref it */
454 priv->loading_default_source_instance = source;
455 priv->loading_default_client = g_cancellable_new ();
456
457 e_client_utils_open_new (
458 source, E_CLIENT_SOURCE_TYPE_EVENTS,
459 FALSE, priv->loading_default_client,
460 cal_shell_sidebar_default_loaded_cb,
461 g_object_ref (shell_sidebar));
462 }
463
464 static void
465 cal_shell_sidebar_row_changed_cb (ECalShellSidebar *cal_shell_sidebar,
466 GtkTreePath *tree_path,
467 GtkTreeIter *tree_iter,
468 GtkTreeModel *tree_model)
469 {
470 ESourceSelector *selector;
471 ESource *source;
472
473 selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
474 source = e_source_selector_ref_source_by_path (selector, tree_path);
475
476 /* XXX This signal gets emitted a lot while the model is being
477 * rebuilt, during which time we won't get a valid ESource.
478 * ESourceSelector should probably block this signal while
479 * rebuilding the model, but we'll be forgiving and not
480 * emit a warning. */
481 if (source == NULL)
482 return;
483
484 if (e_source_selector_source_is_selected (selector, source))
485 e_cal_shell_sidebar_add_source (cal_shell_sidebar, source);
486 else
487 e_cal_shell_sidebar_remove_source (cal_shell_sidebar, source);
488
489 g_object_unref (source);
490 }
491
492 static void
493 cal_shell_sidebar_primary_selection_changed_cb (ECalShellSidebar *cal_shell_sidebar,
494 ESourceSelector *selector)
495 {
496 ESource *source;
497
498 source = e_source_selector_ref_primary_selection (selector);
499 if (source == NULL)
500 return;
501
502 cal_shell_sidebar_set_default (cal_shell_sidebar, source);
503
504 g_object_unref (source);
505 }
506
507 static void
508 cal_shell_sidebar_restore_state_cb (EShellWindow *shell_window,
509 EShellView *shell_view,
510 EShellSidebar *shell_sidebar)
511 {
512 ECalShellSidebarPrivate *priv;
513 EShell *shell;
514 EShellBackend *shell_backend;
515 EShellSettings *shell_settings;
516 ESourceRegistry *registry;
517 ESourceSelector *selector;
518 GSettings *settings;
519 GtkTreeModel *model;
520 GObject *object;
521
522 priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (shell_sidebar);
523
524 shell = e_shell_window_get_shell (shell_window);
525 shell_settings = e_shell_get_shell_settings (shell);
526
527 shell_backend = e_shell_view_get_shell_backend (shell_view);
528 g_return_if_fail (E_IS_CAL_SHELL_BACKEND (shell_backend));
529
530 selector = E_SOURCE_SELECTOR (priv->selector);
531 model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
532
533 registry = e_shell_get_registry (shell);
534
535 g_signal_connect_swapped (
536 model, "row-changed",
537 G_CALLBACK (cal_shell_sidebar_row_changed_cb),
538 shell_sidebar);
539
540 g_signal_connect_swapped (
541 selector, "primary-selection-changed",
542 G_CALLBACK (cal_shell_sidebar_primary_selection_changed_cb),
543 shell_sidebar);
544
545 g_object_bind_property_full (
546 shell_settings, "cal-primary-calendar",
547 selector, "primary-selection",
548 G_BINDING_BIDIRECTIONAL |
549 G_BINDING_SYNC_CREATE,
550 (GBindingTransformFunc) e_binding_transform_uid_to_source,
551 (GBindingTransformFunc) e_binding_transform_source_to_uid,
552 g_object_ref (registry),
553 (GDestroyNotify) g_object_unref);
554
555 /* Bind GObject properties to settings keys. */
556
557 settings = g_settings_new ("org.gnome.evolution.calendar");
558
559 object = G_OBJECT (priv->paned);
560 g_settings_bind (settings, "date-navigator-pane-position", object, "vposition", G_SETTINGS_BIND_DEFAULT);
561
562 g_object_unref (G_OBJECT (settings));
563 }
564
565 static void
566 cal_shell_sidebar_get_property (GObject *object,
567 guint property_id,
568 GValue *value,
569 GParamSpec *pspec)
570 {
571 switch (property_id) {
572 case PROP_DATE_NAVIGATOR:
573 g_value_set_object (
574 value,
575 e_cal_shell_sidebar_get_date_navigator (
576 E_CAL_SHELL_SIDEBAR (object)));
577 return;
578
579 case PROP_DEFAULT_CLIENT:
580 g_value_set_object (
581 value,
582 e_cal_shell_sidebar_get_default_client (
583 E_CAL_SHELL_SIDEBAR (object)));
584 return;
585
586 case PROP_SELECTOR:
587 g_value_set_object (
588 value,
589 e_cal_shell_sidebar_get_selector (
590 E_CAL_SHELL_SIDEBAR (object)));
591 return;
592 }
593
594 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
595 }
596
597 static void
598 cal_shell_sidebar_dispose (GObject *object)
599 {
600 ECalShellSidebarPrivate *priv;
601
602 priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (object);
603
604 if (priv->paned != NULL) {
605 g_object_unref (priv->paned);
606 priv->paned = NULL;
607 }
608
609 if (priv->selector != NULL) {
610 g_object_unref (priv->selector);
611 priv->selector = NULL;
612 }
613
614 if (priv->date_navigator != NULL) {
615 g_object_unref (priv->date_navigator);
616 priv->date_navigator = NULL;
617 }
618
619 if (priv->new_calendar_button != NULL) {
620 g_object_unref (priv->new_calendar_button);
621 priv->new_calendar_button = NULL;
622 }
623
624 if (priv->default_client != NULL) {
625 g_object_unref (priv->default_client);
626 priv->default_client = NULL;
627 }
628
629 if (priv->loading_default_client != NULL) {
630 g_cancellable_cancel (priv->loading_default_client);
631 g_object_unref (priv->loading_default_client);
632 priv->loading_default_client = NULL;
633 }
634
635 if (priv->loading_clients != NULL) {
636 g_cancellable_cancel (priv->loading_clients);
637 g_object_unref (priv->loading_clients);
638 priv->loading_clients = NULL;
639 }
640
641 g_hash_table_remove_all (priv->client_table);
642
643 /* Chain up to parent's dispose() method. */
644 G_OBJECT_CLASS (e_cal_shell_sidebar_parent_class)->dispose (object);
645 }
646
647 static void
648 cal_shell_sidebar_finalize (GObject *object)
649 {
650 ECalShellSidebarPrivate *priv;
651
652 priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (object);
653
654 g_hash_table_destroy (priv->client_table);
655
656 /* Chain up to parent's finalize() method. */
657 G_OBJECT_CLASS (e_cal_shell_sidebar_parent_class)->finalize (object);
658 }
659
660 static void
661 cal_shell_sidebar_constructed (GObject *object)
662 {
663 ECalShellSidebarPrivate *priv;
664 EShell *shell;
665 EShellView *shell_view;
666 EShellWindow *shell_window;
667 EShellBackend *shell_backend;
668 EShellSidebar *shell_sidebar;
669 EShellSettings *shell_settings;
670 ESourceRegistry *registry;
671 ECalendarItem *calitem;
672 GtkWidget *container;
673 GtkWidget *widget;
674 AtkObject *a11y;
675
676 priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (object);
677
678 /* Chain up to parent's constructed() method. */
679 G_OBJECT_CLASS (e_cal_shell_sidebar_parent_class)->constructed (object);
680
681 shell_sidebar = E_SHELL_SIDEBAR (object);
682 shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
683 shell_backend = e_shell_view_get_shell_backend (shell_view);
684 shell_window = e_shell_view_get_shell_window (shell_view);
685
686 shell = e_shell_backend_get_shell (shell_backend);
687 shell_settings = e_shell_get_shell_settings (shell);
688
689 container = GTK_WIDGET (shell_sidebar);
690
691 widget = e_paned_new (GTK_ORIENTATION_VERTICAL);
692 gtk_container_add (GTK_CONTAINER (container), widget);
693 priv->paned = g_object_ref (widget);
694 gtk_widget_show (widget);
695
696 container = widget;
697
698 widget = gtk_vbox_new (FALSE, 6);
699 gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, TRUE);
700 gtk_widget_show (widget);
701
702 container = widget;
703
704 /* "New Calendar" button is only shown in express mode.
705 * ECalShellView will bind the button to an appropriate
706 * GtkAction so we don't have to reimplement it here. */
707 if (e_shell_get_express_mode (shell)) {
708 widget = gtk_button_new ();
709 gtk_box_pack_end (
710 GTK_BOX (container), widget, FALSE, FALSE, 0);
711 priv->new_calendar_button = g_object_ref (widget);
712 gtk_widget_show (widget);
713 }
714
715 widget = gtk_scrolled_window_new (NULL, NULL);
716 gtk_scrolled_window_set_policy (
717 GTK_SCROLLED_WINDOW (widget),
718 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
719 gtk_scrolled_window_set_shadow_type (
720 GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
721 gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
722 gtk_widget_show (widget);
723
724 container = widget;
725
726 registry = e_shell_get_registry (shell);
727 widget = e_calendar_selector_new (registry);
728 e_source_selector_set_select_new (E_SOURCE_SELECTOR (widget), TRUE);
729 gtk_container_add (GTK_CONTAINER (container), widget);
730 a11y = gtk_widget_get_accessible (widget);
731 atk_object_set_name (a11y, _("Calendar Selector"));
732 priv->selector = g_object_ref (widget);
733 gtk_widget_show (widget);
734
735 container = priv->paned;
736
737 widget = e_calendar_new ();
738 calitem = E_CALENDAR (widget)->calitem;
739 e_calendar_item_set_days_start_week_sel (calitem, 9);
740 e_calendar_item_set_max_days_sel (calitem, 42);
741 gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, FALSE);
742 priv->date_navigator = g_object_ref (widget);
743 gtk_widget_show (widget);
744
745 g_object_bind_property (
746 shell_settings, "cal-show-week-numbers",
747 calitem, "show-week-numbers",
748 G_BINDING_SYNC_CREATE);
749
750 g_object_bind_property (
751 shell_settings, "cal-week-start-day",
752 calitem, "week-start-day",
753 G_BINDING_SYNC_CREATE);
754
755 /* Restore widget state from the last session once
756 * the shell view is fully initialized and visible. */
757 g_signal_connect (
758 shell_window, "shell-view-created::calendar",
759 G_CALLBACK (cal_shell_sidebar_restore_state_cb),
760 shell_sidebar);
761 }
762
763 static guint32
764 cal_shell_sidebar_check_state (EShellSidebar *shell_sidebar)
765 {
766 ECalShellSidebar *cal_shell_sidebar;
767 ESourceSelector *selector;
768 ESourceRegistry *registry;
769 ESource *source;
770 gboolean is_writable = FALSE;
771 gboolean is_removable = FALSE;
772 gboolean is_remote_creatable = FALSE;
773 gboolean is_remote_deletable = FALSE;
774 gboolean in_collection = FALSE;
775 gboolean refresh_supported = FALSE;
776 gboolean has_primary_source = FALSE;
777 guint32 state = 0;
778
779 cal_shell_sidebar = E_CAL_SHELL_SIDEBAR (shell_sidebar);
780 selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
781 source = e_source_selector_ref_primary_selection (selector);
782 registry = e_source_selector_get_registry (selector);
783
784 if (source != NULL) {
785 EClient *client;
786 ESource *collection;
787 const gchar *uid;
788
789 has_primary_source = TRUE;
790 is_writable = e_source_get_writable (source);
791 is_removable = e_source_get_removable (source);
792 is_remote_creatable = e_source_get_remote_creatable (source);
793 is_remote_deletable = e_source_get_remote_deletable (source);
794
795 collection = e_source_registry_find_extension (
796 registry, source, E_SOURCE_EXTENSION_COLLECTION);
797 if (collection != NULL) {
798 in_collection = TRUE;
799 g_object_unref (collection);
800 }
801
802 uid = e_source_get_uid (source);
803 client = g_hash_table_lookup (
804 cal_shell_sidebar->priv->client_table, uid);
805 refresh_supported =
806 client != NULL &&
807 e_client_check_refresh_supported (client);
808
809 g_object_unref (source);
810 }
811
812 if (has_primary_source)
813 state |= E_CAL_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE;
814 if (is_writable)
815 state |= E_CAL_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_WRITABLE;
816 if (is_removable)
817 state |= E_CAL_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOVABLE;
818 if (is_remote_creatable)
819 state |= E_CAL_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOTE_CREATABLE;
820 if (is_remote_deletable)
821 state |= E_CAL_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_REMOTE_DELETABLE;
822 if (in_collection)
823 state |= E_CAL_SHELL_SIDEBAR_PRIMARY_SOURCE_IN_COLLECTION;
824 if (refresh_supported)
825 state |= E_CAL_SHELL_SIDEBAR_SOURCE_SUPPORTS_REFRESH;
826
827 return state;
828 }
829
830 static void
831 cal_shell_sidebar_client_removed (ECalShellSidebar *cal_shell_sidebar,
832 ECalClient *client)
833 {
834 ESourceSelector *selector;
835 GHashTable *client_table;
836 ESource *source;
837 const gchar *uid;
838
839 client_table = cal_shell_sidebar->priv->client_table;
840 selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
841
842 g_signal_handlers_disconnect_matched (
843 client, G_SIGNAL_MATCH_DATA, 0, 0,
844 NULL, NULL, cal_shell_sidebar);
845
846 source = e_client_get_source (E_CLIENT (client));
847 uid = e_source_get_uid (source);
848
849 g_hash_table_remove (client_table, uid);
850 e_source_selector_unselect_source (selector, source);
851
852 cal_shell_sidebar_emit_status_message (cal_shell_sidebar, NULL);
853 }
854
855 static void
856 e_cal_shell_sidebar_class_init (ECalShellSidebarClass *class)
857 {
858 GObjectClass *object_class;
859 EShellSidebarClass *shell_sidebar_class;
860
861 g_type_class_add_private (class, sizeof (ECalShellSidebarPrivate));
862
863 object_class = G_OBJECT_CLASS (class);
864 object_class->get_property = cal_shell_sidebar_get_property;
865 object_class->dispose = cal_shell_sidebar_dispose;
866 object_class->finalize = cal_shell_sidebar_finalize;
867 object_class->constructed = cal_shell_sidebar_constructed;
868
869 shell_sidebar_class = E_SHELL_SIDEBAR_CLASS (class);
870 shell_sidebar_class->check_state = cal_shell_sidebar_check_state;
871
872 class->client_removed = cal_shell_sidebar_client_removed;
873
874 g_object_class_install_property (
875 object_class,
876 PROP_DATE_NAVIGATOR,
877 g_param_spec_object (
878 "date-navigator",
879 "Date Navigator Widget",
880 "This widget displays a miniature calendar",
881 E_TYPE_CALENDAR,
882 G_PARAM_READABLE));
883
884 g_object_class_install_property (
885 object_class,
886 PROP_DEFAULT_CLIENT,
887 g_param_spec_object (
888 "default-client",
889 "Default Calendar ECalClient",
890 "Default client for calendar operations",
891 E_TYPE_CAL_CLIENT,
892 G_PARAM_READABLE));
893
894 g_object_class_install_property (
895 object_class,
896 PROP_SELECTOR,
897 g_param_spec_object (
898 "selector",
899 "Source Selector Widget",
900 "This widget displays groups of calendars",
901 E_TYPE_SOURCE_SELECTOR,
902 G_PARAM_READABLE));
903
904 signals[CLIENT_ADDED] = g_signal_new (
905 "client-added",
906 G_OBJECT_CLASS_TYPE (object_class),
907 G_SIGNAL_RUN_LAST,
908 G_STRUCT_OFFSET (ECalShellSidebarClass, client_added),
909 NULL, NULL,
910 g_cclosure_marshal_VOID__OBJECT,
911 G_TYPE_NONE, 1,
912 E_TYPE_CAL_CLIENT);
913
914 signals[CLIENT_REMOVED] = g_signal_new (
915 "client-removed",
916 G_OBJECT_CLASS_TYPE (object_class),
917 G_SIGNAL_RUN_LAST,
918 G_STRUCT_OFFSET (ECalShellSidebarClass, client_removed),
919 NULL, NULL,
920 g_cclosure_marshal_VOID__OBJECT,
921 G_TYPE_NONE, 1,
922 E_TYPE_CAL_CLIENT);
923
924 signals[STATUS_MESSAGE] = g_signal_new (
925 "status-message",
926 G_OBJECT_CLASS_TYPE (object_class),
927 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
928 G_STRUCT_OFFSET (ECalShellSidebarClass, status_message),
929 NULL, NULL,
930 g_cclosure_marshal_VOID__STRING,
931 G_TYPE_NONE, 1,
932 G_TYPE_STRING);
933 }
934
935 static void
936 e_cal_shell_sidebar_class_finalize (ECalShellSidebarClass *class)
937 {
938 }
939
940 static void
941 e_cal_shell_sidebar_init (ECalShellSidebar *cal_shell_sidebar)
942 {
943 GHashTable *client_table;
944
945 client_table = g_hash_table_new_full (
946 g_str_hash, g_str_equal,
947 (GDestroyNotify) g_free,
948 (GDestroyNotify) g_object_unref);
949
950 cal_shell_sidebar->priv =
951 E_CAL_SHELL_SIDEBAR_GET_PRIVATE (cal_shell_sidebar);
952
953 cal_shell_sidebar->priv->client_table = client_table;
954 cal_shell_sidebar->priv->loading_clients = g_cancellable_new ();
955
956 /* Postpone widget construction until we have a shell view. */
957 }
958
959 void
960 e_cal_shell_sidebar_type_register (GTypeModule *type_module)
961 {
962 /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
963 * function, so we have to wrap it with a public function in
964 * order to register types from a separate compilation unit. */
965 e_cal_shell_sidebar_register_type (type_module);
966 }
967
968 GtkWidget *
969 e_cal_shell_sidebar_new (EShellView *shell_view)
970 {
971 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
972
973 return g_object_new (
974 E_TYPE_CAL_SHELL_SIDEBAR,
975 "shell-view", shell_view, NULL);
976 }
977
978 GList *
979 e_cal_shell_sidebar_get_clients (ECalShellSidebar *cal_shell_sidebar)
980 {
981 GHashTable *client_table;
982
983 g_return_val_if_fail (
984 E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar), NULL);
985
986 client_table = cal_shell_sidebar->priv->client_table;
987
988 return g_hash_table_get_values (client_table);
989 }
990
991 ECalendar *
992 e_cal_shell_sidebar_get_date_navigator (ECalShellSidebar *cal_shell_sidebar)
993 {
994 g_return_val_if_fail (
995 E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar), NULL);
996
997 return E_CALENDAR (cal_shell_sidebar->priv->date_navigator);
998 }
999
1000 ECalClient *
1001 e_cal_shell_sidebar_get_default_client (ECalShellSidebar *cal_shell_sidebar)
1002 {
1003 g_return_val_if_fail (
1004 E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar), NULL);
1005
1006 return cal_shell_sidebar->priv->default_client;
1007 }
1008
1009 GtkWidget *
1010 e_cal_shell_sidebar_get_new_calendar_button (ECalShellSidebar *cal_shell_sidebar)
1011 {
1012 g_return_val_if_fail (
1013 E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar), NULL);
1014
1015 return cal_shell_sidebar->priv->new_calendar_button;
1016 }
1017
1018 ESourceSelector *
1019 e_cal_shell_sidebar_get_selector (ECalShellSidebar *cal_shell_sidebar)
1020 {
1021 g_return_val_if_fail (
1022 E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar), NULL);
1023
1024 return E_SOURCE_SELECTOR (cal_shell_sidebar->priv->selector);
1025 }
1026
1027 void
1028 e_cal_shell_sidebar_add_source (ECalShellSidebar *cal_shell_sidebar,
1029 ESource *source)
1030 {
1031 EShellView *shell_view;
1032 EShellContent *shell_content;
1033 EShellSidebar *shell_sidebar;
1034 ECalShellContent *cal_shell_content;
1035 ECalClientSourceType source_type;
1036 ESourceSelector *selector;
1037 GHashTable *client_table;
1038 ECalModel *model;
1039 ECalClient *default_client;
1040 ECalClient *client;
1041 icaltimezone *timezone;
1042 const gchar *display_name;
1043 const gchar *uid;
1044 gchar *message;
1045
1046 g_return_if_fail (E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar));
1047 g_return_if_fail (E_IS_SOURCE (source));
1048
1049 source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
1050 client_table = cal_shell_sidebar->priv->client_table;
1051 default_client = cal_shell_sidebar->priv->default_client;
1052 selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar);
1053
1054 uid = e_source_get_uid (source);
1055 client = g_hash_table_lookup (client_table, uid);
1056
1057 if (client != NULL)
1058 return;
1059
1060 if (default_client != NULL) {
1061 ESource *default_source;
1062 const gchar *default_uid;
1063
1064 default_source = e_client_get_source (E_CLIENT (default_client));
1065 default_uid = e_source_get_uid (default_source);
1066
1067 if (g_strcmp0 (uid, default_uid) == 0)
1068 client = g_object_ref (default_client);
1069 }
1070
1071 if (client == NULL)
1072 client = e_cal_client_new (source, source_type, NULL);
1073
1074 g_return_if_fail (client != NULL);
1075
1076 g_signal_connect_swapped (
1077 client, "backend-died",
1078 G_CALLBACK (cal_shell_sidebar_backend_died_cb),
1079 cal_shell_sidebar);
1080
1081 g_signal_connect_swapped (
1082 client, "backend-error",
1083 G_CALLBACK (cal_shell_sidebar_backend_error_cb),
1084 cal_shell_sidebar);
1085
1086 g_hash_table_insert (client_table, g_strdup (uid), client);
1087 e_source_selector_select_source (selector, source);
1088
1089 display_name = e_source_get_display_name (source);
1090 message = g_strdup_printf (_("Opening calendar '%s'"), display_name);
1091 cal_shell_sidebar_emit_status_message (cal_shell_sidebar, message);
1092 g_free (message);
1093
1094 /* FIXME Sidebar should not be accessing the EShellContent.
1095 * This probably needs to be moved to ECalShellView. */
1096 shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar);
1097 shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
1098 shell_content = e_shell_view_get_shell_content (shell_view);
1099
1100 cal_shell_content = E_CAL_SHELL_CONTENT (shell_content);
1101 model = e_cal_shell_content_get_model (cal_shell_content);
1102 timezone = e_cal_model_get_timezone (model);
1103
1104 e_cal_client_set_default_timezone (client, timezone);
1105
1106 e_client_open (
1107 E_CLIENT (client), FALSE,
1108 cal_shell_sidebar->priv->loading_clients,
1109 cal_shell_sidebar_client_opened_cb, cal_shell_sidebar);
1110 }
1111
1112 void
1113 e_cal_shell_sidebar_remove_source (ECalShellSidebar *cal_shell_sidebar,
1114 ESource *source)
1115 {
1116 GHashTable *client_table;
1117 ECalClient *client;
1118 const gchar *uid;
1119
1120 g_return_if_fail (E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar));
1121 g_return_if_fail (E_IS_SOURCE (source));
1122
1123 client_table = cal_shell_sidebar->priv->client_table;
1124
1125 uid = e_source_get_uid (source);
1126 client = g_hash_table_lookup (client_table, uid);
1127
1128 if (client == NULL)
1129 return;
1130
1131 cal_shell_sidebar_emit_client_removed (cal_shell_sidebar, client);
1132 }