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
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
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.
36 * $Id: curses_form.c,v 1.13 2005/02/08 22:56:06 cpressey Exp $
47 #define _(String) gettext (String)
49 #define _(String) (String)
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"
63 * Create a new curses form with the given title text.
66 curses_form_new(const char *title
)
68 struct curses_form
*cf
;
70 AURA_MALLOC(cf
, curses_form
);
75 cf
->widget_head
= NULL
;
76 cf
->widget_tail
= NULL
;
77 cf
->widget_focus
= NULL
;
79 cf
->width
= strlen(title
) + 4;
87 cf
->title
= aura_strdup(title
);
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.
105 curses_form_free(struct curses_form
*cf
)
107 struct curses_widget
*w
, *t
;
112 curses_widget_free(w
);
116 if (cf
->help_text
!= NULL
) {
120 if (cf
->cleanup
&& cf
->userdata
!= NULL
) {
124 if (cf
->win
!= NULL
) {
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.
139 curses_form_widget_prepare(struct curses_form
*cf
, struct curses_widget
*w
)
142 * Link the widget to the form.
147 * Auto-position the widget to the center of the form,
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
,
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
) {
185 cf
->widget_tail
->next
= w
;
186 w
->prev
= cf
->widget_tail
;
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
,
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
);
211 if (cw
->next
== NULL
)
212 cw
->form
->widget_tail
= w
;
221 * Unlink a widget from a form. Does not deallocate the widget.
224 curses_form_widget_remove(struct curses_widget
*w
)
227 w
->form
->widget_head
= w
->next
;
229 w
->prev
->next
= w
->next
;
232 w
->form
->widget_tail
= w
->prev
;
234 w
->next
->prev
= w
->prev
;
242 curses_form_descriptive_labels_add(struct curses_form
*cf
, const char *text
,
243 unsigned int x
, unsigned int y
,
246 struct curses_widget
*w
;
251 line
= aura_malloc(width
+ 1, "descriptive line");
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
);
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
;
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;
293 if (cf
->height
> (ymax
- 2)) {
294 cf
->height
= ymax
- 2;
302 cf
->win
= newwin(cf
->height
+ 2, cf
->width
+ 2, cf
->top
- 1, cf
->left
- 1);
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.
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.)
407 curses_form_refresh(struct curses_form
*cf __unused
)
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
);
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
);
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
);
452 curses_debug_int(cf
->widget_focus
->user_id
);
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
);
473 curses_debug_int(cf
->widget_focus
->user_id
);
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
))
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
))
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
) {
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
)
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
);
560 for (best
= w
; w
!= NULL
&& w
->y
== y
; w
= w
->next
) {
561 if (!curses_widget_can_take_focus(w
))
563 dist
= (w
->x
+ w
->width
/ 2) - x
;
564 if (dist
< 0) dist
*= -1;
565 if (dist
< best_dist
) {
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
;
585 for (lw
= cf
->widget_head
; lw
!= NULL
; lw
= lw
->next
) {
586 if (curses_widget_can_take_focus(lw
) && lw
->y
< w
->y
)
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
;
604 for (lw
= cf
->widget_head
; lw
!= NULL
; lw
= lw
->next
) {
605 if (curses_widget_can_take_focus(lw
) && lw
->y
> w
->y
)
613 * Move to the next widget whose y is greater than the
614 * current want_y, and whose x is closest to want_x.
617 curses_form_advance_row(struct curses_form
*cf
)
619 struct curses_widget
*w
, *c
;
622 w
= cf
->widget_focus
;
623 if (curses_form_widget_count_below(cf
, w
) > 0) {
626 wy
= curses_form_widget_first_row(cf
);
629 c
= curses_form_widget_closest_on_row(cf
,
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.
647 curses_form_retreat_row(struct curses_form
*cf
)
649 struct curses_widget
*w
, *c
;
652 w
= cf
->widget_focus
;
653 if (curses_form_widget_count_above(cf
, w
) > 0) {
656 wy
= curses_form_widget_last_row(cf
);
659 c
= curses_form_widget_closest_on_row(cf
,
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
);
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
;
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
) {
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
) {
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
);
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
);
713 cf
->widget_focus
= w
;
714 cf
->want_x
= w
->x
+ w
->width
/ 2;
720 curses_form_widget_is_visible(struct curses_widget
*w
)
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
)
734 curses_form_widget_ensure_visible(struct curses_widget
*w
)
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
))
753 wx
= w
->x
+ 1 - w
->form
->x_offset
;
754 wy
= w
->y
+ 1 - w
->form
->y_offset
;
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
);
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);
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
)
801 curses_form_advance_row(cf
);
805 curses_form_retreat_row(cf
);
808 curses_form_advance(cf
);
812 if (cf
->widget_focus
->type
== CURSES_TEXTBOX
) {
813 if (!curses_textbox_advance_char(cf
->widget_focus
))
814 curses_form_advance(cf
);
816 curses_form_advance(cf
);
820 if (cf
->widget_focus
->type
== CURSES_TEXTBOX
) {
821 if (!curses_textbox_retreat_char(cf
->widget_focus
))
822 curses_form_retreat(cf
);
824 curses_form_retreat(cf
);
828 if (cf
->widget_focus
->type
== CURSES_TEXTBOX
) {
829 switch (curses_widget_click(cf
->widget_focus
)) {
831 curses_form_advance(cf
);
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
)) {
847 return(cf
->widget_focus
);
849 } else if (cf
->widget_focus
->type
== CURSES_CHECKBOX
) {
850 curses_checkbox_toggle(cf
->widget_focus
);
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
);
866 if (cf
->widget_focus
->type
== CURSES_TEXTBOX
) {
867 curses_textbox_delete_char(cf
->widget_focus
);
874 if (cf
->widget_focus
->type
== CURSES_TEXTBOX
) {
875 curses_textbox_home(cf
->widget_focus
);
882 if (cf
->widget_focus
->type
== CURSES_TEXTBOX
) {
883 curses_textbox_end(cf
->widget_focus
);
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
);
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
);
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
)) {
914 return(cf
->widget_focus
);
916 } else if (cf
->widget_focus
->type
== CURSES_CHECKBOX
) {
917 curses_checkbox_toggle(cf
->widget_focus
);
922 case KEY_F(1): /* why does this not work in xterm??? */
924 if (cf
->help_text
!= NULL
) {
925 curses_form_show_help(cf
->help_text
);
926 curses_form_refresh(cf
);
932 curses_form_refresh(NULL
);
935 if (isprint(key
) && cf
->widget_focus
->type
== CURSES_TEXTBOX
) {
936 curses_textbox_insert_char(cf
->widget_focus
, (char)key
);
938 struct curses_widget
*cw
;
940 for (cw
= cf
->widget_head
; cw
!= NULL
; cw
= cw
->next
) {
941 if (toupper(key
) == cw
->accel
) {
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
)) {
968 curses_debug_key(key
);
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
)