Initial import from FreeBSD RELENG_4:
[dragonfly.git] / gnu / lib / libdialog / checklist.c
1 /*
2  *  checklist.c -- implements the checklist 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
24 #ifndef lint
25 static const char rcsid[] =
26   "$FreeBSD: src/gnu/lib/libdialog/checklist.c,v 1.35.2.2 2001/08/31 03:04:59 eric Exp $";
27 #endif
28
29 #include <dialog.h>
30 #include "dialog.priv.h"
31
32
33 static void print_item(WINDOW *win, unsigned char *tag, unsigned char *item, int status, int choice, int selected, dialogMenuItem *me, int list_width, int item_x, int check_x);
34
35 #define DREF(di, item)          ((di) ? &((di)[(item)]) : NULL)
36
37 /*
38  * Display a dialog box with a list of options that can be turned on or off
39  */
40 int
41 dialog_checklist(unsigned char *title, unsigned char *prompt, int height, int width,
42                  int list_height, int cnt, void *it, unsigned char *result)
43 {
44     int i, j, x, y, cur_x, cur_y, old_x, old_y, box_x, box_y, key = 0, button,
45         choice, l, k, scroll, max_choice, item_no = 0, *status;
46     int redraw_menu = FALSE, cursor_reset = FALSE;
47     int rval = 0, onlist = 1, ok_space, cancel_space;
48     char okButton, cancelButton;
49     WINDOW *dialog, *list;
50     unsigned char **items = NULL;
51     dialogMenuItem *ditems;
52     int list_width, check_x, item_x;
53
54     /* Allocate space for storing item on/off status */
55     if ((status = alloca(sizeof(int) * abs(cnt))) == NULL) {
56         endwin();
57         fprintf(stderr, "\nCan't allocate memory in dialog_checklist().\n");
58         exit(-1);
59     }
60     
61 draw:
62     choice = scroll = button = 0;
63     /* Previous calling syntax, e.g. just a list of strings? */
64     if (cnt >= 0) {
65         items = it;
66         ditems = NULL;
67         item_no = cnt;
68         /* Initializes status */
69         for (i = 0; i < item_no; i++)
70             status[i] = !strcasecmp(items[i*3 + 2], "on");
71     }
72     /* It's the new specification format - fake the rest of the code out */
73     else {
74         item_no = abs(cnt);
75         ditems = it;
76         if (!items)
77             items = (unsigned char **)alloca((item_no * 3) * sizeof(unsigned char *));
78         
79         /* Initializes status */
80         for (i = 0; i < item_no; i++) {
81             status[i] = ditems[i].checked ? ditems[i].checked(&ditems[i]) : FALSE;
82             items[i*3] = ditems[i].prompt;
83             items[i*3 + 1] = ditems[i].title;
84             items[i*3 + 2] = status[i] ? "on" : "off";
85         }
86     }
87     max_choice = MIN(list_height, item_no);
88     
89     check_x = 0;
90     item_x = 0;
91     /* Find length of longest item in order to center checklist */
92     for (i = 0; i < item_no; i++) {
93         l = strlen(items[i*3]);
94         for (j = 0; j < item_no; j++) {
95             k = strlen(items[j*3 + 1]);
96             check_x = MAX(check_x, l + k + 6);
97         }
98         item_x = MAX(item_x, l);
99     }
100     if (height < 0)
101         height = strheight(prompt)+list_height+4+2;
102     if (width < 0) {
103         i = strwidth(prompt);
104         j = ((title != NULL) ? strwidth(title) : 0);
105         width = MAX(i,j);
106         width = MAX(width,check_x+4)+4;
107     }
108     width = MAX(width,24);
109     
110     if (width > COLS)
111         width = COLS;
112     if (height > LINES)
113         height = LINES;
114     /* center dialog box on screen */
115     x = (COLS - width)/2;
116     y = (LINES - height)/2;
117
118 #ifdef HAVE_NCURSES
119     if (use_shadow)
120         draw_shadow(stdscr, y, x, height, width);
121 #endif
122     dialog = newwin(height, width, y, x);
123     if (dialog == NULL) {
124         endwin();
125         fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height,width, y, x);
126         return -1;
127     }
128     keypad(dialog, TRUE);
129     
130     draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
131     wattrset(dialog, border_attr);
132     wmove(dialog, height-3, 0);
133     waddch(dialog, ACS_LTEE);
134     for (i = 0; i < width-2; i++)
135         waddch(dialog, ACS_HLINE);
136     wattrset(dialog, dialog_attr);
137     waddch(dialog, ACS_RTEE);
138     wmove(dialog, height-2, 1);
139     for (i = 0; i < width-2; i++)
140         waddch(dialog, ' ');
141     
142     if (title != NULL) {
143         wattrset(dialog, title_attr);
144         wmove(dialog, 0, (width - strlen(title))/2 - 1);
145         waddch(dialog, ' ');
146         waddstr(dialog, title);
147         waddch(dialog, ' ');
148     }
149     wattrset(dialog, dialog_attr);
150     wmove(dialog, 1, 2);
151     print_autowrap(dialog, prompt, height - 1, width - 2, width, 1, 2, TRUE, FALSE);
152     
153     list_width = width - 6;
154     getyx(dialog, cur_y, cur_x);
155     box_y = cur_y + 1;
156     box_x = (width - list_width) / 2 - 1;
157     
158     /* create new window for the list */
159     list = subwin(dialog, list_height, list_width, y + box_y + 1, x + box_x + 1);
160     if (list == NULL) {
161         delwin(dialog);
162         endwin();
163         fprintf(stderr, "\nsubwin(dialog,%d,%d,%d,%d) failed, maybe wrong dims\n", list_height, list_width,
164                 y + box_y + 1, x + box_x + 1);
165         return -1;
166     }
167     keypad(list, TRUE);
168     
169     /* draw a box around the list items */
170     draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2, menubox_border_attr, menubox_attr);
171     
172     check_x = (list_width - check_x) / 2;
173     item_x = check_x + item_x + 6;
174     
175     /* Print the list */
176     for (i = 0; i < max_choice; i++)
177         print_item(list, items[i * 3], items[i * 3 + 1], status[i], i, i == choice, DREF(ditems, i), list_width, item_x, check_x);
178     wnoutrefresh(list);
179     print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, cur_x, cur_y);
180     
181     display_helpline(dialog, height - 1, width);
182     
183     x = width / 2 - 11;
184     y = height - 2;
185     /* Is this a fancy new style argument string where we get to override
186      * the buttons, or an old style one where they're fixed?
187      */
188     if (ditems && result) {
189         cancelButton = toupper(ditems[CANCEL_BUTTON].prompt[0]);
190         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);
191         okButton = toupper(ditems[OK_BUTTON].prompt[0]);
192         print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : TRUE);
193     }
194     else {
195         cancelButton = 'C';
196         print_button(dialog, "Cancel", y, x + 14, FALSE);
197         okButton = 'O';
198         print_button(dialog, "  OK  ", y, x, TRUE);
199     }
200     wnoutrefresh(dialog);
201     wmove(list, choice, check_x+1);
202     wrefresh(list);
203     
204     while (key != ESC) {
205         key = wgetch(dialog);
206         
207         /* Shortcut to OK? */
208         if (toupper(key) == okButton) {
209             if (ditems) {
210                 if (result && ditems[OK_BUTTON].fire) {
211                     int st;
212                     WINDOW *save;
213
214                     save = dupwin(newscr);
215                     st = ditems[OK_BUTTON].fire(&ditems[OK_BUTTON]);
216                     if (st & DITEM_RESTORE) {
217                         touchwin(save);
218                         wrefresh(save);
219                     }
220                     delwin(save);
221                 }
222             }
223             else if (result) {
224                 *result = '\0';
225                 for (i = 0; i < item_no; i++) {
226                     if (status[i]) {
227                         strcat(result, items[i*3]);
228                         strcat(result, "\n");
229                     }
230                 }
231             }
232             rval = 0;
233             key = ESC;  /* Lemme out! */
234             break;
235         }
236
237         /* Shortcut to cancel? */
238         if (toupper(key) == cancelButton) {
239             if (ditems && result && ditems[CANCEL_BUTTON].fire) {
240                 int st;
241                 WINDOW *save;
242
243                 save = dupwin(newscr);
244                 st = ditems[CANCEL_BUTTON].fire(&ditems[CANCEL_BUTTON]);
245                 if (st & DITEM_RESTORE) {
246                     touchwin(save);
247                     wrefresh(save);
248                     wmove(dialog, cur_y, cur_x);
249                 }
250                 delwin(save);
251             }
252             rval = 1;
253             key = ESC;  /* I gotta go! */
254             break;
255         }
256         
257         /* Check if key pressed matches first character of any item tag in list */
258         for (i = 0; i < max_choice; i++)
259             if (key != ' ' && key < 0x100 && toupper(key) == toupper(items[(scroll+i)*3][0]))
260                 break;
261
262         if (i < max_choice || (key >= '1' && key <= MIN('9', '0'+max_choice)) ||
263             KEY_IS_UP(key) || KEY_IS_DOWN(key) || ((key == ' ' || key == '\n' ||
264             key == '\r') && onlist)) {
265
266             /* if moving from buttons to the list, reset and redraw buttons */
267             if (!onlist) {
268                 onlist = 1;
269                 button = 0;
270
271                 if (ditems && result) {
272                     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);
273                     print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : !button);
274                 }
275                 else {
276                     print_button(dialog, "Cancel", y, x + 14, button);
277                     print_button(dialog, "  OK  ", y, x, !button);
278                 }
279                 wmove(list, choice, check_x+1);
280                 wnoutrefresh(dialog);
281                 wrefresh(list);
282             }
283
284             if (key >= '1' && key <= MIN('9', '0'+max_choice))
285                 i = key - '1';
286             
287             else if (KEY_IS_UP(key)) {
288                 if (!choice) {
289                     if (scroll) {
290                         /* Scroll list down */
291                         getyx(dialog, cur_y, cur_x);    /* Save cursor position */
292                         if (list_height > 1) {
293                             /* De-highlight current first item before scrolling down */
294                             print_item(list, items[scroll * 3], items[scroll * 3 + 1], status[scroll], 0,
295                                        FALSE, DREF(ditems, scroll), list_width, item_x, check_x);
296                             scrollok(list, TRUE);
297                             wscrl(list, -1);
298                             scrollok(list, FALSE);
299                         }
300                         scroll--;
301                         print_item(list, items[scroll*3], items[scroll*3 + 1], status[scroll], 0,
302                                    TRUE, DREF(ditems, scroll), list_width, item_x, check_x);
303                         print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, cur_x, cur_y);
304                         wmove(list, choice, check_x+1);
305                         wnoutrefresh(dialog);
306                         wrefresh(list);
307                     }
308                     continue;    /* wait for another key press */
309                 }
310                 else
311                     i = choice - 1;
312             }
313             else if (KEY_IS_DOWN(key)) {
314                 if (choice == max_choice - 1) {
315                     if (scroll + choice < item_no - 1) {
316                         /* Scroll list up */
317                         getyx(dialog, cur_y, cur_x);    /* Save cursor position */
318                         if (list_height > 1) {
319                             /* De-highlight current last item before scrolling up */
320                             print_item(list, items[(scroll + max_choice - 1) * 3],
321                                        items[(scroll + max_choice - 1) * 3 + 1],
322                                        status[scroll + max_choice - 1], max_choice - 1,
323                                        FALSE, DREF(ditems, scroll + max_choice - 1), list_width, item_x, check_x);
324                             scrollok(list, TRUE);
325                             scroll(list);
326                             scrollok(list, FALSE);
327                         }
328                         scroll++;
329                         print_item(list, items[(scroll + max_choice - 1) * 3],
330                                    items[(scroll + max_choice - 1) * 3 + 1],
331                                    status[scroll + max_choice - 1], max_choice - 1, TRUE,
332                                    DREF(ditems, scroll + max_choice - 1), list_width, item_x, check_x);
333                         print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, cur_x, cur_y);
334                         wmove(list, choice, check_x+1);
335                         wnoutrefresh(dialog);
336                         wrefresh(list);
337                     }
338                     continue;    /* wait for another key press */
339                 }
340                 else
341                     i = choice + 1;
342             }
343             else if ((key == ' ' || key == '\n' || key == '\r') && onlist) {    /* Toggle item status */
344                 char lbra = 0, rbra = 0, mark = 0;
345
346                 getyx(list, old_y, old_x);    /* Save cursor position */
347                 
348                 if (ditems) {
349                     if (ditems[scroll + choice].fire) {
350                         int st;
351                         WINDOW *save;
352
353                         save = dupwin(newscr);
354                         st = ditems[scroll + choice].fire(&ditems[scroll + choice]);    /* Call "fire" action */
355                         if (st & DITEM_RESTORE) {
356                             touchwin(save);
357                             wrefresh(save);
358                         }
359                         delwin(save);
360                         if (st & DITEM_REDRAW) {
361                             wclear(list);
362                             for (i = 0; i < item_no; i++)
363                                 status[i] = ditems[i].checked ? ditems[i].checked(&ditems[i]) : FALSE;
364                             for (i = 0; i < max_choice; i++) {
365                                 print_item(list, items[(scroll + i) * 3], items[(scroll + i) * 3 + 1],
366                                            status[scroll + i], i, i == choice, DREF(ditems, scroll + i), list_width, item_x, check_x);
367                             }
368                             wnoutrefresh(list);
369                             print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4,
370                                          cur_x, cur_y);
371                             wrefresh(dialog);
372                         }
373                         if (st & DITEM_LEAVE_MENU) {
374                             /* Allow a fire action to take us out of the menu */
375                             key = ESC;
376                             rval = 0;
377                             break;
378                         }
379                         else if (st & DITEM_RECREATE) {
380                             delwin(list);
381                             delwin(dialog);
382                             dialog_clear();
383                             goto draw;
384                         }
385                     }
386                     status[scroll + choice] = ditems[scroll + choice].checked ?
387                         ditems[scroll + choice].checked(&ditems[scroll + choice]) : FALSE;
388                     lbra = ditems[scroll + choice].lbra;
389                     rbra = ditems[scroll + choice].rbra;
390                     mark = ditems[scroll + choice].mark;
391                 }
392                 else
393                     status[scroll + choice] = !status[scroll + choice];
394                 wmove(list, choice, check_x);
395                 wattrset(list, check_selected_attr);
396                 if (!lbra)
397                     lbra = '[';
398                 if (!rbra)
399                     rbra = ']';
400                 if (!mark)
401                     mark = 'X';
402                 wprintw(list, "%c%c%c", lbra, status[scroll + choice] ? mark : ' ', rbra);
403                 wmove(list, old_y, old_x);  /* Restore cursor to previous position */
404                 wrefresh(list);
405                 continue;    /* wait for another key press */
406             }
407             
408             if (i != choice) {
409                 /* De-highlight current item */
410                 getyx(dialog, cur_y, cur_x);    /* Save cursor position */
411                 print_item(list, items[(scroll + choice) * 3], items[(scroll + choice) * 3 + 1],
412                            status[scroll + choice], choice, FALSE, DREF(ditems, scroll + choice), list_width, item_x, check_x);
413                 
414                 /* Highlight new item */
415                 choice = i;
416                 print_item(list, items[(scroll + choice) * 3], items[(scroll + choice) * 3 + 1], status[scroll + choice], choice, TRUE, DREF(ditems, scroll + choice), list_width, item_x, check_x);
417                 wmove(list, choice, check_x+1);   /* Restore cursor to previous position */
418                 wrefresh(list);
419             }
420             continue;    /* wait for another key press */
421         }
422         
423         switch (key) {
424         case KEY_PPAGE: /* can we go up? */
425             if (scroll > height - 4)
426                 scroll -= (height-4);
427             else
428                 scroll = 0;
429             redraw_menu = TRUE;
430             if (!onlist) {
431                 onlist = 1;
432                 button = 0;
433             }
434             break;
435             
436         case KEY_NPAGE:      /* can we go down a full page? */
437             if (scroll + list_height >= item_no-1 - list_height) {
438                 scroll = item_no - list_height;
439                 if (scroll < 0)
440                     scroll = 0;
441             }
442             else
443                 scroll += list_height;
444             redraw_menu = TRUE;
445             if (!onlist) {
446                 onlist = 1;
447                 button = 0;
448             }
449             break;
450             
451         case KEY_HOME:      /* go to the top */
452             scroll = 0;
453             choice = 0;
454             redraw_menu = TRUE;
455             cursor_reset = TRUE;
456             onlist = 1;
457             break;
458             
459         case KEY_END:      /* Go to the bottom */
460             scroll = item_no - list_height;
461             if (scroll < 0)
462                 scroll = 0;
463             choice = max_choice - 1;
464             redraw_menu = TRUE;
465             cursor_reset = TRUE;
466             onlist = 1;
467             break;
468             
469         case TAB:
470         case KEY_BTAB:
471             /* move to next component */
472             if (onlist) {       /* on list, next is ok button */
473                 onlist = 0;
474                 if (ditems && result) {
475                     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);
476                     print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : !button);
477                     ok_space = 1;
478                     cancel_space = strlen(ditems[OK_BUTTON].prompt) + 6;
479                 }
480                 else {
481                     print_button(dialog, "Cancel", y, x + 14, button);
482                     print_button(dialog, "  OK  ", y, x, !button);
483                     ok_space = 3;
484                     cancel_space = 15;
485                 }
486                 if (button)
487                     wmove(dialog, y, x + cancel_space);
488                 else
489                     wmove(dialog, y, x + ok_space);
490                 wrefresh(dialog);
491                 break;
492             }
493             else if (button) {     /* on cancel button, next is list */
494                 button = 0;
495                 onlist = 1;
496                 redraw_menu = TRUE;
497                 break; 
498             }
499             /* on ok button, next is cancel button, same as left/right case */
500
501         case KEY_LEFT:
502         case KEY_RIGHT:
503             onlist = 0;
504             button = !button;
505             if (ditems && result) {
506                 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);
507                 print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ?  ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : !button);
508                 ok_space = 1;
509                 cancel_space = strlen(ditems[OK_BUTTON].prompt) + 6;
510             }
511             else {
512                 print_button(dialog, "Cancel", y, x + 14, button);
513                 print_button(dialog, "  OK  ", y, x, !button);
514                 ok_space = 3;
515                 cancel_space = 15;
516             }
517             if (button)
518                 wmove(dialog, y, x + cancel_space);
519             else
520                 wmove(dialog, y, x + ok_space);
521             wrefresh(dialog);
522             break;
523
524         case ' ':
525         case '\n':
526         case '\r':
527             if (!onlist) {
528                 if (ditems) {
529                     if (result && ditems[button ? CANCEL_BUTTON : OK_BUTTON].fire) {
530                         int st;
531                         WINDOW *save = dupwin(newscr);
532
533                         st = ditems[button ? CANCEL_BUTTON : OK_BUTTON].fire(&ditems[button ? CANCEL_BUTTON : OK_BUTTON]);
534                         if (st & DITEM_RESTORE) {
535                             touchwin(save);
536                             wrefresh(save);
537                         }
538                         delwin(save);
539                         if (st == DITEM_FAILURE)
540                         continue;
541                     }
542                 }
543                 else if (result) {
544                     *result = '\0';
545                     for (i = 0; i < item_no; i++) {
546                         if (status[i]) {
547                             strcat(result, items[i*3]);
548                             strcat(result, "\n");
549                         }
550                     }
551                 }
552                 rval = button;
553                 key = ESC;      /* Bail out! */
554                 break;
555             }
556             
557             /* Let me outta here! */
558         case ESC:
559             rval = -1;
560             break;
561             
562             /* Help! */
563         case KEY_F(1):
564         case '?':
565             display_helpfile();
566             break;
567         }
568         
569         if (redraw_menu) {
570             getyx(list, old_y, old_x);
571             wclear(list);
572             for (i = 0; i < max_choice; i++)
573                 print_item(list, items[(scroll + i) * 3], items[(scroll + i) * 3 + 1], status[scroll + i], i, i == choice, DREF(ditems, scroll + i), list_width, item_x, check_x);
574             print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, cur_x, cur_y);
575
576             /* redraw buttons to fix highlighting */
577             if (ditems && result) {
578                 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);
579                 print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : !button);
580             }
581             else {
582                 print_button(dialog, "Cancel", y, x + 14, button);
583                 print_button(dialog, "  OK  ", y, x, !button);
584             }
585             wnoutrefresh(dialog);
586             if (cursor_reset) {
587                 wmove(list, choice, check_x+1);
588                 cursor_reset = FALSE;
589             }
590             else {
591                 wmove(list, old_y, old_x);
592             }
593             wrefresh(list);
594             redraw_menu = FALSE;
595         }
596     }
597     delwin(list);
598     delwin(dialog);
599     return rval;
600 }
601
602
603 /*
604  * Print list item
605  */
606 static void
607 print_item(WINDOW *win, unsigned char *tag, unsigned char *item, int status, int choice, int selected, dialogMenuItem *me, int list_width, int item_x, int check_x)
608 {
609     int i;
610     
611     /* Clear 'residue' of last item */
612     wattrset(win, menubox_attr);
613     wmove(win, choice, 0);
614     for (i = 0; i < list_width; i++)
615         waddch(win, ' ');
616     wmove(win, choice, check_x);
617     wattrset(win, selected ? check_selected_attr : check_attr);
618     wprintw(win, "%c%c%c",  me && me->lbra ? me->lbra : '[',
619             status ? me && me->mark ? me->mark : 'X' : ' ',
620             me && me->rbra ? me->rbra : ']');
621     wattrset(win, menubox_attr);
622     waddch(win, ' ');
623     wattrset(win, selected ? tag_key_selected_attr : tag_key_attr);
624     waddch(win, tag[0]);
625     wattrset(win, selected ? tag_selected_attr : tag_attr);
626     waddstr(win, tag + 1);
627     wmove(win, choice, item_x);
628     wattrset(win, selected ? item_selected_attr : item_attr);
629     waddstr(win, item);
630     /* If have a selection handler for this, call it */
631     if (me && me->selected) {
632         wrefresh(win);
633         me->selected(me, selected);
634     }
635 }
636 /* End of print_item() */