aboutsummaryrefslogtreecommitdiffstats
path: root/calendar/cal-util/cal-recur.c
diff options
context:
space:
mode:
authorDamon Chaplin <damon@helixcode.com>2000-05-20 18:38:19 +0800
committerDamon Chaplin <damon@src.gnome.org>2000-05-20 18:38:19 +0800
commit4eb47f9a4696877218bf05d53c52f829d2afcdd5 (patch)
tree81a7adf9cc66caa99031a1908d7317d616ea2b64 /calendar/cal-util/cal-recur.c
parentccd4d84234d933ccf05769e71a3372e149d707d1 (diff)
downloadgsoc2013-evolution-4eb47f9a4696877218bf05d53c52f829d2afcdd5.tar
gsoc2013-evolution-4eb47f9a4696877218bf05d53c52f829d2afcdd5.tar.gz
gsoc2013-evolution-4eb47f9a4696877218bf05d53c52f829d2afcdd5.tar.bz2
gsoc2013-evolution-4eb47f9a4696877218bf05d53c52f829d2afcdd5.tar.lz
gsoc2013-evolution-4eb47f9a4696877218bf05d53c52f829d2afcdd5.tar.xz
gsoc2013-evolution-4eb47f9a4696877218bf05d53c52f829d2afcdd5.tar.zst
gsoc2013-evolution-4eb47f9a4696877218bf05d53c52f829d2afcdd5.zip
new files to implement iCalendar recurrence rules. These are only part
2000-05-20 Damon Chaplin <damon@helixcode.com> * cal-util/cal-recur.[hc]: new files to implement iCalendar recurrence rules. These are only part finished, but people may like to check that the architecture seems OK. 2000-05-17 Damon Chaplin <damon@helixcode.com> * gui/e-day-view.c (e_day_view_on_delete_occurrence): * gui/e-week-view.c (e_week_view_on_delete_occurrence): use a copy of the iCalObject so we detect the change in the "update_event" callback. Maybe we should just update the view ourselves and then we wouldn't need to detect any change in the callback. * cal-util/calobj.c (ical_object_reset_recurrence): new function to get rid of any recurrence rules. Used when we 'unrecur' an event. * gui/e-day-view.c (e_day_view_key_press): don't add a new event if it won't fit, or we end up adding a new event for each key press. (e_day_view_update_event_label): don't update it if it doesn't have an EText item (i.e. it isn't visible). * gui/e-day-view-time-item.c: allow selection of times using this column. svn path=/trunk/; revision=3144
Diffstat (limited to 'calendar/cal-util/cal-recur.c')
-rw-r--r--calendar/cal-util/cal-recur.c1131
1 files changed, 1131 insertions, 0 deletions
diff --git a/calendar/cal-util/cal-recur.c b/calendar/cal-util/cal-recur.c
new file mode 100644
index 0000000000..711ef5e64d
--- /dev/null
+++ b/calendar/cal-util/cal-recur.c
@@ -0,0 +1,1131 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Evolution calendar recurrence rule functions
+ *
+ * Copyright (C) 2000 Helix Code, Inc.
+ *
+ * Author: Damon Chaplin <damon@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 <stdlib.h>
+#include <string.h>
+#include <cal-util/cal-recur.h>
+#include <cal-util/timeutil.h>
+
+
+/*
+ * Introduction to The Recurrence Generation Functions:
+ *
+ * Note: This is pretty complicated. See the iCalendar spec (RFC 2445) for
+ * the specification of the recurrence rules and lots of examples
+ * (sections 4.3.10 & 4.8.5). We also want to support the older
+ * vCalendar spec, though this should be easy since it is basically a
+ * subset of iCalendar.
+ *
+ * o An iCalendar event can have any number of recurrence rules specifying
+ * occurrences of the event, as well as dates & times of specific
+ * occurrences. It can also have any number of recurrence rules and
+ * specific dates & times specifying exceptions to the occurrences.
+ * So we first merge all the occurrences generated, eliminating any
+ * duplicates, then we generate all the exceptions and remove these to
+ * form the final set of occurrences.
+ *
+ * o There are 7 frequencies of occurrences: YEARLY, MONTHLY, WEEKLY, DAILY,
+ * HOURLY, MINUTELY & SECONDLY. The 'interval' property specifies the
+ * multiples of the frequency between each 'set' of occurrences. So for
+ * a YEARLY frequency with an interval of 3, we generate a set of occurrences
+ * for every 3rd year. We use complete years here - any generated
+ * occurrences that occur before the event's start (or after its end)
+ * are just discarded.
+ *
+ * o There are 8 frequency modifiers: BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY,
+ * BYDAY, BYHOUR & BYSECOND. These can either add extra occurrences or
+ * filter out occurrences. For example 'FREQ=YEARLY;BYMONTH=1,2' produces
+ * 2 occurrences for each year rather than the default 1. And
+ * 'FREQ=DAILY; BYMONTH=1' filters out all occurrences except those in Jan.
+ * If the modifier works on periods which are less than the recurrence
+ * frequency, then extra occurrences are added, else occurrences are
+ * filtered. So we have 2 functions for each modifier - one to expand events
+ * and the other to filter. We use a table of functions for each frequency
+ * which points to the appropriate function to use for each modifier.
+ *
+ * o Any number of frequency modifiers can be used in a recurrence rule
+ * (though BYWEEKNO can only be used in a YEARLY rule). They are applied in
+ * the order given above.
+ *
+ * o After the set of occurrences for the frequency interval are generated,
+ * the BYSETPOS property is used to select which of the occurrences are
+ * finally output. If BYSETPOS is not specified then all the occurrences are
+ * output.
+ */
+
+
+#define CAL_OBJ_NUM_FILTERS 8
+
+typedef gboolean (*CalObjFindStartFn) (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime);
+typedef gboolean (*CalObjFindNextFn) (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end);
+typedef GArray* (*CalObjFilterFn) (Recurrence *recur,
+ GArray *occs);
+
+typedef struct _CalObjRecurVTable CalObjRecurVTable;
+struct _CalObjRecurVTable {
+ CalObjFindStartFn find_start_position;
+ CalObjFindNextFn find_next_position;
+ CalObjFilterFn filters[CAL_OBJ_NUM_FILTERS];
+};
+
+
+static CalObjRecurVTable* cal_obj_get_vtable (Recurrence *recur);
+static void cal_obj_sort_occurrences (GArray *occs);
+static gint cal_obj_time_compare_func (const void *arg1,
+ const void *arg2);
+static void cal_obj_remove_duplicates (GArray *occs);
+static GArray* cal_obj_bysetpos_filter (GArray *occs);
+
+
+static gboolean cal_obj_yearly_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime);
+static gboolean cal_obj_yearly_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end);
+
+static gboolean cal_obj_monthly_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime);
+static gboolean cal_obj_monthly_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end);
+
+static gboolean cal_obj_weekly_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime);
+static gboolean cal_obj_weekly_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end);
+
+static gboolean cal_obj_daily_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime);
+static gboolean cal_obj_daily_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end);
+
+static gboolean cal_obj_hourly_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime);
+static gboolean cal_obj_hourly_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end);
+
+static gboolean cal_obj_minutely_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime);
+static gboolean cal_obj_minutely_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end);
+
+static gboolean cal_obj_secondly_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime);
+static gboolean cal_obj_secondly_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end);
+
+static GArray* cal_obj_bymonth_expand (Recurrence *recur,
+ GArray *occs);
+static GArray* cal_obj_bymonth_filter (Recurrence *recur,
+ GArray *occs);
+static GArray* cal_obj_byweekno_expand (Recurrence *recur,
+ GArray *occs);
+#if 0
+/* This isn't used at present. */
+static GArray* cal_obj_byweekno_filter (Recurrence *recur,
+ GArray *occs);
+#endif
+static GArray* cal_obj_byyearday_expand (Recurrence *recur,
+ GArray *occs);
+static GArray* cal_obj_byyearday_filter (Recurrence *recur,
+ GArray *occs);
+static GArray* cal_obj_bymonthday_expand (Recurrence *recur,
+ GArray *occs);
+static GArray* cal_obj_bymonthday_filter (Recurrence *recur,
+ GArray *occs);
+static GArray* cal_obj_byday_expand (Recurrence *recur,
+ GArray *occs);
+static GArray* cal_obj_byday_filter (Recurrence *recur,
+ GArray *occs);
+static GArray* cal_obj_byhour_expand (Recurrence *recur,
+ GArray *occs);
+static GArray* cal_obj_byhour_filter (Recurrence *recur,
+ GArray *occs);
+static GArray* cal_obj_byminute_expand (Recurrence *recur,
+ GArray *occs);
+static GArray* cal_obj_byminute_filter (Recurrence *recur,
+ GArray *occs);
+static GArray* cal_obj_bysecond_expand (Recurrence *recur,
+ GArray *occs);
+static GArray* cal_obj_bysecond_filter (Recurrence *recur,
+ GArray *occs);
+
+static void cal_obj_time_add_days (CalObjTime *cotime,
+ gint days);
+
+
+CalObjRecurVTable cal_obj_yearly_vtable = {
+ cal_obj_yearly_find_start_position,
+ cal_obj_yearly_find_next_position,
+ {
+ cal_obj_bymonth_expand,
+ cal_obj_byweekno_expand,
+ cal_obj_byyearday_expand,
+ cal_obj_bymonthday_expand,
+ cal_obj_byday_expand,
+ cal_obj_byhour_expand,
+ cal_obj_byminute_expand,
+ cal_obj_bysecond_expand
+ },
+};
+
+CalObjRecurVTable cal_obj_monthly_vtable = {
+ cal_obj_monthly_find_start_position,
+ cal_obj_monthly_find_next_position,
+ {
+ cal_obj_bymonth_filter,
+ NULL,
+ cal_obj_byyearday_filter,
+ cal_obj_bymonthday_expand,
+ cal_obj_byday_expand,
+ cal_obj_byhour_expand,
+ cal_obj_byminute_expand,
+ cal_obj_bysecond_expand
+ },
+};
+
+CalObjRecurVTable cal_obj_weekly_vtable = {
+ cal_obj_weekly_find_start_position,
+ cal_obj_weekly_find_next_position,
+ {
+ cal_obj_bymonth_filter,
+ NULL,
+ cal_obj_byyearday_filter,
+ cal_obj_bymonthday_filter,
+ cal_obj_byday_expand,
+ cal_obj_byhour_expand,
+ cal_obj_byminute_expand,
+ cal_obj_bysecond_expand
+ },
+};
+
+CalObjRecurVTable cal_obj_daily_vtable = {
+ cal_obj_daily_find_start_position,
+ cal_obj_daily_find_next_position,
+ {
+ cal_obj_bymonth_filter,
+ NULL,
+ cal_obj_byyearday_filter,
+ cal_obj_bymonthday_filter,
+ cal_obj_byday_filter,
+ cal_obj_byhour_expand,
+ cal_obj_byminute_expand,
+ cal_obj_bysecond_expand
+ },
+};
+
+CalObjRecurVTable cal_obj_hourly_vtable = {
+ cal_obj_hourly_find_start_position,
+ cal_obj_hourly_find_next_position,
+ {
+ cal_obj_bymonth_filter,
+ NULL,
+ cal_obj_byyearday_filter,
+ cal_obj_bymonthday_filter,
+ cal_obj_byday_filter,
+ cal_obj_byhour_filter,
+ cal_obj_byminute_expand,
+ cal_obj_bysecond_expand
+ },
+};
+
+CalObjRecurVTable cal_obj_minutely_vtable = {
+ cal_obj_minutely_find_start_position,
+ cal_obj_minutely_find_next_position,
+ {
+ cal_obj_bymonth_filter,
+ NULL,
+ cal_obj_byyearday_filter,
+ cal_obj_bymonthday_filter,
+ cal_obj_byday_filter,
+ cal_obj_byhour_filter,
+ cal_obj_byminute_filter,
+ cal_obj_bysecond_expand
+ },
+};
+
+CalObjRecurVTable cal_obj_secondly_vtable = {
+ cal_obj_secondly_find_start_position,
+ cal_obj_secondly_find_next_position,
+ {
+ cal_obj_bymonth_filter,
+ NULL,
+ cal_obj_byyearday_filter,
+ cal_obj_bymonthday_filter,
+ cal_obj_byday_filter,
+ cal_obj_byhour_filter,
+ cal_obj_byminute_filter,
+ cal_obj_bysecond_filter
+ },
+};
+
+
+
+
+/* Returns an unsorted array of time_t's resulting from expanding the
+ recurrence within the given interval. Each iCalendar event can have any
+ number of recurrence rules specifying occurrences of the event, as well as
+ any number of recurrence rules specifying exceptions. */
+GArray*
+cal_obj_expand_recurrence (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end)
+{
+ CalObjRecurVTable *vtable;
+ CalObjTime occ;
+ GArray *all_occs, *occs;
+ gint filter;
+
+ vtable = cal_obj_get_vtable (recur);
+
+ /* This is the resulting array of CalObjTime elements. */
+ all_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+ /* Get the first period based on the frequency and the interval that
+ intersects the interval between start and end. */
+ if ((*vtable->find_start_position) (event_start, event_end, recur,
+ interval_start, interval_end,
+ &occ))
+ return all_occs;
+
+ /* Loop until the event ends or we go past the end of the required
+ interval. */
+ for (;;) {
+
+ /* We start with just the one time in the set. */
+ occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+ g_array_append_val (occs, occ);
+
+ /* Generate the set of occurrences for this period. */
+ for (filter = 0; filter < CAL_OBJ_NUM_FILTERS; filter++) {
+ if (vtable->filters[filter])
+ occs = (*vtable->filters[filter]) (recur,
+ occs);
+ }
+
+ /* Sort the occurrences and remove duplicates. */
+ cal_obj_sort_occurrences (occs);
+ cal_obj_remove_duplicates (occs);
+
+ /* Apply the BYSETPOS property. */
+ occs = cal_obj_bysetpos_filter (occs);
+
+ /* Add the occurrences onto the main array. */
+ g_array_append_vals (all_occs, occs->data, occs->len);
+
+ /* Skip to the next period, or exit the loop if finished. */
+ if ((*vtable->find_next_position) (&occ, event_end, recur,
+ interval_end))
+ break;
+ }
+
+ return all_occs;
+}
+
+
+/* Returns the function table corresponding to the recurrence frequency. */
+static CalObjRecurVTable*
+cal_obj_get_vtable (Recurrence *recur)
+{
+ switch (recur->type) {
+ case RECUR_YEARLY:
+ return &cal_obj_yearly_vtable;
+ case RECUR_MONTHLY:
+ return &cal_obj_monthly_vtable;
+ case RECUR_WEEKLY:
+ return &cal_obj_weekly_vtable;
+ case RECUR_DAILY:
+ return &cal_obj_daily_vtable;
+ case RECUR_HOURLY:
+ return &cal_obj_hourly_vtable;
+ case RECUR_MINUTELY:
+ return &cal_obj_minutely_vtable;
+ case RECUR_SECONDLY:
+ return &cal_obj_secondly_vtable;
+ }
+ return NULL;
+}
+
+
+static void
+cal_obj_sort_occurrences (GArray *occs)
+{
+ qsort (occs->data, occs->len, sizeof (CalObjTime),
+ cal_obj_time_compare_func);
+}
+
+
+static gint
+cal_obj_time_compare_func (const void *arg1,
+ const void *arg2)
+{
+ CalObjTime *cotime1, *cotime2;
+
+ cotime1 = (CalObjTime*) arg1;
+ cotime2 = (CalObjTime*) arg2;
+
+ if (cotime1->year < cotime2->year)
+ return -1;
+ if (cotime1->year > cotime2->year)
+ return 1;
+
+ if (cotime1->month < cotime2->month)
+ return -1;
+ if (cotime1->month > cotime2->month)
+ return 1;
+
+ if (cotime1->day < cotime2->day)
+ return -1;
+ if (cotime1->day > cotime2->day)
+ return 1;
+
+ if (cotime1->hour < cotime2->hour)
+ return -1;
+ if (cotime1->hour > cotime2->hour)
+ return 1;
+
+ if (cotime1->minute < cotime2->minute)
+ return -1;
+ if (cotime1->minute > cotime2->minute)
+ return 1;
+
+ if (cotime1->second < cotime2->second)
+ return -1;
+ if (cotime1->second > cotime2->second)
+ return 1;
+
+ return 0;
+}
+
+
+static void
+cal_obj_remove_duplicates (GArray *occs)
+{
+ CalObjTime *occ, *prev_occ = NULL;
+ gint len, i, j = 0;
+
+ len = occs->len;
+ for (i = 0; i < len; i++) {
+ occ = &g_array_index (occs, CalObjTime, i);
+
+ if (!prev_occ
+ || cal_obj_time_compare_func (occ, prev_occ) != 0) {
+ if (i != j)
+ g_array_index (occs, CalObjTime, j)
+ = g_array_index (occs, CalObjTime, i);
+ j++;
+ }
+
+ prev_occ = occ;
+ }
+
+ g_array_set_size (occs, j);
+}
+
+
+static GArray*
+cal_obj_bysetpos_filter (GArray *occs)
+{
+
+ return occs;
+}
+
+
+
+
+static gboolean
+cal_obj_yearly_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime)
+{
+
+
+ return FALSE;
+}
+
+
+static gboolean
+cal_obj_yearly_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end)
+{
+ /* NOTE: The day may now be invalid, e.g. 29th Feb.
+ Make sure we remove these eventually. */
+ cotime->year += recur->interval;
+
+ if (cotime->year > event_end->year
+ || cotime->year > interval_end->year)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+
+static gboolean
+cal_obj_monthly_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime)
+{
+
+
+ return FALSE;
+}
+
+
+static gboolean
+cal_obj_monthly_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end)
+{
+ cotime->month += recur->interval;
+ cotime->year += cotime->month / 12;
+ cotime->month %= 12;
+
+ if (cotime->year > event_end->year
+ || cotime->year > interval_end->year
+ || (cotime->year == event_end->year
+ && cotime->month > event_end->month)
+ || (cotime->year == interval_end->year
+ && cotime->month > interval_end->month))
+ return TRUE;
+
+ return FALSE;
+}
+
+
+
+static gboolean
+cal_obj_weekly_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime)
+{
+
+
+ return FALSE;
+}
+
+
+static gboolean
+cal_obj_weekly_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end)
+{
+ cal_obj_time_add_days (cotime, recur->interval);
+
+
+
+ return FALSE;
+}
+
+
+static gboolean
+cal_obj_daily_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime)
+{
+
+
+ return FALSE;
+}
+
+
+static gboolean
+cal_obj_daily_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end)
+{
+
+ cal_obj_time_add_days (cotime, recur->interval);
+
+
+ return FALSE;
+}
+
+
+static gboolean
+cal_obj_hourly_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime)
+{
+
+
+ return FALSE;
+}
+
+
+static gboolean
+cal_obj_hourly_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end)
+{
+
+
+ return FALSE;
+}
+
+
+static gboolean
+cal_obj_minutely_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime)
+{
+
+
+ return FALSE;
+}
+
+
+static gboolean
+cal_obj_minutely_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end)
+{
+
+
+ return FALSE;
+}
+
+
+static gboolean
+cal_obj_secondly_find_start_position (CalObjTime *event_start,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_start,
+ CalObjTime *interval_end,
+ CalObjTime *cotime)
+{
+
+
+ return FALSE;
+}
+
+
+static gboolean
+cal_obj_secondly_find_next_position (CalObjTime *cotime,
+ CalObjTime *event_end,
+ Recurrence *recur,
+ CalObjTime *interval_end)
+{
+
+
+ return FALSE;
+}
+
+
+
+
+
+/* If the BYMONTH rule is specified it expands each occurrence in occs, by
+ using each of the months in the bymonth list. */
+static GArray*
+cal_obj_bymonth_expand (Recurrence *recur,
+ GArray *occs)
+{
+ GArray *new_occs;
+ CalObjTime *occ;
+ GList *elem;
+ gint len, i;
+
+ /* If BYMONTH has not been specified, or the array is empty, just
+ return the array. */
+ if (!recur->bymonth || occs->len == 0)
+ return occs;
+
+ new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+ len = occs->len;
+ for (i = 0; i < len; i++) {
+ occ = &g_array_index (occs, CalObjTime, i);
+
+ elem = recur->bymonth;
+ while (elem) {
+ /* NOTE: The day may now be invalid, e.g. 31st Feb.
+ Make sure we remove these eventually. */
+ occ->month = GPOINTER_TO_INT (elem->data);
+ g_array_append_vals (new_occs, occ, 1);
+ elem = elem->next;
+ }
+ }
+
+ g_array_free (occs, TRUE);
+
+ return new_occs;
+}
+
+
+/* If the BYMONTH rule is specified it filters out all occurrences in occs
+ which do not match one of the months in the bymonth list. */
+static GArray*
+cal_obj_bymonth_filter (Recurrence *recur,
+ GArray *occs)
+{
+ GArray *new_occs;
+ CalObjTime *occ;
+ guint8 months[12];
+ gint mon, len, i;
+ GList *elem;
+
+ /* If BYMONTH has not been specified, or the array is empty, just
+ return the array. */
+ elem = recur->bymonth;
+ if (!elem || occs->len == 0)
+ return occs;
+
+ /* Create an array of months from bymonths for fast lookup. */
+ memset (&months, 0, sizeof (months));
+ while (elem) {
+ mon = GPOINTER_TO_INT (elem->data);
+ months[mon] = 1;
+ elem = elem->next;
+ }
+
+ new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+ len = occs->len;
+ for (i = 0; i < len; i++) {
+ occ = &g_array_index (occs, CalObjTime, i);
+ if (months[occ->month])
+ g_array_append_vals (new_occs, occ, 1);
+ }
+
+ g_array_free (occs, TRUE);
+
+ return new_occs;
+}
+
+
+
+static GArray*
+cal_obj_byweekno_expand (Recurrence *recur,
+ GArray *occs)
+{
+
+ return occs;
+}
+
+
+#if 0
+/* This isn't used at present. */
+static GArray*
+cal_obj_byweekno_filter (Recurrence *recur,
+ GArray *occs)
+{
+
+ return occs;
+}
+#endif
+
+
+static GArray*
+cal_obj_byyearday_expand (Recurrence *recur,
+ GArray *occs)
+{
+
+ return occs;
+}
+
+
+static GArray*
+cal_obj_byyearday_filter (Recurrence *recur,
+ GArray *occs)
+{
+
+ return occs;
+}
+
+
+
+static GArray*
+cal_obj_bymonthday_expand (Recurrence *recur,
+ GArray *occs)
+{
+
+ return occs;
+}
+
+
+static GArray*
+cal_obj_bymonthday_filter (Recurrence *recur,
+ GArray *occs)
+{
+
+ return occs;
+}
+
+
+
+static GArray*
+cal_obj_byday_expand (Recurrence *recur,
+ GArray *occs)
+{
+
+ return occs;
+}
+
+
+static GArray*
+cal_obj_byday_filter (Recurrence *recur,
+ GArray *occs)
+{
+
+ return occs;
+}
+
+
+
+/* If the BYHOUR rule is specified it expands each occurrence in occs, by
+ using each of the hours in the byhour list. */
+static GArray*
+cal_obj_byhour_expand (Recurrence *recur,
+ GArray *occs)
+{
+ GArray *new_occs;
+ CalObjTime *occ;
+ GList *elem;
+ gint len, i;
+
+ /* If BYHOUR has not been specified, or the array is empty, just
+ return the array. */
+ if (!recur->byhour || occs->len == 0)
+ return occs;
+
+ new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+ len = occs->len;
+ for (i = 0; i < len; i++) {
+ occ = &g_array_index (occs, CalObjTime, i);
+
+ elem = recur->byhour;
+ while (elem) {
+ occ->hour = GPOINTER_TO_INT (elem->data);
+ g_array_append_vals (new_occs, occ, 1);
+ elem = elem->next;
+ }
+ }
+
+ g_array_free (occs, TRUE);
+
+ return new_occs;
+}
+
+
+/* If the BYHOUR rule is specified it filters out all occurrences in occs
+ which do not match one of the hours in the byhour list. */
+static GArray*
+cal_obj_byhour_filter (Recurrence *recur,
+ GArray *occs)
+{
+ GArray *new_occs;
+ CalObjTime *occ;
+ guint8 hours[24];
+ gint hour, len, i;
+ GList *elem;
+
+ /* If BYHOURUTE has not been specified, or the array is empty, just
+ return the array. */
+ elem = recur->byhour;
+ if (!elem || occs->len == 0)
+ return occs;
+
+ /* Create an array of hours from byhour for fast lookup. */
+ memset (&hours, 0, sizeof (hours));
+ while (elem) {
+ hour = GPOINTER_TO_INT (elem->data);
+ hours[hour] = 1;
+ elem = elem->next;
+ }
+
+ new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+ len = occs->len;
+ for (i = 0; i < len; i++) {
+ occ = &g_array_index (occs, CalObjTime, i);
+ if (hours[occ->hour])
+ g_array_append_vals (new_occs, occ, 1);
+ }
+
+ g_array_free (occs, TRUE);
+
+ return new_occs;
+}
+
+
+
+/* If the BYMINUTE rule is specified it expands each occurrence in occs, by
+ using each of the minutes in the byminute list. */
+static GArray*
+cal_obj_byminute_expand (Recurrence *recur,
+ GArray *occs)
+{
+ GArray *new_occs;
+ CalObjTime *occ;
+ GList *elem;
+ gint len, i;
+
+ /* If BYMINUTE has not been specified, or the array is empty, just
+ return the array. */
+ if (!recur->byminute || occs->len == 0)
+ return occs;
+
+ new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+ len = occs->len;
+ for (i = 0; i < len; i++) {
+ occ = &g_array_index (occs, CalObjTime, i);
+
+ elem = recur->byminute;
+ while (elem) {
+ occ->minute = GPOINTER_TO_INT (elem->data);
+ g_array_append_vals (new_occs, occ, 1);
+ elem = elem->next;
+ }
+ }
+
+ g_array_free (occs, TRUE);
+
+ return new_occs;
+}
+
+
+/* If the BYMINUTE rule is specified it filters out all occurrences in occs
+ which do not match one of the minutes in the byminute list. */
+static GArray*
+cal_obj_byminute_filter (Recurrence *recur,
+ GArray *occs)
+{
+ GArray *new_occs;
+ CalObjTime *occ;
+ guint8 minutes[60];
+ gint min, len, i;
+ GList *elem;
+
+ /* If BYMINUTE has not been specified, or the array is empty, just
+ return the array. */
+ elem = recur->byminute;
+ if (!elem || occs->len == 0)
+ return occs;
+
+ /* Create an array of minutes from byminutes for fast lookup. */
+ memset (&minutes, 0, sizeof (minutes));
+ while (elem) {
+ min = GPOINTER_TO_INT (elem->data);
+ minutes[min] = 1;
+ elem = elem->next;
+ }
+
+ new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+ len = occs->len;
+ for (i = 0; i < len; i++) {
+ occ = &g_array_index (occs, CalObjTime, i);
+ if (minutes[occ->minute])
+ g_array_append_vals (new_occs, occ, 1);
+ }
+
+ g_array_free (occs, TRUE);
+
+ return new_occs;
+}
+
+
+
+/* If the BYSECOND rule is specified it expands each occurrence in occs, by
+ using each of the seconds in the bysecond list. */
+static GArray*
+cal_obj_bysecond_expand (Recurrence *recur,
+ GArray *occs)
+{
+ GArray *new_occs;
+ CalObjTime *occ;
+ GList *elem;
+ gint len, i;
+
+ /* If BYSECOND has not been specified, or the array is empty, just
+ return the array. */
+ if (!recur->bysecond || occs->len == 0)
+ return occs;
+
+ new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+ len = occs->len;
+ for (i = 0; i < len; i++) {
+ occ = &g_array_index (occs, CalObjTime, i);
+
+ elem = recur->bysecond;
+ while (elem) {
+ occ->second = GPOINTER_TO_INT (elem->data);
+ g_array_append_vals (new_occs, occ, 1);
+ elem = elem->next;
+ }
+ }
+
+ g_array_free (occs, TRUE);
+
+ return new_occs;
+}
+
+
+/* If the BYSECOND rule is specified it filters out all occurrences in occs
+ which do not match one of the seconds in the bysecond list. */
+static GArray*
+cal_obj_bysecond_filter (Recurrence *recur,
+ GArray *occs)
+{
+ GArray *new_occs;
+ CalObjTime *occ;
+ guint8 seconds[61];
+ gint sec, len, i;
+ GList *elem;
+
+ /* If BYSECOND has not been specified, or the array is empty, just
+ return the array. */
+ elem = recur->bysecond;
+ if (!elem || occs->len == 0)
+ return occs;
+
+ /* Create an array of seconds from byseconds for fast lookup. */
+ memset (&seconds, 0, sizeof (seconds));
+ while (elem) {
+ sec = GPOINTER_TO_INT (elem->data);
+ seconds[sec] = 1;
+ elem = elem->next;
+ }
+
+ new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+ len = occs->len;
+ for (i = 0; i < len; i++) {
+ occ = &g_array_index (occs, CalObjTime, i);
+ if (seconds[occ->second])
+ g_array_append_vals (new_occs, occ, 1);
+ }
+
+ g_array_free (occs, TRUE);
+
+ return new_occs;
+}
+
+
+
+static void
+cal_obj_time_add_days (CalObjTime *cotime,
+ gint days)
+{
+ guint day, days_in_month;
+
+ /* We use a guint to avoid overflow on the guint8. */
+ day = (guint) cotime->day;
+ day += days;
+
+ for (;;) {
+ days_in_month = time_days_in_month (cotime->year,
+ cotime->month);
+ if (day <= days_in_month)
+ break;
+
+ cotime->month++;
+ if (cotime->month >= 12) {
+ cotime->year++;
+ cotime->month = 0;
+ }
+
+ day -= days_in_month;
+ }
+
+ cotime->day = (guint8) day;
+}