kernel - Fix races created by a comedy of circumstansces (3)
[dragonfly.git] / contrib / dialog / buttons.c
blob6e565526e50d4d60d1a4b567b86fd87787c46b0f
1 /*
2 * $Id: buttons.c,v 1.96 2015/01/25 23:52:54 tom Exp $
4 * buttons.c -- draw buttons, e.g., OK/Cancel
6 * Copyright 2000-2014,2015 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.
24 #include <dialog.h>
25 #include <dlg_keys.h>
27 #ifdef NEED_WCHAR_H
28 #include <wchar.h>
29 #endif
31 #define MIN_BUTTON (-dialog_state.visit_cols)
32 #define CHR_BUTTON (!dialog_state.plain_buttons)
34 static void
35 center_label(char *buffer, int longest, const char *label)
37 int len = dlg_count_columns(label);
38 int left = 0, right = 0;
40 *buffer = 0;
41 if (len < longest) {
42 left = (longest - len) / 2;
43 right = (longest - len - left);
44 if (left > 0)
45 sprintf(buffer, "%*s", left, " ");
47 strcat(buffer, label);
48 if (right > 0)
49 sprintf(buffer + strlen(buffer), "%*s", right, " ");
53 * Parse a multibyte character out of the string, set it past the parsed
54 * character.
56 static int
57 string_to_char(const char **stringp)
59 int result;
60 #ifdef USE_WIDE_CURSES
61 const char *string = *stringp;
62 size_t have = strlen(string);
63 size_t check;
64 size_t len;
65 wchar_t cmp2[2];
66 mbstate_t state;
68 memset(&state, 0, sizeof(state));
69 len = mbrlen(string, have, &state);
70 if ((int) len > 0 && len <= have) {
71 memset(&state, 0, sizeof(state));
72 memset(cmp2, 0, sizeof(cmp2));
73 check = mbrtowc(cmp2, string, len, &state);
74 if ((int) check <= 0)
75 cmp2[0] = 0;
76 *stringp += len;
77 } else {
78 cmp2[0] = UCH(*string);
79 *stringp += 1;
81 result = cmp2[0];
82 #else
83 const char *string = *stringp;
84 result = UCH(*string);
85 *stringp += 1;
86 #endif
87 return result;
90 static size_t
91 count_labels(const char **labels)
93 size_t result = 0;
94 if (labels != 0) {
95 while (*labels++ != 0) {
96 ++result;
99 return result;
103 * Check if the latest key should be added to the hotkey list.
105 static int
106 was_hotkey(int this_key, int *used_keys, size_t next)
108 int result = FALSE;
110 if (next != 0) {
111 size_t n;
112 for (n = 0; n < next; ++n) {
113 if (used_keys[n] == this_key) {
114 result = TRUE;
115 break;
119 return result;
123 * Determine the hot-keys for a set of button-labels. Normally these are
124 * the first uppercase character in each label. However, if more than one
125 * button has the same first-uppercase, then we will (attempt to) look for
126 * an alternate.
128 * This allocates data which must be freed by the caller.
130 static int *
131 get_hotkeys(const char **labels)
133 int *result = 0;
134 size_t count = count_labels(labels);
135 size_t n;
137 if ((result = dlg_calloc(int, count + 1)) != 0) {
138 for (n = 0; n < count; ++n) {
139 const char *label = labels[n];
140 const int *indx = dlg_index_wchars(label);
141 int limit = dlg_count_wchars(label);
142 int i;
144 for (i = 0; i < limit; ++i) {
145 int first = indx[i];
146 int check = UCH(label[first]);
147 #ifdef USE_WIDE_CURSES
148 int last = indx[i + 1];
149 if ((last - first) != 1) {
150 const char *temp = (label + first);
151 check = string_to_char(&temp);
153 #endif
154 if (dlg_isupper(check) && !was_hotkey(check, result, n)) {
155 result[n] = check;
156 break;
161 return result;
165 * Print a button
167 static void
168 print_button(WINDOW *win, char *label, int hotkey, int y, int x, int selected)
170 int i;
171 int state = 0;
172 const int *indx = dlg_index_wchars(label);
173 int limit = dlg_count_wchars(label);
174 chtype key_attr = (selected
175 ? button_key_active_attr
176 : button_key_inactive_attr);
177 chtype label_attr = (selected
178 ? button_label_active_attr
179 : button_label_inactive_attr);
181 (void) wmove(win, y, x);
182 (void) wattrset(win, selected
183 ? button_active_attr
184 : button_inactive_attr);
185 (void) waddstr(win, "<");
186 (void) wattrset(win, label_attr);
187 for (i = 0; i < limit; ++i) {
188 int check;
189 int first = indx[i];
190 int last = indx[i + 1];
192 switch (state) {
193 case 0:
194 check = UCH(label[first]);
195 #ifdef USE_WIDE_CURSES
196 if ((last - first) != 1) {
197 const char *temp = (label + first);
198 check = string_to_char(&temp);
200 #endif
201 if (check == hotkey) {
202 (void) wattrset(win, key_attr);
203 state = 1;
205 break;
206 case 1:
207 wattrset(win, label_attr);
208 state = 2;
209 break;
211 waddnstr(win, label + first, last - first);
213 (void) wattrset(win, selected
214 ? button_active_attr
215 : button_inactive_attr);
216 (void) waddstr(win, ">");
217 (void) wmove(win, y, x + ((int) (strspn) (label, " ")) + 1);
221 * Count the buttons in the list.
224 dlg_button_count(const char **labels)
226 int result = 0;
227 while (*labels++ != 0)
228 ++result;
229 return result;
233 * Compute the size of the button array in columns. Return the total number of
234 * columns in *length, and the longest button's columns in *longest
236 void
237 dlg_button_sizes(const char **labels,
238 int vertical,
239 int *longest,
240 int *length)
242 int n;
244 *length = 0;
245 *longest = 0;
246 for (n = 0; labels[n] != 0; n++) {
247 if (vertical) {
248 *length += 1;
249 *longest = 1;
250 } else {
251 int len = dlg_count_columns(labels[n]);
252 if (len > *longest)
253 *longest = len;
254 *length += len;
258 * If we can, make all of the buttons the same size. This is only optional
259 * for buttons laid out horizontally.
261 if (*longest < 6 - (*longest & 1))
262 *longest = 6 - (*longest & 1);
263 if (!vertical)
264 *length = *longest * n;
268 * Compute the size of the button array.
271 dlg_button_x_step(const char **labels, int limit, int *gap, int *margin, int *step)
273 int count = dlg_button_count(labels);
274 int longest;
275 int length;
276 int unused;
277 int used;
278 int result;
280 *margin = 0;
281 if (count != 0) {
282 dlg_button_sizes(labels, FALSE, &longest, &length);
283 used = (length + (count * 2));
284 unused = limit - used;
286 if ((*gap = unused / (count + 3)) <= 0) {
287 if ((*gap = unused / (count + 1)) <= 0)
288 *gap = 1;
289 *margin = *gap;
290 } else {
291 *margin = *gap * 2;
293 *step = *gap + (used + count - 1) / count;
294 result = (*gap > 0) && (unused >= 0);
295 } else {
296 result = 0;
298 return result;
302 * Make sure there is enough space for the buttons
304 void
305 dlg_button_layout(const char **labels, int *limit)
307 int width = 1;
308 int gap, margin, step;
310 if (labels != 0 && dlg_button_count(labels)) {
311 while (!dlg_button_x_step(labels, width, &gap, &margin, &step))
312 ++width;
313 width += (4 * MARGIN);
314 if (width > COLS)
315 width = COLS;
316 if (width > *limit)
317 *limit = width;
322 * Print a list of buttons at the given position.
324 void
325 dlg_draw_buttons(WINDOW *win,
326 int y, int x,
327 const char **labels,
328 int selected,
329 int vertical,
330 int limit)
332 chtype save = dlg_get_attrs(win);
333 int n;
334 int step = 0;
335 int length;
336 int longest;
337 int final_x;
338 int final_y;
339 int gap;
340 int margin;
341 size_t need;
342 char *buffer;
344 dlg_mouse_setbase(getbegx(win), getbegy(win));
346 getyx(win, final_y, final_x);
348 dlg_button_sizes(labels, vertical, &longest, &length);
350 if (vertical) {
351 y += 1;
352 step = 1;
353 } else {
354 dlg_button_x_step(labels, limit, &gap, &margin, &step);
355 x += margin;
359 * Allocate a buffer big enough for any label.
361 need = (size_t) longest;
362 if (need != 0) {
363 int *hotkeys = get_hotkeys(labels);
364 assert_ptr(hotkeys, "dlg_draw_buttons");
366 for (n = 0; labels[n] != 0; ++n) {
367 need += strlen(labels[n]) + 1;
369 buffer = dlg_malloc(char, need);
370 assert_ptr(buffer, "dlg_draw_buttons");
373 * Draw the labels.
375 for (n = 0; labels[n] != 0; n++) {
376 center_label(buffer, longest, labels[n]);
377 mouse_mkbutton(y, x, dlg_count_columns(buffer), n);
378 print_button(win, buffer,
379 CHR_BUTTON ? hotkeys[n] : -1,
380 y, x,
381 (selected == n) || (n == 0 && selected < 0));
382 if (selected == n)
383 getyx(win, final_y, final_x);
385 if (vertical) {
386 if ((y += step) > limit)
387 break;
388 } else {
389 if ((x += step) > limit)
390 break;
393 (void) wmove(win, final_y, final_x);
394 wrefresh(win);
395 (void) wattrset(win, save);
396 free(buffer);
397 free(hotkeys);
402 * Match a given character against the beginning of the string, ignoring case
403 * of the given character. The matching string must begin with an uppercase
404 * character.
407 dlg_match_char(int ch, const char *string)
409 if (string != 0) {
410 int cmp2 = string_to_char(&string);
411 #ifdef USE_WIDE_CURSES
412 wint_t cmp1 = dlg_toupper(ch);
413 if (cmp2 != 0 && (wchar_t) cmp1 == (wchar_t) dlg_toupper(cmp2)) {
414 return TRUE;
416 #else
417 if (ch > 0 && ch < 256) {
418 if (dlg_toupper(ch) == dlg_toupper(cmp2))
419 return TRUE;
421 #endif
423 return FALSE;
427 * Find the first uppercase character in the label, which we may use for an
428 * abbreviation.
431 dlg_button_to_char(const char *label)
433 int cmp = -1;
435 while (*label != 0) {
436 cmp = string_to_char(&label);
437 if (dlg_isupper(cmp)) {
438 break;
441 return cmp;
445 * Given a list of button labels, and a character which may be the abbreviation
446 * for one, find it, if it exists. An abbreviation will be the first character
447 * which happens to be capitalized in the label.
450 dlg_char_to_button(int ch, const char **labels)
452 int result = DLG_EXIT_UNKNOWN;
454 if (labels != 0) {
455 int *hotkeys = get_hotkeys(labels);
456 int j;
458 ch = (int) dlg_toupper(dlg_last_getc());
460 if (hotkeys != 0) {
461 for (j = 0; labels[j] != 0; ++j) {
462 if (ch == hotkeys[j]) {
463 dlg_flush_getc();
464 result = j;
465 break;
468 free(hotkeys);
472 return result;
475 static const char *
476 my_yes_label(void)
478 return (dialog_vars.yes_label != NULL)
479 ? dialog_vars.yes_label
480 : _("Yes");
483 static const char *
484 my_no_label(void)
486 return (dialog_vars.no_label != NULL)
487 ? dialog_vars.no_label
488 : _("No");
491 static const char *
492 my_ok_label(void)
494 return (dialog_vars.ok_label != NULL)
495 ? dialog_vars.ok_label
496 : _("OK");
499 static const char *
500 my_cancel_label(void)
502 return (dialog_vars.cancel_label != NULL)
503 ? dialog_vars.cancel_label
504 : _("Cancel");
507 static const char *
508 my_exit_label(void)
510 return (dialog_vars.exit_label != NULL)
511 ? dialog_vars.exit_label
512 : _("EXIT");
515 static const char *
516 my_extra_label(void)
518 return (dialog_vars.extra_label != NULL)
519 ? dialog_vars.extra_label
520 : _("Extra");
523 static const char *
524 my_help_label(void)
526 return (dialog_vars.help_label != NULL)
527 ? dialog_vars.help_label
528 : _("Help");
532 * Return a list of button labels.
534 const char **
535 dlg_exit_label(void)
537 const char **result;
538 DIALOG_VARS save;
540 if (dialog_vars.extra_button) {
541 dlg_save_vars(&save);
542 dialog_vars.nocancel = TRUE;
543 result = dlg_ok_labels();
544 dlg_restore_vars(&save);
545 } else {
546 static const char *labels[3];
547 int n = 0;
549 if (!dialog_vars.nook)
550 labels[n++] = my_exit_label();
551 if (dialog_vars.help_button)
552 labels[n++] = my_help_label();
553 if (n == 0)
554 labels[n++] = my_exit_label();
555 labels[n] = 0;
557 result = labels;
559 return result;
563 * Map the given button index for dlg_exit_label() into our exit-code.
566 dlg_exit_buttoncode(int button)
568 int result;
569 DIALOG_VARS save;
571 dlg_save_vars(&save);
572 dialog_vars.nocancel = TRUE;
574 result = dlg_ok_buttoncode(button);
576 dlg_restore_vars(&save);
578 return result;
581 const char **
582 dlg_ok_label(void)
584 static const char *labels[4];
585 int n = 0;
587 labels[n++] = my_ok_label();
588 if (dialog_vars.extra_button)
589 labels[n++] = my_extra_label();
590 if (dialog_vars.help_button)
591 labels[n++] = my_help_label();
592 labels[n] = 0;
593 return labels;
597 * Return a list of button labels for the OK/Cancel group.
599 const char **
600 dlg_ok_labels(void)
602 static const char *labels[5];
603 int n = 0;
605 if (!dialog_vars.nook)
606 labels[n++] = my_ok_label();
607 if (dialog_vars.extra_button)
608 labels[n++] = my_extra_label();
609 if (!dialog_vars.nocancel)
610 labels[n++] = my_cancel_label();
611 if (dialog_vars.help_button)
612 labels[n++] = my_help_label();
613 labels[n] = 0;
614 return labels;
618 * Map the given button index for dlg_ok_labels() into our exit-code
621 dlg_ok_buttoncode(int button)
623 int result = DLG_EXIT_ERROR;
624 int n = !dialog_vars.nook;
626 if (!dialog_vars.nook && (button <= 0)) {
627 result = DLG_EXIT_OK;
628 } else if (dialog_vars.extra_button && (button == n++)) {
629 result = DLG_EXIT_EXTRA;
630 } else if (!dialog_vars.nocancel && (button == n++)) {
631 result = DLG_EXIT_CANCEL;
632 } else if (dialog_vars.help_button && (button == n)) {
633 result = DLG_EXIT_HELP;
635 dlg_trace_msg("# dlg_ok_buttoncode(%d) = %d\n", button, result);
636 return result;
640 * Given that we're using dlg_ok_labels() to list buttons, find the next index
641 * in the list of buttons. The 'extra' parameter if negative provides a way to
642 * enumerate extra active areas on the widget.
645 dlg_next_ok_buttonindex(int current, int extra)
647 int result = current + 1;
649 if (current >= 0
650 && dlg_ok_buttoncode(result) < 0)
651 result = extra;
652 return result;
656 * Similarly, find the previous button index.
659 dlg_prev_ok_buttonindex(int current, int extra)
661 int result = current - 1;
663 if (result < extra) {
664 for (result = 0; dlg_ok_buttoncode(result + 1) >= 0; ++result) {
668 return result;
672 * Find the button-index for the "OK" or "Cancel" button, according to
673 * whether --defaultno is given. If --nocancel was given, we always return
674 * the index for the first button (usually "OK" unless --nook was used).
677 dlg_defaultno_button(void)
679 int result = 0;
681 if (dialog_vars.defaultno && !dialog_vars.nocancel) {
682 while (dlg_ok_buttoncode(result) != DLG_EXIT_CANCEL)
683 ++result;
685 dlg_trace_msg("# dlg_defaultno_button() = %d\n", result);
686 return result;
690 * Find the button-index for a button named with --default-button. If the
691 * option was not specified, or if the selected button does not exist, return
692 * the index of the first button (usually "OK" unless --nook was used).
695 dlg_default_button(void)
697 int i, n;
698 int result = 0;
700 if (dialog_vars.default_button >= 0) {
701 for (i = 0; (n = dlg_ok_buttoncode(i)) >= 0; i++) {
702 if (n == dialog_vars.default_button) {
703 result = i;
704 break;
708 dlg_trace_msg("# dlg_default_button() = %d\n", result);
709 return result;
713 * Return a list of buttons for Yes/No labels.
715 const char **
716 dlg_yes_labels(void)
718 const char **result;
720 if (dialog_vars.extra_button) {
721 result = dlg_ok_labels();
722 } else {
723 static const char *labels[4];
724 int n = 0;
726 labels[n++] = my_yes_label();
727 labels[n++] = my_no_label();
728 if (dialog_vars.help_button)
729 labels[n++] = my_help_label();
730 labels[n] = 0;
732 result = labels;
735 return result;
739 * Map the given button index for dlg_yes_labels() into our exit-code.
742 dlg_yes_buttoncode(int button)
744 int result = DLG_EXIT_ERROR;
746 if (dialog_vars.extra_button) {
747 result = dlg_ok_buttoncode(button);
748 } else if (button == 0) {
749 result = DLG_EXIT_OK;
750 } else if (button == 1) {
751 result = DLG_EXIT_CANCEL;
752 } else if (button == 2 && dialog_vars.help_button) {
753 result = DLG_EXIT_HELP;
756 return result;
760 * Return the next index in labels[];
763 dlg_next_button(const char **labels, int button)
765 if (button < -1)
766 button = -1;
768 if (labels[button + 1] != 0) {
769 ++button;
770 } else {
771 button = MIN_BUTTON;
773 return button;
777 * Return the previous index in labels[];
780 dlg_prev_button(const char **labels, int button)
782 if (button > MIN_BUTTON) {
783 --button;
784 } else {
785 if (button < -1)
786 button = -1;
788 while (labels[button + 1] != 0)
789 ++button;
791 return button;