aboutsummaryrefslogtreecommitdiffstats
path: root/calendar
diff options
context:
space:
mode:
Diffstat (limited to 'calendar')
-rw-r--r--calendar/ChangeLog25
-rw-r--r--calendar/cal-util/Makefile.am2
-rw-r--r--calendar/cal-util/cal-recur.c1131
-rw-r--r--calendar/cal-util/cal-recur.h102
-rw-r--r--calendar/cal-util/calobj.c8
-rw-r--r--calendar/cal-util/calobj.h4
-rw-r--r--calendar/gui/e-day-view-main-item.c10
-rw-r--r--calendar/gui/e-day-view-time-item.c126
-rw-r--r--calendar/gui/e-day-view-time-item.h3
-rw-r--r--calendar/gui/e-day-view-top-item.c6
-rw-r--r--calendar/gui/e-day-view.c266
-rw-r--r--calendar/gui/e-day-view.h19
-rw-r--r--calendar/gui/e-week-view.c46
13 files changed, 1623 insertions, 125 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog
index c4af6b969c..804605bd84 100644
--- a/calendar/ChangeLog
+++ b/calendar/ChangeLog
@@ -1,3 +1,28 @@
+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.
+
2000-05-19 Federico Mena Quintero <federico@helixcode.com>
* cal-util/timeutil.c (time_add_minutes): Fixed warning message.
diff --git a/calendar/cal-util/Makefile.am b/calendar/cal-util/Makefile.am
index 250cbf95c9..f110e96394 100644
--- a/calendar/cal-util/Makefile.am
+++ b/calendar/cal-util/Makefile.am
@@ -11,6 +11,7 @@ INCLUDES = \
lib_LTLIBRARIES = libcal-util.la
libcal_util_la_SOURCES = \
+ cal-recur.c \
cal-util.c \
calobj.c \
timeutil.c
@@ -18,6 +19,7 @@ libcal_util_la_SOURCES = \
libcal_utilincludedir = $(includedir)/evolution/cal-util
libcal_utilinclude_HEADERS = \
+ cal-recur.h \
cal-util.h \
calobj.h \
timeutil.h
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;
+}
diff --git a/calendar/cal-util/cal-recur.h b/calendar/cal-util/cal-recur.h
new file mode 100644
index 0000000000..340b897417
--- /dev/null
+++ b/calendar/cal-util/cal-recur.h
@@ -0,0 +1,102 @@
+/* -*- 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.
+ */
+
+#ifndef CAL_RECURL_H
+#define CAL_RECUR_H
+
+#include <libgnome/gnome-defs.h>
+#include <glib.h>
+
+BEGIN_GNOME_DECLS
+
+
+/* FIXME: I've put modified versions of RecurType and Recurrence here, since
+ the ones in calobj.h don't support all of iCalendar. Hopefully Seth will
+ update those soon and these can be removed. */
+
+enum RecurType {
+ RECUR_YEARLY,
+ RECUR_MONTHLY,
+ RECUR_WEEKLY,
+ RECUR_DAILY,
+ RECUR_HOURLY,
+ RECUR_MINUTELY,
+ RECUR_SECONDLY,
+};
+
+typedef struct {
+ enum RecurType type;
+
+ int interval;
+
+ int weekday;
+
+ int month_pos;
+
+ int month_day;
+
+
+ /* For BYMONTH modifier. A list of GINT_TO_POINTERs, 0-11. */
+ GList *bymonth;
+
+
+ /* For BYHOUR modifier. A list of GINT_TO_POINTERs, 0-23. */
+ GList *byhour;
+
+ /* For BYMINUTE modifier. A list of GINT_TO_POINTERs, 0-59. */
+ GList *byminute;
+
+ /* For BYSECOND modifier. A list of GINT_TO_POINTERs, 0-60. */
+ GList *bysecond;
+
+} Recurrence;
+
+
+
+/* This is what we use to represent a date & time. */
+typedef struct _CalObjTime CalObjTime;
+struct _CalObjTime {
+ guint16 year;
+ guint8 month; /* 0 - 11 */
+ guint8 day; /* 1 - 31 */
+ guint8 hour; /* 0 - 23 */
+ guint8 minute; /* 0 - 59 */
+ guint8 second; /* 0 - 59 (maybe 60 for leap second) */
+};
+
+
+
+/* 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);
+
+END_GNOME_DECLS
+
+#endif
diff --git a/calendar/cal-util/calobj.c b/calendar/cal-util/calobj.c
index 4a3afd81ec..d2c47fbc65 100644
--- a/calendar/cal-util/calobj.c
+++ b/calendar/cal-util/calobj.c
@@ -157,6 +157,14 @@ ical_object_destroy (iCalObject *ico)
g_free (ico);
}
+/* This resets any recurrence rules of the iCalObject. */
+void
+ical_object_reset_recurrence (iCalObject *ico)
+{
+ free_if_defined (ico->recur);
+ lfree_if_defined (ico->exdate);
+}
+
static GList *
set_list (char *str)
{
diff --git a/calendar/cal-util/calobj.h b/calendar/cal-util/calobj.h
index e44b2ad7b1..cf5483a228 100644
--- a/calendar/cal-util/calobj.h
+++ b/calendar/cal-util/calobj.h
@@ -277,6 +277,10 @@ gboolean ical_object_compare_dates (iCalObject *ico1, iCalObject *ico2);
/* Generates a new uid for a calendar object. Should be g_free'd eventually. */
char *ical_gen_uid (void);
+/* This resets any recurrence rules of the iCalObject. */
+void ical_object_reset_recurrence (iCalObject *ico);
+
+
END_GNOME_DECLS
#endif
diff --git a/calendar/gui/e-day-view-main-item.c b/calendar/gui/e-day-view-main-item.c
index 629d2bd42c..b3a531f62e 100644
--- a/calendar/gui/e-day-view-main-item.c
+++ b/calendar/gui/e-day-view-main-item.c
@@ -229,17 +229,17 @@ e_day_view_main_item_draw (GnomeCanvasItem *canvas_item, GdkDrawable *drawable,
/* Paint the selection background. */
if (GTK_WIDGET_HAS_FOCUS (day_view)
- && day_view->selection_start_col != -1
+ && day_view->selection_start_day != -1
&& !day_view->selection_in_top_canvas) {
- for (day = day_view->selection_start_col;
- day <= day_view->selection_end_col;
+ for (day = day_view->selection_start_day;
+ day <= day_view->selection_end_day;
day++) {
- if (day == day_view->selection_start_col
+ if (day == day_view->selection_start_day
&& day_view->selection_start_row != -1)
start_row = day_view->selection_start_row;
else
start_row = 0;
- if (day == day_view->selection_end_col
+ if (day == day_view->selection_end_day
&& day_view->selection_end_row != -1)
end_row = day_view->selection_end_row;
else
diff --git a/calendar/gui/e-day-view-time-item.c b/calendar/gui/e-day-view-time-item.c
index a3b1c35dd3..c2dcf55727 100644
--- a/calendar/gui/e-day-view-time-item.c
+++ b/calendar/gui/e-day-view-time-item.c
@@ -72,6 +72,14 @@ static void e_day_view_time_item_show_popup_menu (EDayViewTimeItem *dvtmitem,
GdkEvent *event);
static void e_day_view_time_item_on_set_divisions (GtkWidget *item,
EDayViewTimeItem *dvtmitem);
+static void e_day_view_time_item_on_button_press (EDayViewTimeItem *dvtmitem,
+ GdkEvent *event);
+static void e_day_view_time_item_on_button_release (EDayViewTimeItem *dvtmitem,
+ GdkEvent *event);
+static void e_day_view_time_item_on_motion_notify (EDayViewTimeItem *dvtmitem,
+ GdkEvent *event);
+static gint e_day_view_time_item_convert_position_to_row (EDayViewTimeItem *dvtmitem,
+ gint y);
static GnomeCanvasItemClass *parent_class;
@@ -134,9 +142,9 @@ e_day_view_time_item_class_init (EDayViewTimeItemClass *class)
static void
-e_day_view_time_item_init (EDayViewTimeItem *dvtmitm)
+e_day_view_time_item_init (EDayViewTimeItem *dvtmitem)
{
-
+ dvtmitem->dragging_selection = FALSE;
}
@@ -332,14 +340,22 @@ e_day_view_time_item_event (GnomeCanvasItem *item,
switch (event->type) {
case GDK_BUTTON_PRESS:
- if (event->button.button == 3) {
+ if (event->button.button == 1) {
+ e_day_view_time_item_on_button_press (dvtmitem, event);
+ } else if (event->button.button == 3) {
e_day_view_time_item_show_popup_menu (dvtmitem, event);
return TRUE;
}
break;
case GDK_BUTTON_RELEASE:
+ if (event->button.button == 1)
+ e_day_view_time_item_on_button_release (dvtmitem,
+ event);
+ break;
case GDK_MOTION_NOTIFY:
+ e_day_view_time_item_on_motion_notify (dvtmitem, event);
+ break;
default:
break;
@@ -361,8 +377,6 @@ e_day_view_time_item_show_popup_menu (EDayViewTimeItem *dvtmitem,
GSList *group = NULL;
gint current_divisions, i;
- g_print ("In e_day_view_time_item_show_popup_menu\n");
-
day_view = dvtmitem->day_view;
g_return_if_fail (day_view != NULL);
@@ -413,3 +427,105 @@ e_day_view_time_item_on_set_divisions (GtkWidget *item,
"divisions"));
e_day_view_set_mins_per_row (day_view, divisions);
}
+
+
+static void
+e_day_view_time_item_on_button_press (EDayViewTimeItem *dvtmitem,
+ GdkEvent *event)
+{
+ EDayView *day_view;
+ GnomeCanvas *canvas;
+ gint row;
+
+ day_view = dvtmitem->day_view;
+ g_return_if_fail (day_view != NULL);
+
+ canvas = GNOME_CANVAS_ITEM (dvtmitem)->canvas;
+
+ row = e_day_view_time_item_convert_position_to_row (dvtmitem,
+ event->button.y);
+
+ if (row == -1)
+ return;
+
+ if (!GTK_WIDGET_HAS_FOCUS (day_view))
+ gtk_widget_grab_focus (GTK_WIDGET (day_view));
+
+ if (gdk_pointer_grab (GTK_LAYOUT (canvas)->bin_window, FALSE,
+ GDK_POINTER_MOTION_MASK
+ | GDK_BUTTON_RELEASE_MASK,
+ FALSE, NULL, event->button.time) == 0) {
+ e_day_view_start_selection (day_view, -1, row);
+ dvtmitem->dragging_selection = TRUE;
+ }
+}
+
+
+static void
+e_day_view_time_item_on_button_release (EDayViewTimeItem *dvtmitem,
+ GdkEvent *event)
+{
+ EDayView *day_view;
+
+ day_view = dvtmitem->day_view;
+ g_return_if_fail (day_view != NULL);
+
+ if (dvtmitem->dragging_selection) {
+ gdk_pointer_ungrab (event->button.time);
+ e_day_view_finish_selection (day_view);
+ e_day_view_stop_auto_scroll (day_view);
+ }
+
+ dvtmitem->dragging_selection = FALSE;
+}
+
+
+static void
+e_day_view_time_item_on_motion_notify (EDayViewTimeItem *dvtmitem,
+ GdkEvent *event)
+{
+ EDayView *day_view;
+ GnomeCanvas *canvas;
+ gdouble window_y;
+ gint y, row;
+
+ if (!dvtmitem->dragging_selection)
+ return;
+
+ day_view = dvtmitem->day_view;
+ g_return_if_fail (day_view != NULL);
+
+ canvas = GNOME_CANVAS_ITEM (dvtmitem)->canvas;
+
+ y = event->motion.y;
+ row = e_day_view_time_item_convert_position_to_row (dvtmitem, y);
+
+ if (row != -1) {
+ gnome_canvas_world_to_window (canvas, 0, event->motion.y,
+ NULL, &window_y);
+ e_day_view_update_selection (day_view, -1, row);
+ e_day_view_check_auto_scroll (day_view, -1, (gint) window_y);
+ }
+}
+
+
+/* Returns the row corresponding to the y position, or -1. */
+static gint
+e_day_view_time_item_convert_position_to_row (EDayViewTimeItem *dvtmitem,
+ gint y)
+{
+ EDayView *day_view;
+ gint row;
+
+ day_view = dvtmitem->day_view;
+ g_return_val_if_fail (day_view != NULL, -1);
+
+ if (y < 0)
+ return -1;
+
+ row = y / day_view->row_height;
+ if (row >= day_view->rows)
+ return -1;
+
+ return row;
+}
diff --git a/calendar/gui/e-day-view-time-item.h b/calendar/gui/e-day-view-time-item.h
index 9fec96d7dc..8b20fe999e 100644
--- a/calendar/gui/e-day-view-time-item.h
+++ b/calendar/gui/e-day-view-time-item.h
@@ -50,6 +50,9 @@ typedef struct {
/* The width of the time column. */
gint column_width;
+
+ /* TRUE if we are currently dragging the selection times. */
+ gboolean dragging_selection;
} EDayViewTimeItem;
typedef struct {
diff --git a/calendar/gui/e-day-view-top-item.c b/calendar/gui/e-day-view-top-item.c
index 8e084f4d42..45872238a7 100644
--- a/calendar/gui/e-day-view-top-item.c
+++ b/calendar/gui/e-day-view-top-item.c
@@ -230,11 +230,11 @@ e_day_view_top_item_draw (GnomeCanvasItem *canvas_item,
/* Draw the selection background. */
if (GTK_WIDGET_HAS_FOCUS (day_view)
- && day_view->selection_start_col != -1) {
+ && day_view->selection_start_day != -1) {
gint start_col, end_col, rect_x, rect_y, rect_w, rect_h;
- start_col = day_view->selection_start_col;
- end_col = day_view->selection_end_col;
+ start_col = day_view->selection_start_day;
+ end_col = day_view->selection_end_day;
if (end_col > start_col
|| day_view->selection_start_row == -1
diff --git a/calendar/gui/e-day-view.c b/calendar/gui/e-day-view.c
index 84cd31464a..0963b00acb 100644
--- a/calendar/gui/e-day-view.c
+++ b/calendar/gui/e-day-view.c
@@ -92,6 +92,7 @@ static gint e_day_view_focus_out (GtkWidget *widget,
GdkEventFocus *event);
static gint e_day_view_key_press (GtkWidget *widget,
GdkEventKey *event);
+static gboolean e_day_view_check_if_new_event_fits (EDayView *day_view);
static void e_day_view_on_canvas_realized (GtkWidget *widget,
EDayView *day_view);
@@ -121,9 +122,6 @@ static gboolean e_day_view_convert_event_coords (EDayView *day_view,
GdkWindow *window,
gint *x_return,
gint *y_return);
-static void e_day_view_update_selection (EDayView *day_view,
- gint row,
- gint col);
static void e_day_view_update_long_event_resize (EDayView *day_view,
gint day);
static void e_day_view_update_resize (EDayView *day_view,
@@ -271,11 +269,8 @@ static gboolean e_day_view_convert_time_to_grid_position (EDayView *day_view,
gint *col,
gint *row);
-static void e_day_view_check_auto_scroll (EDayView *day_view,
- gint event_y);
static void e_day_view_start_auto_scroll (EDayView *day_view,
gboolean scroll_up);
-static void e_day_view_stop_auto_scroll (EDayView *day_view);
static gboolean e_day_view_auto_scroll_handler (gpointer data);
static void e_day_view_on_new_appointment (GtkWidget *widget,
@@ -470,9 +465,9 @@ e_day_view_init (EDayView *day_view)
day_view->resize_bars_event_num = -1;
day_view->selection_start_row = -1;
- day_view->selection_start_col = -1;
+ day_view->selection_start_day = -1;
day_view->selection_end_row = -1;
- day_view->selection_end_col = -1;
+ day_view->selection_end_day = -1;
day_view->selection_drag_pos = E_DAY_VIEW_DRAG_NONE;
day_view->selection_in_top_canvas = FALSE;
@@ -1098,9 +1093,10 @@ e_day_view_update_event (EDayView *day_view,
g_return_if_fail (E_IS_DAY_VIEW (day_view));
-#if 0
+#if 1
/* FIXME: Just for testing. */
chdir ("/home/damon/tmp");
+ g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
g_print ("In e_day_view_update_event day_view:%p uid:%s\n",
day_view, uid);
@@ -1146,6 +1142,7 @@ e_day_view_update_event (EDayView *day_view,
EDayViewEvent, event_num);
if (ical_object_compare_dates (event->ico, ico)) {
+ g_print ("updated object's dates unchanged\n");
e_day_view_foreach_event_with_uid (day_view, uid, e_day_view_update_event_cb, ico);
ical_object_unref (ico);
gtk_widget_queue_draw (day_view->top_canvas);
@@ -1155,6 +1152,7 @@ e_day_view_update_event (EDayView *day_view,
/* The dates have changed, so we need to remove the
old occurrrences before adding the new ones. */
+ g_print ("dates changed - removing occurrences\n");
e_day_view_foreach_event_with_uid (day_view, uid,
e_day_view_remove_event_cb,
NULL);
@@ -1269,7 +1267,7 @@ e_day_view_remove_event (EDayView *day_view,
{
g_return_if_fail (E_IS_DAY_VIEW (day_view));
-#if 0
+#if 1
g_print ("In e_day_view_remove_event day_view:%p uid:%s\n",
day_view, uid);
#endif
@@ -1291,6 +1289,11 @@ e_day_view_remove_event_cb (EDayView *day_view,
{
EDayViewEvent *event;
+#if 1
+ g_print ("In e_day_view_remove_event_cb day:%i event_num:%i\n",
+ day, event_num);
+#endif
+
if (day == E_DAY_VIEW_LONG_EVENT)
event = &g_array_index (day_view->long_events,
EDayViewEvent, event_num);
@@ -1334,6 +1337,10 @@ e_day_view_update_event_label (EDayView *day_view,
event = &g_array_index (day_view->events[day], EDayViewEvent,
event_num);
+ /* If the event isn't visible just return. */
+ if (!event->canvas_item)
+ return;
+
text = event->ico->summary ? event->ico->summary : "";
if (day_view->editing_event_day == day
@@ -1374,6 +1381,10 @@ e_day_view_update_long_event_label (EDayView *day_view,
event = &g_array_index (day_view->long_events, EDayViewEvent,
event_num);
+ /* If the event isn't visible just return. */
+ if (!event->canvas_item)
+ return;
+
gnome_canvas_item_set (event->canvas_item,
"text", event->ico->summary ? event->ico->summary : "",
NULL);
@@ -1527,19 +1538,19 @@ e_day_view_set_selected_time_range (EDayView *day_view,
}
if (start_row != day_view->selection_start_row
- || start_col != day_view->selection_start_col) {
+ || start_col != day_view->selection_start_day) {
need_redraw = TRUE;
day_view->selection_in_top_canvas = FALSE;
day_view->selection_start_row = start_row;
- day_view->selection_start_col = start_col;
+ day_view->selection_start_day = start_col;
}
if (end_row != day_view->selection_end_row
- || end_col != day_view->selection_end_col) {
+ || end_col != day_view->selection_end_day) {
need_redraw = TRUE;
day_view->selection_in_top_canvas = FALSE;
day_view->selection_end_row = end_row;
- day_view->selection_end_col = end_col;
+ day_view->selection_end_day = end_col;
}
if (need_redraw) {
@@ -1557,9 +1568,9 @@ e_day_view_get_selected_time_range (EDayView *day_view,
{
gint start_col, start_row, end_col, end_row;
- start_col = day_view->selection_start_col;
+ start_col = day_view->selection_start_day;
start_row = day_view->selection_start_row;
- end_col = day_view->selection_end_col;
+ end_col = day_view->selection_end_day;
end_row = day_view->selection_end_row;
if (start_col == -1) {
@@ -1799,16 +1810,7 @@ e_day_view_on_top_canvas_button_press (GtkWidget *widget,
GDK_POINTER_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK,
FALSE, NULL, event->time) == 0) {
- day_view->selection_start_row = -1;
- day_view->selection_start_col = day;
- day_view->selection_end_row = -1;
- day_view->selection_end_col = day;
- day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
- day_view->selection_in_top_canvas = TRUE;
-
- /* FIXME: Optimise? */
- gtk_widget_queue_draw (day_view->top_canvas);
- gtk_widget_queue_draw (day_view->main_canvas);
+ e_day_view_start_selection (day_view, day, -1);
}
} else if (event->button == 3) {
e_day_view_on_event_right_click (day_view, event, -1, -1);
@@ -1912,16 +1914,7 @@ e_day_view_on_main_canvas_button_press (GtkWidget *widget,
GDK_POINTER_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK,
FALSE, NULL, event->time) == 0) {
- day_view->selection_start_row = row;
- day_view->selection_start_col = day;
- day_view->selection_end_row = row;
- day_view->selection_end_col = day;
- day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
- day_view->selection_in_top_canvas = FALSE;
-
- /* FIXME: Optimise? */
- gtk_widget_queue_draw (day_view->top_canvas);
- gtk_widget_queue_draw (day_view->main_canvas);
+ e_day_view_start_selection (day_view, day, row);
}
} else if (event->button == 3) {
e_day_view_on_event_right_click (day_view, event, -1, -1);
@@ -2257,7 +2250,7 @@ e_day_view_on_event_right_click (EDayView *day_view,
};
have_selection = GTK_WIDGET_HAS_FOCUS (day_view)
- && day_view->selection_start_col != -1;
+ && day_view->selection_start_day != -1;
if (event_num == -1) {
items = 1;
@@ -2352,6 +2345,7 @@ e_day_view_on_delete_occurrence (GtkWidget *widget, gpointer data)
{
EDayView *day_view;
EDayViewEvent *event;
+ iCalObject *ico;
day_view = E_DAY_VIEW (data);
@@ -2359,9 +2353,13 @@ e_day_view_on_delete_occurrence (GtkWidget *widget, gpointer data)
if (event == NULL)
return;
- ical_object_add_exdate (event->ico, event->start);
- gnome_calendar_object_changed (day_view->calendar, event->ico,
- CHANGE_DATES);
+ /* We must duplicate the iCalObject, or we won't know it has changed
+ when we get the "update_event" callback. */
+ ico = ical_object_duplicate (event->ico);
+
+ ical_object_add_exdate (ico, event->start);
+ gnome_calendar_object_changed (day_view->calendar, ico, CHANGE_DATES);
+ ical_object_unref (ico);
}
@@ -2389,7 +2387,7 @@ e_day_view_on_unrecur_appointment (GtkWidget *widget, gpointer data)
{
EDayView *day_view;
EDayViewEvent *event;
- iCalObject *ico;
+ iCalObject *ico, *new_ico;
day_view = E_DAY_VIEW (data);
@@ -2397,26 +2395,29 @@ e_day_view_on_unrecur_appointment (GtkWidget *widget, gpointer data)
if (event == NULL)
return;
- /* For the unrecurred instance we duplicate the original object,
- create a new uid for it, get rid of the recurrence rules, and set
- the start & end times to the instances times. */
- ico = ical_object_duplicate (event->ico);
- g_free (ico->uid);
- ico->uid = ical_gen_uid ();
- g_free (ico->recur);
- ico->recur = 0;
- ico->dtstart = event->start;
- ico->dtend = event->end;
-
/* For the recurring object, we add a exception to get rid of the
instance. */
- ical_object_add_exdate (event->ico, event->start);
-
- gnome_calendar_object_changed (day_view->calendar, event->ico,
- CHANGE_ALL);
- gnome_calendar_add_object (day_view->calendar, ico);
+ ico = ical_object_duplicate (event->ico);
+ ical_object_add_exdate (ico, event->start);
+ /* For the unrecurred instance we duplicate the original object,
+ create a new uid for it, get rid of the recurrence rules, and set
+ the start & end times to the instances times. */
+ new_ico = ical_object_duplicate (event->ico);
+ g_free (new_ico->uid);
+ new_ico->uid = ical_gen_uid ();
+ ical_object_reset_recurrence (new_ico);
+ new_ico->dtstart = event->start;
+ new_ico->dtend = event->end;
+
+ /* Now update both iCalObjects. Note that we do this last since at
+ present the updates happen synchronously so our event may disappear.
+ */
+ gnome_calendar_object_changed (day_view->calendar, ico, CHANGE_ALL);
ical_object_unref (ico);
+
+ gnome_calendar_add_object (day_view->calendar, new_ico);
+ ical_object_unref (new_ico);
}
@@ -2443,9 +2444,8 @@ e_day_view_on_top_canvas_button_release (GtkWidget *widget,
EDayView *day_view)
{
if (day_view->selection_drag_pos != E_DAY_VIEW_DRAG_NONE) {
- day_view->selection_drag_pos = E_DAY_VIEW_DRAG_NONE;
gdk_pointer_ungrab (event->time);
- e_day_view_update_calendar_selection_time (day_view);
+ e_day_view_finish_selection (day_view);
} else if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE) {
e_day_view_finish_long_event_resize (day_view);
gdk_pointer_ungrab (event->time);
@@ -2468,10 +2468,9 @@ e_day_view_on_main_canvas_button_release (GtkWidget *widget,
EDayView *day_view)
{
if (day_view->selection_drag_pos != E_DAY_VIEW_DRAG_NONE) {
- day_view->selection_drag_pos = E_DAY_VIEW_DRAG_NONE;
gdk_pointer_ungrab (event->time);
+ e_day_view_finish_selection (day_view);
e_day_view_stop_auto_scroll (day_view);
- e_day_view_update_calendar_selection_time (day_view);
} else if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE) {
e_day_view_finish_resize (day_view);
gdk_pointer_ungrab (event->time);
@@ -2536,7 +2535,7 @@ e_day_view_on_top_canvas_motion (GtkWidget *widget,
event_num);
if (day_view->selection_drag_pos != E_DAY_VIEW_DRAG_NONE) {
- e_day_view_update_selection (day_view, -1, day);
+ e_day_view_update_selection (day_view, day, -1);
return TRUE;
} else if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE) {
if (pos != E_DAY_VIEW_POS_OUTSIDE) {
@@ -2620,9 +2619,6 @@ e_day_view_on_main_canvas_motion (GtkWidget *widget,
&event_x, &event_y))
return FALSE;
- day_view->last_mouse_x = event_x;
- day_view->last_mouse_y = event_y;
-
gnome_canvas_get_scroll_offsets (GNOME_CANVAS (widget),
&scroll_x, &scroll_y);
canvas_x = event_x + scroll_x;
@@ -2638,14 +2634,16 @@ e_day_view_on_main_canvas_motion (GtkWidget *widget,
if (day_view->selection_drag_pos != E_DAY_VIEW_DRAG_NONE) {
if (pos != E_DAY_VIEW_POS_OUTSIDE) {
- e_day_view_update_selection (day_view, row, day);
- e_day_view_check_auto_scroll (day_view, event_y);
+ e_day_view_update_selection (day_view, day, row);
+ e_day_view_check_auto_scroll (day_view,
+ event_x, event_y);
return TRUE;
}
} else if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE) {
if (pos != E_DAY_VIEW_POS_OUTSIDE) {
e_day_view_update_resize (day_view, row);
- e_day_view_check_auto_scroll (day_view, event_y);
+ e_day_view_check_auto_scroll (day_view,
+ event_x, event_y);
return TRUE;
}
} else if (day_view->pressed_event_day != -1
@@ -2705,45 +2703,81 @@ e_day_view_on_main_canvas_motion (GtkWidget *widget,
}
-static void
+/* This sets the selection to a single cell. If day is -1 then the current
+ start day is reused. If row is -1 then the selection is in the top canvas.
+*/
+void
+e_day_view_start_selection (EDayView *day_view,
+ gint day,
+ gint row)
+{
+ if (day == -1) {
+ day = day_view->selection_start_day;
+ if (day == -1)
+ day = 0;
+ }
+
+ day_view->selection_start_day = day;
+ day_view->selection_end_day = day;
+
+ day_view->selection_start_row = row;
+ day_view->selection_end_row = row;
+
+ day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
+ day_view->selection_in_top_canvas = (row == -1) ? TRUE : FALSE;
+
+ /* FIXME: Optimise? */
+ gtk_widget_queue_draw (day_view->top_canvas);
+ gtk_widget_queue_draw (day_view->main_canvas);
+}
+
+
+/* Updates the selection during a drag. If day is -1 the selection day is
+ unchanged. */
+void
e_day_view_update_selection (EDayView *day_view,
- gint row,
- gint col)
+ gint day,
+ gint row)
{
- gint tmp_row, tmp_col;
+ gint tmp_row, tmp_day;
gboolean need_redraw = FALSE;
#if 0
- g_print ("Updating selection %i,%i\n", col, row);
+ g_print ("Updating selection %i,%i\n", day, row);
#endif
day_view->selection_in_top_canvas = (row == -1) ? TRUE : FALSE;
+ if (day == -1)
+ day = (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
+ ? day_view->selection_start_day
+ : day_view->selection_end_day;
+
if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START) {
if (row != day_view->selection_start_row
- || col != day_view->selection_start_col) {
+ || day != day_view->selection_start_day) {
need_redraw = TRUE;
day_view->selection_start_row = row;
- day_view->selection_start_col = col;
+ day_view->selection_start_day = day;
}
} else {
if (row != day_view->selection_end_row
- || col != day_view->selection_end_col) {
+ || day != day_view->selection_end_day) {
need_redraw = TRUE;
day_view->selection_end_row = row;
- day_view->selection_end_col = col;
+ day_view->selection_end_day = day;
}
}
/* Switch the drag position if necessary. */
- if (day_view->selection_start_col > day_view->selection_end_col
- || (day_view->selection_start_col == day_view->selection_end_col
+ if (day_view->selection_start_day > day_view->selection_end_day
+ || (day_view->selection_start_day == day_view->selection_end_day
&& day_view->selection_start_row > day_view->selection_end_row)) {
tmp_row = day_view->selection_start_row;
- tmp_col = day_view->selection_start_col;
- day_view->selection_start_col = day_view->selection_end_col;
+ tmp_day = day_view->selection_start_day;
+ day_view->selection_start_day = day_view->selection_end_day;
day_view->selection_start_row = day_view->selection_end_row;
- day_view->selection_end_col = tmp_col;
+ day_view->selection_end_day = tmp_day;
day_view->selection_end_row = tmp_row;
if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
@@ -2759,6 +2793,14 @@ e_day_view_update_selection (EDayView *day_view,
}
+void
+e_day_view_finish_selection (EDayView *day_view)
+{
+ day_view->selection_drag_pos = E_DAY_VIEW_DRAG_NONE;
+ e_day_view_update_calendar_selection_time (day_view);
+}
+
+
static void
e_day_view_update_long_event_resize (EDayView *day_view,
gint day)
@@ -3808,9 +3850,16 @@ e_day_view_key_press (GtkWidget *widget, GdkEventKey *event)
return FALSE;
}
+ if (day_view->selection_start_day == -1)
+ return FALSE;
- if (day_view->selection_start_col == -1)
+ /* Check if there is room for a new event to be typed in. If there
+ isn't we don't want to add an event as we will then add a new
+ event for every key press. */
+ if (!e_day_view_check_if_new_event_fits (day_view)) {
+ g_print ("Skipping new event. No more room\n");
return FALSE;
+ }
/* We only want to start an edit with a return key or a simple
character. */
@@ -3861,6 +3910,33 @@ e_day_view_key_press (GtkWidget *widget, GdkEventKey *event)
}
+static gboolean
+e_day_view_check_if_new_event_fits (EDayView *day_view)
+{
+ gint day, start_row, end_row, row;
+
+ day = day_view->selection_start_day;
+ start_row = day_view->selection_start_row;
+ end_row = day_view->selection_end_row;
+
+ /* Long events always fit, since we keep adding rows to the top
+ canvas. */
+ if (day != day_view->selection_end_day)
+ return FALSE;
+ if (start_row == 0 && end_row == day_view->rows)
+ return FALSE;
+
+ /* If any of the rows already have E_DAY_VIEW_MAX_COLUMNS columns,
+ return FALSE. */
+ for (row = start_row; row <= end_row; row++) {
+ if (day_view->cols_per_row[day][row] >= E_DAY_VIEW_MAX_COLUMNS)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
static void
e_day_view_start_editing_event (EDayView *day_view,
gint day,
@@ -4160,10 +4236,14 @@ e_day_view_convert_time_to_grid_position (EDayView *day_view,
/* This starts or stops auto-scrolling when dragging a selection or resizing
an event. */
-static void
+void
e_day_view_check_auto_scroll (EDayView *day_view,
+ gint event_x,
gint event_y)
{
+ day_view->last_mouse_x = event_x;
+ day_view->last_mouse_y = event_y;
+
if (event_y < E_DAY_VIEW_AUTO_SCROLL_OFFSET)
e_day_view_start_auto_scroll (day_view, TRUE);
else if (event_y >= day_view->main_canvas->allocation.height
@@ -4186,7 +4266,7 @@ e_day_view_start_auto_scroll (EDayView *day_view,
}
-static void
+void
e_day_view_stop_auto_scroll (EDayView *day_view)
{
if (day_view->auto_scroll_timeout_id != 0) {
@@ -4241,14 +4321,23 @@ e_day_view_auto_scroll_handler (gpointer data)
canvas_x = day_view->last_mouse_x + scroll_x;
canvas_y = day_view->last_mouse_y + new_scroll_y;
+ /* The last_mouse_x position is set to -1 when we are selecting using
+ the time column. In this case we set canvas_x to 0 and we ignore
+ the resulting day. */
+ if (day_view->last_mouse_x == -1)
+ canvas_x = 0;
+
/* Update the selection/resize/drag if necessary. */
pos = e_day_view_convert_position_in_main_canvas (day_view,
canvas_x, canvas_y,
&day, &row, NULL);
+ if (day_view->last_mouse_x == -1)
+ day = -1;
+
if (pos != E_DAY_VIEW_POS_OUTSIDE) {
if (day_view->selection_drag_pos != E_DAY_VIEW_DRAG_NONE) {
- e_day_view_update_selection (day_view, row, day);
+ e_day_view_update_selection (day_view, day, row);
} else if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE) {
e_day_view_update_resize (day_view, row);
} else if (day_view->drag_item->object.flags
@@ -4675,9 +4764,6 @@ e_day_view_on_main_canvas_drag_motion (GtkWidget *widget,
{
gint scroll_x, scroll_y;
- day_view->last_mouse_x = x;
- day_view->last_mouse_y = y;
-
gnome_canvas_get_scroll_offsets (GNOME_CANVAS (widget),
&scroll_x, &scroll_y);
day_view->drag_event_x = x + scroll_x;
@@ -4686,7 +4772,7 @@ e_day_view_on_main_canvas_drag_motion (GtkWidget *widget,
e_day_view_reshape_main_canvas_drag_item (day_view);
e_day_view_reshape_main_canvas_resize_bars (day_view);
- e_day_view_check_auto_scroll (day_view, y);
+ e_day_view_check_auto_scroll (day_view, x, y);
return TRUE;
}
diff --git a/calendar/gui/e-day-view.h b/calendar/gui/e-day-view.h
index 2c992f8207..17bbff6be5 100644
--- a/calendar/gui/e-day-view.h
+++ b/calendar/gui/e-day-view.h
@@ -330,11 +330,11 @@ struct _EDayView
gint popup_event_day;
gint popup_event_num;
- /* The currently selected region. If selection_start_col is -1 there is
+ /* The currently selected region. If selection_start_day is -1 there is
no current selection. If start_row or end_row is -1 then the
selection is in the top canvas. */
- gint selection_start_col;
- gint selection_end_col;
+ gint selection_start_day;
+ gint selection_end_day;
gint selection_start_row;
gint selection_end_row;
@@ -482,6 +482,19 @@ gboolean e_day_view_find_long_event_days (EDayView *day_view,
gint *start_day,
gint *end_day);
+void e_day_view_start_selection (EDayView *day_view,
+ gint day,
+ gint row);
+void e_day_view_update_selection (EDayView *day_view,
+ gint day,
+ gint row);
+void e_day_view_finish_selection (EDayView *day_view);
+
+void e_day_view_check_auto_scroll (EDayView *day_view,
+ gint event_x,
+ gint event_y);
+void e_day_view_stop_auto_scroll (EDayView *day_view);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/calendar/gui/e-week-view.c b/calendar/gui/e-week-view.c
index 26fce815f9..a233d2a66d 100644
--- a/calendar/gui/e-week-view.c
+++ b/calendar/gui/e-week-view.c
@@ -2659,6 +2659,7 @@ e_week_view_on_delete_occurrence (GtkWidget *widget, gpointer data)
{
EWeekView *week_view;
EWeekViewEvent *event;
+ iCalObject *ico;
week_view = E_WEEK_VIEW (data);
@@ -2668,9 +2669,13 @@ e_week_view_on_delete_occurrence (GtkWidget *widget, gpointer data)
event = &g_array_index (week_view->events, EWeekViewEvent,
week_view->popup_event_num);
- ical_object_add_exdate (event->ico, event->start);
- gnome_calendar_object_changed (week_view->calendar, event->ico,
- CHANGE_DATES);
+ /* We must duplicate the iCalObject, or we won't know it has changed
+ when we get the "update_event" callback. */
+ ico = ical_object_duplicate (event->ico);
+
+ ical_object_add_exdate (ico, event->start);
+ gnome_calendar_object_changed (week_view->calendar, ico, CHANGE_DATES);
+ ical_object_unref (ico);
}
@@ -2697,7 +2702,7 @@ e_week_view_on_unrecur_appointment (GtkWidget *widget, gpointer data)
{
EWeekView *week_view;
EWeekViewEvent *event;
- iCalObject *ico;
+ iCalObject *ico, *new_ico;
week_view = E_WEEK_VIEW (data);
@@ -2707,24 +2712,27 @@ e_week_view_on_unrecur_appointment (GtkWidget *widget, gpointer data)
event = &g_array_index (week_view->events, EWeekViewEvent,
week_view->popup_event_num);
+ /* For the recurring object, we add a exception to get rid of the
+ instance. */
+ ico = ical_object_duplicate (event->ico);
+ ical_object_add_exdate (ico, event->start);
+
/* For the unrecurred instance we duplicate the original object,
create a new uid for it, get rid of the recurrence rules, and set
the start & end times to the instances times. */
- ico = ical_object_duplicate (event->ico);
- g_free (ico->uid);
- ico->uid = ical_gen_uid ();
- g_free (ico->recur);
- ico->recur = 0;
- ico->dtstart = event->start;
- ico->dtend = event->end;
+ new_ico = ical_object_duplicate (event->ico);
+ g_free (new_ico->uid);
+ new_ico->uid = ical_gen_uid ();
+ ical_object_reset_recurrence (new_ico);
+ new_ico->dtstart = event->start;
+ new_ico->dtend = event->end;
- /* For the recurring object, we add a exception to get rid of the
- instance. */
- ical_object_add_exdate (event->ico, event->start);
-
- gnome_calendar_object_changed (week_view->calendar, event->ico,
- CHANGE_ALL);
- gnome_calendar_add_object (week_view->calendar, ico);
-
+ /* Now update both iCalObjects. Note that we do this last since at
+ present the updates happen synchronously so our event may disappear.
+ */
+ gnome_calendar_object_changed (week_view->calendar, ico, CHANGE_ALL);
ical_object_unref (ico);
+
+ gnome_calendar_add_object (week_view->calendar, new_ico);
+ ical_object_unref (new_ico);
}