diff options
Diffstat (limited to 'src/ui-day.c')
-rw-r--r-- | src/ui-day.c | 270 |
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); |