games - backgammon - use defined names instead of magic numbers.
[dragonfly.git] / usr.sbin / installer / dfuife_curses / curses_form.c
blobd980643ea8e61ab37b80b7be6d459332f7a07d17
1 /*
2 * Copyright (c)2004 Cat's Eye Technologies. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
8 * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
14 * distribution.
16 * Neither the name of Cat's Eye Technologies nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 * OF THE POSSIBILITY OF SUCH DAMAGE.
35 * curses_form.c
36 * $Id: curses_form.c,v 1.13 2005/02/08 22:56:06 cpressey Exp $
39 #include <ctype.h>
40 #include <ncurses.h>
41 #include <panel.h>
42 #include <stdlib.h>
43 #include <string.h>
45 #ifdef ENABLE_NLS
46 #include <libintl.h>
47 #define _(String) gettext (String)
48 #else
49 #define _(String) (String)
50 #endif
52 #include "libaura/mem.h"
54 #include "libdfui/dump.h"
56 #include "curses_form.h"
57 #include "curses_widget.h"
58 #include "curses_util.h"
60 /*** FORMS ***/
63 * Create a new curses form with the given title text.
65 struct curses_form *
66 curses_form_new(const char *title)
68 struct curses_form *cf;
70 AURA_MALLOC(cf, curses_form);
72 cf->win = NULL;
73 cf->pan = NULL;
75 cf->widget_head = NULL;
76 cf->widget_tail = NULL;
77 cf->widget_focus = NULL;
78 cf->height = 0;
79 cf->width = strlen(title) + 4;
80 cf->x_offset = 0;
81 cf->y_offset = 0;
82 cf->int_width = 0;
83 cf->int_height = 0;
84 cf->want_x = 0;
85 cf->want_y = 0;
87 cf->title = aura_strdup(title);
89 cf->userdata = NULL;
90 cf->cleanup = 0;
92 cf->help_text = NULL;
94 return(cf);
98 * Deallocate the memory used for the given curses form and all of the
99 * widgets it contains. Note that this only frees any data at the form's
100 * userdata pointer IFF cleanup is non-zero. Also, it does not cause the
101 * screen to be refreshed - call curses_form_refresh(NULL) afterwards to
102 * make the form disappear.
104 void
105 curses_form_free(struct curses_form *cf)
107 struct curses_widget *w, *t;
109 w = cf->widget_head;
110 while (w != NULL) {
111 t = w->next;
112 curses_widget_free(w);
113 w = t;
116 if (cf->help_text != NULL) {
117 free(cf->help_text);
120 if (cf->cleanup && cf->userdata != NULL) {
121 free(cf->userdata);
124 if (cf->win != NULL) {
125 del_panel(cf->pan);
126 delwin(cf->win);
129 free(cf->title);
130 AURA_FREE(cf, curses_form);
134 * Prepare the widget for being placed in the form. This implements
135 * automatically positioning the widget and automatically resizing
136 * the form if the widget is too large to fit.
138 static void
139 curses_form_widget_prepare(struct curses_form *cf, struct curses_widget *w)
142 * Link the widget to the form.
144 w->form = cf;
147 * Auto-position the widget to the center of the form,
148 * if requested.
150 if (w->flags & CURSES_WIDGET_CENTER)
151 w->x = (cf->width - w->width) / 2;
154 * If the widget's right edge exceeds the width of
155 * the form, expand the form.
157 dfui_debug("w->x=%d w->width=%d cf->width=%d : ",
158 w->x, w->width, cf->width);
159 if ((w->x + w->width + 1) > cf->width)
160 cf->width = w->x + w->width + 1;
161 dfui_debug("new cf->width=%d\n", cf->width);
165 * Create a new curses_widget and add it to an existing curses_form.
166 * If the width of the widget is larger than will fit on the form, the
167 * form will be expanded, unless it would be expanded larger than the
168 * screen, in which case the widget is shrunk.
170 struct curses_widget *
171 curses_form_widget_add(struct curses_form *cf,
172 unsigned int x, unsigned int y,
173 unsigned int width, widget_t type,
174 const char *text, unsigned int size,
175 unsigned int flags)
177 struct curses_widget *w;
179 w = curses_widget_new(x, y, width, type, text, size, flags);
180 curses_form_widget_prepare(cf, w);
182 if (cf->widget_head == NULL) {
183 cf->widget_head = w;
184 } else {
185 cf->widget_tail->next = w;
186 w->prev = cf->widget_tail;
188 cf->widget_tail = w;
190 return(w);
194 * Create a new curses_widget and add it after an existing curses_widget
195 * in an existing curses_form.
197 struct curses_widget *
198 curses_form_widget_insert_after(struct curses_widget *cw,
199 unsigned int x, unsigned int y,
200 unsigned int width, widget_t type,
201 const char *text, unsigned int size,
202 unsigned int flags)
204 struct curses_widget *w;
206 w = curses_widget_new(x, y, width, type, text, size, flags);
207 curses_form_widget_prepare(cw->form, w);
209 w->prev = cw;
210 w->next = cw->next;
211 if (cw->next == NULL)
212 cw->form->widget_tail = w;
213 else
214 cw->next->prev = w;
215 cw->next = w;
217 return(w);
221 * Unlink a widget from a form. Does not deallocate the widget.
223 void
224 curses_form_widget_remove(struct curses_widget *w)
226 if (w->prev == NULL)
227 w->form->widget_head = w->next;
228 else
229 w->prev->next = w->next;
231 if (w->next == NULL)
232 w->form->widget_tail = w->prev;
233 else
234 w->next->prev = w->prev;
236 w->next = NULL;
237 w->prev = NULL;
238 w->form = NULL;
242 curses_form_descriptive_labels_add(struct curses_form *cf, const char *text,
243 unsigned int x, unsigned int y,
244 unsigned int width)
246 struct curses_widget *w;
247 int done = 0;
248 int pos = 0;
249 char *line;
251 line = aura_malloc(width + 1, "descriptive line");
252 while (!done) {
253 done = extract_wrapped_line(text, line, width, &pos);
254 dfui_debug("line = `%s', done = %d, width = %d, form width = %d : ",
255 line, done, width, cf->width);
256 w = curses_form_widget_add(cf, x, y++, 0,
257 CURSES_LABEL, line, 0, CURSES_WIDGET_WIDEN);
258 dfui_debug("now %d\n", cf->width);
260 free(line);
261 return(y);
264 void
265 curses_form_finalize(struct curses_form *cf)
267 if (cf->widget_head != NULL) {
268 cf->widget_focus = cf->widget_head;
269 curses_form_focus_skip_forward(cf);
270 cf->want_x = cf->widget_focus->x + cf->widget_focus->width / 2;
271 cf->want_y = cf->widget_focus->y;
272 } else {
273 cf->widget_focus = NULL;
276 cf->left = (xmax - cf->width) / 2;
277 cf->top = (ymax - cf->height) / 2;
280 * Set the internal width and height.
283 cf->int_width = cf->width;
284 cf->int_height = cf->height;
287 * Limit form size to physical screen dimensions.
289 if (cf->width > (xmax - 2)) {
290 cf->width = xmax - 2;
291 cf->left = 1;
293 if (cf->height > (ymax - 2)) {
294 cf->height = ymax - 2;
295 cf->top = 1;
297 if (cf->top < 1)
298 cf->top = 1;
299 if (cf->left < 1)
300 cf->left = 1;
302 cf->win = newwin(cf->height + 2, cf->width + 2, cf->top - 1, cf->left - 1);
303 if (cf->win == NULL)
304 fprintf(stderr, "Could not allocate %dx%d window @ %d,%d\n",
305 cf->width + 2, cf->height + 2, cf->left - 1, cf->top - 1);
307 cf->pan = new_panel(cf->win);
311 * Render the given form (and all of the widgets it contains) in the
312 * curses backing store. Does not cause the form to be displayed.
314 void
315 curses_form_draw(struct curses_form *cf)
317 struct curses_widget *w;
318 float sb_factor = 0.0;
319 size_t sb_off = 0, sb_size = 0;
321 curses_colors_set(cf->win, CURSES_COLORS_NORMAL);
322 curses_window_blank(cf->win);
324 curses_colors_set(cf->win, CURSES_COLORS_BORDER);
325 /* draw_frame(cf->left - 1, cf->top - 1, cf->width + 2, cf->height + 2); */
326 wborder(cf->win, 0, 0, 0, 0, 0, 0, 0, 0);
329 * If the internal dimensions of the form exceed the physical
330 * dimensions, draw scrollbar(s) as appropriate.
332 if (cf->int_height > cf->height) {
333 sb_factor = (float)cf->height / (float)cf->int_height;
334 sb_size = cf->height * sb_factor;
335 if (sb_size == 0) sb_size = 1;
336 sb_off = cf->y_offset * sb_factor;
337 curses_colors_set(cf->win, CURSES_COLORS_SCROLLAREA);
338 mvwvline(cf->win, 1, cf->width + 1, ACS_CKBOARD, cf->height);
339 curses_colors_set(cf->win, CURSES_COLORS_SCROLLBAR);
340 mvwvline(cf->win, 1 + sb_off, cf->width + 1, ACS_BLOCK, sb_size);
343 if (cf->int_width > cf->width) {
344 sb_factor = (float)cf->width / (float)cf->int_width;
345 sb_size = cf->width * sb_factor;
346 if (sb_size == 0) sb_size = 1;
347 sb_off = cf->x_offset * sb_factor;
348 curses_colors_set(cf->win, CURSES_COLORS_SCROLLAREA);
349 mvwhline(cf->win, cf->height + 1, 1, ACS_CKBOARD, cf->width);
350 curses_colors_set(cf->win, CURSES_COLORS_SCROLLBAR);
351 mvwhline(cf->win, cf->height + 1, 1 + sb_off, ACS_BLOCK, sb_size);
354 curses_colors_set(cf->win, CURSES_COLORS_BORDER);
357 * Render the title bar text.
359 wmove(cf->win, 0, (cf->width - strlen(cf->title)) / 2 - 1);
360 waddch(cf->win, ACS_RTEE);
361 waddch(cf->win, ' ');
362 curses_colors_set(cf->win, CURSES_COLORS_FORMTITLE);
363 waddstr(cf->win, cf->title);
364 curses_colors_set(cf->win, CURSES_COLORS_BORDER);
365 waddch(cf->win, ' ');
366 waddch(cf->win, ACS_LTEE);
369 * Render a "how to get help" reminder.
371 if (cf->help_text != NULL) {
372 static const char *help_msg = "Press F1 for Help";
374 wmove(cf->win, cf->height + 1,
375 (cf->width - strlen(help_msg)) / 2 - 1);
376 waddch(cf->win, ACS_RTEE);
377 waddch(cf->win, ' ');
378 curses_colors_set(cf->win, CURSES_COLORS_FORMTITLE);
379 waddstr(cf->win, help_msg);
380 curses_colors_set(cf->win, CURSES_COLORS_BORDER);
381 waddch(cf->win, ' ');
382 waddch(cf->win, ACS_LTEE);
386 * Render the widgets.
388 for (w = cf->widget_head; w != NULL; w = w->next) {
389 curses_widget_draw(w);
392 /* to put the cursor there */
393 curses_widget_draw_tooltip(cf->widget_focus);
394 curses_widget_draw(cf->widget_focus);
398 * Cause the given form to be displayed (if it was not displayed previously)
399 * or refreshed (if it was previously displayed.) Passing NULL to this
400 * function causes all visible forms to be refreshed.
402 * (Implementation note: the argument is actually irrelevant - all visible
403 * forms will be refreshed when any form is displayed or refreshed - but
404 * client code should not rely on this behaviour.)
406 void
407 curses_form_refresh(struct curses_form *cf __unused)
409 update_panels();
410 doupdate();
413 void
414 curses_form_focus_skip_forward(struct curses_form *cf)
416 while (!curses_widget_can_take_focus(cf->widget_focus)) {
417 cf->widget_focus = cf->widget_focus->next;
418 if (cf->widget_focus == NULL)
419 cf->widget_focus = cf->widget_head;
421 curses_form_widget_ensure_visible(cf->widget_focus);
424 void
425 curses_form_focus_skip_backward(struct curses_form *cf)
427 while (!curses_widget_can_take_focus(cf->widget_focus)) {
428 cf->widget_focus = cf->widget_focus->prev;
429 if (cf->widget_focus == NULL)
430 cf->widget_focus = cf->widget_tail;
432 curses_form_widget_ensure_visible(cf->widget_focus);
435 void
436 curses_form_advance(struct curses_form *cf)
438 struct curses_widget *w;
440 w = cf->widget_focus;
441 cf->widget_focus = cf->widget_focus->next;
442 if (cf->widget_focus == NULL)
443 cf->widget_focus = cf->widget_head;
444 curses_form_focus_skip_forward(cf);
445 cf->want_x = cf->widget_focus->x + cf->widget_focus->width / 2;
446 cf->want_y = cf->widget_focus->y;
447 curses_widget_draw(w);
448 curses_widget_draw_tooltip(cf->widget_focus);
449 curses_widget_draw(cf->widget_focus);
450 curses_form_refresh(cf);
451 #ifdef DEBUG
452 curses_debug_int(cf->widget_focus->user_id);
453 #endif
456 void
457 curses_form_retreat(struct curses_form *cf)
459 struct curses_widget *w;
461 w = cf->widget_focus;
462 cf->widget_focus = cf->widget_focus->prev;
463 if (cf->widget_focus == NULL)
464 cf->widget_focus = cf->widget_tail;
465 curses_form_focus_skip_backward(cf);
466 cf->want_x = cf->widget_focus->x + cf->widget_focus->width / 2;
467 cf->want_y = cf->widget_focus->y;
468 curses_widget_draw(w);
469 curses_widget_draw_tooltip(cf->widget_focus);
470 curses_widget_draw(cf->widget_focus);
471 curses_form_refresh(cf);
472 #ifdef DEBUG
473 curses_debug_int(cf->widget_focus->user_id);
474 #endif
478 * Returns the widget at (x, y) within a form, or NULL if
479 * there is no widget at that location.
481 struct curses_widget *
482 curses_form_widget_at(struct curses_form *cf, unsigned int x, unsigned int y)
484 struct curses_widget *w;
486 for (w = cf->widget_head; w != NULL; w = w->next) {
487 if (y == w->y && x >= w->x && x <= (w->x + w->width))
488 return(w);
491 return(NULL);
495 * Returns the first (focusable) widget on
496 * the topmost row of the form.
499 curses_form_widget_first_row(struct curses_form *cf)
501 struct curses_widget *w;
503 for (w = cf->widget_head; w != NULL; w = w->next) {
504 if (curses_widget_can_take_focus(w))
505 return(w->y);
508 return(0);
512 * Returns the first (focusable) widget on
513 * the bottommost row of the form.
516 curses_form_widget_last_row(struct curses_form *cf)
518 struct curses_widget *w;
519 unsigned int best_y = 0;
521 for (w = cf->widget_head; w != NULL; w = w->next) {
522 if (curses_widget_can_take_focus(w) && w->y > best_y) {
523 best_y = w->y;
527 return(best_y);
531 * Returns the first (focusable) widget on row y.
533 struct curses_widget *
534 curses_form_widget_first_on_row(struct curses_form *cf, unsigned int y)
536 struct curses_widget *w;
538 for (w = cf->widget_head; w != NULL; w = w->next) {
539 if (curses_widget_can_take_focus(w) && y == w->y)
540 return(w);
543 return(NULL);
547 * Returns the (focusable) widget on row y closest to x.
549 struct curses_widget *
550 curses_form_widget_closest_on_row(struct curses_form *cf,
551 unsigned int x, unsigned int y)
553 struct curses_widget *w, *best = NULL;
554 int dist, best_dist = 999;
556 w = curses_form_widget_first_on_row(cf, y);
557 if (w == NULL)
558 return(NULL);
560 for (best = w; w != NULL && w->y == y; w = w->next) {
561 if (!curses_widget_can_take_focus(w))
562 continue;
563 dist = (w->x + w->width / 2) - x;
564 if (dist < 0) dist *= -1;
565 if (dist < best_dist) {
566 best_dist = dist;
567 best = w;
571 return(best);
575 * Returns the number of (focusable) widgets with y values less than
576 * (above) the given widget.
579 curses_form_widget_count_above(struct curses_form *cf,
580 struct curses_widget *w)
582 struct curses_widget *lw;
583 int count = 0;
585 for (lw = cf->widget_head; lw != NULL; lw = lw->next) {
586 if (curses_widget_can_take_focus(lw) && lw->y < w->y)
587 count++;
590 return(count);
594 * Returns the number of (focusable) widgets with y values greater than
595 * (below) the given widget.
598 curses_form_widget_count_below(struct curses_form *cf,
599 struct curses_widget *w)
601 struct curses_widget *lw;
602 int count = 0;
604 for (lw = cf->widget_head; lw != NULL; lw = lw->next) {
605 if (curses_widget_can_take_focus(lw) && lw->y > w->y)
606 count++;
609 return(count);
613 * Move to the next widget whose y is greater than the
614 * current want_y, and whose x is closest to want_x.
616 void
617 curses_form_advance_row(struct curses_form *cf)
619 struct curses_widget *w, *c;
620 int wy;
622 w = cf->widget_focus;
623 if (curses_form_widget_count_below(cf, w) > 0) {
624 wy = cf->want_y + 1;
625 } else {
626 wy = curses_form_widget_first_row(cf);
628 do {
629 c = curses_form_widget_closest_on_row(cf,
630 cf->want_x, wy++);
631 } while (c == NULL);
633 cf->widget_focus = c;
634 curses_form_focus_skip_forward(cf);
635 cf->want_y = cf->widget_focus->y;
636 curses_widget_draw(w);
637 curses_widget_draw_tooltip(cf->widget_focus);
638 curses_widget_draw(cf->widget_focus);
639 curses_form_refresh(cf);
643 * Move to the next widget whose y is less than the
644 * current want_y, and whose x is closest to want_x.
646 void
647 curses_form_retreat_row(struct curses_form *cf)
649 struct curses_widget *w, *c;
650 int wy;
652 w = cf->widget_focus;
653 if (curses_form_widget_count_above(cf, w) > 0) {
654 wy = cf->want_y - 1;
655 } else {
656 wy = curses_form_widget_last_row(cf);
658 do {
659 c = curses_form_widget_closest_on_row(cf,
660 cf->want_x, wy--);
661 } while (c == NULL);
663 cf->widget_focus = c;
664 curses_form_focus_skip_backward(cf);
665 cf->want_y = cf->widget_focus->y;
666 curses_widget_draw(w);
667 curses_widget_draw_tooltip(cf->widget_focus);
668 curses_widget_draw(cf->widget_focus);
669 curses_form_refresh(cf);
672 void
673 curses_form_scroll_to(struct curses_form *cf,
674 unsigned int x_off, unsigned int y_off)
676 cf->x_offset = x_off;
677 cf->y_offset = y_off;
680 void
681 curses_form_scroll_delta(struct curses_form *cf, int dx, int dy)
683 unsigned int x_off, y_off;
685 if (dx < 0 && (unsigned int)-dx > cf->x_offset) {
686 x_off = 0;
687 } else {
688 x_off = cf->x_offset + dx;
690 if (x_off > (cf->int_width - cf->width))
691 x_off = cf->int_width - cf->width;
693 if (dy < 0 && (unsigned int)-dy > cf->y_offset) {
694 y_off = 0;
695 } else {
696 y_off = cf->y_offset + dy;
698 if (y_off > (cf->int_height - cf->height))
699 y_off = cf->int_height - cf->height;
701 curses_form_scroll_to(cf, x_off, y_off);
704 static void
705 curses_form_refocus_after_scroll(struct curses_form *cf, int dx, int dy)
707 struct curses_widget *w;
709 w = curses_form_widget_closest_on_row(cf,
710 cf->widget_focus->x + dx, cf->widget_focus->y + dy);
712 if (w != NULL) {
713 cf->widget_focus = w;
714 cf->want_x = w->x + w->width / 2;
715 cf->want_y = w->y;
720 curses_form_widget_is_visible(struct curses_widget *w)
722 unsigned int wx, wy;
724 wx = w->x + 1 - w->form->x_offset;
725 wy = w->y + 1 - w->form->y_offset;
727 if (wy < 1 || wy > w->form->height)
728 return(0);
730 return(1);
733 void
734 curses_form_widget_ensure_visible(struct curses_widget *w)
736 unsigned int wx, wy;
737 int dx = 0, dy = 0;
740 * If a textbox's offset is such that we can't see
741 * the cursor inside, adjust it.
743 if (w->type == CURSES_TEXTBOX) {
744 if (w->curpos - w->offset >= w->width - 2)
745 w->offset = w->curpos - (w->width - 3);
746 if (w->offset > w->curpos)
747 w->offset = w->curpos;
750 if (curses_form_widget_is_visible(w))
751 return;
753 wx = w->x + 1 - w->form->x_offset;
754 wy = w->y + 1 - w->form->y_offset;
756 if (wy < 1)
757 dy = -1 * (1 - wy);
758 else if (wy > w->form->height)
759 dy = (wy - w->form->height);
761 curses_form_scroll_delta(w->form, dx, dy);
762 curses_form_draw(w->form);
763 curses_form_refresh(w->form);
766 static void
767 curses_form_show_help(const char *text)
769 struct curses_form *cf;
770 struct curses_widget *w;
772 cf = curses_form_new(_("Help"));
774 cf->height = curses_form_descriptive_labels_add(cf, text, 1, 1, 72);
775 cf->height += 1;
776 w = curses_form_widget_add(cf, 0, cf->height++, 0,
777 CURSES_BUTTON, _("OK"), 0, CURSES_WIDGET_WIDEN);
778 curses_widget_set_click_cb(w, cb_click_close_form);
780 curses_form_finalize(cf);
782 curses_form_draw(cf);
783 curses_form_refresh(cf);
784 curses_form_frob(cf);
785 curses_form_free(cf);
788 #define CTRL(c) (char)(c - 'a' + 1)
790 struct curses_widget *
791 curses_form_frob(struct curses_form *cf)
793 int key;
795 flushinp();
796 for (;;) {
797 key = getch();
798 switch(key) {
799 case KEY_DOWN:
800 case CTRL('n'):
801 curses_form_advance_row(cf);
802 break;
803 case KEY_UP:
804 case CTRL('p'):
805 curses_form_retreat_row(cf);
806 break;
807 case '\t':
808 curses_form_advance(cf);
809 break;
810 case KEY_RIGHT:
811 case CTRL('f'):
812 if (cf->widget_focus->type == CURSES_TEXTBOX) {
813 if (!curses_textbox_advance_char(cf->widget_focus))
814 curses_form_advance(cf);
815 } else
816 curses_form_advance(cf);
817 break;
818 case KEY_LEFT:
819 case CTRL('b'):
820 if (cf->widget_focus->type == CURSES_TEXTBOX) {
821 if (!curses_textbox_retreat_char(cf->widget_focus))
822 curses_form_retreat(cf);
823 } else
824 curses_form_retreat(cf);
825 break;
826 case '\n':
827 case '\r':
828 if (cf->widget_focus->type == CURSES_TEXTBOX) {
829 switch (curses_widget_click(cf->widget_focus)) {
830 case -1:
831 curses_form_advance(cf);
832 break;
833 case 0:
834 break;
835 case 1:
836 /* this would be pretty rare */
837 return(cf->widget_focus);
839 } else if (cf->widget_focus->type == CURSES_BUTTON) {
840 switch (curses_widget_click(cf->widget_focus)) {
841 case -1:
842 beep();
843 break;
844 case 0:
845 break;
846 case 1:
847 return(cf->widget_focus);
849 } else if (cf->widget_focus->type == CURSES_CHECKBOX) {
850 curses_checkbox_toggle(cf->widget_focus);
851 } else {
852 beep();
854 break;
855 case '\b':
856 case KEY_BACKSPACE:
857 case 127: /* why is this not KEY_BACKSPACE on xterm?? */
858 if (cf->widget_focus->type == CURSES_TEXTBOX) {
859 curses_textbox_backspace_char(cf->widget_focus);
860 } else {
861 beep();
863 break;
864 case KEY_DC:
865 case CTRL('k'):
866 if (cf->widget_focus->type == CURSES_TEXTBOX) {
867 curses_textbox_delete_char(cf->widget_focus);
868 } else {
869 beep();
871 break;
872 case KEY_HOME:
873 case CTRL('a'):
874 if (cf->widget_focus->type == CURSES_TEXTBOX) {
875 curses_textbox_home(cf->widget_focus);
876 } else {
877 beep();
879 break;
880 case KEY_END:
881 case CTRL('e'):
882 if (cf->widget_focus->type == CURSES_TEXTBOX) {
883 curses_textbox_end(cf->widget_focus);
884 } else {
885 beep();
887 break;
888 case KEY_NPAGE:
889 case CTRL('g'):
890 curses_form_scroll_delta(cf, 0, cf->height - 1);
891 curses_form_refocus_after_scroll(cf, 0, cf->height - 1);
892 curses_form_draw(cf);
893 curses_form_refresh(cf);
894 break;
895 case KEY_PPAGE:
896 case CTRL('t'):
897 curses_form_scroll_delta(cf, 0, -1 * (cf->height - 1));
898 curses_form_refocus_after_scroll(cf, 0, -1 * (cf->height - 1));
899 curses_form_draw(cf);
900 curses_form_refresh(cf);
901 break;
902 case ' ':
903 if (cf->widget_focus->type == CURSES_TEXTBOX) {
904 /* XXX if non-editable, click it */
905 curses_textbox_insert_char(cf->widget_focus, ' ');
906 } else if (cf->widget_focus->type == CURSES_BUTTON) {
907 switch (curses_widget_click(cf->widget_focus)) {
908 case -1:
909 beep();
910 break;
911 case 0:
912 break;
913 case 1:
914 return(cf->widget_focus);
916 } else if (cf->widget_focus->type == CURSES_CHECKBOX) {
917 curses_checkbox_toggle(cf->widget_focus);
918 } else {
919 beep();
921 break;
922 case KEY_F(1): /* why does this not work in xterm??? */
923 case CTRL('w'):
924 if (cf->help_text != NULL) {
925 curses_form_show_help(cf->help_text);
926 curses_form_refresh(cf);
928 break;
929 case KEY_F(10):
930 case CTRL('l'):
931 redrawwin(stdscr);
932 curses_form_refresh(NULL);
933 break;
934 default:
935 if (isprint(key) && cf->widget_focus->type == CURSES_TEXTBOX) {
936 curses_textbox_insert_char(cf->widget_focus, (char)key);
937 } else {
938 struct curses_widget *cw;
940 for (cw = cf->widget_head; cw != NULL; cw = cw->next) {
941 if (toupper(key) == cw->accel) {
943 * To just refocus:
946 cf->widget_focus = cw;
947 curses_form_widget_ensure_visible(cw);
948 curses_form_draw(cf);
949 curses_form_refresh(cf);
952 * To actually activate:
954 switch (curses_widget_click(cw)) {
955 case -1:
956 beep();
957 break;
958 case 0:
959 break;
960 case 1:
961 return(cw);
964 break;
967 #ifdef DEBUG
968 curses_debug_key(key);
969 #endif
970 beep();
972 break;
977 /*** GENERIC CALLBACKS ***/
980 * Callback to give to curses_button_set_click_cb, for buttons
981 * that simply close the form they are in when they are clicked.
982 * These usually map to dfui actions.
985 cb_click_close_form(struct curses_widget *w __unused)
987 return(1);