source: bootcd/isolinux/syslinux-6.03/com32/cmenu/libmenu/menu.c @ 26ffad7

Last change on this file since 26ffad7 was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 34.9 KB
Line 
1/* -*- c -*- ------------------------------------------------------------- *
2 *
3 *   Copyright 2004-2005 Murali Krishnan Ganapathy - All Rights Reserved
4 *
5 *   This program is free software; you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 *   Boston MA 02111-1307, USA; either version 2 of the License, or
9 *   (at your option) any later version; incorporated herein by reference.
10 *
11 * ----------------------------------------------------------------------- */
12
13#include "cmenu.h"
14#include "com32io.h"
15#include <stdlib.h>
16#include <console.h>
17
18// Local Variables
19static pt_menusystem ms;    // Pointer to the menusystem
20char TITLESTR[] =
21    "COMBOOT Menu System for SYSLINUX developed by Murali Krishnan Ganapathy";
22char TITLELONG[] = " TITLE too long ";
23char ITEMLONG[] = " ITEM too long ";
24char ACTIONLONG[] = " ACTION too long ";
25char STATUSLONG[] = " STATUS too long ";
26char EMPTYSTR[] = "";
27
28/* Forward declarations */
29int calc_visible(pt_menu menu, int first);
30int next_visible(pt_menu menu, int index);
31int prev_visible(pt_menu menu, int index);
32int next_visible_sep(pt_menu menu, int index);
33int prev_visible_sep(pt_menu menu, int index);
34int calc_first_early(pt_menu menu, int curr);
35int calc_first_late(pt_menu menu, int curr);
36int isvisible(pt_menu menu, int first, int curr);
37
38/* Basic Menu routines */
39
40// This is same as inputc except it honors the ontimeout handler
41// and calls it when needed. For the callee, there is no difference
42// as this will not return unless a key has been pressed.
43static int getch(void)
44{
45    t_timeout_handler th;
46    int key;
47    unsigned long i;
48
49    // Wait until keypress if no handler specified
50    if ((ms->ontimeout == NULL) && (ms->ontotaltimeout == NULL))
51        return get_key(stdin, 0);
52
53    th = ms->ontimeout;
54    for (;;) {
55        for (i = 0; i < ms->tm_numsteps; i++) {
56            key = get_key(stdin, ms->tm_stepsize);
57            if (key != KEY_NONE)
58                return key;
59
60            if ((ms->tm_total_timeout == 0) || (ms->ontotaltimeout == NULL))
61                continue;   // Dont bother with calculations if no handler
62            ms->tm_sofar_timeout += ms->tm_stepsize;
63            if (ms->tm_sofar_timeout >= ms->tm_total_timeout) {
64                th = ms->ontotaltimeout;
65                ms->tm_sofar_timeout = 0;
66                break;      // Get out of the for loop
67            }
68        }
69        if (!th)
70            continue;       // no handler
71        key = th();
72        switch (key) {
73        case CODE_ENTER:    // Pretend user hit enter
74            return KEY_ENTER;
75        case CODE_ESCAPE:   // Pretend user hit escape
76            return KEY_ESC;
77        default:
78            break;
79        }
80    }
81    return KEY_NONE;
82}
83
84int find_shortcut(pt_menu menu, uchar shortcut, int index)
85// Find the next index with specified shortcut key
86{
87    int ans;
88    pt_menuitem mi;
89
90    // Garbage in garbage out
91    if ((index < 0) || (index >= menu->numitems))
92    return index;
93    ans = index + 1;
94    // Go till end of menu
95    while (ans < menu->numitems) {
96    mi = menu->items[ans];
97    if ((mi->action == OPT_INVISIBLE) || (mi->action == OPT_SEP)
98        || (mi->shortcut != shortcut))
99        ans++;
100    else
101        return ans;
102    }
103    // Start at the beginning and try again
104    ans = 0;
105    while (ans < index) {
106    mi = menu->items[ans];
107    if ((mi->action == OPT_INVISIBLE) || (mi->action == OPT_SEP)
108        || (mi->shortcut != shortcut))
109        ans++;
110    else
111        return ans;
112    }
113    return index;       // Sorry not found
114}
115
116/* Redraw background and title */
117static void reset_ui(void)
118{
119    uchar tpos;
120
121    cls();
122    clearwindow(ms->minrow, ms->mincol, ms->maxrow, ms->maxcol,
123                ms->fillchar, ms->fillattr);
124
125    tpos = (ms->numcols - strlen(ms->title) - 1) >> 1;  // center it on line
126    gotoxy(ms->minrow, ms->mincol);
127    cprint(ms->tfillchar, ms->titleattr, ms->numcols);
128    gotoxy(ms->minrow, ms->mincol + tpos);
129    csprint(ms->title, ms->titleattr);
130
131    cursoroff();
132}
133
134/*
135 * Print a menu item
136 *
137 * attr[0] is non-hilite attr, attr[1] is highlight attr
138 */
139void printmenuitem(const char *str, uchar * attr)
140{
141    int hlite = NOHLITE;    // Initially no highlighting
142
143    while (*str) {
144        switch (*str) {
145            case BELL:      // No Bell Char
146                break;
147            case ENABLEHLITE:   // Switch on highlighting
148                hlite = HLITE;
149                break;
150            case DISABLEHLITE:  // Turn off highlighting
151                hlite = NOHLITE;
152                break;
153            default:
154                putch(*str, attr[hlite]);
155        }
156        str++;
157    }
158}
159
160
161/**
162 * print_line - Print a whole line in a menu
163 * @menu:   current menu to handle
164 * @curr:   index of the current entry highlighted
165 * @top:    top coordinate of the @menu
166 * @left:   left coordinate of the @menu
167 * @x:      index in the menu of curr
168 * @row:    row currently displayed
169 * @radio:  radio item?
170 **/
171static void print_line(pt_menu menu, int curr, uchar top, uchar left,
172                       int x, int row, bool radio)
173{
174    pt_menuitem ci;
175    char fchar[6], lchar[6];    // The first and last char in for each entry
176    const char *str;            // Item string (cf printmenuitem)
177    char sep[MENULEN];          // Separator (OPT_SEP)
178    uchar *attr;                // Attribute
179    int menuwidth = menu->menuwidth + 3;
180
181    if (row >= menu->menuheight)
182        return;
183
184    ci = menu->items[x];
185
186    memset(sep, ms->box_horiz, menuwidth);
187    sep[menuwidth - 1] = 0;
188
189    // Setup the defaults now
190    if (radio) {
191        fchar[0] = '\b';
192        fchar[1] = SO;
193        fchar[2] = (x == curr ? RADIOSEL : RADIOUNSEL);
194        fchar[3] = SI;
195        fchar[4] = '\0';    // Unselected ( )
196        lchar[0] = '\0';    // Nothing special after
197        attr = ms->normalattr;  // Always same attribute
198    } else {
199        lchar[0] = fchar[0] = ' ';
200        lchar[1] = fchar[1] = '\0'; // fchar and lchar are just spaces
201        attr = (x == curr ? ms->reverseattr : ms->normalattr);  // Normal attributes
202    }
203    str = ci->item;     // Pointer to item string
204    switch (ci->action) // set up attr,str,fchar,lchar for everything
205    {
206    case OPT_INACTIVE:
207        if (radio)
208            attr = ms->inactattr;
209        else
210            attr = (x == curr ? ms->revinactattr : ms->inactattr);
211        break;
212    case OPT_SUBMENU:
213        if (radio)
214            break;      // Not supported for radio menu
215        lchar[0] = '>';
216        lchar[1] = 0;
217        break;
218    case OPT_RADIOMENU:
219        if (radio)
220            break;      // Not supported for radio menu
221        lchar[0] = RADIOMENUCHAR;
222        lchar[1] = 0;
223        break;
224    case OPT_CHECKBOX:
225        if (radio)
226            break;      // Not supported for radio menu
227        lchar[0] = '\b';
228        lchar[1] = SO;
229        lchar[2] = (ci->itemdata.checked ? CHECKED : UNCHECKED);
230        lchar[3] = SI;
231        lchar[4] = 0;
232        break;
233    case OPT_SEP:
234        fchar[0] = '\b';
235        fchar[1] = SO;
236        fchar[2] = LEFT_MIDDLE_BORDER;
237        fchar[3] = MIDDLE_BORDER;
238        fchar[4] = MIDDLE_BORDER;
239        fchar[5] = 0;
240        memset(sep, MIDDLE_BORDER, menuwidth);
241        sep[menuwidth - 1] = 0;
242        str = sep;
243        lchar[0] = MIDDLE_BORDER;
244        lchar[1] = RIGHT_MIDDLE_BORDER;
245        lchar[2] = SI;
246        lchar[3] = 0;
247        break;
248    case OPT_EXITMENU:
249        if (radio)
250            break;      // Not supported for radio menu
251        fchar[0] = '<';
252        fchar[1] = 0;
253        break;
254    default:        // Just to keep the compiler happy
255        break;
256    }
257
258    // Wipe area with spaces
259    gotoxy(top + row, left - 2);
260    cprint(ms->spacechar, attr[NOHLITE], menuwidth + 2);
261
262    // Print first part
263    gotoxy(top + row, left - 2);
264    csprint(fchar, attr[NOHLITE]);
265
266    // Print main part
267    gotoxy(top + row, left);
268    printmenuitem(str, attr);
269
270    // Print last part
271    gotoxy(top + row, left + menuwidth - 1);
272    csprint(lchar, attr[NOHLITE]);
273}
274
275// print the menu starting from FIRST
276// will print a maximum of menu->menuheight items
277static void printmenu(pt_menu menu, int curr, uchar top, uchar left, uchar first, bool radio)
278{
279    int x, row;         // x = index, row = position from top
280    int numitems, menuwidth;
281    pt_menuitem ci;
282
283    numitems = calc_visible(menu, first);
284    if (numitems > menu->menuheight)
285    numitems = menu->menuheight;
286
287    menuwidth = menu->menuwidth + 3;
288    clearwindow(top, left - 2, top + numitems + 1, left + menuwidth + 1,
289        ms->fillchar, ms->shadowattr);
290    drawbox(top - 1, left - 3, top + numitems, left + menuwidth,
291        ms->normalattr[NOHLITE]);
292
293    // Menu title
294    x = (menuwidth - strlen(menu->title) - 1) >> 1;
295    gotoxy(top - 1, left + x);
296    printmenuitem(menu->title, ms->normalattr);
297
298    // All lines in the menu
299    row = -1;           // 1 less than inital value of x
300    for (x = first; x < menu->numitems; x++) {
301        ci = menu->items[x];
302        if (ci->action == OPT_INVISIBLE)
303            continue;
304        row++;
305        if (row >= numitems)
306            break;      // Already have enough number of items
307        print_line(menu, curr, top, left, x, row, radio);
308    }
309    // Check if we need to MOREABOVE and MOREBELOW to be added
310    // reuse x
311    row = 0;
312    x = next_visible_sep(menu, 0);  // First item
313    if (!isvisible(menu, first, x)) // There is more above
314    {
315    row = 1;
316    gotoxy(top, left + menuwidth);
317    cprint(MOREABOVE, ms->normalattr[NOHLITE], 1);
318    }
319    x = prev_visible_sep(menu, menu->numitems); // last item
320    if (!isvisible(menu, first, x)) // There is more above
321    {
322    row = 1;
323    gotoxy(top + numitems - 1, left + menuwidth);
324    cprint(MOREBELOW, ms->normalattr[NOHLITE], 1);
325    }
326    // Add a scroll box
327    x = ((numitems - 1) * curr) / (menu->numitems);
328    if ((x > 0) && (row == 1)) {
329    gotoxy(top + x, left + menuwidth);
330    csprint("\016\141\017", ms->normalattr[NOHLITE]);
331    }
332    if (ms->handler)
333    ms->handler(ms, menu->items[curr]);
334}
335
336void cleanupmenu(pt_menu menu, uchar top, uchar left, int numitems)
337{
338    if (numitems > menu->menuheight)
339    numitems = menu->menuheight;
340    clearwindow(top, left - 2, top + numitems + 1, left + menu->menuwidth + 4, ms->fillchar, ms->fillattr); // Clear the shadow
341    clearwindow(top - 1, left - 3, top + numitems, left + menu->menuwidth + 3, ms->fillchar, ms->fillattr); // main window
342}
343
344
345/* Handle one menu */
346static pt_menuitem getmenuoption(pt_menu menu, uchar top, uchar left, uchar startopt, bool radio)
347     // Return item chosen or NULL if ESC was hit.
348{
349    int prev, prev_first, curr, i, first, tmp;
350    int asc = 0;
351    bool redraw = true; // Need to draw the menu the first time
352    uchar numitems;
353    pt_menuitem ci;     // Current item
354    t_handler_return hr;    // Return value of handler
355
356    numitems = calc_visible(menu, 0);
357    // Setup status line
358    gotoxy(ms->minrow + ms->statline, ms->mincol);
359    cprint(ms->spacechar, ms->statusattr[NOHLITE], ms->numcols);
360
361    // Initialise current menu item
362    curr = next_visible(menu, startopt);
363    prev = curr;
364
365    gotoxy(ms->minrow + ms->statline, ms->mincol);
366    cprint(ms->spacechar, ms->statusattr[NOHLITE], ms->numcols);
367    gotoxy(ms->minrow + ms->statline, ms->mincol);
368    printmenuitem(menu->items[curr]->status, ms->statusattr);
369    first = calc_first_early(menu, curr);
370    prev_first = first;
371    while (1)           // Forever
372    {
373    /* Redraw everything if:
374     *  + we need to scroll (take care of scroll bars, ...)
375     *  + menuoption
376     */
377    if (prev_first != first || redraw) {
378        printmenu(menu, curr, top, left, first, radio);
379    } else {
380        /* Redraw only the highlighted entry */
381        print_line(menu, curr, top, left, prev, prev - first, radio);
382        print_line(menu, curr, top, left, curr, curr - first, radio);
383    }
384    redraw = false;
385    prev = curr;
386    prev_first = first;
387    ci = menu->items[curr];
388    asc = getch();
389    switch (asc) {
390        case KEY_CTRL('L'):
391        redraw = true;
392        break;
393    case KEY_HOME:
394        curr = next_visible(menu, 0);
395        first = calc_first_early(menu, curr);
396        break;
397    case KEY_END:
398        curr = prev_visible(menu, numitems - 1);
399        first = calc_first_late(menu, curr);
400        break;
401    case KEY_PGDN:
402        for (i = 0; i < 5; i++)
403        curr = next_visible(menu, curr + 1);
404        first = calc_first_late(menu, curr);
405        break;
406    case KEY_PGUP:
407        for (i = 0; i < 5; i++)
408        curr = prev_visible(menu, curr - 1);
409        first = calc_first_early(menu, curr);
410        break;
411    case KEY_UP:
412        curr = prev_visible(menu, curr - 1);
413        if (curr < first)
414        first = calc_first_early(menu, curr);
415        break;
416    case KEY_DOWN:
417        curr = next_visible(menu, curr + 1);
418        if (!isvisible(menu, first, curr))
419        first = calc_first_late(menu, curr);
420        break;
421    case KEY_LEFT:
422    case KEY_ESC:
423        return NULL;
424        break;
425    case KEY_RIGHT:
426    case KEY_ENTER:
427        if (ci->action == OPT_INACTIVE)
428        break;
429        if (ci->action == OPT_CHECKBOX)
430        break;
431        if (ci->action == OPT_SEP)
432        break;
433        if (ci->action == OPT_EXITMENU)
434        return NULL;    // As if we hit Esc
435        // If we are going into a radio menu, dont call handler, return ci
436        if (ci->action == OPT_RADIOMENU)
437        return ci;
438        if (ci->handler != NULL)    // Do we have a handler
439        {
440        hr = ci->handler(ms, ci);
441        if (hr.refresh) // Do we need to refresh
442        {
443            // Cleanup menu using old number of items
444            cleanupmenu(menu, top, left, numitems);
445            // Recalculate the number of items
446            numitems = calc_visible(menu, 0);
447            // Reprint the menu
448            printmenu(menu, curr, top, left, first, radio);
449        }
450        if (hr.valid)
451            return ci;
452        } else
453        return ci;
454        break;
455    case SPACECHAR:
456        if (ci->action != OPT_CHECKBOX)
457        break;
458        ci->itemdata.checked = !ci->itemdata.checked;
459        if (ci->handler != NULL)    // Do we have a handler
460        {
461        hr = ci->handler(ms, ci);
462        if (hr.refresh) // Do we need to refresh
463        {
464            // Cleanup menu using old number of items
465            cleanupmenu(menu, top, left, numitems);
466            // Recalculate the number of items
467            numitems = calc_visible(menu, 0);
468            // Reprint the menu
469            printmenu(menu, curr, top, left, first, radio);
470        }
471        }
472        break;
473    default:
474        // Check if this is a shortcut key
475        if (((asc >= 'A') && (asc <= 'Z')) ||
476        ((asc >= 'a') && (asc <= 'z')) ||
477        ((asc >= '0') && (asc <= '9'))) {
478        tmp = find_shortcut(menu, asc, curr);
479        if ((tmp > curr) && (!isvisible(menu, first, tmp)))
480            first = calc_first_late(menu, tmp);
481        if (tmp < curr)
482            first = calc_first_early(menu, tmp);
483        curr = tmp;
484        } else {
485        if (ms->keys_handler)   // Call extra keys handler
486            ms->keys_handler(ms, menu->items[curr], asc);
487
488            /* The handler may have changed the UI, reset it on exit */
489            reset_ui();
490            // Cleanup menu using old number of items
491            cleanupmenu(menu, top, left, numitems);
492            // Recalculate the number of items
493            numitems = calc_visible(menu, 0);
494            // Reprint the menu
495            printmenu(menu, curr, top, left, first, radio);
496        }
497        break;
498    }
499    // Update status line
500    /* Erase the previous status */
501    gotoxy(ms->minrow + ms->statline, ms->mincol);
502    cprint(ms->spacechar, ms->statusattr[NOHLITE], ms->numcols);
503    /* Print the new status */
504    gotoxy(ms->minrow + ms->statline, ms->mincol);
505    printmenuitem(menu->items[curr]->status, ms->statusattr);
506    }
507    return NULL;        // Should never come here
508}
509
510/* Handle the entire system of menu's. */
511pt_menuitem runmenusystem(uchar top, uchar left, pt_menu cmenu, uchar startopt,
512              uchar menutype)
513     /*
514      * cmenu
515      *    Which menu should be currently displayed
516      * top,left
517      *    What is the position of the top,left corner of the menu
518      * startopt
519      *    which menu item do I start with
520      * menutype
521      *    NORMALMENU or RADIOMENU
522      *
523      * Return Value:
524      *    Returns a pointer to the final item chosen, or NULL if nothing chosen.
525      */
526{
527    pt_menuitem opt, choice;
528    uchar startat, mt;
529    uchar row, col;
530
531    if (cmenu == NULL)
532    return NULL;
533
534startover:
535    // Set the menu height
536    cmenu->menuheight = ms->maxrow - top - 3;
537    if (cmenu->menuheight > ms->maxmenuheight)
538    cmenu->menuheight = ms->maxmenuheight;
539    if (menutype == NORMALMENU)
540    opt = getmenuoption(cmenu, top, left, startopt, false);
541    else            // menutype == RADIOMENU
542    opt = getmenuoption(cmenu, top, left, startopt, true);
543
544    if (opt == NULL) {
545    // User hit Esc
546    cleanupmenu(cmenu, top, left, calc_visible(cmenu, 0));
547    return NULL;
548    }
549    // Are we done with the menu system?
550    if ((opt->action != OPT_SUBMENU) && (opt->action != OPT_RADIOMENU)) {
551    cleanupmenu(cmenu, top, left, calc_visible(cmenu, 0));
552    return opt;     // parent cleanup other menus
553    }
554    // Either radiomenu or submenu
555    // Do we have a valid menu number? The next hack uses the fact that
556    // itemdata.submenunum = itemdata.radiomenunum (since enum data type)
557    if (opt->itemdata.submenunum >= ms->nummenus)   // This is Bad....
558    {
559    gotoxy(12, 12); // Middle of screen
560    csprint("ERROR: Invalid submenu requested.", 0x07);
561    cleanupmenu(cmenu, top, left, calc_visible(cmenu, 0));
562    return NULL;        // Pretend user hit esc
563    }
564    // Call recursively for submenu
565    // Position the submenu below the current item,
566    // covering half the current window (horizontally)
567    row = ms->menus[(unsigned int)opt->itemdata.submenunum]->row;
568    col = ms->menus[(unsigned int)opt->itemdata.submenunum]->col;
569    if (row == 0xFF)
570    row = top + opt->index + 2;
571    if (col == 0xFF)
572    col = left + 3 + (cmenu->menuwidth >> 1);
573    mt = (opt->action == OPT_SUBMENU ? NORMALMENU : RADIOMENU);
574    startat = 0;
575    if ((opt->action == OPT_RADIOMENU) && (opt->data != NULL))
576    startat = ((t_menuitem *) opt->data)->index;
577
578    choice = runmenusystem(row, col,
579               ms->menus[(unsigned int)opt->itemdata.submenunum],
580               startat, mt);
581    if (opt->action == OPT_RADIOMENU) {
582    if (choice != NULL)
583        opt->data = (void *)choice; // store choice in data field
584    if (opt->handler != NULL)
585        opt->handler(ms, opt);
586    choice = NULL;      // Pretend user hit esc
587    }
588    if (choice == NULL)     // User hit Esc in submenu
589    {
590    // Startover
591    startopt = opt->index;
592    goto startover;
593    } else {
594    cleanupmenu(cmenu, top, left, calc_visible(cmenu, 0));
595    return choice;
596    }
597}
598
599// Finds the indexof the menu with given name
600uchar find_menu_num(const char *name)
601{
602    int i;
603    pt_menu m;
604
605    if (name == NULL)
606    return (uchar) (-1);
607    for (i = 0; i < ms->nummenus; i++) {
608    m = ms->menus[i];
609    if ((m->name) && (strcmp(m->name, name) == 0))
610        return i;
611    }
612    return (uchar) (-1);
613}
614
615// Run through all items and if they are submenus
616// with a non-trivial "action" and trivial submenunum
617// replace submenunum with the menu with name "action"
618void fix_submenus(void)
619{
620    int i, j;
621    pt_menu m;
622    pt_menuitem mi;
623
624    i = 0;
625    for (i = 0; i < ms->nummenus; i++) {
626    m = ms->menus[i];
627    for (j = 0; j < m->numitems; j++) {
628        mi = m->items[j];
629        // if item is a submenu and has non-empty non-trivial data string
630        if (mi->data && strlen(mi->data) > 0 &&
631        ((mi->action == OPT_SUBMENU)
632         || (mi->action == OPT_RADIOMENU))) {
633        mi->itemdata.submenunum = find_menu_num(mi->data);
634        }
635    }
636    }
637}
638
639/* User Callable functions */
640
641pt_menuitem showmenus(uchar startmenu)
642{
643    pt_menuitem rv;
644
645    fix_submenus();     // Fix submenu numbers incase nick names were used
646
647    /* Turn autowrap off, to avoid scrolling the menu */
648    printf(CSI "?7l");
649
650    // Setup screen for menusystem
651    reset_ui();
652
653    // Go, main menu cannot be a radio menu
654    rv = runmenusystem(ms->minrow + MENUROW, ms->mincol + MENUCOL,
655               ms->menus[(unsigned int)startmenu], 0, NORMALMENU);
656
657    // Hide the garbage we left on the screen
658    cls();
659    gotoxy(ms->minrow, ms->mincol);
660    cursoron();
661
662    // Return user choice
663    return rv;
664}
665
666pt_menusystem init_menusystem(const char *title)
667{
668    int i;
669
670    ms = NULL;
671    ms = (pt_menusystem) malloc(sizeof(t_menusystem));
672    if (ms == NULL)
673    return NULL;
674    ms->nummenus = 0;
675    // Initialise all menu pointers
676    for (i = 0; i < MAXMENUS; i++)
677    ms->menus[i] = NULL;
678
679    ms->title = (char *)malloc(TITLELEN + 1);
680    if (title == NULL)
681    strcpy(ms->title, TITLESTR);    // Copy string
682    else
683    strcpy(ms->title, title);
684
685    // Timeout settings
686    ms->tm_stepsize = TIMEOUTSTEPSIZE;
687    ms->tm_numsteps = TIMEOUTNUMSTEPS;
688
689    ms->normalattr[NOHLITE] = NORMALATTR;
690    ms->normalattr[HLITE] = NORMALHLITE;
691
692    ms->reverseattr[NOHLITE] = REVERSEATTR;
693    ms->reverseattr[HLITE] = REVERSEHLITE;
694
695    ms->inactattr[NOHLITE] = INACTATTR;
696    ms->inactattr[HLITE] = INACTHLITE;
697
698    ms->revinactattr[NOHLITE] = REVINACTATTR;
699    ms->revinactattr[HLITE] = REVINACTHLITE;
700
701    ms->statusattr[NOHLITE] = STATUSATTR;
702    ms->statusattr[HLITE] = STATUSHLITE;
703
704    ms->statline = STATLINE;
705    ms->tfillchar = TFILLCHAR;
706    ms->titleattr = TITLEATTR;
707
708    ms->fillchar = FILLCHAR;
709    ms->fillattr = FILLATTR;
710    ms->spacechar = SPACECHAR;
711    ms->shadowattr = SHADOWATTR;
712
713    ms->menupage = MENUPAGE;    // Usually no need to change this at all
714
715    // Initialise all handlers
716    ms->handler = NULL;
717    ms->keys_handler = NULL;
718    ms->ontimeout = NULL;   // No timeout handler
719    ms->tm_total_timeout = 0;
720    ms->tm_sofar_timeout = 0;
721    ms->ontotaltimeout = NULL;
722
723    // Setup ACTION_{,IN}VALID
724    ACTION_VALID.valid = 1;
725    ACTION_VALID.refresh = 0;
726    ACTION_INVALID.valid = 0;
727    ACTION_INVALID.refresh = 0;
728
729    // Figure out the size of the screen we are in now.
730    // By default we use the whole screen for our menu
731    if (getscreensize(1, &ms->numrows, &ms->numcols)) {
732        /* Unknown screen size? */
733        ms->numcols = 80;
734        ms->numrows = 24;
735    }
736    ms->minrow = ms->mincol = 0;
737    ms->maxcol = ms->numcols - 1;
738    ms->maxrow = ms->numrows - 1;
739
740    // How many entries per menu can we display at a time
741    ms->maxmenuheight = ms->maxrow - ms->minrow - 3;
742    if (ms->maxmenuheight > MAXMENUHEIGHT)
743    ms->maxmenuheight = MAXMENUHEIGHT;
744
745    console_ansi_raw();
746
747    return ms;
748}
749
750void set_normal_attr(uchar normal, uchar selected, uchar inactivenormal,
751             uchar inactiveselected)
752{
753    if (normal != 0xFF)
754    ms->normalattr[0] = normal;
755    if (selected != 0xFF)
756    ms->reverseattr[0] = selected;
757    if (inactivenormal != 0xFF)
758    ms->inactattr[0] = inactivenormal;
759    if (inactiveselected != 0xFF)
760    ms->revinactattr[0] = inactiveselected;
761}
762
763void set_normal_hlite(uchar normal, uchar selected, uchar inactivenormal,
764              uchar inactiveselected)
765{
766    if (normal != 0xFF)
767    ms->normalattr[1] = normal;
768    if (selected != 0xFF)
769    ms->reverseattr[1] = selected;
770    if (inactivenormal != 0xFF)
771    ms->inactattr[1] = inactivenormal;
772    if (inactiveselected != 0xFF)
773    ms->revinactattr[1] = inactiveselected;
774}
775
776void set_status_info(uchar statusattr, uchar statushlite, uchar statline)
777{
778    if (statusattr != 0xFF)
779    ms->statusattr[NOHLITE] = statusattr;
780    if (statushlite != 0xFF)
781    ms->statusattr[HLITE] = statushlite;
782    // statline is relative to minrow
783    if (statline >= ms->numrows)
784    statline = ms->numrows - 1;
785    ms->statline = statline;    // relative to ms->minrow, 0 based
786}
787
788void set_title_info(uchar tfillchar, uchar titleattr)
789{
790    if (tfillchar != 0xFF)
791    ms->tfillchar = tfillchar;
792    if (titleattr != 0xFF)
793    ms->titleattr = titleattr;
794}
795
796void set_misc_info(uchar fillchar, uchar fillattr, uchar spacechar,
797           uchar shadowattr)
798{
799    if (fillchar != 0xFF)
800    ms->fillchar = fillchar;
801    if (fillattr != 0xFF)
802    ms->fillattr = fillattr;
803    if (spacechar != 0xFF)
804    ms->spacechar = spacechar;
805    if (shadowattr != 0xFF)
806    ms->shadowattr = shadowattr;
807}
808
809void set_menu_options(uchar maxmenuheight)
810{
811    if (maxmenuheight != 0xFF)
812    ms->maxmenuheight = maxmenuheight;
813}
814
815// Set the window which menusystem should use
816void set_window_size(uchar top, uchar left, uchar bot, uchar right)
817{
818    int nr, nc;
819
820    if ((top > bot) || (left > right))
821    return;         // Sorry no change will happen here
822
823    if (getscreensize(1, &nr, &nc)) {
824        /* Unknown screen size? */
825        nr = 80;
826        nc = 24;
827    }
828    if (bot >= nr)
829    bot = nr - 1;
830    if (right >= nc)
831    right = nc - 1;
832    ms->minrow = top;
833    ms->mincol = left;
834    ms->maxrow = bot;
835    ms->maxcol = right;
836    ms->numcols = right - left + 1;
837    ms->numrows = bot - top + 1;
838    if (ms->statline >= ms->numrows)
839    ms->statline = ms->numrows - 1; // Clip statline if need be
840}
841
842void reg_handler(t_handler htype, void *handler)
843{
844    // If bad value set to default screen handler
845    switch (htype) {
846    case HDLR_KEYS:
847    ms->keys_handler = (t_keys_handler) handler;
848    break;
849    default:
850    ms->handler = (t_menusystem_handler) handler;
851    break;
852    }
853}
854
855void unreg_handler(t_handler htype)
856{
857    switch (htype) {
858    case HDLR_KEYS:
859    ms->keys_handler = NULL;
860    break;
861    default:
862    ms->handler = NULL;
863    break;
864    }
865}
866
867void reg_ontimeout(t_timeout_handler handler, unsigned int numsteps,
868           unsigned int stepsize)
869{
870    ms->ontimeout = handler;
871    if (numsteps != 0)
872    ms->tm_numsteps = numsteps;
873    if (stepsize != 0)
874    ms->tm_stepsize = stepsize;
875}
876
877void unreg_ontimeout(void)
878{
879    ms->ontimeout = NULL;
880}
881
882void reg_ontotaltimeout(t_timeout_handler handler,
883            unsigned long numcentiseconds)
884{
885    if (numcentiseconds != 0) {
886    ms->ontotaltimeout = handler;
887    ms->tm_total_timeout = numcentiseconds * 10;    // to convert to milliseconds
888    ms->tm_sofar_timeout = 0;
889    }
890}
891
892void unreg_ontotaltimeout(void)
893{
894    ms->ontotaltimeout = NULL;
895}
896
897int next_visible(pt_menu menu, int index)
898{
899    int ans;
900    if (index < 0)
901    ans = 0;
902    else if (index >= menu->numitems)
903    ans = menu->numitems - 1;
904    else
905    ans = index;
906    while ((ans < menu->numitems - 1) &&
907       ((menu->items[ans]->action == OPT_INVISIBLE) ||
908        (menu->items[ans]->action == OPT_SEP)))
909    ans++;
910    return ans;
911}
912
913int prev_visible(pt_menu menu, int index)   // Return index of prev visible
914{
915    int ans;
916    if (index < 0)
917    ans = 0;
918    else if (index >= menu->numitems)
919    ans = menu->numitems - 1;
920    else
921    ans = index;
922    while ((ans > 0) &&
923       ((menu->items[ans]->action == OPT_INVISIBLE) ||
924        (menu->items[ans]->action == OPT_SEP)))
925    ans--;
926    return ans;
927}
928
929int next_visible_sep(pt_menu menu, int index)
930{
931    int ans;
932    if (index < 0)
933    ans = 0;
934    else if (index >= menu->numitems)
935    ans = menu->numitems - 1;
936    else
937    ans = index;
938    while ((ans < menu->numitems - 1) &&
939       (menu->items[ans]->action == OPT_INVISIBLE))
940    ans++;
941    return ans;
942}
943
944int prev_visible_sep(pt_menu menu, int index)   // Return index of prev visible
945{
946    int ans;
947    if (index < 0)
948    ans = 0;
949    else if (index >= menu->numitems)
950    ans = menu->numitems - 1;
951    else
952    ans = index;
953    while ((ans > 0) && (menu->items[ans]->action == OPT_INVISIBLE))
954    ans--;
955    return ans;
956}
957
958int calc_visible(pt_menu menu, int first)
959{
960    int ans, i;
961
962    if (menu == NULL)
963    return 0;
964    ans = 0;
965    for (i = first; i < menu->numitems; i++)
966    if (menu->items[i]->action != OPT_INVISIBLE)
967        ans++;
968    return ans;
969}
970
971// is curr visible if first entry is first?
972int isvisible(pt_menu menu, int first, int curr)
973{
974    if (curr < first)
975    return 0;
976    return (calc_visible(menu, first) - calc_visible(menu, curr) <
977        menu->menuheight);
978}
979
980// Calculate the first entry to be displayed
981// so that curr is visible and make curr as late as possible
982int calc_first_late(pt_menu menu, int curr)
983{
984    int ans, i, nv;
985
986    nv = calc_visible(menu, 0);
987    if (nv <= menu->menuheight)
988    return 0;
989    // Start with curr and go back menu->menuheight times
990    ans = curr + 1;
991    for (i = 0; i < menu->menuheight; i++)
992    ans = prev_visible_sep(menu, ans - 1);
993    return ans;
994}
995
996// Calculate the first entry to be displayed
997// so that curr is visible and make curr as early as possible
998int calc_first_early(pt_menu menu, int curr)
999{
1000    int ans, i, nv;
1001
1002    nv = calc_visible(menu, 0);
1003    if (nv <= menu->menuheight)
1004    return 0;
1005    // Start with curr and go back till >= menu->menuheight
1006    // items are visible
1007    nv = calc_visible(menu, curr);  // Already nv of them are visible
1008    ans = curr;
1009    for (i = 0; i < menu->menuheight - nv; i++)
1010    ans = prev_visible_sep(menu, ans - 1);
1011    return ans;
1012}
1013
1014// Create a new menu and return its position
1015uchar add_menu(const char *title, int maxmenusize)
1016{
1017    int num, i;
1018    pt_menu m;
1019
1020    num = ms->nummenus;
1021    if (num >= MAXMENUS)
1022    return -1;
1023    m = NULL;
1024    m = (pt_menu) malloc(sizeof(t_menu));
1025    if (m == NULL)
1026    return -1;
1027    ms->menus[num] = m;
1028    m->numitems = 0;
1029    m->name = NULL;
1030    m->row = 0xFF;
1031    m->col = 0xFF;
1032    if (maxmenusize < 1)
1033    m->maxmenusize = MAXMENUSIZE;
1034    else
1035    m->maxmenusize = maxmenusize;
1036    m->items = (pt_menuitem *) malloc(sizeof(pt_menuitem) * (m->maxmenusize));
1037    for (i = 0; i < m->maxmenusize; i++)
1038    m->items[i] = NULL;
1039
1040    m->title = (char *)malloc(MENULEN + 1);
1041    if (title) {
1042    if (strlen(title) > MENULEN - 2)
1043        strcpy(m->title, TITLELONG);
1044    else
1045        strcpy(m->title, title);
1046    } else
1047    strcpy(m->title, EMPTYSTR);
1048    m->menuwidth = strlen(m->title);
1049    ms->nummenus++;
1050    return ms->nummenus - 1;
1051}
1052
1053void set_menu_name(const char *name)    // Set the "name" of this menu
1054{
1055    pt_menu m;
1056
1057    m = ms->menus[ms->nummenus - 1];
1058    if (m->name)        // Free up previous name
1059    {
1060    free(m->name);
1061    m->name = NULL;
1062    }
1063
1064    if (name) {
1065    m->name = (char *)malloc(strlen(name) + 1);
1066    strcpy(m->name, name);
1067    }
1068}
1069
1070// Create a new named menu and return its position
1071uchar add_named_menu(const char *name, const char *title, int maxmenusize)
1072{
1073    add_menu(title, maxmenusize);
1074    set_menu_name(name);
1075    return ms->nummenus - 1;
1076}
1077
1078void set_menu_pos(uchar row, uchar col) // Set the position of this menu.
1079{
1080    pt_menu m;
1081
1082    m = ms->menus[ms->nummenus - 1];
1083    m->row = row;
1084    m->col = col;
1085}
1086
1087pt_menuitem add_sep(void)       // Add a separator to current menu
1088{
1089    pt_menuitem mi;
1090    pt_menu m;
1091
1092    m = (ms->menus[ms->nummenus - 1]);
1093    mi = NULL;
1094    mi = (pt_menuitem) malloc(sizeof(t_menuitem));
1095    if (mi == NULL)
1096    return NULL;
1097    m->items[(unsigned int)m->numitems] = mi;
1098    mi->handler = NULL;     // No handler
1099    mi->item = mi->status = mi->data = NULL;
1100    mi->action = OPT_SEP;
1101    mi->index = m->numitems++;
1102    mi->parindex = ms->nummenus - 1;
1103    mi->shortcut = 0;
1104    mi->helpid = 0;
1105    return mi;
1106}
1107
1108// Add item to the "current" menu
1109pt_menuitem add_item(const char *item, const char *status, t_action action,
1110             const char *data, uchar itemdata)
1111{
1112    pt_menuitem mi;
1113    pt_menu m;
1114    const char *str;
1115    uchar inhlite = 0;      // Are we inside hlite area
1116
1117    m = (ms->menus[ms->nummenus - 1]);
1118    mi = NULL;
1119    mi = (pt_menuitem) malloc(sizeof(t_menuitem));
1120    if (mi == NULL)
1121    return NULL;
1122    m->items[(unsigned int)m->numitems] = mi;
1123    mi->handler = NULL;     // No handler
1124
1125    // Allocate space to store stuff
1126    mi->item = (char *)malloc(MENULEN + 1);
1127    mi->status = (char *)malloc(STATLEN + 1);
1128    mi->data = (char *)malloc(ACTIONLEN + 1);
1129
1130    if (item) {
1131    if (strlen(item) > MENULEN) {
1132        strcpy(mi->item, ITEMLONG);
1133    } else {
1134        strcpy(mi->item, item);
1135    }
1136    if (strlen(mi->item) > m->menuwidth)
1137        m->menuwidth = strlen(mi->item);
1138    } else
1139    strcpy(mi->item, EMPTYSTR);
1140
1141    if (status) {
1142    if (strlen(status) > STATLEN) {
1143        strcpy(mi->status, STATUSLONG);
1144    } else {
1145        strcpy(mi->status, status);
1146    }
1147    } else
1148    strcpy(mi->status, EMPTYSTR);
1149
1150    mi->action = action;
1151    str = mi->item;
1152    mi->shortcut = 0;
1153    mi->helpid = 0xFFFF;
1154    inhlite = 0;        // We have not yet seen an ENABLEHLITE char
1155    // Find the first char in [A-Za-z0-9] after ENABLEHLITE and not arg to control char
1156    while (*str) {
1157    if (*str == ENABLEHLITE) {
1158        inhlite = 1;
1159    }
1160    if (*str == DISABLEHLITE) {
1161        inhlite = 0;
1162    }
1163    if ((inhlite == 1) &&
1164        (((*str >= 'A') && (*str <= 'Z')) ||
1165         ((*str >= 'a') && (*str <= 'z')) ||
1166         ((*str >= '0') && (*str <= '9')))) {
1167        mi->shortcut = *str;
1168        break;
1169    }
1170    ++str;
1171    }
1172    if ((mi->shortcut >= 'A') && (mi->shortcut <= 'Z')) // Make lower case
1173    mi->shortcut = mi->shortcut - 'A' + 'a';
1174
1175    if (data) {
1176    if (strlen(data) > ACTIONLEN) {
1177        strcpy(mi->data, ACTIONLONG);
1178    } else {
1179        strcpy(mi->data, data);
1180    }
1181    } else
1182    strcpy(mi->data, EMPTYSTR);
1183
1184    switch (action) {
1185    case OPT_SUBMENU:
1186    mi->itemdata.submenunum = itemdata;
1187    break;
1188    case OPT_CHECKBOX:
1189    mi->itemdata.checked = itemdata;
1190    break;
1191    case OPT_RADIOMENU:
1192    mi->itemdata.radiomenunum = itemdata;
1193    if (mi->data)
1194        free(mi->data);
1195    mi->data = NULL;    // No selection made
1196    break;
1197    default:            // to keep the compiler happy
1198    break;
1199    }
1200    mi->index = m->numitems++;
1201    mi->parindex = ms->nummenus - 1;
1202    return mi;
1203}
1204
1205// Set the shortcut key for the current item
1206void set_item_options(uchar shortcut, int helpid)
1207{
1208    pt_menuitem mi;
1209    pt_menu m;
1210
1211    m = (ms->menus[ms->nummenus - 1]);
1212    if (m->numitems <= 0)
1213    return;
1214    mi = m->items[(unsigned int)m->numitems - 1];
1215
1216    if (shortcut != 0xFF)
1217    mi->shortcut = shortcut;
1218    if (helpid != 0xFFFF)
1219    mi->helpid = helpid;
1220}
1221
1222// Free internal datasutructures
1223void close_menusystem(void)
1224{
1225}
1226
1227// append_line_helper(pt_menu menu,char *line)
1228void append_line_helper(int menunum, char *line)
1229{
1230    pt_menu menu;
1231    pt_menuitem mi, ri;
1232    char *app;
1233    int ctr;
1234
1235    menu = ms->menus[menunum];
1236    for (ctr = 0; ctr < (int)menu->numitems; ctr++) {
1237    mi = menu->items[ctr];
1238    app = NULL;     //What to append
1239    switch (mi->action) {
1240    case OPT_CHECKBOX:
1241        if (mi->itemdata.checked)
1242        app = mi->data;
1243        break;
1244    case OPT_RADIOMENU:
1245        if (mi->data) { // Some selection has been made
1246        ri = (pt_menuitem) (mi->data);
1247        app = ri->data;
1248        }
1249        break;
1250    case OPT_SUBMENU:
1251        append_line_helper(mi->itemdata.submenunum, line);
1252        break;
1253    default:
1254        break;
1255    }
1256    if (app) {
1257        strcat(line, " ");
1258        strcat(line, app);
1259    }
1260    }
1261}
1262
1263// Generate string based on state of checkboxes and radioitem in given menu
1264// Assume line points to large enough buffer
1265void gen_append_line(const char *menu_name, char *line)
1266{
1267    int menunum;
1268
1269    menunum = find_menu_num(menu_name);
1270    if (menunum < 0)
1271    return;         // No such menu
1272    append_line_helper(menunum, line);
1273}
Note: See TracBrowser for help on using the repository browser.