diff options
Diffstat (limited to 'applets/clock/calendar-sources.c')
-rw-r--r-- | applets/clock/calendar-sources.c | 503 |
1 files changed, 503 insertions, 0 deletions
diff --git a/applets/clock/calendar-sources.c b/applets/clock/calendar-sources.c new file mode 100644 index 00000000..54c6dc8c --- /dev/null +++ b/applets/clock/calendar-sources.c @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Mark McLoughlin <[email protected]> + * William Jon McCann <[email protected]> + * Martin Grimme <[email protected]> + * Christian Kellner <[email protected]> + */ + +#include <config.h> + +#include "calendar-sources.h" + +#include <libintl.h> +#include <string.h> +#define HANDLE_LIBICAL_MEMORY + + +#include <libecal/libecal.h> + +#undef CALENDAR_ENABLE_DEBUG +#include "calendar-debug.h" + +#ifndef _ +#define _(x) gettext(x) +#endif + +#ifndef N_ +#define N_(x) x +#endif + +typedef struct _ClientData ClientData; +typedef struct _CalendarSourceData CalendarSourceData; + +struct _ClientData +{ + ECalClient *client; + gulong backend_died_id; +}; + +struct _CalendarSourceData +{ + ECalClientSourceType source_type; + CalendarSources *sources; + guint changed_signal; + + /* ESource -> EClient */ + GHashTable *clients; + + guint timeout_id; + + guint loaded : 1; +}; + +struct _CalendarSourcesPrivate +{ + ESourceRegistry *registry; + gulong source_added_id; + gulong source_changed_id; + gulong source_removed_id; + + CalendarSourceData appointment_sources; + CalendarSourceData task_sources; +}; + +static void calendar_sources_finalize (GObject *object); + +static void backend_died_cb (EClient *client, CalendarSourceData *source_data); +static void calendar_sources_registry_source_changed_cb (ESourceRegistry *registry, + ESource *source, + CalendarSources *sources); +static void calendar_sources_registry_source_removed_cb (ESourceRegistry *registry, + ESource *source, + CalendarSources *sources); + +enum +{ + APPOINTMENT_SOURCES_CHANGED, + TASK_SOURCES_CHANGED, + LAST_SIGNAL +}; +static guint signals [LAST_SIGNAL] = { 0, }; + +static CalendarSources *calendar_sources_singleton = NULL; + +static void +client_data_free (ClientData *data) +{ + g_signal_handler_disconnect (data->client, data->backend_died_id); + g_object_unref (data->client); + g_free (data); +} + +G_DEFINE_TYPE_WITH_PRIVATE (CalendarSources, calendar_sources, G_TYPE_OBJECT) + +static void +calendar_sources_class_init (CalendarSourcesClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->finalize = calendar_sources_finalize; + + signals [APPOINTMENT_SOURCES_CHANGED] = + g_signal_new ("appointment-sources-changed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CalendarSourcesClass, + appointment_sources_changed), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); + + signals [TASK_SOURCES_CHANGED] = + g_signal_new ("task-sources-changed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CalendarSourcesClass, + task_sources_changed), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); +} + +static void +calendar_sources_init (CalendarSources *sources) +{ + GError *error = NULL; + + sources->priv = calendar_sources_get_instance_private (sources); + + /* XXX Not sure what to do if this fails. + * Should this class implement GInitable or pass the + * registry in as a G_PARAM_CONSTRUCT_ONLY property? */ + sources->priv->registry = e_source_registry_new_sync (NULL, &error); + if (error != NULL) { + g_critical ("%s: %s", G_STRFUNC, error->message); + g_error_free (error); + } + + sources->priv->source_added_id = g_signal_connect (sources->priv->registry, + "source-added", + G_CALLBACK (calendar_sources_registry_source_changed_cb), + sources); + sources->priv->source_changed_id = g_signal_connect (sources->priv->registry, + "source-changed", + G_CALLBACK (calendar_sources_registry_source_changed_cb), + sources); + sources->priv->source_removed_id = g_signal_connect (sources->priv->registry, + "source-removed", + G_CALLBACK (calendar_sources_registry_source_removed_cb), + sources); + + sources->priv->appointment_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; + sources->priv->appointment_sources.sources = sources; + sources->priv->appointment_sources.changed_signal = signals [APPOINTMENT_SOURCES_CHANGED]; + sources->priv->appointment_sources.clients = g_hash_table_new_full ((GHashFunc) e_source_hash, + (GEqualFunc) e_source_equal, + (GDestroyNotify) g_object_unref, + (GDestroyNotify) client_data_free); + sources->priv->appointment_sources.timeout_id = 0; + + sources->priv->task_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS; + sources->priv->task_sources.sources = sources; + sources->priv->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED]; + sources->priv->task_sources.clients = g_hash_table_new_full ((GHashFunc) e_source_hash, + (GEqualFunc) e_source_equal, + (GDestroyNotify) g_object_unref, + (GDestroyNotify) client_data_free); + sources->priv->task_sources.timeout_id = 0; +} + +static void +calendar_sources_finalize_source_data (CalendarSources *sources, + CalendarSourceData *source_data) +{ + if (source_data->loaded) + { + g_hash_table_destroy (source_data->clients); + source_data->clients = NULL; + + if (source_data->timeout_id != 0) + { + g_source_remove (source_data->timeout_id); + source_data->timeout_id = 0; + } + + source_data->loaded = FALSE; + } +} + +static void +calendar_sources_finalize (GObject *object) +{ + CalendarSources *sources = CALENDAR_SOURCES (object); + + if (sources->priv->registry) + { + g_signal_handler_disconnect (sources->priv->registry, + sources->priv->source_added_id); + g_signal_handler_disconnect (sources->priv->registry, + sources->priv->source_changed_id); + g_signal_handler_disconnect (sources->priv->registry, + sources->priv->source_removed_id); + g_object_unref (sources->priv->registry); + } + sources->priv->registry = NULL; + + calendar_sources_finalize_source_data (sources, &sources->priv->appointment_sources); + calendar_sources_finalize_source_data (sources, &sources->priv->task_sources); + + G_OBJECT_CLASS (calendar_sources_parent_class)->finalize (object); +} + +CalendarSources * +calendar_sources_get (void) +{ + gpointer singleton_location = &calendar_sources_singleton; + + if (calendar_sources_singleton) + return g_object_ref (calendar_sources_singleton); + + calendar_sources_singleton = g_object_new (CALENDAR_TYPE_SOURCES, NULL); + g_object_add_weak_pointer (G_OBJECT (calendar_sources_singleton), + singleton_location); + + return calendar_sources_singleton; +} + +/* The clients are just created here but not loaded */ +static void +create_client_for_source (ESource *source, + ECalClientSourceType source_type, + CalendarSourceData *source_data) +{ + ClientData *data; + GError *error; + EClient *client; + + client = g_hash_table_lookup (source_data->clients, source); + g_return_if_fail (client == NULL); + + error = NULL; + client = e_cal_client_connect_sync (source, source_type, -1, NULL, &error); + + if (!client) + { + g_warning ("Could not load source '%s': %s", + e_source_get_uid (source), + error->message); + + g_clear_error (&error); + return; + } + + data = g_new0 (ClientData, 1); + data->client = E_CAL_CLIENT (client); /* takes ownership */ + data->backend_died_id = g_signal_connect (client, + "backend-died", + G_CALLBACK (backend_died_cb), + source_data); + + g_hash_table_insert (source_data->clients, g_object_ref (source), data); +} + +static inline void +debug_dump_ecal_list (GHashTable *clients) +{ +#ifdef CALENDAR_ENABLE_DEBUG + GList *list, *link; + + dprintf ("Loaded clients:\n"); + list = g_hash_table_get_keys (clients); + for (link = list; link != NULL; link = g_list_next (link)) + { + ESource *source = E_SOURCE (link->data); + + dprintf (" %s %s\n", + e_source_get_uid (source), + e_source_get_display_name (source)); + } +#endif +} + +static void +calendar_sources_load_esource_list (ESourceRegistry *registry, + CalendarSourceData *source_data); + +static gboolean +backend_restart (gpointer data) +{ + CalendarSourceData *source_data = data; + ESourceRegistry *registry; + + registry = source_data->sources->priv->registry; + calendar_sources_load_esource_list (registry, source_data); + g_signal_emit (source_data->sources, source_data->changed_signal, 0); + + source_data->timeout_id = 0; + + return FALSE; +} + +static void +backend_died_cb (EClient *client, CalendarSourceData *source_data) +{ + ESource *source; + const char *display_name; + + source = e_client_get_source (client); + display_name = e_source_get_display_name (source); + g_warning ("The calendar backend for '%s' has crashed.", display_name); + g_hash_table_remove (source_data->clients, source); + + if (source_data->timeout_id != 0) + { + g_source_remove (source_data->timeout_id); + source_data->timeout_id = 0; + } + + source_data->timeout_id = g_timeout_add_seconds (2, backend_restart, + source_data); +} + +static void +calendar_sources_load_esource_list (ESourceRegistry *registry, + CalendarSourceData *source_data) +{ + GList *list, *link; + const gchar *extension_name; + + switch (source_data->source_type) + { + case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: + extension_name = E_SOURCE_EXTENSION_CALENDAR; + break; + case E_CAL_CLIENT_SOURCE_TYPE_TASKS: + extension_name = E_SOURCE_EXTENSION_TASK_LIST; + break; + case E_CAL_CLIENT_SOURCE_TYPE_MEMOS: + case E_CAL_CLIENT_SOURCE_TYPE_LAST: + default: + g_return_if_reached (); + } + + list = e_source_registry_list_sources (registry, extension_name); + + for (link = list; link != NULL; link = g_list_next (link)) + { + ESource *source = E_SOURCE (link->data); + ESourceSelectable *extension; + gboolean show_source; + + extension = e_source_get_extension (source, extension_name); + show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension); + + if (show_source) + create_client_for_source (source, source_data->source_type, source_data); + } + + debug_dump_ecal_list (source_data->clients); + + g_list_free_full (list, g_object_unref); +} + +static void +calendar_sources_registry_source_changed_cb (ESourceRegistry *registry, + ESource *source, + CalendarSources *sources) +{ + if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) + { + CalendarSourceData *source_data; + ESourceSelectable *extension; + gboolean have_client; + gboolean show_source; + + source_data = &sources->priv->appointment_sources; + extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR); + have_client = (g_hash_table_lookup (source_data->clients, source) != NULL); + show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension); + + if (!show_source && have_client) + { + g_hash_table_remove (source_data->clients, source); + g_signal_emit (sources, source_data->changed_signal, 0); + } + if (show_source && !have_client) + { + create_client_for_source (source, source_data->source_type, source_data); + g_signal_emit (sources, source_data->changed_signal, 0); + } + } + + if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) + { + CalendarSourceData *source_data; + ESourceSelectable *extension; + gboolean have_client; + gboolean show_source; + + source_data = &sources->priv->task_sources; + extension = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST); + have_client = (g_hash_table_lookup (source_data->clients, source) != NULL); + show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension); + + if (!show_source && have_client) + { + g_hash_table_remove (source_data->clients, source); + g_signal_emit (sources, source_data->changed_signal, 0); + } + if (show_source && !have_client) + { + create_client_for_source (source, source_data->source_type, source_data); + g_signal_emit (sources, source_data->changed_signal, 0); + } + } +} + +static void +calendar_sources_registry_source_removed_cb (ESourceRegistry *registry, + ESource *source, + CalendarSources *sources) +{ + if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) + { + CalendarSourceData *source_data; + + source_data = &sources->priv->appointment_sources; + g_hash_table_remove (source_data->clients, source); + g_signal_emit (sources, source_data->changed_signal, 0); + } + + if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) + { + CalendarSourceData *source_data; + + source_data = &sources->priv->task_sources; + g_hash_table_remove (source_data->clients, source); + g_signal_emit (sources, source_data->changed_signal, 0); + } +} + +GList * +calendar_sources_get_appointment_clients (CalendarSources *sources) +{ + GList *list, *link; + + g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); + + if (!sources->priv->appointment_sources.loaded) + { + calendar_sources_load_esource_list (sources->priv->registry, + &sources->priv->appointment_sources); + sources->priv->appointment_sources.loaded = TRUE; + } + + list = g_hash_table_get_values (sources->priv->appointment_sources.clients); + + for (link = list; link != NULL; link = g_list_next (link)) + link->data = ((ClientData *) link->data)->client; + + return list; +} + +GList * +calendar_sources_get_task_clients (CalendarSources *sources) +{ + GList *list, *link; + + g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); + + if (!sources->priv->task_sources.loaded) + { + calendar_sources_load_esource_list (sources->priv->registry, + &sources->priv->task_sources); + sources->priv->task_sources.loaded = TRUE; + } + + list = g_hash_table_get_values (sources->priv->task_sources.clients); + + for (link = list; link != NULL; link = g_list_next (link)) + link->data = ((ClientData *) link->data)->client; + + return list; +} |