1217309Snwhitehorn/*
2251843Sbapt * $Id: timebox.c,v 1.54 2013/03/17 15:03:41 tom Exp $
3217309Snwhitehorn *
4217309Snwhitehorn *  timebox.c -- implements the timebox dialog
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_HIGH 1
30217309Snwhitehorn#define ONE_WIDE 2
31217309Snwhitehorn#define BTN_HIGH 2
32217309Snwhitehorn
33217309Snwhitehorn#define MIN_HIGH (ONE_HIGH + BTN_HIGH + (4 * MARGIN))
34217309Snwhitehorn#define MIN_WIDE ((3 * (ONE_WIDE + 2 * MARGIN)) + 2 + (2 * MARGIN))
35217309Snwhitehorn
36217309Snwhitehorntypedef enum {
37217309Snwhitehorn    sHR = -3
38217309Snwhitehorn    ,sMN = -2
39217309Snwhitehorn    ,sSC = -1
40217309Snwhitehorn} STATES;
41217309Snwhitehorn
42217309Snwhitehornstruct _box;
43217309Snwhitehorn
44217309Snwhitehorntypedef struct _box {
45217309Snwhitehorn    WINDOW *parent;
46217309Snwhitehorn    WINDOW *window;
47217309Snwhitehorn    int x;
48217309Snwhitehorn    int y;
49217309Snwhitehorn    int width;
50217309Snwhitehorn    int height;
51217309Snwhitehorn    int period;
52217309Snwhitehorn    int value;
53217309Snwhitehorn} BOX;
54217309Snwhitehorn
55217309Snwhitehornstatic int
56217309Snwhitehornnext_or_previous(int key)
57217309Snwhitehorn{
58217309Snwhitehorn    int result = 0;
59217309Snwhitehorn
60217309Snwhitehorn    switch (key) {
61217309Snwhitehorn    case DLGK_ITEM_PREV:
62217309Snwhitehorn	result = -1;
63217309Snwhitehorn	break;
64217309Snwhitehorn    case DLGK_ITEM_NEXT:
65217309Snwhitehorn	result = 1;
66217309Snwhitehorn	break;
67217309Snwhitehorn    default:
68217309Snwhitehorn	beep();
69217309Snwhitehorn	break;
70217309Snwhitehorn    }
71217309Snwhitehorn    return result;
72217309Snwhitehorn}
73217309Snwhitehorn/*
74217309Snwhitehorn * Draw the hour-of-month selection box
75217309Snwhitehorn */
76217309Snwhitehornstatic int
77217309Snwhitehorndraw_cell(BOX * data)
78217309Snwhitehorn{
79217309Snwhitehorn    werase(data->window);
80217309Snwhitehorn    dlg_draw_box(data->parent,
81217309Snwhitehorn		 data->y - MARGIN, data->x - MARGIN,
82217309Snwhitehorn		 data->height + (2 * MARGIN), data->width + (2 * MARGIN),
83251843Sbapt		 menubox_border_attr, menubox_border2_attr);
84217309Snwhitehorn
85251843Sbapt    (void) wattrset(data->window, item_attr);
86217309Snwhitehorn    wprintw(data->window, "%02d", data->value);
87217309Snwhitehorn    return 0;
88217309Snwhitehorn}
89217309Snwhitehorn
90217309Snwhitehornstatic int
91217309Snwhitehorninit_object(BOX * data,
92217309Snwhitehorn	    WINDOW *parent,
93217309Snwhitehorn	    int x, int y,
94217309Snwhitehorn	    int width, int height,
95217309Snwhitehorn	    int period, int value,
96217309Snwhitehorn	    int code)
97217309Snwhitehorn{
98220749Snwhitehorn    (void) code;
99220749Snwhitehorn
100217309Snwhitehorn    data->parent = parent;
101217309Snwhitehorn    data->x = x;
102217309Snwhitehorn    data->y = y;
103217309Snwhitehorn    data->width = width;
104217309Snwhitehorn    data->height = height;
105217309Snwhitehorn    data->period = period;
106217309Snwhitehorn    data->value = value % period;
107217309Snwhitehorn
108217309Snwhitehorn    data->window = derwin(data->parent,
109217309Snwhitehorn			  data->height, data->width,
110217309Snwhitehorn			  data->y, data->x);
111217309Snwhitehorn    if (data->window == 0)
112217309Snwhitehorn	return -1;
113217309Snwhitehorn    (void) keypad(data->window, TRUE);
114217309Snwhitehorn
115217309Snwhitehorn    dlg_mouse_setbase(getbegx(parent), getbegy(parent));
116217309Snwhitehorn    dlg_mouse_mkregion(y, x, height, width, code);
117217309Snwhitehorn
118217309Snwhitehorn    return 0;
119217309Snwhitehorn}
120217309Snwhitehorn
121217309Snwhitehornstatic int
122217309SnwhitehornCleanupResult(int code, WINDOW *dialog, char *prompt, DIALOG_VARS * save_vars)
123217309Snwhitehorn{
124217309Snwhitehorn    dlg_del_window(dialog);
125217309Snwhitehorn    dlg_mouse_free_regions();
126217309Snwhitehorn    free(prompt);
127217309Snwhitehorn    dlg_restore_vars(save_vars);
128217309Snwhitehorn
129217309Snwhitehorn    return code;
130217309Snwhitehorn}
131217309Snwhitehorn
132217309Snwhitehorn#define DrawObject(data) draw_cell(data)
133217309Snwhitehorn
134217309Snwhitehorn/*
135217309Snwhitehorn * Display a dialog box for entering a date
136217309Snwhitehorn */
137217309Snwhitehornint
138217309Snwhitehorndialog_timebox(const char *title,
139217309Snwhitehorn	       const char *subtitle,
140217309Snwhitehorn	       int height,
141217309Snwhitehorn	       int width,
142217309Snwhitehorn	       int hour,
143217309Snwhitehorn	       int minute,
144217309Snwhitehorn	       int second)
145217309Snwhitehorn{
146217309Snwhitehorn    /* *INDENT-OFF* */
147217309Snwhitehorn    static DLG_KEYS_BINDING binding[] = {
148217309Snwhitehorn	DLG_KEYS_DATA( DLGK_DELETE_RIGHT,KEY_DC ),
149224014Snwhitehorn	HELPKEY_BINDINGS,
150217309Snwhitehorn	ENTERKEY_BINDINGS,
151217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ENTER,	' ' ),
152217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_FIRST,KEY_HOME ),
153217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_LAST, KEY_END ),
154217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_LAST, KEY_LL ),
155217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_NEXT, CHR_NEXT ),
156217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
157217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
158217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_PREV, CHR_BACKSPACE ),
159217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_PREV, CHR_PREVIOUS ),
160217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
161217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
162217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  '+'),
163217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_DOWN),
164217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_NEXT),
165217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_NPAGE),
166217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_PREV,  '-' ),
167217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_PPAGE ),
168217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_PREVIOUS ),
169217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_UP ),
170217309Snwhitehorn	END_KEYS_BINDING
171217309Snwhitehorn    };
172217309Snwhitehorn    /* *INDENT-ON* */
173217309Snwhitehorn
174217309Snwhitehorn#ifdef KEY_RESIZE
175217309Snwhitehorn    int old_height = height;
176217309Snwhitehorn    int old_width = width;
177217309Snwhitehorn#endif
178217309Snwhitehorn    BOX hr_box, mn_box, sc_box;
179217309Snwhitehorn    int key = 0, key2, fkey;
180217309Snwhitehorn    int button;
181217309Snwhitehorn    int result = DLG_EXIT_UNKNOWN;
182217309Snwhitehorn    WINDOW *dialog;
183217309Snwhitehorn    time_t now_time = time((time_t *) 0);
184217309Snwhitehorn    struct tm current;
185251843Sbapt    int state = dlg_default_button();
186217309Snwhitehorn    const char **buttons = dlg_ok_labels();
187217309Snwhitehorn    char *prompt = dlg_strclone(subtitle);
188217309Snwhitehorn    char buffer[MAX_LEN];
189217309Snwhitehorn    DIALOG_VARS save_vars;
190217309Snwhitehorn
191217309Snwhitehorn    now_time = time((time_t *) 0);
192217309Snwhitehorn    current = *localtime(&now_time);
193217309Snwhitehorn
194217309Snwhitehorn    dlg_save_vars(&save_vars);
195217309Snwhitehorn    dialog_vars.separate_output = TRUE;
196217309Snwhitehorn
197217309Snwhitehorn    dlg_does_output();
198217309Snwhitehorn
199217309Snwhitehorn#ifdef KEY_RESIZE
200217309Snwhitehorn  retry:
201217309Snwhitehorn#endif
202217309Snwhitehorn
203217309Snwhitehorn    dlg_auto_size(title, prompt, &height, &width, 0, 0);
204217309Snwhitehorn    height += MIN_HIGH;
205217309Snwhitehorn    if (width < MIN_WIDE)
206217309Snwhitehorn	width = MIN_WIDE;
207217309Snwhitehorn    dlg_button_layout(buttons, &width);
208217309Snwhitehorn    dlg_print_size(height, width);
209217309Snwhitehorn    dlg_ctl_size(height, width);
210217309Snwhitehorn
211217309Snwhitehorn    dialog = dlg_new_window(height, width,
212217309Snwhitehorn			    dlg_box_y_ordinate(height),
213217309Snwhitehorn			    dlg_box_x_ordinate(width));
214220749Snwhitehorn
215220749Snwhitehorn    if (hour >= 24 || minute >= 60 || second >= 60) {
216220749Snwhitehorn	return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
217220749Snwhitehorn    }
218220749Snwhitehorn
219217309Snwhitehorn    dlg_register_window(dialog, "timebox", binding);
220217309Snwhitehorn    dlg_register_buttons(dialog, "timebox", buttons);
221217309Snwhitehorn
222251843Sbapt    dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr);
223251843Sbapt    dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr);
224217309Snwhitehorn    dlg_draw_title(dialog, title);
225224014Snwhitehorn    dlg_draw_helpline(dialog, FALSE);
226217309Snwhitehorn
227251843Sbapt    (void) wattrset(dialog, dialog_attr);
228217309Snwhitehorn    dlg_print_autowrap(dialog, prompt, height, width);
229217309Snwhitehorn
230217309Snwhitehorn    /* compute positions of hour, month and year boxes */
231217309Snwhitehorn    memset(&hr_box, 0, sizeof(hr_box));
232217309Snwhitehorn    memset(&mn_box, 0, sizeof(mn_box));
233217309Snwhitehorn    memset(&sc_box, 0, sizeof(sc_box));
234217309Snwhitehorn
235217309Snwhitehorn    if (init_object(&hr_box,
236217309Snwhitehorn		    dialog,
237217309Snwhitehorn		    (width - MIN_WIDE + 1) / 2 + MARGIN,
238217309Snwhitehorn		    (height - MIN_HIGH + MARGIN),
239217309Snwhitehorn		    ONE_WIDE,
240217309Snwhitehorn		    ONE_HIGH,
241217309Snwhitehorn		    24,
242217309Snwhitehorn		    hour >= 0 ? hour : current.tm_hour,
243217309Snwhitehorn		    'H') < 0
244217309Snwhitehorn	|| DrawObject(&hr_box) < 0) {
245217309Snwhitehorn	return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
246217309Snwhitehorn    }
247217309Snwhitehorn
248217309Snwhitehorn    mvwprintw(dialog, hr_box.y, hr_box.x + ONE_WIDE + MARGIN, ":");
249217309Snwhitehorn    if (init_object(&mn_box,
250217309Snwhitehorn		    dialog,
251217309Snwhitehorn		    hr_box.x + (ONE_WIDE + 2 * MARGIN + 1),
252217309Snwhitehorn		    hr_box.y,
253217309Snwhitehorn		    hr_box.width,
254217309Snwhitehorn		    hr_box.height,
255217309Snwhitehorn		    60,
256217309Snwhitehorn		    minute >= 0 ? minute : current.tm_min,
257217309Snwhitehorn		    'M') < 0
258217309Snwhitehorn	|| DrawObject(&mn_box) < 0) {
259217309Snwhitehorn	return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
260217309Snwhitehorn    }
261217309Snwhitehorn
262217309Snwhitehorn    mvwprintw(dialog, mn_box.y, mn_box.x + ONE_WIDE + MARGIN, ":");
263217309Snwhitehorn    if (init_object(&sc_box,
264217309Snwhitehorn		    dialog,
265217309Snwhitehorn		    mn_box.x + (ONE_WIDE + 2 * MARGIN + 1),
266217309Snwhitehorn		    mn_box.y,
267217309Snwhitehorn		    mn_box.width,
268217309Snwhitehorn		    mn_box.height,
269217309Snwhitehorn		    60,
270217309Snwhitehorn		    second >= 0 ? second : current.tm_sec,
271217309Snwhitehorn		    'S') < 0
272217309Snwhitehorn	|| DrawObject(&sc_box) < 0) {
273217309Snwhitehorn	return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
274217309Snwhitehorn    }
275217309Snwhitehorn
276251843Sbapt    dlg_trace_win(dialog);
277217309Snwhitehorn    while (result == DLG_EXIT_UNKNOWN) {
278217309Snwhitehorn	BOX *obj = (state == sHR ? &hr_box
279217309Snwhitehorn		    : (state == sMN ? &mn_box :
280217309Snwhitehorn		       (state == sSC ? &sc_box : 0)));
281217309Snwhitehorn
282217309Snwhitehorn	button = (state < 0) ? 0 : state;
283217309Snwhitehorn	dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
284217309Snwhitehorn	if (obj != 0)
285217309Snwhitehorn	    dlg_set_focus(dialog, obj->window);
286217309Snwhitehorn
287217309Snwhitehorn	key = dlg_mouse_wgetch(dialog, &fkey);
288217309Snwhitehorn	if (dlg_result_key(key, fkey, &result))
289217309Snwhitehorn	    break;
290217309Snwhitehorn
291217309Snwhitehorn	if ((key2 = dlg_char_to_button(key, buttons)) >= 0) {
292217309Snwhitehorn	    result = key2;
293217309Snwhitehorn	} else {
294217309Snwhitehorn	    /* handle function-keys */
295217309Snwhitehorn	    if (fkey) {
296217309Snwhitehorn		switch (key) {
297217309Snwhitehorn		case DLGK_MOUSE('H'):
298217309Snwhitehorn		    state = sHR;
299217309Snwhitehorn		    break;
300217309Snwhitehorn		case DLGK_MOUSE('M'):
301217309Snwhitehorn		    state = sMN;
302217309Snwhitehorn		    break;
303217309Snwhitehorn		case DLGK_MOUSE('S'):
304217309Snwhitehorn		    state = sSC;
305217309Snwhitehorn		    break;
306217309Snwhitehorn		case DLGK_ENTER:
307251843Sbapt		    result = dlg_ok_buttoncode(button);
308217309Snwhitehorn		    break;
309217309Snwhitehorn		case DLGK_FIELD_PREV:
310217309Snwhitehorn		    state = dlg_prev_ok_buttonindex(state, sHR);
311217309Snwhitehorn		    break;
312217309Snwhitehorn		case DLGK_FIELD_NEXT:
313217309Snwhitehorn		    state = dlg_next_ok_buttonindex(state, sHR);
314217309Snwhitehorn		    break;
315217309Snwhitehorn		case DLGK_FIELD_FIRST:
316217309Snwhitehorn		    if (obj != 0) {
317217309Snwhitehorn			obj->value = 0;
318217309Snwhitehorn			(void) DrawObject(obj);
319217309Snwhitehorn		    }
320217309Snwhitehorn		    break;
321217309Snwhitehorn		case DLGK_FIELD_LAST:
322217309Snwhitehorn		    if (obj != 0) {
323217309Snwhitehorn			switch (state) {
324217309Snwhitehorn			case sHR:
325217309Snwhitehorn			    obj->value = 23;
326217309Snwhitehorn			    break;
327217309Snwhitehorn			case sMN:
328217309Snwhitehorn			case sSC:
329217309Snwhitehorn			    obj->value = 59;
330217309Snwhitehorn			    break;
331217309Snwhitehorn			}
332217309Snwhitehorn			(void) DrawObject(obj);
333217309Snwhitehorn		    }
334217309Snwhitehorn		    break;
335217309Snwhitehorn		case DLGK_DELETE_RIGHT:
336217309Snwhitehorn		    if (obj != 0) {
337217309Snwhitehorn			obj->value /= 10;
338217309Snwhitehorn			(void) DrawObject(obj);
339217309Snwhitehorn		    }
340217309Snwhitehorn		    break;
341217309Snwhitehorn#ifdef KEY_RESIZE
342217309Snwhitehorn		case KEY_RESIZE:
343217309Snwhitehorn		    /* reset data */
344217309Snwhitehorn		    height = old_height;
345217309Snwhitehorn		    width = old_width;
346217309Snwhitehorn		    hour = hr_box.value;
347217309Snwhitehorn		    minute = mn_box.value;
348217309Snwhitehorn		    second = sc_box.value;
349217309Snwhitehorn		    /* repaint */
350217309Snwhitehorn		    dlg_clear();
351217309Snwhitehorn		    dlg_del_window(dialog);
352217309Snwhitehorn		    refresh();
353217309Snwhitehorn		    dlg_mouse_free_regions();
354217309Snwhitehorn		    goto retry;
355217309Snwhitehorn#endif
356217309Snwhitehorn		default:
357251843Sbapt		    if (is_DLGK_MOUSE(key)) {
358251843Sbapt			result = dlg_ok_buttoncode(key - M_EVENT);
359251843Sbapt			if (result < 0)
360251843Sbapt			    result = DLG_EXIT_OK;
361251843Sbapt		    } else if (obj != 0) {
362217309Snwhitehorn			int step = next_or_previous(key);
363217309Snwhitehorn			if (step != 0) {
364217309Snwhitehorn			    obj->value += step;
365217309Snwhitehorn			    while (obj->value < 0)
366217309Snwhitehorn				obj->value += obj->period;
367217309Snwhitehorn			    obj->value %= obj->period;
368217309Snwhitehorn			    (void) DrawObject(obj);
369217309Snwhitehorn			}
370217309Snwhitehorn		    }
371217309Snwhitehorn		    break;
372217309Snwhitehorn		}
373217309Snwhitehorn	    } else if (isdigit(key)) {
374217309Snwhitehorn		if (obj != 0) {
375217309Snwhitehorn		    int digit = (key - '0');
376217309Snwhitehorn		    int value = (obj->value * 10) + digit;
377217309Snwhitehorn		    if (value < obj->period) {
378217309Snwhitehorn			obj->value = value;
379217309Snwhitehorn			(void) DrawObject(obj);
380217309Snwhitehorn		    } else {
381217309Snwhitehorn			beep();
382217309Snwhitehorn		    }
383217309Snwhitehorn		}
384217309Snwhitehorn	    } else {
385217309Snwhitehorn		beep();
386217309Snwhitehorn	    }
387217309Snwhitehorn	}
388217309Snwhitehorn    }
389217309Snwhitehorn
390217309Snwhitehorn#define DefaultFormat(dst, src) \
391217309Snwhitehorn	sprintf(dst, "%02d:%02d:%02d", \
392217309Snwhitehorn		hr_box.value, mn_box.value, sc_box.value)
393217309Snwhitehorn
394217309Snwhitehorn#if defined(HAVE_STRFTIME)
395217309Snwhitehorn    if (dialog_vars.time_format != 0) {
396217309Snwhitehorn	size_t used;
397217309Snwhitehorn	time_t now = time((time_t *) 0);
398217309Snwhitehorn	struct tm *parts = localtime(&now);
399217309Snwhitehorn
400217309Snwhitehorn	parts->tm_sec = sc_box.value;
401217309Snwhitehorn	parts->tm_min = mn_box.value;
402217309Snwhitehorn	parts->tm_hour = hr_box.value;
403217309Snwhitehorn	used = strftime(buffer,
404217309Snwhitehorn			sizeof(buffer) - 1,
405217309Snwhitehorn			dialog_vars.time_format,
406217309Snwhitehorn			parts);
407217309Snwhitehorn	if (used == 0 || *buffer == '\0')
408217309Snwhitehorn	    DefaultFormat(buffer, hr_box);
409217309Snwhitehorn    } else
410217309Snwhitehorn#endif
411217309Snwhitehorn	DefaultFormat(buffer, hr_box);
412217309Snwhitehorn
413217309Snwhitehorn    dlg_add_result(buffer);
414217309Snwhitehorn    dlg_add_separator();
415251843Sbapt    dlg_add_last_key(-1);
416217309Snwhitehorn
417217309Snwhitehorn    return CleanupResult(result, dialog, prompt, &save_vars);
418217309Snwhitehorn}
419