diff options
-rw-r--r-- | src/calcurse.h | 3 | ||||
-rw-r--r-- | src/custom.c | 1 | ||||
-rw-r--r-- | src/day.c | 19 | ||||
-rw-r--r-- | src/ui-calendar.c | 220 | ||||
-rw-r--r-- | 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, ""); @@ -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) { |