2
* Copyright (C) 2004 Free Software Foundation, Inc.
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License as
6
* published by the Free Software Foundation; either version 2 of the
7
* License, or (at your option) any later version.
9
* This program is distributed in the hope that it will be useful, but
10
* WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20
* Mark McLoughlin <mark@skynet.ie>
21
* William Jon McCann <mccann@jhu.edu>
22
* Martin Grimme <martin@pycage.de>
23
* Christian Kellner <gicmo@xatom.net>
28
#include "calendar-sources.h"
32
#include <gconf/gconf-client.h>
33
#define HANDLE_LIBICAL_MEMORY
34
#include <libecal/e-cal.h>
35
#include <libedataserver/e-source-list.h>
36
#include <libedataserverui/e-passwords.h>
38
#undef CALENDAR_ENABLE_DEBUG
39
#include "calendar-debug.h"
42
#define _(x) gettext(x)
49
#define CALENDAR_SOURCES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CALENDAR_TYPE_SOURCES, CalendarSourcesPrivate))
51
#define CALENDAR_SOURCES_EVO_DIR "/apps/evolution"
52
#define CALENDAR_SOURCES_APPOINTMENT_SOURCES_KEY CALENDAR_SOURCES_EVO_DIR "/calendar/sources"
53
#define CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_DIR CALENDAR_SOURCES_EVO_DIR "/calendar/display"
54
#define CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_KEY CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_DIR "/selected_calendars"
55
#define CALENDAR_SOURCES_TASK_SOURCES_KEY CALENDAR_SOURCES_EVO_DIR "/tasks/sources"
56
#define CALENDAR_SOURCES_SELECTED_TASK_SOURCES_DIR CALENDAR_SOURCES_EVO_DIR "/calendar/tasks"
57
#define CALENDAR_SOURCES_SELECTED_TASK_SOURCES_KEY CALENDAR_SOURCES_SELECTED_TASK_SOURCES_DIR "/selected_tasks"
59
typedef struct _CalendarSourceData CalendarSourceData;
61
struct _CalendarSourceData
63
ECalSourceType source_type;
64
CalendarSources *sources;
68
GSList *selected_sources;
69
ESourceList *esource_list;
71
guint selected_sources_listener;
72
char *selected_sources_dir;
79
struct _CalendarSourcesPrivate
81
CalendarSourceData appointment_sources;
82
CalendarSourceData task_sources;
84
GConfClient *gconf_client;
87
static void calendar_sources_class_init (CalendarSourcesClass *klass);
88
static void calendar_sources_init (CalendarSources *sources);
89
static void calendar_sources_finalize (GObject *object);
91
static void backend_died_cb (ECal *client, CalendarSourceData *source_data);
92
static void calendar_sources_esource_list_changed (ESourceList *source_list,
93
CalendarSourceData *source_data);
97
APPOINTMENT_SOURCES_CHANGED,
101
static guint signals [LAST_SIGNAL] = { 0, };
103
static GObjectClass *parent_class = NULL;
104
static CalendarSources *calendar_sources_singleton = NULL;
107
calendar_sources_get_type (void)
109
static GType sources_type = 0;
113
static const GTypeInfo sources_info =
115
sizeof (CalendarSourcesClass),
116
NULL, /* base_init */
117
NULL, /* base_finalize */
118
(GClassInitFunc) calendar_sources_class_init,
119
NULL, /* class_finalize */
120
NULL, /* class_data */
121
sizeof (CalendarSources),
123
(GInstanceInitFunc) calendar_sources_init,
126
sources_type = g_type_register_static (G_TYPE_OBJECT,
135
calendar_sources_class_init (CalendarSourcesClass *klass)
137
GObjectClass *gobject_class = (GObjectClass *) klass;
139
parent_class = g_type_class_peek_parent (klass);
141
gobject_class->finalize = calendar_sources_finalize;
143
g_type_class_add_private (klass, sizeof (CalendarSourcesPrivate));
145
signals [APPOINTMENT_SOURCES_CHANGED] =
146
g_signal_new ("appointment-sources-changed",
147
G_TYPE_FROM_CLASS (gobject_class),
149
G_STRUCT_OFFSET (CalendarSourcesClass,
150
appointment_sources_changed),
153
g_cclosure_marshal_VOID__VOID,
157
signals [TASK_SOURCES_CHANGED] =
158
g_signal_new ("task-sources-changed",
159
G_TYPE_FROM_CLASS (gobject_class),
161
G_STRUCT_OFFSET (CalendarSourcesClass,
162
task_sources_changed),
165
g_cclosure_marshal_VOID__VOID,
171
calendar_sources_init (CalendarSources *sources)
173
sources->priv = CALENDAR_SOURCES_GET_PRIVATE (sources);
175
sources->priv->appointment_sources.source_type = E_CAL_SOURCE_TYPE_EVENT;
176
sources->priv->appointment_sources.sources = sources;
177
sources->priv->appointment_sources.changed_signal = signals [APPOINTMENT_SOURCES_CHANGED];
178
sources->priv->appointment_sources.timeout_id = 0;
180
sources->priv->task_sources.source_type = E_CAL_SOURCE_TYPE_TODO;
181
sources->priv->task_sources.sources = sources;
182
sources->priv->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED];
183
sources->priv->task_sources.timeout_id = 0;
185
sources->priv->gconf_client = gconf_client_get_default ();
189
calendar_sources_finalize_source_data (CalendarSources *sources,
190
CalendarSourceData *source_data)
192
if (source_data->loaded)
196
if (source_data->selected_sources_dir)
198
gconf_client_remove_dir (sources->priv->gconf_client,
199
source_data->selected_sources_dir,
202
g_free (source_data->selected_sources_dir);
203
source_data->selected_sources_dir = NULL;
206
if (source_data->selected_sources_listener)
208
gconf_client_notify_remove (sources->priv->gconf_client,
209
source_data->selected_sources_listener);
210
source_data->selected_sources_listener = 0;
213
for (l = source_data->clients; l; l = l->next)
215
g_signal_handlers_disconnect_by_func (G_OBJECT (l->data),
216
G_CALLBACK (backend_died_cb),
218
g_object_unref (l->data);
220
g_slist_free (source_data->clients);
221
source_data->clients = NULL;
223
if (source_data->esource_list)
225
g_signal_handlers_disconnect_by_func (source_data->esource_list,
226
G_CALLBACK (calendar_sources_esource_list_changed),
228
g_object_unref (source_data->esource_list);
230
source_data->esource_list = NULL;
232
for (l = source_data->selected_sources; l; l = l->next)
234
g_slist_free (source_data->selected_sources);
235
source_data->selected_sources = NULL;
237
if (source_data->timeout_id != 0)
239
g_source_remove (source_data->timeout_id);
240
source_data->timeout_id = 0;
243
source_data->loaded = FALSE;
248
calendar_sources_finalize (GObject *object)
250
CalendarSources *sources = CALENDAR_SOURCES (object);
252
calendar_sources_finalize_source_data (sources, &sources->priv->appointment_sources);
253
calendar_sources_finalize_source_data (sources, &sources->priv->task_sources);
255
if (sources->priv->gconf_client)
256
g_object_unref (sources->priv->gconf_client);
257
sources->priv->gconf_client = NULL;
259
if (G_OBJECT_CLASS (parent_class)->finalize)
260
G_OBJECT_CLASS (parent_class)->finalize (object);
264
calendar_sources_get (void)
266
gpointer singleton_location = &calendar_sources_singleton;
268
if (calendar_sources_singleton)
269
return g_object_ref (calendar_sources_singleton);
271
calendar_sources_singleton = g_object_new (CALENDAR_TYPE_SOURCES, NULL);
272
g_object_add_weak_pointer (G_OBJECT (calendar_sources_singleton),
275
return calendar_sources_singleton;
279
is_source_selected (ESource *esource,
280
GSList *selected_sources)
285
uid = e_source_peek_uid (esource);
287
for (l = selected_sources; l; l = l->next)
289
const char *source = l->data;
291
if (!strcmp (source, uid))
299
auth_func_cb (ECal *ecal,
305
const gchar *auth_domain;
306
const gchar *component_name;
308
source = e_cal_get_source (ecal);
309
auth_domain = e_source_get_property (source, "auth-domain");
310
component_name = auth_domain ? auth_domain : "Calendar";
312
return e_passwords_get_password (component_name, key);
315
/* The clients are just created here but not loaded */
317
get_ecal_from_source (ESource *esource,
318
ECalSourceType source_type,
319
GSList *existing_clients)
323
if (existing_clients)
327
for (l = existing_clients; l; l = l->next)
329
ECal *client = E_CAL (l->data);
331
if (e_source_equal (esource, e_cal_get_source (client)))
333
dprintf (" load_esource: found existing source ... returning that\n");
335
return g_object_ref (client);
340
retval = e_cal_new (esource, source_type);
343
g_warning ("Could not load source '%s' from '%s'\n",
344
e_source_peek_name (esource),
345
e_source_peek_relative_uri (esource));
349
e_cal_set_auth_func (retval, auth_func_cb, NULL);
354
/* - Order doesn't matter
355
* - Can just compare object pointers since we
356
* re-use client connections
359
compare_ecal_lists (GSList *a,
364
if (g_slist_length (a) != g_slist_length (b))
367
for (l = a; l; l = l->next)
369
if (!g_slist_find (b, l->data))
377
debug_dump_selected_sources (GSList *selected_sources)
379
#ifdef CALENDAR_ENABLE_DEBUG
382
dprintf ("Selected sources:\n");
383
for (l = selected_sources; l; l = l->next)
385
char *source = l->data;
387
dprintf (" %s\n", source);
394
debug_dump_ecal_list (GSList *ecal_list)
396
#ifdef CALENDAR_ENABLE_DEBUG
399
dprintf ("Loaded clients:\n");
400
for (l = ecal_list; l; l = l->next)
402
ECal *client = l->data;
403
ESource *source = e_cal_get_source (client);
405
dprintf (" %s %s %s\n",
406
e_source_peek_uid (source),
407
e_source_peek_name (source),
408
e_cal_get_uri (client));
414
calendar_sources_load_esource_list (CalendarSourceData *source_data);
417
backend_restart (gpointer data)
419
CalendarSourceData *source_data = data;
421
calendar_sources_load_esource_list (source_data);
423
source_data->timeout_id = 0;
429
backend_died_cb (ECal *client, CalendarSourceData *source_data)
433
source_data->clients = g_slist_remove (source_data->clients, client);
434
if (g_slist_length (source_data->clients) < 1)
436
g_slist_free (source_data->clients);
437
source_data->clients = NULL;
439
uristr = e_cal_get_uri (client);
440
g_warning ("The calendar backend for %s has crashed.", uristr);
442
if (source_data->timeout_id != 0)
444
g_source_remove (source_data->timeout_id);
445
source_data->timeout_id = 0;
448
source_data->timeout_id = g_timeout_add_seconds (2, backend_restart,
453
calendar_sources_load_esource_list (CalendarSourceData *source_data)
455
GSList *clients = NULL;
457
gboolean emit_signal = FALSE;
459
g_return_if_fail (source_data->esource_list != NULL);
461
debug_dump_selected_sources (source_data->selected_sources);
463
dprintf ("Source groups:\n");
464
groups = e_source_list_peek_groups (source_data->esource_list);
465
for (l = groups; l; l = l->next)
467
GSList *esources, *s;
469
dprintf (" %s\n", e_source_group_peek_uid (l->data));
470
dprintf (" sources:\n");
472
esources = e_source_group_peek_sources (l->data);
473
for (s = esources; s; s = s->next)
475
ESource *esource = E_SOURCE (s->data);
478
dprintf (" type = '%s' uid = '%s', name = '%s', relative uri = '%s': \n",
479
source_data->source_type == E_CAL_SOURCE_TYPE_EVENT ? "appointment" : "task",
480
e_source_peek_uid (esource),
481
e_source_peek_name (esource),
482
e_source_peek_relative_uri (esource));
484
if (is_source_selected (esource, source_data->selected_sources) &&
485
(client = get_ecal_from_source (esource, source_data->source_type, source_data->clients)))
487
clients = g_slist_prepend (clients, client);
493
if (source_data->loaded &&
494
!compare_ecal_lists (source_data->clients, clients))
497
for (l = source_data->clients; l; l = l->next)
499
g_signal_handlers_disconnect_by_func (G_OBJECT (l->data),
500
G_CALLBACK (backend_died_cb),
503
g_object_unref (l->data);
505
g_slist_free (source_data->clients);
506
source_data->clients = g_slist_reverse (clients);
508
/* connect to backend_died after we disconnected the previous signal
509
* handlers. If we do it before, we'll lose some handlers (for clients that
510
* were already there before) */
511
for (l = source_data->clients; l; l = l->next)
513
g_signal_connect (G_OBJECT (l->data), "backend_died",
514
G_CALLBACK (backend_died_cb), source_data);
519
dprintf ("Emitting %s-sources-changed signal\n",
520
source_data->source_type == E_CAL_SOURCE_TYPE_EVENT ? "appointment" : "task");
521
g_signal_emit (source_data->sources, source_data->changed_signal, 0);
524
debug_dump_ecal_list (source_data->clients);
528
calendar_sources_esource_list_changed (ESourceList *source_list,
529
CalendarSourceData *source_data)
532
dprintf ("ESourceList changed, reloading\n");
534
calendar_sources_load_esource_list (source_data);
538
calendar_sources_selected_sources_notify (GConfClient *client,
541
CalendarSourceData *source_data)
546
entry->value->type != GCONF_VALUE_LIST ||
547
gconf_value_get_list_type (entry->value) != GCONF_VALUE_STRING)
550
dprintf ("Selected sources key (%s) changed, reloading\n", entry->key);
552
for (l = source_data->selected_sources; l; l = l->next)
554
source_data->selected_sources = NULL;
556
for (l = gconf_value_get_list (entry->value); l; l = l->next)
558
const char *source = gconf_value_get_string (l->data);
560
source_data->selected_sources =
561
g_slist_prepend (source_data->selected_sources,
564
source_data->selected_sources =
565
g_slist_reverse (source_data->selected_sources);
567
calendar_sources_load_esource_list (source_data);
571
calendar_sources_load_sources (CalendarSources *sources,
572
CalendarSourceData *source_data,
573
const char *sources_key,
574
const char *selected_sources_key,
575
const char *selected_sources_dir)
577
GConfClient *gconf_client;
580
dprintf ("---------------------------\n");
581
dprintf ("Loading sources:\n");
582
dprintf (" sources_key: %s\n", sources_key);
583
dprintf (" selected_sources_key: %s\n", selected_sources_key);
584
dprintf (" selected_sources_dir: %s\n", selected_sources_dir);
586
gconf_client = sources->priv->gconf_client;
589
source_data->selected_sources = gconf_client_get_list (gconf_client,
590
selected_sources_key,
595
g_warning ("Failed to get selected sources from '%s': %s\n",
596
selected_sources_key,
598
g_error_free (error);
602
gconf_client_add_dir (gconf_client,
603
selected_sources_dir,
604
GCONF_CLIENT_PRELOAD_NONE,
606
source_data->selected_sources_dir = g_strdup (selected_sources_dir);
608
source_data->selected_sources_listener =
609
gconf_client_notify_add (gconf_client,
610
selected_sources_dir,
611
(GConfClientNotifyFunc) calendar_sources_selected_sources_notify,
612
source_data, NULL, NULL);
614
source_data->esource_list = e_source_list_new_for_gconf (gconf_client, sources_key);
615
g_signal_connect (source_data->esource_list, "changed",
616
G_CALLBACK (calendar_sources_esource_list_changed),
619
calendar_sources_load_esource_list (source_data);
621
source_data->loaded = TRUE;
623
dprintf ("---------------------------\n");
627
calendar_sources_get_appointment_sources (CalendarSources *sources)
629
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
631
if (!sources->priv->appointment_sources.loaded)
633
calendar_sources_load_sources (sources,
634
&sources->priv->appointment_sources,
635
CALENDAR_SOURCES_APPOINTMENT_SOURCES_KEY,
636
CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_KEY,
637
CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_DIR);
640
return sources->priv->appointment_sources.clients;
644
calendar_sources_get_task_sources (CalendarSources *sources)
646
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
648
if (!sources->priv->task_sources.loaded)
650
calendar_sources_load_sources (sources,
651
&sources->priv->task_sources,
652
CALENDAR_SOURCES_TASK_SOURCES_KEY,
653
CALENDAR_SOURCES_SELECTED_TASK_SOURCES_KEY,
654
CALENDAR_SOURCES_SELECTED_TASK_SOURCES_DIR);
657
return sources->priv->task_sources.clients;