From 1bed00795bf092ad6e9e076eccf7cc2a8c20cb27 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Mon, 6 Oct 2008 17:41:31 +0000 Subject: Baseline cut at the Calendar sidebar and module. Pretty much identical to Tasks and Memos so far. Now for the interesting part... svn path=/branches/kill-bonobo/; revision=36573 --- calendar/gui/calendar-component.c | 258 ------------------ calendar/gui/gnome-cal.c | 56 ---- calendar/gui/memos-component.c | 23 -- calendar/modules/e-cal-shell-module.c | 87 +++++- calendar/modules/e-cal-shell-sidebar.c | 483 ++++++++++++++++++++++++++++++++- calendar/modules/e-cal-shell-sidebar.h | 19 +- 6 files changed, 569 insertions(+), 357 deletions(-) (limited to 'calendar') diff --git a/calendar/gui/calendar-component.c b/calendar/gui/calendar-component.c index cdec256ac3..f2896a5990 100644 --- a/calendar/gui/calendar-component.c +++ b/calendar/gui/calendar-component.c @@ -87,16 +87,11 @@ typedef struct GList *notifications; - EUserCreatableItemsHandler *creatable_items_handler; - - EActivityHandler *activity_handler; - float vpane_pos; } CalendarComponentView; struct _CalendarComponentPrivate { - GConfClient *gconf_client; int gconf_notify_id; ESourceList *source_list; @@ -159,54 +154,6 @@ is_in_uids (GSList *uids, ESource *source) return FALSE; } -static void -update_uris_for_selection (CalendarComponentView *component_view) -{ - GSList *selection, *l, *uids_selected = NULL; - - selection = e_source_selector_get_selection (E_SOURCE_SELECTOR (component_view->source_selector)); - - for (l = component_view->source_selection; l; l = l->next) { - ESource *old_selected_source = l->data; - - if (!is_in_selection (selection, old_selected_source)) - gnome_calendar_remove_source (component_view->calendar, E_CAL_SOURCE_TYPE_EVENT, old_selected_source); - } - - for (l = selection; l; l = l->next) { - ESource *selected_source = l->data; - - if (gnome_calendar_add_source (component_view->calendar, E_CAL_SOURCE_TYPE_EVENT, selected_source)) - uids_selected = g_slist_append (uids_selected, (char *) e_source_peek_uid (selected_source)); - } - - e_source_selector_free_selection (component_view->source_selection); - component_view->source_selection = selection; - - /* Save the selection for next time we start up */ - calendar_config_set_calendars_selected (uids_selected); - g_slist_free (uids_selected); -} - -static void -update_uri_for_primary_selection (CalendarComponentView *component_view) -{ - ESource *source; - - source = e_source_selector_peek_primary_selection (E_SOURCE_SELECTOR (component_view->source_selector)); - if (!source) - return; - - /* Set the default */ - gnome_calendar_set_default_source (component_view->calendar, E_CAL_SOURCE_TYPE_EVENT, source); - - /* Make sure we are embedded first */ - calendar_control_sensitize_calendar_commands (component_view->view_control, component_view->calendar, TRUE); - - /* Save the selection for next time we start up */ - calendar_config_set_primary_calendar (e_source_peek_uid (source)); -} - static void update_selection (CalendarComponentView *component_view) { @@ -454,18 +401,6 @@ popup_event_cb(ESourceSelector *selector, ESource *insource, GdkEventButton *eve return TRUE; } -static void -source_selection_changed_cb (ESourceSelector *selector, CalendarComponentView *component_view) -{ - update_uris_for_selection (component_view); -} - -static void -primary_source_selection_changed_cb (ESourceSelector *selector, CalendarComponentView *component_view) -{ - update_uri_for_primary_selection (component_view); -} - static void source_changed_cb (ESource *source, GnomeCalendar *calendar) { @@ -624,29 +559,6 @@ impl_handleURI (PortableServer_Servant servant, const char *uri, CORBA_Environme } } -static void -impl_upgradeFromVersion (PortableServer_Servant servant, - CORBA_short major, - CORBA_short minor, - CORBA_short revision, - CORBA_Environment *ev) -{ - GError *err = NULL; - CalendarComponent *calendar_component = CALENDAR_COMPONENT (bonobo_object_from_servant (servant)); - - if (!migrate_calendars (calendar_component, major, minor, revision, &err)) { - GNOME_Evolution_Component_UpgradeFailed *failedex; - - failedex = GNOME_Evolution_Component_UpgradeFailed__alloc(); - failedex->what = CORBA_string_dup(_("Failed upgrading calendars.")); - failedex->why = CORBA_string_dup(err->message); - CORBA_exception_set(ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Component_UpgradeFailed, failedex); - } - - if (err) - g_error_free(err); -} - static void config_create_ecal_changed_cb (GConfClient *client, guint id, GConfEntry *entry, gpointer data) { @@ -745,70 +657,6 @@ setup_create_ecal (CalendarComponent *calendar_component, CalendarComponentView return priv->create_ecal; } -static gboolean -create_new_event (CalendarComponent *calendar_component, CalendarComponentView *component_view, gboolean is_allday, gboolean is_meeting) -{ - ECal *ecal; - ECalendarView *view; - - ecal = setup_create_ecal (calendar_component, component_view); - if (!ecal) - return FALSE; - - if (component_view && (view = E_CALENDAR_VIEW (gnome_calendar_get_current_view_widget (component_view->calendar)))) { - e_calendar_view_new_appointment_full (view, is_allday, is_meeting, TRUE); - } else { - ECalComponent *comp; - CompEditor *editor; - CompEditorFlags flags; - - flags = COMP_EDITOR_USER_ORG | COMP_EDITOR_NEW_ITEM; - if (is_meeting) - flags |= COMP_EDITOR_MEETING; - comp = cal_comp_event_new_with_current_time (ecal, is_allday); - editor = event_editor_new (ecal, flags); - e_cal_component_commit_sequence (comp); - - comp_editor_edit_comp (editor, comp); - if (is_meeting) - event_editor_show_meeting (EVENT_EDITOR (editor)); - gtk_window_present (GTK_WINDOW (editor)); - - e_comp_editor_registry_add (comp_editor_registry, editor, TRUE); - } - - return TRUE; -} - -static void -create_local_item_cb (EUserCreatableItemsHandler *handler, const char *item_type_name, void *data) -{ - CalendarComponent *calendar_component = data; - CalendarComponentPrivate *priv; - CalendarComponentView *component_view = NULL; - GList *l; - - priv = calendar_component->priv; - - for (l = priv->views; l; l = l->next) { - component_view = l->data; - - if (component_view->creatable_items_handler == handler) - break; - - component_view = NULL; - } - - if (strcmp (item_type_name, CREATE_EVENT_ID) == 0) - create_new_event (calendar_component, component_view, FALSE, FALSE); - else if (strcmp (item_type_name, CREATE_ALLDAY_EVENT_ID) == 0) - create_new_event (calendar_component, component_view, TRUE, FALSE); - else if (strcmp (item_type_name, CREATE_MEETING_ID) == 0) - create_new_event (calendar_component, component_view, FALSE, TRUE); - else if (strcmp (item_type_name, CREATE_CALENDAR_ID) == 0) - calendar_setup_new_calendar (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (component_view->calendar)))); -} - static CalendarComponentView * create_component_view (CalendarComponent *calendar_component) { @@ -881,25 +729,16 @@ create_component_view (CalendarComponent *calendar_component) /* Create status bar */ statusbar_widget = e_task_bar_new (); - component_view->activity_handler = e_activity_handler_new (); - e_activity_handler_attach_task_bar (component_view->activity_handler, E_TASK_BAR (statusbar_widget)); gtk_widget_show (statusbar_widget); component_view->statusbar_control = bonobo_control_new (statusbar_widget); - gnome_calendar_set_activity_handler (component_view->calendar, component_view->activity_handler); - /* connect after setting the initial selections, or we'll get unwanted calls to calendar_control_sensitize_calendar_commands */ - g_signal_connect (component_view->source_selector, "selection_changed", - G_CALLBACK (source_selection_changed_cb), component_view); - g_signal_connect (component_view->source_selector, "primary_selection_changed", - G_CALLBACK (primary_source_selection_changed_cb), component_view); g_signal_connect (component_view->source_selector, "popup_event", G_CALLBACK (popup_event_cb), component_view); /* Set up the "new" item handler */ - component_view->creatable_items_handler = e_user_creatable_items_handler_new ("calendar", create_local_item_cb, calendar_component); g_signal_connect (component_view->view_control, "activate", G_CALLBACK (control_activate_cb), component_view); /* Load the selection from the last run */ @@ -952,12 +791,6 @@ destroy_component_view (CalendarComponentView *component_view) calendar_config_remove_notification (GPOINTER_TO_UINT (l->data)); g_list_free (component_view->notifications); - if (component_view->creatable_items_handler) - g_object_unref (component_view->creatable_items_handler); - - if (component_view->activity_handler) - g_object_unref (component_view->activity_handler); - if (component_view->task_source_selection) { g_slist_foreach (component_view->task_source_selection, (GFunc) g_free, NULL); g_slist_free (component_view->task_source_selection); @@ -992,62 +825,6 @@ view_destroyed_cb (gpointer data, GObject *where_the_object_was) } } -static GNOME_Evolution_ComponentView -impl_createView (PortableServer_Servant servant, - GNOME_Evolution_ShellView parent, - CORBA_boolean select_item, - CORBA_Environment *ev) -{ - CalendarComponent *calendar_component = CALENDAR_COMPONENT (bonobo_object_from_servant (servant)); - CalendarComponentPrivate *priv; - CalendarComponentView *component_view; - EComponentView *ecv; - - priv = calendar_component->priv; - - /* Create the calendar component view */ - component_view = create_component_view (calendar_component); - if (!component_view) { - /* FIXME Should we describe the problem in a control? */ - bonobo_exception_set (ev, ex_GNOME_Evolution_Component_Failed); - - return CORBA_OBJECT_NIL; - } - - g_object_weak_ref (G_OBJECT (component_view->view_control), view_destroyed_cb, calendar_component); - priv->views = g_list_append (priv->views, component_view); - - /* TODO: Make CalendarComponentView just subclass EComponentView */ - ecv = e_component_view_new_controls (parent, "calendar", component_view->sidebar_control, - component_view->view_control, component_view->statusbar_control); - - return BONOBO_OBJREF(ecv); -} - - -static void -impl_requestCreateItem (PortableServer_Servant servant, - const CORBA_char *item_type_name, - CORBA_Environment *ev) -{ - CalendarComponent *calendar_component = CALENDAR_COMPONENT (bonobo_object_from_servant (servant)); - gboolean result = FALSE; - - if (strcmp (item_type_name, CREATE_EVENT_ID) == 0) - result = create_new_event (calendar_component, NULL, FALSE, FALSE); - else if (strcmp (item_type_name, CREATE_ALLDAY_EVENT_ID) == 0) - result = create_new_event (calendar_component, NULL, TRUE, FALSE); - else if (strcmp (item_type_name, CREATE_MEETING_ID) == 0) - result = create_new_event (calendar_component, NULL, FALSE, TRUE); - else if (strcmp (item_type_name, CREATE_CALENDAR_ID) == 0) - /* FIXME Should we use the last opened window? */ - calendar_setup_new_calendar (NULL); - else - bonobo_exception_set (ev, ex_GNOME_Evolution_Component_UnknownType); - - if (!result) - bonobo_exception_set (ev, ex_GNOME_Evolution_Component_Failed); -} /* GObject methods. */ @@ -1063,11 +840,6 @@ impl_dispose (GObject *object) priv->source_list = NULL; } - if (priv->gconf_client != NULL) { - g_object_unref (priv->gconf_client); - priv->gconf_client = NULL; - } - if (priv->create_ecal) { g_object_unref (priv->create_ecal); priv->create_ecal = NULL; @@ -1089,24 +861,6 @@ impl_dispose (GObject *object) (* G_OBJECT_CLASS (parent_class)->dispose) (object); } -static void -impl_finalize (GObject *object) -{ - CalendarComponentPrivate *priv = CALENDAR_COMPONENT (object)->priv; - GList *l; - - for (l = priv->views; l; l = l->next) { - CalendarComponentView *component_view = l->data; - - destroy_component_view (component_view); - } - g_list_free (priv->views); - - g_free (priv); - - (* G_OBJECT_CLASS (parent_class)->finalize) (object); -} - static void calendar_component_class_init (CalendarComponentClass *class) { @@ -1115,13 +869,9 @@ calendar_component_class_init (CalendarComponentClass *class) parent_class = g_type_class_peek_parent (class); - epv->upgradeFromVersion = impl_upgradeFromVersion; - epv->createView = impl_createView; - epv->requestCreateItem = impl_requestCreateItem; epv->handleURI = impl_handleURI; object_class->dispose = impl_dispose; - object_class->finalize = impl_finalize; } static void @@ -1130,12 +880,6 @@ calendar_component_init (CalendarComponent *component) CalendarComponentPrivate *priv; guint not; - priv = g_new0 (CalendarComponentPrivate, 1); - - /* EPFIXME: Should use a custom one instead? Also we should add - * calendar_component_peek_gconf_client(). */ - priv->gconf_client = gconf_client_get_default (); - not = calendar_config_add_notification_primary_calendar (config_primary_selection_changed_cb, component); priv->notifications = g_list_prepend (priv->notifications, GUINT_TO_POINTER (not)); @@ -1147,5 +891,3 @@ calendar_component_init (CalendarComponent *component) if (!e_cal_get_sources (&priv->memo_source_list, E_CAL_SOURCE_TYPE_JOURNAL, NULL)) ; } - -BONOBO_TYPE_FUNC_FULL (CalendarComponent, GNOME_Evolution_Component, PARENT_TYPE, calendar_component) diff --git a/calendar/gui/gnome-cal.c b/calendar/gui/gnome-cal.c index 1fc02bd1ac..0bea1e472a 100644 --- a/calendar/gui/gnome-cal.c +++ b/calendar/gui/gnome-cal.c @@ -1318,37 +1318,6 @@ working_days_changed_cb (GConfClient *client, guint id, GConfEntry *entry, gpoin set_working_days (calendar); } -static void -set_timezone (GnomeCalendar *calendar) -{ - GnomeCalendarPrivate *priv; - int i; - - priv = calendar->priv; - - priv->zone = calendar_config_get_icaltimezone (); - - for (i = 0; i < E_CAL_SOURCE_TYPE_LAST; i++) { - GList *l; - - for (l = priv->clients_list[i]; l != NULL; l = l->next) { - ECal *client = l->data; - - if (e_cal_get_load_state (client) == E_CAL_LOAD_LOADED) - /* FIXME Error checking */ - e_cal_set_default_timezone (client, priv->zone, NULL); - } - - if (priv->default_client[i] - && e_cal_get_load_state (priv->default_client[i]) == E_CAL_LOAD_LOADED) - /* FIXME Error checking */ - e_cal_set_default_timezone (priv->default_client[i], priv->zone, NULL); - } - - if (priv->views [priv->current_view_type]) - e_calendar_view_set_timezone (priv->views [priv->current_view_type], priv->zone); -} - static void timezone_changed_cb (GConfClient *client, guint id, GConfEntry *entry, gpointer data) { @@ -2993,31 +2962,6 @@ open_ecal (GnomeCalendar *gcal, ECal *cal, gboolean only_if_exists, open_func of return TRUE; } -/* Callback when we get an error message from the backend */ -static void -backend_error_cb (ECal *client, const char *message, gpointer data) -{ - GnomeCalendar *gcal; - GtkDialog *dialog; - char *uristr; - - gcal = GNOME_CALENDAR (data); - - uristr = get_uri_without_password (e_cal_get_uri (client)); - - dialog = GTK_DIALOG (gtk_message_dialog_new ( - GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (gcal))), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - _("Error on %s:\n %s"), - uristr, message)); - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (GTK_WIDGET (dialog)); - - g_free (uristr); -} - /* Callback when the backend dies */ static void backend_died_cb (ECal *ecal, gpointer data) diff --git a/calendar/gui/memos-component.c b/calendar/gui/memos-component.c index 310e8d4351..51afab099f 100644 --- a/calendar/gui/memos-component.c +++ b/calendar/gui/memos-component.c @@ -157,29 +157,6 @@ source_selection_changed_cb (ESourceSelector *selector, MemosComponentView *comp /* Evolution::Component CORBA methods */ -static void -impl_upgradeFromVersion (PortableServer_Servant servant, - CORBA_short major, - CORBA_short minor, - CORBA_short revision, - CORBA_Environment *ev) -{ - GError *err = NULL; - MemosComponent *component = MEMOS_COMPONENT (bonobo_object_from_servant (servant)); - - if (!migrate_memos(component, major, minor, revision, &err)) { - GNOME_Evolution_Component_UpgradeFailed *failedex; - - failedex = GNOME_Evolution_Component_UpgradeFailed__alloc(); - failedex->what = CORBA_string_dup(_("Failed upgrading memos.")); - failedex->why = CORBA_string_dup(err->message); - CORBA_exception_set(ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Component_UpgradeFailed, failedex); - } - - if (err) - g_error_free(err); -} - static gboolean update_single_object (ECal *client, icalcomponent *icalcomp, gboolean fail_on_modify) { diff --git a/calendar/modules/e-cal-shell-module.c b/calendar/modules/e-cal-shell-module.c index 1dff363a0f..17c4b2152b 100644 --- a/calendar/modules/e-cal-shell-module.c +++ b/calendar/modules/e-cal-shell-module.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,11 @@ #include "shell/e-shell-module.h" #include "shell/e-shell-window.h" +#include "calendar/common/authentication.h" #include "calendar/gui/calendar-config.h" +#include "calendar/gui/comp-util.h" +#include "calendar/gui/dialogs/calendar-setup.h" +#include "calendar/gui/dialogs/event-editor.h" #include "e-cal-shell-view.h" #include "e-cal-shell-module-migrate.h" @@ -284,27 +289,85 @@ cal_module_ensure_sources (EShellModule *shell_module) } static void -action_appointment_new_cb (GtkAction *action, - EShellWindow *shell_window) +cal_module_cal_opened_cb (ECal *cal, + ECalendarStatus status, + GtkAction *action) { -} + ECalComponent *comp; + CompEditor *editor; + CompEditorFlags flags = 0; + const gchar *action_name; + gboolean all_day; + + /* XXX Handle errors better. */ + if (status != E_CALENDAR_STATUS_OK) + return; -static void -action_appointment_all_day_new_cb (GtkAction *action, - EShellWindow *shell_window) -{ + action_name = gtk_action_get_name (action); + + flags |= COMP_EDITOR_NEW_ITEM; + flags |= COMP_EDITOR_USER_ORG; + if (strcmp (action_name, "meeting-new") == 0) + flags |= COMP_EDITOR_MEETING; + + all_day = (strcmp (action_name, "appointment-all-day-new") == 0); + + editor = event_editor_new (cal, flags); + comp = cal_comp_event_new_with_current_time (cal, all_day); + comp_editor_edit_comp (editor, comp); + + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (comp); + g_object_unref (cal); } static void -action_meeting_new_cb (GtkAction *action, - EShellWindow *shell_window) +action_event_new_cb (GtkAction *action, + EShellWindow *shell_window) { + ECal *cal = NULL; + ECalSourceType source_type; + ESourceList *source_list; + gchar *uid; + + /* This callback is used for both appointments and meetings. */ + + source_type = E_CAL_SOURCE_TYPE_EVENT; + + if (!e_cal_get_sources (&source_list, source_type, NULL)) { + g_warning ("Could not get calendar sources from GConf!"); + return; + } + + uid = calendar_config_get_primary_calendar (); + + if (uid != NULL) { + ESource *source; + + source = e_source_list_peek_source_by_uid (source_list, uid); + if (source != NULL) + cal = auth_new_cal_from_source (source, source_type); + g_free (uid); + } + + if (cal == NULL) + cal = auth_new_cal_from_default (source_type); + + g_return_if_fail (cal != NULL); + + g_signal_connect ( + cal, "cal-opened", + G_CALLBACK (cal_module_cal_opened_cb), action); + + e_cal_open_async (cal, FALSE); } static void action_calendar_new_cb (GtkAction *action, EShellWindow *shell_window) { + calendar_setup_new_calendar (GTK_WINDOW (shell_window)); } static GtkActionEntry item_entries[] = { @@ -314,21 +377,21 @@ static GtkActionEntry item_entries[] = { N_("_Appointment"), /* XXX Need C_() here */ "a", N_("Create a new appointment"), - G_CALLBACK (action_appointment_new_cb) }, + G_CALLBACK (action_event_new_cb) }, { "appointment-all-day-new", "stock_new-24h-appointment", N_("All Day A_ppointment"), NULL, N_("Create a new all-day appointment"), - G_CALLBACK (action_appointment_all_day_new_cb) }, + G_CALLBACK (action_event_new_cb) }, { "meeting-new", "stock_new-meeting", N_("M_eeting"), "e", N_("Create a new meeting request"), - G_CALLBACK (action_meeting_new_cb) } + G_CALLBACK (action_event_new_cb) } }; static GtkActionEntry source_entries[] = { diff --git a/calendar/modules/e-cal-shell-sidebar.c b/calendar/modules/e-cal-shell-sidebar.c index b60d1660d1..bbe0c15caf 100644 --- a/calendar/modules/e-cal-shell-sidebar.c +++ b/calendar/modules/e-cal-shell-sidebar.c @@ -24,9 +24,11 @@ #include #include -#include "e-util/e-util.h" -#include "calendar/gui/gnome-cal.h" +#include "e-util/e-error.h" +#include "calendar/common/authentication.h" +#include "calendar/gui/calendar-config.h" #include "calendar/gui/e-calendar-selector.h" +#include "calendar/gui/misc.h" #include "e-cal-shell-view.h" @@ -36,6 +38,9 @@ struct _ECalShellSidebarPrivate { GtkWidget *selector; + + /* UID -> Client */ + GHashTable *client_table; }; enum { @@ -43,13 +48,261 @@ enum { PROP_SELECTOR }; +enum { + CLIENT_ADDED, + CLIENT_REMOVED, + STATUS_MESSAGE, + LAST_SIGNAL +}; + static gpointer parent_class; +static guint signals[LAST_SIGNAL]; + +static void +cal_shell_sidebar_emit_client_added (ECalShellSidebar *cal_shell_sidebar, + ECal *client) +{ + guint signal_id = signals[CLIENT_ADDED]; + + g_signal_emit (cal_shell_sidebar, signal_id, 0, client); +} + +static void +cal_shell_sidebar_emit_client_removed (ECalShellSidebar *cal_shell_sidebar, + ECal *client) +{ + guint signal_id = signals[CLIENT_REMOVED]; + + g_signal_emit (cal_shell_sidebar, signal_id, 0, client); +} + +static void +cal_shell_sidebar_emit_status_message (ECalShellSidebar *cal_shell_sidebar, + const gchar *status_message) +{ + guint signal_id = signals[STATUS_MESSAGE]; + + g_signal_emit (cal_shell_sidebar, signal_id, 0, status_message); +} + +static void +cal_shell_sidebar_update_timezone (ECalShellSidebar *cal_shell_sidebar) +{ + GHashTable *client_table; + icaltimezone *zone; + GList *values; + + zone = calendar_config_get_icaltimezone (); + client_table = cal_shell_sidebar->priv->client_table; + values = g_hash_table_get_values (client_table); + + while (values != NULL) { + ECal *client = values->data; + + if (e_cal_get_load_state (client) == E_CAL_LOAD_LOADED) + e_cal_set_default_timezone (client, zone, NULL); + + values = g_list_delete_link (values, values); + } + + /* XXX Need to call e_calendar_view_set_timezone() here but the + * sidebar is not really supposed to access content stuff. + * I guess we could emit an "update-timezone" signal here, + * but that feels wrong. Maybe this whole thing should be + * in ECalShellView instead. */ +} + +static void +cal_shell_sidebar_backend_died_cb (ECalShellSidebar *cal_shell_sidebar, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + GHashTable *client_table; + ESource *source; + const gchar *uid; + + client_table = cal_shell_sidebar->priv->client_table; + + 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); + + source = e_cal_get_source (client); + uid = e_source_peek_uid (source); + + g_object_ref (source); + + g_hash_table_remove (client_table, uid); + cal_shell_sidebar_emit_status_message (cal_shell_sidebar, NULL); + + e_error_run ( + GTK_WINDOW (shell_window), + "calendar:calendar-crashed", NULL); + + g_object_unref (source); +} + +static void +cal_shell_sidebar_backend_error_cb (ECalShellSidebar *cal_shell_sidebar, + const gchar *message, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + GtkWidget *dialog; + const gchar *uri; + gchar *uri_no_passwd; + + 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); + + uri = e_cal_get_uri (client); + uri_no_passwd = get_uri_without_password (uri); + + dialog = gtk_message_dialog_new ( + GTK_WINDOW (shell_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, + _("Error on %s\n%s"), + uri_no_passwd, message); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + g_free (uri_no_passwd); +} + +static void +cal_shell_sidebar_client_opened_cb (ECalShellSidebar *cal_shell_sidebar, + ECalendarStatus status, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + ESource *source; + + source = e_cal_get_source (client); + + 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); + + 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_BUSY: + break; + + case E_CALENDAR_STATUS_REPOSITORY_OFFLINE: + e_error_run ( + GTK_WINDOW (shell_window), + "calendar:prompt-no-contents-offline-calendar", + NULL); + break; + + default: + cal_shell_sidebar_emit_client_removed ( + cal_shell_sidebar, client); + break; + } +} + +static void +cal_shell_sidebar_row_changed_cb (ECalShellSidebar *cal_shell_sidebar, + GtkTreePath *tree_path, + GtkTreeIter *tree_iter, + GtkTreeModel *tree_model) +{ + ESourceSelector *selector; + ESource *source; + + /* XXX ESourceSelector's underlying tree store has only one + * column: ESource objects. While we're not supposed to + * know this, listening for "row-changed" signals from + * the model is easier to deal with than the selector's + * "selection-changed" signal, which doesn't tell you + * _which_ row changed. */ + + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + gtk_tree_model_get (tree_model, tree_iter, 0, &source, -1); + + /* XXX This signal gets emitted a lot while the model is being + * rebuilt, during which time we won't get a valid ESource. + * ESourceSelector should probably block this signal while + * rebuilding the model, but we'll be forgiving and not + * emit a warning. */ + if (!E_IS_SOURCE (source)) + return; + + if (e_source_selector_source_is_selected (selector, source)) + e_cal_shell_sidebar_add_source (cal_shell_sidebar, source); + else + e_cal_shell_sidebar_remove_source (cal_shell_sidebar, source); +} + +static void +cal_shell_sidebar_selection_changed_cb (ECalShellSidebar *cal_shell_sidebar, + ESourceSelector *selector) +{ + GSList *list, *iter; + + /* This signal is emitted less frequently than "row-changed", + * especially when the model is being rebuilt. So we'll take + * it easy on poor GConf. */ + + list = e_source_selector_get_selection (selector); + + for (iter = list; iter != NULL; iter = iter->next) { + ESource *source = iter->data; + + iter->data = (gpointer) e_source_peek_uid (source); + g_object_unref (source); + } + + calendar_config_set_calendars_selected (list); + + g_slist_free (list); +} + +static void +cal_shell_sidebar_primary_selection_changed_cb (ECalShellSidebar *cal_shell_sidebar, + ESourceSelector *selector) +{ + ESource *source; + const gchar *uid; + + /* XXX ESourceSelector needs a "primary-selection-uid" property + * so we can just bind the property with GConfBridge. */ + + source = e_source_selector_peek_primary_selection (selector); + if (source == NULL) + return; + + uid = e_source_peek_uid (source); + calendar_config_set_primary_calendar (uid); +} static void cal_shell_sidebar_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) + guint property_id, + GValue *value, + GParamSpec *pspec) { switch (property_id) { case PROP_SELECTOR: @@ -74,10 +327,25 @@ cal_shell_sidebar_dispose (GObject *object) priv->selector = NULL; } + g_hash_table_remove_all (priv->client_table); + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } +static void +cal_shell_sidebar_finalize (GObject *object) +{ + ECalShellSidebarPrivate *priv; + + priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (object); + + g_hash_table_destroy (priv->client_table); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + static void cal_shell_sidebar_constructed (GObject *object) { @@ -85,9 +353,14 @@ cal_shell_sidebar_constructed (GObject *object) EShellView *shell_view; EShellSidebar *shell_sidebar; ECalShellView *cal_shell_view; + ESourceSelector *selector; ESourceList *source_list; + ESource *source; GtkContainer *container; + GtkTreeModel *model; GtkWidget *widget; + GSList *list, *iter; + gchar *uid; priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (object); @@ -117,6 +390,83 @@ cal_shell_sidebar_constructed (GObject *object) gtk_container_add (container, widget); priv->selector = g_object_ref (widget); gtk_widget_show (widget); + + /* Restore the selector state from the last session. */ + + selector = E_SOURCE_SELECTOR (priv->selector); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); + + g_signal_connect_swapped ( + model, "row-changed", + G_CALLBACK (cal_shell_sidebar_row_changed_cb), + object); + + source = NULL; + uid = calendar_config_get_primary_calendar (); + if (uid != NULL) + source = e_source_list_peek_source_by_uid (source_list, uid); + if (source == NULL) + source = e_source_list_peek_source_any (source_list); + if (source != NULL) + e_source_selector_set_primary_selection (selector, source); + g_free (uid); + + list = calendar_config_get_tasks_selected (); + for (iter = list; iter != NULL; iter = iter->next) { + uid = iter->data; + source = e_source_list_peek_source_by_uid (source_list, uid); + g_free (uid); + + if (source == NULL) + continue; + + e_source_selector_select_source (selector, source); + } + g_slist_free (list); + + /* Listen for subsequent changes to the selector. */ + + g_signal_connect_swapped ( + widget, "selection-changed", + G_CALLBACK (cal_shell_sidebar_selection_changed_cb), + object); + + g_signal_connect_swapped ( + widget, "primary-selection-changed", + G_CALLBACK (cal_shell_sidebar_primary_selection_changed_cb), + object); +} + +static void +cal_shell_sidebar_client_added (ECalShellSidebar *cal_shell_sidebar, + ECal *client) +{ + cal_shell_sidebar_update_timezone (cal_shell_sidebar); +} + +static void +cal_shell_sidebar_client_removed (ECalShellSidebar *cal_shell_sidebar, + ECal *client) +{ + ESourceSelector *selector; + GHashTable *client_table; + ESource *source; + const gchar *uid; + + client_table = cal_shell_sidebar->priv->client_table; + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + + g_signal_handlers_disconnect_matched ( + client, G_SIGNAL_MATCH_DATA, 0, 0, + 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); + + cal_shell_sidebar_emit_status_message (cal_shell_sidebar, NULL); } static void @@ -130,8 +480,12 @@ cal_shell_sidebar_class_init (ECalShellSidebarClass *class) object_class = G_OBJECT_CLASS (class); object_class->get_property = cal_shell_sidebar_get_property; object_class->dispose = cal_shell_sidebar_dispose; + object_class->finalize = cal_shell_sidebar_finalize; object_class->constructed = cal_shell_sidebar_constructed; + class->client_added = cal_shell_sidebar_client_added; + class->client_removed = cal_shell_sidebar_client_removed; + g_object_class_install_property ( object_class, PROP_SELECTOR, @@ -141,14 +495,53 @@ cal_shell_sidebar_class_init (ECalShellSidebarClass *class) _("This widget displays groups of calendars"), E_TYPE_SOURCE_SELECTOR, G_PARAM_READABLE)); + + signals[CLIENT_ADDED] = g_signal_new ( + "client-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ECalShellSidebarClass, client_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_CAL); + + signals[CLIENT_REMOVED] = g_signal_new ( + "client-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ECalShellSidebarClass, client_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_CAL); + + signals[STATUS_MESSAGE] = g_signal_new ( + "status-message", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (ECalShellSidebarClass, status_message), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); } static void cal_shell_sidebar_init (ECalShellSidebar *cal_shell_sidebar) { + GHashTable *client_table; + + client_table = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + cal_shell_sidebar->priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (cal_shell_sidebar); + cal_shell_sidebar->priv->client_table = client_table; + /* Postpone widget construction until we have a shell view. */ } @@ -189,11 +582,87 @@ e_cal_shell_sidebar_new (EShellView *shell_view) "shell-view", shell_view, NULL); } -GtkWidget * +ESourceSelector * e_cal_shell_sidebar_get_selector (ECalShellSidebar *cal_shell_sidebar) { g_return_val_if_fail ( E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar), NULL); - return cal_shell_sidebar->priv->selector; + return E_SOURCE_SELECTOR (cal_shell_sidebar->priv->selector); +} + +void +e_cal_shell_sidebar_add_source (ECalShellSidebar *cal_shell_sidebar, + ESource *source) +{ + ESourceSelector *selector; + GHashTable *client_table; + ECal *client; + const gchar *uid; + const gchar *uri; + gchar *message; + + g_return_if_fail (E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar)); + g_return_if_fail (E_IS_SOURCE (source)); + + client_table = cal_shell_sidebar->priv->client_table; + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + + uid = e_source_peek_uid (source); + client = g_hash_table_lookup (client_table, uid); + + if (client != NULL) + return; + + client = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_EVENT); + g_return_if_fail (client != NULL); + + g_signal_connect_swapped ( + client, "backend-died", + G_CALLBACK (cal_shell_sidebar_backend_died_cb), + cal_shell_sidebar); + + g_signal_connect_swapped ( + client, "backend-error", + G_CALLBACK (cal_shell_sidebar_backend_error_cb), + cal_shell_sidebar); + + g_hash_table_insert (client_table, g_strdup (uid), client); + e_source_selector_select_source (selector, source); + + uri = e_cal_get_uri (client); + message = g_strdup_printf (_("Opening calendar at %s"), uri); + cal_shell_sidebar_emit_status_message (cal_shell_sidebar, message); + g_free (message); + + g_signal_connect_swapped ( + client, "cal-opened", + G_CALLBACK (cal_shell_sidebar_client_opened_cb), + cal_shell_sidebar); + + e_cal_open_async (client, FALSE); +} + +void +e_cal_shell_sidebar_remove_source (ECalShellSidebar *cal_shell_sidebar, + ESource *source) +{ + ESourceSelector *selector; + GHashTable *client_table; + ECal *client; + const gchar *uid; + + g_return_if_fail (E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar)); + g_return_if_fail (E_IS_SOURCE (source)); + + client_table = cal_shell_sidebar->priv->client_table; + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + + uid = e_source_peek_uid (source); + client = g_hash_table_lookup (client_table, uid); + + if (client == NULL) + return; + + cal_shell_sidebar_emit_client_removed (cal_shell_sidebar, client); } diff --git a/calendar/modules/e-cal-shell-sidebar.h b/calendar/modules/e-cal-shell-sidebar.h index 6a7bccac3c..49b89c556e 100644 --- a/calendar/modules/e-cal-shell-sidebar.h +++ b/calendar/modules/e-cal-shell-sidebar.h @@ -22,6 +22,9 @@ #ifndef E_CAL_SHELL_SIDEBAR_H #define E_CAL_SHELL_SIDEBAR_H +#include +#include + #include #include @@ -57,11 +60,25 @@ struct _ECalShellSidebar { struct _ECalShellSidebarClass { EShellSidebarClass parent_class; + + /* Signals */ + void (*client_added) (ECalShellSidebar *cal_shell_sidebar, + ECal *client); + void (*client_removed) (ECalShellSidebar *cal_shell_sidebar, + ECal *client); + void (*status_message) (ECalShellSidebar *cal_shell_sidebar, + const gchar *status_message); }; GType e_cal_shell_sidebar_get_type (void); GtkWidget * e_cal_shell_sidebar_new (EShellView *shell_view); -GtkWidget * e_cal_shell_sidebar_get_selector(ECalShellSidebar *cal_shell_sidebar); +ESourceSelector * + e_cal_shell_sidebar_get_selector(ECalShellSidebar *cal_shell_sidebar); +void e_cal_shell_sidebar_add_source (ECalShellSidebar *cal_shell_sidebar, + ESource *source); +void e_cal_shell_sidebar_remove_source + (ECalShellSidebar *cal_shell_sidebar, + ESource *source); G_END_DECLS -- cgit v1.2.3