aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Henriksen <LarsHenriksen@get2net.dk>2019-12-08 17:26:52 +0100
committerLukas Fleischer <lfleischer@calcurse.org>2020-04-28 07:32:44 -0400
commitdc161b3b67c6290b52d0ec33e690ada53faada43 (patch)
treed5aa3bc4579a2bd9926408484098a833c4d704a0
parenteda28d3fef5a2a841fa41c620fc976ce11d824b1 (diff)
downloadcalcurse-dc161b3b67c6290b52d0ec33e690ada53faada43.tar.gz
calcurse-dc161b3b67c6290b52d0ec33e690ada53faada43.zip
User interface for recurrence rules
The function update_rept() is extended with editing of the three recurrence rule lists for BYMONTH, BYMONTHDAY and BYDAY. The integers of the bymonth and bymonthday lists are edited directly as integers, while those of the bywday list are mapped to localized weekday names (as they appear in the calendar panel) with an optional integer prefix (in RFC5545 style: 1MO, -2SA). The RFC5545 (icalendar) requirement that the start day must be the first occurrence and must match the recurrence rule, is met by testing that an occurrence indeed appears on the start day, in these circumstances: - when a recurrent item is loaded from file - when the recurrence rule of an item is edited interactively - when a recurrent appointment gets a new start time - when a recurrent appointment is moved Copy and paste of a recurrent item will only retain the basic recurrence properties of type, frequency, until and exception days. Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
-rw-r--r--src/calcurse.h12
-rw-r--r--src/recur.c53
-rw-r--r--src/ui-day.c428
3 files changed, 416 insertions, 77 deletions
diff --git a/src/calcurse.h b/src/calcurse.h
index 6b3905b..dab1543 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -407,6 +407,16 @@ struct rpt {
llist_t exc; /* EXDATE's */
};
+/* Types of integers in rrule lists. */
+typedef enum {
+ BYMONTH,
+ BYDAY_W,
+ BYDAY_M,
+ BYDAY_Y,
+ BYMONTHDAY,
+ NOLL
+} int_list_t;
+
/* Recurrent appointment definition. */
struct recur_apoint {
struct rpt *rpt; /* recurrence rule */
@@ -1042,6 +1052,8 @@ void pcal_export_data(FILE *);
/* recur.c */
extern llist_ts_t recur_alist_p;
extern llist_t recur_elist;
+void recur_free_int_list(llist_t *);
+void recur_int_list_dup(llist_t *, llist_t *);
void recur_free_exc_list(llist_t *);
void recur_exc_dup(llist_t *, llist_t *);
int recur_str2exc(llist_t *, char *);
diff --git a/src/recur.c b/src/recur.c
index 53c4f53..fcad296 100644
--- a/src/recur.c
+++ b/src/recur.c
@@ -51,13 +51,13 @@ static void free_int(int *i)
mem_free(i);
}
-static void free_int_list(llist_t *ilist)
+void recur_free_int_list(llist_t *ilist)
{
LLIST_FREE_INNER(ilist, free_int);
LLIST_FREE(ilist);
}
-static void int_list_dup(llist_t *l, llist_t *ilist)
+void recur_int_list_dup(llist_t *l, llist_t *ilist)
{
llist_item_t *i;
int *o, *p;
@@ -187,9 +187,14 @@ struct recur_event *recur_event_dup(struct recur_event *in)
rev->mesg = mem_strdup(in->mesg);
rev->rpt = mem_malloc(sizeof(struct rpt));
+ /* Note. The linked lists are NOT copied and no memory allocated. */
rev->rpt->type = in->rpt->type;
rev->rpt->freq = in->rpt->freq;
rev->rpt->until = in->rpt->until;
+ LLIST_INIT(&rev->rpt->bymonth);
+ LLIST_INIT(&rev->rpt->bywday);
+ LLIST_INIT(&rev->rpt->bymonthday);
+ LLIST_INIT(&rev->rpt->exc);
recur_exc_dup(&rev->exc, &in->exc);
@@ -214,9 +219,14 @@ struct recur_apoint *recur_apoint_dup(struct recur_apoint *in)
rapt->mesg = mem_strdup(in->mesg);
rapt->rpt = mem_malloc(sizeof(struct rpt));
+ /* Note. The linked lists are NOT copied and no memory allocated. */
rapt->rpt->type = in->rpt->type;
rapt->rpt->freq = in->rpt->freq;
rapt->rpt->until = in->rpt->until;
+ LLIST_INIT(&rapt->rpt->bymonth);
+ LLIST_INIT(&rapt->rpt->bywday);
+ LLIST_INIT(&rapt->rpt->bymonthday);
+ LLIST_INIT(&rapt->rpt->exc);
recur_exc_dup(&rapt->exc, &in->exc);
@@ -311,12 +321,12 @@ struct recur_apoint *recur_apoint_new(char *mesg, char *note, time_t start,
rapt->state = state;
rapt->rpt = mem_malloc(sizeof(struct rpt));
*rapt->rpt = *rpt;
- int_list_dup(&rapt->rpt->bymonth, &rpt->bymonth);
- free_int_list(&rpt->bymonth);
- int_list_dup(&rapt->rpt->bywday, &rpt->bywday);
- free_int_list(&rpt->bywday);
- int_list_dup(&rapt->rpt->bymonthday, &rpt->bymonthday);
- free_int_list(&rpt->bymonthday);
+ recur_int_list_dup(&rapt->rpt->bymonth, &rpt->bymonth);
+ recur_free_int_list(&rpt->bymonth);
+ recur_int_list_dup(&rapt->rpt->bywday, &rpt->bywday);
+ recur_free_int_list(&rpt->bywday);
+ recur_int_list_dup(&rapt->rpt->bymonthday, &rpt->bymonthday);
+ recur_free_int_list(&rpt->bymonthday);
/*
* Note. The exception dates are in the list rapt->exc.
* The (empty) list rapt->rpt->exc is not used.
@@ -344,12 +354,12 @@ struct recur_event *recur_event_new(char *mesg, char *note, time_t day,
rev->id = id;
rev->rpt = mem_malloc(sizeof(struct rpt));
*rev->rpt = *rpt;
- int_list_dup(&rev->rpt->bymonth, &rpt->bymonth);
- free_int_list(&rpt->bymonth);
- int_list_dup(&rev->rpt->bywday, &rpt->bywday);
- free_int_list(&rpt->bywday);
- int_list_dup(&rev->rpt->bymonthday, &rpt->bymonthday);
- free_int_list(&rpt->bymonthday);
+ recur_int_list_dup(&rev->rpt->bymonth, &rpt->bymonth);
+ recur_free_int_list(&rpt->bymonth);
+ recur_int_list_dup(&rev->rpt->bywday, &rpt->bywday);
+ recur_free_int_list(&rpt->bywday);
+ recur_int_list_dup(&rev->rpt->bymonthday, &rpt->bymonthday);
+ recur_free_int_list(&rpt->bymonthday);
/* Similarly as for recurrent appointment. */
recur_exc_dup(&rev->exc, &rpt->exc);
recur_free_exc_list(&rpt->exc);
@@ -506,6 +516,12 @@ char *recur_apoint_scan(FILE *f, struct tm start, struct tm end,
if (tstart == -1 || tend == -1 || tstart > tend)
return _("date error in appointment");
+ /* Does it occur on the start day? */
+ if (!recur_item_find_occurrence(tstart, tend - tstart, rpt, NULL,
+ update_time_in_date(tstart, 0, 0),
+ NULL))
+ return _("recurrence error: not on start day");
+
/* Filter item. */
if (filter) {
cond = (
@@ -571,6 +587,12 @@ char *recur_event_scan(FILE * f, struct tm start, int id,
return _("date error in event");
tend = ENDOFDAY(tstart);
+ /* Does it occur on the start day? */
+ if (!recur_item_find_occurrence(tstart, -1, rpt, NULL,
+ update_time_in_date(tstart, 0, 0),
+ NULL))
+ return _("recurrence error: not on start day");
+
/* Filter item. */
if (filter) {
cond = (
@@ -973,7 +995,7 @@ static int find_occurrence(time_t start, long dur, struct rpt *rpt, llist_t *exc
return 0;
/* Exception day? */
- if (LLIST_FIND_FIRST(exc, &t, exc_inday))
+ if (exc && LLIST_FIND_FIRST(exc, &t, exc_inday))
return 0;
/* Extraneous day? */
@@ -988,7 +1010,6 @@ static int find_occurrence(time_t start, long dur, struct rpt *rpt, llist_t *exc
*occurrence = t;
return 1;
-#undef ITEM_DUR
}
#undef DUR
diff --git a/src/ui-day.c b/src/ui-day.c
index 3aa1e52..eff9d30 100644
--- a/src/ui-day.c
+++ b/src/ui-day.c
@@ -34,6 +34,8 @@
*
*/
+#include <limits.h>
+#include <langinfo.h>
#include "calcurse.h"
/* Cut & paste registers. */
@@ -163,32 +165,44 @@ static time_t day_edit_time(time_t start, long duration, int move)
/*
* Change start time or move an item.
* Input/output: start and dur.
+ * For recurrent items the new start time must match the repetition pattern.
* If move = 0, end time is fixed, and the new duration is calculated
* when the new start time is known.
* If move = 1, duration is fixed, but passed on for validation of new end time.
*/
-static void update_start_time(time_t *start, long *dur, int move)
+static void update_start_time(time_t *start, long *dur, struct rpt *rpt, int move)
{
time_t newtime;
const char *msg_wrong_time =
_("Invalid time: start time must come before end time!");
+ const char *msg_match =
+ _("Repetition must begin on start day.");
const char *msg_enter = _("Press [Enter] to continue");
+ char *msg;
for (;;) {
newtime = day_edit_time(*start, *dur, move);
if (!newtime)
break;
- if (move) {
- *start = newtime;
- break;
+ if (rpt && !recur_item_find_occurrence(
+ newtime, *dur, rpt, NULL,
+ update_time_in_date(newtime, 0, 0),
+ NULL)) {
+ msg = (char *)msg_match;
} else {
- if (newtime <= *start + *dur) {
- *dur -= (newtime - *start);
+ if (move) {
*start = newtime;
break;
+ } else {
+ if (newtime <= *start + *dur) {
+ *dur -= (newtime - *start);
+ *start = newtime;
+ break;
+ }
}
+ msg = (char *)msg_wrong_time;
}
- status_mesg(msg_wrong_time, msg_enter);
+ status_mesg(msg, msg_enter);
keys_wgetch(win[KEY].p);
}
return;
@@ -304,22 +318,267 @@ static int edit_exc(llist_t *exc)
return updated;
}
-static void update_rept(struct rpt **rpt, const time_t start, llist_t *exc)
+/*
+ * Decode an integer representing a weekday or ordered weekday.
+ * The return value is the (abbreviated) localized day name.
+ * The order is returned in the second argument.
+ */
+static char *int2wday(int i, int *ord, int_list_t type)
+{
+ if (type == BYDAY_W ||
+ ((type == BYDAY_M || type == BYDAY_Y) && -1 < i && i < 7))
+ *ord = 0;
+ else if ((type == BYDAY_M && 6 < i && i < 42) ||
+ (type == BYDAY_Y && 6 < i && i < 378))
+ *ord = i / 7;
+ else if ((type == BYDAY_M && -42 < i && i < -6) ||
+ (type == BYDAY_Y && -378 < i && i < -6)) {
+ i = -i;
+ *ord = -(i / 7);
+ } else
+ return NULL;
+
+ return nl_langinfo(ABDAY_1 + i % 7);
+}
+
+/*
+ * Given a (linked) list of integers representing weekdays, monthdays or months.
+ * Return a string containing the weekdays or integers separated by spaces.
+ */
+static char *int2str(llist_t *il, int_list_t type)
+{
+ llist_item_t *i;
+ int *p, ord = 0;
+ char *wday;
+ struct string s;
+
+ string_init(&s);
+ LLIST_FOREACH(il, i) {
+ p = LLIST_GET_DATA(i);
+ wday = int2wday(*p, &ord, type);
+ if (wday)
+ string_catf(&s, ord ? "%d%s " : "%.0d%s ", ord, wday);
+ else
+ string_catf(&s, "%i ", *p);
+ }
+
+ return string_buf(&s);
+}
+
+/*
+ * Encode a weekday or ordered weekday as an integer.
+ */
+static int wday2int(char *s)
+{
+ int i, ord;
+ char *tail;
+
+ i = strtol(s, &tail, 10);
+ if (!i && tail == s)
+ ord = 0;
+ else
+ ord = i > 0 ? i : -i;
+
+ if (!strcmp(tail, nl_langinfo(ABDAY_1)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 0);
+ else if (!strcmp(tail, nl_langinfo(ABDAY_2)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 1);
+ else if (!strcmp(tail, nl_langinfo(ABDAY_3)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 2);
+ else if (!strcmp(tail, nl_langinfo(ABDAY_4)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 3);
+ else if (!strcmp(tail, nl_langinfo(ABDAY_5)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 4);
+ else if (!strcmp(tail, nl_langinfo(ABDAY_6)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 5);
+ else if (!strcmp(tail, nl_langinfo(ABDAY_7)))
+ return (i < 0 ? -1 : 1) * (ord * 7 + 6);
+ else
+ return -1;
+}
+
+/*
+ * Parse an integer or weekday string. Valid values depend on type.
+ * On success the integer or integer code is returned in *i.
+ */
+static int parse_int(char *s, long *i, int_list_t type)
+{
+ char *eos;
+
+ if (type == BYDAY_W || type == BYDAY_M || type == BYDAY_Y) {
+ *i = wday2int(s);
+ if (*i == -1)
+ return 0;
+ } else {
+ *i = strtol(s, &eos, 10);
+ if (*eos || *i > INT_MAX)
+ return 0;
+ }
+
+ switch (type) {
+ case BYMONTH:
+ /* 1,..,12 */
+ if (0 < *i && *i < 13)
+ return 1;
+ break;
+ case BYDAY_W:
+ /* 0,..,6 */
+ if (-1 < *i && *i < 7)
+ return 1;
+ break;
+ case BYDAY_M:
+ /* 0,..,6 or 7,..,41 or -7,..,-41 */
+ /* 41 = 5*7 + 6, i.e. fifth Saturday of the month */
+ if ((-42 < *i && *i < -6) || (-1 < *i && *i < 42))
+ return 1;
+ break;
+ case BYDAY_Y:
+ /* 0,..,6 or 7,..,377 or -7,..,-377 */
+ /* 377 = 53*7 + 6, i.e. 53th Saturday of the year */
+ if ((-378 < *i && *i < -6) || (-1 < *i && *i < 378))
+ return 1;
+ break;
+ case BYMONTHDAY:
+ /* 1,..,31 or -1,..,-31 */
+ if ((0 < *i && *i < 32) || (-32 < *i && *i < 0))
+ return 1;
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * Update a (linked) list of integer values from a string of such values. Any
+ * positive number of spaces are allowed before, between and after the values.
+ */
+static int str2int(llist_t *l, char *s, int type) {
+ int *j, updated = 0;
+ char *c;
+ long i;
+ llist_t nl;
+ LLIST_INIT(&nl);
+
+ while (1) {
+ while (*s == ' ')
+ s++;
+ if ((c = strchr(s, ' ')))
+ *c = '\0';
+ else if (!strlen(s))
+ break;
+ if (parse_int(s, &i, type)) {
+ j = mem_malloc(sizeof(int));
+ *j = i;
+ LLIST_ADD(&nl, j);
+ } else
+ goto cleanup;
+ if (c)
+ s = c + 1;
+ else
+ break;
+ }
+ recur_free_int_list(l);
+ recur_int_list_dup(l, &nl);
+ updated = 1;
+cleanup:
+ recur_free_int_list(&nl);
+ return updated;
+}
+
+/* Edit an rrule (linked) list of integers. */
+static int edit_ilist(llist_t *ilist, int_list_t type)
+{
+ char *msg;
+ char *msg_wday = NULL;
+ char *msg_format_w = _("Weekdays (%s|..|%s):");
+ char *msg_format_m =
+ _("Weekdays (%s|..|%s, optional prefix 1..5 or -1..-5)):");
+ char *msg_format_y =
+ _("Weekdays (%s|..|%s, optional prefix 1..53 or -1..-53):");
+ char *msg_month = _("Months (1..12):");
+ char *msg_mday = _("Monthdays (1..31 or -1..-31):");
+ char *msg_invalid = _("Invalid format - try again.");
+ char *msg_cont = _("Press any key to continue.");
+ int updated = 0;
+
+ if (type == NOLL)
+ return !updated;
+ char *istr;
+ enum getstr ret;
+
+ switch (type) {
+ case BYDAY_W:
+ asprintf(&msg_wday, msg_format_w,
+ nl_langinfo(ABDAY_2), nl_langinfo(ABDAY_1));
+ msg = msg_wday;
+ break;
+ case BYDAY_M:
+ asprintf(&msg_wday, msg_format_m,
+ nl_langinfo(ABDAY_2), nl_langinfo(ABDAY_1));
+ msg = msg_wday;
+ break;
+ case BYDAY_Y:
+ asprintf(&msg_wday, msg_format_y,
+ nl_langinfo(ABDAY_2), nl_langinfo(ABDAY_1));
+ msg = msg_wday;
+ break;
+ case BYMONTH:
+ msg = msg_month;
+ break;
+ case BYMONTHDAY:
+ msg = msg_mday;
+ break;
+ default:
+ msg = NULL;
+ break;
+ }
+ status_mesg(msg, "");
+ istr = int2str(ilist, type);
+ while (1) {
+ ret = updatestring(win[STA].p, &istr, 0, 1);
+ if (ret == GETSTRING_VALID || ret == GETSTRING_RET) {
+ if (str2int(ilist, istr, type)) {
+ updated = 1;
+ break;
+ } else {
+ status_mesg(msg_invalid, msg_cont);
+ keys_wgetch(win[KEY].p);
+ mem_free(istr);
+ status_mesg(msg, "");
+ istr = int2str(ilist, type);
+ }
+ } else if (ret == GETSTRING_ESC)
+ break;
+ }
+ mem_free(istr);
+ mem_free(msg_wday);
+
+ return updated;
+}
+
+static void update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc)
{
- /* Pointers to dynamically allocated memory. */
+ struct rpt nrpt;
char *msg_rpt_current = NULL;
char *msg_rpt_asktype = NULL;
char *freqstr = NULL;
char *timstr = NULL;
char *outstr = NULL;
+ const char *msg_cont = _("Press any key to continue.");
+
+ LLIST_INIT(&nrpt.exc);
+ LLIST_INIT(&nrpt.bywday);
+ LLIST_INIT(&nrpt.bymonth);
+ LLIST_INIT(&nrpt.bymonthday);
/* Edit repetition type. */
- int newtype;
- const char *msg_rpt_prefix = _("Enter the new repetition type:");
+ const char *msg_rpt_prefix = _("Enter the repetition type:");
const char *msg_rpt_daily = _("(d)aily");
const char *msg_rpt_weekly = _("(w)eekly");
const char *msg_rpt_monthly = _("(m)onthly");
const char *msg_rpt_yearly = _("(y)early");
+ const char *msg_rpt_choice = _("[dwmy]");
/* Find the current repetition type. */
const char *rpt_current;
@@ -340,50 +599,48 @@ static void update_rept(struct rpt **rpt, const time_t start, llist_t *exc)
/* NOTREACHED, but makes the compiler happier. */
rpt_current = msg_rpt_daily;
}
- asprintf(&msg_rpt_current, _("(currently using %s)"), rpt_current);
+ asprintf(&msg_rpt_current, _("(p.t. %s)"), rpt_current);
asprintf(&msg_rpt_asktype, "%s %s, %s, %s, %s? %s", msg_rpt_prefix,
msg_rpt_daily, msg_rpt_weekly, msg_rpt_monthly,
msg_rpt_yearly, msg_rpt_current);
- const char *msg_rpt_choice = _("[dwmy]");
switch (status_ask_choice(msg_rpt_asktype, msg_rpt_choice, 4)) {
case 1:
- newtype = 'D';
+ nrpt.type = 'D';
break;
case 2:
- newtype = 'W';
+ nrpt.type = 'W';
break;
case 3:
- newtype = 'M';
+ nrpt.type = 'M';
break;
case 4:
- newtype = 'Y';
+ nrpt.type = 'Y';
break;
default:
goto cleanup;
}
+ nrpt.type = recur_char2def(nrpt.type);
/* Edit frequency. */
- int newfreq;
+ const char *msg_freq = _("Enter the repetition frequency:");
const char *msg_wrong_freq = _("Invalid frequency.");
- const char *msg_enter = _("Press [Enter] to continue");
do {
- status_mesg(_("Enter the repetition frequency:"), "");
+ status_mesg(msg_freq, "");
mem_free(freqstr);
asprintf(&freqstr, "%d", (*rpt)->freq);
if (updatestring(win[STA].p, &freqstr, 0, 1) !=
GETSTRING_VALID) {
goto cleanup;
}
- newfreq = atoi(freqstr);
- if (newfreq == 0) {
- status_mesg(msg_wrong_freq, msg_enter);
+ nrpt.freq = atoi(freqstr);
+ if (nrpt.freq == 0) {
+ status_mesg(msg_wrong_freq, msg_cont);
keys_wait_for_any_key(win[KEY].p);
}
}
- while (newfreq == 0);
+ while (nrpt.freq == 0);
/* Edit end date. */
- time_t newuntil;
const char *msg_until_1 =
_("Enter end date or duration ('?' for input formats):");
const char *msg_help_1 =
@@ -404,7 +661,7 @@ static void update_rept(struct rpt **rpt, const time_t start, llist_t *exc)
if (updatestring(win[STA].p, &timstr, 0, 1) == GETSTRING_ESC)
goto cleanup;
if (strcmp(timstr, "") == 0 || strcmp(timstr, "0") == 0) {
- newuntil = 0;
+ nrpt.until = 0;
break;
}
if (*(timstr + strlen(timstr) - 1) == '?') {
@@ -417,12 +674,12 @@ static void update_rept(struct rpt **rpt, const time_t start, llist_t *exc)
if (*timstr == '+') {
unsigned days;
if (!parse_date_duration(timstr + 1, &days, start)) {
- status_mesg(msg_wrong_date, msg_enter);
+ status_mesg(msg_wrong_date, msg_cont);
keys_wgetch(win[KEY].p);
continue;
}
/* Until is midnight of the day. */
- newuntil = date_sec_change(
+ nrpt.until = date_sec_change(
update_time_in_date(start, 0, 0),
0, days
);
@@ -430,40 +687,97 @@ static void update_rept(struct rpt **rpt, const time_t start, llist_t *exc)
int year, month, day;
if (!parse_date(timstr, conf.input_datefmt, &year,
&month, &day, ui_calendar_get_slctd_day())) {
- status_mesg(msg_wrong_date, msg_enter);
+ status_mesg(msg_wrong_date, msg_cont);
keys_wgetch(win[KEY].p);
continue;
}
struct date d = { day, month, year };
- newuntil = date2sec(d, 0, 0);
+ nrpt.until = date2sec(d, 0, 0);
}
/* Conmpare days (midnights) - until-day may equal start day. */
- if (newuntil >= update_time_in_date(start, 0, 0))
+ if (nrpt.until >= update_time_in_date(start, 0, 0))
break;
mem_free(timstr);
mem_free(outstr);
timstr = date_sec2date_str(start, DATEFMT(conf.input_datefmt));
asprintf(&outstr, msg_wrong_time, timstr);
- status_mesg(outstr, msg_enter);
+ status_mesg(outstr, msg_cont);
keys_wgetch(win[KEY].p);
}
/* Edit exception list. */
- llist_t newexc;
- recur_exc_dup(&newexc, exc);
- if (!edit_exc(&newexc)) {
- recur_free_exc_list(&newexc);
+ recur_exc_dup(&nrpt.exc, exc);
+ if (!edit_exc(&nrpt.exc))
+ goto cleanup;
+
+ /* Edit BYDAY list. */
+ int_list_t byday_type;
+ switch (nrpt.type) {
+ case RECUR_DAILY:
+ byday_type = BYDAY_W;
+ break;
+ case RECUR_WEEKLY:
+ byday_type = BYDAY_W;
+ break;
+ case RECUR_MONTHLY:
+ byday_type = BYDAY_M;
+ break;
+ case RECUR_YEARLY:
+ byday_type = BYDAY_Y;
+ break;
+ default:
+ byday_type = NOLL;
+ break;
+ }
+ recur_int_list_dup(&nrpt.bywday, &(*rpt)->bywday);
+ if (!edit_ilist(&nrpt.bywday, byday_type))
+ goto cleanup;
+
+ /* Edit BYMONTH list. */
+ recur_int_list_dup(&nrpt.bymonth, &(*rpt)->bymonth);
+ if (!edit_ilist(&nrpt.bymonth, BYMONTH))
+ goto cleanup;
+
+ /* Edit BYMONTHDAY list. */
+ if (nrpt.type != RECUR_WEEKLY) {
+ recur_int_list_dup(&nrpt.bymonthday, &(*rpt)->bymonthday);
+ if (!edit_ilist(&nrpt.bymonthday, BYMONTHDAY))
+ goto cleanup;
+ }
+
+ /*
+ * Check whether the start occurrence matches the recurrence rule, in
+ * other words, does it occur on the start day? This is required by
+ * RFC5545 and ensures that the recurrence set is non-empty (unless it
+ * is an exception day).
+ */
+ const char *msg_match =
+ _("Repetition must begin on start day; any change discarded.");
+ if (!recur_item_find_occurrence(start, dur, &nrpt, NULL,
+ update_time_in_date(start, 0, 0),
+ NULL)) {
+ status_mesg(msg_match, msg_cont);
+ keys_wgetch(win[KEY].p);
goto cleanup;
}
/* Update all recurrence parameters. */
- (*rpt)->type = recur_char2def(newtype);
- (*rpt)->freq = newfreq;
- (*rpt)->until = newuntil;
+ (*rpt)->type = nrpt.type;
+ (*rpt)->freq = nrpt.freq;
+ (*rpt)->until = nrpt.until;
+
recur_free_exc_list(exc);
- recur_exc_dup(exc, &newexc);
- recur_free_exc_list(&newexc);
+ recur_exc_dup(exc, &nrpt.exc);
+
+ recur_free_int_list(&(*rpt)->bywday);
+ recur_int_list_dup(&(*rpt)->bywday, &nrpt.bywday);
+
+ recur_free_int_list(&(*rpt)->bymonth);
+ recur_int_list_dup(&(*rpt)->bymonth, &nrpt.bymonth);
+
+ recur_free_int_list(&(*rpt)->bymonthday);
+ recur_int_list_dup(&(*rpt)->bymonthday, &nrpt.bymonthday);
cleanup:
mem_free(msg_rpt_current);
@@ -471,6 +785,10 @@ cleanup:
mem_free(freqstr);
mem_free(timstr);
mem_free(outstr);
+ recur_free_exc_list(&nrpt.exc);
+ recur_free_int_list(&nrpt.bywday);
+ recur_free_int_list(&nrpt.bymonth);
+ recur_free_int_list(&nrpt.bymonthday);
}
/* Edit an already existing item. */
@@ -490,7 +808,7 @@ void ui_day_item_edit(void)
switch (p->type) {
case RECUR_EVNT:
re = p->item.rev;
- const char *choice_recur_evnt[2] = {
+ const char *choice_recur_evnt[] = {
_("Description"),
_("Repetition")
};
@@ -498,11 +816,9 @@ void ui_day_item_edit(void)
(_("Edit: "), choice_recur_evnt, 2)) {
case 1:
update_desc(&re->mesg);
- io_set_modified();
break;
case 2:
- update_rept(&re->rpt, re->day, &re->exc);
- io_set_modified();
+ update_rept(re->day, -1, &re->rpt, &re->exc);
break;
default:
return;
@@ -511,7 +827,6 @@ void ui_day_item_edit(void)
case EVNT:
e = p->item.ev;
update_desc(&e->mesg);
- io_set_modified();
break;
case RECUR_APPT:
ra = p->item.rapt;
@@ -526,29 +841,24 @@ void ui_day_item_edit(void)
(_("Edit: "), choice_recur_appt, 5)) {
case 1:
need_check_notify = 1;
- update_start_time(&ra->start, &ra->dur, ra->dur == 0);
- io_set_modified();
+ update_start_time(&ra->start, &ra->dur, ra->rpt, ra->dur == 0);
break;
case 2:
update_duration(&ra->start, &ra->dur);
- io_set_modified();
break;
case 3:
if (notify_bar())
need_check_notify =
notify_same_recur_item(ra);
update_desc(&ra->mesg);
- io_set_modified();
break;
case 4:
need_check_notify = 1;
- update_rept(&ra->rpt, ra->start, &ra->exc);
- io_set_modified();
+ update_rept(ra->start, ra->dur, &ra->rpt, &ra->exc);
break;
case 5:
need_check_notify = 1;
- update_start_time(&ra->start, &ra->dur, 1);
- io_set_modified();
+ update_start_time(&ra->start, &ra->dur, ra->rpt, 1);
break;
default:
return;
@@ -566,24 +876,20 @@ void ui_day_item_edit(void)
(_("Edit: "), choice_appt, 4)) {
case 1:
need_check_notify = 1;
- update_start_time(&a->start, &a->dur, a->dur == 0);
- io_set_modified();
+ update_start_time(&a->start, &a->dur, NULL, a->dur == 0);
break;
case 2:
update_duration(&a->start, &a->dur);
- io_set_modified();
break;
case 3:
if (notify_bar())
need_check_notify =
notify_same_item(a->start);
update_desc(&a->mesg);
- io_set_modified();
break;
case 4:
need_check_notify = 1;
- update_start_time(&a->start, &a->dur, 1);
- io_set_modified();
+ update_start_time(&a->start, &a->dur, NULL, 1);
break;
default:
return;
@@ -592,7 +898,7 @@ void ui_day_item_edit(void)
default:
break;
}
-
+ io_set_modified();
ui_calendar_monthly_view_cache_set_invalid();
if (need_check_notify)