1217309Snwhitehorn/*
2251843Sbapt * $Id: calendar.c,v 1.67 2013/03/17 15:03:41 tom Exp $
3217309Snwhitehorn *
4217309Snwhitehorn *  calendar.c -- implements the calendar box
5217309Snwhitehorn *
6251843Sbapt *  Copyright 2001-2012,2013	Thomas E. Dickey
7217309Snwhitehorn *
8217309Snwhitehorn *  This program is free software; you can redistribute it and/or modify
9217309Snwhitehorn *  it under the terms of the GNU Lesser General Public License, version 2.1
10217309Snwhitehorn *  as published by the Free Software Foundation.
11217309Snwhitehorn *
12217309Snwhitehorn *  This program is distributed in the hope that it will be useful, but
13217309Snwhitehorn *  WITHOUT ANY WARRANTY; without even the implied warranty of
14217309Snwhitehorn *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15217309Snwhitehorn *  Lesser General Public License for more details.
16217309Snwhitehorn *
17217309Snwhitehorn *  You should have received a copy of the GNU Lesser General Public
18217309Snwhitehorn *  License along with this program; if not, write to
19217309Snwhitehorn *	Free Software Foundation, Inc.
20217309Snwhitehorn *	51 Franklin St., Fifth Floor
21217309Snwhitehorn *	Boston, MA 02110, USA.
22217309Snwhitehorn */
23217309Snwhitehorn
24217309Snwhitehorn#include <dialog.h>
25217309Snwhitehorn#include <dlg_keys.h>
26217309Snwhitehorn
27217309Snwhitehorn#include <time.h>
28217309Snwhitehorn
29217309Snwhitehorn#define ONE_DAY  (60 * 60 * 24)
30217309Snwhitehorn
31217309Snwhitehorn#define MON_WIDE 4		/* width of a month-name */
32217309Snwhitehorn#define DAY_HIGH 6		/* maximum lines in day-grid */
33217309Snwhitehorn#define DAY_WIDE (8 * MON_WIDE)	/* width of the day-grid */
34217309Snwhitehorn#define HDR_HIGH 1		/* height of cells with month/year */
35217309Snwhitehorn#define BTN_HIGH 1		/* height of button-row excluding margin */
36217309Snwhitehorn
37217309Snwhitehorn/* two more lines: titles for day-of-week and month/year boxes */
38217309Snwhitehorn#define MIN_HIGH (DAY_HIGH + 2 + HDR_HIGH + BTN_HIGH + (7 * MARGIN))
39217309Snwhitehorn#define MIN_WIDE (DAY_WIDE + (4 * MARGIN))
40217309Snwhitehorn
41217309Snwhitehorntypedef enum {
42217309Snwhitehorn    sMONTH = -3
43217309Snwhitehorn    ,sYEAR = -2
44217309Snwhitehorn    ,sDAY = -1
45217309Snwhitehorn} STATES;
46217309Snwhitehorn
47217309Snwhitehornstruct _box;
48217309Snwhitehorn
49217309Snwhitehorntypedef int (*BOX_DRAW) (struct _box *, struct tm *);
50217309Snwhitehorn
51217309Snwhitehorntypedef struct _box {
52217309Snwhitehorn    WINDOW *parent;
53217309Snwhitehorn    WINDOW *window;
54217309Snwhitehorn    int x;
55217309Snwhitehorn    int y;
56217309Snwhitehorn    int width;
57217309Snwhitehorn    int height;
58217309Snwhitehorn    BOX_DRAW box_draw;
59217309Snwhitehorn} BOX;
60217309Snwhitehorn
61217309Snwhitehornstatic const char *
62217309SnwhitehornnameOfDayOfWeek(int n)
63217309Snwhitehorn{
64217309Snwhitehorn    static const char *table[7]
65217309Snwhitehorn#ifndef ENABLE_NLS
66217309Snwhitehorn    =
67217309Snwhitehorn    {
68217309Snwhitehorn	"Sunday",
69217309Snwhitehorn	"Monday",
70217309Snwhitehorn	"Tuesday",
71217309Snwhitehorn	"Wednesday",
72217309Snwhitehorn	"Thursday",
73217309Snwhitehorn	"Friday",
74217309Snwhitehorn	"Saturday"
75217309Snwhitehorn    }
76217309Snwhitehorn#endif
77217309Snwhitehorn     ;
78217309Snwhitehorn    const char *result = 0;
79217309Snwhitehorn
80217309Snwhitehorn    if (n >= 0 && n < 7) {
81217309Snwhitehorn#ifdef ENABLE_NLS
82217309Snwhitehorn	if (table[n] == 0) {
83217309Snwhitehorn	    nl_item items[7] =
84217309Snwhitehorn	    {
85217309Snwhitehorn		ABDAY_1, ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7
86217309Snwhitehorn	    };
87217309Snwhitehorn	    table[n] = nl_langinfo(items[n]);
88217309Snwhitehorn	}
89217309Snwhitehorn#endif
90217309Snwhitehorn	result = table[n];
91217309Snwhitehorn    }
92217309Snwhitehorn    if (result == 0) {
93217309Snwhitehorn	result = "?";
94217309Snwhitehorn    }
95217309Snwhitehorn    return result;
96217309Snwhitehorn}
97217309Snwhitehorn
98217309Snwhitehornstatic const char *
99217309SnwhitehornnameOfMonth(int n)
100217309Snwhitehorn{
101217309Snwhitehorn    static const char *table[12]
102217309Snwhitehorn#ifndef ENABLE_NLS
103217309Snwhitehorn    =
104217309Snwhitehorn    {
105217309Snwhitehorn	"January",
106217309Snwhitehorn	"February",
107217309Snwhitehorn	"March",
108217309Snwhitehorn	"April",
109217309Snwhitehorn	"May",
110217309Snwhitehorn	"June",
111217309Snwhitehorn	"July",
112217309Snwhitehorn	"August",
113217309Snwhitehorn	"September",
114217309Snwhitehorn	"October",
115217309Snwhitehorn	"November",
116217309Snwhitehorn	"December"
117217309Snwhitehorn    }
118217309Snwhitehorn#endif
119217309Snwhitehorn     ;
120217309Snwhitehorn    const char *result = 0;
121217309Snwhitehorn
122217309Snwhitehorn    if (n >= 0 && n < 12) {
123217309Snwhitehorn#ifdef ENABLE_NLS
124217309Snwhitehorn	if (table[n] == 0) {
125217309Snwhitehorn	    nl_item items[12] =
126217309Snwhitehorn	    {
127217309Snwhitehorn		MON_1, MON_2, MON_3, MON_4, MON_5, MON_6,
128217309Snwhitehorn		MON_7, MON_8, MON_9, MON_10, MON_11, MON_12
129217309Snwhitehorn	    };
130217309Snwhitehorn	    table[n] = nl_langinfo(items[n]);
131217309Snwhitehorn	}
132217309Snwhitehorn#endif
133217309Snwhitehorn	result = table[n];
134217309Snwhitehorn    }
135217309Snwhitehorn    if (result == 0) {
136217309Snwhitehorn	result = "?";
137217309Snwhitehorn    }
138217309Snwhitehorn    return result;
139217309Snwhitehorn}
140217309Snwhitehorn
141217309Snwhitehornstatic int
142217309Snwhitehorndays_in_month(struct tm *current, int offset /* -1, 0, 1 */ )
143217309Snwhitehorn{
144217309Snwhitehorn    static const int nominal[] =
145217309Snwhitehorn    {
146217309Snwhitehorn	31, 28, 31, 30, 31, 30,
147217309Snwhitehorn	31, 31, 30, 31, 30, 31
148217309Snwhitehorn    };
149217309Snwhitehorn    int year = current->tm_year;
150217309Snwhitehorn    int month = current->tm_mon + offset;
151217309Snwhitehorn    int result;
152217309Snwhitehorn
153217309Snwhitehorn    while (month < 0) {
154217309Snwhitehorn	month += 12;
155217309Snwhitehorn	year -= 1;
156217309Snwhitehorn    }
157217309Snwhitehorn    while (month >= 12) {
158217309Snwhitehorn	month -= 12;
159217309Snwhitehorn	year += 1;
160217309Snwhitehorn    }
161217309Snwhitehorn    result = nominal[month];
162217309Snwhitehorn    if (month == 1)
163217309Snwhitehorn	result += ((year % 4) == 0);
164217309Snwhitehorn    return result;
165217309Snwhitehorn}
166217309Snwhitehorn
167217309Snwhitehornstatic int
168217309Snwhitehorndays_in_year(struct tm *current, int offset /* -1, 0, 1 */ )
169217309Snwhitehorn{
170217309Snwhitehorn    int year = current->tm_year + 1900 + offset;
171217309Snwhitehorn
172217309Snwhitehorn    return ((year % 4) == 0) ? 366 : 365;
173217309Snwhitehorn}
174217309Snwhitehorn
175217309Snwhitehornstatic int
176217309Snwhitehornday_cell_number(struct tm *current)
177217309Snwhitehorn{
178217309Snwhitehorn    int cell;
179217309Snwhitehorn    cell = current->tm_mday - ((6 + current->tm_mday - current->tm_wday) % 7);
180217309Snwhitehorn    if ((current->tm_mday - 1) % 7 != current->tm_wday)
181217309Snwhitehorn	cell += 6;
182217309Snwhitehorn    else
183217309Snwhitehorn	cell--;
184217309Snwhitehorn    return cell;
185217309Snwhitehorn}
186217309Snwhitehorn
187217309Snwhitehornstatic int
188217309Snwhitehornnext_or_previous(int key, int two_d)
189217309Snwhitehorn{
190217309Snwhitehorn    int result = 0;
191217309Snwhitehorn
192217309Snwhitehorn    switch (key) {
193217309Snwhitehorn    case DLGK_GRID_UP:
194217309Snwhitehorn	result = two_d ? -7 : -1;
195217309Snwhitehorn	break;
196217309Snwhitehorn    case DLGK_GRID_LEFT:
197217309Snwhitehorn	result = -1;
198217309Snwhitehorn	break;
199217309Snwhitehorn    case DLGK_GRID_DOWN:
200217309Snwhitehorn	result = two_d ? 7 : 1;
201217309Snwhitehorn	break;
202217309Snwhitehorn    case DLGK_GRID_RIGHT:
203217309Snwhitehorn	result = 1;
204217309Snwhitehorn	break;
205217309Snwhitehorn    default:
206217309Snwhitehorn	beep();
207217309Snwhitehorn	break;
208217309Snwhitehorn    }
209217309Snwhitehorn    return result;
210217309Snwhitehorn}
211217309Snwhitehorn
212217309Snwhitehorn/*
213217309Snwhitehorn * Draw the day-of-month selection box
214217309Snwhitehorn */
215217309Snwhitehornstatic int
216217309Snwhitehorndraw_day(BOX * data, struct tm *current)
217217309Snwhitehorn{
218217309Snwhitehorn    int cell_wide = MON_WIDE;
219217309Snwhitehorn    int y, x, this_x = 0;
220217309Snwhitehorn    int save_y = 0, save_x = 0;
221217309Snwhitehorn    int day = current->tm_mday;
222217309Snwhitehorn    int mday;
223217309Snwhitehorn    int week;
224217309Snwhitehorn    int last = days_in_month(current, 0);
225217309Snwhitehorn    int prev = days_in_month(current, -1);
226217309Snwhitehorn
227217309Snwhitehorn    werase(data->window);
228251843Sbapt    dlg_draw_box2(data->parent,
229251843Sbapt		  data->y - MARGIN, data->x - MARGIN,
230251843Sbapt		  data->height + (2 * MARGIN), data->width + (2 * MARGIN),
231251843Sbapt		  menubox_attr,
232251843Sbapt		  menubox_border_attr,
233251843Sbapt		  menubox_border2_attr);
234217309Snwhitehorn
235251843Sbapt    (void) wattrset(data->window, menubox_attr);	/* daynames headline */
236217309Snwhitehorn    for (x = 0; x < 7; x++) {
237217309Snwhitehorn	mvwprintw(data->window,
238217309Snwhitehorn		  0, (x + 1) * cell_wide, "%*.*s ",
239217309Snwhitehorn		  cell_wide - 1,
240217309Snwhitehorn		  cell_wide - 1,
241217309Snwhitehorn		  nameOfDayOfWeek(x));
242217309Snwhitehorn    }
243217309Snwhitehorn
244217309Snwhitehorn    mday = ((6 + current->tm_mday - current->tm_wday) % 7) - 7;
245217309Snwhitehorn    if (mday <= -7)
246217309Snwhitehorn	mday += 7;
247217309Snwhitehorn    /* mday is now in the range -6 to 0. */
248217309Snwhitehorn    week = (current->tm_yday + 6 + mday - current->tm_mday) / 7;
249217309Snwhitehorn
250217309Snwhitehorn    for (y = 1; mday < last; y++) {
251251843Sbapt	(void) wattrset(data->window, menubox_attr);	/* weeknumbers headline */
252217309Snwhitehorn	mvwprintw(data->window,
253217309Snwhitehorn		  y, 0,
254217309Snwhitehorn		  "%*d ",
255217309Snwhitehorn		  cell_wide - 1,
256217309Snwhitehorn		  ++week);
257217309Snwhitehorn	for (x = 0; x < 7; x++) {
258217309Snwhitehorn	    this_x = 1 + (x + 1) * cell_wide;
259217309Snwhitehorn	    ++mday;
260217309Snwhitehorn	    if (wmove(data->window, y, this_x) == ERR)
261217309Snwhitehorn		continue;
262251843Sbapt	    (void) wattrset(data->window, item_attr);	/* not selected days */
263217309Snwhitehorn	    if (mday == day) {
264251843Sbapt		(void) wattrset(data->window, item_selected_attr);	/* selected day */
265217309Snwhitehorn		save_y = y;
266217309Snwhitehorn		save_x = this_x;
267217309Snwhitehorn	    }
268217309Snwhitehorn	    if (mday > 0) {
269217309Snwhitehorn		if (mday <= last) {
270217309Snwhitehorn		    wprintw(data->window, "%*d", cell_wide - 2, mday);
271217309Snwhitehorn		} else if (mday == day) {
272217309Snwhitehorn		    wprintw(data->window, "%*d", cell_wide - 2, mday - last);
273217309Snwhitehorn		}
274217309Snwhitehorn	    } else if (mday == day) {
275217309Snwhitehorn		wprintw(data->window, "%*d", cell_wide - 2, mday + prev);
276217309Snwhitehorn	    }
277217309Snwhitehorn	}
278217309Snwhitehorn	wmove(data->window, save_y, save_x);
279217309Snwhitehorn    }
280217309Snwhitehorn    /* just draw arrows - scrollbar is unsuitable here */
281217309Snwhitehorn    dlg_draw_arrows(data->parent, TRUE, TRUE,
282217309Snwhitehorn		    data->x + ARROWS_COL,
283217309Snwhitehorn		    data->y - 1,
284217309Snwhitehorn		    data->y + data->height);
285217309Snwhitehorn
286217309Snwhitehorn    return 0;
287217309Snwhitehorn}
288217309Snwhitehorn
289217309Snwhitehorn/*
290217309Snwhitehorn * Draw the month-of-year selection box
291217309Snwhitehorn */
292217309Snwhitehornstatic int
293217309Snwhitehorndraw_month(BOX * data, struct tm *current)
294217309Snwhitehorn{
295217309Snwhitehorn    int month;
296217309Snwhitehorn
297217309Snwhitehorn    month = current->tm_mon + 1;
298217309Snwhitehorn
299251843Sbapt    (void) wattrset(data->parent, dialog_attr);		/* Headline "Month" */
300217309Snwhitehorn    (void) mvwprintw(data->parent, data->y - 2, data->x - 1, _("Month"));
301251843Sbapt    dlg_draw_box2(data->parent,
302251843Sbapt		  data->y - 1, data->x - 1,
303251843Sbapt		  data->height + 2, data->width + 2,
304251843Sbapt		  menubox_attr,
305251843Sbapt		  menubox_border_attr,
306251843Sbapt		  menubox_border2_attr);
307251843Sbapt    (void) wattrset(data->window, item_attr);	/* color the month selection */
308217309Snwhitehorn    mvwprintw(data->window, 0, 0, "%s", nameOfMonth(month - 1));
309217309Snwhitehorn    wmove(data->window, 0, 0);
310217309Snwhitehorn    return 0;
311217309Snwhitehorn}
312217309Snwhitehorn
313217309Snwhitehorn/*
314217309Snwhitehorn * Draw the year selection box
315217309Snwhitehorn */
316217309Snwhitehornstatic int
317217309Snwhitehorndraw_year(BOX * data, struct tm *current)
318217309Snwhitehorn{
319217309Snwhitehorn    int year = current->tm_year + 1900;
320217309Snwhitehorn
321251843Sbapt    (void) wattrset(data->parent, dialog_attr);		/* Headline "Year" */
322217309Snwhitehorn    (void) mvwprintw(data->parent, data->y - 2, data->x - 1, _("Year"));
323251843Sbapt    dlg_draw_box2(data->parent,
324251843Sbapt		  data->y - 1, data->x - 1,
325251843Sbapt		  data->height + 2, data->width + 2,
326251843Sbapt		  menubox_attr,
327251843Sbapt		  menubox_border_attr,
328251843Sbapt		  menubox_border2_attr);
329251843Sbapt    (void) wattrset(data->window, item_attr);	/* color the year selection */
330217309Snwhitehorn    mvwprintw(data->window, 0, 0, "%4d", year);
331217309Snwhitehorn    wmove(data->window, 0, 0);
332217309Snwhitehorn    return 0;
333217309Snwhitehorn}
334217309Snwhitehorn
335217309Snwhitehornstatic int
336217309Snwhitehorninit_object(BOX * data,
337217309Snwhitehorn	    WINDOW *parent,
338217309Snwhitehorn	    int x, int y,
339217309Snwhitehorn	    int width, int height,
340217309Snwhitehorn	    BOX_DRAW box_draw,
341217309Snwhitehorn	    int code)
342217309Snwhitehorn{
343217309Snwhitehorn    data->parent = parent;
344217309Snwhitehorn    data->x = x;
345217309Snwhitehorn    data->y = y;
346217309Snwhitehorn    data->width = width;
347217309Snwhitehorn    data->height = height;
348217309Snwhitehorn    data->box_draw = box_draw;
349217309Snwhitehorn
350217309Snwhitehorn    data->window = derwin(data->parent,
351217309Snwhitehorn			  data->height, data->width,
352217309Snwhitehorn			  data->y, data->x);
353217309Snwhitehorn    if (data->window == 0)
354217309Snwhitehorn	return -1;
355217309Snwhitehorn    (void) keypad(data->window, TRUE);
356217309Snwhitehorn
357217309Snwhitehorn    dlg_mouse_setbase(getbegx(parent), getbegy(parent));
358217309Snwhitehorn    if (code == 'D') {
359217309Snwhitehorn	dlg_mouse_mkbigregion(y + 1, x + MON_WIDE, height - 1, width - MON_WIDE,
360217309Snwhitehorn			      KEY_MAX, 1, MON_WIDE, 3);
361217309Snwhitehorn    } else {
362217309Snwhitehorn	dlg_mouse_mkregion(y, x, height, width, code);
363217309Snwhitehorn    }
364217309Snwhitehorn
365217309Snwhitehorn    return 0;
366217309Snwhitehorn}
367217309Snwhitehorn
368217309Snwhitehornstatic int
369217309SnwhitehornCleanupResult(int code, WINDOW *dialog, char *prompt, DIALOG_VARS * save_vars)
370217309Snwhitehorn{
371217309Snwhitehorn    if (dialog != 0)
372217309Snwhitehorn	dlg_del_window(dialog);
373217309Snwhitehorn    dlg_mouse_free_regions();
374217309Snwhitehorn    if (prompt != 0)
375217309Snwhitehorn	free(prompt);
376217309Snwhitehorn    dlg_restore_vars(save_vars);
377217309Snwhitehorn
378217309Snwhitehorn    return code;
379217309Snwhitehorn}
380217309Snwhitehorn
381217309Snwhitehorn#define DrawObject(data) (data)->box_draw(data, &current)
382217309Snwhitehorn
383217309Snwhitehorn/*
384217309Snwhitehorn * Display a dialog box for entering a date
385217309Snwhitehorn */
386217309Snwhitehornint
387217309Snwhitehorndialog_calendar(const char *title,
388217309Snwhitehorn		const char *subtitle,
389217309Snwhitehorn		int height,
390217309Snwhitehorn		int width,
391217309Snwhitehorn		int day,
392217309Snwhitehorn		int month,
393217309Snwhitehorn		int year)
394217309Snwhitehorn{
395217309Snwhitehorn    /* *INDENT-OFF* */
396217309Snwhitehorn    static DLG_KEYS_BINDING binding[] = {
397224014Snwhitehorn	HELPKEY_BINDINGS,
398217309Snwhitehorn	ENTERKEY_BINDINGS,
399217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ENTER,	' ' ),
400217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
401217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
402217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_DOWN,	'j' ),
403217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_DOWN,	DLGK_MOUSE(KEY_NPAGE) ),
404217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_DOWN,	KEY_DOWN ),
405217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_DOWN,	KEY_NPAGE ),
406217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_LEFT,	'-' ),
407217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_LEFT,  'h' ),
408217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_LEFT,  CHR_BACKSPACE ),
409217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_LEFT,  CHR_PREVIOUS ),
410217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_LEFT,  KEY_LEFT ),
411217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_RIGHT,	'+' ),
412217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'l' ),
413217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_RIGHT, CHR_NEXT ),
414217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_NEXT ),
415217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_RIGHT ),
416217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_UP,	'k' ),
417217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_UP,	KEY_PPAGE ),
418217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_UP,	KEY_PREVIOUS ),
419217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_UP,	KEY_UP ),
420217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_UP,  	DLGK_MOUSE(KEY_PPAGE) ),
421217309Snwhitehorn	END_KEYS_BINDING
422217309Snwhitehorn    };
423217309Snwhitehorn    /* *INDENT-ON* */
424217309Snwhitehorn
425217309Snwhitehorn#ifdef KEY_RESIZE
426217309Snwhitehorn    int old_height = height;
427217309Snwhitehorn    int old_width = width;
428217309Snwhitehorn#endif
429217309Snwhitehorn    BOX dy_box, mn_box, yr_box;
430217309Snwhitehorn    int fkey;
431217309Snwhitehorn    int key = 0;
432217309Snwhitehorn    int key2;
433217309Snwhitehorn    int step;
434217309Snwhitehorn    int button;
435217309Snwhitehorn    int result = DLG_EXIT_UNKNOWN;
436217309Snwhitehorn    WINDOW *dialog;
437217309Snwhitehorn    time_t now_time = time((time_t *) 0);
438217309Snwhitehorn    struct tm current;
439251843Sbapt    int state = dlg_default_button();
440217309Snwhitehorn    const char **buttons = dlg_ok_labels();
441217309Snwhitehorn    char *prompt = dlg_strclone(subtitle);
442217309Snwhitehorn    int mincols = MIN_WIDE;
443217309Snwhitehorn    char buffer[MAX_LEN];
444217309Snwhitehorn    DIALOG_VARS save_vars;
445217309Snwhitehorn
446217309Snwhitehorn    dlg_save_vars(&save_vars);
447217309Snwhitehorn    dialog_vars.separate_output = TRUE;
448217309Snwhitehorn
449217309Snwhitehorn    dlg_does_output();
450217309Snwhitehorn
451217309Snwhitehorn    now_time = time((time_t *) 0);
452217309Snwhitehorn    current = *localtime(&now_time);
453217309Snwhitehorn    if (day < 0)
454217309Snwhitehorn	day = current.tm_mday;
455217309Snwhitehorn    if (month < 0)
456217309Snwhitehorn	month = current.tm_mon + 1;
457217309Snwhitehorn    if (year < 0)
458217309Snwhitehorn	year = current.tm_year + 1900;
459217309Snwhitehorn
460217309Snwhitehorn    /* compute a struct tm that matches the day/month/year parameters */
461217309Snwhitehorn    if (((year -= 1900) > 0) && (year < 200)) {
462217309Snwhitehorn	/* ugly, but I'd like to run this on older machines w/o mktime -TD */
463217309Snwhitehorn	for (;;) {
464217309Snwhitehorn	    if (year > current.tm_year) {
465217309Snwhitehorn		now_time += ONE_DAY * days_in_year(&current, 0);
466217309Snwhitehorn	    } else if (year < current.tm_year) {
467217309Snwhitehorn		now_time -= ONE_DAY * days_in_year(&current, -1);
468217309Snwhitehorn	    } else if (month > current.tm_mon + 1) {
469217309Snwhitehorn		now_time += ONE_DAY * days_in_month(&current, 0);
470217309Snwhitehorn	    } else if (month < current.tm_mon + 1) {
471217309Snwhitehorn		now_time -= ONE_DAY * days_in_month(&current, -1);
472217309Snwhitehorn	    } else if (day > current.tm_mday) {
473217309Snwhitehorn		now_time += ONE_DAY;
474217309Snwhitehorn	    } else if (day < current.tm_mday) {
475217309Snwhitehorn		now_time -= ONE_DAY;
476217309Snwhitehorn	    } else {
477217309Snwhitehorn		break;
478217309Snwhitehorn	    }
479217309Snwhitehorn	    current = *localtime(&now_time);
480217309Snwhitehorn	}
481217309Snwhitehorn    }
482217309Snwhitehorn    dlg_button_layout(buttons, &mincols);
483217309Snwhitehorn
484217309Snwhitehorn#ifdef KEY_RESIZE
485217309Snwhitehorn  retry:
486217309Snwhitehorn#endif
487217309Snwhitehorn
488217309Snwhitehorn    dlg_auto_size(title, prompt, &height, &width, 0, mincols);
489217309Snwhitehorn    height += MIN_HIGH - 1;
490217309Snwhitehorn    dlg_print_size(height, width);
491217309Snwhitehorn    dlg_ctl_size(height, width);
492217309Snwhitehorn
493217309Snwhitehorn    dialog = dlg_new_window(height, width,
494217309Snwhitehorn			    dlg_box_y_ordinate(height),
495217309Snwhitehorn			    dlg_box_x_ordinate(width));
496217309Snwhitehorn    dlg_register_window(dialog, "calendar", binding);
497217309Snwhitehorn    dlg_register_buttons(dialog, "calendar", buttons);
498217309Snwhitehorn
499217309Snwhitehorn    /* mainbox */
500251843Sbapt    dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr);
501251843Sbapt    dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr);
502217309Snwhitehorn    dlg_draw_title(dialog, title);
503217309Snwhitehorn
504251843Sbapt    (void) wattrset(dialog, dialog_attr);	/* text mainbox */
505217309Snwhitehorn    dlg_print_autowrap(dialog, prompt, height, width);
506217309Snwhitehorn
507217309Snwhitehorn    /* compute positions of day, month and year boxes */
508217309Snwhitehorn    memset(&dy_box, 0, sizeof(dy_box));
509217309Snwhitehorn    memset(&mn_box, 0, sizeof(mn_box));
510217309Snwhitehorn    memset(&yr_box, 0, sizeof(yr_box));
511217309Snwhitehorn
512217309Snwhitehorn    if (init_object(&dy_box,
513217309Snwhitehorn		    dialog,
514217309Snwhitehorn		    (width - DAY_WIDE) / 2,
515217309Snwhitehorn		    1 + (height - (DAY_HIGH + BTN_HIGH + (5 * MARGIN))),
516217309Snwhitehorn		    DAY_WIDE,
517217309Snwhitehorn		    DAY_HIGH + 1,
518217309Snwhitehorn		    draw_day,
519217309Snwhitehorn		    'D') < 0
520217309Snwhitehorn	|| DrawObject(&dy_box) < 0) {
521217309Snwhitehorn	return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
522217309Snwhitehorn    }
523217309Snwhitehorn
524217309Snwhitehorn    if (init_object(&mn_box,
525217309Snwhitehorn		    dialog,
526217309Snwhitehorn		    dy_box.x,
527217309Snwhitehorn		    dy_box.y - (HDR_HIGH + 2 * MARGIN),
528217309Snwhitehorn		    (DAY_WIDE / 2) - MARGIN,
529217309Snwhitehorn		    HDR_HIGH,
530217309Snwhitehorn		    draw_month,
531217309Snwhitehorn		    'M') < 0
532217309Snwhitehorn	|| DrawObject(&mn_box) < 0) {
533217309Snwhitehorn	return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
534217309Snwhitehorn    }
535217309Snwhitehorn
536217309Snwhitehorn    if (init_object(&yr_box,
537217309Snwhitehorn		    dialog,
538217309Snwhitehorn		    dy_box.x + mn_box.width + 2,
539217309Snwhitehorn		    mn_box.y,
540217309Snwhitehorn		    mn_box.width,
541217309Snwhitehorn		    mn_box.height,
542217309Snwhitehorn		    draw_year,
543217309Snwhitehorn		    'Y') < 0
544217309Snwhitehorn	|| DrawObject(&yr_box) < 0) {
545217309Snwhitehorn	return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
546217309Snwhitehorn    }
547217309Snwhitehorn
548251843Sbapt    dlg_trace_win(dialog);
549217309Snwhitehorn    while (result == DLG_EXIT_UNKNOWN) {
550217309Snwhitehorn	BOX *obj = (state == sDAY ? &dy_box
551217309Snwhitehorn		    : (state == sMONTH ? &mn_box :
552217309Snwhitehorn		       (state == sYEAR ? &yr_box : 0)));
553217309Snwhitehorn
554217309Snwhitehorn	button = (state < 0) ? 0 : state;
555217309Snwhitehorn	dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
556217309Snwhitehorn	if (obj != 0)
557217309Snwhitehorn	    dlg_set_focus(dialog, obj->window);
558217309Snwhitehorn
559217309Snwhitehorn	key = dlg_mouse_wgetch(dialog, &fkey);
560217309Snwhitehorn	if (dlg_result_key(key, fkey, &result))
561217309Snwhitehorn	    break;
562217309Snwhitehorn
563217309Snwhitehorn	if (fkey && (key >= DLGK_MOUSE(KEY_MIN) && key <= DLGK_MOUSE(KEY_MAX))) {
564217309Snwhitehorn	    key = dlg_lookup_key(dialog, key - M_EVENT, &fkey);
565217309Snwhitehorn	}
566217309Snwhitehorn
567217309Snwhitehorn	if ((key2 = dlg_char_to_button(key, buttons)) >= 0) {
568217309Snwhitehorn	    result = key2;
569217309Snwhitehorn	} else if (fkey) {
570217309Snwhitehorn	    /* handle function-keys */
571217309Snwhitehorn	    switch (key) {
572217309Snwhitehorn	    case DLGK_MOUSE('D'):
573217309Snwhitehorn		state = sDAY;
574217309Snwhitehorn		break;
575217309Snwhitehorn	    case DLGK_MOUSE('M'):
576217309Snwhitehorn		state = sMONTH;
577217309Snwhitehorn		break;
578217309Snwhitehorn	    case DLGK_MOUSE('Y'):
579217309Snwhitehorn		state = sYEAR;
580217309Snwhitehorn		break;
581217309Snwhitehorn	    case DLGK_ENTER:
582224014Snwhitehorn		result = dlg_enter_buttoncode(button);
583217309Snwhitehorn		break;
584217309Snwhitehorn	    case DLGK_FIELD_PREV:
585217309Snwhitehorn		state = dlg_prev_ok_buttonindex(state, sMONTH);
586217309Snwhitehorn		break;
587217309Snwhitehorn	    case DLGK_FIELD_NEXT:
588217309Snwhitehorn		state = dlg_next_ok_buttonindex(state, sMONTH);
589217309Snwhitehorn		break;
590217309Snwhitehorn#ifdef KEY_RESIZE
591217309Snwhitehorn	    case KEY_RESIZE:
592217309Snwhitehorn		/* reset data */
593217309Snwhitehorn		height = old_height;
594217309Snwhitehorn		width = old_width;
595217309Snwhitehorn		/* repaint */
596217309Snwhitehorn		dlg_clear();
597217309Snwhitehorn		dlg_del_window(dialog);
598217309Snwhitehorn		refresh();
599217309Snwhitehorn		dlg_mouse_free_regions();
600217309Snwhitehorn		goto retry;
601217309Snwhitehorn#endif
602217309Snwhitehorn	    default:
603217309Snwhitehorn		step = 0;
604217309Snwhitehorn		key2 = -1;
605217309Snwhitehorn		if (is_DLGK_MOUSE(key)) {
606217309Snwhitehorn		    if ((key2 = dlg_ok_buttoncode(key - M_EVENT)) >= 0) {
607217309Snwhitehorn			result = key2;
608217309Snwhitehorn			break;
609217309Snwhitehorn		    } else if (key >= DLGK_MOUSE(KEY_MAX)) {
610217309Snwhitehorn			state = sDAY;
611217309Snwhitehorn			obj = &dy_box;
612217309Snwhitehorn			key2 = 1;
613217309Snwhitehorn			step = (key
614217309Snwhitehorn				- DLGK_MOUSE(KEY_MAX)
615217309Snwhitehorn				- day_cell_number(&current));
616217309Snwhitehorn		    }
617217309Snwhitehorn		}
618217309Snwhitehorn		if (obj != 0) {
619217309Snwhitehorn		    if (key2 < 0)
620217309Snwhitehorn			step = next_or_previous(key, (obj == &dy_box));
621217309Snwhitehorn		    if (step != 0) {
622217309Snwhitehorn			struct tm old = current;
623217309Snwhitehorn
624217309Snwhitehorn			/* see comment regarding mktime -TD */
625217309Snwhitehorn			if (obj == &dy_box) {
626217309Snwhitehorn			    now_time += ONE_DAY * step;
627217309Snwhitehorn			} else if (obj == &mn_box) {
628217309Snwhitehorn			    if (step > 0)
629217309Snwhitehorn				now_time += ONE_DAY *
630217309Snwhitehorn				    days_in_month(&current, 0);
631217309Snwhitehorn			    else
632217309Snwhitehorn				now_time -= ONE_DAY *
633217309Snwhitehorn				    days_in_month(&current, -1);
634217309Snwhitehorn			} else if (obj == &yr_box) {
635217309Snwhitehorn			    if (step > 0)
636217309Snwhitehorn				now_time += (ONE_DAY
637217309Snwhitehorn					     * days_in_year(&current, 0));
638217309Snwhitehorn			    else
639217309Snwhitehorn				now_time -= (ONE_DAY
640217309Snwhitehorn					     * days_in_year(&current, -1));
641217309Snwhitehorn			}
642217309Snwhitehorn
643217309Snwhitehorn			current = *localtime(&now_time);
644217309Snwhitehorn
645217309Snwhitehorn			if (obj != &dy_box
646217309Snwhitehorn			    && (current.tm_mday != old.tm_mday
647217309Snwhitehorn				|| current.tm_mon != old.tm_mon
648217309Snwhitehorn				|| current.tm_year != old.tm_year))
649217309Snwhitehorn			    DrawObject(&dy_box);
650217309Snwhitehorn			if (obj != &mn_box && current.tm_mon != old.tm_mon)
651217309Snwhitehorn			    DrawObject(&mn_box);
652217309Snwhitehorn			if (obj != &yr_box && current.tm_year != old.tm_year)
653217309Snwhitehorn			    DrawObject(&yr_box);
654217309Snwhitehorn			(void) DrawObject(obj);
655217309Snwhitehorn		    }
656217309Snwhitehorn		} else if (state >= 0) {
657217309Snwhitehorn		    if (next_or_previous(key, FALSE) < 0)
658217309Snwhitehorn			state = dlg_prev_ok_buttonindex(state, sMONTH);
659217309Snwhitehorn		    else if (next_or_previous(key, FALSE) > 0)
660217309Snwhitehorn			state = dlg_next_ok_buttonindex(state, sMONTH);
661217309Snwhitehorn		}
662217309Snwhitehorn		break;
663217309Snwhitehorn	    }
664217309Snwhitehorn	}
665217309Snwhitehorn    }
666217309Snwhitehorn
667217309Snwhitehorn#define DefaultFormat(dst, src) \
668217309Snwhitehorn	sprintf(dst, "%02d/%02d/%0d", \
669217309Snwhitehorn		src.tm_mday, src.tm_mon + 1, src.tm_year + 1900)
670217309Snwhitehorn#ifdef HAVE_STRFTIME
671217309Snwhitehorn    if (dialog_vars.date_format != 0) {
672220749Snwhitehorn	size_t used = strftime(buffer,
673220749Snwhitehorn			       sizeof(buffer) - 1,
674220749Snwhitehorn			       dialog_vars.date_format,
675220749Snwhitehorn			       &current);
676217309Snwhitehorn	if (used == 0 || *buffer == '\0')
677217309Snwhitehorn	    DefaultFormat(buffer, current);
678217309Snwhitehorn    } else
679217309Snwhitehorn#endif
680217309Snwhitehorn	DefaultFormat(buffer, current);
681217309Snwhitehorn
682217309Snwhitehorn    dlg_add_result(buffer);
683217309Snwhitehorn    dlg_add_separator();
684251843Sbapt    dlg_add_last_key(-1);
685217309Snwhitehorn
686217309Snwhitehorn    return CleanupResult(result, dialog, prompt, &save_vars);
687217309Snwhitehorn}
688