/* Evolution calendar - iCalendar component object
*
* Copyright (C) 2000 Helix Code, Inc.
*
* Author: Federico Mena-Quintero <federico@helixcode.com>
*
* 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 <config.h>
#include <unistd.h>
#include "cal-component.h"
#include "timeutil.h"
/* Private part of the CalComponent structure */
typedef struct {
/* The icalcomponent we wrap */
icalcomponent *icalcomp;
/* Properties */
icalproperty *uid_prop;
struct {
icalproperty *prop;
icalparameter *altrep_param;
} summary;
struct description {
icalproperty *prop;
icalparameter *altrep_param;
};
GSList *description_list;
} CalComponentPrivate;
static void cal_component_class_init (CalComponentClass *class);
static void cal_component_init (CalComponent *comp);
static void cal_component_destroy (GtkObject *object);
static GtkObjectClass *parent_class;
/**
* cal_component_get_type:
* @void:
*
* Registers the #CalComponent class if necessary, and returns the type ID
* associated to it.
*
* Return value: The type ID of the #CalComponent class.
**/
GtkType
cal_component_get_type (void)
{
static GtkType cal_component_type = 0;
if (!cal_component_type) {
static const GtkTypeInfo cal_component_info = {
"CalComponent",
sizeof (CalComponent),
sizeof (CalComponentClass),
(GtkClassInitFunc) cal_component_class_init,
(GtkObjectInitFunc) cal_component_init,
NULL, /* reserved_1 */
NULL, /* reserved_2 */
(GtkClassInitFunc) NULL
};
cal_component_type = gtk_type_unique (GTK_TYPE_OBJECT, &cal_component_info);
}
return cal_component_type;
}
/* Class initialization function for the calendar component object */
static void
cal_component_class_init (CalComponentClass *class)
{
GtkObjectClass *object_class;
object_class = (GtkObjectClass *) class;
parent_class = gtk_type_class (GTK_TYPE_OBJECT);
object_class->destroy = cal_component_destroy;
}
/* Object initialization function for the calendar component object */
static void
cal_component_init (CalComponent *comp)
{
CalComponentPrivate *priv;
priv = g_new0 (CalComponentPrivate, 1);
comp->priv = priv;
priv->uid_prop = cal_component_gen_uid ();
}
/* Frees the internal icalcomponent only if it does not have a parent. If it
* does, it means we don't own it and we shouldn't free it.
*/
static void
free_icalcomponent (CalComponent *comp)
{
CalComponentPrivate *priv;
priv = comp->priv;
if (!priv->icalcomp)
return;
/* FIXME: remove the mappings! */
if (icalcomponent_get_parent (priv->icalcomp) != NULL)
icalcomponent_free (priv->icalcomp);
priv->icalcomp = NULL;
}
/* Destroy handler for the calendar component object */
static void
cal_component_destroy (GtkObject *object)
{
CalComponent *comp;
CalComponentPrivate *priv;
g_return_if_fail (object != NULL);
g_return_if_fail (IS_CAL_COMPONENT (object));
comp = CAL_COMPONENT (object);
priv = comp->priv;
free_icalcomponent (comp);
g_free (priv);
comp->priv = NULL;
if (GTK_OBJECT_CLASS (parent_class)->destroy)
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}
/**
* cal_component_gen_uid:
* @void:
*
* Generates a unique identifier suitable for calendar components.
*
* Return value: A unique identifier string. Every time this function is called
* a different string is returned.
**/
char *
cal_component_gen_uid (void)
{
static char *hostname;
time_t t = time (NULL);
static int serial;
if (!hostname) {
static char buffer [512];
if ((gethostname (buffer, sizeof (buffer) - 1) == 0) &&
(buffer [0] != 0))
hostname = buffer;
else
hostname = "localhost";
}
return g_strdup_printf (
"%s-%d-%d-%d-%d@%s",
isodate_from_time_t (t),
getpid (),
getgid (),
getppid (),
serial++,
hostname);
}
/**
* cal_component_new:
* @void:
*
* Creates a new empty calendar component object. You should set it from an
* #icalcomponent structure by using cal_component_set_icalcomponent() or with a
* new empty component type by using cal_component_set_new_vtype().
*
* Return value: A newly-created calendar component object.
**/
CalComponent *
cal_component_new (void)
{
return CAL_COMPONENT (gtk_type_new (CAL_COMPONENT_TYPE));
}
/* Scans the summary property */
static void
scan_summary (CalComponent *comp, icalproperty *prop)
{
CalComponentPrivate *priv;
icalparameter *param;
priv = comp->priv;
priv->summary.prop = prop;
for (param = icalproperty_get_first_parameter (prop, ICAL_ANY_PARAMETER);
param;
param = icalproperty_get_next_parameter (prop, ICAL_ANY_PARAMETER)) {
icalparameter_kind kind;
kind = icalparameter_isa (param);
switch (kind) {
case ICAL_ALTREP_PARAMETER:
priv->summary.altrep_param = param;
break;
default:
break;
}
}
}
/* Scans the description property */
static void
scan_description (CalComponent *comp, icalproperty *prop)
{
CalComponentPrivate *priv;
struct description *desc;
icalparameter *param;
priv = comp->priv;
desc = g_new (struct description, 1);
desc->prop = prop;
for (param = icalproperty_get_first_parameter (prop, ICAL_ANY_PARAMETER);
param;
param = icalproperty_get_next_parameter (prop, ICAL_ANY_PARAMETER)) {
icalparameter_kind kind;
kind = icalparameter_isa (param);
switch (kind) {
case ICAL_ALTREP_PARAMETER:
desc->altrep_param = param;
break;
default:
break;
}
}
priv->description_list = g_slist_append (priv->description_list, desc);
}
/* Scans an icalproperty and adds its mapping to the component */
static void
scan_property (CalComponent *comp, icalproperty *prop)
{
CalComponentPrivate *priv;
icalproperty_kind kind;
priv = comp->priv;
kind = icalproperty_isa (prop);
switch (kind) {
case ICAL_DESCRIPTION_PROPERTY:
scan_description (comp, prop);
break;
case ICAL_SUMMARY_PROPERTY:
scan_summary (comp, prop);
break;
case ICAL_UID_PROPERTY:
priv->uid_prop = prop;
break;
default:
break;
}
}
/* Scans an icalcomponent for its properties so that we can provide
* random-access to them.
*/
static void
scan_icalcomponent (CalComponent *comp)
{
CalComponentPrivate *priv;
icalproperty *prop;
priv = comp->priv;
g_assert (priv->icalcomp != NULL);
for (prop = icalcomponent_get_first_property (priv->icalcomp, ICAL_ANY_PROPERTY);
prop;
prop = icalcomponent_get_next_property (priv->icalcomp, ICAL_ANY_PROPERTY))
scan_property (comp, prop);
/* FIXME: parse ALARM subcomponents */
}
/**
* cal_component_set_new_vtype:
* @comp: A calendar component object.
* @type: Type of calendar component to create.
*
* Clears any existing component data from a calendar component object and
* creates a new #icalcomponent of the specified type for it. The only property
* that will be set in the new component will be its unique identifier.
**/
void
cal_component_set_new_vtype (CalComponent *comp, CalComponentVType type)
{
CalComponentPrivate *priv;
icalcomponent *icalcomp;
icalcomponent_kind kind;
char *uid;
icalproperty *prop;
g_return_if_fail (comp != NULL);
g_return_if_fail (IS_CAL_COMPONENT (comp));
priv = comp->priv;
free_icalcomponent (comp);
if (type == CAL_COMPONENT_NO_TYPE)
return;
/* Figure out the kind */
switch (type) {
case CAL_COMPONENT_EVENT:
kind = ICAL_VEVENT_COMPONENT;
break;
case CAL_COMPONENT_TODO:
kind = ICAL_VTODO_COMPONENT;
break;
case CAL_COMPONENT_JOURNAL:
kind = ICAL_VJOURNAL_COMPONENT;
break;
case CAL_COMPONENT_FREEBUSY:
kind = ICAL_VFREEBUSY_COMPONENT;
break;
case CAL_COMPONENT_TIMEZONE:
kind = ICAL_VTIMEZONE_COMPONENT;
break;
default:
g_assert_not_reached ();
kind = ICAL_NO_COMPONENT;
}
/* Create an UID */
icalcomp = icalcomponent_new (kind);
if (!icalcomp) {
g_message ("cal_component_set_new_vtype(): Could not create the icalcomponent!");
return;
}
uid = cal_component_gen_uid ();
prop = icalproperty_new_uid (uid);
g_free (uid);
if (!prop) {
icalcomponent_free (icalcomp);
g_message ("cal_component_set_new_vtype(): Could not create the UID property!");
return;
}
icalcomponent_add_property (icalcomp, prop);
/* Scan the component to build our mapping table */
priv->icalcomp = icalcomp;
scan_icalcomponent (comp);
}
/**
* cal_component_set_icalcomponent:
* @comp: A calendar component object.
* @icalcomp: An #icalcomponent.
*
* Sets the contents of a calendar component object from an #icalcomponent
* structure. If the @comp already had an #icalcomponent set into it, it will
* will be freed automatically if the #icalcomponent does not have a parent
* component itself.
**/
void
cal_component_set_icalcomponent (CalComponent *comp, icalcomponent *icalcomp)
{
CalComponentPrivate *priv;
g_return_if_fail (comp != NULL);
g_return_if_fail (IS_CAL_COMPONENT (comp));
priv = comp->priv;
if (priv->icalcomp == icalcomp)
return;
free_icalcomponent (comp);
priv->icalcomp = icalcomp;
if (priv->icalcomp)
scan_icalcomponent (comp);
}
/**
* cal_component_get_icalcomponent:
* @comp: A calendar component object.
*
* Queries the #icalcomponent structure that a calendar component object is
* wrapping.
*
* Return value: An #icalcomponent structure, or NULL if the @comp has no
* #icalcomponent set to it.
**/
icalcomponent *
cal_component_get_icalcomponent (CalComponent *comp)
{
CalComponentPrivate *priv;
g_return_val_if_fail (comp != NULL, NULL);
g_return_val_if_fail (IS_CAL_COMPONENT (comp), NULL);
priv = comp->priv;
return priv->icalcomp;
}
/**
* cal_component_get_vtype:
* @comp: A calendar component object.
*
* Queries the type of a calendar component object.
*
* Return value: The type of the component, as defined by RFC 2445.
**/
CalComponentVType
cal_component_get_vtype (CalComponent *comp)
{
CalComponentPrivate *priv;
icalcomponent_kind kind;
g_return_val_if_fail (comp != NULL, CAL_COMPONENT_NO_TYPE);
g_return_val_if_fail (IS_CAL_COMPONENT (comp), CAL_COMPONENT_NO_TYPE);
priv = comp->priv;
g_return_val_if_fail (priv->icalcomp != NULL, CAL_COMPONENT_NO_TYPE);
kind = icalcomponent_isa (priv->icalcomp);
switch (kind) {
case ICAL_VEVENT_COMPONENT:
return CAL_COMPONENT_EVENT;
case ICAL_VTODO_COMPONENT:
return CAL_COMPONENT_TODO;
case ICAL_VJOURNAL_COMPONENT:
return CAL_COMPONENT_JOURNAL;
case ICAL_VFREEBUSY_COMPONENT:
return CAL_COMPONENT_FREEBUSY;
case ICAL_VTIMEZONE_COMPONENT:
return CAL_COMPONENT_TIMEZONE;
default:
/* We should have been loaded with a supported type! */
g_assert_not_reached ();
return CAL_COMPONENT_NO_TYPE;
}
}
/**
* cal_component_get_uid:
* @comp: A calendar component object.
*
* Queries the unique identifier of a calendar component object.
*
* Return value: The unique identifier string.
**/
const char *
cal_component_get_uid (CalComponent *comp)
{
CalComponentPrivate *priv;
g_return_val_if_fail (comp != NULL, NULL);
g_return_val_if_fail (IS_CAL_COMPONENT (comp), NULL);
priv = comp->priv;
g_return_val_if_fail (priv->icalcomp != NULL, NULL);
/* This MUST exist, since we ensured that it did */
g_assert (priv->uid_prop != NULL);
return icalproperty_get_uid (priv->uid_prop);
}
/**
* cal_component_set_uid:
* @comp: A calendar component object.
* @uid: Unique identifier.
*
* Sets the unique identifier string of a calendar component object.
**/
void
cal_component_set_uid (CalComponent *comp, const char *uid)
{
CalComponentPrivate *priv;
g_return_if_fail (comp != NULL);
g_return_if_fail (IS_CAL_COMPONENT (comp));
g_return_if_fail (uid != NULL);
priv = comp->priv;
/* This MUST exist, since we ensured that it did */
g_assert (priv->uid_prop != NULL);
icalproperty_set_uid (priv->uid_prop, (char *) uid);
}
/**
* cal_component_get_summary:
* @comp: A calendar component object.
* @summary: Return value for the summary property and its parameters.
*
* Queries the summary of a calendar component object.
**/
void
cal_component_get_summary (CalComponent *comp, CalComponentPropSummary *summary)
{
CalComponentPrivate *priv;
g_return_if_fail (comp != NULL);
g_return_if_fail (IS_CAL_COMPONENT (comp));
g_return_if_fail (summary != NULL);
priv = comp->priv;
if (priv->summary.prop)
summary->value = icalproperty_get_summary (priv->summary.prop);
else
summary->value = NULL;
if (priv->summary.altrep_param)
summary->altrep_param = icalparameter_get_altrep (priv->summary.altrep_param);
else
summary->altrep_param = NULL;
}
/**
* cal_component_set_summary:
* @comp: A calendar component object.
* @summary: Summary property and its parameters.
*
* Sets the summary of a calendar component object.
**/
void
cal_component_set_summary (CalComponent *comp, const CalComponentPropSummary *summary)
{
CalComponentPrivate *priv;
g_return_if_fail (comp != NULL);
g_return_if_fail (IS_CAL_COMPONENT (comp));
priv = comp->priv;
g_return_if_fail (priv->icalcomp != NULL);
if (!summary) {
if (priv->summary.prop) {
icalcomponent_remove_property (priv->icalcomp, priv->summary.prop);
icalproperty_free (priv->summary.prop);
priv->summary.prop = NULL;
priv->summary.altrep_param = NULL;
}
return;
}
g_return_if_fail (summary->value != NULL);
if (priv->summary.prop)
icalproperty_set_summary (priv->summary.prop, (char *) summary->value);
else {
priv->summary.prop = icalproperty_new_summary ((char *) summary->value);
icalcomponent_add_property (priv->icalcomp, priv->summary.prop);
}
if (summary->altrep_param) {
g_assert (priv->summary.prop != NULL);
if (priv->summary.altrep_param)
icalparameter_set_altrep (priv->summary.altrep_param,
(char *) summary->altrep_param);
else {
priv->summary.altrep_param = icalparameter_new_altrep (
(char *) summary->altrep_param);
icalproperty_add_parameter (priv->summary.prop,
priv->summary.altrep_param);
}
} else if (priv->summary.altrep_param) {
#if 0
/* FIXME: this fucking routine will assert(0) since it is not implemented */
icalproperty_remove_parameter (priv->summary.prop, ICAL_ALTREP_PARAMETER);
icalparameter_free (priv->summary.altrep_param);
#endif
priv->summary.altrep_param = NULL;
}
}
/**
* cal_component_get_description_list:
* @comp: A calendar component object.
* @desc_list: Return value for the description properties and their parameters,
* as a list of #CalComponentDescription structures. This should be freed using
* the cal_component_free_description_list() function.
*
* Queries the description of a calendar component object. Journal components
* may have more than one description, and as such this function returns a list
* of #CalComponentDescription structures. All other types of components can
* have at most one description.
**/
void
cal_component_get_description_list (CalComponent *comp, GSList **desc_list)
{
CalComponentPrivate *priv;
GSList *list;
GSList *l;
g_return_if_fail (comp != NULL);
g_return_if_fail (IS_CAL_COMPONENT (comp));
g_return_if_fail (desc_list != NULL);
priv = comp->priv;
list = NULL;
for (l = priv->description_list; l; l = l->next) {
struct description *desc;
CalComponentDescription *d;
desc = l->data;
g_assert (desc->prop != NULL);
d = g_new (CalComponentDescription, 1);
d->value = icalproperty_get_description (desc->prop);
if (desc->altrep_param)
d->altrep_param = icalparameter_get_altrep (desc->altrep_param);
else
d->altrep_param = NULL;
list = g_slist_prepend (list, d);
}
*desc_list = g_slist_reverse (list);
}
/**
* cal_component_set_description_list:
* @comp: A calendar component object.
* @desc_list: List of #CalComponentSummary structures.
*
* Sets the description of a calendar component object. Journal components may
* have more than one description, and as such this function takes in a list of
* #CalComponentDescription structures. All other types of components can have
* at most one description.
**/
void
cal_component_set_description_list (CalComponent *comp, GSList *desc_list)
{
CalComponentPrivate *priv;
GSList *l;
g_return_if_fail (comp != NULL);
g_return_if_fail (IS_CAL_COMPONENT (comp));
priv = comp->priv;
g_return_if_fail (priv->icalcomp != NULL);
/* Remove old descriptions */
for (l = priv->description_list; l; l = l->next) {
struct description *desc;
desc = l->data;
g_assert (desc->prop != NULL);
icalcomponent_remove_property (priv->icalcomp, desc->prop);
g_free (desc);
}
g_slist_free (priv->description_list);
priv->description_list = NULL;
/* Add in new descriptions */
for (l = desc_list; l; l = l->next) {
CalComponentDescription *d;
struct description *desc;
d = l->data;
g_return_if_fail (d->value != NULL);
desc = g_new (struct description, 1);
desc->prop = icalproperty_new_description ((char *) d->value);
icalcomponent_add_property (priv->icalcomp, desc->prop);
if (d->altrep_param) {
desc->altrep_param = icalparameter_new_altrep ((char *) d->altrep_param);
icalproperty_add_parameter (desc->prop, desc->altrep_param);
} else
desc->altrep_param = NULL;
priv->description_list = g_slist_prepend (priv->description_list, desc);
}
priv->description_list = g_slist_reverse (priv->description_list);
}
/**
* cal_component_free_description_list:
* @desc_list: List of #CalComponentDescription structures.
*
* Frees a list of #CalComponentDescription structures as was returned by the
* cal_component_get_description_list() function.
**/
void
cal_component_free_description_list (GSList *desc_list)
{
GSList *l;
for (l = desc_list; l; l = l->next) {
CalComponentDescription *desc;
desc = l->data;
g_return_if_fail (desc != NULL);
g_free (desc);
}
g_slist_free (desc_list);
}