xref: /freebsd-src/contrib/bsddialog/lib/timebox.c (revision 8c4f402881b3a926f1bafdf275b015c6d76a31b2)
1c76f0793SBaptiste Daroussin /*-
2c76f0793SBaptiste Daroussin  * SPDX-License-Identifier: BSD-2-Clause
3c76f0793SBaptiste Daroussin  *
4c76f0793SBaptiste Daroussin  * Copyright (c) 2021 Alfonso Sabato Siciliano
5c76f0793SBaptiste Daroussin  *
6c76f0793SBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
7c76f0793SBaptiste Daroussin  * modification, are permitted provided that the following conditions
8c76f0793SBaptiste Daroussin  * are met:
9c76f0793SBaptiste Daroussin  * 1. Redistributions of source code must retain the above copyright
10c76f0793SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer.
11c76f0793SBaptiste Daroussin  * 2. Redistributions in binary form must reproduce the above copyright
12c76f0793SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer in the
13c76f0793SBaptiste Daroussin  *    documentation and/or other materials provided with the distribution.
14c76f0793SBaptiste Daroussin  *
15c76f0793SBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16c76f0793SBaptiste Daroussin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17c76f0793SBaptiste Daroussin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18c76f0793SBaptiste Daroussin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19c76f0793SBaptiste Daroussin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20c76f0793SBaptiste Daroussin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21c76f0793SBaptiste Daroussin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22c76f0793SBaptiste Daroussin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23c76f0793SBaptiste Daroussin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24c76f0793SBaptiste Daroussin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25c76f0793SBaptiste Daroussin  * SUCH DAMAGE.
26c76f0793SBaptiste Daroussin  */
27c76f0793SBaptiste Daroussin 
28f499134dSBaptiste Daroussin #include <sys/param.h>
29f499134dSBaptiste Daroussin 
30c76f0793SBaptiste Daroussin #ifdef PORTNCURSES
31*8c4f4028SBaptiste Daroussin #include <ncurses/ncurses.h>
32c76f0793SBaptiste Daroussin #else
33*8c4f4028SBaptiste Daroussin #include <ncurses.h>
34c76f0793SBaptiste Daroussin #endif
35c76f0793SBaptiste Daroussin 
36f499134dSBaptiste Daroussin #include <ctype.h>
37f499134dSBaptiste Daroussin #include <string.h>
38f499134dSBaptiste Daroussin 
39c76f0793SBaptiste Daroussin #include "bsddialog.h"
40c76f0793SBaptiste Daroussin #include "lib_util.h"
41f499134dSBaptiste Daroussin #include "bsddialog_theme.h"
42c76f0793SBaptiste Daroussin 
43f499134dSBaptiste Daroussin #define MINWDATE 25 /* 23 wins + 2 VBORDERS */
44f499134dSBaptiste Daroussin #define MINWTIME 16 /*14 wins + 2 VBORDERS */
45f499134dSBaptiste Daroussin #define MINHEIGHT 8 /* 2 for text */
46c76f0793SBaptiste Daroussin 
47f499134dSBaptiste Daroussin /* "Time": timebox - datebox */
48f499134dSBaptiste Daroussin 
49f499134dSBaptiste Daroussin extern struct bsddialog_theme t;
50f499134dSBaptiste Daroussin 
51f499134dSBaptiste Daroussin static int
52f499134dSBaptiste Daroussin datetime_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h,
53f499134dSBaptiste Daroussin     int *w, int minw, char *text, struct buttons bs)
54f499134dSBaptiste Daroussin {
55f499134dSBaptiste Daroussin 	int maxword, maxline, nlines, line;
56f499134dSBaptiste Daroussin 
57f499134dSBaptiste Daroussin 	if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0)
58f499134dSBaptiste Daroussin 		return BSDDIALOG_ERROR;
59f499134dSBaptiste Daroussin 
60f499134dSBaptiste Daroussin 	if (cols == BSDDIALOG_AUTOSIZE) {
61f499134dSBaptiste Daroussin 		*w = VBORDERS;
62f499134dSBaptiste Daroussin 		/* buttons size */
63f499134dSBaptiste Daroussin 		*w += bs.nbuttons * bs.sizebutton;
64f499134dSBaptiste Daroussin 		*w += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0;
65f499134dSBaptiste Daroussin 		/* text size */
66f499134dSBaptiste Daroussin 		line = maxline + VBORDERS + t.text.hmargin * 2;
67f499134dSBaptiste Daroussin 		line = MAX(line, (int) (maxword + VBORDERS + t.text.hmargin * 2));
68f499134dSBaptiste Daroussin 		*w = MAX(*w, line);
69f499134dSBaptiste Daroussin 		/* date windows */
70f499134dSBaptiste Daroussin 		*w = MAX(*w, minw);
71*8c4f4028SBaptiste Daroussin 		/* conf.auto_minwidth */
72*8c4f4028SBaptiste Daroussin 		*w = MAX(*w, (int)conf->auto_minwidth);
73f499134dSBaptiste Daroussin 		/* avoid terminal overflow */
74f499134dSBaptiste Daroussin 		*w = MIN(*w, widget_max_width(conf) -1);
75f499134dSBaptiste Daroussin 	}
76f499134dSBaptiste Daroussin 
77f499134dSBaptiste Daroussin 	if (rows == BSDDIALOG_AUTOSIZE) {
78f499134dSBaptiste Daroussin 		*h = MINHEIGHT;
79f499134dSBaptiste Daroussin 		if (maxword > 0)
80*8c4f4028SBaptiste Daroussin 			*h += MAX(nlines, (int)(*w / GET_ASPECT_RATIO(conf)));
81*8c4f4028SBaptiste Daroussin 		/* conf.auto_minheight */
82*8c4f4028SBaptiste Daroussin 		*h = MAX(*h, (int)conf->auto_minheight);
83f499134dSBaptiste Daroussin 		/* avoid terminal overflow */
84f499134dSBaptiste Daroussin 		*h = MIN(*h, widget_max_height(conf) -1);
85f499134dSBaptiste Daroussin 	}
86f499134dSBaptiste Daroussin 
87f499134dSBaptiste Daroussin 	return 0;
88f499134dSBaptiste Daroussin }
89f499134dSBaptiste Daroussin 
90f499134dSBaptiste Daroussin static int
91f499134dSBaptiste Daroussin datetime_checksize(int rows, int cols, char *text, int minw, struct buttons bs)
92f499134dSBaptiste Daroussin {
93f499134dSBaptiste Daroussin 	int mincols;
94f499134dSBaptiste Daroussin 
95f499134dSBaptiste Daroussin 	mincols = VBORDERS;
96f499134dSBaptiste Daroussin 	mincols += bs.nbuttons * bs.sizebutton;
97f499134dSBaptiste Daroussin 	mincols += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0;
98f499134dSBaptiste Daroussin 	mincols = MAX(minw, mincols);
99f499134dSBaptiste Daroussin 
100f499134dSBaptiste Daroussin 	if (cols < mincols)
101f499134dSBaptiste Daroussin 		RETURN_ERROR("Few cols for this timebox/datebox");
102f499134dSBaptiste Daroussin 
103f499134dSBaptiste Daroussin 	if (rows < MINHEIGHT + (strlen(text) > 0 ? 1 : 0))
104f499134dSBaptiste Daroussin 		RETURN_ERROR("Few rows for this timebox/datebox");
105f499134dSBaptiste Daroussin 
106f499134dSBaptiste Daroussin 	return 0;
107f499134dSBaptiste Daroussin }
108f499134dSBaptiste Daroussin 
109f499134dSBaptiste Daroussin int bsddialog_timebox(struct bsddialog_conf *conf, char* text, int rows, int cols,
110c76f0793SBaptiste Daroussin     unsigned int *hh, unsigned int *mm, unsigned int *ss)
111c76f0793SBaptiste Daroussin {
112f499134dSBaptiste Daroussin 	WINDOW *widget, *textpad, *shadow;
113f499134dSBaptiste Daroussin 	int i, input, output, y, x, h, w, sel, htextpad;
114c76f0793SBaptiste Daroussin 	struct buttons bs;
115f499134dSBaptiste Daroussin 	bool loop;
116c76f0793SBaptiste Daroussin 	struct myclockstruct {
117c76f0793SBaptiste Daroussin 		unsigned int max;
118f499134dSBaptiste Daroussin 		unsigned int value;
119c76f0793SBaptiste Daroussin 		WINDOW *win;
120f499134dSBaptiste Daroussin 	};
121c76f0793SBaptiste Daroussin 
122f499134dSBaptiste Daroussin 	if (hh == NULL || mm == NULL || ss == NULL)
123f499134dSBaptiste Daroussin 		RETURN_ERROR("hh / mm / ss cannot be NULL");
124c76f0793SBaptiste Daroussin 
125f499134dSBaptiste Daroussin 	struct myclockstruct c[3] = {
126f499134dSBaptiste Daroussin 		{23, *hh, NULL},
127f499134dSBaptiste Daroussin 		{59, *mm, NULL},
128f499134dSBaptiste Daroussin 		{59, *ss, NULL}
129f499134dSBaptiste Daroussin 	};
130f499134dSBaptiste Daroussin 
131f499134dSBaptiste Daroussin 	for (i = 0 ; i < 3; i++) {
132f499134dSBaptiste Daroussin 		if (c[i].value > c[i].max)
133f499134dSBaptiste Daroussin 			c[i].value = c[i].max;
134f499134dSBaptiste Daroussin 	}
135c76f0793SBaptiste Daroussin 
136c76f0793SBaptiste Daroussin 	get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label),
137c76f0793SBaptiste Daroussin 	    BUTTONLABEL(cancel_label), BUTTONLABEL(help_label));
138c76f0793SBaptiste Daroussin 
139f499134dSBaptiste Daroussin 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
140f499134dSBaptiste Daroussin 		return BSDDIALOG_ERROR;
141f499134dSBaptiste Daroussin 	if (datetime_autosize(conf, rows, cols, &h, &w, MINWTIME, text, bs) != 0)
142f499134dSBaptiste Daroussin 		return BSDDIALOG_ERROR;
143f499134dSBaptiste Daroussin 	if (datetime_checksize(h, w, text, MINWTIME, bs) != 0)
144f499134dSBaptiste Daroussin 		return BSDDIALOG_ERROR;
145f499134dSBaptiste Daroussin 	if (set_widget_position(conf, &y, &x, h, w) != 0)
146f499134dSBaptiste Daroussin 		return BSDDIALOG_ERROR;
147f499134dSBaptiste Daroussin 
148f499134dSBaptiste Daroussin 	if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED,
149f499134dSBaptiste Daroussin 	    &textpad, &htextpad, text, true) != 0)
150f499134dSBaptiste Daroussin 		return BSDDIALOG_ERROR;
151f499134dSBaptiste Daroussin 
152f499134dSBaptiste Daroussin 	draw_buttons(widget, h-2, w, bs, true);
153f499134dSBaptiste Daroussin 
154f499134dSBaptiste Daroussin 	wrefresh(widget);
155f499134dSBaptiste Daroussin 
156f499134dSBaptiste Daroussin 	prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2);
157f499134dSBaptiste Daroussin 
158f499134dSBaptiste Daroussin 	c[0].win = new_boxed_window(conf, y + h - 6, x + w/2 - 7, 3, 4, LOWERED);
159f499134dSBaptiste Daroussin 	mvwaddch(widget, h - 5, w/2 - 3, ':');
160f499134dSBaptiste Daroussin 	c[1].win = new_boxed_window(conf, y + h - 6, x + w/2 - 2, 3, 4, LOWERED);
161f499134dSBaptiste Daroussin 	mvwaddch(widget, h - 5, w/2 + 2, ':');
162f499134dSBaptiste Daroussin 	c[2].win = new_boxed_window(conf, y + h - 6, x + w/2 + 3, 3, 4, LOWERED);
163f499134dSBaptiste Daroussin 
164f499134dSBaptiste Daroussin 	wrefresh(widget);
165f499134dSBaptiste Daroussin 
166c76f0793SBaptiste Daroussin 	sel = 0;
167c76f0793SBaptiste Daroussin 	curs_set(2);
168f499134dSBaptiste Daroussin 	loop = true;
169c76f0793SBaptiste Daroussin 	while(loop) {
170c76f0793SBaptiste Daroussin 		for (i=0; i<3; i++) {
171f499134dSBaptiste Daroussin 			mvwprintw(c[i].win, 1, 1, "%2d", c[i].value);
172c76f0793SBaptiste Daroussin 			wrefresh(c[i].win);
173c76f0793SBaptiste Daroussin 		}
174c76f0793SBaptiste Daroussin 		wmove(c[sel].win, 1, 2);
175c76f0793SBaptiste Daroussin 		wrefresh(c[sel].win);
176c76f0793SBaptiste Daroussin 
177c76f0793SBaptiste Daroussin 		input = getch();
178c76f0793SBaptiste Daroussin 		switch(input) {
179f499134dSBaptiste Daroussin 		case KEY_ENTER:
180c76f0793SBaptiste Daroussin 		case 10: /* Enter */
181c76f0793SBaptiste Daroussin 			output = bs.value[bs.curr];
182*8c4f4028SBaptiste Daroussin 			if (output == BSDDIALOG_OK) {
183f499134dSBaptiste Daroussin 				*hh = c[0].value;
184f499134dSBaptiste Daroussin 				*mm = c[1].value;
185f499134dSBaptiste Daroussin 				*ss = c[2].value;
186c76f0793SBaptiste Daroussin 			}
187c76f0793SBaptiste Daroussin 			loop = false;
188c76f0793SBaptiste Daroussin 			break;
189c76f0793SBaptiste Daroussin 		case 27: /* Esc */
190c76f0793SBaptiste Daroussin 			output = BSDDIALOG_ESC;
191c76f0793SBaptiste Daroussin 			loop = false;
192c76f0793SBaptiste Daroussin 			break;
193c76f0793SBaptiste Daroussin 		case '\t': /* TAB */
194c76f0793SBaptiste Daroussin 			sel = (sel + 1) % 3;
195c76f0793SBaptiste Daroussin 			break;
196c76f0793SBaptiste Daroussin 		case KEY_LEFT:
197c76f0793SBaptiste Daroussin 			if (bs.curr > 0) {
198c76f0793SBaptiste Daroussin 				bs.curr--;
199f499134dSBaptiste Daroussin 				draw_buttons(widget, h-2, w, bs, true);
200f499134dSBaptiste Daroussin 				wrefresh(widget);
201c76f0793SBaptiste Daroussin 			}
202c76f0793SBaptiste Daroussin 			break;
203c76f0793SBaptiste Daroussin 		case KEY_RIGHT:
204c76f0793SBaptiste Daroussin 			if (bs.curr < (int) bs.nbuttons - 1) {
205c76f0793SBaptiste Daroussin 				bs.curr++;
206f499134dSBaptiste Daroussin 				draw_buttons(widget, h-2, w, bs, true);
207f499134dSBaptiste Daroussin 				wrefresh(widget);
208c76f0793SBaptiste Daroussin 			}
209c76f0793SBaptiste Daroussin 			break;
210c76f0793SBaptiste Daroussin 		case KEY_UP:
211f499134dSBaptiste Daroussin 			c[sel].value = c[sel].value < c[sel].max ? c[sel].value + 1 : 0;
212c76f0793SBaptiste Daroussin 			break;
213c76f0793SBaptiste Daroussin 		case KEY_DOWN:
214f499134dSBaptiste Daroussin 			c[sel].value = c[sel].value > 0 ? c[sel].value - 1 : c[sel].max;
215c76f0793SBaptiste Daroussin 			break;
216f499134dSBaptiste Daroussin 		case KEY_F(1):
217*8c4f4028SBaptiste Daroussin 			if (conf->f1_file == NULL && conf->f1_message == NULL)
218f499134dSBaptiste Daroussin 				break;
219f499134dSBaptiste Daroussin 			curs_set(0);
220f499134dSBaptiste Daroussin 			if (f1help(conf) != 0)
221f499134dSBaptiste Daroussin 				return BSDDIALOG_ERROR;
222f499134dSBaptiste Daroussin 			curs_set(2);
223f499134dSBaptiste Daroussin 			/* No break! the terminal size can change */
224f499134dSBaptiste Daroussin 		case KEY_RESIZE:
225f499134dSBaptiste Daroussin 			hide_widget(y, x, h, w,conf->shadow);
226f499134dSBaptiste Daroussin 
227f499134dSBaptiste Daroussin 			/*
228f499134dSBaptiste Daroussin 			 * Unnecessary, but, when the columns decrease the
229f499134dSBaptiste Daroussin 			 * following "refresh" seem not work
230f499134dSBaptiste Daroussin 			 */
231f499134dSBaptiste Daroussin 			refresh();
232f499134dSBaptiste Daroussin 
233f499134dSBaptiste Daroussin 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
234f499134dSBaptiste Daroussin 				return BSDDIALOG_ERROR;
235f499134dSBaptiste Daroussin 			if (datetime_autosize(conf, rows, cols, &h, &w, MINWTIME, text, bs) != 0)
236f499134dSBaptiste Daroussin 				return BSDDIALOG_ERROR;
237f499134dSBaptiste Daroussin 			if (datetime_checksize(h, w, text, MINWTIME, bs) != 0)
238f499134dSBaptiste Daroussin 				return BSDDIALOG_ERROR;
239f499134dSBaptiste Daroussin 			if (set_widget_position(conf, &y, &x, h, w) != 0)
240f499134dSBaptiste Daroussin 				return BSDDIALOG_ERROR;
241f499134dSBaptiste Daroussin 
242f499134dSBaptiste Daroussin 			wclear(shadow);
243f499134dSBaptiste Daroussin 			mvwin(shadow, y + t.shadow.h, x + t.shadow.w);
244f499134dSBaptiste Daroussin 			wresize(shadow, h, w);
245f499134dSBaptiste Daroussin 
246f499134dSBaptiste Daroussin 			wclear(widget);
247f499134dSBaptiste Daroussin 			mvwin(widget, y, x);
248f499134dSBaptiste Daroussin 			wresize(widget, h, w);
249f499134dSBaptiste Daroussin 
250f499134dSBaptiste Daroussin 			htextpad = 1;
251f499134dSBaptiste Daroussin 			wclear(textpad);
252f499134dSBaptiste Daroussin 			wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2);
253f499134dSBaptiste Daroussin 
254f499134dSBaptiste Daroussin 			if(update_widget_withtextpad(conf, shadow, widget, h, w,
255f499134dSBaptiste Daroussin 			    RAISED, textpad, &htextpad, text, true) != 0)
256f499134dSBaptiste Daroussin 				return BSDDIALOG_ERROR;
257f499134dSBaptiste Daroussin 
258f499134dSBaptiste Daroussin 			mvwaddch(widget, h - 5, w/2 - 3, ':');
259f499134dSBaptiste Daroussin 			mvwaddch(widget, h - 5, w/2 + 2, ':');
260f499134dSBaptiste Daroussin 
261f499134dSBaptiste Daroussin 			draw_buttons(widget, h-2, w, bs, true);
262f499134dSBaptiste Daroussin 
263f499134dSBaptiste Daroussin 			wrefresh(widget);
264f499134dSBaptiste Daroussin 
265f499134dSBaptiste Daroussin 			prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2);
266f499134dSBaptiste Daroussin 
267f499134dSBaptiste Daroussin 			wclear(c[0].win);
268f499134dSBaptiste Daroussin 			mvwin(c[0].win, y + h - 6, x + w/2 - 7);
269f499134dSBaptiste Daroussin 			draw_borders(conf, c[0].win, 3, 4, LOWERED);
270f499134dSBaptiste Daroussin 			wrefresh(c[0].win);
271f499134dSBaptiste Daroussin 
272f499134dSBaptiste Daroussin 			wclear(c[1].win);
273f499134dSBaptiste Daroussin 			mvwin(c[1].win, y + h - 6, x + w/2 - 2);
274f499134dSBaptiste Daroussin 			draw_borders(conf, c[1].win, 3, 4, LOWERED);
275f499134dSBaptiste Daroussin 			wrefresh(c[1].win);
276f499134dSBaptiste Daroussin 
277f499134dSBaptiste Daroussin 			wclear(c[2].win);
278f499134dSBaptiste Daroussin 			mvwin(c[2].win, y + h - 6, x + w/2 + 3);
279f499134dSBaptiste Daroussin 			draw_borders(conf, c[2].win, 3, 4, LOWERED);
280f499134dSBaptiste Daroussin 			wrefresh(c[2].win);
281f499134dSBaptiste Daroussin 
282f499134dSBaptiste Daroussin 			/* Important to avoid grey lines expanding screen */
283f499134dSBaptiste Daroussin 			refresh();
284f499134dSBaptiste Daroussin 			break;
285f499134dSBaptiste Daroussin 		default:
286f499134dSBaptiste Daroussin 			for (i = 0; i < (int) bs.nbuttons; i++)
287f499134dSBaptiste Daroussin 				if (tolower(input) == tolower((bs.label[i])[0])) {
288f499134dSBaptiste Daroussin 					output = bs.value[i];
289f499134dSBaptiste Daroussin 					loop = false;
290f499134dSBaptiste Daroussin 			}
291c76f0793SBaptiste Daroussin 		}
292c76f0793SBaptiste Daroussin 	}
293c76f0793SBaptiste Daroussin 
294c76f0793SBaptiste Daroussin 	curs_set(0);
295c76f0793SBaptiste Daroussin 
296c76f0793SBaptiste Daroussin 	for (i=0; i<3; i++)
297c76f0793SBaptiste Daroussin 		delwin(c[i].win);
298f499134dSBaptiste Daroussin 	end_widget_withtextpad(conf, widget, h, w, textpad, shadow);
299c76f0793SBaptiste Daroussin 
300c76f0793SBaptiste Daroussin 	return output;
301c76f0793SBaptiste Daroussin }
302c76f0793SBaptiste Daroussin 
303f499134dSBaptiste Daroussin int
304f499134dSBaptiste Daroussin bsddialog_datebox(struct bsddialog_conf *conf, char* text, int rows, int cols,
305c76f0793SBaptiste Daroussin     unsigned int *yy, unsigned int *mm, unsigned int *dd)
306c76f0793SBaptiste Daroussin {
307f499134dSBaptiste Daroussin 	WINDOW *widget, *textpad, *shadow;
308f499134dSBaptiste Daroussin 	int i, input, output, y, x, h, w, sel, htextpad;
309c76f0793SBaptiste Daroussin 	struct buttons bs;
310f499134dSBaptiste Daroussin 	bool loop;
311c76f0793SBaptiste Daroussin 	struct calendar {
312f499134dSBaptiste Daroussin 		int max;
313f499134dSBaptiste Daroussin 		int value;
314c76f0793SBaptiste Daroussin 		WINDOW *win;
315c76f0793SBaptiste Daroussin 		unsigned int x;
316f499134dSBaptiste Daroussin 	};
317c76f0793SBaptiste Daroussin 	struct month {
318c76f0793SBaptiste Daroussin 		char *name;
319c76f0793SBaptiste Daroussin 		unsigned int days;
320c76f0793SBaptiste Daroussin 	};
321c76f0793SBaptiste Daroussin 
322f499134dSBaptiste Daroussin 	if (yy == NULL || mm == NULL || dd == NULL)
323f499134dSBaptiste Daroussin 		RETURN_ERROR("yy / mm / dd cannot be NULL");
324c76f0793SBaptiste Daroussin 
325f499134dSBaptiste Daroussin 	struct calendar c[3] = {
326f499134dSBaptiste Daroussin 		{9999, *yy, NULL, 4 },
327f499134dSBaptiste Daroussin 		{12,   *mm, NULL, 9 },
328f499134dSBaptiste Daroussin 		{31,   *dd, NULL, 2 }
329f499134dSBaptiste Daroussin 	};
330c76f0793SBaptiste Daroussin 
331f499134dSBaptiste Daroussin 	struct month m[12] = {
332f499134dSBaptiste Daroussin 		{ "January", 31 }, { "February", 28 }, { "March",     31 },
333f499134dSBaptiste Daroussin 		{ "April",   30 }, { "May",      31 }, { "June",      30 },
334f499134dSBaptiste Daroussin 		{ "July",    31 }, { "August",   31 }, { "September", 30 },
335f499134dSBaptiste Daroussin 		{ "October", 31 }, { "November", 30 }, { "December",  31 }
336f499134dSBaptiste Daroussin 	};
337f499134dSBaptiste Daroussin 
338f499134dSBaptiste Daroussin #define ISLEAF(year) ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
339f499134dSBaptiste Daroussin 
340f499134dSBaptiste Daroussin 	for (i = 0 ; i < 3; i++) {
341f499134dSBaptiste Daroussin 		if (c[i].value > c[i].max)
342f499134dSBaptiste Daroussin 			c[i].value = c[i].max;
343f499134dSBaptiste Daroussin 		if (c[i].value < 1)
344f499134dSBaptiste Daroussin 			c[i].value = 1;
345f499134dSBaptiste Daroussin 	}
346f499134dSBaptiste Daroussin 	c[2].max = m[c[1].value -1].days;
347f499134dSBaptiste Daroussin 	if (c[1].value == 2 && ISLEAF(c[0].value))
348f499134dSBaptiste Daroussin 		c[2].max = 29;
349f499134dSBaptiste Daroussin 	if (c[2].value > c[2].max)
350f499134dSBaptiste Daroussin 		c[2].value = c[2].max;
351c76f0793SBaptiste Daroussin 
352c76f0793SBaptiste Daroussin 	get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label),
353c76f0793SBaptiste Daroussin 	    BUTTONLABEL(cancel_label), BUTTONLABEL(help_label));
354c76f0793SBaptiste Daroussin 
355f499134dSBaptiste Daroussin 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
356f499134dSBaptiste Daroussin 		return BSDDIALOG_ERROR;
357f499134dSBaptiste Daroussin 	if (datetime_autosize(conf, rows, cols, &h, &w, MINWDATE, text, bs) != 0)
358f499134dSBaptiste Daroussin 		return BSDDIALOG_ERROR;
359f499134dSBaptiste Daroussin 	if (datetime_checksize(h, w, text, MINWDATE, bs) != 0)
360f499134dSBaptiste Daroussin 		return BSDDIALOG_ERROR;
361f499134dSBaptiste Daroussin 	if (set_widget_position(conf, &y, &x, h, w) != 0)
362f499134dSBaptiste Daroussin 		return BSDDIALOG_ERROR;
363f499134dSBaptiste Daroussin 
364f499134dSBaptiste Daroussin 	if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED,
365f499134dSBaptiste Daroussin 	    &textpad, &htextpad, text, true) != 0)
366f499134dSBaptiste Daroussin 		return BSDDIALOG_ERROR;
367f499134dSBaptiste Daroussin 
368f499134dSBaptiste Daroussin 	draw_buttons(widget, h-2, w, bs, true);
369f499134dSBaptiste Daroussin 
370f499134dSBaptiste Daroussin 	wrefresh(widget);
371f499134dSBaptiste Daroussin 
372f499134dSBaptiste Daroussin 	prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2);
373f499134dSBaptiste Daroussin 
374f499134dSBaptiste Daroussin 	c[0].win = new_boxed_window(conf, y + h - 6, x + w/2 - 11, 3, 6, LOWERED);
375f499134dSBaptiste Daroussin 	mvwaddch(widget, h - 5, w/2 - 5, '/');
376f499134dSBaptiste Daroussin 	c[1].win = new_boxed_window(conf, y + h - 6, x + w/2 - 4, 3, 11, LOWERED);
377f499134dSBaptiste Daroussin 	mvwaddch(widget, h - 5, w/2 + 7, '/');
378f499134dSBaptiste Daroussin 	c[2].win = new_boxed_window(conf, y + h - 6, x + w/2 + 8, 3, 4, LOWERED);
379f499134dSBaptiste Daroussin 
380f499134dSBaptiste Daroussin 	wrefresh(widget);
381f499134dSBaptiste Daroussin 
382c76f0793SBaptiste Daroussin 	sel = 2;
383c76f0793SBaptiste Daroussin 	curs_set(2);
384f499134dSBaptiste Daroussin 	loop = true;
385c76f0793SBaptiste Daroussin 	while(loop) {
386f499134dSBaptiste Daroussin 		mvwprintw(c[0].win, 1, 1, "%4d", c[0].value);
387f499134dSBaptiste Daroussin 		mvwprintw(c[1].win, 1, 1, "%9s", m[c[1].value-1].name);
388f499134dSBaptiste Daroussin 		mvwprintw(c[2].win, 1, 1, "%2d", c[2].value);
389c76f0793SBaptiste Daroussin 		for (i=0; i<3; i++) {
390c76f0793SBaptiste Daroussin 			wrefresh(c[i].win);
391c76f0793SBaptiste Daroussin 		}
392c76f0793SBaptiste Daroussin 		wmove(c[sel].win, 1, c[sel].x);
393c76f0793SBaptiste Daroussin 		wrefresh(c[sel].win);
394c76f0793SBaptiste Daroussin 
395c76f0793SBaptiste Daroussin 		input = getch();
396c76f0793SBaptiste Daroussin 		switch(input) {
397f499134dSBaptiste Daroussin 		case KEY_ENTER:
398f499134dSBaptiste Daroussin 		case 10: /* Enter */
399f499134dSBaptiste Daroussin 			output = bs.value[bs.curr];
400*8c4f4028SBaptiste Daroussin 			if (output == BSDDIALOG_OK) {
401f499134dSBaptiste Daroussin 				*yy = c[0].value;
402f499134dSBaptiste Daroussin 				*mm = c[1].value;
403f499134dSBaptiste Daroussin 				*dd = c[2].value;
404c76f0793SBaptiste Daroussin 			}
405c76f0793SBaptiste Daroussin 			loop = false;
406c76f0793SBaptiste Daroussin 			break;
407f499134dSBaptiste Daroussin 		case 27: /* Esc */
408c76f0793SBaptiste Daroussin 			output = BSDDIALOG_ESC;
409c76f0793SBaptiste Daroussin 			loop = false;
410c76f0793SBaptiste Daroussin 			break;
411f499134dSBaptiste Daroussin 		case '\t': /* TAB */
412c76f0793SBaptiste Daroussin 			sel = (sel + 1) % 3;
413c76f0793SBaptiste Daroussin 			break;
414c76f0793SBaptiste Daroussin 		case KEY_LEFT:
415c76f0793SBaptiste Daroussin 			if (bs.curr > 0) {
416c76f0793SBaptiste Daroussin 				bs.curr--;
417f499134dSBaptiste Daroussin 				draw_buttons(widget, h-2, w, bs, true);
418f499134dSBaptiste Daroussin 				wrefresh(widget);
419c76f0793SBaptiste Daroussin 			}
420c76f0793SBaptiste Daroussin 			break;
421c76f0793SBaptiste Daroussin 		case KEY_RIGHT:
422c76f0793SBaptiste Daroussin 			if (bs.curr < (int) bs.nbuttons - 1) {
423c76f0793SBaptiste Daroussin 				bs.curr++;
424f499134dSBaptiste Daroussin 				draw_buttons(widget, h-2, w, bs, true);
425f499134dSBaptiste Daroussin 				wrefresh(widget);
426c76f0793SBaptiste Daroussin 			}
427c76f0793SBaptiste Daroussin 			break;
428c76f0793SBaptiste Daroussin 		case KEY_UP:
429f499134dSBaptiste Daroussin 			c[sel].value = c[sel].value > 1 ? c[sel].value - 1 : c[sel].max ;
430f499134dSBaptiste Daroussin 			/* if mount change */
431f499134dSBaptiste Daroussin 			c[2].max = m[c[1].value -1].days;
432f499134dSBaptiste Daroussin 			/* if year change */
433f499134dSBaptiste Daroussin 			if (c[1].value == 2 && ISLEAF(c[0].value))
434f499134dSBaptiste Daroussin 				c[2].max = 29;
435f499134dSBaptiste Daroussin 			/* set new day */
436f499134dSBaptiste Daroussin 			if (c[2].value > c[2].max)
437f499134dSBaptiste Daroussin 				c[2].value = c[2].max;
438c76f0793SBaptiste Daroussin 			break;
439c76f0793SBaptiste Daroussin 		case KEY_DOWN:
440f499134dSBaptiste Daroussin 			c[sel].value = c[sel].value < c[sel].max ? c[sel].value + 1 : 1;
441f499134dSBaptiste Daroussin 			/* if mount change */
442f499134dSBaptiste Daroussin 			c[2].max = m[c[1].value -1].days;
443f499134dSBaptiste Daroussin 			/* if year change */
444f499134dSBaptiste Daroussin 			if (c[1].value == 2 && ISLEAF(c[0].value))
445f499134dSBaptiste Daroussin 				c[2].max = 29;
446f499134dSBaptiste Daroussin 			/* set new day */
447f499134dSBaptiste Daroussin 			if (c[2].value > c[2].max)
448f499134dSBaptiste Daroussin 				c[2].value = c[2].max;
449c76f0793SBaptiste Daroussin 			break;
450f499134dSBaptiste Daroussin 		case KEY_F(1):
451*8c4f4028SBaptiste Daroussin 			if (conf->f1_file == NULL && conf->f1_message == NULL)
452f499134dSBaptiste Daroussin 				break;
453f499134dSBaptiste Daroussin 			curs_set(0);
454f499134dSBaptiste Daroussin 			if (f1help(conf) != 0)
455f499134dSBaptiste Daroussin 				return BSDDIALOG_ERROR;
456f499134dSBaptiste Daroussin 			curs_set(2);
457f499134dSBaptiste Daroussin 			/* No break! the terminal size can change */
458f499134dSBaptiste Daroussin 		case KEY_RESIZE:
459f499134dSBaptiste Daroussin 			hide_widget(y, x, h, w,conf->shadow);
460f499134dSBaptiste Daroussin 
461f499134dSBaptiste Daroussin 			/*
462f499134dSBaptiste Daroussin 			 * Unnecessary, but, when the columns decrease the
463f499134dSBaptiste Daroussin 			 * following "refresh" seem not work
464f499134dSBaptiste Daroussin 			 */
465f499134dSBaptiste Daroussin 			refresh();
466f499134dSBaptiste Daroussin 
467f499134dSBaptiste Daroussin 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
468f499134dSBaptiste Daroussin 				return BSDDIALOG_ERROR;
469f499134dSBaptiste Daroussin 			if (datetime_autosize(conf, rows, cols, &h, &w, MINWDATE, text, bs) != 0)
470f499134dSBaptiste Daroussin 				return BSDDIALOG_ERROR;
471f499134dSBaptiste Daroussin 			if (datetime_checksize(h, w, text, MINWDATE, bs) != 0)
472f499134dSBaptiste Daroussin 				return BSDDIALOG_ERROR;
473f499134dSBaptiste Daroussin 			if (set_widget_position(conf, &y, &x, h, w) != 0)
474f499134dSBaptiste Daroussin 				return BSDDIALOG_ERROR;
475f499134dSBaptiste Daroussin 
476f499134dSBaptiste Daroussin 			wclear(shadow);
477f499134dSBaptiste Daroussin 			mvwin(shadow, y + t.shadow.h, x + t.shadow.w);
478f499134dSBaptiste Daroussin 			wresize(shadow, h, w);
479f499134dSBaptiste Daroussin 
480f499134dSBaptiste Daroussin 			wclear(widget);
481f499134dSBaptiste Daroussin 			mvwin(widget, y, x);
482f499134dSBaptiste Daroussin 			wresize(widget, h, w);
483f499134dSBaptiste Daroussin 
484f499134dSBaptiste Daroussin 			htextpad = 1;
485f499134dSBaptiste Daroussin 			wclear(textpad);
486f499134dSBaptiste Daroussin 			wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2);
487f499134dSBaptiste Daroussin 
488f499134dSBaptiste Daroussin 			if(update_widget_withtextpad(conf, shadow, widget, h, w,
489f499134dSBaptiste Daroussin 			    RAISED, textpad, &htextpad, text, true) != 0)
490f499134dSBaptiste Daroussin 				return BSDDIALOG_ERROR;
491f499134dSBaptiste Daroussin 
492f499134dSBaptiste Daroussin 			mvwaddch(widget, h - 5, w/2 - 5, '/');
493f499134dSBaptiste Daroussin 			mvwaddch(widget, h - 5, w/2 + 7, '/');
494f499134dSBaptiste Daroussin 
495f499134dSBaptiste Daroussin 			draw_buttons(widget, h-2, w, bs, true);
496f499134dSBaptiste Daroussin 
497f499134dSBaptiste Daroussin 			wrefresh(widget);
498f499134dSBaptiste Daroussin 
499f499134dSBaptiste Daroussin 			prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2);
500f499134dSBaptiste Daroussin 
501f499134dSBaptiste Daroussin 			wclear(c[0].win);
502f499134dSBaptiste Daroussin 			mvwin(c[0].win, y + h - 6, x + w/2 - 11);
503f499134dSBaptiste Daroussin 			draw_borders(conf, c[0].win, 3, 6, LOWERED);
504f499134dSBaptiste Daroussin 			wrefresh(c[0].win);
505f499134dSBaptiste Daroussin 
506f499134dSBaptiste Daroussin 			wclear(c[1].win);
507f499134dSBaptiste Daroussin 			mvwin(c[1].win, y + h - 6, x + w/2 - 4);
508f499134dSBaptiste Daroussin 			draw_borders(conf, c[1].win, 3, 11, LOWERED);
509f499134dSBaptiste Daroussin 			wrefresh(c[1].win);
510f499134dSBaptiste Daroussin 
511f499134dSBaptiste Daroussin 			wclear(c[2].win);
512f499134dSBaptiste Daroussin 			mvwin(c[2].win, y + h - 6, x + w/2 + 8);
513f499134dSBaptiste Daroussin 			draw_borders(conf, c[2].win, 3, 4, LOWERED);
514f499134dSBaptiste Daroussin 			wrefresh(c[2].win);
515f499134dSBaptiste Daroussin 
516f499134dSBaptiste Daroussin 			/* Important to avoid grey lines expanding screen */
517f499134dSBaptiste Daroussin 			refresh();
518f499134dSBaptiste Daroussin 			break;
519f499134dSBaptiste Daroussin 		default:
520f499134dSBaptiste Daroussin 			for (i = 0; i < (int) bs.nbuttons; i++)
521f499134dSBaptiste Daroussin 				if (tolower(input) == tolower((bs.label[i])[0])) {
522f499134dSBaptiste Daroussin 					output = bs.value[i];
523f499134dSBaptiste Daroussin 					loop = false;
524f499134dSBaptiste Daroussin 			}
525c76f0793SBaptiste Daroussin 		}
526c76f0793SBaptiste Daroussin 	}
527c76f0793SBaptiste Daroussin 
528c76f0793SBaptiste Daroussin 	curs_set(0);
529c76f0793SBaptiste Daroussin 
530c76f0793SBaptiste Daroussin 	for (i=0; i<3; i++)
531c76f0793SBaptiste Daroussin 		delwin(c[i].win);
532f499134dSBaptiste Daroussin 	end_widget_withtextpad(conf, widget, h, w, textpad, shadow);
533c76f0793SBaptiste Daroussin 
534c76f0793SBaptiste Daroussin 	return output;
535c76f0793SBaptiste Daroussin }
536