]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - gnu/lib/libodialog/menubox.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / gnu / lib / libodialog / menubox.c
1 /*
2  *  menubox.c -- implements the menu box
3  *
4  *  AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
5  *
6  *      Substantial rennovation:  12/18/95, Jordan K. Hubbard
7  *
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.
12  *
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.
17  *
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.
21  */
22
23 #include <sys/cdefs.h>
24 __FBSDID("$FreeBSD$");
25
26 #include <dialog.h>
27 #include "dialog.priv.h"
28 #include <err.h>
29 #include <ncurses.h>
30
31 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
33 #define DREF(di, item)          ((di) ? &((di)[(item)]) : NULL)
34
35 /*
36  * Display a menu for choosing among a number of options
37  */
38 int
39 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 {
41     int i, j, x, y, cur_x, cur_y, box_x, box_y, key = 0, button, choice,
42         l, k, scroll, max_choice, item_no, redraw_menu = FALSE;
43     char okButton, cancelButton;
44     int rval = 0, ok_space, cancel_space;
45     WINDOW *dialog, *menu;
46     unsigned char **items = NULL;
47     dialogMenuItem *ditems;
48     int menu_width, tag_x, item_x;
49     
50 draw:
51     choice = ch ? *ch : 0;
52     scroll = sc ? *sc : 0;
53     button = 0;
54     
55     /* If item_no is a positive integer, use old item specification format */
56     if (cnt >= 0) {
57         items = it;
58         ditems = NULL;
59         item_no = cnt;
60     }
61     /* It's the new specification format - fake the rest of the code out */
62     else {
63         item_no = abs(cnt);
64         ditems = it;
65         if (!items)
66             items = (unsigned char **)alloca((item_no * 2) * sizeof(unsigned char *));
67         
68         /* Initializes status */
69         for (i = 0; i < item_no; i++) {
70             items[i*2] = ditems[i].prompt;
71             items[i*2 + 1] = ditems[i].title;
72         }
73     }
74     max_choice = MIN(menu_height, item_no);
75     
76     tag_x = 0;
77     item_x = 0;
78     /* Find length of longest item in order to center menu */
79     for (i = 0; i < item_no; i++) {
80         l = strlen(items[i * 2]);
81         for (j = 0; j < item_no; j++) {
82             k = strlen(items[j * 2 + 1]);
83             tag_x = MAX(tag_x, l + k + 2);
84         }
85         item_x = MAX(item_x, l);
86     }
87     if (height < 0)
88         height = strheight(prompt) + menu_height + 4 + 2;
89     if (width < 0) {
90         i = strwidth(prompt);
91         j = ((title != NULL) ? strwidth(title) : 0);
92         width = MAX(i, j);
93         width = MAX(width, tag_x + 4) + 4;
94     }
95     width = MAX(width, 24);
96     
97     if (width > COLS)
98         width = COLS;
99     if (height > LINES)
100         height = LINES;
101     /* center dialog box on screen */
102     x = DialogX ? DialogX : (COLS - width) / 2;
103     y = DialogY ? DialogY : (LINES - height) / 2;
104     
105 #ifdef HAVE_NCURSES
106     if (use_shadow)
107         draw_shadow(stdscr, y, x, height, width);
108 #endif
109     dialog = newwin(height, width, y, x);
110     if (dialog == NULL) {
111         endwin();
112         fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height, width, y, x);
113         return -1;
114     }
115     keypad(dialog, TRUE);
116     
117     draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
118     wattrset(dialog, border_attr);
119     wmove(dialog, height - 3, 0);
120     waddch(dialog, ACS_LTEE);
121     for (i = 0; i < width - 2; i++)
122         waddch(dialog, ACS_HLINE);
123     wattrset(dialog, dialog_attr);
124     waddch(dialog, ACS_RTEE);
125     wmove(dialog, height - 2, 1);
126     for (i = 0; i < width - 2; i++)
127         waddch(dialog, ' ');
128     
129     if (title != NULL) {
130         wattrset(dialog, title_attr);
131         wmove(dialog, 0, (width - strlen(title)) / 2 - 1);
132         waddch(dialog, ' ');
133         waddstr(dialog, title);
134         waddch(dialog, ' ');
135     }
136     wattrset(dialog, dialog_attr);
137     wmove(dialog, 1, 2);
138     print_autowrap(dialog, prompt, height - 1, width - 2, width, 1, 2, TRUE, FALSE);
139     
140     menu_width = width - 6;
141     getyx(dialog, cur_y, cur_x);
142     box_y = cur_y + 1;
143     box_x = (width - menu_width) / 2 - 1;
144     
145     /* create new window for the menu */
146     menu = subwin(dialog, menu_height, menu_width, y + box_y + 1, x + box_x + 1);
147     if (menu == NULL) {
148         delwin(dialog);
149         endwin();
150         fprintf(stderr, "\nsubwin(dialog,%d,%d,%d,%d) failed, maybe wrong dims\n", menu_height, menu_width,
151                 y + box_y + 1, x + box_x + 1);
152         return -1;
153     }
154     keypad(menu, TRUE);
155     
156     /* draw a box around the menu items */
157     draw_box(dialog, box_y, box_x, menu_height+2, menu_width+2, menubox_border_attr, menubox_attr);
158     
159     tag_x = menu_width > tag_x + 1 ? (menu_width - tag_x) / 2 : 1;
160     item_x = menu_width > item_x + 4 ? tag_x + item_x + 2 : menu_width - 3;
161     
162     /* Print the menu */
163     for (i = 0; i < max_choice; i++)
164         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     wnoutrefresh(menu);
166     print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, tag_x, cur_x, cur_y);
167     
168     display_helpline(dialog, height - 1, width);
169     
170     x = width / 2 - 11;
171     y = height - 2;
172     
173     if (ditems && result) {
174         cancelButton = toupper(ditems[CANCEL_BUTTON].prompt[0]);
175         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);
176         okButton = toupper(ditems[OK_BUTTON].prompt[0]);
177         print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : TRUE);
178     }
179     else {
180         cancelButton = 'C';
181         print_button(dialog, "Cancel", y, x + 14, FALSE);
182         okButton = 'O';
183         print_button(dialog, "  OK  ", y, x, TRUE);
184     }
185     
186     wrefresh(dialog);
187     while (key != ESC) {
188         key = wgetch(dialog);
189         
190         /* Shortcut to OK? */
191         if (toupper(key) == okButton) {
192             if (ditems) {
193                 if (result && ditems[OK_BUTTON].fire) {
194                     int status;
195                     WINDOW *save;
196
197                     save = dupwin(newscr);
198                     status = ditems[OK_BUTTON].fire(&ditems[OK_BUTTON]);
199                     if (status & DITEM_RESTORE) {
200                         touchwin(save);
201                         wrefresh(save);
202                     }
203                     delwin(save);
204                 }
205             }
206             else if (result)
207                 strcpy(result, items[(scroll + choice) * 2]);
208             rval = 0;
209             key = ESC;  /* Punt! */
210             break;
211         }
212
213         /* Shortcut to cancel? */
214         if (toupper(key) == cancelButton) {
215             if (ditems && result && ditems[CANCEL_BUTTON].fire) {
216                 int status;
217                 WINDOW *save;
218
219                 save = dupwin(newscr);
220                 status = ditems[CANCEL_BUTTON].fire(&ditems[CANCEL_BUTTON]);
221                 if (status & DITEM_RESTORE) {
222                     touchwin(save);
223                     wrefresh(save);
224                 }
225                 delwin(save);
226             }
227             rval = 1;
228             key = ESC;  /* Run away! */
229             break;
230         }
231
232         /* Check if key pressed matches first character of any item tag in menu */
233         for (i = 0; i < max_choice; i++)        
234             if (key < 0x100 && key != ' ' && toupper(key) == toupper(items[(scroll + i) * 2][0]))
235                 break;
236
237         if (i < max_choice || (key >= '1' && key <= MIN('9', '0'+max_choice)) || KEY_IS_UP(key) || KEY_IS_DOWN(key)) {
238             if (key >= '1' && key <= MIN('9', '0'+max_choice))
239                 i = key - '1';
240             else if (KEY_IS_UP(key)) {
241                 if (!choice) {
242                     if (scroll) {
243                         /* Scroll menu down */
244                         getyx(dialog, cur_y, cur_x);    /* Save cursor position */
245                         if (menu_height > 1) {
246                             /* De-highlight current first item before scrolling down */
247                             print_item(menu, items[scroll * 2], items[scroll * 2 + 1], 0, FALSE, DREF(ditems, scroll), menu_width, tag_x, item_x);
248                             scrollok(menu, TRUE);
249                             wscrl(menu, -1);
250                             scrollok(menu, FALSE);
251                         }
252                         scroll--;
253                         print_item(menu, items[scroll * 2], items[scroll * 2 + 1], 0, TRUE, DREF(ditems, scroll), menu_width, tag_x, item_x);
254                         wnoutrefresh(menu);
255                         print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, tag_x, cur_x, cur_y);
256                         wrefresh(dialog);
257                     }
258                     continue;    /* wait for another key press */
259                 }
260                 else
261                     i = choice - 1;
262             }
263             else if (KEY_IS_DOWN(key)) {
264                 if (choice == max_choice - 1) {
265                     if (scroll + choice < item_no - 1) {
266                         /* Scroll menu up */
267                         getyx(dialog, cur_y, cur_x);    /* Save cursor position */
268                         if (menu_height > 1) {
269                             /* De-highlight current last item before scrolling up */
270                             print_item(menu, items[(scroll + max_choice - 1) * 2],
271                                        items[(scroll + max_choice - 1) * 2 + 1],
272                                        max_choice-1, FALSE, DREF(ditems, scroll + max_choice - 1), menu_width, tag_x, item_x);
273                             scrollok(menu, TRUE);
274                             scroll(menu);
275                             scrollok(menu, FALSE);
276                         }
277                         scroll++;
278                         print_item(menu, items[(scroll + max_choice - 1) * 2],
279                                    items[(scroll + max_choice - 1) * 2 + 1],
280                                    max_choice - 1, TRUE, DREF(ditems, scroll + max_choice - 1), menu_width, tag_x, item_x);
281                         wnoutrefresh(menu);
282                         print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, tag_x, cur_x, cur_y);
283                         wrefresh(dialog);
284                     }
285                     continue;    /* wait for another key press */
286                 }
287                 else
288                     i = choice + 1;
289             }
290             
291             if (i != choice) {
292                 /* De-highlight current item */
293                 getyx(dialog, cur_y, cur_x);    /* Save cursor position */
294                 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                 
296                 /* Highlight new item */
297                 choice = i;
298                 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                 wnoutrefresh(menu);
300                 wmove(dialog, cur_y, cur_x);  /* Restore cursor to previous position */
301                 wrefresh(dialog);
302             }
303             continue;    /* wait for another key press */
304         }
305         
306         switch (key) {
307         case KEY_PPAGE:
308             if (scroll > height - 4) {  /* can we go up? */
309                 scroll -= (height - 4);
310             } else {
311                 scroll = 0;
312             }
313             redraw_menu = TRUE;
314             break;
315             
316         case KEY_NPAGE:
317             if (scroll + menu_height >= item_no-1 - menu_height) { /* can we go down a full page? */
318                 scroll = item_no - menu_height;
319                 if (scroll < 0)
320                     scroll = 0;
321             } else {
322                 scroll += menu_height;
323             }
324             redraw_menu = TRUE;
325             break;
326             
327         case KEY_HOME:
328             scroll = 0;
329             choice = 0;
330             redraw_menu = TRUE;
331             break;
332             
333         case KEY_END:
334             scroll = item_no - menu_height;
335             if (scroll < 0)
336                 scroll = 0;
337             choice = max_choice - 1;
338             redraw_menu = TRUE;
339             break;
340             
341         case KEY_BTAB:
342         case TAB:
343         case KEY_LEFT:
344         case KEY_RIGHT:
345             button = !button;
346             if (ditems && result) {
347                 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);
348                 print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : !button);
349                 ok_space = 1;
350                 cancel_space = strlen(ditems[OK_BUTTON].prompt) + 6;
351             }
352             else {
353                 print_button(dialog, "Cancel", y, x + 14, button);
354                 print_button(dialog, "  OK  ", y, x, !button);
355                 ok_space = 3;
356                 cancel_space = 15;
357             }
358             if (button)
359                 wmove(dialog, y, x+cancel_space);
360             else
361                 wmove(dialog, y, x+ok_space);
362             wrefresh(dialog);
363             break;
364             
365         case ' ':
366         case '\r':
367         case '\n':
368             if (!button) {
369                 /* A fire routine can do just about anything to the screen, so be prepared
370                    to accept some hints as to what to do in the aftermath. */
371                 if (ditems) {
372                     if (ditems[scroll + choice].fire) {
373                         int status;
374                         WINDOW *save;
375
376                         save = dupwin(newscr);
377                         status = ditems[scroll + choice].fire(&ditems[scroll + choice]);
378                         if (status & DITEM_RESTORE) {
379                             touchwin(save);
380                             wrefresh(save);
381                         }
382                         delwin(save);
383                         if (status & DITEM_CONTINUE)
384                             continue;
385                         else if (status & DITEM_LEAVE_MENU) {
386                             /* Allow a fire action to take us out of the menu */
387                             key = ESC;
388                             break;
389                         }
390                         else if (status & DITEM_RECREATE) {
391                             delwin(menu);
392                             delwin(dialog);
393                             dialog_clear();
394                             goto draw;
395                         }
396                     }
397                 }
398                 else if (result)
399                     strcpy(result, items[(scroll+choice)*2]);
400             }
401             rval = button;
402             key = ESC;
403             break;
404             
405         case ESC:
406             rval = -1;
407             break;
408             
409         case KEY_F(1):
410         case '?':
411             display_helpfile();
412             break;
413         }
414         
415         /* save info about menu item position */
416         if (ch)
417             *ch = choice;
418         if (sc)
419             *sc = scroll;
420         
421         if (redraw_menu) {
422             for (i = 0; i < max_choice; i++) {
423                 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);
424             }
425             wnoutrefresh(menu);
426             getyx(dialog, cur_y, cur_x);    /* Save cursor position */
427             print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, tag_x, cur_x, cur_y);
428             wmove(dialog, cur_y, cur_x);  /* Restore cursor to previous position */
429             wrefresh(dialog);
430             redraw_menu = FALSE;
431         }
432     }
433     delwin(menu);
434     delwin(dialog);
435     return rval;
436 }
437
438
439 /*
440  * Print menu item
441  */
442 static void
443 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)
444 {
445     int i;
446
447     if (tag == NULL)
448         errx(1, "bad parameter to print_item()\n");
449     
450     /* Clear 'residue' of last item */
451     wattrset(win, menubox_attr);
452     wmove(win, choice, 0);
453     for (i = 0; i < menu_width; i++)
454         waddch(win, ' ');
455     wmove(win, choice, tag_x);
456     wattrset(win, selected ? tag_key_selected_attr : tag_key_attr);
457     waddch(win, tag[0]);
458     wattrset(win, selected ? tag_selected_attr : tag_attr);
459     waddnstr(win, tag + 1, item_x - tag_x - 3);
460     wmove(win, choice, item_x);
461     wattrset(win, selected ? item_selected_attr : item_attr);
462     waddnstr(win, item, menu_width - item_x - 1);
463     /* If have a selection handler for this, call it */
464     if (me && me->selected) {
465         wrefresh(win);
466         me->selected(me, selected);
467     }
468 }
469 /* End of print_item() */