From 8160d7388e27c6f97e3ed96bc5d61fbf20ff2d16 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Mon, 8 May 2000 16:58:27 +0000 Subject: CalBackendClass now is just an interface for calendar backends; this is an 2000-05-08 Federico Mena Quintero * pcs/cal-backend.h (CalBackendClass): CalBackendClass now is just an interface for calendar backends; this is an abstract class. Put in the vtable for the backend methods. * pcs/cal-backend.c (cal_backend_new): Removed function, since CalBackend is not just an abstract class. Removed implementation-specific functions and made public functions call the virtual methods instead. * pcs/cal-backend-imc.[ch]: New files with the CalBackendIMC implementation; this implements a backend for iCalendar and vCalendar files. Moved the implementation-specific stuff from cal-backend.[ch] to here. * pcs/cal-backend-imc.c (CalendarFormat): Moved enumeration to here. Added a CAL_UNKNOWN value for when the backend is not loaded yet. (cal_backend_imc_init): Initialize priv->format as CAL_UNKNOWN. (save_to_vcal): Use the same VCProdIdProp value as in cal-util/calobj.c. Use "1.0" as the VCVersionProp as per the vCalendar spec. (ensure_uid): Return nothing, since the result value need not be used anymore. (add_object): Since we mark the calendar as dirty anyways, we do not need to check the result value of ensure_uid() anymore. (remove_object): Asssert that we know how to handle the object's type. We do this in add_object() anyways. * pcs/Makefile.am (libpcs_a_SOURCES): Added cal-backend-imc.[ch]. * gui/gnome-cal.c: Replaced debugging printf()s with g_message() so that we can see the line number where they occur. * gui/gnome-cal.c (gnome_calendar_load_cb): Sort of handle the LOAD_METHOD_NOT_SUPPORTED result code, and added a default for the switch. * cal-client/cal-listener.h (CalListenerLoadStatus): Removed enumeration; it is stupid to translate all values for the CalClient when it is going to translate them again. (CalListenerClass::cal_loaded): This signal now passes the LoadStatus directly from the CORBA side. * cal-client/cal-listener.c (Listener_cal_loaded): Do not translate the status value. * cal-client/cal-client.h (CalClientLoadStatus): Added the CAL_CLIENT_LOAD_METHOD_NOT_SUPPORTED error code. * cal-client/cal-client.c (cal_loaded_cb): Translate the CORBA version of the LoadStatus result code. * pcs/cal-factory.c (CalFactoryPrivate): New methods field for the hash table from method strings to the GtkTypes for backend class types. (cal_factory_init): Create the priv->methods hash table. (cal_factory_destroy): Free the priv->methods hash table. (cal_factory_register_method): New function to register a backend class for a particular URI method. (launch_backend_for_uri): New function to launch a backend for a particular URI's method. (load_backend): Use launch_backend_for_uri(). Move the error notification code from load_fn() to here. (create_backend): Use launch_backend_for_uri(). Move the error notification code form create_fn() to here; it is #ifdefed out since currently cal_backend_create() does not have any error reporting capabilities. * idl/evolution-calendar.idl (Listener::LoadStatus): Added a PROTOCOL_NOT_SUPPORTED error code. * pcs/cal-factory.c (cal_factory_load cal_factory_create): Removed functions, since they were supposed to be internal only. (CalFactory_load): Call queue_load_create_job() directly. (CalFactory_create): Likewise. svn path=/trunk/; revision=2921 --- calendar/ChangeLog | 78 +++ calendar/cal-client/cal-client.c | 12 +- calendar/cal-client/cal-client.h | 3 +- calendar/cal-client/cal-listener.c | 21 +- calendar/cal-client/cal-listener.h | 9 +- calendar/gui/gnome-cal.c | 34 +- calendar/idl/evolution-calendar.idl | 11 +- calendar/pcs/Makefile.am | 2 + calendar/pcs/cal-backend-imc.c | 1068 +++++++++++++++++++++++++++++++++++ calendar/pcs/cal-backend-imc.h | 60 ++ calendar/pcs/cal-backend.c | 933 ++---------------------------- calendar/pcs/cal-backend.h | 23 +- calendar/pcs/cal-factory.c | 422 ++++++++------ calendar/pcs/cal-factory.h | 3 +- wombat/ChangeLog | 5 + wombat/wombat.c | 3 + 16 files changed, 1570 insertions(+), 1117 deletions(-) create mode 100644 calendar/pcs/cal-backend-imc.c create mode 100644 calendar/pcs/cal-backend-imc.h diff --git a/calendar/ChangeLog b/calendar/ChangeLog index 4c1f5810cb..29a917a70c 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,81 @@ +2000-05-08 Federico Mena Quintero + + * pcs/cal-backend.h (CalBackendClass): CalBackendClass now is just + an interface for calendar backends; this is an abstract class. + Put in the vtable for the backend methods. + + * pcs/cal-backend.c (cal_backend_new): Removed function, since + CalBackend is not just an abstract class. + Removed implementation-specific functions and made public + functions call the virtual methods instead. + + * pcs/cal-backend-imc.[ch]: New files with the CalBackendIMC + implementation; this implements a backend for iCalendar and + vCalendar files. Moved the implementation-specific stuff from + cal-backend.[ch] to here. + + * pcs/cal-backend-imc.c (CalendarFormat): Moved enumeration to + here. Added a CAL_UNKNOWN value for when the backend is not + loaded yet. + (cal_backend_imc_init): Initialize priv->format as CAL_UNKNOWN. + (save_to_vcal): Use the same VCProdIdProp value as in + cal-util/calobj.c. Use "1.0" as the VCVersionProp as per the + vCalendar spec. + (ensure_uid): Return nothing, since the result value need not be + used anymore. + (add_object): Since we mark the calendar as dirty anyways, we do + not need to check the result value of ensure_uid() anymore. + (remove_object): Asssert that we know how to handle the object's + type. We do this in add_object() anyways. + + * pcs/Makefile.am (libpcs_a_SOURCES): Added cal-backend-imc.[ch]. + + * gui/gnome-cal.c: Replaced debugging printf()s with g_message() + so that we can see the line number where they occur. + + * gui/gnome-cal.c (gnome_calendar_load_cb): Sort of handle the + LOAD_METHOD_NOT_SUPPORTED result code, and added a default for the + switch. + + * cal-client/cal-listener.h (CalListenerLoadStatus): Removed + enumeration; it is stupid to translate all values for the + CalClient when it is going to translate them again. + (CalListenerClass::cal_loaded): This signal now passes the + LoadStatus directly from the CORBA side. + + * cal-client/cal-listener.c (Listener_cal_loaded): Do not + translate the status value. + + * cal-client/cal-client.h (CalClientLoadStatus): Added the + CAL_CLIENT_LOAD_METHOD_NOT_SUPPORTED error code. + + * cal-client/cal-client.c (cal_loaded_cb): Translate the CORBA + version of the LoadStatus result code. + + * pcs/cal-factory.c (CalFactoryPrivate): New methods field for the + hash table from method strings to the GtkTypes for backend class + types. + (cal_factory_init): Create the priv->methods hash table. + (cal_factory_destroy): Free the priv->methods hash table. + (cal_factory_register_method): New function to register a backend + class for a particular URI method. + (launch_backend_for_uri): New function to launch a backend for a + particular URI's method. + (load_backend): Use launch_backend_for_uri(). Move the error + notification code from load_fn() to here. + (create_backend): Use launch_backend_for_uri(). Move the error + notification code form create_fn() to here; it is #ifdefed out + since currently cal_backend_create() does not have any error + reporting capabilities. + + * idl/evolution-calendar.idl (Listener::LoadStatus): Added a + PROTOCOL_NOT_SUPPORTED error code. + + * pcs/cal-factory.c (cal_factory_load cal_factory_create): Removed + functions, since they were supposed to be internal only. + (CalFactory_load): Call queue_load_create_job() directly. + (CalFactory_create): Likewise. + 2000-05-08 Damon Chaplin * gui/e-week-view.c (e_week_view_remove_event_cb): diff --git a/calendar/cal-client/cal-client.c b/calendar/cal-client/cal-client.c index bffa12ac65..c7f5302d14 100644 --- a/calendar/cal-client/cal-client.c +++ b/calendar/cal-client/cal-client.c @@ -281,7 +281,7 @@ cal_client_destroy (GtkObject *object) /* Handle the cal_loaded signal from the listener */ static void cal_loaded_cb (CalListener *listener, - CalListenerLoadStatus status, + Evolution_Calendar_Listener_LoadStatus status, Evolution_Calendar_Cal cal, gpointer data) { @@ -299,7 +299,7 @@ cal_loaded_cb (CalListener *listener, client_status = CAL_CLIENT_LOAD_ERROR; switch (status) { - case CAL_LISTENER_LOAD_SUCCESS: + case Evolution_Calendar_Listener_SUCCESS: CORBA_exception_init (&ev); cal_copy = CORBA_Object_duplicate (cal, &ev); if (ev._major != CORBA_NO_EXCEPTION) { @@ -315,14 +315,18 @@ cal_loaded_cb (CalListener *listener, client_status = CAL_CLIENT_LOAD_SUCCESS; goto out; - case CAL_LISTENER_LOAD_ERROR: + case Evolution_Calendar_Listener_ERROR: client_status = CAL_CLIENT_LOAD_ERROR; goto error; - case CAL_LISTENER_LOAD_IN_USE: + case Evolution_Calendar_Listener_IN_USE: client_status = CAL_CLIENT_LOAD_IN_USE; goto error; + case Evolution_Calendar_Listener_METHOD_NOT_SUPPORTED: + client_status = CAL_CLIENT_LOAD_METHOD_NOT_SUPPORTED; + goto error; + default: g_assert_not_reached (); } diff --git a/calendar/cal-client/cal-client.h b/calendar/cal-client/cal-client.h index e03008ca67..e4731980e0 100644 --- a/calendar/cal-client/cal-client.h +++ b/calendar/cal-client/cal-client.h @@ -43,7 +43,8 @@ typedef struct _CalClientClass CalClientClass; typedef enum { CAL_CLIENT_LOAD_SUCCESS, CAL_CLIENT_LOAD_ERROR, - CAL_CLIENT_LOAD_IN_USE + CAL_CLIENT_LOAD_IN_USE, + CAL_CLIENT_LOAD_METHOD_NOT_SUPPORTED } CalClientLoadStatus; struct _CalClient { diff --git a/calendar/cal-client/cal-listener.c b/calendar/cal-client/cal-listener.c index 2c875c4c38..884d64e8f9 100644 --- a/calendar/cal-client/cal-listener.c +++ b/calendar/cal-client/cal-listener.c @@ -219,7 +219,6 @@ Listener_cal_loaded (PortableServer_Servant servant, CalListenerPrivate *priv; CORBA_Environment aev; Evolution_Calendar_Cal cal_copy; - CalListenerLoadStatus load_status; listener = CAL_LISTENER (bonobo_object_from_servant (servant)); priv = listener->priv; @@ -241,26 +240,8 @@ Listener_cal_loaded (PortableServer_Servant servant, priv->cal = cal_copy; - switch (status) { - case Evolution_Calendar_Listener_SUCCESS: - load_status = CAL_LISTENER_LOAD_SUCCESS; - break; - - case Evolution_Calendar_Listener_ERROR: - load_status = CAL_LISTENER_LOAD_ERROR; - break; - - case Evolution_Calendar_Listener_IN_USE: - load_status = CAL_LISTENER_LOAD_IN_USE; - break; - - default: - load_status = CAL_LISTENER_LOAD_ERROR; /* keep gcc happy */ - g_assert_not_reached (); - } - gtk_signal_emit (GTK_OBJECT (listener), cal_listener_signals[CAL_LOADED], - load_status, cal); + status, cal); } /* Listener::obj_updated method */ diff --git a/calendar/cal-client/cal-listener.h b/calendar/cal-client/cal-listener.h index 23590e6ff8..171da0d5c3 100644 --- a/calendar/cal-client/cal-listener.h +++ b/calendar/cal-client/cal-listener.h @@ -40,13 +40,6 @@ BEGIN_GNOME_DECLS typedef struct _CalListener CalListener; typedef struct _CalListenerClass CalListenerClass; -/* Load status for the cal_loaded signal. We need better error reporting. */ -typedef enum { - CAL_LISTENER_LOAD_SUCCESS, - CAL_LISTENER_LOAD_ERROR, - CAL_LISTENER_LOAD_IN_USE -} CalListenerLoadStatus; - struct _CalListener { BonoboObject object; @@ -60,7 +53,7 @@ struct _CalListenerClass { /* Notification signals */ void (* cal_loaded) (CalListener *listener, - CalListenerLoadStatus status, + Evolution_Calendar_Listener_LoadStatus status, Evolution_Calendar_Cal cal); void (* obj_updated) (CalListener *listener, const Evolution_Calendar_CalObjUID uid); void (* obj_removed) (CalListener *listener, const Evolution_Calendar_CalObjUID uid); diff --git a/calendar/gui/gnome-cal.c b/calendar/gui/gnome-cal.c index f7a6eb5ac4..db46b113ee 100644 --- a/calendar/gui/gnome-cal.c +++ b/calendar/gui/gnome-cal.c @@ -412,8 +412,8 @@ gnome_calendar_object_updated_cb (GtkWidget *cal_client, const char *uid, GnomeCalendar *gcal) { - printf ("gnome-cal: got object changed_cb, uid='%s'\n", - uid?uid:""); + g_message ("gnome-cal: got object changed_cb, uid='%s'", + uid?uid:""); /* FIXME: do we really want each view to reload the event itself? Maybe we should keep track of events globally, maybe with ref @@ -438,8 +438,8 @@ gnome_calendar_object_removed_cb (GtkWidget *cal_client, const char *uid, GnomeCalendar *gcal) { - printf ("gnome-cal: got object removed _cb, uid='%s'\n", - uid?uid:""); + g_message ("gnome-cal: got object removed _cb, uid='%s'", + uid?uid:""); e_day_view_remove_event (E_DAY_VIEW (gcal->day_view), uid); e_day_view_remove_event (E_DAY_VIEW (gcal->work_week_view), uid); @@ -493,21 +493,22 @@ typedef struct static void gnome_calendar_load_cb (GtkWidget *cal_client, - CalClientLoadStatus success, + CalClientLoadStatus status, load_or_create_data *locd) { g_return_if_fail (locd); g_return_if_fail (GNOME_IS_CALENDAR (locd->gcal)); - switch (success) { + switch (status) { case CAL_CLIENT_LOAD_SUCCESS: gnome_calendar_update_all (locd->gcal, NULL, 0); - printf ("gnome_calendar_load_cb: success\n"); + g_message ("gnome_calendar_load_cb: success"); break; + case CAL_CLIENT_LOAD_ERROR: - printf ("gnome_calendar_load_cb: load error.\n"); + g_message ("gnome_calendar_load_cb: load error"); if (locd->gcom == CALENDAR_OPEN_OR_CREATE) { - printf ("gnome_calendar_load_cb: trying create...\n"); + g_message ("gnome_calendar_load_cb: trying create..."); /* FIXME: connect to the cal_loaded signal of the * CalClient and get theasynchronous notification * properly! */ @@ -523,9 +524,20 @@ gnome_calendar_load_cb (GtkWidget *cal_client, gnome_calendar_update_all (locd->gcal, NULL, 0); } break; + case CAL_CLIENT_LOAD_IN_USE: - printf ("gnome_calendar_load_cb: in use\n"); + /* FIXME: what to do? */ + g_message ("gnome_calendar_load_cb: in use"); + break; + + case CAL_CLIENT_LOAD_METHOD_NOT_SUPPORTED: + /* FIXME: what to do? */ + g_message ("gnome_calendar_load_cb(): method not supported"); break; + + default: + g_message ("gnome_calendar_load_cb(): unhandled result code %d!", (int) status); + g_assert_not_reached (); } g_free (locd->uri); @@ -555,7 +567,7 @@ gnome_calendar_open (GnomeCalendar *gcal, locd); if (cal_client_load_calendar (gcal->client, file) == FALSE){ - printf ("Error loading calendar: %s\n", file); + g_message ("Error loading calendar: %s", file); return 0; } diff --git a/calendar/idl/evolution-calendar.idl b/calendar/idl/evolution-calendar.idl index 6de8fa57b4..eda35a3419 100644 --- a/calendar/idl/evolution-calendar.idl +++ b/calendar/idl/evolution-calendar.idl @@ -88,11 +88,12 @@ module Calendar { interface Listener : Bonobo::Unknown { /* Return status when loading a calendar; we need better error reporting */ enum LoadStatus { - SUCCESS, /* All OK */ - ERROR, /* Generic error */ - IN_USE /* Requested create while a calendar - * with the same URI was in use. - */ + SUCCESS, /* All OK */ + ERROR, /* Generic error */ + IN_USE, /* Requested create while a calendar + * with the same URI was in use. + */ + METHOD_NOT_SUPPORTED /* A method handler is not registered */ }; /* Called from a CalFactory when a calendar is initially loaded diff --git a/calendar/pcs/Makefile.am b/calendar/pcs/Makefile.am index b9ea5fecd0..b496659f03 100644 --- a/calendar/pcs/Makefile.am +++ b/calendar/pcs/Makefile.am @@ -28,6 +28,8 @@ libpcs_a_SOURCES = \ cal.h \ cal-backend.c \ cal-backend.h \ + cal-backend-imc.c \ + cal-backend-imc.h \ cal-common.h \ cal-factory.c \ cal-factory.h \ diff --git a/calendar/pcs/cal-backend-imc.c b/calendar/pcs/cal-backend-imc.c new file mode 100644 index 0000000000..29c806a89f --- /dev/null +++ b/calendar/pcs/cal-backend-imc.c @@ -0,0 +1,1068 @@ +/* Evolution calendar - Internet Mail Consortium formats backend + * + * Copyright (C) 2000 Helix Code, Inc. + * + * Authors: Federico Mena-Quintero + * Seth Alves + * + * 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. + */ + +#include +#include +#include "cal-backend-imc.h" +#include "icalendar.h" + + + +/* Supported calendar formats from the IMC */ +typedef enum { + CAL_FORMAT_UNKNOWN, + CAL_FORMAT_VCALENDAR, + CAL_FORMAT_ICALENDAR +} CalendarFormat; + +/* Private part of the CalBackendIMC structure */ +typedef struct { + /* URI where the calendar data is stored */ + GnomeVFSURI *uri; + + /* Format of this calendar (iCalendar or vCalendar) */ + CalendarFormat format; + + /* List of Cal objects with their listeners */ + GList *clients; + + /* All the iCalObject structures in the calendar, hashed by UID. The + * hash key *is* icalobj->uid; it is not copied, so don't free it when + * you remove an object from the hash table. + */ + GHashTable *object_hash; + + /* All events, TODOs, and journals in the calendar */ + GList *events; + GList *todos; + GList *journals; + + /* Whether a calendar has been loaded */ + guint loaded : 1; + + /* Do we need to sync to permanent storage? */ + gboolean dirty : 1; +} IMCPrivate; + + + +static void cal_backend_imc_class_init (CalBackendIMCClass *class); +static void cal_backend_imc_init (CalBackendIMC *bimc); +static void cal_backend_imc_destroy (GtkObject *object); + +static GnomeVFSURI *cal_backend_imc_get_uri (CalBackend *backend); +static void cal_backend_imc_add_cal (CalBackend *backend, Cal *cal); +static CalBackendLoadStatus cal_backend_imc_load (CalBackend *backend, GnomeVFSURI *uri); +static void cal_backend_imc_create (CalBackend *backend, GnomeVFSURI *uri); +static char *cal_backend_imc_get_object (CalBackend *backend, const char *uid); +static GList *cal_backend_imc_get_uids (CalBackend *backend, CalObjType type); +static GList *cal_backend_imc_get_events_in_range (CalBackend *backend, time_t start, time_t end); +static gboolean cal_backend_imc_update_object (CalBackend *backend, const char *uid, + const char *calobj); +static gboolean cal_backend_imc_remove_object (CalBackend *backend, const char *uid); + +static CalBackendClass *parent_class; + + + +/** + * cal_backend_imc_get_type: + * @void: + * + * Registers the #CalBackendIMC class if necessary, and returns the type ID + * associated to it. + * + * Return value: The type ID of the #CalBackendIMC class. + **/ +GtkType +cal_backend_imc_get_type (void) +{ + static GtkType cal_backend_imc_type = 0; + + if (!cal_backend_imc_type) { + static const GtkTypeInfo cal_backend_imc_info = { + "CalBackendIMC", + sizeof (CalBackendIMC), + sizeof (CalBackendIMCClass), + (GtkClassInitFunc) cal_backend_imc_class_init, + (GtkObjectInitFunc) cal_backend_imc_init, + NULL, /* reserved_1 */ + NULL, /* reserved_2 */ + (GtkClassInitFunc) NULL + }; + + cal_backend_imc_type = gtk_type_unique (CAL_BACKEND_TYPE, &cal_backend_imc_info); + } + + return cal_backend_imc_type; +} + +/* Class initialization function for the IMC backend */ +static void +cal_backend_imc_class_init (CalBackendIMCClass *class) +{ + GtkObjectClass *object_class; + CalBackendClass *backend_class; + + object_class = (GtkObjectClass *) class; + backend_class = (CalBackendClass *) class; + + parent_class = gtk_type_class (CAL_BACKEND_TYPE); + + backend_class->get_uri = cal_backend_imc_get_uri; + backend_class->add_cal = cal_backend_imc_add_cal; + backend_class->load = cal_backend_imc_load; + backend_class->create = cal_backend_imc_create; + backend_class->get_object = cal_backend_imc_get_object; + backend_class->get_uids = cal_backend_imc_get_uids; + backend_class->get_events_in_range = cal_backend_imc_get_events_in_range; + backend_class->update_object = cal_backend_imc_update_object; + backend_class->remove_object = cal_backend_imc_remove_object; + + object_class->destroy = cal_backend_imc_destroy; +} + +/* Object initialization function for the IMC backend */ +static void +cal_backend_imc_init (CalBackendIMC *cbimc) +{ + IMCPrivate *priv; + + priv = g_new0 (IMCPrivate, 1); + cbimc->priv = priv; + + priv->format = CAL_FORMAT_UNKNOWN; +} + +static void +save_to_vcal (CalBackendIMC *cbimc, char *fname) +{ + FILE *fp; + IMCPrivate *priv; + VObject *vcal; + GList *l; + + priv = cbimc->priv; + + if (g_file_exists (fname)) { + char *backup_name = g_strconcat (fname, "~", NULL); + + /* FIXME: do error checking on system calls!!!! */ + + if (g_file_exists (backup_name)) + unlink (backup_name); + + rename (fname, backup_name); + g_free (backup_name); + } + + vcal = newVObject (VCCalProp); + addPropValue (vcal, VCProdIdProp, + "-//Helix Code//NONSGML Evolution Calendar//EN"); + + /* Per the vCalendar spec, this must be "1.0" */ + addPropValue (vcal, VCVersionProp, "1.0"); + + /* FIXME: this should really iterate over the object hash table instead + * of the lists; that way we won't lose objects if they are of a type + * that we don't support but are in the calendar anyways. + */ + + for (l = priv->events; l; l = l->next) { + iCalObject *ical = l->data; + VObject *vobject = ical_object_to_vobject (ical); + addVObjectProp (vcal, vobject); + } + + for (l = priv->todos; l; l = l->next) { + iCalObject *ical = l->data; + VObject *vobject = ical_object_to_vobject (ical); + addVObjectProp (vcal, vobject); + } + + for (l = priv->journals; l; l = l->next) { + iCalObject *ical = l->data; + VObject *vobject = ical_object_to_vobject (ical); + addVObjectProp (vcal, vobject); + } + + fp = fopen(fname,"w"); + if (fp) { + writeVObject(fp, vcal); + fclose(fp); + } + cleanVObject (vcal); + cleanStrTbl (); +} + +/* Saves a calendar */ +static void +save (CalBackendIMC *cbimc) +{ + char *str_uri; + IMCPrivate *priv = cbimc->priv; + + str_uri = gnome_vfs_uri_to_string (priv->uri, + (GNOME_VFS_URI_HIDE_USER_NAME + | GNOME_VFS_URI_HIDE_PASSWORD + | GNOME_VFS_URI_HIDE_HOST_NAME + | GNOME_VFS_URI_HIDE_HOST_PORT + | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD)); + + if (!priv->dirty) + return; + + switch (priv->format) { + case CAL_FORMAT_VCALENDAR: + save_to_vcal (cbimc, str_uri); + break; + + case CAL_FORMAT_ICALENDAR: + /*icalendar_calendar_save (cbimc, str_uri);*/ + /* FIX ME */ + break; + + default: + g_message ("save(): Attempt to save a calendar with an unknown format!"); + break; + } + + printf ("cal-backend-imc: '%s' saved\n", str_uri); + + g_free (str_uri); +} + +/* g_hash_table_foreach() callback to destroy an iCalObject */ +static void +free_ical_object (gpointer key, gpointer value, gpointer data) +{ + iCalObject *ico; + + ico = value; + ical_object_destroy (ico); +} + +/* Destroys an IMC backend's data */ +static void +destroy (CalBackendIMC *cbimc) +{ + IMCPrivate *priv; + + priv = cbimc->priv; + + if (priv->uri) { + gnome_vfs_uri_unref (priv->uri); + priv->uri = NULL; + } + + g_assert (priv->clients == NULL); + + if (priv->object_hash) { + g_hash_table_foreach (priv->object_hash, free_ical_object, NULL); + g_hash_table_destroy (priv->object_hash); + priv->object_hash = NULL; + } + + g_list_free (priv->events); + g_list_free (priv->todos); + g_list_free (priv->journals); + + priv->events = NULL; + priv->todos = NULL; + priv->journals = NULL; + + priv->loaded = FALSE; + priv->format = CAL_FORMAT_UNKNOWN; +} + +/* Destroy handler for the IMC backend */ +static void +cal_backend_imc_destroy (GtkObject *object) +{ + CalBackendIMC *cbimc; + IMCPrivate *priv; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_CAL_BACKEND_IMC (object)); + + cbimc = CAL_BACKEND_IMC (object); + priv = cbimc->priv; + + /* + if (priv->loaded) + save (cbimc); + */ + + destroy (cbimc); + + g_free (priv); + cbimc->priv = NULL; + + if (GTK_OBJECT_CLASS (parent_class)->destroy) + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + + + +/* iCalObject manipulation functions */ + +/* Looks up an object by its UID in the backend's object hash table */ +static iCalObject * +lookup_object (CalBackendIMC *cbimc, const char *uid) +{ + IMCPrivate *priv; + iCalObject *ico; + + priv = cbimc->priv; + ico = g_hash_table_lookup (priv->object_hash, uid); + + return ico; +} + +/* Ensures that an iCalObject has a unique identifier. If it doesn't have one, + * it will create one for it. + */ +static void +ensure_uid (iCalObject *ico) +{ + char *buf; + gulong str_time; + static guint seqno = 0; + + if (ico->uid) + return; + + str_time = (gulong) time (NULL); + + /* Is this good enough? */ + + buf = g_strdup_printf ("Evolution-Calendar-%d-%ld-%u", + (int) getpid(), str_time, seqno++); + ico->uid = buf; +} + +/* Adds an object to the calendar backend. Does *not* perform notification to + * calendar clients. + */ +static void +add_object (CalBackendIMC *cbimc, iCalObject *ico) +{ + IMCPrivate *priv; + + g_assert (ico != NULL); + + priv = cbimc->priv; + +#if 0 + /* FIXME: gnomecal old code */ + ico->new = 0; +#endif + + ensure_uid (ico); + g_hash_table_insert (priv->object_hash, ico->uid, ico); + + priv->dirty = TRUE; + + switch (ico->type) { + case ICAL_EVENT: + priv->events = g_list_prepend (priv->events, ico); +#if 0 + /* FIXME: gnomecal old code */ + ical_object_try_alarms (ico); +# ifdef DEBUGGING_MAIL_ALARM + ico->malarm.trigger = 0; + calendar_notify (0, ico); +# endif +#endif + break; + + case ICAL_TODO: + priv->todos = g_list_prepend (priv->todos, ico); + break; + + case ICAL_JOURNAL: + priv->journals = g_list_prepend (priv->journals, ico); + break; + + default: + g_assert_not_reached (); + } + +#if 0 + /* FIXME: gnomecal old code */ + ico->last_mod = time (NULL); +#endif +} + +/* Removes an object from the backend's hash and lists. Does not perform + * notification on the clients. + */ +static void +remove_object (CalBackendIMC *cbimc, iCalObject *ico) +{ + IMCPrivate *priv; + GList **list, *l; + + priv = cbimc->priv; + + g_assert (ico->uid != NULL); + g_hash_table_remove (priv->object_hash, ico->uid); + + priv->dirty = TRUE; + + switch (ico->type) { + case ICAL_EVENT: + list = &priv->events; + break; + + case ICAL_TODO: + list = &priv->todos; + break; + + case ICAL_JOURNAL: + list = &priv->journals; + break; + + default: + g_assert_not_reached (); + } + + l = g_list_find (*list, ico); + g_assert (l != NULL); + + *list = g_list_remove_link (*list, l); + g_list_free_1 (l); + + ical_object_destroy (ico); +} + +/* Load a calendar from a VObject */ +static void +load_from_vobject (CalBackendIMC *cbimc, VObject *vobject) +{ + IMCPrivate *priv; + VObjectIterator i; + + priv = cbimc->priv; + + g_assert (!priv->loaded); + g_assert (priv->object_hash == NULL); + priv->object_hash = g_hash_table_new (g_str_hash, g_str_equal); + + initPropIterator (&i, vobject); + + while (moreIteration (&i)) { + VObject *this; + iCalObject *ical; + const char *object_name; + + this = nextVObject (&i); + object_name = vObjectName (this); +#if 0 + /* FIXME? What is this used for in gnomecal? */ + if (strcmp (object_name, VCDCreatedProp) == 0) { + cal->created = time_from_isodate (str_val (this)); + continue; + } +#endif + if (strcmp (object_name, VCLocationProp) == 0) + continue; /* FIXME: imlement */ + + if (strcmp (object_name, VCProdIdProp) == 0) + continue; /* FIXME: implement */ + + if (strcmp (object_name, VCVersionProp) == 0) + continue; /* FIXME: implement */ + + if (strcmp (object_name, VCTimeZoneProp) == 0) + continue; /* FIXME: implement */ + + ical = ical_object_create_from_vobject (this, object_name); + + /* FIXME: some broken files (ahem, old KOrganizer files) may + * have duplicated UIDs. This is Bad(tm). Deal with it by + * creating new UIDs for them and spitting some messages to the + * console. + */ + + if (ical) + add_object (cbimc, ical); + } +} + + + +/* Calendar backend methods */ + +/* Get_uri handler for the IMC backend */ +static GnomeVFSURI * +cal_backend_imc_get_uri (CalBackend *backend) +{ + CalBackendIMC *cbimc; + IMCPrivate *priv; + + cbimc = CAL_BACKEND_IMC (backend); + priv = cbimc->priv; + + g_return_val_if_fail (priv->loaded, NULL); + g_assert (priv->uri != NULL); + + return priv->uri; +} + +/* Callback used when a Cal is destroyed */ +static void +cal_destroy_cb (GtkObject *object, gpointer data) +{ + Cal *cal; + Cal *lcal; + CalBackendIMC *cbimc; + IMCPrivate *priv; + GList *l; + + cal = CAL (object); + + cbimc = CAL_BACKEND_IMC (data); + priv = cbimc->priv; + + /* Find the cal in the list of clients */ + + for (l = priv->clients; l; l = l->next) { + lcal = CAL (l->data); + + if (lcal == cal) + break; + } + + g_assert (l != NULL); + + /* Disconnect */ + + priv->clients = g_list_remove_link (priv->clients, l); + g_list_free_1 (l); + + /* When all clients go away, notify the parent factory about it so that + * it may decide whether to kill the backend or not. + */ + if (!priv->clients) + cal_backend_last_client_gone (CAL_BACKEND (cbimc)); +} + +/* Add_cal handler for the IMC backend */ +static void +cal_backend_imc_add_cal (CalBackend *backend, Cal *cal) +{ + CalBackendIMC *cbimc; + IMCPrivate *priv; + + cbimc = CAL_BACKEND_IMC (backend); + priv = cbimc->priv; + + g_return_if_fail (priv->loaded); + g_return_if_fail (cal != NULL); + g_return_if_fail (IS_CAL (cal)); + + /* We do not keep a reference to the Cal since the calendar user agent + * owns it. + */ + + gtk_signal_connect (GTK_OBJECT (cal), "destroy", + GTK_SIGNAL_FUNC (cal_destroy_cb), + backend); + + priv->clients = g_list_prepend (priv->clients, cal); +} + +static icalcomponent * +icalendar_parse_file (char *fname) +{ + FILE *fp; + icalcomponent *comp = NULL; + char *str; + struct stat st; + int n; + + fp = fopen (fname, "r"); + if (!fp) { + /* FIXME: remove message */ + g_message ("icalendar_parse_file(): Cannot open open calendar file."); + return NULL; + } + + stat (fname, &st); + + str = g_malloc (st.st_size + 2); + + n = fread (str, 1, st.st_size, fp); + if (n != st.st_size) { + /* FIXME: remove message, return error code instead */ + g_message ("icalendar_parse_file(): Read error."); + } + str[n] = '\0'; + + fclose (fp); + + comp = icalparser_parse_string (str); + g_free (str); + + return comp; +} + +static void +icalendar_calendar_load (CalBackendIMC *cbimc, char *fname) +{ + IMCPrivate *priv; + icalcomponent *comp; + icalcomponent *subcomp; + iCalObject *ical; + + priv = cbimc->priv; + + g_assert (!priv->loaded); + g_assert (priv->object_hash == NULL); + + priv->object_hash = g_hash_table_new (g_str_hash, g_str_equal); + + comp = icalendar_parse_file (fname); + subcomp = icalcomponent_get_first_component (comp, + ICAL_ANY_COMPONENT); + while (subcomp) { + ical = ical_object_create_from_icalcomponent (subcomp); + if (ical->type != ICAL_EVENT && + ical->type != ICAL_TODO && + ical->type != ICAL_JOURNAL) { + g_message ("icalendar_calendar_load(): Skipping unsupported " + "iCalendar component."); + } else + add_object (cbimc, ical); + + subcomp = icalcomponent_get_next_component (comp, + ICAL_ANY_COMPONENT); + } +} + +/* ics is to be used to designate a file containing (an arbitrary set of) + * calendaring and scheduling information. + * + * ifb is to be used to designate a file containing free or busy time + * information. + * + * anything else is assumed to be a vcal file. + * + * FIXME: should we return UNKNOWN at some point? + */ +static CalendarFormat +cal_get_type_from_filename (char *str_uri) +{ + int len; + + if (str_uri == NULL) + return CAL_FORMAT_VCALENDAR; + + len = strlen (str_uri); + if (len < 4) + return CAL_FORMAT_VCALENDAR; + + if (str_uri[len - 4] == '.' && str_uri[len - 3] == 'i' && + str_uri[len - 2] == 'c' && str_uri[len - 1] == 's') + return CAL_FORMAT_ICALENDAR; + + if (str_uri[len - 4] == '.' && str_uri[len - 3] == 'i' && + str_uri[len - 2] == 'f' && str_uri[len - 1] == 'b') + return CAL_FORMAT_ICALENDAR; + + if (str_uri[len - 4] == '.' && str_uri[len - 3] == 'i' && + str_uri[len - 2] == 'c' && str_uri[len - 1] == 's') + return CAL_FORMAT_ICALENDAR; + + if (len < 5) + return CAL_FORMAT_VCALENDAR; + + if (str_uri[len - 5] == '.' && str_uri[len - 4] == 'i' && + str_uri[len - 3] == 'c' && str_uri[len - 2] == 'a' && + str_uri[len - 1] == 'l') + return CAL_FORMAT_ICALENDAR; + + return CAL_FORMAT_VCALENDAR; +} + +/* Load handler for the IMC backend */ +static CalBackendLoadStatus +cal_backend_imc_load (CalBackend *backend, GnomeVFSURI *uri) +{ + CalBackendIMC *cbimc; + IMCPrivate *priv; + VObject *vobject; + char *str_uri; + + cbimc = CAL_BACKEND_IMC (backend); + priv = cbimc->priv; + + g_return_val_if_fail (!priv->loaded, CAL_BACKEND_LOAD_ERROR); + g_return_val_if_fail (uri != NULL, CAL_BACKEND_LOAD_ERROR); + + /* FIXME: this looks rather bad; maybe we should check for local files + * and fail if they are remote. + */ + + str_uri = gnome_vfs_uri_to_string (uri, + (GNOME_VFS_URI_HIDE_USER_NAME + | GNOME_VFS_URI_HIDE_PASSWORD + | GNOME_VFS_URI_HIDE_HOST_NAME + | GNOME_VFS_URI_HIDE_HOST_PORT + | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD)); + + /* look at the extension on the filename and decide if this is a + * iCalendar or vCalendar file. + */ + priv->format = cal_get_type_from_filename (str_uri); + + /* load */ + + switch (priv->format) { + case CAL_FORMAT_VCALENDAR: + vobject = Parse_MIME_FromFileName (str_uri); + + if (!vobject){ + g_free (str_uri); + return CAL_BACKEND_LOAD_ERROR; + } + + load_from_vobject (cbimc, vobject); + cleanVObject (vobject); + cleanStrTbl (); + break; + + case CAL_FORMAT_ICALENDAR: + icalendar_calendar_load (cbimc, str_uri); + break; + + default: + g_free (str_uri); + return CAL_BACKEND_LOAD_ERROR; + } + + g_free (str_uri); + + gnome_vfs_uri_ref (uri); + + priv->uri = uri; + priv->loaded = TRUE; + + return CAL_BACKEND_LOAD_SUCCESS; +} + +/* Create handler for the IMC backend */ +static void +cal_backend_imc_create (CalBackend *backend, GnomeVFSURI *uri) +{ + CalBackendIMC *cbimc; + IMCPrivate *priv; + + cbimc = CAL_BACKEND_IMC (backend); + priv = cbimc->priv; + + g_return_if_fail (!priv->loaded); + g_return_if_fail (uri != NULL); + + /* Create the new calendar information */ + + g_assert (priv->object_hash == NULL); + priv->object_hash = g_hash_table_new (g_str_hash, g_str_equal); + + priv->dirty = TRUE; + + /* Done */ + + gnome_vfs_uri_ref (uri); + + priv->uri = uri; + priv->loaded = TRUE; + + save (cbimc); +} + +/* Get_object handler for the IMC backend */ +static char * +cal_backend_imc_get_object (CalBackend *backend, const char *uid) +{ + CalBackendIMC *cbimc; + IMCPrivate *priv; + iCalObject *ico; + char *buf; + + cbimc = CAL_BACKEND_IMC (backend); + priv = cbimc->priv; + + g_return_val_if_fail (uid != NULL, NULL); + + g_return_val_if_fail (priv->loaded, NULL); + g_assert (priv->object_hash != NULL); + + ico = lookup_object (cbimc, uid); + + if (!ico) + return NULL; + + buf = ical_object_to_string (ico); + + return buf; +} + +struct get_uids_closure { + CalObjType type; + GList *uid_list; +}; + +/* Builds a list of UIDs for objects that match the sought type. Called from + * g_hash_table_foreach(). + */ +static void +build_uids_list (gpointer key, gpointer value, gpointer data) +{ + iCalObject *ico; + struct get_uids_closure *c; + gboolean store; + + ico = value; + c = data; + + store = FALSE; + + if (ico->type == ICAL_EVENT) + store = (c->type & CALOBJ_TYPE_EVENT) ? TRUE : FALSE; + else if (ico->type == ICAL_TODO) + store = (c->type & CALOBJ_TYPE_TODO) ? TRUE : FALSE; + else if (ico->type == ICAL_JOURNAL) + store = (c->type & CALOBJ_TYPE_JOURNAL) ? TRUE : FALSE; + else + store = (c->type & CALOBJ_TYPE_OTHER) ? TRUE : FALSE; + + if (store) + c->uid_list = g_list_prepend (c->uid_list, g_strdup (ico->uid)); +} + +/* Get_uids handler for the IMC backend */ +static GList * +cal_backend_imc_get_uids (CalBackend *backend, CalObjType type) +{ + CalBackendIMC *cbimc; + IMCPrivate *priv; + struct get_uids_closure c; + + cbimc = CAL_BACKEND_IMC (backend); + priv = cbimc->priv; + + g_return_val_if_fail (priv->loaded, NULL); + + /* We go through the hash table instead of the lists of particular + * object types so that we can pick up CALOBJ_TYPE_OTHER objects. + */ + c.type = type; + c.uid_list = NULL; + g_hash_table_foreach (priv->object_hash, build_uids_list, &c); + + return c.uid_list; +} + +struct build_event_list_closure { + CalBackendIMC *cbimc; + GList *event_list; +}; + +/* Builds a sorted list of event object instances. Used as a callback from + * ical_object_generate_events(). + */ +static int +build_event_list (iCalObject *ico, time_t start, time_t end, void *data) +{ + CalObjInstance *icoi; + struct build_event_list_closure *c; + + c = data; + + icoi = g_new (CalObjInstance, 1); + + g_assert (ico->uid != NULL); + icoi->uid = g_strdup (ico->uid); + icoi->start = start; + icoi->end = end; + + c->event_list = g_list_prepend (c->event_list, icoi); + + return TRUE; +} + +/* Compares two CalObjInstance structures by their start times. Called from + * g_list_sort(). + */ +static gint +compare_instance_func (gconstpointer a, gconstpointer b) +{ + const CalObjInstance *ca, *cb; + time_t diff; + + ca = a; + cb = b; + + diff = ca->start - cb->start; + return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; +} + +/* Get_events_in_range handler for the IMC backend */ +static GList * +cal_backend_imc_get_events_in_range (CalBackend *backend, time_t start, time_t end) +{ + struct build_event_list_closure c; + GList *l; + + CalBackendIMC *cbimc; + IMCPrivate *priv; + + cbimc = CAL_BACKEND_IMC (backend); + priv = cbimc->priv; + + g_return_val_if_fail (priv->loaded, NULL); + + g_return_val_if_fail (start != -1 && end != -1, NULL); + g_return_val_if_fail (start <= end, NULL); + + c.cbimc = cbimc; + c.event_list = NULL; + + for (l = priv->events; l; l = l->next) { + iCalObject *ico; + + ico = l->data; + ical_object_generate_events (ico, start, end, + build_event_list, &c); + } + + c.event_list = g_list_sort (c.event_list, compare_instance_func); + + return c.event_list; +} + +/* Notifies a backend's clients that an object was updated */ +static void +notify_update (CalBackendIMC *cbimc, const char *uid) +{ + IMCPrivate *priv; + GList *l; + + priv = cbimc->priv; + + for (l = priv->clients; l; l = l->next) { + Cal *cal; + + cal = CAL (l->data); + cal_notify_update (cal, uid); + } +} + +/* Notifies a backend's clients that an object was removed */ +static void +notify_remove (CalBackendIMC *cbimc, const char *uid) +{ + IMCPrivate *priv; + GList *l; + + priv = cbimc->priv; + + for (l = priv->clients; l; l = l->next) { + Cal *cal; + + cal = CAL (l->data); + cal_notify_remove (cal, uid); + } +} + +/* Update_object handler for the IMC backend */ +static gboolean +cal_backend_imc_update_object (CalBackend *backend, const char *uid, const char *calobj) +{ + CalBackendIMC *cbimc; + IMCPrivate *priv; + iCalObject *ico, *new_ico; + CalObjFindStatus status; + + cbimc = CAL_BACKEND_IMC (backend); + priv = cbimc->priv; + + g_return_val_if_fail (priv->loaded, FALSE); + + g_return_val_if_fail (uid != NULL, FALSE); + g_return_val_if_fail (calobj != NULL, FALSE); + + /* Pull the object from the string */ + + status = ical_object_find_in_string (uid, calobj, &new_ico); + + if (status != CAL_OBJ_FIND_SUCCESS) + return FALSE; + + /* Update the object */ + + ico = lookup_object (cbimc, uid); + + if (ico) + remove_object (cbimc, ico); + + add_object (cbimc, new_ico); + save (cbimc); + + /* FIXME: do the notification asynchronously */ + + notify_update (cbimc, new_ico->uid); + + return TRUE; +} + +/* Remove_object handler for the IMC backend */ +static gboolean +cal_backend_imc_remove_object (CalBackend *backend, const char *uid) +{ + CalBackendIMC *cbimc; + IMCPrivate *priv; + iCalObject *ico; + + cbimc = CAL_BACKEND_IMC (backend); + priv = cbimc->priv; + + g_return_val_if_fail (priv->loaded, FALSE); + + g_return_val_if_fail (uid != NULL, FALSE); + + ico = lookup_object (cbimc, uid); + if (!ico) + return FALSE; + + remove_object (cbimc, ico); + + priv->dirty = TRUE; + save (cbimc); + + /* FIXME: do the notification asynchronously */ + notify_remove (cbimc, uid); + + return TRUE; +} diff --git a/calendar/pcs/cal-backend-imc.h b/calendar/pcs/cal-backend-imc.h new file mode 100644 index 0000000000..b0fa93611e --- /dev/null +++ b/calendar/pcs/cal-backend-imc.h @@ -0,0 +1,60 @@ +/* Evolution calendar - Internet Mail Consortium formats backend + * + * Copyright (C) 2000 Helix Code, Inc. + * + * Authors: Federico Mena-Quintero + * Seth Alves + * + * 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. + */ + +#ifndef CAL_BACKEND_IMC_H +#define CAL_BACKEND_IMC_H + +#include +#include "cal-backend.h" + +BEGIN_GNOME_DECLS + + + +#define CAL_BACKEND_IMC_TYPE (cal_backend_imc_get_type ()) +#define CAL_BACKEND_IMC(obj) (GTK_CHECK_CAST ((obj), CAL_BACKEND_IMC_TYPE, CalBackendIMC)) +#define CAL_BACKEND_IMC_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), CAL_BACKEND_IMC_TYPE, \ + CalBackendIMCClass)) +#define IS_CAL_BACKEND_IMC(obj) (GTK_CHECK_TYPE ((obj), CAL_BACKEND_IMC_TYPE)) +#define IS_CAL_BACKEND_IMC_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), CAL_BACKEND_IMC_TYPE)) + +typedef struct _CalBackendIMC CalBackendIMC; +typedef struct _CalBackendIMCClass CalBackendIMCClass; + +struct _CalBackendIMC { + CalBackend backend; + + /* Private data */ + gpointer priv; +}; + +struct _CalBackendIMCClass { + CalBackendClass parent_class; +}; + +GtkType cal_backend_imc_get_type (void); + + + +END_GNOME_DECLS + +#endif diff --git a/calendar/pcs/cal-backend.c b/calendar/pcs/cal-backend.c index 3aed2a41fa..27cc3e53ce 100644 --- a/calendar/pcs/cal-backend.c +++ b/calendar/pcs/cal-backend.c @@ -1,5 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* Evolution calendar backend +/* Evolution calendar - generic backend class * * Copyright (C) 2000 Helix Code, Inc. * @@ -25,39 +25,6 @@ #include #include "cal-backend.h" #include "libversit/vcc.h" -#include "icalendar.h" - - - - -/* Private part of the CalBackend structure */ -typedef struct { - /* URI where the calendar data is stored */ - GnomeVFSURI *uri; - - /* format of this calendar (ical or vcal) */ - CalendarFormat format; - - /* List of Cal objects with their listeners */ - GList *clients; - - /* All the iCalObject structures in the calendar, hashed by UID. The - * hash key *is* icalobj->uid; it is not copied, so don't free it when - * you remove an object from the hash table. - */ - GHashTable *object_hash; - - /* All events, TODOs, and journals in the calendar */ - GList *events; - GList *todos; - GList *journals; - - /* Whether a calendar has been loaded */ - guint loaded : 1; - - /* Do we need to sync to permanent storage? */ - gboolean dirty : 1; -} CalBackendPrivate; @@ -68,13 +35,13 @@ enum { }; static void cal_backend_class_init (CalBackendClass *class); -static void cal_backend_init (CalBackend *backend); -static void cal_backend_destroy (GtkObject *object); static GtkObjectClass *parent_class; static guint cal_backend_signals[LAST_SIGNAL]; +#define CLASS(backend) (CAL_BACKEND_CLASS (GTK_OBJECT (backend)->klass)) + /** @@ -97,7 +64,7 @@ cal_backend_get_type (void) sizeof (CalBackend), sizeof (CalBackendClass), (GtkClassInitFunc) cal_backend_class_init, - (GtkObjectInitFunc) cal_backend_init, + (GtkObjectInitFunc) NULL, NULL, /* reserved_1 */ NULL, /* reserved_2 */ (GtkClassInitFunc) NULL @@ -129,390 +96,10 @@ cal_backend_class_init (CalBackendClass *class) GTK_TYPE_NONE, 0); gtk_object_class_add_signals (object_class, cal_backend_signals, LAST_SIGNAL); - - object_class->destroy = cal_backend_destroy; -} - -/* Object initialization function for the calendar backend */ -static void -cal_backend_init (CalBackend *backend) -{ - CalBackendPrivate *priv; - - priv = g_new0 (CalBackendPrivate, 1); - backend->priv = priv; - - /* FIXME can be CAL_VCAL or CAL_ICAL */ - priv->format = CAL_VCAL; -} - -static void -save_to_vcal (CalBackend *backend, char *fname) -{ - FILE *fp; - CalBackendPrivate *priv = backend->priv; - VObject *vcal; - GList *l; - - if (g_file_exists (fname)){ - char *backup_name = g_strconcat (fname, "~", NULL); - - if (g_file_exists (backup_name)){ - unlink (backup_name); - } - rename (fname, backup_name); - g_free (backup_name); - } - - vcal = newVObject (VCCalProp); - addPropValue (vcal, VCProdIdProp, - "-//GNOME//NONSGML GnomeCalendar//EN"); - addPropValue (vcal, VCVersionProp, VERSION); - - for (l = priv->events; l; l = l->next) { - iCalObject *ical = l->data; - VObject *vobject = ical_object_to_vobject (ical); - addVObjectProp (vcal, vobject); - } - for (l = priv->todos; l; l = l->next) { - iCalObject *ical = l->data; - VObject *vobject = ical_object_to_vobject (ical); - addVObjectProp (vcal, vobject); - } - for (l = priv->journals; l; l = l->next) { - iCalObject *ical = l->data; - VObject *vobject = ical_object_to_vobject (ical); - addVObjectProp (vcal, vobject); - } - - fp = fopen(fname,"w"); - if (fp) { - writeVObject(fp, vcal); - fclose(fp); - } - cleanVObject (vcal); - cleanStrTbl (); -} - -/* Saves a calendar */ -static void -save (CalBackend *backend) -{ - char *str_uri; - CalBackendPrivate *priv = backend->priv; - - str_uri = gnome_vfs_uri_to_string (priv->uri, - (GNOME_VFS_URI_HIDE_USER_NAME - | GNOME_VFS_URI_HIDE_PASSWORD - | GNOME_VFS_URI_HIDE_HOST_NAME - | GNOME_VFS_URI_HIDE_HOST_PORT - | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD)); - - if (! priv->dirty){ - return; - } - - switch (priv->format) { - case CAL_VCAL: - save_to_vcal (backend, str_uri); - break; - case CAL_ICAL: - /*icalendar_calendar_save (backend, str_uri);*/ - /* FIX ME */ - break; - default: - /* FIX ME log */ - break; - } - - printf ("cal-backend: '%s' saved\n", str_uri); - - g_free (str_uri); -} - - -/* g_hash_table_foreach() callback to destroy an iCalObject */ -static void -free_ical_object (gpointer key, gpointer value, gpointer data) -{ - iCalObject *ico; - - ico = value; - ical_object_destroy (ico); -} - -/* Destroys a backend's data */ -static void -destroy (CalBackend *backend) -{ - CalBackendPrivate *priv; - - priv = backend->priv; - - if (priv->uri) { - gnome_vfs_uri_unref (priv->uri); - priv->uri = NULL; - } - - g_assert (priv->clients == NULL); - - if (priv->object_hash) { - g_hash_table_foreach (priv->object_hash, free_ical_object, NULL); - g_hash_table_destroy (priv->object_hash); - priv->object_hash = NULL; - } - - g_list_free (priv->events); - g_list_free (priv->todos); - g_list_free (priv->journals); - - priv->events = NULL; - priv->todos = NULL; - priv->journals = NULL; - - priv->loaded = FALSE; -} - -/* Destroy handler for the calendar backend */ -static void -cal_backend_destroy (GtkObject *object) -{ - CalBackend *backend; - CalBackendPrivate *priv; - - g_return_if_fail (object != NULL); - g_return_if_fail (IS_CAL_BACKEND (object)); - - backend = CAL_BACKEND (object); - priv = backend->priv; - - /* - if (priv->loaded) - save (backend); - */ - - destroy (backend); - - g_free (priv); - - if (GTK_OBJECT_CLASS (parent_class)->destroy) - (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); -} - - - -/* iCalObject manipulation functions */ - -/* Looks up an object by its UID in the backend's object hash table */ -static iCalObject * -lookup_object (CalBackend *backend, const char *uid) -{ - CalBackendPrivate *priv; - iCalObject *ico; - - priv = backend->priv; - ico = g_hash_table_lookup (priv->object_hash, uid); - - return ico; -} - -/* Ensures that an iCalObject has a unique identifier. If it doesn't have one, - * it will create one for it. Returns whether an UID was created or not. - */ -static gboolean -ensure_uid (iCalObject *ico) -{ - char *buf; - gulong str_time; - static guint seqno = 0; - - if (ico->uid){ - return FALSE; - } - - str_time = (gulong) time (NULL); - - /* Is this good enough? */ - - buf = g_strdup_printf ("Evolution-Tlacuache-%d-%ld-%u", - (int) getpid(), str_time, seqno++); - ico->uid = buf; - return TRUE; -} - -/* Adds an object to the calendar backend. Does *not* perform notification to - * calendar clients. - */ -static void -add_object (CalBackend *backend, iCalObject *ico) -{ - CalBackendPrivate *priv; - - g_assert (ico != NULL); - priv = backend->priv; - -#if 0 - /* FIXME: gnomecal old code */ - ico->new = 0; -#endif - - if (ensure_uid (ico)) - /* FIXME: mark the calendar as dirty so that we can re-save it - * with the object's new UID. - */ - ; - - g_hash_table_insert (priv->object_hash, ico->uid, ico); - - priv->dirty = TRUE; - - switch (ico->type) { - case ICAL_EVENT: - priv->events = g_list_prepend (priv->events, ico); -#if 0 - /* FIXME: gnomecal old code */ - ical_object_try_alarms (ico); -# ifdef DEBUGGING_MAIL_ALARM - ico->malarm.trigger = 0; - calendar_notify (0, ico); -# endif -#endif - break; - - case ICAL_TODO: - priv->todos = g_list_prepend (priv->todos, ico); - break; - - case ICAL_JOURNAL: - priv->journals = g_list_prepend (priv->journals, ico); - break; - - default: - g_assert_not_reached (); - } - -#if 0 - /* FIXME: gnomecal old code */ - ico->last_mod = time (NULL); -#endif -} - -/* Removes an object from the backend's hash and lists. Does not perform - * notification on the clients. - */ -static void -remove_object (CalBackend *backend, iCalObject *ico) -{ - CalBackendPrivate *priv; - GList **list, *l; - - priv = backend->priv; - - g_assert (ico->uid != NULL); - g_hash_table_remove (priv->object_hash, ico->uid); - - priv->dirty = TRUE; - - switch (ico->type) { - case ICAL_EVENT: - list = &priv->events; - break; - - case ICAL_TODO: - list = &priv->todos; - break; - - case ICAL_JOURNAL: - list = &priv->journals; - break; - - default: - list = NULL; - } - - if (!list){ - return; - } - - l = g_list_find (*list, ico); - g_assert (l != NULL); - - *list = g_list_remove_link (*list, l); - g_list_free_1 (l); - - ical_object_destroy (ico); -} - -/* Load a calendar from a VObject */ -static void -load_from_vobject (CalBackend *backend, VObject *vobject) -{ - CalBackendPrivate *priv; - VObjectIterator i; - - priv = backend->priv; - - g_assert (!priv->loaded); - g_assert (priv->object_hash == NULL); - priv->object_hash = g_hash_table_new (g_str_hash, g_str_equal); - - initPropIterator (&i, vobject); - - while (moreIteration (&i)) { - VObject *this; - iCalObject *ical; - const char *object_name; - - this = nextVObject (&i); - object_name = vObjectName (this); -#if 0 - /* FIXME? What is this used for in gnomecal? */ - if (strcmp (object_name, VCDCreatedProp) == 0) { - cal->created = time_from_isodate (str_val (this)); - continue; - } -#endif - if (strcmp (object_name, VCLocationProp) == 0) - continue; /* FIXME: imlement */ - - if (strcmp (object_name, VCProdIdProp) == 0) - continue; /* FIXME: implement */ - - if (strcmp (object_name, VCVersionProp) == 0) - continue; /* FIXME: implement */ - - if (strcmp (object_name, VCTimeZoneProp) == 0) - continue; /* FIXME: implement */ - - ical = ical_object_create_from_vobject (this, object_name); - - /* FIXME: some broken files may have duplicated UIDs. This is - * Bad(tm). Deal with it by creating new UIDs for them and - * spitting some messages to the console. - */ - - if (ical) - add_object (backend, ical); - } } -/** - * cal_backend_new: - * @void: - * - * Creates a new empty calendar backend. A calendar must then be loaded or - * created before the backend can be used. - * - * Return value: A newly-created calendar backend. - **/ -CalBackend * -cal_backend_new (void) -{ - return CAL_BACKEND (gtk_type_new (CAL_BACKEND_TYPE)); -} - /** * cal_backend_get_uri: * @backend: A calendar backend. @@ -525,54 +112,11 @@ cal_backend_new (void) GnomeVFSURI * cal_backend_get_uri (CalBackend *backend) { - CalBackendPrivate *priv; - g_return_val_if_fail (backend != NULL, NULL); g_return_val_if_fail (IS_CAL_BACKEND (backend), NULL); - priv = backend->priv; - g_return_val_if_fail (priv->loaded, NULL); - g_assert (priv->uri != NULL); - - return priv->uri; -} - -/* Callback used when a Cal is destroyed */ -static void -cal_destroy_cb (GtkObject *object, gpointer data) -{ - Cal *cal; - Cal *lcal; - CalBackend *backend; - CalBackendPrivate *priv; - GList *l; - - cal = CAL (object); - - backend = CAL_BACKEND (data); - priv = backend->priv; - - /* Find the cal in the list of clients */ - - for (l = priv->clients; l; l = l->next) { - lcal = CAL (l->data); - - if (lcal == cal) - break; - } - - g_assert (l != NULL); - - /* Disconnect */ - - priv->clients = g_list_remove_link (priv->clients, l); - g_list_free_1 (l); - - /* When all clients go away, notify the parent factory about it so that - * it may decide whether to kill the backend or not. - */ - if (!priv->clients) - gtk_signal_emit (GTK_OBJECT (backend), cal_backend_signals[LAST_CLIENT_GONE]); + g_assert (CLASS (backend)->get_uri != NULL); + return (* CLASS (backend)->get_uri) (backend); } /** @@ -586,143 +130,13 @@ cal_destroy_cb (GtkObject *object, gpointer data) void cal_backend_add_cal (CalBackend *backend, Cal *cal) { - CalBackendPrivate *priv; - g_return_if_fail (backend != NULL); g_return_if_fail (IS_CAL_BACKEND (backend)); - priv = backend->priv; - g_return_if_fail (priv->loaded); - - g_return_if_fail (cal != NULL); - g_return_if_fail (IS_CAL (cal)); - - /* We do not keep a reference to the Cal since the calendar user agent - * owns it. - */ - - gtk_signal_connect (GTK_OBJECT (cal), "destroy", - GTK_SIGNAL_FUNC (cal_destroy_cb), - backend); - - priv->clients = g_list_prepend (priv->clients, cal); -} - - -static icalcomponent* -icalendar_parse_file (char* fname) -{ - FILE* fp; - icalcomponent* comp = NULL; - gchar* str; - struct stat st; - int n; - - fp = fopen (fname, "r"); - if (!fp) { - g_warning ("Cannot open open calendar file."); - return NULL; - } - - stat (fname, &st); - - str = g_malloc (st.st_size + 2); - - n = fread ((gchar*) str, 1, st.st_size, fp); - if (n != st.st_size) { - g_warning ("Read error."); - } - str[n] = '\0'; - - fclose (fp); - - comp = icalparser_parse_string (str); - g_free (str); - - return comp; -} - - -static void -icalendar_calendar_load (CalBackend * cal, char* fname) -{ - CalBackendPrivate *priv; - icalcomponent *comp; - icalcomponent *subcomp; - iCalObject *ical; - - g_assert (cal); - - priv = cal->priv; - - g_assert (!priv->loaded); - g_assert (priv->object_hash == NULL); - - priv->object_hash = g_hash_table_new (g_str_hash, g_str_equal); - - comp = icalendar_parse_file (fname); - subcomp = icalcomponent_get_first_component (comp, - ICAL_ANY_COMPONENT); - while (subcomp) { - ical = ical_object_create_from_icalcomponent (subcomp); - if (ical->type != ICAL_EVENT && - ical->type != ICAL_TODO && - ical->type != ICAL_JOURNAL) { - g_warning ("Skipping unsupported iCalendar component."); - } else - add_object (cal, ical); - subcomp = icalcomponent_get_next_component (comp, - ICAL_ANY_COMPONENT); - } + g_assert (CLASS (backend)->add_cal != NULL); + return (* CLASS (backend)->add_cal) (backend, cal); } - -/* -ics is to be used to designate a file containing (an arbitrary set of) -calendaring and scheduling information. - -ifb is to be used to designate a file containing free or busy time -information. - -anything else is assumed to be a vcal file. -*/ - -static CalendarFormat -cal_get_type_from_filename (char *str_uri) -{ - int len; - - if (str_uri == NULL) - return CAL_VCAL; - - len = strlen (str_uri); - if (len < 4) - return CAL_VCAL; - - if (str_uri[ len-4 ] == '.' && str_uri[ len-3 ] == 'i' && - str_uri[ len-2 ] == 'c' && str_uri[ len-1 ] == 's') - return CAL_ICAL; - - if (str_uri[ len-4 ] == '.' && str_uri[ len-3 ] == 'i' && - str_uri[ len-2 ] == 'f' && str_uri[ len-1 ] == 'b') - return CAL_ICAL; - - if (str_uri[ len-4 ] == '.' && str_uri[ len-3 ] == 'i' && - str_uri[ len-2 ] == 'c' && str_uri[ len-1 ] == 's') - return CAL_ICAL; - - if (len < 5) - return CAL_VCAL; - - if (str_uri[ len-5 ] == '.' && str_uri[ len-4 ] == 'i' && - str_uri[ len-3 ] == 'c' && str_uri[ len-2 ] == 'a' && - str_uri[ len-1 ] == 'l') - return CAL_ICAL; - - return CAL_VCAL; -} - - /** * cal_backend_load: * @backend: A calendar backend. @@ -736,66 +150,12 @@ cal_get_type_from_filename (char *str_uri) CalBackendLoadStatus cal_backend_load (CalBackend *backend, GnomeVFSURI *uri) { - CalBackendPrivate *priv; - VObject *vobject; - char *str_uri; - g_return_val_if_fail (backend != NULL, CAL_BACKEND_LOAD_ERROR); - g_return_val_if_fail (IS_CAL_BACKEND (backend),CAL_BACKEND_LOAD_ERROR); - - priv = backend->priv; - g_return_val_if_fail (!priv->loaded, CAL_BACKEND_LOAD_ERROR); - + g_return_val_if_fail (IS_CAL_BACKEND (backend), CAL_BACKEND_LOAD_ERROR); g_return_val_if_fail (uri != NULL, CAL_BACKEND_LOAD_ERROR); - /* FIXME: this looks rather bad; maybe we should check for local files - * and fail if they are remote. - */ - - str_uri = gnome_vfs_uri_to_string (uri, - (GNOME_VFS_URI_HIDE_USER_NAME - | GNOME_VFS_URI_HIDE_PASSWORD - | GNOME_VFS_URI_HIDE_HOST_NAME - | GNOME_VFS_URI_HIDE_HOST_PORT - | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD)); - - - /* look at the extension on the filename and decide - if this is a ical or vcal file */ - - priv->format = cal_get_type_from_filename (str_uri); - - /* load */ - - switch (priv->format) { - case CAL_VCAL: - vobject = Parse_MIME_FromFileName (str_uri); - - if (!vobject){ - g_free (str_uri); - return CAL_BACKEND_LOAD_ERROR; - } - - load_from_vobject (backend, vobject); - cleanVObject (vobject); - cleanStrTbl (); - break; - case CAL_ICAL: - icalendar_calendar_load (backend, str_uri); - break; - default: - g_free (str_uri); - return CAL_BACKEND_LOAD_ERROR; - } - - g_free (str_uri); - - gnome_vfs_uri_ref (uri); - - priv->uri = uri; - priv->loaded = TRUE; - - return CAL_BACKEND_LOAD_SUCCESS; + g_assert (CLASS (backend)->load != NULL); + return (* CLASS (backend)->load) (backend, uri); } /** @@ -808,31 +168,12 @@ cal_backend_load (CalBackend *backend, GnomeVFSURI *uri) void cal_backend_create (CalBackend *backend, GnomeVFSURI *uri) { - CalBackendPrivate *priv; - g_return_if_fail (backend != NULL); g_return_if_fail (IS_CAL_BACKEND (backend)); - - priv = backend->priv; - g_return_if_fail (!priv->loaded); - g_return_if_fail (uri != NULL); - /* Create the new calendar information */ - - g_assert (priv->object_hash == NULL); - priv->object_hash = g_hash_table_new (g_str_hash, g_str_equal); - - priv->dirty = TRUE; - - /* Done */ - - gnome_vfs_uri_ref (uri); - - priv->uri = uri; - priv->loaded = TRUE; - - save (backend); + g_assert (CLASS (backend)->create != NULL); + (* CLASS (backend)->create) (backend, uri); } /** @@ -850,66 +191,12 @@ cal_backend_create (CalBackend *backend, GnomeVFSURI *uri) char * cal_backend_get_object (CalBackend *backend, const char *uid) { - CalBackendPrivate *priv; - iCalObject *ico; - char *buf; - g_return_val_if_fail (backend != NULL, NULL); g_return_val_if_fail (IS_CAL_BACKEND (backend), NULL); - - priv = backend->priv; - g_return_val_if_fail (priv->loaded, NULL); - g_return_val_if_fail (uid != NULL, NULL); - g_assert (priv->object_hash != NULL); - - ico = lookup_object (backend, uid); - - if (!ico){ - return NULL; - } - - buf = ical_object_to_string (ico); - - return buf; -} - -struct get_uids_closure { - CalObjType type; - GList *uid_list; -}; - -/* Builds a list of UIDs for objects that match the sought type. Called from - * g_hash_table_foreach(). - */ -static void -build_uids_list (gpointer key, gpointer value, gpointer data) -{ - iCalObject *ico; - struct get_uids_closure *c; - gboolean store; - - ico = value; - c = data; - - store = FALSE; - - /* - if (c->type & CALOBJ_TYPE_ANY) - store = TRUE; - else */ - if (ico->type == ICAL_EVENT) - store = (c->type & CALOBJ_TYPE_EVENT) ? TRUE : FALSE; - else if (ico->type == ICAL_TODO) - store = (c->type & CALOBJ_TYPE_TODO) ? TRUE : FALSE; - else if (ico->type == ICAL_JOURNAL) - store = (c->type & CALOBJ_TYPE_JOURNAL) ? TRUE : FALSE; - else - store = (c->type & CALOBJ_TYPE_OTHER) ? TRUE : FALSE; - - if (store) - c->uid_list = g_list_prepend (c->uid_list, g_strdup (ico->uid)); + g_assert (CLASS (backend)->get_object != NULL); + return (* CLASS (backend)->get_object) (backend, uid); } /** @@ -925,68 +212,11 @@ build_uids_list (gpointer key, gpointer value, gpointer data) GList * cal_backend_get_uids (CalBackend *backend, CalObjType type) { - CalBackendPrivate *priv; - struct get_uids_closure c; - g_return_val_if_fail (backend != NULL, NULL); g_return_val_if_fail (IS_CAL_BACKEND (backend), NULL); - priv = backend->priv; - g_return_val_if_fail (priv->loaded, NULL); - - /* We go through the hash table instead of the lists of particular - * object types so that we can pick up CALOBJ_TYPE_OTHER objects. - */ - - c.type = type; - c.uid_list = NULL; - g_hash_table_foreach (priv->object_hash, build_uids_list, &c); - - return c.uid_list; -} - -struct build_event_list_closure { - CalBackend *backend; - GList *event_list; -}; - -/* Builds a sorted list of event object instances. Used as a callback from - * ical_object_generate_events(). - */ -static int -build_event_list (iCalObject *ico, time_t start, time_t end, void *data) -{ - CalObjInstance *icoi; - struct build_event_list_closure *c; - - c = data; - - icoi = g_new (CalObjInstance, 1); - - g_assert (ico->uid != NULL); - icoi->uid = g_strdup (ico->uid); - icoi->start = start; - icoi->end = end; - - c->event_list = g_list_prepend (c->event_list, icoi); - - return TRUE; -} - -/* Compares two CalObjInstance structures by their start times. Called from - * g_list_sort(). - */ -static gint -compare_instance_func (gconstpointer a, gconstpointer b) -{ - const CalObjInstance *ca, *cb; - time_t diff; - - ca = a; - cb = b; - - diff = ca->start - cb->start; - return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; + g_assert (CLASS (backend)->get_uids != NULL); + return (* CLASS (backend)->get_uids) (backend, type); } /** @@ -1005,67 +235,13 @@ compare_instance_func (gconstpointer a, gconstpointer b) GList * cal_backend_get_events_in_range (CalBackend *backend, time_t start, time_t end) { - CalBackendPrivate *priv; - struct build_event_list_closure c; - GList *l; - g_return_val_if_fail (backend != NULL, NULL); g_return_val_if_fail (IS_CAL_BACKEND (backend), NULL); - - priv = backend->priv; - g_return_val_if_fail (priv->loaded, NULL); - g_return_val_if_fail (start != -1 && end != -1, NULL); g_return_val_if_fail (start <= end, NULL); - c.backend = backend; - c.event_list = NULL; - - for (l = priv->events; l; l = l->next) { - iCalObject *ico; - - ico = l->data; - ical_object_generate_events (ico, start, end, - build_event_list, &c); - } - - c.event_list = g_list_sort (c.event_list, compare_instance_func); - - return c.event_list; -} - -/* Notifies a backend's clients that an object was updated */ -static void -notify_update (CalBackend *backend, const char *uid) -{ - CalBackendPrivate *priv; - GList *l; - - priv = backend->priv; - - for (l = priv->clients; l; l = l->next) { - Cal *cal; - - cal = CAL (l->data); - cal_notify_update (cal, uid); - } -} - -/* Notifies a backend's clients that an object was removed */ -static void -notify_remove (CalBackend *backend, const char *uid) -{ - CalBackendPrivate *priv; - GList *l; - - priv = backend->priv; - - for (l = priv->clients; l; l = l->next) { - Cal *cal; - - cal = CAL (l->data); - cal_notify_remove (cal, uid); - } + g_assert (CLASS (backend)->get_events_in_range != NULL); + return (* CLASS (backend)->get_events_in_range) (backend, start, end); } /** @@ -1081,45 +257,15 @@ notify_remove (CalBackend *backend, const char *uid) * Return value: TRUE on success, FALSE on being passed an invalid object. **/ gboolean -cal_backend_update_object (CalBackend *backend, const char *uid, - const char *calobj) +cal_backend_update_object (CalBackend *backend, const char *uid, const char *calobj) { - CalBackendPrivate *priv; - iCalObject *ico, *new_ico; - CalObjFindStatus status; - g_return_val_if_fail (backend != NULL, FALSE); g_return_val_if_fail (IS_CAL_BACKEND (backend), FALSE); - - priv = backend->priv; - g_return_val_if_fail (priv->loaded, FALSE); - g_return_val_if_fail (uid != NULL, FALSE); g_return_val_if_fail (calobj != NULL, FALSE); - /* Pull the object from the string */ - - status = ical_object_find_in_string (uid, calobj, &new_ico); - - if (status != CAL_OBJ_FIND_SUCCESS){ - return FALSE; - } - - /* Update the object */ - - ico = lookup_object (backend, uid); - - if (ico) - remove_object (backend, ico); - - add_object (backend, new_ico); - save (backend); - - /* FIXME: do the notification asynchronously */ - - notify_update (backend, new_ico->uid); - - return TRUE; + g_assert (CLASS (backend)->update_object != NULL); + return (* CLASS (backend)->update_object) (backend, uid, calobj); } /** @@ -1136,29 +282,26 @@ cal_backend_update_object (CalBackend *backend, const char *uid, gboolean cal_backend_remove_object (CalBackend *backend, const char *uid) { - CalBackendPrivate *priv; - iCalObject *ico; - g_return_val_if_fail (backend != NULL, FALSE); g_return_val_if_fail (IS_CAL_BACKEND (backend), FALSE); - - priv = backend->priv; - g_return_val_if_fail (priv->loaded, FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - ico = lookup_object (backend, uid); - if (!ico){ - return FALSE; - } - - remove_object (backend, ico); - - priv->dirty = TRUE; - save (backend); + g_assert (CLASS (backend)->remove_object != NULL); + return (* CLASS (backend)->remove_object) (backend, uid); +} - /* FIXME: do the notification asynchronously */ - notify_remove (backend, uid); +/** + * cal_backend_last_client_gone: + * @backend: A calendar backend. + * + * Emits the "last_client_gone" signal of a calendar backend. This function is + * to be used only by backend implementations. + **/ +void +cal_backend_last_client_gone (CalBackend *backend) +{ + g_return_if_fail (backend != NULL); + g_return_if_fail (IS_CAL_BACKEND (backend)); - return TRUE; + gtk_signal_emit (GTK_OBJECT (backend), cal_backend_signals[LAST_CLIENT_GONE]); } diff --git a/calendar/pcs/cal-backend.h b/calendar/pcs/cal-backend.h index e5102bf8bc..3c9c76009b 100644 --- a/calendar/pcs/cal-backend.h +++ b/calendar/pcs/cal-backend.h @@ -1,4 +1,4 @@ -/* Evolution calendar backend +/* Evolution calendar - generic backend class * * Copyright (C) 2000 Helix Code, Inc. * @@ -58,21 +58,24 @@ struct _CalBackendClass { /* Notification signals */ void (* last_client_gone) (CalBackend *backend); -}; -typedef enum { - CAL_VCAL, - CAL_ICAL -} CalendarFormat; + /* Virtual methods */ + GnomeVFSURI *(* get_uri) (CalBackend *backend); + void (* add_cal) (CalBackend *backend, Cal *cal); + CalBackendLoadStatus (* load) (CalBackend *backend, GnomeVFSURI *uri); + void (* create) (CalBackend *backend, GnomeVFSURI *uri); + char *(* get_object) (CalBackend *backend, const char *uid); + GList *(* get_uids) (CalBackend *backend, CalObjType type); + GList *(* get_events_in_range) (CalBackend *backend, time_t start, time_t end); + gboolean (* update_object) (CalBackend *backend, const char *uid, const char *calobj); + gboolean (* remove_object) (CalBackend *backend, const char *uid); +}; GtkType cal_backend_get_type (void); -CalBackend *cal_backend_new (void); - GnomeVFSURI *cal_backend_get_uri (CalBackend *backend); void cal_backend_add_cal (CalBackend *backend, Cal *cal); -void cal_backend_remove_cal (CalBackend *backend, Cal *cal); CalBackendLoadStatus cal_backend_load (CalBackend *backend, GnomeVFSURI *uri); @@ -88,6 +91,8 @@ gboolean cal_backend_update_object (CalBackend *backend, const char *uid, const gboolean cal_backend_remove_object (CalBackend *backend, const char *uid); +void cal_backend_last_client_gone (CalBackend *backend); + END_GNOME_DECLS diff --git a/calendar/pcs/cal-factory.c b/calendar/pcs/cal-factory.c index 6d6c28b889..1112245184 100644 --- a/calendar/pcs/cal-factory.c +++ b/calendar/pcs/cal-factory.c @@ -20,6 +20,7 @@ */ #include +#include #include #include "cal.h" #include "cal-backend.h" @@ -30,6 +31,9 @@ /* Private part of the CalFactory structure */ typedef struct { + /* Hash table from URI method strings to GtkType * for backend class types */ + GHashTable *methods; + /* Hash table from GnomeVFSURI structures to CalBackend objects */ GHashTable *backends; } CalFactoryPrivate; @@ -128,9 +132,24 @@ cal_factory_init (CalFactory *factory) priv = g_new0 (CalFactoryPrivate, 1); factory->priv = priv; + priv->methods = g_hash_table_new (g_str_hash, g_str_equal); priv->backends = g_hash_table_new (gnome_vfs_uri_hash, gnome_vfs_uri_hequal); } +/* Frees a method/GtkType * pair from the methods hash table */ +static void +free_method (gpointer key, gpointer value, gpointer data) +{ + char *method; + GtkType *type; + + method = key; + type = value; + + g_free (method); + g_free (type); +} + /* Frees a uri/backend pair from the backends hash table */ static void free_backend (gpointer key, gpointer value, gpointer data) @@ -158,6 +177,10 @@ cal_factory_destroy (GtkObject *object) factory = CAL_FACTORY (object); priv = factory->priv; + g_hash_table_foreach (priv->methods, free_method, NULL); + g_hash_table_destroy (priv->methods); + priv->methods = NULL; + /* Should we assert that there are no more backends? */ g_hash_table_foreach (priv->backends, free_backend, NULL); @@ -165,6 +188,7 @@ cal_factory_destroy (GtkObject *object) priv->backends = NULL; g_free (priv); + factory->priv = NULL; if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); @@ -172,86 +196,57 @@ cal_factory_destroy (GtkObject *object) -/* CORBA servant implementation */ +/* Loading and creating calendars */ -/* CalFactory::load method */ +/* Job data */ +typedef struct { + CalFactory *factory; + char *uri; + Evolution_Calendar_Listener listener; +} LoadCreateJobData; + +/* Queues a load or create request */ static void -CalFactory_load (PortableServer_Servant servant, - const CORBA_char *uri, - Evolution_Calendar_Listener listener, - CORBA_Environment *ev) +queue_load_create_job (CalFactory *factory, const char *uri, Evolution_Calendar_Listener listener, + JobFunc func) { - CalFactory *factory; - CalFactoryPrivate *priv; - CORBA_Environment ev2; + LoadCreateJobData *jd; + CORBA_Environment ev; + Evolution_Calendar_Listener listener_copy; gboolean result; - factory = CAL_FACTORY (bonobo_object_from_servant (servant)); - priv = factory->priv; - - CORBA_exception_init (&ev2); - result = CORBA_Object_is_nil (listener, &ev2); - - if (ev2._major != CORBA_NO_EXCEPTION || result) { - CORBA_exception_set (ev, CORBA_USER_EXCEPTION, - ex_Evolution_Calendar_CalFactory_NilListener, - NULL); - - CORBA_exception_free (&ev2); + CORBA_exception_init (&ev); + result = CORBA_Object_is_nil (listener, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_message ("queue_load_create_job(): could not see if the listener was NIL"); + CORBA_exception_free (&ev); return; } - CORBA_exception_free (&ev2); - - cal_factory_load (factory, uri, listener); -} + CORBA_exception_free (&ev); -/* CalFactory::create method */ -static void -CalFactory_create (PortableServer_Servant servant, - const CORBA_char *uri, - Evolution_Calendar_Listener listener, - CORBA_Environment *ev) -{ - CalFactory *factory; - CalFactoryPrivate *priv; + if (result) { + g_message ("queue_load_create_job(): cannot operate on a NIL listener!"); + return; + } - factory = CAL_FACTORY (bonobo_object_from_servant (servant)); - priv = factory->priv; + listener_copy = CORBA_Object_duplicate (listener, &ev); - cal_factory_create (factory, uri, listener); -} + if (ev._major != CORBA_NO_EXCEPTION) { + g_message ("queue_load_create_job(): could not duplicate the listener"); + CORBA_exception_free (&ev); + return; + } -/** - * cal_factory_get_epv: - * @void: - * - * Creates an EPV for the CalFactory CORBA class. - * - * Return value: A newly-allocated EPV. - **/ -POA_Evolution_Calendar_CalFactory__epv * -cal_factory_get_epv (void) -{ - POA_Evolution_Calendar_CalFactory__epv *epv; + CORBA_exception_free (&ev); - epv = g_new0 (POA_Evolution_Calendar_CalFactory__epv, 1); - epv->load = CalFactory_load; - epv->create = CalFactory_create; + jd = g_new (LoadCreateJobData, 1); + jd->factory = factory; + jd->uri = g_strdup (uri); + jd->listener = listener_copy; - return epv; + job_add (func, jd); } - - -/* Loading and creating calendars */ - -/* Job data */ -typedef struct { - CalFactory *factory; - char *uri; - Evolution_Calendar_Listener listener; -} LoadCreateJobData; - /* Looks up a calendar backend in a factory's hash table of uri->cal */ static CalBackend * lookup_backend (CalFactory *factory, GnomeVFSURI *uri) @@ -316,21 +311,64 @@ add_backend (CalFactory *factory, GnomeVFSURI *uri, CalBackend *backend) factory); } +/* Tries to launch a backend for the method of the specified URI. If there is + * no such method registered in the factory, it sends the listener the + * MethodNotSupported error code. + */ +static CalBackend * +launch_backend_for_uri (CalFactory *factory, GnomeVFSURI *uri, Evolution_Calendar_Listener listener) +{ + CalFactoryPrivate *priv; + char *method; + GtkType *type; + CalBackend *backend; + + priv = factory->priv; + + /* FIXME: add an accessor function to gnome-vfs */ + method = uri->method_string; + + type = g_hash_table_lookup (priv->methods, method); + g_free (method); + + if (!type) { + CORBA_Environment ev; + + CORBA_exception_init (&ev); + Evolution_Calendar_Listener_cal_loaded ( + listener, + Evolution_Calendar_Listener_METHOD_NOT_SUPPORTED, + CORBA_OBJECT_NIL, + &ev); + + if (ev._major != CORBA_NO_EXCEPTION) + g_message ("launch_backend_for_uri(): could not notify the listener"); + + CORBA_exception_free (&ev); + return NULL; + } + + backend = gtk_type_new (*type); + if (!backend) + g_message ("launch_backend_for_uri(): could not launch the backend"); + + return backend; +} + /* Loads a calendar backend and puts it in the factory's backend hash table */ static CalBackend * -load_backend (CalFactory *factory, GnomeVFSURI *uri) +load_backend (CalFactory *factory, GnomeVFSURI *uri, Evolution_Calendar_Listener listener) { CalFactoryPrivate *priv; CalBackend *backend; CalBackendLoadStatus status; + CORBA_Environment ev; priv = factory->priv; - backend = cal_backend_new (); - if (!backend) { - g_message ("load_backend(): could not create the backend"); + backend = launch_backend_for_uri (factory, uri, listener); + if (!backend) return NULL; - } status = cal_backend_load (backend, uri); @@ -341,6 +379,17 @@ load_backend (CalFactory *factory, GnomeVFSURI *uri) case CAL_BACKEND_LOAD_ERROR: gtk_object_unref (GTK_OBJECT (backend)); + + CORBA_exception_init (&ev); + Evolution_Calendar_Listener_cal_loaded (listener, + Evolution_Calendar_Listener_ERROR, + CORBA_OBJECT_NIL, + &ev); + + if (ev._major != CORBA_NO_EXCEPTION) + g_message ("load_backend(): could not notify the listener"); + + CORBA_exception_free (&ev); return NULL; default: @@ -351,20 +400,37 @@ load_backend (CalFactory *factory, GnomeVFSURI *uri) /* Creates a calendar backend and puts it in the factory's backend hash table */ static CalBackend * -create_backend (CalFactory *factory, GnomeVFSURI *uri) +create_backend (CalFactory *factory, GnomeVFSURI *uri, Evolution_Calendar_Listener listener) { CalFactoryPrivate *priv; CalBackend *backend; priv = factory->priv; - backend = cal_backend_new (); - if (!backend) { - g_message ("create_backend(): could not create the backend"); + backend = launch_backend_for_uri (factory, uri, listener); + if (!backend) return NULL; - } cal_backend_create (backend, uri); + + /* FIXME: add error reporting to cal_backend_create() */ +#if 0 + { + CORBA_Environment ev; + + CORBA_exception_init (&ev); + Evolution_Calendar_Listener_cal_loaded (listener, + Evolution_Calendar_Listener_ERROR, + CORBA_OBJECT_NIL, + &ev); + + if (ev._major != CORBA_NO_EXCEPTION) + g_message ("create_fn(): could not notify the listener"); + + CORBA_exception_free (&ev); + } +#endif + add_backend (factory, uri, backend); return backend; @@ -438,21 +504,10 @@ load_fn (gpointer data) backend = lookup_backend (factory, uri); if (!backend) - backend = load_backend (factory, uri); - - if (!backend) { - CORBA_exception_init (&ev); - Evolution_Calendar_Listener_cal_loaded (listener, - Evolution_Calendar_Listener_ERROR, - CORBA_OBJECT_NIL, - &ev); + backend = load_backend (factory, uri, listener); - if (ev._major != CORBA_NO_EXCEPTION) - g_message ("load_fn(): could not notify the listener"); - - CORBA_exception_free (&ev); + if (!backend) goto out; - } add_calendar_client (factory, backend, listener); @@ -510,21 +565,10 @@ create_fn (gpointer data) /* Create the backend */ - backend = create_backend (factory, uri); + backend = create_backend (factory, uri, listener); - if (!backend) { - CORBA_exception_init (&ev); - Evolution_Calendar_Listener_cal_loaded (listener, - Evolution_Calendar_Listener_ERROR, - CORBA_OBJECT_NIL, - &ev); - - if (ev._major != CORBA_NO_EXCEPTION) - g_message ("create_fn(): could not notify the listener"); - - CORBA_exception_free (&ev); + if (!backend) goto out; - } add_calendar_client (factory, backend, listener); @@ -543,6 +587,77 @@ create_fn (gpointer data) +/* CORBA servant implementation */ + +/* CalFactory::load method */ +static void +CalFactory_load (PortableServer_Servant servant, + const CORBA_char *uri, + Evolution_Calendar_Listener listener, + CORBA_Environment *ev) +{ + CalFactory *factory; + CalFactoryPrivate *priv; + CORBA_Environment ev2; + gboolean result; + + factory = CAL_FACTORY (bonobo_object_from_servant (servant)); + priv = factory->priv; + + CORBA_exception_init (&ev2); + result = CORBA_Object_is_nil (listener, &ev2); + + if (ev2._major != CORBA_NO_EXCEPTION || result) { + CORBA_exception_set (ev, CORBA_USER_EXCEPTION, + ex_Evolution_Calendar_CalFactory_NilListener, + NULL); + + CORBA_exception_free (&ev2); + return; + } + CORBA_exception_free (&ev2); + + queue_load_create_job (factory, uri, listener, load_fn); +} + +/* CalFactory::create method */ +static void +CalFactory_create (PortableServer_Servant servant, + const CORBA_char *uri, + Evolution_Calendar_Listener listener, + CORBA_Environment *ev) +{ + CalFactory *factory; + CalFactoryPrivate *priv; + + factory = CAL_FACTORY (bonobo_object_from_servant (servant)); + priv = factory->priv; + + queue_load_create_job (factory, uri, listener, create_fn); +} + +/** + * cal_factory_get_epv: + * @void: + * + * Creates an EPV for the CalFactory CORBA class. + * + * Return value: A newly-allocated EPV. + **/ +POA_Evolution_Calendar_CalFactory__epv * +cal_factory_get_epv (void) +{ + POA_Evolution_Calendar_CalFactory__epv *epv; + + epv = g_new0 (POA_Evolution_Calendar_CalFactory__epv, 1); + epv->load = CalFactory_load; + epv->create = CalFactory_create; + + return epv; +} + + + /** * cal_factory_construct: * @factory: A calendar factory. @@ -634,78 +749,61 @@ cal_factory_new (void) return cal_factory_construct (factory, corba_factory); } -/* Queues a load or create request */ -static void -queue_load_create_job (CalFactory *factory, const char *uri, Evolution_Calendar_Listener listener, - JobFunc func) +/* Returns the lowercase version of a string */ +static char * +str_tolower (const char *s) { - LoadCreateJobData *jd; - CORBA_Environment ev; - Evolution_Calendar_Listener listener_copy; - gboolean result; + char *str; + char *p; - CORBA_exception_init (&ev); - result = CORBA_Object_is_nil (listener, &ev); - if (ev._major != CORBA_NO_EXCEPTION) { - g_message ("queue_load_create_job(): could not see if the listener was NIL"); - CORBA_exception_free (&ev); - return; - } - CORBA_exception_free (&ev); + str = g_strdup (s); + for (p = str; *p; p++) + if (isalpha (*p)) + *p = tolower (*p); - if (result) { - g_message ("queue_load_create_job(): cannot operate on a NIL listener!"); - return; - } - - listener_copy = CORBA_Object_duplicate (listener, &ev); - - if (ev._major != CORBA_NO_EXCEPTION) { - g_message ("queue_load_create_job(): could not duplicate the listener"); - CORBA_exception_free (&ev); - return; - } - - CORBA_exception_free (&ev); - - jd = g_new (LoadCreateJobData, 1); - jd->factory = factory; - jd->uri = g_strdup (uri); - jd->listener = listener_copy; - - job_add (func, jd); -} - -/** - * cal_factory_load: - * @factory: A calendar factory. - * @uri: URI of calendar to load. - * @listener: Listener for notification of the load result. - * - * Initiates a load request in a calendar factory. A calendar will be loaded - * asynchronously and the result code will be reported to the specified - * listener. - **/ -void -cal_factory_load (CalFactory *factory, const char *uri, Evolution_Calendar_Listener listener) -{ - queue_load_create_job (factory, uri, listener, load_fn); + return str; } /** - * cal_factory_create: + * cal_factory_register_method: * @factory: A calendar factory. - * @uri: URI of calendar to create. - * @listener: Listener for notification of the create result. + * @method: Method for the URI, i.e. "http", "file", etc. + * @backend_type: Class type of the backend to create for this @method. * - * Initiates a create request in a calendar factory. A calendar will be created - * asynchronously and the result code will be reported to the specified - * listener. + * Registers the type of a #CalBackend subclass that will be used to handle URIs + * with a particular method. When the factory is asked to load a particular + * URI, it will look in its list of registered methods and create a backend of + * the appropriate type. **/ void -cal_factory_create (CalFactory *factory, const char *uri, Evolution_Calendar_Listener listener) +cal_factory_register_method (CalFactory *factory, const char *method, GtkType backend_type) { - queue_load_create_job (factory, uri, listener, create_fn); + CalFactoryPrivate *priv; + GtkType *type; + char *method_str; + + g_return_if_fail (factory != NULL); + g_return_if_fail (IS_CAL_FACTORY (factory)); + g_return_if_fail (method != NULL); + g_return_if_fail (backend_type != 0); + g_return_if_fail (gtk_type_is_a (backend_type, CAL_BACKEND_TYPE)); + + priv = factory->priv; + + method_str = str_tolower (method); + + type = g_hash_table_lookup (priv->methods, method_str); + if (type) { + g_message ("cal_factory_register_method(): Method `%s' already registered!", + method_str); + g_free (method_str); + return; + } + + type = g_new (GtkType, 1); + *type = backend_type; + + g_hash_table_insert (priv->methods, method_str, type); } /** diff --git a/calendar/pcs/cal-factory.h b/calendar/pcs/cal-factory.h index 03b2cc8dc3..f96ba4ba92 100644 --- a/calendar/pcs/cal-factory.h +++ b/calendar/pcs/cal-factory.h @@ -62,8 +62,7 @@ Evolution_Calendar_CalFactory cal_factory_corba_object_create (BonoboObject *obj CalFactory *cal_factory_new (void); -void cal_factory_load (CalFactory *factory, const char *uri, Evolution_Calendar_Listener listener); -void cal_factory_create (CalFactory *factory, const char *uri, Evolution_Calendar_Listener listener); +void cal_factory_register_method (CalFactory *factory, const char *method, GtkType backend_type); int cal_factory_get_n_backends (CalFactory *factory); diff --git a/wombat/ChangeLog b/wombat/ChangeLog index 4112a83f0a..25ff905961 100644 --- a/wombat/ChangeLog +++ b/wombat/ChangeLog @@ -1,3 +1,8 @@ +2000-05-08 Federico Mena Quintero + + * wombat.c (setup_pcs): Register the "file" method with the IMC + backend. + 2000-05-05 Chris Toshok * .cvsignore: ignore wombat.pure diff --git a/wombat/wombat.c b/wombat/wombat.c index 5d582de6be..4eb5a39878 100644 --- a/wombat/wombat.c +++ b/wombat/wombat.c @@ -16,6 +16,7 @@ #endif #include "calendar/pcs/cal-factory.h" +#include "calendar/pcs/cal-backend-imc.h" /* The and addressbook calendar factories */ @@ -179,6 +180,8 @@ setup_pcs (int argc, char **argv) return; } + cal_factory_register_method (cal_factory, "file", CAL_BACKEND_IMC_TYPE); + object = bonobo_object_corba_objref (BONOBO_OBJECT (cal_factory)); if (! register_pcs (object)) { -- cgit v1.2.3