From 5ecbc259fa5b8f8c66c5a1eb85fd16fcfcf73b6c Mon Sep 17 00:00:00 2001 From: Damon Chaplin Date: Fri, 16 Jun 2000 06:59:18 +0000 Subject: updated. 2000-06-16 Damon Chaplin * cal-util/test-recur.c: updated. * cal-util/cal-recur.[hc]: mostly finished, though it depends on the iCalObject struct being updated to support more of iCalendar. svn path=/trunk/; revision=3591 --- calendar/ChangeLog | 7 + calendar/cal-util/cal-recur.c | 384 ++++++++++++++++++++++++++++++++++++----- calendar/cal-util/cal-recur.h | 9 + calendar/cal-util/test-recur.c | 259 +++++++++++++++++++++++++-- calendar/gui/e-day-view.c | 27 ++- 5 files changed, 623 insertions(+), 63 deletions(-) diff --git a/calendar/ChangeLog b/calendar/ChangeLog index 16f1f779b5..05d6ed68a7 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,10 @@ +2000-06-16 Damon Chaplin + + * cal-util/test-recur.c: updated. + + * cal-util/cal-recur.[hc]: mostly finished, though it depends on the + iCalObject struct being updated to support more of iCalendar. + 2000-06-16 Damon Chaplin * pcs/.cvsignore: added icalendar-test. diff --git a/calendar/cal-util/cal-recur.c b/calendar/cal-util/cal-recur.c index e3a0872934..d5ea774e9b 100644 --- a/calendar/cal-util/cal-recur.c +++ b/calendar/cal-util/cal-recur.c @@ -160,6 +160,21 @@ typedef enum { CALOBJ_SECOND } CalObjTimeComparison; +static void cal_object_compute_duration (CalObjTime *start, + CalObjTime *end, + gint *days, + gint *seconds); +static gboolean cal_object_generate_events_for_year (iCalObject *ico, + CalObjTime *event_start, + CalObjTime *interval_start, + CalObjTime *interval_end, + time_t interval_start_time, + time_t interval_end_time, + gint duration_days, + gint duration_seconds, + calendarfn cb, + void *closure); + static GArray* cal_obj_generate_set_yearly (RecurData *recur_data, CalObjRecurVTable *vtable, CalObjTime *occ); @@ -179,6 +194,8 @@ 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_and_invalid_dates (GArray *occs); +static void cal_obj_remove_exceptions (GArray *occs, + GArray *ex_occs); static GArray* cal_obj_bysetpos_filter (CalObjRecurrence *recur, GArray *occs); @@ -320,6 +337,8 @@ static gint cal_obj_time_weekday (CalObjTime *cotime, static gint cal_obj_time_day_of_year (CalObjTime *cotime); static void cal_obj_time_find_first_week (CalObjTime *cotime, RecurData *recur_data); +static void cal_object_time_from_time (CalObjTime *cotime, + time_t t); CalObjRecurVTable cal_obj_yearly_vtable = { @@ -437,60 +456,228 @@ cal_object_generate_events (iCalObject *ico, calendarfn cb, void *closure) { - - /* FIXME: The iCalObject should have a list of RRULES & RDATES and a - list of EXRULES & EXDATES. */ - + CalObjTime interval_start, interval_end, event_start; + CalObjTime chunk_start, chunk_end; + gint days, seconds, year; /* If there is no recurrence, just call the callback if the event intersects the given interval. */ if (!ico->recur) { - if ((end && (ico->dtstart < end) && (ico->dtend > start)) - || ((end == 0) && (ico->dtend > start))) { + if ((end && ico->dtstart < end && ico->dtend > start) + || (end == 0 && ico->dtend > start)) { (* cb) (ico, ico->dtstart, ico->dtend, closure); } return; } + /* Convert the interval start & end to CalObjTime. */ + cal_object_time_from_time (&interval_start, start); + cal_object_time_from_time (&interval_end, end); + + cal_object_time_from_time (&event_start, ico->dtstart); + + /* Calculate the duration of the event, which we use for all + occurrences. We can't just subtract start from end since that may + be affected by daylight-saving time. We also don't want to just + use the number of seconds, since leap seconds will then cause a + problem. So we want a value of days + seconds. */ + cal_object_compute_duration (&interval_start, &interval_end, + &days, &seconds); + + /* Expand the recurrence for each year between start & end, or until + the callback returns 0 if end is 0. */ + for (year = interval_start.year; year <= interval_end.year; year++) { + chunk_start = interval_start; + chunk_start.year = year; + chunk_end = interval_end; + chunk_end.year = year; + + if (year != interval_start.year) { + chunk_start.month = 0; + chunk_start.day = 0; + chunk_start.hour = 0; + chunk_start.minute = 0; + chunk_start.second = 0; + } + if (year != interval_end.year) { + chunk_end.year++; + chunk_end.month = 0; + chunk_end.day = 0; + chunk_end.hour = 0; + chunk_end.minute = 0; + chunk_end.second = 0; + } - /* Expand each of the recurrence rules. */ + if (!cal_object_generate_events_for_year (ico, &event_start, + &interval_start, + &interval_end, + start, end, + days, seconds, + cb, closure)) + break; + } +} - /* Add on specific occurrence dates. */ +static gboolean +cal_object_generate_events_for_year (iCalObject *ico, + CalObjTime *event_start, + CalObjTime *interval_start, + CalObjTime *interval_end, + time_t interval_start_time, + time_t interval_end_time, + gint duration_days, + gint duration_seconds, + calendarfn cb, + void *closure) +{ + GArray *occs, *ex_occs, *tmp_occs; + CalObjTime cotime, *occ; + CalObjRecurrence *recur; + GList *rrules = NULL, *rdates = NULL, *exrules = NULL, *exdates = NULL; + GList *elem; + gint i, status; + time_t occ_time, start_time, end_time; + struct tm start_tm, end_tm; + occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime)); + ex_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime)); - /* Expand each of the exception rules. */ + /* Expand each of the recurrence rules. */ + for (elem = rrules; elem; elem = elem->next) { + recur = elem->data; + tmp_occs = cal_obj_expand_recurrence (event_start, recur, + interval_start, + interval_end); + g_array_append_vals (occs, tmp_occs->data, tmp_occs->len); + g_array_free (tmp_occs, TRUE); + } + + /* Add on specific occurrence dates. */ + for (elem = rdates; elem; elem = elem->next) { + occ_time = (*(time_t*)elem->data); + cal_object_time_from_time (&cotime, occ_time); + g_array_append_val (occs, cotime); + } + + /* Expand each of the exception rules. */ + for (elem = exrules; elem; elem = elem->next) { + recur = elem->data; + tmp_occs = cal_obj_expand_recurrence (event_start, recur, + interval_start, + interval_end); + g_array_append_vals (ex_occs, tmp_occs->data, tmp_occs->len); + g_array_free (tmp_occs, TRUE); + } /* Add on specific exception dates. */ + for (elem = exdates; elem; elem = elem->next) { + occ_time = (*(time_t*)elem->data); + cal_object_time_from_time (&cotime, occ_time); + g_array_append_val (ex_occs, cotime); + } /* Sort both arrays. */ - + cal_obj_sort_occurrences (occs); + cal_obj_sort_occurrences (ex_occs); /* Create the final array, by removing the exceptions from the occurrences, and removing any duplicates. */ + cal_obj_remove_exceptions (occs, ex_occs); + + + /* Call the callback for each occurrence. If it returns 0 we break + out of the loop. */ + for (i = 0; i < occs->len; i++) { + /* Convert each CalObjTime into a start & end time_t, and + check it is within the bounds of the event & interval. */ + occ = &g_array_index (occs, CalObjTime, i); + start_tm.tm_year = occ->year - 1900; + start_tm.tm_mon = occ->month; + start_tm.tm_mday = occ->day; + start_tm.tm_hour = occ->hour; + start_tm.tm_min = occ->minute; + start_tm.tm_sec = occ->second; + start_time = mktime (&start_tm); + + if (start_time < ico->dtstart + || start_time >= interval_end_time) + continue; + + cal_obj_time_add_days (occ, duration_days); + cal_obj_time_add_seconds (occ, duration_seconds); + + end_tm.tm_year = occ->year - 1900; + end_tm.tm_mon = occ->month; + end_tm.tm_mday = occ->day; + end_tm.tm_hour = occ->hour; + end_tm.tm_min = occ->minute; + end_tm.tm_sec = occ->second; + end_time = mktime (&end_tm); + + if (end_time < interval_start_time) + continue; + + status = (*cb) (ico, start_time, end_time, closure); + if (status == 0) + return FALSE; + } + return TRUE; } +static void +cal_object_compute_duration (CalObjTime *start, + CalObjTime *end, + gint *days, + gint *seconds) +{ + GDate start_date, end_date; + gint start_seconds, end_seconds; + + g_date_clear (&start_date, 1); + g_date_clear (&end_date, 1); + g_date_set_dmy (&start_date, start->day, start->month + 1, + start->year); + g_date_set_dmy (&end_date, end->day, end->month + 1, + end->year); + + *days = g_date_julian (&end_date) - g_date_julian (&start_date); + start_seconds = start->hour * 3600 + start->minute * 60 + + start->second; + end_seconds = end->hour * 3600 + end->minute * 60 + end->second; + + *seconds = end_seconds - start_seconds; + if (*seconds < 0) { + *days = *days - 1; + *seconds += 24 * 60 * 60; + } +} + + /* Returns an unsorted GArray of CalObjTime's resulting from expanding the - given recurrence rule within the given interval. */ + given recurrence rule within the given interval. Note that it doesn't + clip the generated occurrences to the interval, i.e. if the interval + starts part way through the year this function still returns all the + occurrences for the year. Clipping is done later. */ GArray* -cal_obj_expand_recurrence (CalObjTime *event_start, +cal_obj_expand_recurrence (CalObjTime *event_start, CalObjRecurrence *recur, - CalObjTime *interval_start, - CalObjTime *interval_end) + CalObjTime *interval_start, + CalObjTime *interval_end) { CalObjRecurVTable *vtable; CalObjTime *event_end = NULL, event_end_cotime; RecurData recur_data; - CalObjTime occ; + CalObjTime occ, *cotime; GArray *all_occs, *occs; - struct tm *event_end_tm; + gint len; vtable = cal_obj_get_vtable (recur->type); @@ -502,14 +689,8 @@ cal_obj_expand_recurrence (CalObjTime *event_start, /* Compute the event_end, if the recur's enddate is set. */ if (recur->enddate) { - event_end_tm = localtime (&recur->enddate); - event_end_cotime.year = event_end_tm->tm_year + 1900; - event_end_cotime.month = event_end_tm->tm_mon; - event_end_cotime.day = event_end_tm->tm_mday; - event_end_cotime.hour = event_end_tm->tm_hour; - event_end_cotime.minute = event_end_tm->tm_min; - event_end_cotime.second = event_end_tm->tm_sec; - + cal_object_time_from_time (&event_end_cotime, + recur->enddate); event_end = &event_end_cotime; } @@ -525,11 +706,6 @@ cal_obj_expand_recurrence (CalObjTime *event_start, interval. */ for (;;) { /* Generate the set of occurrences for this period. */ - /* FIXME: We shouldn't allow multiple expansion filters to be - used to expand the days. They should be done separately and - combined after, if needed. */ - /* FIXME: Remove invalid days before HOURLY/MINUTELY/SECONDLY - rules? Problems with WEEKLY as well? */ switch (recur->type) { case CAL_RECUR_YEARLY: occs = cal_obj_generate_set_yearly (&recur_data, @@ -552,8 +728,24 @@ cal_obj_expand_recurrence (CalObjTime *event_start, /* Apply the BYSETPOS property. */ occs = cal_obj_bysetpos_filter (recur, occs); + /* Remove any occs after event_end. */ + len = occs->len - 1; + if (event_end) { + while (len >= 0) { + cotime = &g_array_index (occs, CalObjTime, + len); + if (cal_obj_time_compare_func (cotime, + event_end) <= 0) + break; + len--; + } + } + /* Add the occurrences onto the main array. */ - g_array_append_vals (all_occs, occs->data, occs->len); + if (len >= 0) + g_array_append_vals (all_occs, occs->data, len + 1); + + g_array_free (occs, TRUE); /* Skip to the next period, or exit the loop if finished. */ if ((*vtable->find_next_position) (&occ, event_end, @@ -585,9 +777,10 @@ cal_obj_generate_set_yearly (RecurData *recur_data, But they aren't all completely independant, e.g. BYMONTHDAY and BYDAY are related to BYMONTH, and BYDAY is related to BYWEEKNO. - BYDAY can also be applied independently, which makes it worse. - So we assume that if BYMONTH or BYWEEKNO is used, then the BYDAY - modifier applies to those, else it is applied independantly. + BYDAY & BYMONTHDAY can also be applied independently, which makes + it worse. So we assume that if BYMONTH or BYWEEKNO is used, then + the BYDAY modifier applies to those, else it is applied + independantly. We expand the occurrences in parallel into the occs_arrays[] array, and then merge them all into one GArray before expanding BYHOUR, @@ -630,8 +823,6 @@ cal_obj_generate_set_yearly (RecurData *recur_data, occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime)); g_array_append_vals (occs, occ, 1); - /* FIXME: We may have an invalid date here. What to do?? */ - occs = (*vtable->byweekno_filter) (recur_data, occs); /* Note that we explicitly call the weekly version of the BYDAY expansion filter. */ @@ -649,6 +840,17 @@ cal_obj_generate_set_yearly (RecurData *recur_data, occs_arrays[num_occs_arrays++] = occs; } + /* If BYMONTHDAY is set, and BYMONTH is not set, we need to + expand BYMONTHDAY independantly. */ + if (recur->bymonthday && !recur->bymonth) { + occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime)); + g_array_append_vals (occs, occ, 1); + + occs = (*vtable->bymonthday_filter) (recur_data, occs); + + occs_arrays[num_occs_arrays++] = occs; + } + /* If BYDAY is set, and BYMONTH and BYWEEKNO are not set, we need to expand BYDAY independantly. */ if (recur->byday && !recur->bymonth && !recur->byweekno) { @@ -722,7 +924,11 @@ cal_obj_generate_set_default (RecurData *recur_data, CalObjTime *occ) { GArray *occs; - +#if 0 + g_print ("Generating set for %i/%i/%i %02i:%02i:%02i\n", + occ->day, occ->month, occ->year, occ->hour, occ->minute, + occ->second); +#endif /* We start with just the one time in the set. */ occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime)); g_array_append_vals (occs, occ, 1); @@ -913,6 +1119,75 @@ cal_obj_remove_duplicates_and_invalid_dates (GArray *occs) } +/* Removes the exceptions from the ex_occs array from the occurrences in the + occs array, and removes any duplicates. Both arrays are sorted. */ +static void +cal_obj_remove_exceptions (GArray *occs, + GArray *ex_occs) +{ + CalObjTime *occ, *prev_occ = NULL, *ex_occ; + gint i, j = 0, cmp, ex_index, occs_len, ex_occs_len; + gboolean keep_occ; + + if (occs->len == 0 || ex_occs->len == 0) + return; + + ex_index = 0; + occs_len = occs->len; + ex_occs_len = ex_occs->len; + + ex_occ = &g_array_index (ex_occs, CalObjTime, ex_index); + for (i = 0; i < occs_len; i++) { + occ = &g_array_index (occs, CalObjTime, i); + keep_occ = TRUE; + + /* If the occurrence is a duplicate of the previous one, skip + it. */ + if (prev_occ + && cal_obj_time_compare_func (occ, prev_occ) == 0) { + keep_occ = FALSE; + } else if (ex_occ) { + /* Step through the exceptions until we come to one + that matches or follows this occurrence. */ + while (ex_occ) { + cmp = cal_obj_time_compare_func (ex_occ, occ); + if (cmp > 0) + break; + + /* Move to the next exception, or set ex_occ + to NULL when we reach the end of array. */ + ex_index++; + if (ex_index < ex_occs_len) + ex_occ = &g_array_index (ex_occs, + CalObjTime, + ex_index); + else + ex_occ = NULL; + + /* If the current exception matches this + occurrence we remove it. */ + if (cmp == 0) { + keep_occ = FALSE; + break; + } + } + } + + if (keep_occ) { + 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 (CalObjRecurrence *recur, GArray *occs) @@ -1548,7 +1823,6 @@ cal_obj_byweekno_expand (RecurData *recur_data, elem = recur_data->recur->byweekno; while (elem) { weekno = GPOINTER_TO_INT (elem->data); - /* FIXME: Skip occurrences outside the year? */ if (weekno > 0) { cotime = year_start_cotime; cal_obj_time_add_days (&cotime, @@ -1559,7 +1833,9 @@ cal_obj_byweekno_expand (RecurData *recur_data, -weekno * 7); } - g_array_append_val (new_occs, cotime); + /* Skip occurrences if they fall outside the year. */ + if (cotime.year == occ->year) + g_array_append_val (new_occs, cotime); elem = elem->next; } } @@ -1832,6 +2108,9 @@ cal_obj_byday_expand_yearly (RecurData *recur_data, if (occ->year == year) g_array_append_vals (new_occs, occ, 1); } + + /* Reset the year, as we may have gone past the end. */ + occ->year = year; } } @@ -1904,6 +2183,11 @@ cal_obj_byday_expand_monthly (RecurData *recur_data, if (occ->year == year && occ->month == month) g_array_append_vals (new_occs, occ, 1); } + + /* Reset the year & month, as we may have gone past + the end. */ + occ->year = year; + occ->month = month; } } @@ -2287,7 +2571,7 @@ cal_obj_time_add_hours (CalObjTime *cotime, /* We use a guint to avoid overflow on the guint8. */ hour = cotime->hour + hours; cotime->hour = hour % 24; - if (hour > 24) + if (hour >= 24) cal_obj_time_add_days (cotime, hour / 24); } @@ -2303,7 +2587,7 @@ cal_obj_time_add_minutes (CalObjTime *cotime, /* We use a guint to avoid overflow on the guint8. */ minute = cotime->minute + minutes; cotime->minute = minute % 60; - if (minute > 60) + if (minute >= 60) cal_obj_time_add_hours (cotime, minute / 60); } @@ -2319,7 +2603,7 @@ cal_obj_time_add_seconds (CalObjTime *cotime, /* We use a guint to avoid overflow on the guint8. */ second = cotime->second + seconds; cotime->second = second % 60; - if (second > 60) + if (second >= 60) cal_obj_time_add_minutes (cotime, second / 60); } @@ -2507,3 +2791,21 @@ cal_obj_time_find_first_week (CalObjTime *cotime, } +static void +cal_object_time_from_time (CalObjTime *cotime, + time_t t) +{ + struct tm *tmp_tm; + time_t tmp_time_t; + + tmp_time_t = t; + tmp_tm = localtime (&tmp_time_t); + + cotime->year = tmp_tm->tm_year + 1900; + cotime->month = tmp_tm->tm_mon; + cotime->day = tmp_tm->tm_mday; + cotime->hour = tmp_tm->tm_hour; + cotime->minute = tmp_tm->tm_min; + cotime->second = tmp_tm->tm_sec; +} + diff --git a/calendar/cal-util/cal-recur.h b/calendar/cal-util/cal-recur.h index 6069ff5f75..391fbfd491 100644 --- a/calendar/cal-util/cal-recur.h +++ b/calendar/cal-util/cal-recur.h @@ -26,6 +26,7 @@ #include #include +#include "calobj.h" BEGIN_GNOME_DECLS @@ -106,7 +107,15 @@ struct _CalObjTime { }; +/* This will eventually replace ical_object_generate_events(). */ +void cal_object_generate_events (iCalObject *ico, + time_t start, + time_t end, + calendarfn cb, + void *closure); + +/* This is an internal function, only here for testing. */ GArray* cal_obj_expand_recurrence (CalObjTime *event_start, CalObjRecurrence *recur, CalObjTime *interval_start, diff --git a/calendar/cal-util/test-recur.c b/calendar/cal-util/test-recur.c index fd3059113e..83602bb0d6 100644 --- a/calendar/cal-util/test-recur.c +++ b/calendar/cal-util/test-recur.c @@ -35,41 +35,198 @@ static void set_time (CalObjTime *cotime, gint year, gint month, gint day, gint hour, gint minute, gint second); static void display_occs (GArray *occs); +static GList* build_list (gint first, ...); +static gchar* time_to_string (CalObjTime *cotime); +static void do_test (gchar *description, + CalObjTime *event_start, + CalObjRecurrence *recur, + CalObjTime *interval_start, + CalObjTime *interval_end); -#define LIST_ADD(list, num) \ - list = g_list_prepend (list, GINT_TO_POINTER (num)); +#define LIST_END 999 -int -main (int argc, char *argv[]) +static void +test_yearly () { CalObjTime event_start, interval_start, interval_end; CalObjRecurrence recur; - GArray *occs; set_time (&event_start, 2000, 0, 1, 0, 0, 0); /* We set the interval to a wide range so we just test the event. */ - set_time (&interval_start, 1900, 0, 1, 0, 0, 0); - set_time (&interval_end, 2100, 0, 1, 0, 0, 0); + set_time (&interval_start, 2000, 0, 1, 0, 0, 0); + set_time (&interval_end, 2010, 0, 1, 0, 0, 0); + + memset (&recur, 0, sizeof (recur)); + recur.type = CAL_RECUR_YEARLY; + recur.interval = 3; + recur.byweekno = build_list (3, 9, 24, LIST_END); + recur.byday = build_list (3, 0, 5, 0, LIST_END); + do_test ("YEARLY every 3 years in weeks 3, 9, 24 on Thu/Sat", + &event_start, &recur, &interval_start, &interval_end); + + set_time (&interval_end, 2002, 0, 1, 0, 0, 0); memset (&recur, 0, sizeof (recur)); recur.type = CAL_RECUR_YEARLY; recur.interval = 1; + recur.bymonth = build_list (0, 6, LIST_END); + recur.byday = build_list (0, 0, 6, 0, LIST_END); + do_test ("YEARLY every year in Jan/Jul on Mon/Sun", + &event_start, &recur, &interval_start, &interval_end); - LIST_ADD (recur.byweekno, 3) - LIST_ADD (recur.byweekno, 9) - LIST_ADD (recur.byweekno, 24) - LIST_ADD (recur.byday, 0) - LIST_ADD (recur.byday, 3) - LIST_ADD (recur.byday, 0) - LIST_ADD (recur.byday, 5) + memset (&recur, 0, sizeof (recur)); + recur.type = CAL_RECUR_YEARLY; + recur.interval = 1; + recur.bymonthday = build_list (3, 7, LIST_END); + do_test ("YEARLY every year on 3rd & 7th of the month", + &event_start, &recur, &interval_start, &interval_end); - occs = cal_obj_expand_recurrence (&event_start, &recur, - &interval_start, &interval_end); - display_occs (occs); - g_array_free (occs, TRUE); + + memset (&recur, 0, sizeof (recur)); + recur.type = CAL_RECUR_YEARLY; + recur.interval = 1; + recur.byyearday = build_list (15, 126, 360, LIST_END); + do_test ("YEARLY every year on 15th, 126th & 360th day of the year", + &event_start, &recur, &interval_start, &interval_end); + +} + + +static void +test_monthly () +{ + CalObjTime event_start, interval_start, interval_end; + CalObjRecurrence recur; + + set_time (&event_start, 2000, 0, 1, 0, 0, 0); + + /* We set the interval to a wide range so we just test the event. */ + set_time (&interval_start, 2000, 0, 1, 0, 0, 0); + set_time (&interval_end, 2002, 0, 1, 0, 0, 0); + + memset (&recur, 0, sizeof (recur)); + recur.type = CAL_RECUR_MONTHLY; + recur.interval = 1; + do_test ("MONTHLY every month", + &event_start, &recur, &interval_start, &interval_end); + +} + +static void +test_weekly () +{ + CalObjTime event_start, interval_start, interval_end; + CalObjRecurrence recur; + + set_time (&event_start, 2000, 0, 1, 0, 0, 0); + + /* We set the interval to a wide range so we just test the event. */ + set_time (&interval_start, 2000, 0, 1, 0, 0, 0); + set_time (&interval_end, 2002, 0, 1, 0, 0, 0); + + memset (&recur, 0, sizeof (recur)); + recur.type = CAL_RECUR_WEEKLY; + recur.interval = 1; + do_test ("WEEKLY every week", + &event_start, &recur, &interval_start, &interval_end); + +} + +static void +test_daily () +{ + CalObjTime event_start, interval_start, interval_end; + CalObjRecurrence recur; + + set_time (&event_start, 2000, 0, 1, 0, 0, 0); + + /* We set the interval to a wide range so we just test the event. */ + set_time (&interval_start, 2000, 0, 1, 0, 0, 0); + set_time (&interval_end, 2000, 6, 1, 0, 0, 0); + + memset (&recur, 0, sizeof (recur)); + recur.type = CAL_RECUR_DAILY; + recur.interval = 1; + do_test ("DAILY every day", + &event_start, &recur, &interval_start, &interval_end); + +} + +static void +test_hourly () +{ + CalObjTime event_start, interval_start, interval_end; + CalObjRecurrence recur; + + set_time (&event_start, 2000, 0, 1, 2, 15, 0); + + /* We set the interval to a wide range so we just test the event. */ + set_time (&interval_start, 2000, 0, 1, 0, 0, 0); + set_time (&interval_end, 2002, 0, 1, 0, 0, 0); + + memset (&recur, 0, sizeof (recur)); + recur.type = CAL_RECUR_HOURLY; + recur.interval = 3; + recur.bymonth = build_list (3, 11, LIST_END); + recur.byday = build_list (2, 0, 4, 0, LIST_END); + do_test ("HOURLY every 3 hours in Apr/Dec on Wed & Fri", + &event_start, &recur, &interval_start, &interval_end); +} + +static void +test_minutely () +{ + CalObjTime event_start, interval_start, interval_end; + CalObjRecurrence recur; + + set_time (&event_start, 2000, 0, 1, 0, 0, 0); + + /* We set the interval to a wide range so we just test the event. */ + set_time (&interval_start, 2000, 0, 1, 0, 0, 0); + set_time (&interval_end, 2000, 0, 2, 0, 0, 0); + + memset (&recur, 0, sizeof (recur)); + recur.type = CAL_RECUR_MINUTELY; + recur.interval = 45; + do_test ("MINUTELY every 45 minutes", + &event_start, &recur, &interval_start, &interval_end); +} + +static void +test_secondly () +{ + CalObjTime event_start, interval_start, interval_end; + CalObjRecurrence recur; + + set_time (&event_start, 2000, 0, 1, 0, 0, 0); + + /* We set the interval to a wide range so we just test the event. */ + set_time (&interval_start, 2000, 0, 1, 0, 0, 0); + set_time (&interval_end, 2000, 0, 2, 0, 0, 0); + + memset (&recur, 0, sizeof (recur)); + recur.type = CAL_RECUR_SECONDLY; + recur.interval = 15; + recur.byhour = build_list (2, 4, 6, LIST_END); + recur.byminute = build_list (0, 30, LIST_END); + do_test ("SECONDLY every 15 seconds at 2:00,2:30,4:00,4:30,6:00,6:30", + &event_start, &recur, &interval_start, &interval_end); +} + +int +main (int argc, char *argv[]) +{ + + test_yearly (); + test_monthly (); + test_weekly (); + test_daily (); + test_hourly (); + test_minutely (); + test_secondly (); return 0; } @@ -88,6 +245,72 @@ set_time (CalObjTime *cotime, gint year, gint month, gint day, } +static GList* +build_list (gint first, ...) +{ + va_list args; + GList *list; + gint num; + + va_start (args, first); + + list = g_list_prepend (NULL, GINT_TO_POINTER (first)); + + num = va_arg (args, gint); + while (num != LIST_END) { + list = g_list_prepend (list, GINT_TO_POINTER (num)); + num = va_arg (args, gint); + } + + list = g_list_reverse (list); + + va_end (args); + + return list; +} + + +static void +do_test (gchar *description, + CalObjTime *event_start, + CalObjRecurrence *recur, + CalObjTime *interval_start, + CalObjTime *interval_end) +{ + GArray *occs; + + g_print ("========================================================\n"); + g_print ("%s\n", description); + g_print ("(From %s", time_to_string (interval_start)); + g_print (" To %s)\n\n", time_to_string (interval_end)); + + occs = cal_obj_expand_recurrence (event_start, recur, + interval_start, interval_end); + display_occs (occs); + g_array_free (occs, TRUE); +} + + +static gchar* +time_to_string (CalObjTime *cotime) +{ + static gchar buffer[64]; + gint month; + gchar *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "XXX" }; + + month = cotime->month; + if (month < 0 || month > 12) + month = 12; + + sprintf (buffer, "%s %2i %02i:%02i:%02i %4i", + months[month], cotime->day, + cotime->hour, cotime->minute, cotime->second, + cotime->year); + + return buffer; +} + static void display_occs (GArray *occs) { diff --git a/calendar/gui/e-day-view.c b/calendar/gui/e-day-view.c index 8508d08a56..e01fbeb6e9 100644 --- a/calendar/gui/e-day-view.c +++ b/calendar/gui/e-day-view.c @@ -5052,6 +5052,7 @@ e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget, EDayViewEvent *event; EDayViewPosition pos; gint day, start_day, end_day, num_days; + gint start_offset, end_offset; gchar *event_uid; iCalObject ico; @@ -5061,6 +5062,9 @@ e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget, NULL); if (pos != E_DAY_VIEW_POS_OUTSIDE) { num_days = 1; + start_offset = 0; + end_offset = 0; + if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) { event = &g_array_index (day_view->long_events, EDayViewEvent, day_view->drag_event_num); @@ -5074,6 +5078,10 @@ e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget, num_days = end_day - start_day + 1; /* Make sure we don't go off the screen. */ day = MIN (day, day_view->days_shown - num_days); + + start_offset = event->start_minute; + end_offset = event->end_minute; + } else if (day_view->drag_event_day != -1) { event = &g_array_index (day_view->events[day_view->drag_event_day], EDayViewEvent, @@ -5092,8 +5100,8 @@ e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget, had changed in the "update_event" callback. */ ico = *event->ico; - ico.dtstart = day_view->day_starts[day]; - ico.dtend = day_view->day_starts[day + num_days]; + ico.dtstart = day_view->day_starts[day] + start_offset * 60; + ico.dtend = day_view->day_starts[day + num_days - 1] + end_offset * 60; gtk_drag_finish (context, TRUE, TRUE, time); @@ -5127,6 +5135,7 @@ e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget, EDayViewEvent *event; EDayViewPosition pos; gint day, row, start_row, end_row, num_rows, scroll_x, scroll_y; + gint start_offset, end_offset; gchar *event_uid; iCalObject ico; @@ -5141,6 +5150,9 @@ e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget, &row, NULL); if (pos != E_DAY_VIEW_POS_OUTSIDE) { num_rows = 1; + start_offset = 0; + end_offset = 0; + if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) { event = &g_array_index (day_view->long_events, EDayViewEvent, day_view->drag_event_num); @@ -5154,6 +5166,13 @@ e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget, start_row = event->start_minute / day_view->mins_per_row; end_row = (event->end_minute - 1) / day_view->mins_per_row; num_rows = end_row - start_row + 1; + + start_offset = event->start_minute % day_view->mins_per_row; + end_offset = event->end_minute % day_view->mins_per_row; + if (end_offset != 0) + end_offset = day_view->mins_per_row - end_offset; + g_print ("Start offset: %i End offset: %i\n", + start_offset, end_offset); } event_uid = data->data; @@ -5168,8 +5187,8 @@ e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget, had changed in the "update_event" callback. */ ico = *event->ico; - ico.dtstart = e_day_view_convert_grid_position_to_time (day_view, day, row); - ico.dtend = e_day_view_convert_grid_position_to_time (day_view, day, row + num_rows); + ico.dtstart = e_day_view_convert_grid_position_to_time (day_view, day, row) + start_offset * 60; + ico.dtend = e_day_view_convert_grid_position_to_time (day_view, day, row + num_rows) - end_offset * 60; gtk_drag_finish (context, TRUE, TRUE, time); -- cgit v1.2.3