freebsd-dev/contrib/dialog/buttons.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

666 lines
13 KiB
C

/*
* $Id: buttons.c,v 1.86 2011/06/28 10:46:46 tom Exp $
*
* buttons.c -- draw buttons, e.g., OK/Cancel
*
* Copyright 2000-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>
#ifdef NEED_WCHAR_H
#include <wchar.h>
#endif
#define MIN_BUTTON (dialog_state.visit_items ? -1 : 0)
static void
center_label(char *buffer, int longest, const char *label)
{
int len = dlg_count_columns(label);
int left = 0, right = 0;
*buffer = 0;
if (len < longest) {
left = (longest - len) / 2;
right = (longest - len - left);
if (left > 0)
sprintf(buffer, "%*s", left, " ");
}
strcat(buffer, label);
if (right > 0)
sprintf(buffer + strlen(buffer), "%*s", right, " ");
}
/*
* Parse a multibyte character out of the string, set it past the parsed
* character.
*/
static int
string_to_char(const char **stringp)
{
int result;
#ifdef USE_WIDE_CURSES
const char *string = *stringp;
size_t have = strlen(string);
size_t check;
size_t len;
wchar_t cmp2[2];
mbstate_t state;
memset(&state, 0, sizeof(state));
len = mbrlen(string, have, &state);
if ((int) len > 0 && len <= have) {
memset(&state, 0, sizeof(state));
memset(cmp2, 0, sizeof(cmp2));
check = mbrtowc(cmp2, string, len, &state);
if ((int) check <= 0)
cmp2[0] = 0;
*stringp += len;
} else {
cmp2[0] = UCH(*string);
*stringp += 1;
}
result = cmp2[0];
#else
const char *string = *stringp;
result = UCH(*string);
*stringp += 1;
#endif
return result;
}
/*
* Print a button
*/
static void
print_button(WINDOW *win, char *label, int y, int x, int selected)
{
int i;
int state = 0;
const int *indx = dlg_index_wchars(label);
int limit = dlg_count_wchars(label);
chtype key_attr = (selected
? button_key_active_attr
: button_key_inactive_attr);
chtype label_attr = (selected
? button_label_active_attr
: button_label_inactive_attr);
(void) wmove(win, y, x);
wattrset(win, selected
? button_active_attr
: button_inactive_attr);
(void) waddstr(win, "<");
wattrset(win, label_attr);
for (i = 0; i < limit; ++i) {
int first = indx[i];
int last = indx[i + 1];
switch (state) {
case 0:
#ifdef USE_WIDE_CURSES
if ((last - first) != 1) {
const char *temp = (label + first);
int cmp = string_to_char(&temp);
if (dlg_isupper(cmp)) {
wattrset(win, key_attr);
state = 1;
}
break;
}
#endif
if (dlg_isupper(UCH(label[first]))) {
wattrset(win, key_attr);
state = 1;
}
break;
case 1:
wattrset(win, label_attr);
state = 2;
break;
}
waddnstr(win, label + first, last - first);
}
wattrset(win, selected
? button_active_attr
: button_inactive_attr);
(void) waddstr(win, ">");
(void) wmove(win, y, x + ((int) strspn(label, " ")) + 1);
}
/*
* Count the buttons in the list.
*/
int
dlg_button_count(const char **labels)
{
int result = 0;
while (*labels++ != 0)
++result;
return result;
}
/*
* Compute the size of the button array in columns. Return the total number of
* columns in *length, and the longest button's columns in *longest
*/
void
dlg_button_sizes(const char **labels,
int vertical,
int *longest,
int *length)
{
int n;
*length = 0;
*longest = 0;
for (n = 0; labels[n] != 0; n++) {
if (vertical) {
*length += 1;
*longest = 1;
} else {
int len = dlg_count_columns(labels[n]);
if (len > *longest)
*longest = len;
*length += len;
}
}
/*
* If we can, make all of the buttons the same size. This is only optional
* for buttons laid out horizontally.
*/
if (*longest < 6 - (*longest & 1))
*longest = 6 - (*longest & 1);
if (!vertical)
*length = *longest * n;
}
/*
* Compute the size of the button array.
*/
int
dlg_button_x_step(const char **labels, int limit, int *gap, int *margin, int *step)
{
int count = dlg_button_count(labels);
int longest;
int length;
int unused;
int used;
if (count == 0)
return 0;
dlg_button_sizes(labels, FALSE, &longest, &length);
used = (length + (count * 2));
unused = limit - used;
if ((*gap = unused / (count + 3)) <= 0) {
if ((*gap = unused / (count + 1)) <= 0)
*gap = 1;
*margin = *gap;
} else {
*margin = *gap * 2;
}
*step = *gap + (used + count - 1) / count;
return (*gap > 0) && (unused >= 0);
}
/*
* Make sure there is enough space for the buttons
*/
void
dlg_button_layout(const char **labels, int *limit)
{
int width = 1;
int gap, margin, step;
if (labels != 0 && dlg_button_count(labels)) {
while (!dlg_button_x_step(labels, width, &gap, &margin, &step))
++width;
width += (4 * MARGIN);
if (width > COLS)
width = COLS;
if (width > *limit)
*limit = width;
}
}
/*
* Print a list of buttons at the given position.
*/
void
dlg_draw_buttons(WINDOW *win,
int y, int x,
const char **labels,
int selected,
int vertical,
int limit)
{
chtype save = dlg_get_attrs(win);
int n;
int step = 0;
int length;
int longest;
int final_x;
int final_y;
int gap;
int margin;
size_t need;
char *buffer;
dlg_mouse_setbase(getbegx(win), getbegy(win));
getyx(win, final_y, final_x);
dlg_button_sizes(labels, vertical, &longest, &length);
if (vertical) {
y += 1;
step = 1;
} else {
dlg_button_x_step(labels, limit, &gap, &margin, &step);
x += margin;
}
/*
* Allocate a buffer big enough for any label.
*/
need = (size_t) longest;
for (n = 0; labels[n] != 0; ++n) {
need += strlen(labels[n]) + 1;
}
buffer = dlg_malloc(char, need);
assert_ptr(buffer, "dlg_draw_buttons");
/*
* Draw the labels.
*/
for (n = 0; labels[n] != 0; n++) {
center_label(buffer, longest, labels[n]);
mouse_mkbutton(y, x, dlg_count_columns(buffer), n);
print_button(win, buffer, y, x,
(selected == n) || (n == 0 && selected < 0));
if (selected == n)
getyx(win, final_y, final_x);
if (vertical) {
if ((y += step) > limit)
break;
} else {
if ((x += step) > limit)
break;
}
}
(void) wmove(win, final_y, final_x);
wrefresh(win);
free(buffer);
wattrset(win, save);
}
/*
* Match a given character against the beginning of the string, ignoring case
* of the given character. The matching string must begin with an uppercase
* character.
*/
int
dlg_match_char(int ch, const char *string)
{
if (string != 0) {
int cmp2 = string_to_char(&string);
#ifdef USE_WIDE_CURSES
wint_t cmp1 = dlg_toupper(ch);
if (cmp2 != 0 && (wchar_t) cmp1 == (wchar_t) dlg_toupper(cmp2)) {
return TRUE;
}
#else
if (ch > 0 && ch < 256) {
if (dlg_toupper(ch) == dlg_toupper(cmp2))
return TRUE;
}
#endif
}
return FALSE;
}
/*
* Find the first uppercase character in the label, which we may use for an
* abbreviation.
*/
int
dlg_button_to_char(const char *label)
{
int cmp = -1;
while (*label != 0) {
cmp = string_to_char(&label);
if (dlg_isupper(cmp)) {
break;
}
}
return cmp;
}
/*
* Given a list of button labels, and a character which may be the abbreviation
* for one, find it, if it exists. An abbreviation will be the first character
* which happens to be capitalized in the label.
*/
int
dlg_char_to_button(int ch, const char **labels)
{
if (labels != 0) {
int j;
ch = (int) dlg_toupper(dlg_last_getc());
for (j = 0; labels[j] != 0; ++j) {
int cmp = dlg_button_to_char(labels[j]);
if (ch == cmp) {
dlg_flush_getc();
return j;
}
}
}
return DLG_EXIT_UNKNOWN;
}
static const char *
my_yes_label(void)
{
return (dialog_vars.yes_label != NULL)
? dialog_vars.yes_label
: _("Yes");
}
static const char *
my_no_label(void)
{
return (dialog_vars.no_label != NULL)
? dialog_vars.no_label
: _("No");
}
static const char *
my_ok_label(void)
{
return (dialog_vars.ok_label != NULL)
? dialog_vars.ok_label
: _("OK");
}
static const char *
my_cancel_label(void)
{
return (dialog_vars.cancel_label != NULL)
? dialog_vars.cancel_label
: _("Cancel");
}
static const char *
my_exit_label(void)
{
return (dialog_vars.exit_label != NULL)
? dialog_vars.exit_label
: _("EXIT");
}
static const char *
my_extra_label(void)
{
return (dialog_vars.extra_label != NULL)
? dialog_vars.extra_label
: _("Extra");
}
static const char *
my_help_label(void)
{
return (dialog_vars.help_label != NULL)
? dialog_vars.help_label
: _("Help");
}
/*
* Return a list of button labels.
*/
const char **
dlg_exit_label(void)
{
const char **result;
DIALOG_VARS save;
if (dialog_vars.extra_button) {
dlg_save_vars(&save);
dialog_vars.nocancel = TRUE;
result = dlg_ok_labels();
dlg_restore_vars(&save);
} else {
static const char *labels[3];
int n = 0;
if (!dialog_vars.nook)
labels[n++] = my_exit_label();
if (dialog_vars.help_button)
labels[n++] = my_help_label();
if (n == 0)
labels[n++] = my_exit_label();
labels[n] = 0;
result = labels;
}
return result;
}
/*
* Map the given button index for dlg_exit_label() into our exit-code.
*/
int
dlg_exit_buttoncode(int button)
{
int result;
DIALOG_VARS save;
dlg_save_vars(&save);
dialog_vars.nocancel = TRUE;
result = dlg_ok_buttoncode(button);
dlg_restore_vars(&save);
return result;
}
const char **
dlg_ok_label(void)
{
static const char *labels[3];
int n = 0;
labels[n++] = my_ok_label();
if (dialog_vars.help_button)
labels[n++] = my_help_label();
labels[n] = 0;
return labels;
}
/*
* Return a list of button labels for the OK/Cancel group.
*/
const char **
dlg_ok_labels(void)
{
static const char *labels[5];
int n = 0;
if (!dialog_vars.nook)
labels[n++] = my_ok_label();
if (dialog_vars.extra_button)
labels[n++] = my_extra_label();
if (!dialog_vars.nocancel)
labels[n++] = my_cancel_label();
if (dialog_vars.help_button)
labels[n++] = my_help_label();
labels[n] = 0;
return labels;
}
/*
* Map the given button index for dlg_ok_labels() into our exit-code
*/
int
dlg_ok_buttoncode(int button)
{
int result = DLG_EXIT_ERROR;
int n = !dialog_vars.nook;
if (!dialog_vars.nook && (button <= 0)) {
result = DLG_EXIT_OK;
} else if (dialog_vars.extra_button && (button == n++)) {
result = DLG_EXIT_EXTRA;
} else if (!dialog_vars.nocancel && (button == n++)) {
result = DLG_EXIT_CANCEL;
} else if (dialog_vars.help_button && (button == n)) {
result = DLG_EXIT_HELP;
}
return result;
}
/*
* Given that we're using dlg_ok_labels() to list buttons, find the next index
* in the list of buttons. The 'extra' parameter if negative provides a way to
* enumerate extra active areas on the widget.
*/
int
dlg_next_ok_buttonindex(int current, int extra)
{
int result = current + 1;
if (current >= 0
&& dlg_ok_buttoncode(result) < 0)
result = extra;
return result;
}
/*
* Similarly, find the previous button index.
*/
int
dlg_prev_ok_buttonindex(int current, int extra)
{
int result = current - 1;
if (result < extra) {
for (result = 0; dlg_ok_buttoncode(result + 1) >= 0; ++result) {
;
}
}
return result;
}
/*
* Find the button-index for the "OK" or "Cancel" button, according to
* whether --defaultno is given. If --nocancel was given, we always return
* the index for "OK".
*/
int
dlg_defaultno_button(void)
{
int result = 0;
if (dialog_vars.defaultno && !dialog_vars.nocancel) {
while (dlg_ok_buttoncode(result) != DLG_EXIT_CANCEL)
++result;
}
return result;
}
/*
* Return a list of buttons for Yes/No labels.
*/
const char **
dlg_yes_labels(void)
{
const char **result;
if (dialog_vars.extra_button) {
result = dlg_ok_labels();
} else {
static const char *labels[4];
int n = 0;
labels[n++] = my_yes_label();
labels[n++] = my_no_label();
if (dialog_vars.help_button)
labels[n++] = my_help_label();
labels[n] = 0;
result = labels;
}
return result;
}
/*
* Map the given button index for dlg_yes_labels() into our exit-code.
*/
int
dlg_yes_buttoncode(int button)
{
int result = DLG_EXIT_ERROR;
if (dialog_vars.extra_button) {
result = dlg_ok_buttoncode(button);
} else if (button == 0) {
result = DLG_EXIT_OK;
} else if (button == 1) {
result = DLG_EXIT_CANCEL;
} else if (button == 2 && dialog_vars.help_button) {
result = DLG_EXIT_HELP;
}
return result;
}
/*
* Return the next index in labels[];
*/
int
dlg_next_button(const char **labels, int button)
{
if (labels[button + 1] != 0)
++button;
else
button = MIN_BUTTON;
return button;
}
/*
* Return the previous index in labels[];
*/
int
dlg_prev_button(const char **labels, int button)
{
if (button > MIN_BUTTON)
--button;
else {
while (labels[button + 1] != 0)
++button;
}
return button;
}