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