2 * menubox.c -- implements the menu box
4 * AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
6 * Substantial rennovation: 12/18/95, Jordan K. Hubbard
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * $FreeBSD: src/gnu/lib/libdialog/menubox.c,v 1.35.2.1 2001/07/31 20:34:00 eric Exp $
23 * $DragonFly: src/gnu/lib/libdialog/menubox.c,v 1.2 2003/06/17 04:25:43 dillon Exp $
27 #include "dialog.priv.h"
30 static void print_item(WINDOW
*win
, unsigned char *tag
, unsigned char *item
, int choice
, int selected
, dialogMenuItem
*me
, int menu_width
, int tag_x
, int item_x
);
32 #define DREF(di, item) ((di) ? &((di)[(item)]) : NULL)
35 * Display a menu for choosing among a number of options
38 dialog_menu(unsigned char *title
, unsigned char *prompt
, int height
, int width
, int menu_height
, int cnt
, void *it
, unsigned char *result
, int *ch
, int *sc
)
40 int i
, j
, x
, y
, cur_x
, cur_y
, box_x
, box_y
, key
= 0, button
, choice
,
41 l
, k
, scroll
, max_choice
, item_no
, redraw_menu
= FALSE
;
42 char okButton
, cancelButton
;
43 int rval
= 0, ok_space
, cancel_space
;
44 WINDOW
*dialog
, *menu
;
45 unsigned char **items
= NULL
;
46 dialogMenuItem
*ditems
;
47 int menu_width
, tag_x
, item_x
;
50 choice
= ch
? *ch
: 0;
51 scroll
= sc
? *sc
: 0;
54 /* If item_no is a positive integer, use old item specification format */
60 /* It's the new specification format - fake the rest of the code out */
65 items
= (unsigned char **)alloca((item_no
* 2) * sizeof(unsigned char *));
67 /* Initializes status */
68 for (i
= 0; i
< item_no
; i
++) {
69 items
[i
*2] = ditems
[i
].prompt
;
70 items
[i
*2 + 1] = ditems
[i
].title
;
73 max_choice
= MIN(menu_height
, item_no
);
77 /* Find length of longest item in order to center menu */
78 for (i
= 0; i
< item_no
; i
++) {
79 l
= strlen(items
[i
* 2]);
80 for (j
= 0; j
< item_no
; j
++) {
81 k
= strlen(items
[j
* 2 + 1]);
82 tag_x
= MAX(tag_x
, l
+ k
+ 2);
84 item_x
= MAX(item_x
, l
);
87 height
= strheight(prompt
) + menu_height
+ 4 + 2;
90 j
= ((title
!= NULL
) ? strwidth(title
) : 0);
92 width
= MAX(width
, tag_x
+ 4) + 4;
94 width
= MAX(width
, 24);
100 /* center dialog box on screen */
101 x
= DialogX
? DialogX
: (COLS
- width
) / 2;
102 y
= DialogY
? DialogY
: (LINES
- height
) / 2;
106 draw_shadow(stdscr
, y
, x
, height
, width
);
108 dialog
= newwin(height
, width
, y
, x
);
109 if (dialog
== NULL
) {
111 fprintf(stderr
, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height
, width
, y
, x
);
114 keypad(dialog
, TRUE
);
116 draw_box(dialog
, 0, 0, height
, width
, dialog_attr
, border_attr
);
117 wattrset(dialog
, border_attr
);
118 wmove(dialog
, height
- 3, 0);
119 waddch(dialog
, ACS_LTEE
);
120 for (i
= 0; i
< width
- 2; i
++)
121 waddch(dialog
, ACS_HLINE
);
122 wattrset(dialog
, dialog_attr
);
123 waddch(dialog
, ACS_RTEE
);
124 wmove(dialog
, height
- 2, 1);
125 for (i
= 0; i
< width
- 2; i
++)
129 wattrset(dialog
, title_attr
);
130 wmove(dialog
, 0, (width
- strlen(title
)) / 2 - 1);
132 waddstr(dialog
, title
);
135 wattrset(dialog
, dialog_attr
);
137 print_autowrap(dialog
, prompt
, height
- 1, width
- 2, width
, 1, 2, TRUE
, FALSE
);
139 menu_width
= width
- 6;
140 getyx(dialog
, cur_y
, cur_x
);
142 box_x
= (width
- menu_width
) / 2 - 1;
144 /* create new window for the menu */
145 menu
= subwin(dialog
, menu_height
, menu_width
, y
+ box_y
+ 1, x
+ box_x
+ 1);
149 fprintf(stderr
, "\nsubwin(dialog,%d,%d,%d,%d) failed, maybe wrong dims\n", menu_height
, menu_width
,
150 y
+ box_y
+ 1, x
+ box_x
+ 1);
155 /* draw a box around the menu items */
156 draw_box(dialog
, box_y
, box_x
, menu_height
+2, menu_width
+2, menubox_border_attr
, menubox_attr
);
158 tag_x
= menu_width
> tag_x
+ 1 ? (menu_width
- tag_x
) / 2 : 1;
159 item_x
= menu_width
> item_x
+ 4 ? tag_x
+ item_x
+ 2 : menu_width
- 3;
162 for (i
= 0; i
< max_choice
; i
++)
163 print_item(menu
, items
[(scroll
+ i
) * 2], items
[(scroll
+ i
) * 2 + 1], i
, i
== choice
, DREF(ditems
, scroll
+ i
), menu_width
, tag_x
, item_x
);
165 print_arrows(dialog
, scroll
, menu_height
, item_no
, box_x
, box_y
, tag_x
, cur_x
, cur_y
);
167 display_helpline(dialog
, height
- 1, width
);
172 if (ditems
&& result
) {
173 cancelButton
= toupper(ditems
[CANCEL_BUTTON
].prompt
[0]);
174 print_button(dialog
, ditems
[CANCEL_BUTTON
].prompt
, y
, x
+ strlen(ditems
[OK_BUTTON
].prompt
) + 5, ditems
[CANCEL_BUTTON
].checked
? ditems
[CANCEL_BUTTON
].checked(&ditems
[CANCEL_BUTTON
]) : FALSE
);
175 okButton
= toupper(ditems
[OK_BUTTON
].prompt
[0]);
176 print_button(dialog
, ditems
[OK_BUTTON
].prompt
, y
, x
, ditems
[OK_BUTTON
].checked
? ditems
[OK_BUTTON
].checked(&ditems
[OK_BUTTON
]) : TRUE
);
180 print_button(dialog
, "Cancel", y
, x
+ 14, FALSE
);
182 print_button(dialog
, " OK ", y
, x
, TRUE
);
187 key
= wgetch(dialog
);
189 /* Shortcut to OK? */
190 if (toupper(key
) == okButton
) {
192 if (result
&& ditems
[OK_BUTTON
].fire
) {
196 save
= dupwin(newscr
);
197 status
= ditems
[OK_BUTTON
].fire(&ditems
[OK_BUTTON
]);
198 if (status
& DITEM_RESTORE
) {
206 strcpy(result
, items
[(scroll
+ choice
) * 2]);
208 key
= ESC
; /* Punt! */
212 /* Shortcut to cancel? */
213 if (toupper(key
) == cancelButton
) {
214 if (ditems
&& result
&& ditems
[CANCEL_BUTTON
].fire
) {
218 save
= dupwin(newscr
);
219 status
= ditems
[CANCEL_BUTTON
].fire(&ditems
[CANCEL_BUTTON
]);
220 if (status
& DITEM_RESTORE
) {
227 key
= ESC
; /* Run away! */
231 /* Check if key pressed matches first character of any item tag in menu */
232 for (i
= 0; i
< max_choice
; i
++)
233 if (key
< 0x100 && key
!= ' ' && toupper(key
) == toupper(items
[(scroll
+ i
) * 2][0]))
236 if (i
< max_choice
|| (key
>= '1' && key
<= MIN('9', '0'+max_choice
)) || KEY_IS_UP(key
) || KEY_IS_DOWN(key
)) {
237 if (key
>= '1' && key
<= MIN('9', '0'+max_choice
))
239 else if (KEY_IS_UP(key
)) {
242 /* Scroll menu down */
243 getyx(dialog
, cur_y
, cur_x
); /* Save cursor position */
244 if (menu_height
> 1) {
245 /* De-highlight current first item before scrolling down */
246 print_item(menu
, items
[scroll
* 2], items
[scroll
* 2 + 1], 0, FALSE
, DREF(ditems
, scroll
), menu_width
, tag_x
, item_x
);
247 scrollok(menu
, TRUE
);
249 scrollok(menu
, FALSE
);
252 print_item(menu
, items
[scroll
* 2], items
[scroll
* 2 + 1], 0, TRUE
, DREF(ditems
, scroll
), menu_width
, tag_x
, item_x
);
254 print_arrows(dialog
, scroll
, menu_height
, item_no
, box_x
, box_y
, tag_x
, cur_x
, cur_y
);
257 continue; /* wait for another key press */
262 else if (KEY_IS_DOWN(key
)) {
263 if (choice
== max_choice
- 1) {
264 if (scroll
+ choice
< item_no
- 1) {
266 getyx(dialog
, cur_y
, cur_x
); /* Save cursor position */
267 if (menu_height
> 1) {
268 /* De-highlight current last item before scrolling up */
269 print_item(menu
, items
[(scroll
+ max_choice
- 1) * 2],
270 items
[(scroll
+ max_choice
- 1) * 2 + 1],
271 max_choice
-1, FALSE
, DREF(ditems
, scroll
+ max_choice
- 1), menu_width
, tag_x
, item_x
);
272 scrollok(menu
, TRUE
);
274 scrollok(menu
, FALSE
);
277 print_item(menu
, items
[(scroll
+ max_choice
- 1) * 2],
278 items
[(scroll
+ max_choice
- 1) * 2 + 1],
279 max_choice
- 1, TRUE
, DREF(ditems
, scroll
+ max_choice
- 1), menu_width
, tag_x
, item_x
);
281 print_arrows(dialog
, scroll
, menu_height
, item_no
, box_x
, box_y
, tag_x
, cur_x
, cur_y
);
284 continue; /* wait for another key press */
291 /* De-highlight current item */
292 getyx(dialog
, cur_y
, cur_x
); /* Save cursor position */
293 print_item(menu
, items
[(scroll
+ choice
) * 2], items
[(scroll
+ choice
) * 2 + 1], choice
, FALSE
, DREF(ditems
, scroll
+ choice
), menu_width
, tag_x
, item_x
);
295 /* Highlight new item */
297 print_item(menu
, items
[(scroll
+ choice
) * 2], items
[(scroll
+ choice
) * 2 + 1], choice
, TRUE
, DREF(ditems
, scroll
+ choice
), menu_width
, tag_x
, item_x
);
299 wmove(dialog
, cur_y
, cur_x
); /* Restore cursor to previous position */
302 continue; /* wait for another key press */
307 if (scroll
> height
- 4) { /* can we go up? */
308 scroll
-= (height
- 4);
316 if (scroll
+ menu_height
>= item_no
-1 - menu_height
) { /* can we go down a full page? */
317 scroll
= item_no
- menu_height
;
321 scroll
+= menu_height
;
333 scroll
= item_no
- menu_height
;
336 choice
= max_choice
- 1;
345 if (ditems
&& result
) {
346 print_button(dialog
, ditems
[CANCEL_BUTTON
].prompt
, y
, x
+ strlen(ditems
[OK_BUTTON
].prompt
) + 5, ditems
[CANCEL_BUTTON
].checked
? ditems
[CANCEL_BUTTON
].checked(&ditems
[CANCEL_BUTTON
]) : button
);
347 print_button(dialog
, ditems
[OK_BUTTON
].prompt
, y
, x
, ditems
[OK_BUTTON
].checked
? ditems
[OK_BUTTON
].checked(&ditems
[OK_BUTTON
]) : !button
);
349 cancel_space
= strlen(ditems
[OK_BUTTON
].prompt
) + 6;
352 print_button(dialog
, "Cancel", y
, x
+ 14, button
);
353 print_button(dialog
, " OK ", y
, x
, !button
);
358 wmove(dialog
, y
, x
+cancel_space
);
360 wmove(dialog
, y
, x
+ok_space
);
368 /* A fire routine can do just about anything to the screen, so be prepared
369 to accept some hints as to what to do in the aftermath. */
371 if (ditems
[scroll
+ choice
].fire
) {
375 save
= dupwin(newscr
);
376 status
= ditems
[scroll
+ choice
].fire(&ditems
[scroll
+ choice
]);
377 if (status
& DITEM_RESTORE
) {
382 if (status
& DITEM_CONTINUE
)
384 else if (status
& DITEM_LEAVE_MENU
) {
385 /* Allow a fire action to take us out of the menu */
389 else if (status
& DITEM_RECREATE
) {
398 strcpy(result
, items
[(scroll
+choice
)*2]);
414 /* save info about menu item position */
421 for (i
= 0; i
< max_choice
; i
++) {
422 print_item(menu
, items
[(scroll
+ i
) * 2], items
[(scroll
+ i
) * 2 + 1], i
, i
== choice
, DREF(ditems
, scroll
+ i
), menu_width
, tag_x
, item_x
);
425 getyx(dialog
, cur_y
, cur_x
); /* Save cursor position */
426 print_arrows(dialog
, scroll
, menu_height
, item_no
, box_x
, box_y
, tag_x
, cur_x
, cur_y
);
427 wmove(dialog
, cur_y
, cur_x
); /* Restore cursor to previous position */
442 print_item(WINDOW
*win
, unsigned char *tag
, unsigned char *item
, int choice
, int selected
, dialogMenuItem
*me
, int menu_width
, int tag_x
, int item_x
)
446 /* Clear 'residue' of last item */
447 wattrset(win
, menubox_attr
);
448 wmove(win
, choice
, 0);
449 for (i
= 0; i
< menu_width
; i
++)
451 wmove(win
, choice
, tag_x
);
452 wattrset(win
, selected
? tag_key_selected_attr
: tag_key_attr
);
454 wattrset(win
, selected
? tag_selected_attr
: tag_attr
);
455 waddnstr(win
, tag
+ 1, item_x
- tag_x
- 3);
456 wmove(win
, choice
, item_x
);
457 wattrset(win
, selected
? item_selected_attr
: item_attr
);
458 waddnstr(win
, item
, menu_width
- item_x
- 1);
459 /* If have a selection handler for this, call it */
460 if (me
&& me
->selected
) {
462 me
->selected(me
, selected
);
465 /* End of print_item() */