aboutsummaryrefslogtreecommitdiffstats
path: root/src/ui-day.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui-day.c')
-rw-r--r--src/ui-day.c270
1 files changed, 155 insertions, 115 deletions
diff --git a/src/ui-day.c b/src/ui-day.c
index 5a8d960..6a038fa 100644
--- a/src/ui-day.c
+++ b/src/ui-day.c
@@ -1,7 +1,7 @@
/*
* Calcurse - text-based organizer
*
- * Copyright (c) 2004-2020 calcurse Development Team <misc@calcurse.org>
+ * Copyright (c) 2004-2023 calcurse Development Team <misc@calcurse.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -79,7 +79,7 @@ void ui_day_find_sel(void)
*/
time_t ui_day_sel_date(void)
{
- return update_time_in_date(ui_day_get_sel()->order, 0, 0);
+ return DAY(ui_day_get_sel()->order);
}
/*
@@ -175,8 +175,8 @@ static void update_start_time(time_t *start, long *dur, struct rpt *rpt, int mov
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.");
+ char *msg_match =
+ _("Repetition must begin on start day (%s).");
const char *msg_enter = _("Press [Enter] to continue");
char *msg;
@@ -184,11 +184,12 @@ static void update_start_time(time_t *start, long *dur, struct rpt *rpt, int mov
newtime = day_edit_time(*start, *dur, move);
if (!newtime)
break;
- if (rpt && !recur_item_find_occurrence(
- newtime, *dur, rpt, NULL,
- update_time_in_date(newtime, 0, 0),
- NULL)) {
- msg = (char *)msg_match;
+ if (rpt && !recur_item_find_occurrence(newtime, *dur, rpt, NULL,
+ DAY(newtime),
+ NULL)) {
+ msg = day_ins(&msg_match, newtime);
+ status_mesg(msg, msg_enter);
+ mem_free(msg);
} else {
if (move) {
*start = newtime;
@@ -200,9 +201,8 @@ static void update_start_time(time_t *start, long *dur, struct rpt *rpt, int mov
break;
}
}
- msg = (char *)msg_wrong_time;
+ status_mesg(msg_wrong_time, msg_enter);
}
- status_mesg(msg, msg_enter);
keys_wgetch(win[KEY].p);
}
return;
@@ -490,22 +490,22 @@ 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_w_d = _("Limit repetition to listed days.");
+ char *byday_w_w = _("Expand repetition to listed days.");
char *byday_m_m_1 =
- _("also on these weekdays of month - or only on given monthdays");
+ _("Expand repetition to listed days, either all or 1st, 2nd, ... of month.");
char *byday_m_m_2 =
- _("either all weekdays or 1st, 2nd, ... weekday or 1st, 2nd, ... from the end");
+ _("Note: limit to monthdays, if any.");
char *byday_y_y_1 =
- _("also on these weekdays of year or given months - or only on given monthdays");
+ _("Expand repetition to listed days, either all or 1st, 2nd, ... of year.");
char *byday_y_y_2 =
- _("positive: 1st, 2nd,... weekday of month or year, negative: 1st, 2nd,... from end");
+ _("Note: expand to listed months, if any; limit to monthdays, if any.");
char *bymonth_dwm =
- _("only in these months");
+ _("Limit repetition to listed 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");
+ _("Expand repetition to listed months.");
+ char *bymonthday_d = _("Limit repetition to listed days of month.");
+ char *bymonthday_my = _("Expand repetition to listed days of month.");
switch (list) {
@@ -586,13 +586,13 @@ static int edit_ilist(llist_t *ilist, int_list_t list_type, int rule_type)
{
char *msg;
char *wday = NULL;
- char *wday_w = _("Weekdays (%s|..|%s), '?' for help:");
+ char *wday_w = _("Weekdays %s|..|%s, space-separated list, '?' 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:");
+ _("Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,5,-5, '?' 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:");
+ _("Weekdays [n]%s|..|[n]%s, space-separated list, n=1,-1,..,53,-53, '?' for help:");
+ char *month = _("Months 1|..|12, space-separated list, '?' for help:");
+ char *mday = _("Monthdays 1|..|31 or -1|..|-31, space-separated list, '?' for help:");
char *invalid = _("Invalid format - try again.");
char *cont = _("Press any key to continue.");
int updated = 0;
@@ -657,8 +657,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,
int simple)
{
- int updated = 0;
+ int updated = 0, count;
struct rpt nrpt;
+ time_t until;
char *types = NULL;
char *freqstr = NULL;
char *timstr = NULL;
@@ -671,11 +672,11 @@ static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
LLIST_INIT(&nrpt.bymonthday);
/* Edit repetition type. */
- const char *msg_prefix = _("Repetition type:");
- const char *daily = _("(d)aily");
- const char *weekly = _("(w)eekly");
- const char *monthly = _("(m)onthly");
- const char *yearly = _("(y)early");
+ const char *msg_prefix = _("Base period:");
+ const char *daily = _("day");
+ const char *weekly = _("week");
+ const char *monthly = _("month");
+ const char *yearly = _("year");
const char *dwmy = _("[dwmy]");
/* Find the current repetition type. */
@@ -697,30 +698,34 @@ static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
/* New item. */
current = "";
}
- asprintf(&types, "%s %s, %s, %s, %s?",
+ asprintf(&types, "%s %s/%s/%s/%s?",
msg_prefix, daily, weekly, monthly, yearly);
if (current[0])
- asprintf(&types, "%s (now %s)", types, current);
+ asprintf(&types, "%s [%s]", types, current);
switch (status_ask_choice(types, dwmy, 4)) {
case 1:
- nrpt.type = 'D';
+ nrpt.type = recur_char2def('D');
break;
case 2:
- nrpt.type = 'W';
+ nrpt.type = recur_char2def('W');
break;
case 3:
- nrpt.type = 'M';
+ nrpt.type = recur_char2def('M');
break;
case 4:
- nrpt.type = 'Y';
+ nrpt.type = recur_char2def('Y');
break;
+ case -2: /* user typed RETURN */
+ if (current[0]) {
+ nrpt.type = (*rpt)->type;
+ break;
+ }
default:
goto cleanup;
}
- nrpt.type = recur_char2def(nrpt.type);
/* Edit frequency. */
- const char *msg_freq = _("Repetition frequency:");
+ const char *msg_freq = _("Frequency:");
const char *msg_inv_freq = _("Invalid frequency.");
do {
status_mesg(msg_freq, "");
@@ -740,16 +745,19 @@ static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
/* Edit until date. */
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 =
- _("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 =
- _("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 =
_("Invalid date: until date must come after start date (%s).");
const char *msg_inv_date = _("Invalid date.");
+ const char *msg_count = _("Repeat count is too big.");
for (;;) {
+ count = 0;
mem_free(timstr);
if ((*rpt)->until)
timstr = date_sec2date_str((*rpt)->until, DATEFMT(conf.input_datefmt));
@@ -771,16 +779,27 @@ static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
}
if (*timstr == '+') {
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);
keys_wgetch(win[KEY].p);
continue;
}
/* Until is midnight of the day. */
- nrpt.until = date_sec_change(
- update_time_in_date(start, 0, 0),
- 0, days
- );
+ nrpt.until = date_sec_change(DAY(start), 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 = DAY(until);
+ break;
} else {
int year, month, day;
if (!parse_date(timstr, conf.input_datefmt, &year,
@@ -793,7 +812,7 @@ static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
nrpt.until = date2sec(d, 0, 0);
}
/* Conmpare days (midnights) - until-day may equal start day. */
- if (nrpt.until >= update_time_in_date(start, 0, 0))
+ if (nrpt.until >= DAY(start))
break;
mem_free(timstr);
@@ -852,18 +871,31 @@ static int update_rept(time_t start, long dur, struct rpt **rpt, llist_t *exc,
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 = DAY(until);
+ }
/*
* 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),
+ char *msg_match =
+ _("Repetition must begin on start day (%s); "
+ "any change discarded.");
+ if (!recur_item_find_occurrence(start, dur, &nrpt, NULL, DAY(start),
NULL)) {
- status_mesg(msg_match, msg_cont);
+ mem_free(outstr);
+ outstr = day_ins(&msg_match, start);
+ status_mesg(outstr, msg_cont);
keys_wgetch(win[KEY].p);
goto cleanup;
}
@@ -1035,7 +1067,7 @@ void ui_day_item_pipe(void)
return;
wins_prepare_external();
- if ((pid = shell_exec(NULL, &pout, *arg, arg))) {
+ if ((pid = shell_exec(NULL, &pout, NULL, 0, *arg, arg))) {
fpout = fdopen(pout, "w");
switch (p->type) {
@@ -1056,7 +1088,7 @@ void ui_day_item_pipe(void)
}
fclose(fpout);
- child_wait(NULL, &pout, pid);
+ child_wait(NULL, &pout, NULL, pid);
press_any_key();
}
wins_unprepare_external();
@@ -1192,77 +1224,85 @@ void ui_day_item_add(void)
/* Delete an item from the appointment list. */
void ui_day_item_delete(unsigned reg)
{
- const char *del_app_str =
- _("Do you really want to delete this item?");
-
- const char *erase_warning =
- _("This item is recurrent. "
- "Delete (a)ll occurences or just this (o)ne?");
- const char *erase_choices = _("[ao]");
- const int nb_erase_choices = 2;
-
- const char *note_warning =
- _("This item has a note attached to it. "
- "Delete (i)tem or just its (n)ote?");
- const char *note_choices = _("[in]");
- const int nb_note_choices = 2;
+ const char *msg, *choices;
+ int nb_choices;
+
time_t occurrence;
if (day_item_count(0) <= 0)
return;
struct day_item *p = ui_day_get_sel();
-
- if (conf.confirm_delete) {
- if (status_ask_bool(del_app_str) != 1) {
- wins_erase_status_bar();
- return;
- }
+ int has_note = (day_item_get_note(p) != NULL);
+ int is_recur = (p->type == RECUR_EVNT || p->type == RECUR_APPT);
+
+ if (has_note && is_recur) {
+ msg = _("This item is recurrent and has a note attached to it. "
+ "Delete (s)elected occurrence, (a)ll occurrences, "
+ "or just its (n)ote?");
+ choices = _("[san]");
+ nb_choices = 3;
+ } else if (has_note) {
+ msg = _("This item has a note attached to it. "
+ "Delete (s)elected occurrence or just its (n)ote?");
+ choices = _("[sn]");
+ nb_choices = 2;
+ } else if (is_recur) {
+ msg = _("This item is recurrent. "
+ "Delete (s)elected occurrence or (a)ll occurrences?");
+ choices = _("[sa]");
+ nb_choices = 2;
+ } else {
+ msg = _("Confirm deletion. "
+ "Delete (s)elected occurrence? Press (s) to confirm.");
+ choices = _("[s]");
+ nb_choices = 1;
}
- if (day_item_get_note(p)) {
- switch (status_ask_choice
- (note_warning, note_choices, nb_note_choices)) {
- case 1:
- break;
- case 2:
- day_item_erase_note(p);
- io_set_modified();
- return;
- default: /* User escaped */
- return;
- }
+ int answer = 1;
+ if (nb_choices > 1 || conf.confirm_delete) {
+ answer = status_ask_choice(msg, choices, nb_choices);
}
- if (p->type == RECUR_EVNT || p->type == RECUR_APPT) {
- switch (status_ask_choice
- (erase_warning, erase_choices, nb_erase_choices)) {
- case 1:
- break;
- case 2:
- if (p->type == RECUR_EVNT) {
- day_item_add_exc(p, ui_day_sel_date());
- } else {
- recur_apoint_find_occurrence(p->item.rapt,
- ui_day_sel_date(),
- &occurrence);
- day_item_add_exc(p, occurrence);
- }
+ /* Always map "all occurrences" to 2 and "note" to 3. */
+ if (has_note && !is_recur && answer == 2)
+ answer = 3;
+ /*
+ * The option "selected occurrence" should be treated like "all
+ * occurrences" for a non-recurrent item (delete the whole item).
+ */
+ if (!is_recur && answer == 1)
+ answer = 2;
- io_set_modified();
- ui_calendar_monthly_view_cache_set_invalid();
- /* Keep the selection on the same day. */
- day_set_sel_data(
- day_get_item(listbox_get_sel(&lb_apt) - 1)
- );
- return;
- default:
- return;
+ switch (answer) {
+ case 1:
+ /* Delete selected occurrence (of a recurrent item) only. */
+ if (p->type == RECUR_EVNT) {
+ day_item_add_exc(p, ui_day_sel_date());
+ } else {
+ recur_apoint_find_occurrence(p->item.rapt,
+ ui_day_sel_date(),
+ &occurrence);
+ day_item_add_exc(p, occurrence);
}
+ /* Keep the selection on the same day. */
+ day_set_sel_data(day_get_item(listbox_get_sel(&lb_apt) - 1));
+ break;
+ case 2:
+ /* Delete all occurrences (or a non-recurrent item). */
+ ui_day_item_cut(reg);
+ /* Keep the selection on the same day. */
+ day_set_sel_data(day_get_item(listbox_get_sel(&lb_apt) - 1));
+ break;
+ case 3:
+ /* Delete note. */
+ day_item_erase_note(p);
+ break;
+ default:
+ /* User escaped, do nothing. */
+ return;
}
- ui_day_item_cut(reg);
- /* Keep the selection on the same day. */
- day_set_sel_data(day_get_item(listbox_get_sel(&lb_apt) - 1));
+
io_set_modified();
ui_calendar_monthly_view_cache_set_invalid();
}
@@ -1480,7 +1520,7 @@ void ui_day_draw(int n, WINDOW *win, int y, int hilt, void *cb_data)
{
struct day_item *item = day_get_item(n);
/* The item order always indicates the date. */
- time_t date = update_time_in_date(item->order, 0, 0);
+ time_t date = DAY(item->order);
int width = lb_apt.sw.w - 2, is_slctd;
hilt = hilt && (wins_slctd() == APP);