From 0bb4a59b5fb089062f0eb369d39289ff1ccfc2ba Mon Sep 17 00:00:00 2001 From: Lars Henriksen Date: Thu, 27 Dec 2018 12:08:25 +0100 Subject: Add week numbers in the calendar and full first and last week Much in the calendar is based on the selected day, struct date slctd_day, in ui-calendar.c. On the screen it is highlighted with a deviating colour. The highlight effect has been changed to a pair of red square brackets that do not obscure the day colour. The week number (in the frame) used to be that of the selected day, but has no obvious relation to the days in the APP panel. It has been replaced by the year day number of the selected day. The week numbers of all visible weeks are displayed to the left of the calendar. Dates are displayed also for the overlapping parts of the first and last week of the month (which do not belong to the month). Days are accessible in the appointments panel as well as in the calendar. Hence, validation of days (= inside UNIX time limits) must be extended from the calendar (in ui_calendar_move()) to include loaded days (in day_store_items()). Signed-off-by: Lars Henriksen Signed-off-by: Lukas Fleischer --- src/calcurse.h | 3 +- src/custom.c | 1 + src/day.c | 19 +++-- src/ui-calendar.c | 220 +++++++++++++++++++++++++++++++++--------------------- src/utils.c | 18 +++++ 5 files changed, 165 insertions(+), 96 deletions(-) diff --git a/src/calcurse.h b/src/calcurse.h index e4cfe79..afe4430 100644 --- a/src/calcurse.h +++ b/src/calcurse.h @@ -145,8 +145,6 @@ #define ENDOFDAY(date) (NEXTDAY(date) - 1) #define HOURINSEC (HOURINMIN * MININSEC) -#define MAXDAYSPERMONTH 31 - /* Calendar window. */ #define CALHEIGHT 8 @@ -1176,6 +1174,7 @@ int get_item_min(time_t); struct tm date2tm(struct date, unsigned, unsigned); time_t date2sec(struct date, unsigned, unsigned); time_t utcdate2sec(struct date, unsigned, unsigned); +int date_cmp(struct date *, struct date *); int date_cmp_day(time_t, time_t); char *date_sec2date_str(time_t, const char *); void date_sec2date_fmt(time_t, const char *, char *); diff --git a/src/custom.c b/src/custom.c index f0c99b4..f3498b8 100644 --- a/src/custom.c +++ b/src/custom.c @@ -800,6 +800,7 @@ static void general_option_edit(int i) break; case FIRST_DAY_OF_WEEK: ui_calendar_change_first_day_of_week(); + ui_calendar_monthly_view_cache_set_invalid(); break; case OUTPUT_DATE_FMT: status_mesg(output_datefmt_str, ""); diff --git a/src/day.c b/src/day.c index c340988..c88713c 100644 --- a/src/day.c +++ b/src/day.c @@ -437,6 +437,9 @@ day_store_items(time_t date, int include_captions, int n) day_init_vector(); for (i = 0; i < n; i++, date = NEXTDAY(date)) { + if (YEAR1902_2037 && !check_sec(&date)) + break; + if (include_captions) day_add_item(DAY_HEADING, 0, date, p); @@ -584,10 +587,10 @@ void day_popup_item(struct day_item *day) } /* - * Check whether there is an item on a given day. - * - * Returns 2 if the selected day contains a regular event or appointment. - * Returns 1 if the selected day does not contain a regular event or + * Check whether there is an item on a given day and return the colour + * attribute for the item: + * ATTR_TRUE if the selected day contains a regular event or appointment, + * ATTR_LOW if the selected day does not contain a regular event or * appointment but an occurrence of a recurrent item. Returns 0 otherwise. */ int day_check_if_item(struct date day) @@ -595,23 +598,23 @@ int day_check_if_item(struct date day) const time_t t = date2sec(day, 0, 0); if (LLIST_FIND_FIRST(&eventlist, (time_t *)&t, event_inday)) - return 2; + return ATTR_TRUE; LLIST_TS_LOCK(&alist_p); if (LLIST_TS_FIND_FIRST(&alist_p, (time_t *)&t, apoint_inday)) { LLIST_TS_UNLOCK(&alist_p); - return 2; + return ATTR_TRUE; } LLIST_TS_UNLOCK(&alist_p); if (LLIST_FIND_FIRST(&recur_elist, (time_t *)&t, recur_event_inday)) - return 1; + return ATTR_LOW; LLIST_TS_LOCK(&recur_alist_p); if (LLIST_TS_FIND_FIRST(&recur_alist_p, (time_t *)&t, recur_apoint_inday)) { LLIST_TS_UNLOCK(&recur_alist_p); - return 1; + return ATTR_LOW; } LLIST_TS_UNLOCK(&recur_alist_p); diff --git a/src/ui-calendar.c b/src/ui-calendar.c index 5b8ab1f..e4e1b4d 100644 --- a/src/ui-calendar.c +++ b/src/ui-calendar.c @@ -54,7 +54,8 @@ static void (*draw_calendar[CAL_VIEWS]) (struct scrollwin *, struct date *, unsigned) = { draw_monthly_view, draw_weekly_view}; -static int monthly_view_cache[MAXDAYSPERMONTH]; +/* Six weeks cover a month. */ +static int monthly_view_cache[WEEKINDAYS * 6]; static int monthly_view_cache_valid = 0; static int monthly_view_cache_month = 0; @@ -207,29 +208,6 @@ static int ui_calendar_get_wday(struct date *date) return t.tm_wday; } -static unsigned months_to_days(unsigned month) -{ - return (month * 3057 - 3007) / 100; -} - -static long years_to_days(unsigned year) -{ - return year * 365L + year / 4 - year / 100 + year / 400; -} - -static long ymd_to_scalar(unsigned year, unsigned month, unsigned day) -{ - long scalar; - - scalar = day + months_to_days(month); - if (month > 2) - scalar -= ISLEAP(year) ? 1 : 2; - year--; - scalar += years_to_days(year); - - return scalar; -} - void ui_calendar_monthly_view_cache_set_invalid(void) { monthly_view_cache_valid = 0; @@ -308,6 +286,29 @@ static int ISO8601weeknum(const struct tm *t) return wnum; } +/* + * Return the tm structure for the first day of the first week + * (containing a day) of the selected month. + */ +static struct tm get_first_day(unsigned sunday_first) +{ + struct tm t; + struct date d; + + d = slctd_day; + + /* get the first day of the month */ + d.dd = 1; + t = date2tm(d, 0, 0); + mktime(&t); + /* get the first day of the week */ + date_change(&t, 0, + -(sunday_first ? + t.tm_wday : + (t.tm_wday + WEEKINDAYS - 1) % WEEKINDAYS)); + return t; +} + static struct tm get_first_weekday(unsigned sunday_first) { int c_wday, days_to_remove; @@ -342,112 +343,160 @@ static void draw_monthly_view(struct scrollwin *sw, struct date *current_day, unsigned sunday_first) { - struct date check_day; - int c_day, c_day_1, day_1_sav, numdays, j; + struct date c_day; + int slctd, w_day, numdays, j, week = 0; unsigned yr, mo; - int w, ofs_x, ofs_y; - int item_this_day = 0; - struct tm t; + int w, monthw, weekw, dayw, ofs_x, ofs_y; + struct tm t, t_first; char *cp; + char bo, bc; + unsigned attr, day_attr; + int first_day, last_day; werase(sw->inner); + /* + * number of days to display + */ + first_day = last_day = 0; + /* days in the selected month */ + numdays = days[slctd_day.mm - 1]; + if (2 == slctd_day.mm && ISLEAP(slctd_day.yyyy)) + ++numdays; + /* + * Step forward by week until past the last day of the month. + * The first day of the first week may belong to the previous month. + */ + t = t_first = get_first_day(sunday_first); + t.tm_mday += WEEKINDAYS; + mktime(&t); + last_day += WEEKINDAYS; + /* following weeks */ + for (j = t.tm_mday; j <= numdays; j += WEEKINDAYS ) + last_day += WEEKINDAYS; + mo = slctd_day.mm; yr = slctd_day.yyyy; + /* a week column plus seven day columns */ + weekw = 3; + dayw = 4; + monthw = weekw + 7 * dayw; + /* offset for centering calendar in window */ w = wins_sbar_width() - 2; ofs_y = 0; - ofs_x = (w - 27) / 2; - - /* checking the number of days in february */ - numdays = days[mo - 1]; - if (2 == mo && ISLEAP(yr)) - ++numdays; + ofs_x = (w - monthw) / 2; - /* - * the first calendar day will be monday or sunday, depending on - * 'week_begins_on_monday' value - */ - c_day_1 = - (int)((ymd_to_scalar(yr, mo, 1 + sunday_first) - - (long)1) % 7L); + /* invalidate cache if a new month is selected */ + if (yr * YEARINMONTHS + mo != monthly_view_cache_month) { + monthly_view_cache_month = yr * YEARINMONTHS + mo; + monthly_view_cache_valid = 0; + } - /* Print the week number, calculated from monday. */ - t = get_first_weekday(0); - draw_week_number(sw, t); + WINS_CALENDAR_LOCK; + /* Print the day number. */ + t = date2tm(slctd_day, 0, 0); + mktime(&t); + custom_apply_attr(sw->win, ATTR_HIGHEST); + mvwprintw(sw->win, conf.compact_panels ? 0 : 2, + ofs_x + monthw - 6, + "(#%3d)", t.tm_yday + 1); + custom_remove_attr(sw->win, ATTR_HIGHEST); /* Write the current month and year on top of the calendar */ - WINS_CALENDAR_LOCK; custom_apply_attr(sw->inner, ATTR_HIGHEST); cp = nl_langinfo(MON_1 + mo - 1); mvwprintw(sw->inner, ofs_y, (w - (strlen(cp) + 5)) / 2, "%s %d", cp, slctd_day.yyyy); custom_remove_attr(sw->inner, ATTR_HIGHEST); + ++ofs_y; - /* print the days, with regards to the first day of the week */ + /* print the days with regard to the first day of the week */ custom_apply_attr(sw->inner, ATTR_HIGHEST); for (j = 0; j < WEEKINDAYS; j++) { - mvwaddstr(sw->inner, ofs_y, ofs_x + 4 * j, + mvwaddstr(sw->inner, ofs_y, ofs_x + weekw + 4 * j, nl_langinfo(ABDAY_1 + (1 + j - sunday_first) % WEEKINDAYS)); } custom_remove_attr(sw->inner, ATTR_HIGHEST); WINS_CALENDAR_UNLOCK; - day_1_sav = (c_day_1 + 1) * 3 + c_day_1 - 7; + ++ofs_y; - /* invalidate cache if a new month is selected */ - if (yr * YEARINMONTHS + mo != monthly_view_cache_month) { - monthly_view_cache_month = yr * YEARINMONTHS + mo; - monthly_view_cache_valid = 0; - } + /* print the dates */ + for (j = first_day, t = t_first, w_day = 0; + j < last_day; + j++, date_change(&t, 0, 1), w_day++, w_day %= WEEKINDAYS) { + + c_day.dd = t.tm_mday; + c_day.mm = t.tm_mon + 1; + c_day.yyyy = t.tm_year + 1900; + slctd = !date_cmp(&c_day, &slctd_day); + + /* Next line, week over. */ + if (!w_day && j != first_day) { + ofs_y++; + ofs_x = (w - monthw) / 2; + } - for (c_day = 1; c_day <= numdays; ++c_day, ++c_day_1, c_day_1 %= 7) { - unsigned attr; + /* Week number, beware of first and last week of the year. */ + if (!w_day) { + if (j == first_day || + (mo == 1 && j == WEEKINDAYS) || + (mo == 12 && j >= 4 * WEEKINDAYS)) { + if (sunday_first) + date_change(&t, 0, 1); + week = ISO8601weeknum(&t); + if (sunday_first) + date_change(&t, 0, -1); + } else + week++; + } - check_day.dd = c_day; - check_day.mm = slctd_day.mm; - check_day.yyyy = slctd_day.yyyy; + /* Brackets for the selected day. */ + bo = slctd ? '[' : ' '; + bc = slctd ? ']' : ' '; /* check if the day contains an event or an appointment */ if (monthly_view_cache_valid) { - item_this_day = monthly_view_cache[c_day - 1]; + day_attr = monthly_view_cache[j]; } else { - item_this_day = monthly_view_cache[c_day - 1] = - day_check_if_item(check_day); - } - - /* Go to next line, the week is over. */ - if (!c_day_1 && 1 != c_day) { - ofs_y++; - ofs_x = (w - 27) / 2 - day_1_sav - 4 * c_day; + day_attr = monthly_view_cache[j] = + day_check_if_item(c_day); } - if (c_day == current_day->dd - && current_day->mm == slctd_day.mm - && current_day->yyyy == slctd_day.yyyy - && current_day->dd != slctd_day.dd) + /* Set day colours. */ + if (date_cmp(&c_day, current_day) == 0) attr = ATTR_LOWEST; - else if (c_day == slctd_day.dd) - attr = ATTR_MIDDLE; - else if (item_this_day == 1) - attr = ATTR_LOW; - else if (item_this_day == 2) - attr = ATTR_TRUE; else - attr = 0; + attr = day_attr; WINS_CALENDAR_LOCK; + /* Print week number. */ + if (!w_day) { + custom_apply_attr(sw->inner, ATTR_HIGHEST); + mvwprintw(sw->inner, ofs_y, ofs_x, "%2d", week); + custom_remove_attr(sw->inner, ATTR_HIGHEST); + } + /* Print date with attributes.*/ if (attr) custom_apply_attr(sw->inner, attr); - mvwprintw(sw->inner, ofs_y + 1, - ofs_x + day_1_sav + 4 * c_day + 1, "%2d", c_day); + mvwprintw(sw->inner, ofs_y, ofs_x + weekw + w_day * 4, + "%c%2d%c", bo, c_day.dd, bc); if (attr) custom_remove_attr(sw->inner, attr); + /* Colour the brackets. */ + if (slctd) { + mvwchgat(sw->inner, ofs_y, ofs_x + weekw + w_day * 4, + 1, A_BOLD, + (colorize ? (COLR_RED | A_BOLD) : 0), NULL); + mvwchgat(sw->inner, ofs_y, ofs_x + weekw + 3 + w_day * 4, + 1, A_BOLD, + (colorize ? (COLR_RED | A_BOLD) : 0), NULL); + } WINS_CALENDAR_UNLOCK; } - monthly_view_cache_valid = 1; } @@ -704,10 +753,9 @@ void ui_calendar_move(enum move move, int count) ret = 1; /* NOTREACHED */ } - if (ret == 1 || (YEAR1902_2037 && t.tm_year < 2) - || (YEAR1902_2037 && t.tm_year > 137)) { - char *out, *msg = _("The move failed (%d/%d/%d, ret=%d)."), ch; - asprintf(&out, msg, t.tm_mday, t.tm_mon + 1, t.tm_year + 1900, ret); + if (ret || !check_date(t.tm_year + 1900, t.tm_mon + 1, t.tm_mday)) { + char *out, *msg = _("The move failed (%d/%d/%d)."), ch; + asprintf(&out, msg, t.tm_mday, t.tm_mon + 1, t.tm_year + 1900); do { status_mesg(out, _("Press [ENTER] to continue")); ch = keys_wgetch(win[KEY].p); diff --git a/src/utils.c b/src/utils.c index fd47de6..8bd72e6 100644 --- a/src/utils.c +++ b/src/utils.c @@ -432,6 +432,24 @@ time_t utcdate2sec(struct date day, unsigned hour, unsigned min) return t; } +/* Compare two calcurse dates. */ +int date_cmp(struct date *d1, struct date *d2) +{ + if (d1->yyyy < d2->yyyy) + return -1; + if (d1->yyyy > d2->yyyy) + return 1; + if (d1->mm < d2->mm) + return -1; + if (d1->mm > d2->mm) + return 1; + if (d1->dd < d2->dd) + return -1; + if (d1->dd > d2->dd) + return 1; + return 0; +} + /* Compare two dates (without comparing times). */ int date_cmp_day(time_t d1, time_t d2) { -- cgit v1.2.3-70-g09d2