2 * $Id: buttons.c,v 1.86 2011/06/28 10:46:46 tom Exp $
4 * buttons.c -- draw buttons, e.g., OK/Cancel
6 * Copyright 2000-2010,2011 Thomas E. Dickey
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License, version 2.1
10 * as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to
19 * Free Software Foundation, Inc.
20 * 51 Franklin St., Fifth Floor
21 * Boston, MA 02110, USA.
31 #define MIN_BUTTON (dialog_state.visit_items ? -1 : 0)
34 center_label(char *buffer, int longest, const char *label)
36 int len = dlg_count_columns(label);
37 int left = 0, right = 0;
41 left = (longest - len) / 2;
42 right = (longest - len - left);
44 sprintf(buffer, "%*s", left, " ");
46 strcat(buffer, label);
48 sprintf(buffer + strlen(buffer), "%*s", right, " ");
52 * Parse a multibyte character out of the string, set it past the parsed
56 string_to_char(const char **stringp)
59 #ifdef USE_WIDE_CURSES
60 const char *string = *stringp;
61 size_t have = strlen(string);
67 memset(&state, 0, sizeof(state));
68 len = mbrlen(string, have, &state);
69 if ((int) len > 0 && len <= have) {
70 memset(&state, 0, sizeof(state));
71 memset(cmp2, 0, sizeof(cmp2));
72 check = mbrtowc(cmp2, string, len, &state);
77 cmp2[0] = UCH(*string);
82 const char *string = *stringp;
83 result = UCH(*string);
93 print_button(WINDOW *win, char *label, int y, int x, int selected)
97 const int *indx = dlg_index_wchars(label);
98 int limit = dlg_count_wchars(label);
99 chtype key_attr = (selected
100 ? button_key_active_attr
101 : button_key_inactive_attr);
102 chtype label_attr = (selected
103 ? button_label_active_attr
104 : button_label_inactive_attr);
106 (void) wmove(win, y, x);
107 wattrset(win, selected
109 : button_inactive_attr);
110 (void) waddstr(win, "<");
111 wattrset(win, label_attr);
112 for (i = 0; i < limit; ++i) {
114 int last = indx[i + 1];
118 #ifdef USE_WIDE_CURSES
119 if ((last - first) != 1) {
120 const char *temp = (label + first);
121 int cmp = string_to_char(&temp);
122 if (dlg_isupper(cmp)) {
123 wattrset(win, key_attr);
129 if (dlg_isupper(UCH(label[first]))) {
130 wattrset(win, key_attr);
135 wattrset(win, label_attr);
139 waddnstr(win, label + first, last - first);
141 wattrset(win, selected
143 : button_inactive_attr);
144 (void) waddstr(win, ">");
145 (void) wmove(win, y, x + ((int) strspn(label, " ")) + 1);
149 * Count the buttons in the list.
152 dlg_button_count(const char **labels)
155 while (*labels++ != 0)
161 * Compute the size of the button array in columns. Return the total number of
162 * columns in *length, and the longest button's columns in *longest
165 dlg_button_sizes(const char **labels,
174 for (n = 0; labels[n] != 0; n++) {
179 int len = dlg_count_columns(labels[n]);
186 * If we can, make all of the buttons the same size. This is only optional
187 * for buttons laid out horizontally.
189 if (*longest < 6 - (*longest & 1))
190 *longest = 6 - (*longest & 1);
192 *length = *longest * n;
196 * Compute the size of the button array.
199 dlg_button_x_step(const char **labels, int limit, int *gap, int *margin, int *step)
201 int count = dlg_button_count(labels);
209 dlg_button_sizes(labels, FALSE, &longest, &length);
210 used = (length + (count * 2));
211 unused = limit - used;
213 if ((*gap = unused / (count + 3)) <= 0) {
214 if ((*gap = unused / (count + 1)) <= 0)
220 *step = *gap + (used + count - 1) / count;
221 return (*gap > 0) && (unused >= 0);
225 * Make sure there is enough space for the buttons
228 dlg_button_layout(const char **labels, int *limit)
231 int gap, margin, step;
233 if (labels != 0 && dlg_button_count(labels)) {
234 while (!dlg_button_x_step(labels, width, &gap, &margin, &step))
236 width += (4 * MARGIN);
245 * Print a list of buttons at the given position.
248 dlg_draw_buttons(WINDOW *win,
255 chtype save = dlg_get_attrs(win);
267 dlg_mouse_setbase(getbegx(win), getbegy(win));
269 getyx(win, final_y, final_x);
271 dlg_button_sizes(labels, vertical, &longest, &length);
277 dlg_button_x_step(labels, limit, &gap, &margin, &step);
282 * Allocate a buffer big enough for any label.
284 need = (size_t) longest;
285 for (n = 0; labels[n] != 0; ++n) {
286 need += strlen(labels[n]) + 1;
288 buffer = dlg_malloc(char, need);
289 assert_ptr(buffer, "dlg_draw_buttons");
294 for (n = 0; labels[n] != 0; n++) {
295 center_label(buffer, longest, labels[n]);
296 mouse_mkbutton(y, x, dlg_count_columns(buffer), n);
297 print_button(win, buffer, y, x,
298 (selected == n) || (n == 0 && selected < 0));
300 getyx(win, final_y, final_x);
303 if ((y += step) > limit)
306 if ((x += step) > limit)
310 (void) wmove(win, final_y, final_x);
317 * Match a given character against the beginning of the string, ignoring case
318 * of the given character. The matching string must begin with an uppercase
322 dlg_match_char(int ch, const char *string)
325 int cmp2 = string_to_char(&string);
326 #ifdef USE_WIDE_CURSES
327 wint_t cmp1 = dlg_toupper(ch);
328 if (cmp2 != 0 && (wchar_t) cmp1 == (wchar_t) dlg_toupper(cmp2)) {
332 if (ch > 0 && ch < 256) {
333 if (dlg_toupper(ch) == dlg_toupper(cmp2))
342 * Find the first uppercase character in the label, which we may use for an
346 dlg_button_to_char(const char *label)
350 while (*label != 0) {
351 cmp = string_to_char(&label);
352 if (dlg_isupper(cmp)) {
360 * Given a list of button labels, and a character which may be the abbreviation
361 * for one, find it, if it exists. An abbreviation will be the first character
362 * which happens to be capitalized in the label.
365 dlg_char_to_button(int ch, const char **labels)
370 ch = (int) dlg_toupper(dlg_last_getc());
371 for (j = 0; labels[j] != 0; ++j) {
372 int cmp = dlg_button_to_char(labels[j]);
379 return DLG_EXIT_UNKNOWN;
385 return (dialog_vars.yes_label != NULL)
386 ? dialog_vars.yes_label
393 return (dialog_vars.no_label != NULL)
394 ? dialog_vars.no_label
401 return (dialog_vars.ok_label != NULL)
402 ? dialog_vars.ok_label
407 my_cancel_label(void)
409 return (dialog_vars.cancel_label != NULL)
410 ? dialog_vars.cancel_label
417 return (dialog_vars.exit_label != NULL)
418 ? dialog_vars.exit_label
425 return (dialog_vars.extra_label != NULL)
426 ? dialog_vars.extra_label
433 return (dialog_vars.help_label != NULL)
434 ? dialog_vars.help_label
439 * Return a list of button labels.
447 if (dialog_vars.extra_button) {
448 dlg_save_vars(&save);
449 dialog_vars.nocancel = TRUE;
450 result = dlg_ok_labels();
451 dlg_restore_vars(&save);
453 static const char *labels[3];
456 if (!dialog_vars.nook)
457 labels[n++] = my_exit_label();
458 if (dialog_vars.help_button)
459 labels[n++] = my_help_label();
461 labels[n++] = my_exit_label();
470 * Map the given button index for dlg_exit_label() into our exit-code.
473 dlg_exit_buttoncode(int button)
478 dlg_save_vars(&save);
479 dialog_vars.nocancel = TRUE;
481 result = dlg_ok_buttoncode(button);
483 dlg_restore_vars(&save);
491 static const char *labels[3];
494 labels[n++] = my_ok_label();
495 if (dialog_vars.help_button)
496 labels[n++] = my_help_label();
502 * Return a list of button labels for the OK/Cancel group.
507 static const char *labels[5];
510 if (!dialog_vars.nook)
511 labels[n++] = my_ok_label();
512 if (dialog_vars.extra_button)
513 labels[n++] = my_extra_label();
514 if (!dialog_vars.nocancel)
515 labels[n++] = my_cancel_label();
516 if (dialog_vars.help_button)
517 labels[n++] = my_help_label();
523 * Map the given button index for dlg_ok_labels() into our exit-code
526 dlg_ok_buttoncode(int button)
528 int result = DLG_EXIT_ERROR;
529 int n = !dialog_vars.nook;
531 if (!dialog_vars.nook && (button <= 0)) {
532 result = DLG_EXIT_OK;
533 } else if (dialog_vars.extra_button && (button == n++)) {
534 result = DLG_EXIT_EXTRA;
535 } else if (!dialog_vars.nocancel && (button == n++)) {
536 result = DLG_EXIT_CANCEL;
537 } else if (dialog_vars.help_button && (button == n)) {
538 result = DLG_EXIT_HELP;
544 * Given that we're using dlg_ok_labels() to list buttons, find the next index
545 * in the list of buttons. The 'extra' parameter if negative provides a way to
546 * enumerate extra active areas on the widget.
549 dlg_next_ok_buttonindex(int current, int extra)
551 int result = current + 1;
554 && dlg_ok_buttoncode(result) < 0)
560 * Similarly, find the previous button index.
563 dlg_prev_ok_buttonindex(int current, int extra)
565 int result = current - 1;
567 if (result < extra) {
568 for (result = 0; dlg_ok_buttoncode(result + 1) >= 0; ++result) {
576 * Find the button-index for the "OK" or "Cancel" button, according to
577 * whether --defaultno is given. If --nocancel was given, we always return
578 * the index for "OK".
581 dlg_defaultno_button(void)
585 if (dialog_vars.defaultno && !dialog_vars.nocancel) {
586 while (dlg_ok_buttoncode(result) != DLG_EXIT_CANCEL)
593 * Return a list of buttons for Yes/No labels.
600 if (dialog_vars.extra_button) {
601 result = dlg_ok_labels();
603 static const char *labels[4];
606 labels[n++] = my_yes_label();
607 labels[n++] = my_no_label();
608 if (dialog_vars.help_button)
609 labels[n++] = my_help_label();
619 * Map the given button index for dlg_yes_labels() into our exit-code.
622 dlg_yes_buttoncode(int button)
624 int result = DLG_EXIT_ERROR;
626 if (dialog_vars.extra_button) {
627 result = dlg_ok_buttoncode(button);
628 } else if (button == 0) {
629 result = DLG_EXIT_OK;
630 } else if (button == 1) {
631 result = DLG_EXIT_CANCEL;
632 } else if (button == 2 && dialog_vars.help_button) {
633 result = DLG_EXIT_HELP;
640 * Return the next index in labels[];
643 dlg_next_button(const char **labels, int button)
645 if (labels[button + 1] != 0)
653 * Return the previous index in labels[];
656 dlg_prev_button(const char **labels, int button)
658 if (button > MIN_BUTTON)
661 while (labels[button + 1] != 0)