Re: [evolution-patches] Birthday/Anniversary calendar backend



On Thu, 2004-01-08 at 17:13, ERDI Gergo wrote:
> On Thu, 8 Jan 2004, JP Rosevear wrote:
> 
> > On Wed, 2004-01-07 at 10:26, ERDI Gergo wrote:
> > > So here it is, for bounty #127535
> > 
> > It seems to be missing e-cal-backend-contacts.h.

Ok, while testing this I found a few e-d-s bugs :-).  So I fixed those
and discovered a couple of things, 

1. You don't need your whole own server for contacts, just register the
new method in server.c:setup_cals
2. The date displays one day after it should.  
3. If the date for birthday is unset it still displays, but I think this
is a bug in the contact editor (it doesn't respect the None setting).

Finally, you should probably handle migrating from 1.5.0 and 1.5.1,
where groups might not be NULL.  Not that I'm sure I love that check in
our migration code to begin with.

All in all though, pretty cool.

Comments below:

> Index: calendar/backends/contacts/e-cal-backend-contacts.c
> ===================================================================
> RCS file: calendar/backends/contacts/e-cal-backend-contacts.c
> diff -N calendar/backends/contacts/e-cal-backend-contacts.c
> --- /dev/null	1 Jan 1970 00:00:00 -0000
> +++ calendar/backends/contacts/e-cal-backend-contacts.c	8 Jan 2004 22:08:07 -0000
> @@ -0,0 +1,832 @@
> +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
> +/* Evolution calendar - iCalendar file backend
> + *
> + * Copyright (C) 2000-2003 Ximian, Inc.
> + * Copyright (C) 2003 GergÅ? Ã?rdi
> + *
> + * Authors: Federico Mena-Quintero <federico ximian com>
> + *          Rodrigo Moya <rodrigo ximian com>
> + *          GergÅ? Ã?rdi <cactus cactus rulez org>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of version 2 of the GNU General Public
> + * License as published by the Free Software Foundation.
> + *
> + * 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.
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +#include <bonobo/bonobo-i18n.h>
> +
> +#include "e-cal-backend-contacts.h"
> +
> +#include <libedataserver/e-xml-hash-utils.h>
> +#include <libecal/e-cal-recur.h>
> +#include <libecal/e-cal-util.h>
> +#include <libedata-cal/e-cal-backend-util.h>
> +#include <libedata-cal/e-cal-backend-sexp.h>
> +
> +#include <libebook/e-book.h>
> +
> +#include <libedataserver/e-source-list.h>
> +
> +static ECalBackendSyncClass *parent_class;
> +
> +/* Private part of the ECalBackendContacts structure */
> +struct _ECalBackendContactsPrivate {
> +        ESourceList  *addressbook_sources;
> +        
> +        GHashTable   *addressbooks;       /* UID -> BookRecord */
> +        gboolean      addressbook_loaded;
> +
> +        EBookView    *book_view;
> +        GHashTable   *tracked_contacts;   /* UID -> ContactRecord */
> +
> +	icaltimezone *default_zone;
> +};
> +
> +typedef struct _BookRecord {
> +        EBook     *book;
> +        EBookView *book_view;
> +} BookRecord;
> +
> +typedef struct _ContactRecord {
> +        ECalBackendContacts *cbc;
> +        EContact            *contact;
> +        ECalComponent       *comp_birthday, *comp_anniversary;
> +} ContactRecord;
> +
> +static ECalComponent * create_birthday (ECalBackendContacts *cbc, EContact *contact);
> +static ECalComponent * create_anniversary (ECalBackendContacts *cbc, EContact *contact);
> +
> +static void contacts_changed_cb (EBookView *book_view, const GList *contacts, gpointer user_data);
> +static void contacts_added_cb   (EBookView *book_view, const GList *contacts, gpointer user_data);
> +static void contacts_removed_cb (EBookView *book_view, const GList *contact_ids, gpointer user_data);
> +
> +/* BookRecord methods */
> +static BookRecord *
> +book_record_new (ECalBackendContacts *cbc, ESource *source)
> +{
> +        EBook      *book = e_book_new ();
> +        GList      *fields = 0;
> +        EBookQuery *query;
> +        EBookView  *book_view;
> +        BookRecord *br;
> +        
> +        e_book_load_source (book, source, TRUE, NULL);
> +        
> +        /* Create book view */
> +        fields = g_list_append (fields, e_contact_field_name (E_CONTACT_FILE_AS));
> +        fields = g_list_append (fields, e_contact_field_name (E_CONTACT_BIRTH_DATE));
> +        fields = g_list_append (fields, e_contact_field_name (E_CONTACT_ANNIVERSARY));
> +        query = e_book_query_any_field_contains ("");
> +
> +        if (!e_book_get_book_view (book, query, fields, -1, &book_view, NULL)) {
> +                e_book_query_unref (query);
> +                return NULL;
> +        }
> +        e_book_query_unref (query);
> +
> +        g_signal_connect (book_view, "contacts_added", G_CALLBACK (contacts_added_cb), cbc);
> +        g_signal_connect (book_view, "contacts_removed", G_CALLBACK (contacts_removed_cb), cbc);
> +        g_signal_connect (book_view, "contacts_changed", G_CALLBACK (contacts_changed_cb), cbc);
> +
> +        e_book_view_start (book_view);
> +
> +        br = g_new (BookRecord, 1);
> +        br->book = book;
> +        br->book_view = book_view;
> +
> +        return br;
> +}
> +
> +static void
> +book_record_free (BookRecord *br)
> +{
> +        if (!br)
> +                return;
> +        
> +        g_object_unref (br->book_view);
> +        g_object_unref (br->book);
> +
> +        g_free (br);
> +}
> +
> +/* ContactRecord methods */
> +static ContactRecord *
> +contact_record_new (ECalBackendContacts *cbc, EContact *contact)
> +{
> +        ContactRecord *cr = g_new0 (ContactRecord, 1);
> +        char *comp_str;
> +        
> +        cr->cbc = cbc;
> +        cr->contact = contact;
> +        cr->comp_birthday = create_birthday (cbc, contact);
> +        cr->comp_anniversary = create_anniversary (cbc, contact);
> +
> +        comp_str = e_cal_component_get_as_string (cr->comp_birthday);
> +        e_cal_backend_notify_object_created (E_CAL_BACKEND (cbc),
> +                                             comp_str);
> +        g_free (comp_str);
> +        
> +        comp_str = e_cal_component_get_as_string (cr->comp_anniversary);
> +        e_cal_backend_notify_object_created (E_CAL_BACKEND (cbc),
> +                                             comp_str);
> +        g_free (comp_str);
> +
> +        g_object_ref (G_OBJECT (contact));
> +        
> +        return cr;
> +}
> +
> +static void
> +contact_record_free (ContactRecord *cr)
> +{
> +        char *comp_str;
> +        const char *uid;
> +
> +        g_object_unref (G_OBJECT (cr->contact));
> +
> +	/* Remove the birthday event */
> +        comp_str = e_cal_component_get_as_string (cr->comp_birthday);
> +        e_cal_component_get_uid (cr->comp_birthday, &uid);
> +        e_cal_backend_notify_object_removed (E_CAL_BACKEND (cr->cbc), uid, comp_str);
> +        g_free (comp_str);
> +
> +	/* Remove the anniversary event */
> +        comp_str = e_cal_component_get_as_string (cr->comp_anniversary);
> +        e_cal_component_get_uid (cr->comp_anniversary, &uid);
> +        e_cal_backend_notify_object_removed (E_CAL_BACKEND (cr->cbc), uid, comp_str);
> +        g_free (comp_str);
> +        
> +        g_object_unref (G_OBJECT (cr->comp_birthday));
> +        g_object_unref (G_OBJECT (cr->comp_anniversary));
> +        
> +        g_free (cr);
> +}
> +
> +/* ContactRecordCB methods */
> +typedef struct _ContactRecordCB {
> +        ECalBackendContacts *cbc;
> +        ECalBackendSExp     *sexp;        
> +        GList               *result;
> +} ContactRecordCB;
> +
> +static ContactRecordCB *
> +contact_record_cb_new (ECalBackendContacts *cbc, ECalBackendSExp *sexp)
> +{
> +        ContactRecordCB *cb_data = g_new (ContactRecordCB, 1);
> +
> +        cb_data->cbc = cbc;
> +        cb_data->sexp = sexp;
> +        cb_data->result = 0;
> +
> +        return cb_data;
> +}
> +
> +static void
> +contact_record_cb_free (ContactRecordCB *cb_data)
> +{
> +        g_list_foreach (cb_data->result, (GFunc) g_free, 0);
> +        g_list_free (cb_data->result);
> +        
> +        g_free (cb_data);
> +}
> +
> +static void
> +contact_record_cb (gpointer key, gpointer value, gpointer user_data)
> +{
> +        ContactRecordCB *cb_data = user_data;
> +        ContactRecord   *record = value;
> +
> +        if (e_cal_backend_sexp_match_comp (cb_data->sexp, record->comp_birthday, E_CAL_BACKEND (cb_data->cbc))) {
> +                char * comp_str = e_cal_component_get_as_string (record->comp_birthday);
> +                cb_data->result = g_list_append (cb_data->result, comp_str);
> +        }
> +
> +        if (e_cal_backend_sexp_match_comp (cb_data->sexp, record->comp_anniversary, E_CAL_BACKEND (cb_data->cbc))) {
> +                char * comp_str = e_cal_component_get_as_string (record->comp_anniversary);
> +                cb_data->result = g_list_append (cb_data->result, comp_str);
> +        }
> +}
> +
> +/* SourceList callbacks */
> +static void
> +add_source (ECalBackendContacts *cbc, ESource *source)
> +{
> +        BookRecord *br = book_record_new (cbc, source);
> +        const char *uid = e_source_peek_uid (source);
> +
> +        g_hash_table_insert (cbc->priv->addressbooks, g_strdup (uid), br);
> +}
> +
> +static void
> +source_added_cb (ESourceGroup *group, ESource *source, gpointer user_data)
> +{
> +        ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
> +        
> +        g_return_if_fail (cbc);
> +
> +        add_source (cbc, source);
> +}
> +
> +static void
> +source_removed_cb (ESourceGroup *group, ESource *source, gpointer user_data)
> +{
> +        ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
> +        const char          *uid = e_source_peek_uid (source);
> +        
> +        g_return_if_fail (cbc);
> +
> +        g_hash_table_remove (cbc->priv->addressbooks, uid);
> +}
> +
> +static void
> +source_group_added_cb (ESourceList *source_list, ESourceGroup *group, gpointer user_data)
> +{
> +        ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
> +        GSList *i;
> +        
> +        g_return_if_fail (cbc);
> +
> +        /* Load all address books from this group */
> +        for (i = e_source_group_peek_sources (group); i; i = i->next)
> +        {
> +                ESource *source = E_SOURCE (i->data);
> +                add_source (cbc, source);
> +        }
> +
> +        /* Watch for future changes */
> +        g_signal_connect (group, "source_added", G_CALLBACK (source_added_cb), cbc);
> +        g_signal_connect (group, "source_removed", G_CALLBACK (source_removed_cb), cbc);
> +}
> +
> +static void
> +source_group_removed_cb (ESourceList *source_list, ESourceGroup *group, gpointer user_data)
> +{
> +        ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
> +        GSList *i = 0;
> +        
> +        g_return_if_fail (cbc);
> +        
> +        /* Unload all address books from this group */
> +        for (i = e_source_group_peek_sources (group); i; i = i->next)
> +        {
> +                ESource *source = E_SOURCE (i->data);
> +                const char *uid = e_source_peek_uid (source);
> +                
> +                g_hash_table_remove (cbc->priv->addressbooks, uid);
> +        }
> +}
> +
> +/************************************************************************************/
> +
> +static void
> +contacts_changed_cb (EBookView *book_view, const GList *contacts, gpointer user_data)
> +{
> +        ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
> +        const GList *i;
> +
> +        for (i = contacts; i; i = i->next)
> +        {
> +                EContact *contact = E_CONTACT (i->data);
> +                char *uid = e_contact_get_const (contact, E_CONTACT_UID);
> +                
> +                /* If no date is set, remove from list of tracked contacts */                 
> +                if (!e_contact_get (contact, E_CONTACT_BIRTH_DATE) &&
> +                    !e_contact_get (contact, E_CONTACT_ANNIVERSARY)) {
> +                        g_hash_table_remove (cbc->priv->tracked_contacts, uid);
> +                } else {
> +                        ContactRecord *cr = contact_record_new (cbc, contact);
> +                        g_hash_table_insert (cbc->priv->tracked_contacts, g_strdup (uid), cr);
> +                }
> +        }
> +}
> +
> +static void
> +contacts_added_cb (EBookView *book_view, const GList *contacts, gpointer user_data)
> +{
> +        ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
> +        const GList *i;
> +
> +        /* See if any new contacts have BIRTHDAY or ANNIVERSARY fields */
> +        for (i = contacts; i; i = i->next)
> +        {
> +                EContact *contact = E_CONTACT (i->data);
> +                
> +                if (e_contact_get (contact, E_CONTACT_BIRTH_DATE) ||
> +                    e_contact_get (contact, E_CONTACT_ANNIVERSARY)) {
> +                        ContactRecord *cr = contact_record_new (cbc, contact);
> +                        const char    *uid = e_contact_get_const (contact, E_CONTACT_UID);
> +                        
> +                        g_hash_table_insert (cbc->priv->tracked_contacts, g_strdup (uid), cr);
> +                }
> +        }
> +}
> +
> +static void
> +contacts_removed_cb (EBookView *book_view, const GList *contact_ids, gpointer user_data)
> +{
> +        ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
> +        const GList *i;
> +
> +        /* Stop tracking these */
> +        for (i = contact_ids; i; i = i->next)
> +                g_hash_table_remove (cbc->priv->tracked_contacts, i->data);
> +}
> +
> +/************************************************************************************/
> +static time_t
> +cdate_to_time (EContactDate *cdate)
> +{
> +        struct tm time_tm = { 0, 0, 0, 0, 0, 0, 0, 0 };
> +        time_t    time;
> +
> +        time_tm.tm_year = cdate->year - 1900;
> +        time_tm.tm_mon = cdate->month - 1;
> +        time_tm.tm_mday = cdate->day + 1;
> +        time = mktime (&time_tm);
> +
> +        return time;
> +}

This is bad, you should not use mktime directly, you should convert it
to an icaltimetype with and is_date value of 1.

> +/* Contact -> Event creator */
> +static ECalComponent *
> +create_component (ECalBackendContacts *cbc, EContactDate *cdate, char *summary)
> +{
> +        ECalComponent             *cal_comp;
> +	ECalComponentText          comp_summary;
> +        icalcomponent             *ical_comp;
> +        time_t                     start;
> +        struct icaltimetype        itt;
> +        ECalComponentDateTime      dt;
> +	struct icalrecurrencetype  r;
> +        GSList recur_list;
> +
> +        g_return_val_if_fail (E_IS_CAL_BACKEND_CONTACTS (cbc), 0);
> +
> +        if (!cdate)
> +                return NULL;
> +        
> +        ical_comp = icalcomponent_new (ICAL_VEVENT_COMPONENT);
> +
> +        /* Create the event object */
> +        cal_comp = e_cal_component_new ();
> +        e_cal_component_gen_uid ();
> +	e_cal_component_set_icalcomponent (cal_comp, ical_comp);
> +
> +        /* Set all-day event's date from contact data */
> +        start = cdate_to_time (cdate);
> +	itt = icaltime_from_timet (start, TRUE);

You should always try to use the icaltime_from_timet_with_zone version
(but if you do as suggested above i don't think you need this call at
all).

> +        dt.value = &itt;
> +        dt.tzid = 0;
> +        e_cal_component_set_dtstart (cal_comp, &dt);
> +        
> +        /* Create yearly recurrence */
> +        icalrecurrencetype_clear (&r);
> +        r.freq = ICAL_YEARLY_RECURRENCE;
> +	r.interval = 1;
> +        recur_list.data = &r;
> +        recur_list.next = 0;        
> +        e_cal_component_set_rrule_list (cal_comp, &recur_list);
> +
> +        /* Create summary */
> +        comp_summary.value = summary;
> +        comp_summary.altrep = 0;
> +        e_cal_component_set_summary (cal_comp, &comp_summary);
> +	
> +	/* Set category and visibility */
> +	e_cal_component_set_categories (cal_comp, _("Birthday"));
> +	e_cal_component_set_classification (cal_comp, E_CAL_COMPONENT_CLASS_PRIVATE);
> +
> +	/* Birthdays/anniversaries are shown as free time */
> +	e_cal_component_set_transparency (cal_comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
> +	
> +        /* Don't forget to call commit()! */
> +	e_cal_component_commit_sequence (cal_comp);
> +        
> +        return cal_comp;
> +}
> +
> +static ECalComponent *
> +create_birthday (ECalBackendContacts *cbc, EContact *contact)
> +{
> +        EContactDate  *cdate;
> +        ECalComponent *cal_comp;
> +	char          *summary;
> +        const char    *name;
> +        
> +        cdate = e_contact_get (contact, E_CONTACT_BIRTH_DATE);
> +        name = e_contact_get_const (contact, E_CONTACT_FILE_AS);
> +        summary = g_strdup_printf (_("Birthday: %s"), name);
> +        
> +        cal_comp = create_component (cbc, cdate, summary);
> +
> +        g_free (summary);
> +        
> +        return cal_comp;
> +}
> +
> +static ECalComponent *
> +create_anniversary (ECalBackendContacts *cbc, EContact *contact)
> +{
> +        EContactDate  *cdate;
> +        ECalComponent *cal_comp;
> +	char          *summary;
> +        const char    *name;
> +        
> +        cdate = e_contact_get (contact, E_CONTACT_ANNIVERSARY);
> +        name = e_contact_get_const (contact, E_CONTACT_FILE_AS);
> +        summary = g_strdup_printf (_("Anniversary: %s"), name);
> +        
> +        cal_comp = create_component (cbc, cdate, summary);
> +
> +        g_free (summary);
> +
> +        return cal_comp;
> +}
> +
> +/************************************************************************************/
> +/* Calendar backend method implementations */
> +
> +/* First the empty stubs */
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_get_cal_address (ECalBackendSync *backend, EDataCal *cal,
> +					char **address)
> +{
> +	/* WRITE ME */
> +	return GNOME_Evolution_Calendar_Success;
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_get_ldap_attribute (ECalBackendSync *backend, EDataCal *cal,
> +					   char **attribute)
> +{
> +	/* WRITE ME */
> +	return GNOME_Evolution_Calendar_Success;
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_get_alarm_email_address (ECalBackendSync *backend, EDataCal *cal,
> +						char **address)
> +{
> +	/* WRITE ME */
> +	return GNOME_Evolution_Calendar_Success;
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_get_static_capabilities (ECalBackendSync *backend, EDataCal *cal,
> +						char **capabilities)
> +{
> +	/* WRITE ME */
> +	return GNOME_Evolution_Calendar_Success;
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_remove (ECalBackendSync *backend, EDataCal *cal)
> +{
> +	/* WRITE ME */
> +	return GNOME_Evolution_Calendar_Success;
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_get_default_object (ECalBackendSync *backend, EDataCal *cal,
> +					   char **object)
> +{
> +	return GNOME_Evolution_Calendar_UnsupportedMethod;
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_get_object (ECalBackendSync *backend, EDataCal *cal,
> +				   const char *uid, const char *rid,
> +				   char **object)
> +{
> +	/* WRITE ME */
> +	return GNOME_Evolution_Calendar_Success;
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_get_free_busy (ECalBackendSync *backend, EDataCal *cal,
> +				      GList *users, time_t start, time_t end,
> +				      GList **freebusy)
> +{
> +	/* Birthdays/anniversaries don't count as busy time */
> +
> +	icalcomponent *vfb = icalcomponent_new_vfreebusy ();
> +	icaltimezone *utc_zone = icaltimezone_get_utc_timezone ();
> +	char *calobj;
> +
> +#if 0
> +	icalproperty *prop;
> +	icalparameter *param;
> +		
> +	prop = icalproperty_new_organizer (address);
> +	if (prop != NULL && cn != NULL) {
> +		param = icalparameter_new_cn (cn);
> +		icalproperty_add_parameter (prop, param);			
> +	}
> +	if (prop != NULL)
> +		icalcomponent_add_property (vfb, prop);		
> +#endif
> +	
> +	icalcomponent_set_dtstart (vfb, icaltime_from_timet_with_zone (start, FALSE, utc_zone));
> +	icalcomponent_set_dtend (vfb, icaltime_from_timet_with_zone (end, FALSE, utc_zone));
> +
> +	calobj = icalcomponent_as_ical_string (vfb);
> +	*freebusy = g_list_append (NULL, g_strdup (calobj));
> +	icalcomponent_free (vfb);	
> +	
> +	/* WRITE ME */
> +	return GNOME_Evolution_Calendar_Success;
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_get_changes (ECalBackendSync *backend, EDataCal *cal,
> +				    const char *change_id,
> +				    GList **adds, GList **modifies, GList **deletes)
> +{
> +	/* WRITE ME */
> +	return GNOME_Evolution_Calendar_Success;
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_discard_alarm (ECalBackendSync *backend, EDataCal *cal,
> +				      const char *uid, const char *auid)
> +{
> +	/* WRITE ME */
> +	return GNOME_Evolution_Calendar_Success;
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_receive_objects (ECalBackendSync *backend, EDataCal *cal,
> +					const char *calobj)
> +{
> +	return GNOME_Evolution_Calendar_UnsupportedMethod;	
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_send_objects (ECalBackendSync *backend, EDataCal *cal,
> +				     const char *calobj)
> +{
> +	/* TODO: Investigate this */
> +	return GNOME_Evolution_Calendar_Success;
> +}
> +
> +/* Then the real implementations */
> +
> +static CalMode
> +e_cal_backend_contacts_get_mode (ECalBackend *backend)
> +{
> +	return CAL_MODE_LOCAL;	
> +}
> +
> +static void
> +e_cal_backend_contacts_set_mode (ECalBackend *backend, CalMode mode)
> +{
> +	e_cal_backend_notify_mode (backend,
> +				   GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED,
> +				   GNOME_Evolution_Calendar_MODE_LOCAL);
> +	
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_is_read_only (ECalBackendSync *backend, EDataCal *cal,
> +				     gboolean *read_only)
> +{
> +	*read_only = TRUE;
> +	
> +	return GNOME_Evolution_Calendar_Success;
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_open (ECalBackendSync *backend, EDataCal *cal,
> +			     gboolean only_if_exists)
> +{
> +        ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
> +        ECalBackendContactsPrivate *priv = cbc->priv;
> +
> +        GSList *i;
> +
> +        if (priv->addressbook_loaded)
> +                return GNOME_Evolution_Calendar_Success;
> +
> +        /* Create address books for existing sources */
> +        for (i = e_source_list_peek_groups (priv->addressbook_sources); i; i = i->next)
> +        {
> +                ESourceGroup *source_group = E_SOURCE_GROUP (i->data);
> +                        
> +                source_group_added_cb (priv->addressbook_sources, source_group, cbc);
> +        }
> +
> +        /* Listen for source list changes */
> +        g_signal_connect (priv->addressbook_sources, "group_added", G_CALLBACK (source_group_added_cb), cbc);
> +        g_signal_connect (priv->addressbook_sources, "group_removed", G_CALLBACK (source_group_removed_cb), cbc);
> +
> +        priv->addressbook_loaded = TRUE;
> +        return GNOME_Evolution_Calendar_Success;
> +}
> +
> +static gboolean
> +e_cal_backend_contacts_is_loaded (ECalBackend *backend)
> +{
> +        ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
> +        ECalBackendContactsPrivate *priv = cbc->priv;
> +
> +        return priv->addressbook_loaded;
> +}
> +
> +static ECalBackendSyncStatus
> +e_cal_backend_contacts_get_object_list (ECalBackendSync *backend, EDataCal *cal,
> +					const char *sexp_string, GList **objects)
> +{
> +        ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
> +        ECalBackendContactsPrivate *priv = cbc->priv;
> +        ECalBackendSExp *sexp = e_cal_backend_sexp_new (sexp_string);
> +        ContactRecordCB *cb_data;
> +
> +	if (!sexp)
> +		return GNOME_Evolution_Calendar_InvalidQuery;
> +
> +	cb_data = contact_record_cb_new (cbc, sexp);
> +        g_hash_table_foreach (priv->tracked_contacts, contact_record_cb, cb_data);
> +	*objects = cb_data->result;
> +
> +	/* Don't call cb_data_free as that would destroy the results
> +	 * in *objects */
> +	g_free (cb_data);
> +	
> +	return GNOME_Evolution_Calendar_Success;	
> +}
> +
> +static void
> +e_cal_backend_contacts_start_query (ECalBackend *backend, EDataCalView *query)
> +{
> +        ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
> +        ECalBackendContactsPrivate *priv = cbc->priv;
> +        ECalBackendSExp *sexp;
> +        ContactRecordCB *cb_data;
> +        
> +        sexp = e_data_cal_view_get_object_sexp (query);
> +	if (!sexp) {
> +		e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_InvalidQuery);
> +		return;
> +	}
> +
> +        cb_data = contact_record_cb_new (cbc, sexp);
> +        
> +        g_hash_table_foreach (priv->tracked_contacts, contact_record_cb, cb_data);
> +        e_data_cal_view_notify_objects_added (query, cb_data->result);
> +
> +        contact_record_cb_free (cb_data);
> +        
> +	e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_Success);
> +}
> +
> +static icaltimezone *
> +e_cal_backend_contacts_internal_get_default_timezone (ECalBackend *backend)
> +{
> +        ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
> +
> +	return cbc->priv->default_zone;
> +}
> +
> +static icaltimezone *
> +e_cal_backend_contacts_internal_get_timezone (ECalBackend *backend, const char *tzid)
> +{
> +        ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
> +
> +        return cbc->priv->default_zone;
> +}
> +
> +/***********************************************************************************
> + */
> +
> +/* Finalize handler for the contacts backend */
> +static void
> +e_cal_backend_contacts_finalize (GObject *object)
> +{
> +	ECalBackendContacts *cbc;
> +	ECalBackendContactsPrivate *priv;
> +
> +	g_return_if_fail (object != NULL);
> +	g_return_if_fail (E_IS_CAL_BACKEND_CONTACTS (object));
> +
> +	cbc = E_CAL_BACKEND_CONTACTS (object);
> +	priv = cbc->priv;
> +
> +        g_hash_table_destroy (priv->tracked_contacts);
> +        
> +	g_free (priv);
> +	cbc->priv = NULL;
> +
> +	if (G_OBJECT_CLASS (parent_class)->finalize)
> +		(* G_OBJECT_CLASS (parent_class)->finalize) (object);
> +}
> +
> +/* Object initialization function for the contacts backend */
> +static void
> +e_cal_backend_contacts_init (ECalBackendContacts *cbc, ECalBackendContactsClass *class)
> +{
> +	ECalBackendContactsPrivate *priv;
> +        GConfClient *gconf_client;
> +
> +	priv = g_new0 (ECalBackendContactsPrivate, 1);
> +
> +        gconf_client = gconf_client_get_default ();
> +        priv->addressbook_sources = e_source_list_new_for_gconf (
> +                gconf_client, "/apps/evolution/addressbook/sources");
> +
> +        priv->addressbooks = g_hash_table_new_full (g_str_hash, g_str_equal,
> +                                                    g_free, (GDestroyNotify)book_record_free);
> +        priv->tracked_contacts = g_hash_table_new_full (g_str_hash, g_str_equal,
> +                                                        g_free, (GDestroyNotify)contact_record_free);
> +        
> +	priv->default_zone = icaltimezone_get_utc_timezone ();
> +        
> +	cbc->priv = priv;
> +}
> +
> +/* Class initialization function for the contacts backend */
> +static void
> +e_cal_backend_contacts_class_init (ECalBackendContactsClass *class)
> +{
> +	GObjectClass *object_class;
> +	ECalBackendClass *backend_class;
> +	ECalBackendSyncClass *sync_class;
> +
> +	object_class = (GObjectClass *) class;
> +	backend_class = (ECalBackendClass *) class;
> +	sync_class = (ECalBackendSyncClass *) class;
> +
> +	parent_class = (ECalBackendSyncClass *) g_type_class_peek_parent (class);
> +
> +	object_class->finalize = e_cal_backend_contacts_finalize;
> +
> +	sync_class->is_read_only_sync = e_cal_backend_contacts_is_read_only;
> +	sync_class->get_cal_address_sync = e_cal_backend_contacts_get_cal_address;
> + 	sync_class->get_alarm_email_address_sync = e_cal_backend_contacts_get_alarm_email_address;
> + 	sync_class->get_ldap_attribute_sync = e_cal_backend_contacts_get_ldap_attribute;
> + 	sync_class->get_static_capabilities_sync = e_cal_backend_contacts_get_static_capabilities;
> +	sync_class->open_sync = e_cal_backend_contacts_open;
> +	sync_class->remove_sync = e_cal_backend_contacts_remove;
> +	sync_class->discard_alarm_sync = e_cal_backend_contacts_discard_alarm;
> +	sync_class->receive_objects_sync = e_cal_backend_contacts_receive_objects;
> +	sync_class->send_objects_sync = e_cal_backend_contacts_send_objects;
> + 	sync_class->get_default_object_sync = e_cal_backend_contacts_get_default_object;
> +	sync_class->get_object_sync = e_cal_backend_contacts_get_object;
> +	sync_class->get_object_list_sync = e_cal_backend_contacts_get_object_list;
> +	sync_class->get_freebusy_sync = e_cal_backend_contacts_get_free_busy;
> +	sync_class->get_changes_sync = e_cal_backend_contacts_get_changes;
> +	backend_class->is_loaded = e_cal_backend_contacts_is_loaded;
> +	backend_class->start_query = e_cal_backend_contacts_start_query;
> +	backend_class->get_mode = e_cal_backend_contacts_get_mode;
> +	backend_class->set_mode = e_cal_backend_contacts_set_mode;
> +
> +	backend_class->internal_get_default_timezone = e_cal_backend_contacts_internal_get_default_timezone;
> +	backend_class->internal_get_timezone = e_cal_backend_contacts_internal_get_timezone;
> +}
> +
> +
> +/**
> + * e_cal_backend_contacts_get_type:
> + * @void: 
> + * 
> + * Registers the #ECalBackendContacts class if necessary, and returns
> + * the type ID associated to it.
> + * 
> + * Return value: The type ID of the #ECalBackendContacts class.
> + **/
> +GType
> +e_cal_backend_contacts_get_type (void)
> +{
> +	static GType e_cal_backend_contacts_type = 0;
> +
> +        fprintf (stderr, "e_cal_backend_contacts_get_type ()\n");
> +        
> +	if (!e_cal_backend_contacts_type) {
> +		static GTypeInfo info = {
> +                        sizeof (ECalBackendContactsClass),
> +                        (GBaseInitFunc) NULL,
> +                        (GBaseFinalizeFunc) NULL,
> +                        (GClassInitFunc) e_cal_backend_contacts_class_init,
> +                        NULL, NULL,
> +                        sizeof (ECalBackendContacts),
> +                        0,
> +                        (GInstanceInitFunc) e_cal_backend_contacts_init
> +                };
> +		e_cal_backend_contacts_type = g_type_register_static (E_TYPE_CAL_BACKEND_SYNC,
> +                                                                     "ECalBackendContacts", &info, 0);
> +	}
> +
> +	return e_cal_backend_contacts_type;
> +}
> Index: calendar/backends/contacts/e-cal-backend-contacts.h
> ===================================================================
> RCS file: calendar/backends/contacts/e-cal-backend-contacts.h
> diff -N calendar/backends/contacts/e-cal-backend-contacts.h
> --- /dev/null	1 Jan 1970 00:00:00 -0000
> +++ calendar/backends/contacts/e-cal-backend-contacts.h	8 Jan 2004 22:08:07 -0000
> @@ -0,0 +1,62 @@
> +/* Evolution calendar - iCalendar file backend
> + *
> + * Copyright (C) 2000 Ximian, Inc.
> + * Copyright (C) 2003 GergÅ? Ã?rdi
> + *
> + * Authors: Federico Mena-Quintero <federico ximian com>,
> + *          GergÅ? Ã?rdi <cactus cactus rulez org>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of version 2 of the GNU General Public
> + * License as published by the Free Software Foundation.
> + *
> + * 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 E_CAL_BACKEND_CONTACTS_H
> +#define E_CAL_BACKEND_CONTACTS_H
> +
> +#include <libedata-cal/e-cal-backend-sync.h>
> +
> +G_BEGIN_DECLS
> +
> +
> +
> +#define E_TYPE_CAL_BACKEND_CONTACTS            (e_cal_backend_contacts_get_type ())
> +#define E_CAL_BACKEND_CONTACTS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CAL_BACKEND_CONTACTS,		\
> +					  ECalBackendContacts))
> +#define E_CAL_BACKEND_CONTACTS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_CAL_BACKEND_CONTACTS,	\
> +					  ECalBackendContactsClass))
> +#define E_IS_CAL_BACKEND_CONTACTS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_CAL_BACKEND_CONTACTS))
> +#define E_IS_CAL_BACKEND_CONTACTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_CAL_BACKEND_CONTACTS))
> +
> +typedef struct _ECalBackendContacts ECalBackendContacts;
> +typedef struct _ECalBackendContactsClass ECalBackendContactsClass;
> +
> +typedef struct _ECalBackendContactsPrivate ECalBackendContactsPrivate;
> +
> +struct _ECalBackendContacts {
> +	ECalBackendSync backend;
> +
> +	/* Private data */
> +	ECalBackendContactsPrivate *priv;
> +};
> +
> +struct _ECalBackendContactsClass {
> +	ECalBackendSyncClass parent_class;
> +};
> +
> +GType e_cal_backend_contacts_get_type (void);
> +
> +
> +
> +G_END_DECLS
> +
> +#endif
> Index: calendar/libecal/e-cal.c
> ===================================================================
> RCS file: /cvs/gnome/evolution-data-server/calendar/libecal/e-cal.c,v
> retrieving revision 1.23
> diff -u -r1.23 e-cal.c
> --- calendar/libecal/e-cal.c	7 Jan 2004 14:21:33 -0000	1.23
> +++ calendar/libecal/e-cal.c	8 Jan 2004 22:08:18 -0000
> @@ -2155,7 +2155,10 @@
>  	g_cond_wait (our_op->cond, our_op->mutex);
>  
>  	status = our_op->status;
> -	*icalcomp = icalparser_parse_string (our_op->string);
> +        if (status)
> +                *icalcomp = NULL;
> +        else
> +                *icalcomp = icalparser_parse_string (our_op->string);
>  	g_free (our_op->string);
>  
>  	e_calendar_remove_op (ecal, our_op);
> Index: libedataserver/e-source-group.c
> ===================================================================
> RCS file: /cvs/gnome/evolution-data-server/libedataserver/e-source-group.c,v
> retrieving revision 1.3
> diff -u -r1.3 e-source-group.c
> --- libedataserver/e-source-group.c	14 Nov 2003 22:02:10 -0000	1.3
> +++ libedataserver/e-source-group.c	8 Jan 2004 22:08:38 -0000
> @@ -41,6 +41,7 @@
>  	GSList *sources;
>  
>  	gboolean ignore_source_changed;
> +	gboolean readonly;
>  };
>  
> 
> @@ -221,6 +222,7 @@
>  	xmlChar *uid;
>  	xmlChar *name;
>  	xmlChar *base_uri;
> +	xmlChar *readonly_str;
>  	ESourceGroup *new = NULL;
>  
>  	g_return_val_if_fail (doc != NULL, NULL);
> @@ -232,6 +234,7 @@
>  	uid = xmlGetProp (root, "uid");
>  	name = xmlGetProp (root, "name");
>  	base_uri = xmlGetProp (root, "base_uri");
> +	readonly_str = xmlGetProp (root, "readonly");
>  
>  	if (uid == NULL || name == NULL || base_uri == NULL)
>  		goto done;
> @@ -241,17 +244,21 @@
>  
>  	e_source_group_set_name (new, name);
>  	e_source_group_set_base_uri (new, base_uri);
> -
> +	
>  	for (p = root->children; p != NULL; p = p->next) {
>  		ESource *new_source = e_source_new_from_xml_node (p);
>  		e_source_group_add_source (new, new_source, -1);
>  	}
>  
> +	e_source_group_set_readonly (new, readonly_str && !strcmp (readonly_str, "yes"));
> +
>   done:
>  	if (name != NULL)
>  		xmlFree (name);
>  	if (base_uri != NULL)
>  		xmlFree (base_uri);
> +	if (readonly_str != NULL)
> +		xmlFree (readonly_str);
>  	return new;
>  }
>  
> @@ -283,7 +290,8 @@
>  	GHashTable *new_sources_hash;
>  	GSList *new_sources_list = NULL;
>  	xmlNodePtr root, nodep;
> -	xmlChar *name, *base_uri;
> +	xmlChar *name, *base_uri, *readonly_str;
> +	gboolean readonly;
>  	gboolean changed = FALSE;
>  	GSList *p, *q;
>  
> @@ -320,6 +328,14 @@
>  	}
>  	xmlFree (base_uri);
>  
> +	readonly_str = xmlGetProp (root, "readonly");
> +	readonly = readonly_str && !strcmp (readonly_str, "yes");
> +	if (readonly != group->priv->readonly) {
> +		group->priv->readonly = readonly;
> +		changed = TRUE;
> +	}
> +	xmlFree (readonly_str);
> +	
>  	new_sources_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
>  
>  	for (nodep = root->children; nodep != NULL; nodep = nodep->next) {
> @@ -431,6 +447,9 @@
>  {
>  	g_return_if_fail (E_IS_SOURCE_GROUP (group));
>  	g_return_if_fail (name != NULL);
> +
> +	if (group->priv->readonly)
> +		return;
>  	
>  	if (group->priv->name == name)
>  		return;
> @@ -447,6 +466,9 @@
>  	g_return_if_fail (E_IS_SOURCE_GROUP (group));
>  	g_return_if_fail (base_uri != NULL);
>  	
> +	if (group->priv->readonly)
> +		return;
> +	
>  	if (group->priv->base_uri == base_uri)
>  		return;
>  
> @@ -456,6 +478,25 @@
>  	g_signal_emit (group, signals[CHANGED], 0);
>  }
>  
> +void e_source_group_set_readonly (ESourceGroup *group,
> +				  gboolean      readonly)
> +{
> +	GSList *i;
> +	
> +	g_return_if_fail (E_IS_SOURCE_GROUP (group));
> +	
> +	if (group->priv->readonly)
> +		return;
> +	
> +	if (group->priv->readonly == readonly)
> +		return;
> +
> +	group->priv->readonly = readonly;
> +	for (i = group->priv->sources; i != NULL; i = i->next)
> +		e_source_set_readonly (E_SOURCE (i->data), readonly);	
> +
> +	g_signal_emit (group, signals[CHANGED], 0);
> +}
>  
>  const char *
>  e_source_group_peek_uid (ESourceGroup *group)
> @@ -481,6 +522,13 @@
>  	return group->priv->base_uri;
>  }
>  
> +gboolean
> +e_source_group_get_readonly (ESourceGroup *group)
> +{
> +	g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
> +
> +	return group->priv->readonly;
> +}
>  
>  GSList *
>  e_source_group_peek_sources (ESourceGroup *group)
> @@ -525,10 +573,14 @@
>  {
>  	g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
>  
> +	if (group->priv->readonly)
> +		return FALSE;
> +	
>  	if (e_source_group_peek_source_by_uid (group, e_source_peek_uid (source)) != NULL)
>  		return FALSE;
>  
>  	e_source_set_group (source, group);
> +	e_source_set_readonly (source, group->priv->readonly);
>  	g_object_ref (source);
>  
>  	g_signal_connect (source, "changed", G_CALLBACK (source_changed_callback), group);
> @@ -549,6 +601,9 @@
>  	g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
>  	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
>  
> +	if (group->priv->readonly)
> +		return FALSE;
> +
>  	for (p = group->priv->sources; p != NULL; p = p->next) {
>  		if (E_SOURCE (p->data) == source) {
>  			group->priv->sources = g_slist_remove_link (group->priv->sources, p);
> @@ -570,6 +625,9 @@
>  	g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
>  	g_return_val_if_fail (uid != NULL, FALSE);
>  
> +	if (group->priv->readonly)
> +		return FALSE;
> +	
>  	for (p = group->priv->sources; p != NULL; p = p->next) {
>  		ESource *source = E_SOURCE (p->data);
>  
> @@ -601,7 +659,8 @@
>  	xmlSetProp (root, "uid", e_source_group_peek_uid (group));
>  	xmlSetProp (root, "name", e_source_group_peek_name (group));
>  	xmlSetProp (root, "base_uri", e_source_group_peek_base_uri (group));
> -
> +	xmlSetProp (root, "readonly", group->priv->readonly ? "yes" : "no");
> +	
>  	xmlDocSetRootElement (doc, root);
>  
>  	for (p = group->priv->sources; p != NULL; p = p->next)
> Index: libedataserver/e-source-group.h
> ===================================================================
> RCS file: /cvs/gnome/evolution-data-server/libedataserver/e-source-group.h,v
> retrieving revision 1.2
> diff -u -r1.2 e-source-group.h
> --- libedataserver/e-source-group.h	21 Oct 2003 18:51:19 -0000	1.2
> +++ libedataserver/e-source-group.h	8 Jan 2004 22:08:38 -0000
> @@ -78,9 +78,13 @@
>  void  e_source_group_set_base_uri  (ESourceGroup *group,
>  				    const char   *base_uri);
>  
> +void e_source_group_set_readonly (ESourceGroup *group,
> +				  gboolean      readonly);
> +
>  const char *e_source_group_peek_uid       (ESourceGroup *group);
>  const char *e_source_group_peek_name      (ESourceGroup *group);
>  const char *e_source_group_peek_base_uri  (ESourceGroup *group);
> +gboolean    e_source_group_get_readonly   (ESourceGroup *group);
>  
>  GSList  *e_source_group_peek_sources        (ESourceGroup *group);
>  ESource *e_source_group_peek_source_by_uid  (ESourceGroup *group,
> Index: libedataserver/e-source.c
> ===================================================================
> RCS file: /cvs/gnome/evolution-data-server/libedataserver/e-source.c,v
> retrieving revision 1.10
> diff -u -r1.10 e-source.c
> --- libedataserver/e-source.c	18 Dec 2003 02:15:56 -0000	1.10
> +++ libedataserver/e-source.c	8 Jan 2004 22:08:40 -0000
> @@ -48,6 +48,8 @@
>  	char *relative_uri;
>  	char *absolute_uri;
>  
> +	gboolean readonly;
> +	
>  	gboolean has_color;
>  	guint32 color;
>  
> @@ -414,6 +416,9 @@
>  	g_return_if_fail (E_IS_SOURCE (source));
>  	g_return_if_fail (group == NULL || E_IS_SOURCE_GROUP (group));
>  
> +	if (source->priv->readonly)
> +		return;
> +	
>  	if (source->priv->group == group)
>  		return;
>  
> @@ -439,6 +444,9 @@
>  {
>  	g_return_if_fail (E_IS_SOURCE (source));
>  
> +	if (source->priv->readonly)
> +		return;
> +	
>  	if (source->priv->name == name)
>  		return;
>  
> @@ -454,6 +462,9 @@
>  {
>  	g_return_if_fail (E_IS_SOURCE (source));
>  
> +	if (source->priv->readonly)
> +		return;
> +	
>  	if (source->priv->relative_uri == relative_uri)
>  		return;
>  
> @@ -464,11 +475,29 @@
>  }
>  
>  void
> +e_source_set_readonly (ESource  *source,
> +		       gboolean  readonly)
> +{
> +	g_return_if_fail (E_IS_SOURCE (source));
> +
> +	if (source->priv->readonly == readonly)
> +		return;
> +
> +	source->priv->readonly = readonly;
> +
> +	g_signal_emit (source, signals[CHANGED], 0);
> +	
> +}
> +
> +void
>  e_source_set_color (ESource *source,
>  		    guint32 color)
>  {
>  	g_return_if_fail (E_IS_SOURCE (source));
>  
> +	if (source->priv->readonly)
> +		return;
> +	
>  	if (source->priv->has_color && source->priv->color == color)
>  		return;
>  
> @@ -523,6 +552,15 @@
>  	return source->priv->relative_uri;
>  }
>  
> +gboolean
> +e_source_get_readonly (ESource *source)
> +{
> +	g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
> +
> +	return source->priv->readonly;
> +}
> +
> +
>  /**
>   * e_source_get_color:
>   * @source: An ESource
> @@ -596,7 +634,7 @@
>  	xmlSetProp (node, "uid", e_source_peek_uid (source));
>  	xmlSetProp (node, "name", e_source_peek_name (source));
>  	xmlSetProp (node, "relative_uri", e_source_peek_relative_uri (source));
> -
> +	
>  	has_color = e_source_get_color (source, &color);
>  	if (has_color) {
>  		char *color_string = g_strdup_printf (COLOR_FORMAT_STRING, color);
> Index: libedataserver/e-source.h
> ===================================================================
> RCS file: /cvs/gnome/evolution-data-server/libedataserver/e-source.h,v
> retrieving revision 1.6
> diff -u -r1.6 e-source.h
> --- libedataserver/e-source.h	17 Dec 2003 00:43:56 -0000	1.6
> +++ libedataserver/e-source.h	8 Jan 2004 22:08:40 -0000
> @@ -76,6 +76,8 @@
>  				  const char   *name);
>  void  e_source_set_relative_uri  (ESource      *source,
>  				  const char   *relative_uri);
> +void  e_source_set_readonly      (ESource      *source,
> +				  gboolean      readonly);
>  void  e_source_set_color         (ESource      *source,
>  				  guint32       color);
>  void  e_source_unset_color       (ESource      *source);
> @@ -84,6 +86,7 @@
>  const char   *e_source_peek_uid           (ESource *source);
>  const char   *e_source_peek_name          (ESource *source);
>  const char   *e_source_peek_relative_uri  (ESource *source);
> +gboolean      e_source_get_readonly       (ESource *source);
>  gboolean      e_source_get_color          (ESource *source,
>  					   guint32 *color_return);

I'm still not positive on this, I think users should be able to get rid
of it if they want.  I guess the alternative is to have a setting in the
prefs dialog.

-JP
-- 
JP Rosevear <jpr ximian com>
Ximian, Inc.




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]