From ab794abcd3fa23969a1f8b04d0236838f721180a Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Fri, 29 Jan 2010 14:02:53 -0500 Subject: Improve sidebar and ECalModel interaction. Restores the "default client" behavior from 2.28, so that "Click to Add" task and memo fields work properly. --- modules/calendar/e-cal-shell-sidebar.c | 251 +++++++++++++++++++++++---- modules/calendar/e-cal-shell-sidebar.h | 2 + modules/calendar/e-cal-shell-view-private.c | 5 + modules/calendar/e-memo-shell-sidebar.c | 250 +++++++++++++++++++++----- modules/calendar/e-memo-shell-sidebar.h | 2 + modules/calendar/e-memo-shell-view-private.c | 7 +- modules/calendar/e-task-shell-sidebar.c | 250 +++++++++++++++++++++----- modules/calendar/e-task-shell-sidebar.h | 2 + modules/calendar/e-task-shell-view-private.c | 8 +- 9 files changed, 657 insertions(+), 120 deletions(-) (limited to 'modules') diff --git a/modules/calendar/e-cal-shell-sidebar.c b/modules/calendar/e-cal-shell-sidebar.c index 390be5e6da..3b78afa0d3 100644 --- a/modules/calendar/e-cal-shell-sidebar.c +++ b/modules/calendar/e-cal-shell-sidebar.c @@ -34,8 +34,8 @@ #include "calendar/gui/e-calendar-selector.h" #include "calendar/gui/misc.h" -#include "e-cal-shell-backend.h" #include "e-cal-shell-view.h" +#include "e-cal-shell-backend.h" #define E_CAL_SHELL_SIDEBAR_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ @@ -48,11 +48,21 @@ struct _ECalShellSidebarPrivate { /* UID -> Client */ GHashTable *client_table; + + /* The default client is for ECalModel. It follows the + * sidebar's primary selection, even if the highlighted + * source is not selected. The tricky part is we don't + * update the property until the client is successfully + * opened. So the user first highlights a source, then + * sometime later we update our default-client property + * which is bound by an EBinding to ECalModel. */ + ECal *default_client; }; enum { PROP_0, PROP_DATE_NAVIGATOR, + PROP_DEFAULT_CLIENT, PROP_SELECTOR }; @@ -166,6 +176,7 @@ cal_shell_sidebar_client_opened_cb (ECalShellSidebar *cal_shell_sidebar, EShellView *shell_view; EShellWindow *shell_window; EShellSidebar *shell_sidebar; + const gchar *message; shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar); shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); @@ -175,39 +186,121 @@ cal_shell_sidebar_client_opened_cb (ECalShellSidebar *cal_shell_sidebar, status == E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED) e_auth_cal_forget_password (client); + /* Handle errors. */ switch (status) { case E_CALENDAR_STATUS_OK: - g_signal_handlers_disconnect_matched ( - client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, - cal_shell_sidebar_client_opened_cb, NULL); - - cal_shell_sidebar_emit_status_message ( - cal_shell_sidebar, _("Loading calendars")); - cal_shell_sidebar_emit_client_added ( - cal_shell_sidebar, client); - cal_shell_sidebar_emit_status_message ( - cal_shell_sidebar, NULL); break; case E_CALENDAR_STATUS_AUTHENTICATION_FAILED: e_cal_open_async (client, FALSE); - break; + return; case E_CALENDAR_STATUS_BUSY: - break; + return; case E_CALENDAR_STATUS_REPOSITORY_OFFLINE: e_alert_run_dialog_for_args ( GTK_WINDOW (shell_window), "calendar:prompt-no-contents-offline-calendar", NULL); - break; + /* fall through */ default: - cal_shell_sidebar_emit_client_removed ( - cal_shell_sidebar, client); + e_cal_shell_sidebar_remove_source ( + cal_shell_sidebar, + e_cal_get_source (client)); + return; + } + + g_assert (status == E_CALENDAR_STATUS_OK); + + g_signal_handlers_disconnect_matched ( + client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + cal_shell_sidebar_client_opened_cb, NULL); + + message = _("Loading calendars"); + cal_shell_sidebar_emit_status_message (cal_shell_sidebar, message); + cal_shell_sidebar_emit_client_added (cal_shell_sidebar, client); + cal_shell_sidebar_emit_status_message (cal_shell_sidebar, NULL); +} + +static void +cal_shell_sidebar_default_opened_cb (ECalShellSidebar *cal_shell_sidebar, + ECalendarStatus status, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + + shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + if (status == E_CALENDAR_STATUS_AUTHENTICATION_FAILED || + status == E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED) + e_auth_cal_forget_password (client); + + /* Handle errors. */ + switch (status) { + case E_CALENDAR_STATUS_OK: break; + + case E_CALENDAR_STATUS_AUTHENTICATION_FAILED: + e_cal_open_async (client, FALSE); + return; + + case E_CALENDAR_STATUS_BUSY: + return; + + default: + e_cal_shell_sidebar_remove_source ( + cal_shell_sidebar, + e_cal_get_source (client)); + return; } + + g_assert (status == E_CALENDAR_STATUS_OK); + + g_signal_handlers_disconnect_matched ( + client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + cal_shell_sidebar_default_opened_cb, NULL); + + g_object_notify (G_OBJECT (cal_shell_sidebar), "default-client"); +} + +static void +cal_shell_sidebar_set_default (ECalShellSidebar *cal_shell_sidebar, + ESource *source) +{ + ECalSourceType source_type; + GHashTable *client_table; + ECal *client; + const gchar *uid; + + source_type = E_CAL_SOURCE_TYPE_EVENT; + client_table = cal_shell_sidebar->priv->client_table; + + uid = e_source_peek_uid (source); + client = g_hash_table_lookup (client_table, uid); + + if (cal_shell_sidebar->priv->default_client != NULL) + g_object_unref (cal_shell_sidebar->priv->default_client); + + if (client != NULL) + g_object_ref (client); + else + client = e_auth_new_cal_from_source (source, source_type); + + cal_shell_sidebar->priv->default_client = client; + g_return_if_fail (client != NULL); + + g_signal_connect_swapped ( + client, "cal-opened", + G_CALLBACK (cal_shell_sidebar_default_opened_cb), + cal_shell_sidebar); + + e_cal_open_async (client, FALSE); } static void @@ -295,6 +388,8 @@ cal_shell_sidebar_primary_selection_changed_cb (ECalShellSidebar *cal_shell_side e_shell_settings_set_string ( shell_settings, "cal-primary-calendar", e_source_peek_uid (source)); + + cal_shell_sidebar_set_default (cal_shell_sidebar, source); } static void @@ -306,13 +401,22 @@ cal_shell_sidebar_get_property (GObject *object, switch (property_id) { case PROP_DATE_NAVIGATOR: g_value_set_object ( - value, e_cal_shell_sidebar_get_date_navigator ( + value, + e_cal_shell_sidebar_get_date_navigator ( + E_CAL_SHELL_SIDEBAR (object))); + return; + + case PROP_DEFAULT_CLIENT: + g_value_set_object ( + value, + e_cal_shell_sidebar_get_default_client ( E_CAL_SHELL_SIDEBAR (object))); return; case PROP_SELECTOR: g_value_set_object ( - value, e_cal_shell_sidebar_get_selector ( + value, + e_cal_shell_sidebar_get_selector ( E_CAL_SHELL_SIDEBAR (object))); return; } @@ -342,6 +446,11 @@ cal_shell_sidebar_dispose (GObject *object) priv->date_navigator = NULL; } + if (priv->default_client != NULL) { + g_object_unref (priv->default_client); + priv->default_client = NULL; + } + g_hash_table_remove_all (priv->client_table); /* Chain up to parent's dispose() method. */ @@ -370,18 +479,11 @@ cal_shell_sidebar_constructed (GObject *object) EShellBackend *shell_backend; EShellSidebar *shell_sidebar; EShellSettings *shell_settings; - ESourceSelector *selector; ESourceList *source_list; - ESource *source; ECalendarItem *calitem; - GConfBridge *bridge; - GtkTreeModel *model; GtkWidget *container; GtkWidget *widget; AtkObject *a11y; - GSList *list, *iter; - const gchar *key; - gchar *uid; priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (object); @@ -443,16 +545,55 @@ cal_shell_sidebar_constructed (GObject *object) e_binding_new ( shell_settings, "cal-week-start-day", calitem, "week-start-day"); +} - /* Restore the selector state from the last session. */ +static void +cal_shell_sidebar_realize (GtkWidget *widget) +{ + ECalShellSidebarPrivate *priv; + EShell *shell; + EShellView *shell_view; + EShellBackend *shell_backend; + EShellSidebar *shell_sidebar; + EShellSettings *shell_settings; + ESourceSelector *selector; + ESourceList *source_list; + ESource *source; + GConfBridge *bridge; + GtkTreeModel *model; + GSList *list, *iter; + GObject *object; + const gchar *key; + gchar *uid; + + priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (widget); + + /* Restore the selector state from the last session. We do this + * in realize() instead of constructed() so the shell view has a + * chance to connect handlers to our signals. */ + + shell_sidebar = E_SHELL_SIDEBAR (widget); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); selector = E_SOURCE_SELECTOR (priv->selector); model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + source_list = e_cal_shell_backend_get_source_list ( + E_CAL_SHELL_BACKEND (shell_backend)); + g_signal_connect_swapped ( model, "row-changed", G_CALLBACK (cal_shell_sidebar_row_changed_cb), - object); + shell_sidebar); + + g_signal_connect_swapped ( + selector, "primary-selection-changed", + G_CALLBACK (cal_shell_sidebar_primary_selection_changed_cb), + shell_sidebar); source = NULL; uid = e_shell_settings_get_string ( @@ -483,12 +624,7 @@ cal_shell_sidebar_constructed (GObject *object) g_signal_connect_swapped ( selector, "selection-changed", G_CALLBACK (cal_shell_sidebar_selection_changed_cb), - object); - - g_signal_connect_swapped ( - selector, "primary-selection-changed", - G_CALLBACK (cal_shell_sidebar_primary_selection_changed_cb), - object); + shell_sidebar); /* Bind GObject properties to GConf keys. */ @@ -497,6 +633,9 @@ cal_shell_sidebar_constructed (GObject *object) object = G_OBJECT (priv->paned); key = "/apps/evolution/calendar/display/date_navigator_vpane_position"; gconf_bridge_bind_property_delayed (bridge, key, object, "vposition"); + + /* Chain up to parent's realize() method. */ + GTK_WIDGET_CLASS (parent_class)->realize (widget); } static guint32 @@ -562,10 +701,10 @@ cal_shell_sidebar_client_removed (ECalShellSidebar *cal_shell_sidebar, NULL, NULL, cal_shell_sidebar); source = e_cal_get_source (client); - e_source_selector_unselect_source (selector, source); - uid = e_source_peek_uid (source); + g_hash_table_remove (client_table, uid); + e_source_selector_unselect_source (selector, source); cal_shell_sidebar_emit_status_message (cal_shell_sidebar, NULL); } @@ -574,6 +713,7 @@ static void cal_shell_sidebar_class_init (ECalShellSidebarClass *class) { GObjectClass *object_class; + GtkWidgetClass *widget_class; EShellSidebarClass *shell_sidebar_class; parent_class = g_type_class_peek_parent (class); @@ -585,6 +725,9 @@ cal_shell_sidebar_class_init (ECalShellSidebarClass *class) object_class->finalize = cal_shell_sidebar_finalize; object_class->constructed = cal_shell_sidebar_constructed; + widget_class = GTK_WIDGET_CLASS (class); + widget_class->realize = cal_shell_sidebar_realize; + shell_sidebar_class = E_SHELL_SIDEBAR_CLASS (class); shell_sidebar_class->check_state = cal_shell_sidebar_check_state; @@ -600,6 +743,16 @@ cal_shell_sidebar_class_init (ECalShellSidebarClass *class) E_TYPE_CALENDAR, G_PARAM_READABLE)); + g_object_class_install_property ( + object_class, + PROP_DEFAULT_CLIENT, + g_param_spec_object ( + "default-client", + _("Default Calendar Client"), + _("Default client for calendar operations"), + E_TYPE_CAL, + G_PARAM_READABLE)); + g_object_class_install_property ( object_class, PROP_SELECTOR, @@ -718,6 +871,15 @@ e_cal_shell_sidebar_get_date_navigator (ECalShellSidebar *cal_shell_sidebar) return E_CALENDAR (cal_shell_sidebar->priv->date_navigator); } +ECal * +e_cal_shell_sidebar_get_default_client (ECalShellSidebar *cal_shell_sidebar) +{ + g_return_val_if_fail ( + E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar), NULL); + + return cal_shell_sidebar->priv->default_client; +} + ESourceSelector * e_cal_shell_sidebar_get_selector (ECalShellSidebar *cal_shell_sidebar) { @@ -731,8 +893,10 @@ void e_cal_shell_sidebar_add_source (ECalShellSidebar *cal_shell_sidebar, ESource *source) { + ECalSourceType source_type; ESourceSelector *selector; GHashTable *client_table; + ECal *default_client; ECal *client; const gchar *uid; const gchar *uri; @@ -741,7 +905,9 @@ e_cal_shell_sidebar_add_source (ECalShellSidebar *cal_shell_sidebar, g_return_if_fail (E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar)); g_return_if_fail (E_IS_SOURCE (source)); + source_type = E_CAL_SOURCE_TYPE_EVENT; client_table = cal_shell_sidebar->priv->client_table; + default_client = cal_shell_sidebar->priv->default_client; selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); uid = e_source_peek_uid (source); @@ -750,7 +916,20 @@ e_cal_shell_sidebar_add_source (ECalShellSidebar *cal_shell_sidebar, if (client != NULL) return; - client = e_auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_EVENT); + if (default_client != NULL) { + ESource *default_source; + const gchar *default_uid; + + default_source = e_cal_get_source (default_client); + default_uid = e_source_peek_uid (default_source); + + if (g_strcmp0 (uid, default_uid) == 0) + client = g_object_ref (default_client); + } + + if (client == NULL) + client = e_auth_new_cal_from_source (source, source_type); + g_return_if_fail (client != NULL); g_signal_connect_swapped ( diff --git a/modules/calendar/e-cal-shell-sidebar.h b/modules/calendar/e-cal-shell-sidebar.h index 6169d303d6..6919d7ab52 100644 --- a/modules/calendar/e-cal-shell-sidebar.h +++ b/modules/calendar/e-cal-shell-sidebar.h @@ -86,6 +86,8 @@ GList * e_cal_shell_sidebar_get_clients (ECalShellSidebar *cal_shell_sidebar); ECalendar * e_cal_shell_sidebar_get_date_navigator (ECalShellSidebar *cal_shell_sidebar); +ECal * e_cal_shell_sidebar_get_default_client + (ECalShellSidebar *cal_shell_sidebar); ESourceSelector * e_cal_shell_sidebar_get_selector (ECalShellSidebar *cal_shell_sidebar); diff --git a/modules/calendar/e-cal-shell-view-private.c b/modules/calendar/e-cal-shell-view-private.c index 74a2bbcaf5..b5f9ba55d7 100644 --- a/modules/calendar/e-cal-shell-view-private.c +++ b/modules/calendar/e-cal-shell-view-private.c @@ -566,6 +566,11 @@ e_cal_shell_view_private_constructed (ECalShellView *cal_shell_view) e_cal_shell_view_update_search_filter (cal_shell_view); e_cal_shell_view_update_timezone (cal_shell_view); + /* Keep the ECalModel in sync with the sidebar. */ + e_binding_new ( + shell_sidebar, "default-client", + model, "default-client"); + /* Keep the toolbar view buttons in sync with the calendar. */ e_mutual_binding_new ( calendar, "view", diff --git a/modules/calendar/e-memo-shell-sidebar.c b/modules/calendar/e-memo-shell-sidebar.c index e76341c83a..7ce7441eec 100644 --- a/modules/calendar/e-memo-shell-sidebar.c +++ b/modules/calendar/e-memo-shell-sidebar.c @@ -25,7 +25,6 @@ #include #include -#include "e-util/e-binding.h" #include "e-util/e-alert-dialog.h" #include "e-util/e-util.h" #include "calendar/common/authentication.h" @@ -42,14 +41,23 @@ struct _EMemoShellSidebarPrivate { GtkWidget *selector; - icaltimezone *timezone; /* UID -> Client */ GHashTable *client_table; + + /* The default client is for ECalModel. It follows the + * sidebar's primary selection, even if the highlighted + * source is not selected. The tricky part is we don't + * update the property until the client is successfully + * opened. So the user first highlights a source, then + * sometime later we update our default-client property + * which is bound by an EBinding to ECalModel. */ + ECal *default_client; }; enum { PROP_0, + PROP_DEFAULT_CLIENT, PROP_SELECTOR }; @@ -163,6 +171,7 @@ memo_shell_sidebar_client_opened_cb (EMemoShellSidebar *memo_shell_sidebar, EShellView *shell_view; EShellWindow *shell_window; EShellSidebar *shell_sidebar; + const gchar *message; shell_sidebar = E_SHELL_SIDEBAR (memo_shell_sidebar); shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); @@ -172,39 +181,121 @@ memo_shell_sidebar_client_opened_cb (EMemoShellSidebar *memo_shell_sidebar, status == E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED) e_auth_cal_forget_password (client); + /* Handle errors. */ switch (status) { case E_CALENDAR_STATUS_OK: - g_signal_handlers_disconnect_matched ( - client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, - memo_shell_sidebar_client_opened_cb, NULL); - - memo_shell_sidebar_emit_status_message ( - memo_shell_sidebar, _("Loading memos")); - memo_shell_sidebar_emit_client_added ( - memo_shell_sidebar, client); - memo_shell_sidebar_emit_status_message ( - memo_shell_sidebar, NULL); break; case E_CALENDAR_STATUS_AUTHENTICATION_FAILED: e_cal_open_async (client, FALSE); - break; + return; case E_CALENDAR_STATUS_BUSY: - break; + return; case E_CALENDAR_STATUS_REPOSITORY_OFFLINE: e_alert_run_dialog_for_args ( GTK_WINDOW (shell_window), "calendar:prompt-no-contents-offline-memos", NULL); - break; + /* fall through */ default: - memo_shell_sidebar_emit_client_removed ( - memo_shell_sidebar, client); + e_memo_shell_sidebar_remove_source ( + memo_shell_sidebar, + e_cal_get_source (client)); + return; + } + + g_assert (status == E_CALENDAR_STATUS_OK); + + g_signal_handlers_disconnect_matched ( + client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + memo_shell_sidebar_client_opened_cb, NULL); + + message = _("Loading memos"); + memo_shell_sidebar_emit_status_message (memo_shell_sidebar, message); + memo_shell_sidebar_emit_client_added (memo_shell_sidebar, client); + memo_shell_sidebar_emit_status_message (memo_shell_sidebar, NULL); +} + +static void +memo_shell_sidebar_default_opened_cb (EMemoShellSidebar *memo_shell_sidebar, + ECalendarStatus status, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + + shell_sidebar = E_SHELL_SIDEBAR (memo_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + if (status == E_CALENDAR_STATUS_AUTHENTICATION_FAILED || + status == E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED) + e_auth_cal_forget_password (client); + + /* Handle errors. */ + switch (status) { + case E_CALENDAR_STATUS_OK: break; + + case E_CALENDAR_STATUS_AUTHENTICATION_FAILED: + e_cal_open_async (client, FALSE); + return; + + case E_CALENDAR_STATUS_BUSY: + return; + + default: + e_memo_shell_sidebar_remove_source ( + memo_shell_sidebar, + e_cal_get_source (client)); + return; } + + g_assert (status == E_CALENDAR_STATUS_OK); + + g_signal_handlers_disconnect_matched ( + client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + memo_shell_sidebar_default_opened_cb, NULL); + + g_object_notify (G_OBJECT (memo_shell_sidebar), "default-client"); +} + +static void +memo_shell_sidebar_set_default (EMemoShellSidebar *memo_shell_sidebar, + ESource *source) +{ + ECalSourceType source_type; + GHashTable *client_table; + ECal *client; + const gchar *uid; + + source_type = E_CAL_SOURCE_TYPE_JOURNAL; + client_table = memo_shell_sidebar->priv->client_table; + + uid = e_source_peek_uid (source); + client = g_hash_table_lookup (client_table, uid); + + if (memo_shell_sidebar->priv->default_client != NULL) + g_object_unref (memo_shell_sidebar->priv->default_client); + + if (client != NULL) + g_object_ref (client); + else + client = e_auth_new_cal_from_source (source, source_type); + + memo_shell_sidebar->priv->default_client = client; + g_return_if_fail (client != NULL); + + g_signal_connect_swapped ( + client, "cal-opened", + G_CALLBACK (memo_shell_sidebar_default_opened_cb), + memo_shell_sidebar); + + e_cal_open_async (client, FALSE); } static void @@ -292,6 +383,8 @@ memo_shell_sidebar_primary_selection_changed_cb (EMemoShellSidebar *memo_shell_s e_shell_settings_set_string ( shell_settings, "cal-primary-memo-list", e_source_peek_uid (source)); + + memo_shell_sidebar_set_default (memo_shell_sidebar, source); } static void @@ -301,9 +394,17 @@ memo_shell_sidebar_get_property (GObject *object, GParamSpec *pspec) { switch (property_id) { + case PROP_DEFAULT_CLIENT: + g_value_set_object ( + value, + e_memo_shell_sidebar_get_default_client ( + E_MEMO_SHELL_SIDEBAR (object))); + return; + case PROP_SELECTOR: g_value_set_object ( - value, e_memo_shell_sidebar_get_selector ( + value, + e_memo_shell_sidebar_get_selector ( E_MEMO_SHELL_SIDEBAR (object))); return; } @@ -323,6 +424,11 @@ memo_shell_sidebar_dispose (GObject *object) priv->selector = NULL; } + if (priv->default_client != NULL) { + g_object_unref (priv->default_client); + priv->default_client = NULL; + } + g_hash_table_remove_all (priv->client_table); /* Chain up to parent's dispose() method. */ @@ -346,20 +452,13 @@ static void memo_shell_sidebar_constructed (GObject *object) { EMemoShellSidebarPrivate *priv; - EShell *shell; EShellView *shell_view; EShellBackend *shell_backend; EShellSidebar *shell_sidebar; - EShellSettings *shell_settings; - ESourceSelector *selector; ESourceList *source_list; - ESource *source; GtkContainer *container; - GtkTreeModel *model; GtkWidget *widget; AtkObject *a11y; - GSList *list, *iter; - gchar *uid; priv = E_MEMO_SHELL_SIDEBAR_GET_PRIVATE (object); @@ -370,9 +469,6 @@ memo_shell_sidebar_constructed (GObject *object) shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); shell_backend = e_shell_view_get_shell_backend (shell_view); - shell = e_shell_backend_get_shell (shell_backend); - shell_settings = e_shell_get_shell_settings (shell); - source_list = e_memo_shell_backend_get_source_list ( E_MEMO_SHELL_BACKEND (shell_backend)); @@ -396,16 +492,52 @@ memo_shell_sidebar_constructed (GObject *object) atk_object_set_name (a11y, _("Memo List Selector")); priv->selector = g_object_ref (widget); gtk_widget_show (widget); +} + +static void +memo_shell_sidebar_realize (GtkWidget *widget) +{ + EMemoShellSidebarPrivate *priv; + EShell *shell; + EShellView *shell_view; + EShellBackend *shell_backend; + EShellSidebar *shell_sidebar; + EShellSettings *shell_settings; + ESourceSelector *selector; + ESourceList *source_list; + ESource *source; + GtkTreeModel *model; + GSList *list, *iter; + gchar *uid; + + priv = E_MEMO_SHELL_SIDEBAR_GET_PRIVATE (widget); + + /* Restore the selector state from the last session. We do this + * in realize() instead of constructed() so the shell view has a + * chance to connect handlers to our signals. */ - /* Restore the selector state from the last session. */ + shell_sidebar = E_SHELL_SIDEBAR (widget); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); selector = E_SOURCE_SELECTOR (priv->selector); - model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + + source_list = e_memo_shell_backend_get_source_list ( + E_MEMO_SHELL_BACKEND (shell_backend)); g_signal_connect_swapped ( model, "row-changed", G_CALLBACK (memo_shell_sidebar_row_changed_cb), - object); + shell_sidebar); + + g_signal_connect_swapped ( + selector, "primary-selection-changed", + G_CALLBACK (memo_shell_sidebar_primary_selection_changed_cb), + shell_sidebar); source = NULL; uid = e_shell_settings_get_string ( @@ -434,14 +566,12 @@ memo_shell_sidebar_constructed (GObject *object) /* Listen for subsequent changes to the selector. */ g_signal_connect_swapped ( - widget, "selection-changed", + selector, "selection-changed", G_CALLBACK (memo_shell_sidebar_selection_changed_cb), - object); + shell_sidebar); - g_signal_connect_swapped ( - widget, "primary-selection-changed", - G_CALLBACK (memo_shell_sidebar_primary_selection_changed_cb), - object); + /* Chain up to parent's realize() method. */ + GTK_WIDGET_CLASS (parent_class)->realize (widget); } static guint32 @@ -507,10 +637,10 @@ memo_shell_sidebar_client_removed (EMemoShellSidebar *memo_shell_sidebar, NULL, NULL, memo_shell_sidebar); source = e_cal_get_source (client); - e_source_selector_unselect_source (selector, source); - uid = e_source_peek_uid (source); + g_hash_table_remove (client_table, uid); + e_source_selector_unselect_source (selector, source); memo_shell_sidebar_emit_status_message (memo_shell_sidebar, NULL); } @@ -519,6 +649,7 @@ static void memo_shell_sidebar_class_init (EMemoShellSidebarClass *class) { GObjectClass *object_class; + GtkWidgetClass *widget_class; EShellSidebarClass *shell_sidebar_class; parent_class = g_type_class_peek_parent (class); @@ -530,11 +661,24 @@ memo_shell_sidebar_class_init (EMemoShellSidebarClass *class) object_class->finalize = memo_shell_sidebar_finalize; object_class->constructed = memo_shell_sidebar_constructed; + widget_class = GTK_WIDGET_CLASS (class); + widget_class->realize = memo_shell_sidebar_realize; + shell_sidebar_class = E_SHELL_SIDEBAR_CLASS (class); shell_sidebar_class->check_state = memo_shell_sidebar_check_state; class->client_removed = memo_shell_sidebar_client_removed; + g_object_class_install_property ( + object_class, + PROP_DEFAULT_CLIENT, + g_param_spec_object ( + "default-client", + _("Default Memo Client"), + _("Default client for memo operations"), + E_TYPE_CAL, + G_PARAM_READABLE)); + g_object_class_install_property ( object_class, PROP_SELECTOR, @@ -645,6 +789,15 @@ e_memo_shell_sidebar_get_clients (EMemoShellSidebar *memo_shell_sidebar) return g_hash_table_get_values (client_table); } +ECal * +e_memo_shell_sidebar_get_default_client (EMemoShellSidebar *memo_shell_sidebar) +{ + g_return_val_if_fail ( + E_IS_MEMO_SHELL_SIDEBAR (memo_shell_sidebar), NULL); + + return memo_shell_sidebar->priv->default_client; +} + ESourceSelector * e_memo_shell_sidebar_get_selector (EMemoShellSidebar *memo_shell_sidebar) { @@ -658,8 +811,10 @@ void e_memo_shell_sidebar_add_source (EMemoShellSidebar *memo_shell_sidebar, ESource *source) { + ECalSourceType source_type; ESourceSelector *selector; GHashTable *client_table; + ECal *default_client; ECal *client; const gchar *uid; const gchar *uri; @@ -668,7 +823,9 @@ e_memo_shell_sidebar_add_source (EMemoShellSidebar *memo_shell_sidebar, g_return_if_fail (E_IS_MEMO_SHELL_SIDEBAR (memo_shell_sidebar)); g_return_if_fail (E_IS_SOURCE (source)); + source_type = E_CAL_SOURCE_TYPE_JOURNAL; client_table = memo_shell_sidebar->priv->client_table; + default_client = memo_shell_sidebar->priv->default_client; selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar); uid = e_source_peek_uid (source); @@ -677,7 +834,20 @@ e_memo_shell_sidebar_add_source (EMemoShellSidebar *memo_shell_sidebar, if (client != NULL) return; - client = e_auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_JOURNAL); + if (default_client != NULL) { + ESource *default_source; + const gchar *default_uid; + + default_source = e_cal_get_source (default_client); + default_uid = e_source_peek_uid (default_source); + + if (g_strcmp0 (uid, default_uid) == 0) + client = g_object_ref (default_client); + } + + if (client == NULL) + client = e_auth_new_cal_from_source (source, source_type); + g_return_if_fail (client != NULL); g_signal_connect_swapped ( diff --git a/modules/calendar/e-memo-shell-sidebar.h b/modules/calendar/e-memo-shell-sidebar.h index 8eabee103d..0d32c7b429 100644 --- a/modules/calendar/e-memo-shell-sidebar.h +++ b/modules/calendar/e-memo-shell-sidebar.h @@ -84,6 +84,8 @@ void e_memo_shell_sidebar_register_type GtkWidget * e_memo_shell_sidebar_new(EShellView *shell_view); GList * e_memo_shell_sidebar_get_clients (EMemoShellSidebar *memo_shell_sidebar); +ECal * e_memo_shell_sidebar_get_default_client + (EMemoShellSidebar *memo_shell_sidebar); ESourceSelector * e_memo_shell_sidebar_get_selector (EMemoShellSidebar *memo_shell_sidebar); diff --git a/modules/calendar/e-memo-shell-view-private.c b/modules/calendar/e-memo-shell-view-private.c index 2caa6ec2ed..decd9002d0 100644 --- a/modules/calendar/e-memo-shell-view-private.c +++ b/modules/calendar/e-memo-shell-view-private.c @@ -38,8 +38,6 @@ memo_shell_view_model_row_appended_cb (EMemoShellView *memo_shell_view, memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar; e_memo_shell_sidebar_add_source (memo_shell_sidebar, source); - - e_cal_model_add_client (model, client); } static void @@ -273,6 +271,11 @@ e_memo_shell_view_private_constructed (EMemoShellView *memo_shell_view) (GHookFunc) e_memo_shell_view_update_search_filter, memo_shell_view); + /* Keep the ECalModel in sync with the sidebar. */ + e_binding_new ( + shell_sidebar, "default-client", + model, "default-client"); + e_memo_shell_view_actions_init (memo_shell_view); e_memo_shell_view_update_sidebar (memo_shell_view); e_memo_shell_view_update_search_filter (memo_shell_view); diff --git a/modules/calendar/e-task-shell-sidebar.c b/modules/calendar/e-task-shell-sidebar.c index 7708f7234a..347c64b8b3 100644 --- a/modules/calendar/e-task-shell-sidebar.c +++ b/modules/calendar/e-task-shell-sidebar.c @@ -32,8 +32,8 @@ #include "calendar/gui/e-task-list-selector.h" #include "calendar/gui/misc.h" -#include "e-task-shell-backend.h" #include "e-task-shell-view.h" +#include "e-task-shell-backend.h" #define E_TASK_SHELL_SIDEBAR_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ @@ -44,10 +44,20 @@ struct _ETaskShellSidebarPrivate { /* UID -> Client */ GHashTable *client_table; + + /* The default client is for ECalModel. It follows the + * sidebar's primary selection, even if the highlighted + * source is not selected. The tricky part is we don't + * update the property until the client is successfully + * opened. So the user first highlights a source, then + * sometime later we update our default-client property + * which is bound by an EBinding to ECalModel. */ + ECal *default_client; }; enum { PROP_0, + PROP_DEFAULT_CLIENT, PROP_SELECTOR }; @@ -161,6 +171,7 @@ task_shell_sidebar_client_opened_cb (ETaskShellSidebar *task_shell_sidebar, EShellView *shell_view; EShellWindow *shell_window; EShellSidebar *shell_sidebar; + const gchar *message; shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar); shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); @@ -170,39 +181,121 @@ task_shell_sidebar_client_opened_cb (ETaskShellSidebar *task_shell_sidebar, status == E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED) e_auth_cal_forget_password (client); + /* Handle errors. */ switch (status) { case E_CALENDAR_STATUS_OK: - g_signal_handlers_disconnect_matched ( - client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, - task_shell_sidebar_client_opened_cb, NULL); - - task_shell_sidebar_emit_status_message ( - task_shell_sidebar, _("Loading tasks")); - task_shell_sidebar_emit_client_added ( - task_shell_sidebar, client); - task_shell_sidebar_emit_status_message ( - task_shell_sidebar, NULL); break; case E_CALENDAR_STATUS_AUTHENTICATION_FAILED: e_cal_open_async (client, FALSE); - break; + return; case E_CALENDAR_STATUS_BUSY: - break; + return; case E_CALENDAR_STATUS_REPOSITORY_OFFLINE: e_alert_run_dialog_for_args ( GTK_WINDOW (shell_window), "calendar:prompt-no-contents-offline-tasks", NULL); - break; + /* fall through */ default: - task_shell_sidebar_emit_client_removed ( - task_shell_sidebar, client); + e_task_shell_sidebar_remove_source ( + task_shell_sidebar, + e_cal_get_source (client)); + return; + } + + g_assert (status == E_CALENDAR_STATUS_OK); + + g_signal_handlers_disconnect_matched ( + client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + task_shell_sidebar_client_opened_cb, NULL); + + message = _("Loading tasks"); + task_shell_sidebar_emit_status_message (task_shell_sidebar, message); + task_shell_sidebar_emit_client_added (task_shell_sidebar, client); + task_shell_sidebar_emit_status_message (task_shell_sidebar, NULL); +} + +static void +task_shell_sidebar_default_opened_cb (ETaskShellSidebar *task_shell_sidebar, + ECalendarStatus status, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + + shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + if (status == E_CALENDAR_STATUS_AUTHENTICATION_FAILED || + status == E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED) + e_auth_cal_forget_password (client); + + /* Handle errors. */ + switch (status) { + case E_CALENDAR_STATUS_OK: break; + + case E_CALENDAR_STATUS_AUTHENTICATION_FAILED: + e_cal_open_async (client, FALSE); + return; + + case E_CALENDAR_STATUS_BUSY: + return; + + default: + e_task_shell_sidebar_remove_source ( + task_shell_sidebar, + e_cal_get_source (client)); + return; } + + g_assert (status == E_CALENDAR_STATUS_OK); + + g_signal_handlers_disconnect_matched ( + client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + task_shell_sidebar_default_opened_cb, NULL); + + g_object_notify (G_OBJECT (task_shell_sidebar), "default-client"); +} + +static void +task_shell_sidebar_set_default (ETaskShellSidebar *task_shell_sidebar, + ESource *source) +{ + ECalSourceType source_type; + GHashTable *client_table; + ECal *client; + const gchar *uid; + + source_type = E_CAL_SOURCE_TYPE_TODO; + client_table = task_shell_sidebar->priv->client_table; + + uid = e_source_peek_uid (source); + client = g_hash_table_lookup (client_table, uid); + + if (task_shell_sidebar->priv->default_client != NULL) + g_object_unref (task_shell_sidebar->priv->default_client); + + if (client != NULL) + g_object_ref (client); + else + client = e_auth_new_cal_from_source (source, source_type); + + task_shell_sidebar->priv->default_client = client; + g_return_if_fail (client != NULL); + + g_signal_connect_swapped ( + client, "cal-opened", + G_CALLBACK (task_shell_sidebar_default_opened_cb), + task_shell_sidebar); + + e_cal_open_async (client, FALSE); } static void @@ -290,6 +383,8 @@ task_shell_sidebar_primary_selection_changed_cb (ETaskShellSidebar *task_shell_s e_shell_settings_set_string ( shell_settings, "cal-primary-task-list", e_source_peek_uid (source)); + + task_shell_sidebar_set_default (task_shell_sidebar, source); } static void @@ -299,9 +394,17 @@ task_shell_sidebar_get_property (GObject *object, GParamSpec *pspec) { switch (property_id) { + case PROP_DEFAULT_CLIENT: + g_value_set_object ( + value, + e_task_shell_sidebar_get_default_client ( + E_TASK_SHELL_SIDEBAR (object))); + return; + case PROP_SELECTOR: g_value_set_object ( - value, e_task_shell_sidebar_get_selector ( + value, + e_task_shell_sidebar_get_selector ( E_TASK_SHELL_SIDEBAR (object))); return; } @@ -321,6 +424,11 @@ task_shell_sidebar_dispose (GObject *object) priv->selector = NULL; } + if (priv->default_client != NULL) { + g_object_unref (priv->default_client); + priv->default_client = NULL; + } + g_hash_table_remove_all (priv->client_table); /* Chain up to parent's dispose() method. */ @@ -344,20 +452,13 @@ static void task_shell_sidebar_constructed (GObject *object) { ETaskShellSidebarPrivate *priv; - EShell *shell; EShellView *shell_view; EShellBackend *shell_backend; EShellSidebar *shell_sidebar; - EShellSettings *shell_settings; - ESourceSelector *selector; ESourceList *source_list; - ESource *source; GtkContainer *container; - GtkTreeModel *model; GtkWidget *widget; AtkObject *a11y; - GSList *list, *iter; - gchar *uid; priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (object); @@ -368,9 +469,6 @@ task_shell_sidebar_constructed (GObject *object) shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); shell_backend = e_shell_view_get_shell_backend (shell_view); - shell = e_shell_backend_get_shell (shell_backend); - shell_settings = e_shell_get_shell_settings (shell); - source_list = e_task_shell_backend_get_source_list ( E_TASK_SHELL_BACKEND (shell_backend)); @@ -394,16 +492,52 @@ task_shell_sidebar_constructed (GObject *object) atk_object_set_name (a11y, _("Task List Selector")); priv->selector = g_object_ref (widget); gtk_widget_show (widget); +} + +static void +task_shell_sidebar_realize (GtkWidget *widget) +{ + ETaskShellSidebarPrivate *priv; + EShell *shell; + EShellView *shell_view; + EShellBackend *shell_backend; + EShellSidebar *shell_sidebar; + EShellSettings *shell_settings; + ESourceSelector *selector; + ESourceList *source_list; + ESource *source; + GtkTreeModel *model; + GSList *list, *iter; + gchar *uid; - /* Restore the selector state from the last session. */ + priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (widget); + + /* Restore the selector state from the last session. We do this + * in realize() instead of constructed() so the shell view has a + * chance to connect handlers to our signals. */ + + shell_sidebar = E_SHELL_SIDEBAR (widget); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); selector = E_SOURCE_SELECTOR (priv->selector); - model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + + source_list = e_task_shell_backend_get_source_list ( + E_TASK_SHELL_BACKEND (shell_backend)); g_signal_connect_swapped ( model, "row-changed", G_CALLBACK (task_shell_sidebar_row_changed_cb), - object); + shell_sidebar); + + g_signal_connect_swapped ( + selector, "primary-selection-changed", + G_CALLBACK (task_shell_sidebar_primary_selection_changed_cb), + shell_sidebar); source = NULL; uid = e_shell_settings_get_string ( @@ -432,14 +566,12 @@ task_shell_sidebar_constructed (GObject *object) /* Listen for subsequent changes to the selector. */ g_signal_connect_swapped ( - widget, "selection-changed", + selector, "selection-changed", G_CALLBACK (task_shell_sidebar_selection_changed_cb), - object); + shell_sidebar); - g_signal_connect_swapped ( - widget, "primary-selection-changed", - G_CALLBACK (task_shell_sidebar_primary_selection_changed_cb), - object); + /* Chain up to parent's realize() method. */ + GTK_WIDGET_CLASS (parent_class)->realize (widget); } static guint32 @@ -505,10 +637,10 @@ task_shell_sidebar_client_removed (ETaskShellSidebar *task_shell_sidebar, NULL, NULL, task_shell_sidebar); source = e_cal_get_source (client); - e_source_selector_unselect_source (selector, source); - uid = e_source_peek_uid (source); + g_hash_table_remove (client_table, uid); + e_source_selector_unselect_source (selector, source); task_shell_sidebar_emit_status_message (task_shell_sidebar, NULL); } @@ -517,6 +649,7 @@ static void task_shell_sidebar_class_init (ETaskShellSidebarClass *class) { GObjectClass *object_class; + GtkWidgetClass *widget_class; EShellSidebarClass *shell_sidebar_class; parent_class = g_type_class_peek_parent (class); @@ -528,11 +661,24 @@ task_shell_sidebar_class_init (ETaskShellSidebarClass *class) object_class->finalize = task_shell_sidebar_finalize; object_class->constructed = task_shell_sidebar_constructed; + widget_class = GTK_WIDGET_CLASS (class); + widget_class->realize = task_shell_sidebar_realize; + shell_sidebar_class = E_SHELL_SIDEBAR_CLASS (class); shell_sidebar_class->check_state = task_shell_sidebar_check_state; class->client_removed = task_shell_sidebar_client_removed; + g_object_class_install_property ( + object_class, + PROP_DEFAULT_CLIENT, + g_param_spec_object ( + "default-client", + _("Default Task Client"), + _("Default client for task operations"), + E_TYPE_CAL, + G_PARAM_READABLE)); + g_object_class_install_property ( object_class, PROP_SELECTOR, @@ -643,6 +789,15 @@ e_task_shell_sidebar_get_clients (ETaskShellSidebar *task_shell_sidebar) return g_hash_table_get_values (client_table); } +ECal * +e_task_shell_sidebar_get_default_client (ETaskShellSidebar *task_shell_sidebar) +{ + g_return_val_if_fail ( + E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar), NULL); + + return task_shell_sidebar->priv->default_client; +} + ESourceSelector * e_task_shell_sidebar_get_selector (ETaskShellSidebar *task_shell_sidebar) { @@ -656,8 +811,10 @@ void e_task_shell_sidebar_add_source (ETaskShellSidebar *task_shell_sidebar, ESource *source) { + ECalSourceType source_type; ESourceSelector *selector; GHashTable *client_table; + ECal *default_client; ECal *client; const gchar *uid; const gchar *uri; @@ -666,7 +823,9 @@ e_task_shell_sidebar_add_source (ETaskShellSidebar *task_shell_sidebar, g_return_if_fail (E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar)); g_return_if_fail (E_IS_SOURCE (source)); + source_type = E_CAL_SOURCE_TYPE_TODO; client_table = task_shell_sidebar->priv->client_table; + default_client = task_shell_sidebar->priv->default_client; selector = e_task_shell_sidebar_get_selector (task_shell_sidebar); uid = e_source_peek_uid (source); @@ -675,7 +834,20 @@ e_task_shell_sidebar_add_source (ETaskShellSidebar *task_shell_sidebar, if (client != NULL) return; - client = e_auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_TODO); + if (default_client != NULL) { + ESource *default_source; + const gchar *default_uid; + + default_source = e_cal_get_source (default_client); + default_uid = e_source_peek_uid (default_source); + + if (g_strcmp0 (uid, default_uid) == 0) + client = g_object_ref (default_client); + } + + if (client == NULL) + client = e_auth_new_cal_from_source (source, source_type); + g_return_if_fail (client != NULL); g_signal_connect_swapped ( diff --git a/modules/calendar/e-task-shell-sidebar.h b/modules/calendar/e-task-shell-sidebar.h index 6a18279753..152c7ca32f 100644 --- a/modules/calendar/e-task-shell-sidebar.h +++ b/modules/calendar/e-task-shell-sidebar.h @@ -84,6 +84,8 @@ void e_task_shell_sidebar_register_type GtkWidget * e_task_shell_sidebar_new(EShellView *shell_view); GList * e_task_shell_sidebar_get_clients (ETaskShellSidebar *task_shell_sidebar); +ECal * e_task_shell_sidebar_get_default_client + (ETaskShellSidebar *task_shell_sidebar); ESourceSelector * e_task_shell_sidebar_get_selector (ETaskShellSidebar *task_shell_sidebar); diff --git a/modules/calendar/e-task-shell-view-private.c b/modules/calendar/e-task-shell-view-private.c index 1cdf6697ff..ada67c7c08 100644 --- a/modules/calendar/e-task-shell-view-private.c +++ b/modules/calendar/e-task-shell-view-private.c @@ -38,8 +38,6 @@ task_shell_view_model_row_appended_cb (ETaskShellView *task_shell_view, task_shell_sidebar = task_shell_view->priv->task_shell_sidebar; e_task_shell_sidebar_add_source (task_shell_sidebar, source); - - e_cal_model_add_client (model, client); } static void @@ -326,11 +324,15 @@ e_task_shell_view_private_constructed (ETaskShellView *task_shell_view) task_shell_view); /* Listen for configuration changes. */ - e_mutual_binding_new ( shell_settings, "cal-confirm-purge", task_shell_view, "confirm-purge"); + /* Keep the ECalModel in sync with the sidebar. */ + e_binding_new ( + shell_sidebar, "default-client", + model, "default-client"); + /* Hide Completed Tasks (enable/units/value) */ g_signal_connect_swapped ( shell_settings, "notify::cal-hide-completed-tasks", -- cgit