timebox.c revision 224014
11541Srgrimes/*
21541Srgrimes * $Id: timebox.c,v 1.45 2011/06/27 08:20:22 tom Exp $
31541Srgrimes *
41541Srgrimes *  timebox.c -- implements the timebox dialog
51541Srgrimes *
61541Srgrimes *  Copyright 2001-2010,2011   Thomas E. Dickey
71541Srgrimes *
81541Srgrimes *  This program is free software; you can redistribute it and/or modify
91541Srgrimes *  it under the terms of the GNU Lesser General Public License, version 2.1
101541Srgrimes *  as published by the Free Software Foundation.
111541Srgrimes *
121541Srgrimes *  This program is distributed in the hope that it will be useful, but
131541Srgrimes *  WITHOUT ANY WARRANTY; without even the implied warranty of
141541Srgrimes *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
151541Srgrimes *  Lesser General Public License for more details.
161541Srgrimes *
171541Srgrimes *  You should have received a copy of the GNU Lesser General Public
181541Srgrimes *  License along with this program; if not, write to
191541Srgrimes *	Free Software Foundation, Inc.
201541Srgrimes *	51 Franklin St., Fifth Floor
211541Srgrimes *	Boston, MA 02110, USA.
221541Srgrimes */
231541Srgrimes
241541Srgrimes#include <dialog.h>
251541Srgrimes#include <dlg_keys.h>
261541Srgrimes
271541Srgrimes#include <time.h>
281541Srgrimes
291541Srgrimes#define ONE_HIGH 1
301541Srgrimes#define ONE_WIDE 2
311541Srgrimes#define BTN_HIGH 2
321541Srgrimes
331541Srgrimes#define MIN_HIGH (ONE_HIGH + BTN_HIGH + (4 * MARGIN))
341541Srgrimes#define MIN_WIDE ((3 * (ONE_WIDE + 2 * MARGIN)) + 2 + (2 * MARGIN))
351541Srgrimes
361541Srgrimestypedef enum {
371541Srgrimes    sHR = -3
3814486Shsu    ,sMN = -2
3934925Sdufault    ,sSC = -1
401541Srgrimes} STATES;
411541Srgrimes
421541Srgrimesstruct _box;
431541Srgrimes
441541Srgrimestypedef int (*BOX_DRAW) (struct _box *, struct tm *);
459343Sbde
4634925Sdufaulttypedef struct _box {
479343Sbde    WINDOW *parent;
489343Sbde    WINDOW *window;
499343Sbde    int x;
501541Srgrimes    int y;
511541Srgrimes    int width;
521541Srgrimes    int height;
531541Srgrimes    int period;
541541Srgrimes    int value;
551541Srgrimes} BOX;
561541Srgrimes
571541Srgrimesstatic int
581541Srgrimesnext_or_previous(int key)
591541Srgrimes{
601541Srgrimes    int result = 0;
611541Srgrimes
621541Srgrimes    switch (key) {
631541Srgrimes    case DLGK_ITEM_PREV:
641541Srgrimes	result = -1;
651541Srgrimes	break;
661541Srgrimes    case DLGK_ITEM_NEXT:
671541Srgrimes	result = 1;
681541Srgrimes	break;
691541Srgrimes    default:
701541Srgrimes	beep();
711541Srgrimes	break;
7213561Smpp    }
731541Srgrimes    return result;
741541Srgrimes}
751541Srgrimes/*
761541Srgrimes * Draw the hour-of-month selection box
771541Srgrimes */
781541Srgrimesstatic int
791541Srgrimesdraw_cell(BOX * data)
801541Srgrimes{
811541Srgrimes    werase(data->window);
821541Srgrimes    dlg_draw_box(data->parent,
831541Srgrimes		 data->y - MARGIN, data->x - MARGIN,
841541Srgrimes		 data->height + (2 * MARGIN), data->width + (2 * MARGIN),
851541Srgrimes		 menubox_border_attr, menubox_attr);
861541Srgrimes
871541Srgrimes    wattrset(data->window, item_attr);
881541Srgrimes    wprintw(data->window, "%02d", data->value);
891541Srgrimes    return 0;
901541Srgrimes}
911541Srgrimes
921541Srgrimesstatic int
931541Srgrimesinit_object(BOX * data,
941541Srgrimes	    WINDOW *parent,
951541Srgrimes	    int x, int y,
961541Srgrimes	    int width, int height,
971541Srgrimes	    int period, int value,
989343Sbde	    int code)
999343Sbde{
1009343Sbde    (void) code;
1019343Sbde
1021541Srgrimes    data->parent = parent;
1039343Sbde    data->x = x;
1049343Sbde    data->y = y;
1059343Sbde    data->width = width;
1069343Sbde    data->height = height;
1079343Sbde    data->period = period;
1089343Sbde    data->value = value % period;
1099343Sbde
1109343Sbde    data->window = derwin(data->parent,
1119343Sbde			  data->height, data->width,
1129343Sbde			  data->y, data->x);
1131541Srgrimes    if (data->window == 0)
1149343Sbde	return -1;
1151541Srgrimes    (void) keypad(data->window, TRUE);
1169343Sbde
1179343Sbde    dlg_mouse_setbase(getbegx(parent), getbegy(parent));
1189343Sbde    dlg_mouse_mkregion(y, x, height, width, code);
1199343Sbde
1201541Srgrimes    return 0;
1211541Srgrimes}
1221541Srgrimes
1231541Srgrimesstatic int
1241541SrgrimesCleanupResult(int code, WINDOW *dialog, char *prompt, DIALOG_VARS * save_vars)
1251541Srgrimes{
1261541Srgrimes    dlg_del_window(dialog);
1279343Sbde    dlg_mouse_free_regions();
1281541Srgrimes    free(prompt);
1291541Srgrimes    dlg_restore_vars(save_vars);
1301541Srgrimes
1311541Srgrimes    return code;
1321541Srgrimes}
1339343Sbde
13414331Speter#define DrawObject(data) draw_cell(data)
13514331Speter
13629340Sjoerg/*
1371541Srgrimes * Display a dialog box for entering a date
1381541Srgrimes */
1391541Srgrimesint
14014331Speterdialog_timebox(const char *title,
1411541Srgrimes	       const char *subtitle,
1421541Srgrimes	       int height,
1431541Srgrimes	       int width,
1441541Srgrimes	       int hour,
1451541Srgrimes	       int minute,
1461541Srgrimes	       int second)
1471541Srgrimes{
1481541Srgrimes    /* *INDENT-OFF* */
1491541Srgrimes    static DLG_KEYS_BINDING binding[] = {
1501541Srgrimes	DLG_KEYS_DATA( DLGK_DELETE_RIGHT,KEY_DC ),
1519343Sbde	HELPKEY_BINDINGS,
1521541Srgrimes	ENTERKEY_BINDINGS,
1531541Srgrimes	DLG_KEYS_DATA( DLGK_ENTER,	' ' ),
1541541Srgrimes	DLG_KEYS_DATA( DLGK_FIELD_FIRST,KEY_HOME ),
1551541Srgrimes	DLG_KEYS_DATA( DLGK_FIELD_LAST, KEY_END ),
1561541Srgrimes	DLG_KEYS_DATA( DLGK_FIELD_LAST, KEY_LL ),
1575999Sats	DLG_KEYS_DATA( DLGK_FIELD_NEXT, CHR_NEXT ),
1581541Srgrimes	DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
15914331Speter	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
1601541Srgrimes	DLG_KEYS_DATA( DLGK_FIELD_PREV, CHR_BACKSPACE ),
16114331Speter	DLG_KEYS_DATA( DLGK_FIELD_PREV, CHR_PREVIOUS ),
16214331Speter	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
1631541Srgrimes	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
1641541Srgrimes	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  '+'),
1651541Srgrimes	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_DOWN),
1661541Srgrimes	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_NEXT),
1671541Srgrimes	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_NPAGE),
1681541Srgrimes	DLG_KEYS_DATA( DLGK_ITEM_PREV,  '-' ),
1691541Srgrimes	DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_PPAGE ),
1701541Srgrimes	DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_PREVIOUS ),
1719343Sbde	DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_UP ),
1721541Srgrimes	END_KEYS_BINDING
1731541Srgrimes    };
1741541Srgrimes    /* *INDENT-ON* */
1751541Srgrimes
1761541Srgrimes#ifdef KEY_RESIZE
1771541Srgrimes    int old_height = height;
17814331Speter    int old_width = width;
17914927Speter#endif
18014927Speter    BOX hr_box, mn_box, sc_box;
1811541Srgrimes    int key = 0, key2, fkey;
1821541Srgrimes    int button;
1831541Srgrimes    int result = DLG_EXIT_UNKNOWN;
1841541Srgrimes    WINDOW *dialog;
1851541Srgrimes    time_t now_time = time((time_t *) 0);
1861541Srgrimes    struct tm current;
1871541Srgrimes    int state = dlg_defaultno_button();
1881541Srgrimes    const char **buttons = dlg_ok_labels();
1891541Srgrimes    char *prompt = dlg_strclone(subtitle);
1901541Srgrimes    char buffer[MAX_LEN];
1911541Srgrimes    DIALOG_VARS save_vars;
1921541Srgrimes
1931541Srgrimes    now_time = time((time_t *) 0);
1941541Srgrimes    current = *localtime(&now_time);
1951541Srgrimes
1961541Srgrimes    dlg_save_vars(&save_vars);
1971541Srgrimes    dialog_vars.separate_output = TRUE;
1981541Srgrimes
1991541Srgrimes    dlg_does_output();
2001541Srgrimes
2011541Srgrimes#ifdef KEY_RESIZE
20234925Sdufault  retry:
20334925Sdufault#endif
20434925Sdufault
20534925Sdufault    dlg_auto_size(title, prompt, &height, &width, 0, 0);
20634925Sdufault    height += MIN_HIGH;
20734925Sdufault    if (width < MIN_WIDE)
20834925Sdufault	width = MIN_WIDE;
20934925Sdufault    dlg_button_layout(buttons, &width);
21034925Sdufault    dlg_print_size(height, width);
21134925Sdufault    dlg_ctl_size(height, width);
21234925Sdufault
21334925Sdufault    dialog = dlg_new_window(height, width,
21434925Sdufault			    dlg_box_y_ordinate(height),
21534925Sdufault			    dlg_box_x_ordinate(width));
21634925Sdufault
21734925Sdufault    if (hour >= 24 || minute >= 60 || second >= 60) {
21834925Sdufault	return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
21934925Sdufault    }
22034925Sdufault
22134925Sdufault    dlg_register_window(dialog, "timebox", binding);
22234925Sdufault    dlg_register_buttons(dialog, "timebox", buttons);
22334925Sdufault
22434925Sdufault    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
22534925Sdufault    dlg_draw_bottom_box(dialog);
22634925Sdufault    dlg_draw_title(dialog, title);
22734925Sdufault    dlg_draw_helpline(dialog, FALSE);
2281541Srgrimes
2291541Srgrimes    wattrset(dialog, dialog_attr);
2301541Srgrimes    dlg_print_autowrap(dialog, prompt, height, width);
2311541Srgrimes
2321541Srgrimes    /* compute positions of hour, month and year boxes */
2339343Sbde    memset(&hr_box, 0, sizeof(hr_box));
2341541Srgrimes    memset(&mn_box, 0, sizeof(mn_box));
2359343Sbde    memset(&sc_box, 0, sizeof(sc_box));
2361541Srgrimes
237    if (init_object(&hr_box,
238		    dialog,
239		    (width - MIN_WIDE + 1) / 2 + MARGIN,
240		    (height - MIN_HIGH + MARGIN),
241		    ONE_WIDE,
242		    ONE_HIGH,
243		    24,
244		    hour >= 0 ? hour : current.tm_hour,
245		    'H') < 0
246	|| DrawObject(&hr_box) < 0) {
247	return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
248    }
249
250    mvwprintw(dialog, hr_box.y, hr_box.x + ONE_WIDE + MARGIN, ":");
251    if (init_object(&mn_box,
252		    dialog,
253		    hr_box.x + (ONE_WIDE + 2 * MARGIN + 1),
254		    hr_box.y,
255		    hr_box.width,
256		    hr_box.height,
257		    60,
258		    minute >= 0 ? minute : current.tm_min,
259		    'M') < 0
260	|| DrawObject(&mn_box) < 0) {
261	return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
262    }
263
264    mvwprintw(dialog, mn_box.y, mn_box.x + ONE_WIDE + MARGIN, ":");
265    if (init_object(&sc_box,
266		    dialog,
267		    mn_box.x + (ONE_WIDE + 2 * MARGIN + 1),
268		    mn_box.y,
269		    mn_box.width,
270		    mn_box.height,
271		    60,
272		    second >= 0 ? second : current.tm_sec,
273		    'S') < 0
274	|| DrawObject(&sc_box) < 0) {
275	return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
276    }
277
278    while (result == DLG_EXIT_UNKNOWN) {
279	BOX *obj = (state == sHR ? &hr_box
280		    : (state == sMN ? &mn_box :
281		       (state == sSC ? &sc_box : 0)));
282
283	button = (state < 0) ? 0 : state;
284	dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
285	if (obj != 0)
286	    dlg_set_focus(dialog, obj->window);
287
288	key = dlg_mouse_wgetch(dialog, &fkey);
289	if (dlg_result_key(key, fkey, &result))
290	    break;
291
292	if ((key2 = dlg_char_to_button(key, buttons)) >= 0) {
293	    result = key2;
294	} else {
295	    /* handle function-keys */
296	    if (fkey) {
297		switch (key) {
298		case DLGK_MOUSE(0):
299		    result = DLG_EXIT_OK;
300		    break;
301		case DLGK_MOUSE(1):
302		    result = DLG_EXIT_CANCEL;
303		    break;
304		case DLGK_MOUSE('H'):
305		    state = sHR;
306		    break;
307		case DLGK_MOUSE('M'):
308		    state = sMN;
309		    break;
310		case DLGK_MOUSE('S'):
311		    state = sSC;
312		    break;
313		case DLGK_ENTER:
314		    result = button;
315		    break;
316		case DLGK_FIELD_PREV:
317		    state = dlg_prev_ok_buttonindex(state, sHR);
318		    break;
319		case DLGK_FIELD_NEXT:
320		    state = dlg_next_ok_buttonindex(state, sHR);
321		    break;
322		case DLGK_FIELD_FIRST:
323		    if (obj != 0) {
324			obj->value = 0;
325			(void) DrawObject(obj);
326		    }
327		    break;
328		case DLGK_FIELD_LAST:
329		    if (obj != 0) {
330			switch (state) {
331			case sHR:
332			    obj->value = 23;
333			    break;
334			case sMN:
335			case sSC:
336			    obj->value = 59;
337			    break;
338			}
339			(void) DrawObject(obj);
340		    }
341		    break;
342		case DLGK_DELETE_RIGHT:
343		    if (obj != 0) {
344			obj->value /= 10;
345			(void) DrawObject(obj);
346		    }
347		    break;
348#ifdef KEY_RESIZE
349		case KEY_RESIZE:
350		    /* reset data */
351		    height = old_height;
352		    width = old_width;
353		    hour = hr_box.value;
354		    minute = mn_box.value;
355		    second = sc_box.value;
356		    /* repaint */
357		    dlg_clear();
358		    dlg_del_window(dialog);
359		    refresh();
360		    dlg_mouse_free_regions();
361		    goto retry;
362#endif
363		default:
364		    if (obj != 0) {
365			int step = next_or_previous(key);
366			if (step != 0) {
367			    obj->value += step;
368			    while (obj->value < 0)
369				obj->value += obj->period;
370			    obj->value %= obj->period;
371			    (void) DrawObject(obj);
372			}
373		    }
374		    break;
375		}
376	    } else if (isdigit(key)) {
377		if (obj != 0) {
378		    int digit = (key - '0');
379		    int value = (obj->value * 10) + digit;
380		    if (value < obj->period) {
381			obj->value = value;
382			(void) DrawObject(obj);
383		    } else {
384			beep();
385		    }
386		}
387	    } else {
388		beep();
389	    }
390	}
391    }
392
393#define DefaultFormat(dst, src) \
394	sprintf(dst, "%02d:%02d:%02d", \
395		hr_box.value, mn_box.value, sc_box.value)
396
397#if defined(HAVE_STRFTIME)
398    if (dialog_vars.time_format != 0) {
399	size_t used;
400	time_t now = time((time_t *) 0);
401	struct tm *parts = localtime(&now);
402
403	parts->tm_sec = sc_box.value;
404	parts->tm_min = mn_box.value;
405	parts->tm_hour = hr_box.value;
406	used = strftime(buffer,
407			sizeof(buffer) - 1,
408			dialog_vars.time_format,
409			parts);
410	if (used == 0 || *buffer == '\0')
411	    DefaultFormat(buffer, hr_box);
412    } else
413#endif
414	DefaultFormat(buffer, hr_box);
415
416    dlg_add_result(buffer);
417    dlg_add_separator();
418
419    return CleanupResult(result, dialog, prompt, &save_vars);
420}
421