From c51ef797a707f4e2c6f9688d4378f2b0e9898a66 Mon Sep 17 00:00:00 2001 From: Perberos Date: Thu, 1 Dec 2011 22:56:10 -0300 Subject: moving from https://github.com/perberos/mate-desktop-environment --- applets/clock/calendar-sources.c | 658 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 658 insertions(+) create mode 100644 applets/clock/calendar-sources.c (limited to 'applets/clock/calendar-sources.c') diff --git a/applets/clock/calendar-sources.c b/applets/clock/calendar-sources.c new file mode 100644 index 00000000..721ff062 --- /dev/null +++ b/applets/clock/calendar-sources.c @@ -0,0 +1,658 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: + * Mark McLoughlin + * William Jon McCann + * Martin Grimme + * Christian Kellner + */ + +#include + +#include "calendar-sources.h" + +#include +#include +#include +#define HANDLE_LIBICAL_MEMORY +#include +#include +#include + +#undef CALENDAR_ENABLE_DEBUG +#include "calendar-debug.h" + +#ifndef _ +#define _(x) gettext(x) +#endif + +#ifndef N_ +#define N_(x) x +#endif + +#define CALENDAR_SOURCES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CALENDAR_TYPE_SOURCES, CalendarSourcesPrivate)) + +#define CALENDAR_SOURCES_EVO_DIR "/apps/evolution" +#define CALENDAR_SOURCES_APPOINTMENT_SOURCES_KEY CALENDAR_SOURCES_EVO_DIR "/calendar/sources" +#define CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_DIR CALENDAR_SOURCES_EVO_DIR "/calendar/display" +#define CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_KEY CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_DIR "/selected_calendars" +#define CALENDAR_SOURCES_TASK_SOURCES_KEY CALENDAR_SOURCES_EVO_DIR "/tasks/sources" +#define CALENDAR_SOURCES_SELECTED_TASK_SOURCES_DIR CALENDAR_SOURCES_EVO_DIR "/calendar/tasks" +#define CALENDAR_SOURCES_SELECTED_TASK_SOURCES_KEY CALENDAR_SOURCES_SELECTED_TASK_SOURCES_DIR "/selected_tasks" + +typedef struct _CalendarSourceData CalendarSourceData; + +struct _CalendarSourceData +{ + ECalSourceType source_type; + CalendarSources *sources; + guint changed_signal; + + GSList *clients; + GSList *selected_sources; + ESourceList *esource_list; + + guint selected_sources_listener; + char *selected_sources_dir; + + guint timeout_id; + + guint loaded : 1; +}; + +struct _CalendarSourcesPrivate +{ + CalendarSourceData appointment_sources; + CalendarSourceData task_sources; + + MateConfClient *mateconf_client; +}; + +static void calendar_sources_class_init (CalendarSourcesClass *klass); +static void calendar_sources_init (CalendarSources *sources); +static void calendar_sources_finalize (GObject *object); + +static void backend_died_cb (ECal *client, CalendarSourceData *source_data); +static void calendar_sources_esource_list_changed (ESourceList *source_list, + CalendarSourceData *source_data); + +enum +{ + APPOINTMENT_SOURCES_CHANGED, + TASK_SOURCES_CHANGED, + LAST_SIGNAL +}; +static guint signals [LAST_SIGNAL] = { 0, }; + +static GObjectClass *parent_class = NULL; +static CalendarSources *calendar_sources_singleton = NULL; + +GType +calendar_sources_get_type (void) +{ + static GType sources_type = 0; + + if (!sources_type) + { + static const GTypeInfo sources_info = + { + sizeof (CalendarSourcesClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) calendar_sources_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (CalendarSources), + 0, /* n_preallocs */ + (GInstanceInitFunc) calendar_sources_init, + }; + + sources_type = g_type_register_static (G_TYPE_OBJECT, + "CalendarSources", + &sources_info, 0); + } + + return sources_type; +} + +static void +calendar_sources_class_init (CalendarSourcesClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = calendar_sources_finalize; + + g_type_class_add_private (klass, sizeof (CalendarSourcesPrivate)); + + 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, + g_cclosure_marshal_VOID__VOID, + 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, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + +static void +calendar_sources_init (CalendarSources *sources) +{ + sources->priv = CALENDAR_SOURCES_GET_PRIVATE (sources); + + sources->priv->appointment_sources.source_type = E_CAL_SOURCE_TYPE_EVENT; + sources->priv->appointment_sources.sources = sources; + sources->priv->appointment_sources.changed_signal = signals [APPOINTMENT_SOURCES_CHANGED]; + sources->priv->appointment_sources.timeout_id = 0; + + sources->priv->task_sources.source_type = E_CAL_SOURCE_TYPE_TODO; + sources->priv->task_sources.sources = sources; + sources->priv->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED]; + sources->priv->task_sources.timeout_id = 0; + + sources->priv->mateconf_client = mateconf_client_get_default (); +} + +static void +calendar_sources_finalize_source_data (CalendarSources *sources, + CalendarSourceData *source_data) +{ + if (source_data->loaded) + { + GSList *l; + + if (source_data->selected_sources_dir) + { + mateconf_client_remove_dir (sources->priv->mateconf_client, + source_data->selected_sources_dir, + NULL); + + g_free (source_data->selected_sources_dir); + source_data->selected_sources_dir = NULL; + } + + if (source_data->selected_sources_listener) + { + mateconf_client_notify_remove (sources->priv->mateconf_client, + source_data->selected_sources_listener); + source_data->selected_sources_listener = 0; + } + + for (l = source_data->clients; l; l = l->next) + { + g_signal_handlers_disconnect_by_func (G_OBJECT (l->data), + G_CALLBACK (backend_died_cb), + source_data); + g_object_unref (l->data); + } + g_slist_free (source_data->clients); + source_data->clients = NULL; + + if (source_data->esource_list) + { + g_signal_handlers_disconnect_by_func (source_data->esource_list, + G_CALLBACK (calendar_sources_esource_list_changed), + source_data); + g_object_unref (source_data->esource_list); + } + source_data->esource_list = NULL; + + for (l = source_data->selected_sources; l; l = l->next) + g_free (l->data); + g_slist_free (source_data->selected_sources); + source_data->selected_sources = 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); + + calendar_sources_finalize_source_data (sources, &sources->priv->appointment_sources); + calendar_sources_finalize_source_data (sources, &sources->priv->task_sources); + + if (sources->priv->mateconf_client) + g_object_unref (sources->priv->mateconf_client); + sources->priv->mateconf_client = NULL; + + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (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; +} + +static gboolean +is_source_selected (ESource *esource, + GSList *selected_sources) +{ + const char *uid; + GSList *l; + + uid = e_source_peek_uid (esource); + + for (l = selected_sources; l; l = l->next) + { + const char *source = l->data; + + if (!strcmp (source, uid)) + return TRUE; + } + + return FALSE; +} + +static char * +auth_func_cb (ECal *ecal, + const char *prompt, + const char *key, + gpointer user_data) +{ + ESource *source; + const gchar *auth_domain; + const gchar *component_name; + + source = e_cal_get_source (ecal); + auth_domain = e_source_get_property (source, "auth-domain"); + component_name = auth_domain ? auth_domain : "Calendar"; + + return e_passwords_get_password (component_name, key); +} + +/* The clients are just created here but not loaded */ +static ECal * +get_ecal_from_source (ESource *esource, + ECalSourceType source_type, + GSList *existing_clients) +{ + ECal *retval; + + if (existing_clients) + { + GSList *l; + + for (l = existing_clients; l; l = l->next) + { + ECal *client = E_CAL (l->data); + + if (e_source_equal (esource, e_cal_get_source (client))) + { + dprintf (" load_esource: found existing source ... returning that\n"); + + return g_object_ref (client); + } + } + } + + retval = e_cal_new (esource, source_type); + if (!retval) + { + g_warning ("Could not load source '%s' from '%s'\n", + e_source_peek_name (esource), + e_source_peek_relative_uri (esource)); + return NULL; + } + + e_cal_set_auth_func (retval, auth_func_cb, NULL); + + return retval; +} + +/* - Order doesn't matter + * - Can just compare object pointers since we + * re-use client connections + */ +static gboolean +compare_ecal_lists (GSList *a, + GSList *b) +{ + GSList *l; + + if (g_slist_length (a) != g_slist_length (b)) + return FALSE; + + for (l = a; l; l = l->next) + { + if (!g_slist_find (b, l->data)) + return FALSE; + } + + return TRUE; +} + +static inline void +debug_dump_selected_sources (GSList *selected_sources) +{ +#ifdef CALENDAR_ENABLE_DEBUG + GSList *l; + + dprintf ("Selected sources:\n"); + for (l = selected_sources; l; l = l->next) + { + char *source = l->data; + + dprintf (" %s\n", source); + } + dprintf ("\n"); +#endif +} + +static inline void +debug_dump_ecal_list (GSList *ecal_list) +{ +#ifdef CALENDAR_ENABLE_DEBUG + GSList *l; + + dprintf ("Loaded clients:\n"); + for (l = ecal_list; l; l = l->next) + { + ECal *client = l->data; + ESource *source = e_cal_get_source (client); + + dprintf (" %s %s %s\n", + e_source_peek_uid (source), + e_source_peek_name (source), + e_cal_get_uri (client)); + } +#endif +} + +static void +calendar_sources_load_esource_list (CalendarSourceData *source_data); + +static gboolean +backend_restart (gpointer data) +{ + CalendarSourceData *source_data = data; + + calendar_sources_load_esource_list (source_data); + + source_data->timeout_id = 0; + + return FALSE; +} + +static void +backend_died_cb (ECal *client, CalendarSourceData *source_data) +{ + const char *uristr; + + source_data->clients = g_slist_remove (source_data->clients, client); + if (g_slist_length (source_data->clients) < 1) + { + g_slist_free (source_data->clients); + source_data->clients = NULL; + } + uristr = e_cal_get_uri (client); + g_warning ("The calendar backend for %s has crashed.", uristr); + + 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 (CalendarSourceData *source_data) +{ + GSList *clients = NULL; + GSList *groups, *l; + gboolean emit_signal = FALSE; + + g_return_if_fail (source_data->esource_list != NULL); + + debug_dump_selected_sources (source_data->selected_sources); + + dprintf ("Source groups:\n"); + groups = e_source_list_peek_groups (source_data->esource_list); + for (l = groups; l; l = l->next) + { + GSList *esources, *s; + + dprintf (" %s\n", e_source_group_peek_uid (l->data)); + dprintf (" sources:\n"); + + esources = e_source_group_peek_sources (l->data); + for (s = esources; s; s = s->next) + { + ESource *esource = E_SOURCE (s->data); + ECal *client; + + dprintf (" type = '%s' uid = '%s', name = '%s', relative uri = '%s': \n", + source_data->source_type == E_CAL_SOURCE_TYPE_EVENT ? "appointment" : "task", + e_source_peek_uid (esource), + e_source_peek_name (esource), + e_source_peek_relative_uri (esource)); + + if (is_source_selected (esource, source_data->selected_sources) && + (client = get_ecal_from_source (esource, source_data->source_type, source_data->clients))) + { + clients = g_slist_prepend (clients, client); + } + } + } + dprintf ("\n"); + + if (source_data->loaded && + !compare_ecal_lists (source_data->clients, clients)) + emit_signal = TRUE; + + for (l = source_data->clients; l; l = l->next) + { + g_signal_handlers_disconnect_by_func (G_OBJECT (l->data), + G_CALLBACK (backend_died_cb), + source_data); + + g_object_unref (l->data); + } + g_slist_free (source_data->clients); + source_data->clients = g_slist_reverse (clients); + + /* connect to backend_died after we disconnected the previous signal + * handlers. If we do it before, we'll lose some handlers (for clients that + * were already there before) */ + for (l = source_data->clients; l; l = l->next) + { + g_signal_connect (G_OBJECT (l->data), "backend_died", + G_CALLBACK (backend_died_cb), source_data); + } + + if (emit_signal) + { + dprintf ("Emitting %s-sources-changed signal\n", + source_data->source_type == E_CAL_SOURCE_TYPE_EVENT ? "appointment" : "task"); + g_signal_emit (source_data->sources, source_data->changed_signal, 0); + } + + debug_dump_ecal_list (source_data->clients); +} + +static void +calendar_sources_esource_list_changed (ESourceList *source_list, + CalendarSourceData *source_data) + +{ + dprintf ("ESourceList changed, reloading\n"); + + calendar_sources_load_esource_list (source_data); +} + +static void +calendar_sources_selected_sources_notify (MateConfClient *client, + guint cnx_id, + MateConfEntry *entry, + CalendarSourceData *source_data) +{ + GSList *l; + + if (!entry->value || + entry->value->type != MATECONF_VALUE_LIST || + mateconf_value_get_list_type (entry->value) != MATECONF_VALUE_STRING) + return; + + dprintf ("Selected sources key (%s) changed, reloading\n", entry->key); + + for (l = source_data->selected_sources; l; l = l->next) + g_free (l->data); + source_data->selected_sources = NULL; + + for (l = mateconf_value_get_list (entry->value); l; l = l->next) + { + const char *source = mateconf_value_get_string (l->data); + + source_data->selected_sources = + g_slist_prepend (source_data->selected_sources, + g_strdup (source)); + } + source_data->selected_sources = + g_slist_reverse (source_data->selected_sources); + + calendar_sources_load_esource_list (source_data); +} + +static void +calendar_sources_load_sources (CalendarSources *sources, + CalendarSourceData *source_data, + const char *sources_key, + const char *selected_sources_key, + const char *selected_sources_dir) +{ + MateConfClient *mateconf_client; + GError *error; + + dprintf ("---------------------------\n"); + dprintf ("Loading sources:\n"); + dprintf (" sources_key: %s\n", sources_key); + dprintf (" selected_sources_key: %s\n", selected_sources_key); + dprintf (" selected_sources_dir: %s\n", selected_sources_dir); + + mateconf_client = sources->priv->mateconf_client; + + error = NULL; + source_data->selected_sources = mateconf_client_get_list (mateconf_client, + selected_sources_key, + MATECONF_VALUE_STRING, + &error); + if (error) + { + g_warning ("Failed to get selected sources from '%s': %s\n", + selected_sources_key, + error->message); + g_error_free (error); + return; + } + + mateconf_client_add_dir (mateconf_client, + selected_sources_dir, + MATECONF_CLIENT_PRELOAD_NONE, + NULL); + source_data->selected_sources_dir = g_strdup (selected_sources_dir); + + source_data->selected_sources_listener = + mateconf_client_notify_add (mateconf_client, + selected_sources_dir, + (MateConfClientNotifyFunc) calendar_sources_selected_sources_notify, + source_data, NULL, NULL); + + source_data->esource_list = e_source_list_new_for_mateconf (mateconf_client, sources_key); + g_signal_connect (source_data->esource_list, "changed", + G_CALLBACK (calendar_sources_esource_list_changed), + source_data); + + calendar_sources_load_esource_list (source_data); + + source_data->loaded = TRUE; + + dprintf ("---------------------------\n"); +} + +GSList * +calendar_sources_get_appointment_sources (CalendarSources *sources) +{ + g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); + + if (!sources->priv->appointment_sources.loaded) + { + calendar_sources_load_sources (sources, + &sources->priv->appointment_sources, + CALENDAR_SOURCES_APPOINTMENT_SOURCES_KEY, + CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_KEY, + CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_DIR); + } + + return sources->priv->appointment_sources.clients; +} + +GSList * +calendar_sources_get_task_sources (CalendarSources *sources) +{ + g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); + + if (!sources->priv->task_sources.loaded) + { + calendar_sources_load_sources (sources, + &sources->priv->task_sources, + CALENDAR_SOURCES_TASK_SOURCES_KEY, + CALENDAR_SOURCES_SELECTED_TASK_SOURCES_KEY, + CALENDAR_SOURCES_SELECTED_TASK_SOURCES_DIR); + } + + return sources->priv->task_sources.clients; +} -- cgit v1.2.1