freebsd-dev/contrib/dialog/timebox.c
Nathan Whitehorn 1971864966 Revert r241818 that updated dialog to 20120706. This turns out to horribly
break mixed form dialogs in conjunction with the FreeBSD termcap, making
the bsdinstall partition editor Add dialog, among other things, completely
nonfunctional. This restores dialog 20110707.
2012-12-30 04:22:34 +00:00

421 lines
9.9 KiB
C

/*
* $Id: timebox.c,v 1.45 2011/06/27 08:20:22 tom Exp $
*
* timebox.c -- implements the timebox dialog
*
* Copyright 2001-2010,2011 Thomas E. Dickey
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License, version 2.1
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to
* Free Software Foundation, Inc.
* 51 Franklin St., Fifth Floor
* Boston, MA 02110, USA.
*/
#include <dialog.h>
#include <dlg_keys.h>
#include <time.h>
#define ONE_HIGH 1
#define ONE_WIDE 2
#define BTN_HIGH 2
#define MIN_HIGH (ONE_HIGH + BTN_HIGH + (4 * MARGIN))
#define MIN_WIDE ((3 * (ONE_WIDE + 2 * MARGIN)) + 2 + (2 * MARGIN))
typedef enum {
sHR = -3
,sMN = -2
,sSC = -1
} STATES;
struct _box;
typedef int (*BOX_DRAW) (struct _box *, struct tm *);
typedef struct _box {
WINDOW *parent;
WINDOW *window;
int x;
int y;
int width;
int height;
int period;
int value;
} BOX;
static int
next_or_previous(int key)
{
int result = 0;
switch (key) {
case DLGK_ITEM_PREV:
result = -1;
break;
case DLGK_ITEM_NEXT:
result = 1;
break;
default:
beep();
break;
}
return result;
}
/*
* Draw the hour-of-month selection box
*/
static int
draw_cell(BOX * data)
{
werase(data->window);
dlg_draw_box(data->parent,
data->y - MARGIN, data->x - MARGIN,
data->height + (2 * MARGIN), data->width + (2 * MARGIN),
menubox_border_attr, menubox_attr);
wattrset(data->window, item_attr);
wprintw(data->window, "%02d", data->value);
return 0;
}
static int
init_object(BOX * data,
WINDOW *parent,
int x, int y,
int width, int height,
int period, int value,
int code)
{
(void) code;
data->parent = parent;
data->x = x;
data->y = y;
data->width = width;
data->height = height;
data->period = period;
data->value = value % period;
data->window = derwin(data->parent,
data->height, data->width,
data->y, data->x);
if (data->window == 0)
return -1;
(void) keypad(data->window, TRUE);
dlg_mouse_setbase(getbegx(parent), getbegy(parent));
dlg_mouse_mkregion(y, x, height, width, code);
return 0;
}
static int
CleanupResult(int code, WINDOW *dialog, char *prompt, DIALOG_VARS * save_vars)
{
dlg_del_window(dialog);
dlg_mouse_free_regions();
free(prompt);
dlg_restore_vars(save_vars);
return code;
}
#define DrawObject(data) draw_cell(data)
/*
* Display a dialog box for entering a date
*/
int
dialog_timebox(const char *title,
const char *subtitle,
int height,
int width,
int hour,
int minute,
int second)
{
/* *INDENT-OFF* */
static DLG_KEYS_BINDING binding[] = {
DLG_KEYS_DATA( DLGK_DELETE_RIGHT,KEY_DC ),
HELPKEY_BINDINGS,
ENTERKEY_BINDINGS,
DLG_KEYS_DATA( DLGK_ENTER, ' ' ),
DLG_KEYS_DATA( DLGK_FIELD_FIRST,KEY_HOME ),
DLG_KEYS_DATA( DLGK_FIELD_LAST, KEY_END ),
DLG_KEYS_DATA( DLGK_FIELD_LAST, KEY_LL ),
DLG_KEYS_DATA( DLGK_FIELD_NEXT, CHR_NEXT ),
DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
DLG_KEYS_DATA( DLGK_FIELD_PREV, CHR_BACKSPACE ),
DLG_KEYS_DATA( DLGK_FIELD_PREV, CHR_PREVIOUS ),
DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
DLG_KEYS_DATA( DLGK_ITEM_NEXT, '+'),
DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_DOWN),
DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_NEXT),
DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_NPAGE),
DLG_KEYS_DATA( DLGK_ITEM_PREV, '-' ),
DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_PPAGE ),
DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_PREVIOUS ),
DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_UP ),
END_KEYS_BINDING
};
/* *INDENT-ON* */
#ifdef KEY_RESIZE
int old_height = height;
int old_width = width;
#endif
BOX hr_box, mn_box, sc_box;
int key = 0, key2, fkey;
int button;
int result = DLG_EXIT_UNKNOWN;
WINDOW *dialog;
time_t now_time = time((time_t *) 0);
struct tm current;
int state = dlg_defaultno_button();
const char **buttons = dlg_ok_labels();
char *prompt = dlg_strclone(subtitle);
char buffer[MAX_LEN];
DIALOG_VARS save_vars;
now_time = time((time_t *) 0);
current = *localtime(&now_time);
dlg_save_vars(&save_vars);
dialog_vars.separate_output = TRUE;
dlg_does_output();
#ifdef KEY_RESIZE
retry:
#endif
dlg_auto_size(title, prompt, &height, &width, 0, 0);
height += MIN_HIGH;
if (width < MIN_WIDE)
width = MIN_WIDE;
dlg_button_layout(buttons, &width);
dlg_print_size(height, width);
dlg_ctl_size(height, width);
dialog = dlg_new_window(height, width,
dlg_box_y_ordinate(height),
dlg_box_x_ordinate(width));
if (hour >= 24 || minute >= 60 || second >= 60) {
return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
}
dlg_register_window(dialog, "timebox", binding);
dlg_register_buttons(dialog, "timebox", buttons);
dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
dlg_draw_bottom_box(dialog);
dlg_draw_title(dialog, title);
dlg_draw_helpline(dialog, FALSE);
wattrset(dialog, dialog_attr);
dlg_print_autowrap(dialog, prompt, height, width);
/* compute positions of hour, month and year boxes */
memset(&hr_box, 0, sizeof(hr_box));
memset(&mn_box, 0, sizeof(mn_box));
memset(&sc_box, 0, sizeof(sc_box));
if (init_object(&hr_box,
dialog,
(width - MIN_WIDE + 1) / 2 + MARGIN,
(height - MIN_HIGH + MARGIN),
ONE_WIDE,
ONE_HIGH,
24,
hour >= 0 ? hour : current.tm_hour,
'H') < 0
|| DrawObject(&hr_box) < 0) {
return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
}
mvwprintw(dialog, hr_box.y, hr_box.x + ONE_WIDE + MARGIN, ":");
if (init_object(&mn_box,
dialog,
hr_box.x + (ONE_WIDE + 2 * MARGIN + 1),
hr_box.y,
hr_box.width,
hr_box.height,
60,
minute >= 0 ? minute : current.tm_min,
'M') < 0
|| DrawObject(&mn_box) < 0) {
return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
}
mvwprintw(dialog, mn_box.y, mn_box.x + ONE_WIDE + MARGIN, ":");
if (init_object(&sc_box,
dialog,
mn_box.x + (ONE_WIDE + 2 * MARGIN + 1),
mn_box.y,
mn_box.width,
mn_box.height,
60,
second >= 0 ? second : current.tm_sec,
'S') < 0
|| DrawObject(&sc_box) < 0) {
return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars);
}
while (result == DLG_EXIT_UNKNOWN) {
BOX *obj = (state == sHR ? &hr_box
: (state == sMN ? &mn_box :
(state == sSC ? &sc_box : 0)));
button = (state < 0) ? 0 : state;
dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
if (obj != 0)
dlg_set_focus(dialog, obj->window);
key = dlg_mouse_wgetch(dialog, &fkey);
if (dlg_result_key(key, fkey, &result))
break;
if ((key2 = dlg_char_to_button(key, buttons)) >= 0) {
result = key2;
} else {
/* handle function-keys */
if (fkey) {
switch (key) {
case DLGK_MOUSE(0):
result = DLG_EXIT_OK;
break;
case DLGK_MOUSE(1):
result = DLG_EXIT_CANCEL;
break;
case DLGK_MOUSE('H'):
state = sHR;
break;
case DLGK_MOUSE('M'):
state = sMN;
break;
case DLGK_MOUSE('S'):
state = sSC;
break;
case DLGK_ENTER:
result = button;
break;
case DLGK_FIELD_PREV:
state = dlg_prev_ok_buttonindex(state, sHR);
break;
case DLGK_FIELD_NEXT:
state = dlg_next_ok_buttonindex(state, sHR);
break;
case DLGK_FIELD_FIRST:
if (obj != 0) {
obj->value = 0;
(void) DrawObject(obj);
}
break;
case DLGK_FIELD_LAST:
if (obj != 0) {
switch (state) {
case sHR:
obj->value = 23;
break;
case sMN:
case sSC:
obj->value = 59;
break;
}
(void) DrawObject(obj);
}
break;
case DLGK_DELETE_RIGHT:
if (obj != 0) {
obj->value /= 10;
(void) DrawObject(obj);
}
break;
#ifdef KEY_RESIZE
case KEY_RESIZE:
/* reset data */
height = old_height;
width = old_width;
hour = hr_box.value;
minute = mn_box.value;
second = sc_box.value;
/* repaint */
dlg_clear();
dlg_del_window(dialog);
refresh();
dlg_mouse_free_regions();
goto retry;
#endif
default:
if (obj != 0) {
int step = next_or_previous(key);
if (step != 0) {
obj->value += step;
while (obj->value < 0)
obj->value += obj->period;
obj->value %= obj->period;
(void) DrawObject(obj);
}
}
break;
}
} else if (isdigit(key)) {
if (obj != 0) {
int digit = (key - '0');
int value = (obj->value * 10) + digit;
if (value < obj->period) {
obj->value = value;
(void) DrawObject(obj);
} else {
beep();
}
}
} else {
beep();
}
}
}
#define DefaultFormat(dst, src) \
sprintf(dst, "%02d:%02d:%02d", \
hr_box.value, mn_box.value, sc_box.value)
#if defined(HAVE_STRFTIME)
if (dialog_vars.time_format != 0) {
size_t used;
time_t now = time((time_t *) 0);
struct tm *parts = localtime(&now);
parts->tm_sec = sc_box.value;
parts->tm_min = mn_box.value;
parts->tm_hour = hr_box.value;
used = strftime(buffer,
sizeof(buffer) - 1,
dialog_vars.time_format,
parts);
if (used == 0 || *buffer == '\0')
DefaultFormat(buffer, hr_box);
} else
#endif
DefaultFormat(buffer, hr_box);
dlg_add_result(buffer);
dlg_add_separator();
return CleanupResult(result, dialog, prompt, &save_vars);
}