summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/repeat.txt170
-rw-r--r--src/ui-day.c155
2 files changed, 267 insertions, 58 deletions
diff --git a/doc/repeat.txt b/doc/repeat.txt
index 16a71da..7b8679b 100644
--- a/doc/repeat.txt
+++ b/doc/repeat.txt
@@ -4,32 +4,144 @@ Repeat
Repeat an event or an appointment.
You must first select the item to be repeated by moving inside the appointment
-panel. Then running the repeat command will lead you to a set of three
-questions, with which you will be able to specify the repetition
-characteristics:
-
- o type: you can choose between a daily, weekly, monthly or
- yearly repetition by pressing 'D', 'W', 'M' or 'Y'
- respectively.
-
- o frequency: this indicates how often the item shall be repeated.
- For example, if you want to remember an anniversary,
- choose a 'yearly' repetition with a frequency of '1',
- which means it must be repeated every year. Another
- example: if you go to the restaurant every two days,
- choose a 'daily' repetition with a frequency of '2'.
-
- o ending date: this specifies when to stop repeating the selected
- event or appointment. To indicate an endless
- repetition, enter '0' and the item will be repeated
- forever.
-
-Notes
------
-
-* Repeated items are marked with an '*' inside the appointment panel, to be
- easily recognizable from non-repeated ones.
-
-* The 'Repeat' and 'Delete' command can be mixed to create complicated
- configurations, as it is possible to delete only one occurrence of a repeated
- item.
+panel. Then invoke the repeat command, and you will be asked you to select a
+simple or advanced repetition. A simple repetition will lead you to a set of
+three questions with which you specify the basic repetition characteristics:
+
+ o type: choose between a daily, weekly, monthly or yearly base period
+
+ o frequency: choose the interval between base periods. '1' means every day
+ (week, month or year), '2' means every other day (week, ...).
+ For example, if you want to remember an anniversary, choose a
+ yearly type with a frequency of 1, which means it will be
+ repeated every year. Another example: if you go to a restaurant
+ every second day, choose a daily type with a frequency of 2.
+
+ o until date: specifies a day after which the repetitions do not occur. To
+ indicate an endless repetition, enter 0 (zero) or RETURN, and
+ the item will be repeated forever.
+
+For an advanced repetition you may, in addition to the basic characteristics,
+specify three lists of either days of the week, months of the year or days of
+the month. The three lists modify the simple repetition in some way by either
+limiting or expanding the basic pattern.
+
+ o Weekdays: abbriviated names of days of the week (as they appear above
+ the calendar). For monthly or yearly repetitions the name may
+ have a numerical prefix (1, 2, ... or -1, -2, ...) to specify
+ a particular weekday of the month or year, counted from either
+ the start or the end of the month or year.
+
+ o Months: the numerical name of a month (1, 2,..., 12).
+
+ o Monthdays: the numerical name of a day of the month (1, 2,..., 31) or the
+ opposites (-1, -2,..., -31) which count from the end of the
+ month.
+
+For each list you may enter one or several values separated by spaces. The
+prompt for the list gives a hint as to the format. If you enter '?' only, you
+get very terse, context dependent information as to the effect on the
+repetition. Note that both format and effect depend on the basic type. The
+combined effect on the basic type of the listed days and months is derived
+from the iCalendar specification (RFC5545).
+
+Briefly, a weekday or monthday limits the repetitions of type daily, but
+expands those of type weekly, monthly and yearly. For example, with 'Weekdays'
+set to 'Sat Sun' a daily type (with frequency one) is only repeated on
+Saturdays and Sundays (two instead of seven repetitions per week), while a
+weekly type is also repeated on the extra day (two instead of one repetition
+per week).
+
+Similarly, a month limits the repetitions of type daily, weekly and monthly,
+but expands those of type yearly. For example, with 'Months' set to '3 10', a
+weekly type is only repeated in March and October, while a yearly type is also
+repeated in the extra month (two instead of one yearly repetition).
+
+Mnemonic. If the list type (day or month) is shorter than the basic type (day,
+week, month, year), it expands the repetitions of the basic type, if not, it
+limits them.
+
+There are a some important exceptions, though, for 'Weekdays':
+
+ o For a monthly type, the expanded repetitions are special: a weekday with a
+ prefix expands to that particular weekday of the month ('3Mon' = third
+ Monday), while a weekday without prefix expands to all weekdays of the
+ month ('Wed' = all Wednesdays). Furthermore, repetitions are limited if
+ 'Monthdays' is also set (for example, if 'Weekdays' is set to 'Fri' with
+ 'Monthdays' set to '13', the monthly repetition occurs only on Friday 13th).
+
+ o For a yearly type, the expanded repetitions are special and depend also on
+ the setting of 'Months'. A weekday with a prefix expands to that particular
+ weekday of the year ('-1Sun' = last Sunday of the year), or, if 'Months' is
+ also set, to that weekday of the listed months. A weekday without prefix
+ expands to all weekdays ('Thu' = all Thurdays in either the year or listed
+ months). Furthermore, repetitions are limited if 'Monthdays' is also set
+ as for monthly type.
+
+When you are finished setting up the repeating item, you may test it
+interactively with the 'next' command, a subcommand of the generic command
+(default ': n'). Invoked with a repeating item selected in the appointments
+panel, it will go to the next repetition. By doing this repeatedly, you may
+step through the repetitions one by one.
+
+If you edit an item that is already repeating, you will be led through all six
+characteristics and can modify any of them.
+
+Here are some typical examples. It is assumed that you have invoked the repeat
+command on what will become the first repetition and have selected an advanced
+repetition, that your date input format is dd/mm/yyyy, and that your language
+is english.
+
+ o an event that occurs every Tuesday and Thursday forever
+ type: weekly
+ frequency: 1
+ until: 0
+ weekdays: Tue Thu
+ months: (empty)
+ monthdays: (not possible)
+
+ o an event that occurs on workdays except in July forever (holidays are not
+ taken into account)
+ type: daily
+ frequency: 1
+ until: 0
+ weekdays: Mon Tue Wed Thu Fri
+ months: 1 2 3 4 5 6 8 9 10 11 12
+ monthdays: (empty)
+
+ o an appointment that occurs on the first and last Monday of the month forever
+ type: monthly
+ frequency: 1
+ until: 0
+ weekdays: 1Mon -1Mon
+ months: (empty)
+ monthdays: (empty)
+
+ o an appointment that occurs on all Wednesdays in June and July until the
+ end of June 2022
+ type: monthly
+ frequency: 1
+ until: 30/6/2022
+ weekdays: Wed
+ months: 6 7
+ monthdays: (empty)
+
+ o an event that occurs when Daylight Saving Time begins in the EU (last
+ Sunday in March)
+ type: yearly
+ frequency: 1
+ until: 0
+ weekdays: -1Sun
+ months: 3
+ monthdays: (empty)
+
+ o an event that occurs on the penultimate day of February forever
+ type: monthly
+ frequency: 1
+ until: 0
+ weekdays: (empty)
+ months: 2
+ monthdays: -2
+
+For an example that is not possible to specify consider an event that occurs
+on the last workday of each month.
diff --git a/src/ui-day.c b/src/ui-day.c
index e359c82..5a8d960 100644
--- a/src/ui-day.c
+++ b/src/ui-day.c
@@ -486,73 +486,170 @@ cleanup:
return updated;
}
+static void help_ilist(int_list_t list, int rule)
+{
+ char *msg1 = "";
+ char *msg2 = "";
+ char *byday_w_d = _("only on these weekdays");
+ char *byday_w_w = _("also on these weekdays");
+ char *byday_m_m_1 =
+ _("also on these weekdays of month - or only on given monthdays");
+ char *byday_m_m_2 =
+ _("either all weekdays or 1st, 2nd, ... weekday or 1st, 2nd, ... from the end");
+ char *byday_y_y_1 =
+ _("also on these weekdays of year or given months - or only on given monthdays");
+ char *byday_y_y_2 =
+ _("positive: 1st, 2nd,... weekday of month or year, negative: 1st, 2nd,... from end");
+ char *bymonth_dwm =
+ _("only in these months");
+ char *bymonth_y =
+ _("also in these months");
+ char *bymonthday_d = _("only on these days of the month");
+ char *bymonthday_my = _("also on these days of the month");
+
+
+ switch (list) {
+ case BYDAY_W:
+ switch (rule) {
+ case RECUR_DAILY:
+ msg1 = byday_w_d;
+ msg2 = "";
+ break;
+ case RECUR_WEEKLY:
+ msg1 = byday_w_w;
+ msg2 = "";
+ break;
+ default:
+ EXIT("internal inconsistency");
+ }
+ break;
+ case BYDAY_M:
+ switch (rule) {
+ case RECUR_MONTHLY:
+ msg1 = byday_m_m_1;
+ msg2 = byday_m_m_2;
+ break;
+ default:
+ EXIT("internal inconsistency");
+ }
+ break;
+ case BYDAY_Y:
+ switch (rule) {
+ case RECUR_YEARLY:
+ msg1 = byday_y_y_1;
+ msg2 = byday_y_y_2;
+ break;
+ default:
+ EXIT("internal inconsistency");
+ }
+ break;
+ case BYMONTH:
+ switch (rule) {
+ case RECUR_DAILY:
+ case RECUR_WEEKLY:
+ case RECUR_MONTHLY:
+ msg1 = bymonth_dwm;
+ msg2 = "";
+ break;
+ case RECUR_YEARLY:
+ msg1 = bymonth_y;
+ msg2 = "";
+ break;
+ default:
+ break;
+ }
+ break;
+ case BYMONTHDAY:
+ switch (rule) {
+ case RECUR_DAILY:
+ msg1 = bymonthday_d;
+ msg2 = "";
+ break;
+ case RECUR_MONTHLY:
+ case RECUR_YEARLY:
+ msg1 = bymonthday_my;
+ msg2 = "";
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ status_mesg(msg1, msg2);
+ keys_wgetch(win[KEY].p);
+}
+
/* Edit an rrule (linked) list of integers. */
-static int edit_ilist(llist_t *ilist, int_list_t type)
+static int edit_ilist(llist_t *ilist, int_list_t list_type, int rule_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.");
+ char *wday = NULL;
+ char *wday_w = _("Weekdays (%s|..|%s), '?' for help:");
+ char *wday_m =
+ _("Weekdays (%1$s|..|%2$s or 1%1$s|..|5%2$s or -1%1$s|..|-5%2$s), '?' for help:");
+ char *wday_y =
+ _("Weekdays (%1$s|..|%2$s or 1%1$s|..|53%2$s or -1%1$s|..|-53%2$s), '?' for help:");
+ char *month = _("Months (1..12), '?' for help:");
+ char *mday = _("Monthdays (1..31 or -1..-31), '?' for help:");
+ char *invalid = _("Invalid format - try again.");
+ char *cont = _("Press any key to continue.");
int updated = 0;
- if (type == NOLL)
+ if (list_type == NOLL)
return !updated;
char *istr;
enum getstr ret;
- switch (type) {
+ switch (list_type) {
case BYDAY_W:
- asprintf(&msg_wday, msg_format_w,
+ asprintf(&wday, wday_w,
nl_langinfo(ABDAY_2), nl_langinfo(ABDAY_1));
- msg = msg_wday;
+ msg = wday;
break;
case BYDAY_M:
- asprintf(&msg_wday, msg_format_m,
+ asprintf(&wday, wday_m,
nl_langinfo(ABDAY_2), nl_langinfo(ABDAY_1));
- msg = msg_wday;
+ msg = wday;
break;
case BYDAY_Y:
- asprintf(&msg_wday, msg_format_y,
+ asprintf(&wday, wday_y,
nl_langinfo(ABDAY_2), nl_langinfo(ABDAY_1));
- msg = msg_wday;
+ msg = wday;
break;
case BYMONTH:
- msg = msg_month;
+ msg = month;
break;
case BYMONTHDAY:
- msg = msg_mday;
+ msg = mday;
break;
default:
msg = NULL;
break;
}
status_mesg(msg, "");
- istr = int2str(ilist, type);
+ istr = int2str(ilist, list_type);
while (1) {
ret = updatestring(win[STA].p, &istr, 0, 1);
if (ret == GETSTRING_VALID || ret == GETSTRING_RET) {
- if (str2int(ilist, istr, type)) {
+ if (*(istr + strlen(istr) - 1) == '?')
+ help_ilist(list_type, rule_type);
+ else if (str2int(ilist, istr, list_type)) {
updated = 1;
break;
} else {
- status_mesg(msg_invalid, msg_cont);
+ status_mesg(invalid, cont);
keys_wgetch(win[KEY].p);
}
mem_free(istr);
status_mesg(msg, "");
- istr = int2str(ilist, type);
+ istr = int2str(ilist, list_type);
} else if (ret == GETSTRING_ESC)
break;
}
mem_free(istr);
- mem_free(msg_wday);
+ mem_free(wday);
return updated;
}
@@ -740,18 +837,18 @@ static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
break;
}
recur_int_list_dup(&nrpt.bywday, &(*rpt)->bywday);
- if (!edit_ilist(&nrpt.bywday, byday_type))
+ if (!edit_ilist(&nrpt.bywday, byday_type, nrpt.type))
goto cleanup;
/* Edit BYMONTH list. */
recur_int_list_dup(&nrpt.bymonth, &(*rpt)->bymonth);
- if (!edit_ilist(&nrpt.bymonth, BYMONTH))
+ if (!edit_ilist(&nrpt.bymonth, BYMONTH, nrpt.type))
goto cleanup;
/* Edit BYMONTHDAY list. */
if (nrpt.type != RECUR_WEEKLY) {
recur_int_list_dup(&nrpt.bymonthday, &(*rpt)->bymonthday);
- if (!edit_ilist(&nrpt.bymonthday, BYMONTHDAY))
+ if (!edit_ilist(&nrpt.bymonthday, BYMONTHDAY, nrpt.type))
goto cleanup;
}