Allow repeat count input (for until) in interactive UI

Shortened repetition type text.

Avoid "duration" in until-contexts (reserve it for appointment duration):
"duration" changed to "increment" in user texts as well as source.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
This commit is contained in:
Lars Henriksen 2020-05-29 15:13:28 +02:00 committed by Lukas Fleischer
parent 382e60ba69
commit 223f722b1d
3 changed files with 48 additions and 18 deletions

View File

@ -1246,7 +1246,7 @@ int check_sec(time_t *);
int check_time(unsigned, unsigned); int check_time(unsigned, unsigned);
int parse_time(const char *, unsigned *, unsigned *); int parse_time(const char *, unsigned *, unsigned *);
int parse_duration(const char *, unsigned *, time_t); int parse_duration(const char *, unsigned *, time_t);
int parse_date_duration(const char *, unsigned *, time_t); int parse_date_increment(const char *, unsigned *, time_t);
int parse_datetime(const char *, time_t *, time_t); int parse_datetime(const char *, time_t *, time_t);
void file_close(FILE *, const char *); void file_close(FILE *, const char *);
void psleep(unsigned); void psleep(unsigned);

View File

@ -658,8 +658,9 @@ static int edit_ilist(llist_t *ilist, int_list_t list_type, int rule_type)
static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc, static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
int simple) int simple)
{ {
int updated = 0; int updated = 0, count;
struct rpt nrpt; struct rpt nrpt;
time_t until;
char *types = NULL; char *types = NULL;
char *freqstr = NULL; char *freqstr = NULL;
char *timstr = NULL; char *timstr = NULL;
@ -745,16 +746,19 @@ static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
/* Edit until date. */ /* Edit until date. */
const char *msg_until_1 = const char *msg_until_1 =
_("Until date or duration ('?' for input formats):"); _("Until date, increment or repeat count ('?' for input formats):");
const char *msg_help_1 = const char *msg_help_1 =
_("Date: %s (year or month may be omitted). Endless duration: 0."); _("Date: %s (year, month may be omitted, endless: 0).");
const char *msg_help_2 = const char *msg_help_2 =
_("Duration in days: +dd. Duration in weeks and days: +??w??d."); _("Increment: +?? (days) or: +??w??d (weeks). "
"Repeat count: #?? (number).");
const char *msg_inv_until = const char *msg_inv_until =
_("Invalid date: until date must come after start date (%s)."); _("Invalid date: until date must come after start date (%s).");
const char *msg_inv_date = _("Invalid date."); const char *msg_inv_date = _("Invalid date.");
const char *msg_count = _("Repeat count is too big.");
for (;;) { for (;;) {
count = 0;
mem_free(timstr); mem_free(timstr);
if ((*rpt)->until) if ((*rpt)->until)
timstr = date_sec2date_str((*rpt)->until, DATEFMT(conf.input_datefmt)); timstr = date_sec2date_str((*rpt)->until, DATEFMT(conf.input_datefmt));
@ -776,7 +780,7 @@ static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
} }
if (*timstr == '+') { if (*timstr == '+') {
unsigned days; unsigned days;
if (!parse_date_duration(timstr + 1, &days, start)) { if (!parse_date_increment(timstr + 1, &days, start)) {
status_mesg(msg_inv_date, msg_cont); status_mesg(msg_inv_date, msg_cont);
keys_wgetch(win[KEY].p); keys_wgetch(win[KEY].p);
continue; continue;
@ -786,6 +790,20 @@ static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
update_time_in_date(start, 0, 0), update_time_in_date(start, 0, 0),
0, days 0, days
); );
} else if (*timstr == '#') {
char *eos;
count = strtol(timstr + 1, &eos, 10);
if (*eos || !(count > 0))
continue;
nrpt.until = 0;
if (!recur_nth_occurrence(start, dur, &nrpt, exc,
count, &until)) {
status_mesg(msg_count, msg_cont);
keys_wgetch(win[KEY].p);
continue;
}
nrpt.until = update_time_in_date(until, 0, 0);
break;
} else { } else {
int year, month, day; int year, month, day;
if (!parse_date(timstr, conf.input_datefmt, &year, if (!parse_date(timstr, conf.input_datefmt, &year,
@ -857,6 +875,17 @@ static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
goto cleanup; goto cleanup;
} }
/* The new until may no longer be valid. */
if (count) {
nrpt.until = 0;
if (!recur_nth_occurrence(start, dur, &nrpt, exc,
count, &until)) {
status_mesg(msg_count, msg_cont);
keys_wgetch(win[KEY].p);
goto cleanup;
}
nrpt.until = update_time_in_date(until, 0, 0);
}
/* /*
* Check whether the start occurrence matches the recurrence rule, in * Check whether the start occurrence matches the recurrence rule, in
* other words, does it occur on the start day? This is required by * other words, does it occur on the start day? This is required by
@ -864,7 +893,8 @@ static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
* is an exception day). * is an exception day).
*/ */
char *msg_match = char *msg_match =
_("Repetition must begin on start day (%s); any change discarded."); _("Repetition must begin on start day (%s); "
"any change discarded.");
if (!recur_item_find_occurrence(start, dur, &nrpt, NULL, if (!recur_item_find_occurrence(start, dur, &nrpt, NULL,
update_time_in_date(start, 0, 0), update_time_in_date(start, 0, 0),
NULL)) { NULL)) {

View File

@ -990,11 +990,11 @@ parse_date_interactive(const char *datestr, int *year, int *month, int *day)
} }
/* /*
* Convert a date duration string into a number of days. * Convert a date increment string into a number of days.
* If start is non-zero, the final end time is validated. * If start is non-zero, the final end time is validated.
* *
* Allowed formats in lenient BNF: * Allowed formats in lenient BNF:
* <duration> ::= <days> | <period> * <increment>::= <days> | <period>
* <period> ::= [ <weeks>w ][ <days>d ] * <period> ::= [ <weeks>w ][ <days>d ]
* Notes: * Notes:
* <days> and <weeks> are any integer >= 0. * <days> and <weeks> are any integer >= 0.
@ -1002,7 +1002,7 @@ parse_date_interactive(const char *datestr, int *year, int *month, int *day)
* *
* Returns 1 on success and 0 on failure. * Returns 1 on success and 0 on failure.
*/ */
int parse_date_duration(const char *string, unsigned *days, time_t start) int parse_date_increment(const char *string, unsigned *days, time_t start)
{ {
enum { enum {
STATE_INITIAL, STATE_INITIAL,
@ -1012,7 +1012,7 @@ int parse_date_duration(const char *string, unsigned *days, time_t start)
const char *p; const char *p;
unsigned in = 0, frac = 0, denom = 1; unsigned in = 0, frac = 0, denom = 1;
unsigned dur = 0; unsigned incr = 0;
if (!string || *string == '\0') if (!string || *string == '\0')
return 0; return 0;
@ -1033,10 +1033,10 @@ int parse_date_duration(const char *string, unsigned *days, time_t start)
switch (state) { switch (state) {
case STATE_INITIAL: case STATE_INITIAL:
if (*p == 'w') { if (*p == 'w') {
dur += in * WEEKINDAYS / denom; incr += in * WEEKINDAYS / denom;
state = STATE_WWDD_DD; state = STATE_WWDD_DD;
} else if (*p == 'd') { } else if (*p == 'd') {
dur += in / denom; incr += in / denom;
state = STATE_DONE; state = STATE_DONE;
} else { } else {
return 0; return 0;
@ -1044,7 +1044,7 @@ int parse_date_duration(const char *string, unsigned *days, time_t start)
break; break;
case STATE_WWDD_DD: case STATE_WWDD_DD:
if (*p == 'd') { if (*p == 'd') {
dur += in / denom; incr += in / denom;
state = STATE_DONE; state = STATE_DONE;
} else { } else {
return 0; return 0;
@ -1060,18 +1060,18 @@ int parse_date_duration(const char *string, unsigned *days, time_t start)
} }
if (state == STATE_DONE && in > 0) if (state == STATE_DONE && in > 0)
return 0; return 0;
dur += in; incr += in;
if (start) { if (start) {
/* wanted: start = start + dur * DAYINSEC */ /* wanted: start = start + incr * DAYINSEC */
long p; long p;
if (overflow_mul(dur, DAYINSEC, &p)) if (overflow_mul(incr, DAYINSEC, &p))
return 0; return 0;
if (overflow_add(start, p, &start)) if (overflow_add(start, p, &start))
return 0; return 0;
if (!check_sec(&start)) if (!check_sec(&start))
return 0; return 0;
} }
*days = dur; *days = incr;
return 1; return 1;
} }