diff options
Diffstat (limited to 'base/dialog/calendar.c')
-rw-r--r-- | base/dialog/calendar.c | 645 |
1 files changed, 645 insertions, 0 deletions
diff --git a/base/dialog/calendar.c b/base/dialog/calendar.c new file mode 100644 index 000000000..92936e698 --- /dev/null +++ b/base/dialog/calendar.c @@ -0,0 +1,645 @@ +/* + * $Id: calendar.c,v 1.54 2008/06/21 12:36:41 tom Exp $ + * + * calendar.c -- implements the calendar box + * + * Copyright 2001-2007,2008 Thomas E. Dickey + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License, version 2.1 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to + * Free Software Foundation, Inc. + * 51 Franklin St., Fifth Floor + * Boston, MA 02110, USA. + */ + +#include <dialog.h> +#include <dlg_keys.h> + +#include <time.h> + +#define ONE_DAY (60 * 60 * 24) + +#define MON_WIDE 4 /* width of a month-name */ +#define DAY_HIGH 6 /* maximum lines in day-grid */ +#define DAY_WIDE (8 * MON_WIDE) /* width of the day-grid */ +#define HDR_HIGH 1 /* height of cells with month/year */ +#define BTN_HIGH 1 /* height of button-row excluding margin */ + +/* two more lines: titles for day-of-week and month/year boxes */ +#define MIN_HIGH (DAY_HIGH + 2 + HDR_HIGH + BTN_HIGH + (7 * MARGIN)) +#define MIN_WIDE (DAY_WIDE + (4 * MARGIN)) + +typedef enum { + sMONTH = -3 + ,sYEAR = -2 + ,sDAY = -1 +} STATES; + +struct _box; + +typedef int (*BOX_DRAW) (struct _box *, struct tm *); + +typedef struct _box { + WINDOW *parent; + WINDOW *window; + int x; + int y; + int width; + int height; + BOX_DRAW box_draw; +} BOX; + +static int +days_in_month(struct tm *current, int offset /* -1, 0, 1 */ ) +{ + static const int nominal[] = + { + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 + }; + int year = current->tm_year; + int month = current->tm_mon + offset; + int result; + + while (month < 0) { + month += 12; + year -= 1; + } + while (month >= 12) { + month -= 12; + year += 1; + } + result = nominal[month]; + if (month == 1) + result += ((year % 4) == 0); + return result; +} + +static int +days_in_year(struct tm *current, int offset /* -1, 0, 1 */ ) +{ + int year = current->tm_year + 1900 + offset; + + return ((year % 4) == 0) ? 366 : 365; +} + +static int +day_cell_number(struct tm *current) +{ + int cell; + cell = current->tm_mday - ((6 + current->tm_mday - current->tm_wday) % 7); + if ((current->tm_mday - 1) % 7 != current->tm_wday) + cell += 6; + else + cell--; + return cell; +} + +static int +next_or_previous(int key, int two_d) +{ + int result = 0; + + switch (key) { + case DLGK_GRID_UP: + result = two_d ? -7 : -1; + break; + case DLGK_GRID_LEFT: + result = -1; + break; + case DLGK_GRID_DOWN: + result = two_d ? 7 : 1; + break; + case DLGK_GRID_RIGHT: + result = 1; + break; + default: + beep(); + break; + } + return result; +} + +/* + * Draw the day-of-month selection box + */ +static int +draw_day(BOX * data, struct tm *current) +{ +#ifdef ENABLE_NLS + char *of_week[] = + { + nl_langinfo(ABDAY_1), + nl_langinfo(ABDAY_2), + nl_langinfo(ABDAY_3), + nl_langinfo(ABDAY_4), + nl_langinfo(ABDAY_5), + nl_langinfo(ABDAY_6), + nl_langinfo(ABDAY_7) + }; +#else + static const char *const of_week[] = + { + "Dimanche", + "Lundi", + "Mardi", + "Mercredi", + "Jeudi", + "Vendredi", + "Samedi" + }; +#endif + int cell_wide = MON_WIDE; + int y, x, this_x = 0; + int save_y = 0, save_x = 0; + int day = current->tm_mday; + int mday; + int week; + int last = days_in_month(current, 0); + int prev = days_in_month(current, -1); + + werase(data->window); + dlg_draw_box(data->parent, + data->y - MARGIN, data->x - MARGIN, + data->height + (2 * MARGIN), data->width + (2 * MARGIN), + menubox_border_attr, menubox_attr); /* border of daybox */ + + wattrset(data->window, menubox_attr); /* daynames headline */ + for (x = 0; x < 7; x++) { + mvwprintw(data->window, + 0, (x + 1) * cell_wide, "%*.*s ", + cell_wide - 1, + cell_wide - 1, + of_week[x]); + } + + mday = ((6 + current->tm_mday - current->tm_wday) % 7) - 7; + if (mday <= -7) + mday += 7; + /* mday is now in the range -6 to 0. */ + week = (current->tm_yday + 6 + mday - current->tm_mday) / 7; + + for (y = 1; mday < last; y++) { + wattrset(data->window, menubox_attr); /* weeknumbers headline */ + mvwprintw(data->window, + y, 0, + "%*d ", + cell_wide - 1, + ++week); + for (x = 0; x < 7; x++) { + this_x = 1 + (x + 1) * cell_wide; + ++mday; + if (wmove(data->window, y, this_x) == ERR) + continue; + wattrset(data->window, item_attr); /* not selected days */ + if (mday == day) { + wattrset(data->window, item_selected_attr); /* selected day */ + save_y = y; + save_x = this_x; + } + if (mday > 0) { + if (mday <= last) { + wprintw(data->window, "%*d", cell_wide - 2, mday); + } else if (mday == day) { + wprintw(data->window, "%*d", cell_wide - 2, mday - last); + } + } else if (mday == day) { + wprintw(data->window, "%*d", cell_wide - 2, mday + prev); + } + } + wmove(data->window, save_y, save_x); + } + dlg_draw_arrows(data->parent, TRUE, TRUE, + data->x + ARROWS_COL, + data->y - 1, + data->y + data->height); + + return 0; +} + +/* + * Draw the month-of-year selection box + */ +static int +draw_month(BOX * data, struct tm *current) +{ +#ifdef ENABLE_NLS + char *months[] = + { + nl_langinfo(MON_1), + nl_langinfo(MON_2), + nl_langinfo(MON_3), + nl_langinfo(MON_4), + nl_langinfo(MON_5), + nl_langinfo(MON_6), + nl_langinfo(MON_7), + nl_langinfo(MON_8), + nl_langinfo(MON_9), + nl_langinfo(MON_10), + nl_langinfo(MON_11), + nl_langinfo(MON_12) + }; +#else + static const char *const months[] = + { + "Janvier", + "Fevrier", + "Mars", + "Avril", + "Mai", + "Juin", + "Juillet", + "Aout", + "Septembre", + "Octobre", + "Novembre", + "Decembre" + }; +#endif + int month; + + month = current->tm_mon + 1; + + wattrset(data->parent, dialog_attr); /* Headline "Month" */ + (void) mvwprintw(data->parent, data->y - 2, data->x - 1, _("Mois")); + dlg_draw_box(data->parent, + data->y - 1, data->x - 1, + data->height + 2, data->width + 2, + menubox_border_attr, menubox_attr); /* borders of monthbox */ + wattrset(data->window, item_attr); /* color the month selection */ + mvwprintw(data->window, 0, 0, "%s", months[month - 1]); + wmove(data->window, 0, 0); + return 0; +} + +/* + * Draw the year selection box + */ +static int +draw_year(BOX * data, struct tm *current) +{ + int year = current->tm_year + 1900; + + wattrset(data->parent, dialog_attr); /* Headline "Year" */ + (void) mvwprintw(data->parent, data->y - 2, data->x - 1, _("Année")); + dlg_draw_box(data->parent, + data->y - 1, data->x - 1, + data->height + 2, data->width + 2, + menubox_border_attr, menubox_attr); /* borders of yearbox */ + wattrset(data->window, item_attr); /* color the year selection */ + mvwprintw(data->window, 0, 0, "%4d", year); + wmove(data->window, 0, 0); + return 0; +} + +static int +init_object(BOX * data, + WINDOW *parent, + int x, int y, + int width, int height, + BOX_DRAW box_draw, + int code) +{ + data->parent = parent; + data->x = x; + data->y = y; + data->width = width; + data->height = height; + data->box_draw = box_draw; + + data->window = derwin(data->parent, + data->height, data->width, + data->y, data->x); + if (data->window == 0) + return -1; + (void) keypad(data->window, TRUE); + + dlg_mouse_setbase(getbegx(parent), getbegy(parent)); + if (code == 'D') { + dlg_mouse_mkbigregion(y + 1, x + MON_WIDE, height - 1, width - MON_WIDE, + KEY_MAX, 1, MON_WIDE, 3); + } else { + dlg_mouse_mkregion(y, x, height, width, code); + } + + return 0; +} + +static int +CleanupResult(int code, WINDOW *dialog, char *prompt, DIALOG_VARS * save_vars) +{ + if (dialog != 0) + dlg_del_window(dialog); + dlg_mouse_free_regions(); + if (prompt != 0) + free(prompt); + dlg_restore_vars(save_vars); + + return code; +} + +#define DrawObject(data) (data)->box_draw(data, ¤t) + +/* + * Display a dialog box for entering a date + */ +int +dialog_calendar(const char *title, + const char *subtitle, + int height, + int width, + int day, + int month, + int year) +{ + /* *INDENT-OFF* */ + static DLG_KEYS_BINDING binding[] = { + ENTERKEY_BINDINGS, + DLG_KEYS_DATA( DLGK_ENTER, ' ' ), + DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ), + DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ), + DLG_KEYS_DATA( DLGK_GRID_DOWN, 'j' ), + DLG_KEYS_DATA( DLGK_GRID_DOWN, KEY_DOWN ), + DLG_KEYS_DATA( DLGK_GRID_DOWN, KEY_NPAGE ), + DLG_KEYS_DATA( DLGK_GRID_DOWN, DLGK_MOUSE(KEY_NPAGE) ), + DLG_KEYS_DATA( DLGK_GRID_LEFT, 'h' ), + DLG_KEYS_DATA( DLGK_GRID_LEFT, CHR_BACKSPACE ), + DLG_KEYS_DATA( DLGK_GRID_LEFT, CHR_PREVIOUS ), + DLG_KEYS_DATA( DLGK_GRID_LEFT, KEY_LEFT ), + DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'l' ), + DLG_KEYS_DATA( DLGK_GRID_RIGHT, CHR_NEXT ), + DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_NEXT ), + DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_RIGHT ), + DLG_KEYS_DATA( DLGK_GRID_UP, 'k' ), + DLG_KEYS_DATA( DLGK_GRID_UP, KEY_PPAGE ), + DLG_KEYS_DATA( DLGK_GRID_UP, KEY_PREVIOUS ), + DLG_KEYS_DATA( DLGK_GRID_UP, KEY_UP ), + DLG_KEYS_DATA( DLGK_GRID_UP, DLGK_MOUSE(KEY_PPAGE) ), + END_KEYS_BINDING + }; + /* *INDENT-ON* */ + +#ifdef KEY_RESIZE + int old_height = height; + int old_width = width; +#endif + BOX dy_box, mn_box, yr_box; + int fkey; + int key = 0; + int key2; + int step; + int button; + int result = DLG_EXIT_UNKNOWN; + WINDOW *dialog; + time_t now_time = time((time_t *) 0); + struct tm current; + int state = dlg_defaultno_button(); + const char **buttons = dlg_ok_labels(); + char *prompt = dlg_strclone(subtitle); + int longest; + int mincols; + char buffer[MAX_LEN]; + DIALOG_VARS save_vars; + + dlg_save_vars(&save_vars); + dialog_vars.separate_output = TRUE; + + dlg_does_output(); + + now_time = time((time_t *) 0); + current = *localtime(&now_time); + if (day < 0) + day = current.tm_mday; + if (month < 0) + month = current.tm_mon + 1; + if (year < 0) + year = current.tm_year + 1900; + + /* compute a struct tm that matches the day/month/year parameters */ + if (((year -= 1900) > 0) && (year < 200)) { + /* ugly, but I'd like to run this on older machines w/o mktime -TD */ + for (;;) { + if (year > current.tm_year) { + now_time += ONE_DAY * days_in_year(¤t, 0); + } else if (year < current.tm_year) { + now_time -= ONE_DAY * days_in_year(¤t, -1); + } else if (month > current.tm_mon + 1) { + now_time += ONE_DAY * days_in_month(¤t, 0); + } else if (month < current.tm_mon + 1) { + now_time -= ONE_DAY * days_in_month(¤t, -1); + } else if (day > current.tm_mday) { + now_time += ONE_DAY; + } else if (day < current.tm_mday) { + now_time -= ONE_DAY; + } else { + break; + } + current = *localtime(&now_time); + } + } + + dlg_button_sizes(buttons, FALSE, &longest, &mincols); + mincols += (0 * MARGIN) + (dlg_button_count(buttons) * 3) - 1; + if (mincols < MIN_WIDE) + mincols = MIN_WIDE; + +#ifdef KEY_RESIZE + retry: +#endif + + dlg_auto_size(title, prompt, &height, &width, 0, mincols); + height += MIN_HIGH - 1; + if (width < MIN_WIDE) + width = MIN_WIDE; + dlg_print_size(height, width); + dlg_ctl_size(height, width); + + dialog = dlg_new_window(height, width, + dlg_box_y_ordinate(height), + dlg_box_x_ordinate(width)); + dlg_register_window(dialog, "calendar", binding); + dlg_register_buttons(dialog, "calendar", buttons); + + /* mainbox */ + dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); + dlg_draw_bottom_box(dialog); + dlg_draw_title(dialog, title); + + wattrset(dialog, dialog_attr); /* text mainbox */ + dlg_print_autowrap(dialog, prompt, height, width); + + /* compute positions of day, month and year boxes */ + memset(&dy_box, 0, sizeof(dy_box)); + memset(&mn_box, 0, sizeof(mn_box)); + memset(&yr_box, 0, sizeof(yr_box)); + + if (init_object(&dy_box, + dialog, + (width - DAY_WIDE) / 2, + 1 + (height - (DAY_HIGH + BTN_HIGH + (5 * MARGIN))), + DAY_WIDE, + DAY_HIGH + 1, + draw_day, + 'D') < 0 + || DrawObject(&dy_box) < 0) { + return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars); + } + + if (init_object(&mn_box, + dialog, + dy_box.x, + dy_box.y - (HDR_HIGH + 2 * MARGIN), + (DAY_WIDE / 2) - MARGIN, + HDR_HIGH, + draw_month, + 'M') < 0 + || DrawObject(&mn_box) < 0) { + return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars); + } + + if (init_object(&yr_box, + dialog, + dy_box.x + mn_box.width + 2, + mn_box.y, + mn_box.width, + mn_box.height, + draw_year, + 'Y') < 0 + || DrawObject(&yr_box) < 0) { + return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars); + } + + while (result == DLG_EXIT_UNKNOWN) { + BOX *obj = (state == sDAY ? &dy_box + : (state == sMONTH ? &mn_box : + (state == sYEAR ? &yr_box : 0))); + + button = (state < 0) ? 0 : state; + dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); + if (obj != 0) + dlg_set_focus(dialog, obj->window); + + key = dlg_mouse_wgetch(dialog, &fkey); + if (dlg_result_key(key, fkey, &result)) + break; + + if (fkey && (key >= DLGK_MOUSE(KEY_MIN) && key <= DLGK_MOUSE(KEY_MAX))) { + key = dlg_lookup_key(dialog, key - M_EVENT, &fkey); + } + + if ((key2 = dlg_char_to_button(key, buttons)) >= 0) { + result = key2; + } else if (fkey) { + /* handle function-keys */ + switch (key) { + case DLGK_MOUSE('D'): + state = sDAY; + break; + case DLGK_MOUSE('M'): + state = sMONTH; + break; + case DLGK_MOUSE('Y'): + state = sYEAR; + break; + case DLGK_ENTER: + result = dlg_ok_buttoncode(button); + break; + case DLGK_FIELD_PREV: + state = dlg_prev_ok_buttonindex(state, sMONTH); + break; + case DLGK_FIELD_NEXT: + state = dlg_next_ok_buttonindex(state, sMONTH); + break; +#ifdef KEY_RESIZE + case KEY_RESIZE: + /* reset data */ + height = old_height; + width = old_width; + /* repaint */ + dlg_clear(); + dlg_del_window(dialog); + refresh(); + dlg_mouse_free_regions(); + goto retry; +#endif + default: + step = 0; + key2 = -1; + if (is_DLGK_MOUSE(key)) { + if ((key2 = dlg_ok_buttoncode(key - M_EVENT)) >= 0) { + result = key2; + break; + } else if (key >= DLGK_MOUSE(KEY_MAX)) { + state = sDAY; + obj = &dy_box; + key2 = 1; + step = (key + - DLGK_MOUSE(KEY_MAX) + - day_cell_number(¤t)); + } + } + if (obj != 0) { + if (key2 < 0) + step = next_or_previous(key, (obj == &dy_box)); + if (step != 0) { + struct tm old = current; + + /* see comment regarding mktime -TD */ + if (obj == &dy_box) { + now_time += ONE_DAY * step; + } else if (obj == &mn_box) { + if (step > 0) + now_time += ONE_DAY * + days_in_month(¤t, 0); + else + now_time -= ONE_DAY * + days_in_month(¤t, -1); + } else if (obj == &yr_box) { + if (step > 0) + now_time += (ONE_DAY + * days_in_year(¤t, 0)); + else + now_time -= (ONE_DAY + * days_in_year(¤t, -1)); + } + + current = *localtime(&now_time); + + if (obj != &dy_box + && (current.tm_mday != old.tm_mday + || current.tm_mon != old.tm_mon + || current.tm_year != old.tm_year)) + DrawObject(&dy_box); + if (obj != &mn_box && current.tm_mon != old.tm_mon) + DrawObject(&mn_box); + if (obj != &yr_box && current.tm_year != old.tm_year) + DrawObject(&yr_box); + (void) DrawObject(obj); + } + } else if (state >= 0) { + if (next_or_previous(key, FALSE) < 0) + state = dlg_prev_ok_buttonindex(state, sMONTH); + else if (next_or_previous(key, FALSE) > 0) + state = dlg_next_ok_buttonindex(state, sMONTH); + } + break; + } + } + } + + sprintf(buffer, "%02d/%02d/%0d", + current.tm_mday, current.tm_mon + 1, current.tm_year + 1900); + dlg_add_result(buffer); + dlg_add_separator(); + + return CleanupResult(result, dialog, prompt, &save_vars); +} |