summaryrefslogtreecommitdiffstats
path: root/contrib/syslinux-4.02/com32/cmenu/libmenu/menu.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/syslinux-4.02/com32/cmenu/libmenu/menu.c')
-rw-r--r--contrib/syslinux-4.02/com32/cmenu/libmenu/menu.c1273
1 files changed, 1273 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/com32/cmenu/libmenu/menu.c b/contrib/syslinux-4.02/com32/cmenu/libmenu/menu.c
new file mode 100644
index 0000000..1375476
--- /dev/null
+++ b/contrib/syslinux-4.02/com32/cmenu/libmenu/menu.c
@@ -0,0 +1,1273 @@
+/* -*- c -*- ------------------------------------------------------------- *
+ *
+ * Copyright 2004-2005 Murali Krishnan Ganapathy - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include "menu.h"
+#include "com32io.h"
+#include <stdlib.h>
+#include <console.h>
+
+// Local Variables
+static pt_menusystem ms; // Pointer to the menusystem
+char TITLESTR[] =
+ "COMBOOT Menu System for SYSLINUX developed by Murali Krishnan Ganapathy";
+char TITLELONG[] = " TITLE too long ";
+char ITEMLONG[] = " ITEM too long ";
+char ACTIONLONG[] = " ACTION too long ";
+char STATUSLONG[] = " STATUS too long ";
+char EMPTYSTR[] = "";
+
+/* Forward declarations */
+int calc_visible(pt_menu menu, int first);
+int next_visible(pt_menu menu, int index);
+int prev_visible(pt_menu menu, int index);
+int next_visible_sep(pt_menu menu, int index);
+int prev_visible_sep(pt_menu menu, int index);
+int calc_first_early(pt_menu menu, int curr);
+int calc_first_late(pt_menu menu, int curr);
+int isvisible(pt_menu menu, int first, int curr);
+
+/* Basic Menu routines */
+
+// This is same as inputc except it honors the ontimeout handler
+// and calls it when needed. For the callee, there is no difference
+// as this will not return unless a key has been pressed.
+static int getch(void)
+{
+ t_timeout_handler th;
+ int key;
+ unsigned long i;
+
+ // Wait until keypress if no handler specified
+ if ((ms->ontimeout == NULL) && (ms->ontotaltimeout == NULL))
+ return get_key(stdin, 0);
+
+ th = ms->ontimeout;
+ for (;;) {
+ for (i = 0; i < ms->tm_numsteps; i++) {
+ key = get_key(stdin, ms->tm_stepsize);
+ if (key != KEY_NONE)
+ return key;
+
+ if ((ms->tm_total_timeout == 0) || (ms->ontotaltimeout == NULL))
+ continue; // Dont bother with calculations if no handler
+ ms->tm_sofar_timeout += ms->tm_stepsize;
+ if (ms->tm_sofar_timeout >= ms->tm_total_timeout) {
+ th = ms->ontotaltimeout;
+ ms->tm_sofar_timeout = 0;
+ break; // Get out of the for loop
+ }
+ }
+ if (!th)
+ continue; // no handler
+ key = th();
+ switch (key) {
+ case CODE_ENTER: // Pretend user hit enter
+ return KEY_ENTER;
+ case CODE_ESCAPE: // Pretend user hit escape
+ return KEY_ESC;
+ default:
+ break;
+ }
+ }
+ return KEY_NONE;
+}
+
+int find_shortcut(pt_menu menu, uchar shortcut, int index)
+// Find the next index with specified shortcut key
+{
+ int ans;
+ pt_menuitem mi;
+
+ // Garbage in garbage out
+ if ((index < 0) || (index >= menu->numitems))
+ return index;
+ ans = index + 1;
+ // Go till end of menu
+ while (ans < menu->numitems) {
+ mi = menu->items[ans];
+ if ((mi->action == OPT_INVISIBLE) || (mi->action == OPT_SEP)
+ || (mi->shortcut != shortcut))
+ ans++;
+ else
+ return ans;
+ }
+ // Start at the beginning and try again
+ ans = 0;
+ while (ans < index) {
+ mi = menu->items[ans];
+ if ((mi->action == OPT_INVISIBLE) || (mi->action == OPT_SEP)
+ || (mi->shortcut != shortcut))
+ ans++;
+ else
+ return ans;
+ }
+ return index; // Sorry not found
+}
+
+/* Redraw background and title */
+static void reset_ui(void)
+{
+ uchar tpos;
+
+ cls();
+ clearwindow(ms->minrow, ms->mincol, ms->maxrow, ms->maxcol,
+ ms->fillchar, ms->fillattr);
+
+ tpos = (ms->numcols - strlen(ms->title) - 1) >> 1; // center it on line
+ gotoxy(ms->minrow, ms->mincol);
+ cprint(ms->tfillchar, ms->titleattr, ms->numcols);
+ gotoxy(ms->minrow, ms->mincol + tpos);
+ csprint(ms->title, ms->titleattr);
+
+ cursoroff();
+}
+
+/*
+ * Print a menu item
+ *
+ * attr[0] is non-hilite attr, attr[1] is highlight attr
+ */
+void printmenuitem(const char *str, uchar * attr)
+{
+ int hlite = NOHLITE; // Initially no highlighting
+
+ while (*str) {
+ switch (*str) {
+ case BELL: // No Bell Char
+ break;
+ case ENABLEHLITE: // Switch on highlighting
+ hlite = HLITE;
+ break;
+ case DISABLEHLITE: // Turn off highlighting
+ hlite = NOHLITE;
+ break;
+ default:
+ putch(*str, attr[hlite]);
+ }
+ str++;
+ }
+}
+
+
+/**
+ * print_line - Print a whole line in a menu
+ * @menu: current menu to handle
+ * @curr: index of the current entry highlighted
+ * @top: top coordinate of the @menu
+ * @left: left coordinate of the @menu
+ * @x: index in the menu of curr
+ * @row: row currently displayed
+ * @radio: radio item?
+ **/
+static void print_line(pt_menu menu, int curr, uchar top, uchar left,
+ int x, int row, bool radio)
+{
+ pt_menuitem ci;
+ char fchar[6], lchar[6]; // The first and last char in for each entry
+ const char *str; // Item string (cf printmenuitem)
+ char sep[MENULEN]; // Separator (OPT_SEP)
+ uchar *attr; // Attribute
+ int menuwidth = menu->menuwidth + 3;
+
+ if (row >= menu->menuheight)
+ return;
+
+ ci = menu->items[x];
+
+ memset(sep, ms->box_horiz, menuwidth);
+ sep[menuwidth - 1] = 0;
+
+ // Setup the defaults now
+ if (radio) {
+ fchar[0] = '\b';
+ fchar[1] = SO;
+ fchar[2] = (x == curr ? RADIOSEL : RADIOUNSEL);
+ fchar[3] = SI;
+ fchar[4] = '\0'; // Unselected ( )
+ lchar[0] = '\0'; // Nothing special after
+ attr = ms->normalattr; // Always same attribute
+ } else {
+ lchar[0] = fchar[0] = ' ';
+ lchar[1] = fchar[1] = '\0'; // fchar and lchar are just spaces
+ attr = (x == curr ? ms->reverseattr : ms->normalattr); // Normal attributes
+ }
+ str = ci->item; // Pointer to item string
+ switch (ci->action) // set up attr,str,fchar,lchar for everything
+ {
+ case OPT_INACTIVE:
+ if (radio)
+ attr = ms->inactattr;
+ else
+ attr = (x == curr ? ms->revinactattr : ms->inactattr);
+ break;
+ case OPT_SUBMENU:
+ if (radio)
+ break; // Not supported for radio menu
+ lchar[0] = '>';
+ lchar[1] = 0;
+ break;
+ case OPT_RADIOMENU:
+ if (radio)
+ break; // Not supported for radio menu
+ lchar[0] = RADIOMENUCHAR;
+ lchar[1] = 0;
+ break;
+ case OPT_CHECKBOX:
+ if (radio)
+ break; // Not supported for radio menu
+ lchar[0] = '\b';
+ lchar[1] = SO;
+ lchar[2] = (ci->itemdata.checked ? CHECKED : UNCHECKED);
+ lchar[3] = SI;
+ lchar[4] = 0;
+ break;
+ case OPT_SEP:
+ fchar[0] = '\b';
+ fchar[1] = SO;
+ fchar[2] = LEFT_MIDDLE_BORDER;
+ fchar[3] = MIDDLE_BORDER;
+ fchar[4] = MIDDLE_BORDER;
+ fchar[5] = 0;
+ memset(sep, MIDDLE_BORDER, menuwidth);
+ sep[menuwidth - 1] = 0;
+ str = sep;
+ lchar[0] = MIDDLE_BORDER;
+ lchar[1] = RIGHT_MIDDLE_BORDER;
+ lchar[2] = SI;
+ lchar[3] = 0;
+ break;
+ case OPT_EXITMENU:
+ if (radio)
+ break; // Not supported for radio menu
+ fchar[0] = '<';
+ fchar[1] = 0;
+ break;
+ default: // Just to keep the compiler happy
+ break;
+ }
+
+ // Wipe area with spaces
+ gotoxy(top + row, left - 2);
+ cprint(ms->spacechar, attr[NOHLITE], menuwidth + 2);
+
+ // Print first part
+ gotoxy(top + row, left - 2);
+ csprint(fchar, attr[NOHLITE]);
+
+ // Print main part
+ gotoxy(top + row, left);
+ printmenuitem(str, attr);
+
+ // Print last part
+ gotoxy(top + row, left + menuwidth - 1);
+ csprint(lchar, attr[NOHLITE]);
+}
+
+// print the menu starting from FIRST
+// will print a maximum of menu->menuheight items
+static void printmenu(pt_menu menu, int curr, uchar top, uchar left, uchar first, bool radio)
+{
+ int x, row; // x = index, row = position from top
+ int numitems, menuwidth;
+ pt_menuitem ci;
+
+ numitems = calc_visible(menu, first);
+ if (numitems > menu->menuheight)
+ numitems = menu->menuheight;
+
+ menuwidth = menu->menuwidth + 3;
+ clearwindow(top, left - 2, top + numitems + 1, left + menuwidth + 1,
+ ms->fillchar, ms->shadowattr);
+ drawbox(top - 1, left - 3, top + numitems, left + menuwidth,
+ ms->normalattr[NOHLITE]);
+
+ // Menu title
+ x = (menuwidth - strlen(menu->title) - 1) >> 1;
+ gotoxy(top - 1, left + x);
+ printmenuitem(menu->title, ms->normalattr);
+
+ // All lines in the menu
+ row = -1; // 1 less than inital value of x
+ for (x = first; x < menu->numitems; x++) {
+ ci = menu->items[x];
+ if (ci->action == OPT_INVISIBLE)
+ continue;
+ row++;
+ if (row >= numitems)
+ break; // Already have enough number of items
+ print_line(menu, curr, top, left, x, row, radio);
+ }
+ // Check if we need to MOREABOVE and MOREBELOW to be added
+ // reuse x
+ row = 0;
+ x = next_visible_sep(menu, 0); // First item
+ if (!isvisible(menu, first, x)) // There is more above
+ {
+ row = 1;
+ gotoxy(top, left + menuwidth);
+ cprint(MOREABOVE, ms->normalattr[NOHLITE], 1);
+ }
+ x = prev_visible_sep(menu, menu->numitems); // last item
+ if (!isvisible(menu, first, x)) // There is more above
+ {
+ row = 1;
+ gotoxy(top + numitems - 1, left + menuwidth);
+ cprint(MOREBELOW, ms->normalattr[NOHLITE], 1);
+ }
+ // Add a scroll box
+ x = ((numitems - 1) * curr) / (menu->numitems);
+ if ((x > 0) && (row == 1)) {
+ gotoxy(top + x, left + menuwidth);
+ csprint("\016\141\017", ms->normalattr[NOHLITE]);
+ }
+ if (ms->handler)
+ ms->handler(ms, menu->items[curr]);
+}
+
+void cleanupmenu(pt_menu menu, uchar top, uchar left, int numitems)
+{
+ if (numitems > menu->menuheight)
+ numitems = menu->menuheight;
+ clearwindow(top, left - 2, top + numitems + 1, left + menu->menuwidth + 4, ms->fillchar, ms->fillattr); // Clear the shadow
+ clearwindow(top - 1, left - 3, top + numitems, left + menu->menuwidth + 3, ms->fillchar, ms->fillattr); // main window
+}
+
+
+/* Handle one menu */
+static pt_menuitem getmenuoption(pt_menu menu, uchar top, uchar left, uchar startopt, bool radio)
+ // Return item chosen or NULL if ESC was hit.
+{
+ int prev, prev_first, curr, i, first, tmp;
+ int asc = 0;
+ bool redraw = true; // Need to draw the menu the first time
+ uchar numitems;
+ pt_menuitem ci; // Current item
+ t_handler_return hr; // Return value of handler
+
+ numitems = calc_visible(menu, 0);
+ // Setup status line
+ gotoxy(ms->minrow + ms->statline, ms->mincol);
+ cprint(ms->spacechar, ms->statusattr[NOHLITE], ms->numcols);
+
+ // Initialise current menu item
+ curr = next_visible(menu, startopt);
+ prev = curr;
+
+ gotoxy(ms->minrow + ms->statline, ms->mincol);
+ cprint(ms->spacechar, ms->statusattr[NOHLITE], ms->numcols);
+ gotoxy(ms->minrow + ms->statline, ms->mincol);
+ printmenuitem(menu->items[curr]->status, ms->statusattr);
+ first = calc_first_early(menu, curr);
+ prev_first = first;
+ while (1) // Forever
+ {
+ /* Redraw everything if:
+ * + we need to scroll (take care of scroll bars, ...)
+ * + menuoption
+ */
+ if (prev_first != first || redraw) {
+ printmenu(menu, curr, top, left, first, radio);
+ } else {
+ /* Redraw only the highlighted entry */
+ print_line(menu, curr, top, left, prev, prev - first, radio);
+ print_line(menu, curr, top, left, curr, curr - first, radio);
+ }
+ redraw = false;
+ prev = curr;
+ prev_first = first;
+ ci = menu->items[curr];
+ asc = getch();
+ switch (asc) {
+ case KEY_CTRL('L'):
+ redraw = true;
+ break;
+ case KEY_HOME:
+ curr = next_visible(menu, 0);
+ first = calc_first_early(menu, curr);
+ break;
+ case KEY_END:
+ curr = prev_visible(menu, numitems - 1);
+ first = calc_first_late(menu, curr);
+ break;
+ case KEY_PGDN:
+ for (i = 0; i < 5; i++)
+ curr = next_visible(menu, curr + 1);
+ first = calc_first_late(menu, curr);
+ break;
+ case KEY_PGUP:
+ for (i = 0; i < 5; i++)
+ curr = prev_visible(menu, curr - 1);
+ first = calc_first_early(menu, curr);
+ break;
+ case KEY_UP:
+ curr = prev_visible(menu, curr - 1);
+ if (curr < first)
+ first = calc_first_early(menu, curr);
+ break;
+ case KEY_DOWN:
+ curr = next_visible(menu, curr + 1);
+ if (!isvisible(menu, first, curr))
+ first = calc_first_late(menu, curr);
+ break;
+ case KEY_LEFT:
+ case KEY_ESC:
+ return NULL;
+ break;
+ case KEY_RIGHT:
+ case KEY_ENTER:
+ if (ci->action == OPT_INACTIVE)
+ break;
+ if (ci->action == OPT_CHECKBOX)
+ break;
+ if (ci->action == OPT_SEP)
+ break;
+ if (ci->action == OPT_EXITMENU)
+ return NULL; // As if we hit Esc
+ // If we are going into a radio menu, dont call handler, return ci
+ if (ci->action == OPT_RADIOMENU)
+ return ci;
+ if (ci->handler != NULL) // Do we have a handler
+ {
+ hr = ci->handler(ms, ci);
+ if (hr.refresh) // Do we need to refresh
+ {
+ // Cleanup menu using old number of items
+ cleanupmenu(menu, top, left, numitems);
+ // Recalculate the number of items
+ numitems = calc_visible(menu, 0);
+ // Reprint the menu
+ printmenu(menu, curr, top, left, first, radio);
+ }
+ if (hr.valid)
+ return ci;
+ } else
+ return ci;
+ break;
+ case SPACECHAR:
+ if (ci->action != OPT_CHECKBOX)
+ break;
+ ci->itemdata.checked = !ci->itemdata.checked;
+ if (ci->handler != NULL) // Do we have a handler
+ {
+ hr = ci->handler(ms, ci);
+ if (hr.refresh) // Do we need to refresh
+ {
+ // Cleanup menu using old number of items
+ cleanupmenu(menu, top, left, numitems);
+ // Recalculate the number of items
+ numitems = calc_visible(menu, 0);
+ // Reprint the menu
+ printmenu(menu, curr, top, left, first, radio);
+ }
+ }
+ break;
+ default:
+ // Check if this is a shortcut key
+ if (((asc >= 'A') && (asc <= 'Z')) ||
+ ((asc >= 'a') && (asc <= 'z')) ||
+ ((asc >= '0') && (asc <= '9'))) {
+ tmp = find_shortcut(menu, asc, curr);
+ if ((tmp > curr) && (!isvisible(menu, first, tmp)))
+ first = calc_first_late(menu, tmp);
+ if (tmp < curr)
+ first = calc_first_early(menu, tmp);
+ curr = tmp;
+ } else {
+ if (ms->keys_handler) // Call extra keys handler
+ ms->keys_handler(ms, menu->items[curr], asc);
+
+ /* The handler may have changed the UI, reset it on exit */
+ reset_ui();
+ // Cleanup menu using old number of items
+ cleanupmenu(menu, top, left, numitems);
+ // Recalculate the number of items
+ numitems = calc_visible(menu, 0);
+ // Reprint the menu
+ printmenu(menu, curr, top, left, first, radio);
+ }
+ break;
+ }
+ // Update status line
+ /* Erase the previous status */
+ gotoxy(ms->minrow + ms->statline, ms->mincol);
+ cprint(ms->spacechar, ms->statusattr[NOHLITE], ms->numcols);
+ /* Print the new status */
+ gotoxy(ms->minrow + ms->statline, ms->mincol);
+ printmenuitem(menu->items[curr]->status, ms->statusattr);
+ }
+ return NULL; // Should never come here
+}
+
+/* Handle the entire system of menu's. */
+pt_menuitem runmenusystem(uchar top, uchar left, pt_menu cmenu, uchar startopt,
+ uchar menutype)
+ /*
+ * cmenu
+ * Which menu should be currently displayed
+ * top,left
+ * What is the position of the top,left corner of the menu
+ * startopt
+ * which menu item do I start with
+ * menutype
+ * NORMALMENU or RADIOMENU
+ *
+ * Return Value:
+ * Returns a pointer to the final item chosen, or NULL if nothing chosen.
+ */
+{
+ pt_menuitem opt, choice;
+ uchar startat, mt;
+ uchar row, col;
+
+ if (cmenu == NULL)
+ return NULL;
+
+startover:
+ // Set the menu height
+ cmenu->menuheight = ms->maxrow - top - 3;
+ if (cmenu->menuheight > ms->maxmenuheight)
+ cmenu->menuheight = ms->maxmenuheight;
+ if (menutype == NORMALMENU)
+ opt = getmenuoption(cmenu, top, left, startopt, false);
+ else // menutype == RADIOMENU
+ opt = getmenuoption(cmenu, top, left, startopt, true);
+
+ if (opt == NULL) {
+ // User hit Esc
+ cleanupmenu(cmenu, top, left, calc_visible(cmenu, 0));
+ return NULL;
+ }
+ // Are we done with the menu system?
+ if ((opt->action != OPT_SUBMENU) && (opt->action != OPT_RADIOMENU)) {
+ cleanupmenu(cmenu, top, left, calc_visible(cmenu, 0));
+ return opt; // parent cleanup other menus
+ }
+ // Either radiomenu or submenu
+ // Do we have a valid menu number? The next hack uses the fact that
+ // itemdata.submenunum = itemdata.radiomenunum (since enum data type)
+ if (opt->itemdata.submenunum >= ms->nummenus) // This is Bad....
+ {
+ gotoxy(12, 12); // Middle of screen
+ csprint("ERROR: Invalid submenu requested.", 0x07);
+ cleanupmenu(cmenu, top, left, calc_visible(cmenu, 0));
+ return NULL; // Pretend user hit esc
+ }
+ // Call recursively for submenu
+ // Position the submenu below the current item,
+ // covering half the current window (horizontally)
+ row = ms->menus[(unsigned int)opt->itemdata.submenunum]->row;
+ col = ms->menus[(unsigned int)opt->itemdata.submenunum]->col;
+ if (row == 0xFF)
+ row = top + opt->index + 2;
+ if (col == 0xFF)
+ col = left + 3 + (cmenu->menuwidth >> 1);
+ mt = (opt->action == OPT_SUBMENU ? NORMALMENU : RADIOMENU);
+ startat = 0;
+ if ((opt->action == OPT_RADIOMENU) && (opt->data != NULL))
+ startat = ((t_menuitem *) opt->data)->index;
+
+ choice = runmenusystem(row, col,
+ ms->menus[(unsigned int)opt->itemdata.submenunum],
+ startat, mt);
+ if (opt->action == OPT_RADIOMENU) {
+ if (choice != NULL)
+ opt->data = (void *)choice; // store choice in data field
+ if (opt->handler != NULL)
+ opt->handler(ms, opt);
+ choice = NULL; // Pretend user hit esc
+ }
+ if (choice == NULL) // User hit Esc in submenu
+ {
+ // Startover
+ startopt = opt->index;
+ goto startover;
+ } else {
+ cleanupmenu(cmenu, top, left, calc_visible(cmenu, 0));
+ return choice;
+ }
+}
+
+// Finds the indexof the menu with given name
+uchar find_menu_num(const char *name)
+{
+ int i;
+ pt_menu m;
+
+ if (name == NULL)
+ return (uchar) (-1);
+ for (i = 0; i < ms->nummenus; i++) {
+ m = ms->menus[i];
+ if ((m->name) && (strcmp(m->name, name) == 0))
+ return i;
+ }
+ return (uchar) (-1);
+}
+
+// Run through all items and if they are submenus
+// with a non-trivial "action" and trivial submenunum
+// replace submenunum with the menu with name "action"
+void fix_submenus(void)
+{
+ int i, j;
+ pt_menu m;
+ pt_menuitem mi;
+
+ i = 0;
+ for (i = 0; i < ms->nummenus; i++) {
+ m = ms->menus[i];
+ for (j = 0; j < m->numitems; j++) {
+ mi = m->items[j];
+ // if item is a submenu and has non-empty non-trivial data string
+ if (mi->data && strlen(mi->data) > 0 &&
+ ((mi->action == OPT_SUBMENU)
+ || (mi->action == OPT_RADIOMENU))) {
+ mi->itemdata.submenunum = find_menu_num(mi->data);
+ }
+ }
+ }
+}
+
+/* User Callable functions */
+
+pt_menuitem showmenus(uchar startmenu)
+{
+ pt_menuitem rv;
+
+ fix_submenus(); // Fix submenu numbers incase nick names were used
+
+ /* Turn autowrap off, to avoid scrolling the menu */
+ printf(CSI "?7l");
+
+ // Setup screen for menusystem
+ reset_ui();
+
+ // Go, main menu cannot be a radio menu
+ rv = runmenusystem(ms->minrow + MENUROW, ms->mincol + MENUCOL,
+ ms->menus[(unsigned int)startmenu], 0, NORMALMENU);
+
+ // Hide the garbage we left on the screen
+ cursoron();
+ cls();
+ gotoxy(ms->minrow, ms->mincol);
+
+ // Return user choice
+ return rv;
+}
+
+pt_menusystem init_menusystem(const char *title)
+{
+ int i;
+
+ ms = NULL;
+ ms = (pt_menusystem) malloc(sizeof(t_menusystem));
+ if (ms == NULL)
+ return NULL;
+ ms->nummenus = 0;
+ // Initialise all menu pointers
+ for (i = 0; i < MAXMENUS; i++)
+ ms->menus[i] = NULL;
+
+ ms->title = (char *)malloc(TITLELEN + 1);
+ if (title == NULL)
+ strcpy(ms->title, TITLESTR); // Copy string
+ else
+ strcpy(ms->title, title);
+
+ // Timeout settings
+ ms->tm_stepsize = TIMEOUTSTEPSIZE;
+ ms->tm_numsteps = TIMEOUTNUMSTEPS;
+
+ ms->normalattr[NOHLITE] = NORMALATTR;
+ ms->normalattr[HLITE] = NORMALHLITE;
+
+ ms->reverseattr[NOHLITE] = REVERSEATTR;
+ ms->reverseattr[HLITE] = REVERSEHLITE;
+
+ ms->inactattr[NOHLITE] = INACTATTR;
+ ms->inactattr[HLITE] = INACTHLITE;
+
+ ms->revinactattr[NOHLITE] = REVINACTATTR;
+ ms->revinactattr[HLITE] = REVINACTHLITE;
+
+ ms->statusattr[NOHLITE] = STATUSATTR;
+ ms->statusattr[HLITE] = STATUSHLITE;
+
+ ms->statline = STATLINE;
+ ms->tfillchar = TFILLCHAR;
+ ms->titleattr = TITLEATTR;
+
+ ms->fillchar = FILLCHAR;
+ ms->fillattr = FILLATTR;
+ ms->spacechar = SPACECHAR;
+ ms->shadowattr = SHADOWATTR;
+
+ ms->menupage = MENUPAGE; // Usually no need to change this at all
+
+ // Initialise all handlers
+ ms->handler = NULL;
+ ms->keys_handler = NULL;
+ ms->ontimeout = NULL; // No timeout handler
+ ms->tm_total_timeout = 0;
+ ms->tm_sofar_timeout = 0;
+ ms->ontotaltimeout = NULL;
+
+ // Setup ACTION_{,IN}VALID
+ ACTION_VALID.valid = 1;
+ ACTION_VALID.refresh = 0;
+ ACTION_INVALID.valid = 0;
+ ACTION_INVALID.refresh = 0;
+
+ // Figure out the size of the screen we are in now.
+ // By default we use the whole screen for our menu
+ if (getscreensize(1, &ms->numrows, &ms->numcols)) {
+ /* Unknown screen size? */
+ ms->numcols = 80;
+ ms->numrows = 24;
+ }
+ ms->minrow = ms->mincol = 0;
+ ms->maxcol = ms->numcols - 1;
+ ms->maxrow = ms->numrows - 1;
+
+ // How many entries per menu can we display at a time
+ ms->maxmenuheight = ms->maxrow - ms->minrow - 3;
+ if (ms->maxmenuheight > MAXMENUHEIGHT)
+ ms->maxmenuheight = MAXMENUHEIGHT;
+
+ console_ansi_raw();
+
+ return ms;
+}
+
+void set_normal_attr(uchar normal, uchar selected, uchar inactivenormal,
+ uchar inactiveselected)
+{
+ if (normal != 0xFF)
+ ms->normalattr[0] = normal;
+ if (selected != 0xFF)
+ ms->reverseattr[0] = selected;
+ if (inactivenormal != 0xFF)
+ ms->inactattr[0] = inactivenormal;
+ if (inactiveselected != 0xFF)
+ ms->revinactattr[0] = inactiveselected;
+}
+
+void set_normal_hlite(uchar normal, uchar selected, uchar inactivenormal,
+ uchar inactiveselected)
+{
+ if (normal != 0xFF)
+ ms->normalattr[1] = normal;
+ if (selected != 0xFF)
+ ms->reverseattr[1] = selected;
+ if (inactivenormal != 0xFF)
+ ms->inactattr[1] = inactivenormal;
+ if (inactiveselected != 0xFF)
+ ms->revinactattr[1] = inactiveselected;
+}
+
+void set_status_info(uchar statusattr, uchar statushlite, uchar statline)
+{
+ if (statusattr != 0xFF)
+ ms->statusattr[NOHLITE] = statusattr;
+ if (statushlite != 0xFF)
+ ms->statusattr[HLITE] = statushlite;
+ // statline is relative to minrow
+ if (statline >= ms->numrows)
+ statline = ms->numrows - 1;
+ ms->statline = statline; // relative to ms->minrow, 0 based
+}
+
+void set_title_info(uchar tfillchar, uchar titleattr)
+{
+ if (tfillchar != 0xFF)
+ ms->tfillchar = tfillchar;
+ if (titleattr != 0xFF)
+ ms->titleattr = titleattr;
+}
+
+void set_misc_info(uchar fillchar, uchar fillattr, uchar spacechar,
+ uchar shadowattr)
+{
+ if (fillchar != 0xFF)
+ ms->fillchar = fillchar;
+ if (fillattr != 0xFF)
+ ms->fillattr = fillattr;
+ if (spacechar != 0xFF)
+ ms->spacechar = spacechar;
+ if (shadowattr != 0xFF)
+ ms->shadowattr = shadowattr;
+}
+
+void set_menu_options(uchar maxmenuheight)
+{
+ if (maxmenuheight != 0xFF)
+ ms->maxmenuheight = maxmenuheight;
+}
+
+// Set the window which menusystem should use
+void set_window_size(uchar top, uchar left, uchar bot, uchar right)
+{
+ int nr, nc;
+
+ if ((top > bot) || (left > right))
+ return; // Sorry no change will happen here
+
+ if (getscreensize(1, &nr, &nc)) {
+ /* Unknown screen size? */
+ nr = 80;
+ nc = 24;
+ }
+ if (bot >= nr)
+ bot = nr - 1;
+ if (right >= nc)
+ right = nc - 1;
+ ms->minrow = top;
+ ms->mincol = left;
+ ms->maxrow = bot;
+ ms->maxcol = right;
+ ms->numcols = right - left + 1;
+ ms->numrows = bot - top + 1;
+ if (ms->statline >= ms->numrows)
+ ms->statline = ms->numrows - 1; // Clip statline if need be
+}
+
+void reg_handler(t_handler htype, void *handler)
+{
+ // If bad value set to default screen handler
+ switch (htype) {
+ case HDLR_KEYS:
+ ms->keys_handler = (t_keys_handler) handler;
+ break;
+ default:
+ ms->handler = (t_menusystem_handler) handler;
+ break;
+ }
+}
+
+void unreg_handler(t_handler htype)
+{
+ switch (htype) {
+ case HDLR_KEYS:
+ ms->keys_handler = NULL;
+ break;
+ default:
+ ms->handler = NULL;
+ break;
+ }
+}
+
+void reg_ontimeout(t_timeout_handler handler, unsigned int numsteps,
+ unsigned int stepsize)
+{
+ ms->ontimeout = handler;
+ if (numsteps != 0)
+ ms->tm_numsteps = numsteps;
+ if (stepsize != 0)
+ ms->tm_stepsize = stepsize;
+}
+
+void unreg_ontimeout(void)
+{
+ ms->ontimeout = NULL;
+}
+
+void reg_ontotaltimeout(t_timeout_handler handler,
+ unsigned long numcentiseconds)
+{
+ if (numcentiseconds != 0) {
+ ms->ontotaltimeout = handler;
+ ms->tm_total_timeout = numcentiseconds * 10; // to convert to milliseconds
+ ms->tm_sofar_timeout = 0;
+ }
+}
+
+void unreg_ontotaltimeout(void)
+{
+ ms->ontotaltimeout = NULL;
+}
+
+int next_visible(pt_menu menu, int index)
+{
+ int ans;
+ if (index < 0)
+ ans = 0;
+ else if (index >= menu->numitems)
+ ans = menu->numitems - 1;
+ else
+ ans = index;
+ while ((ans < menu->numitems - 1) &&
+ ((menu->items[ans]->action == OPT_INVISIBLE) ||
+ (menu->items[ans]->action == OPT_SEP)))
+ ans++;
+ return ans;
+}
+
+int prev_visible(pt_menu menu, int index) // Return index of prev visible
+{
+ int ans;
+ if (index < 0)
+ ans = 0;
+ else if (index >= menu->numitems)
+ ans = menu->numitems - 1;
+ else
+ ans = index;
+ while ((ans > 0) &&
+ ((menu->items[ans]->action == OPT_INVISIBLE) ||
+ (menu->items[ans]->action == OPT_SEP)))
+ ans--;
+ return ans;
+}
+
+int next_visible_sep(pt_menu menu, int index)
+{
+ int ans;
+ if (index < 0)
+ ans = 0;
+ else if (index >= menu->numitems)
+ ans = menu->numitems - 1;
+ else
+ ans = index;
+ while ((ans < menu->numitems - 1) &&
+ (menu->items[ans]->action == OPT_INVISIBLE))
+ ans++;
+ return ans;
+}
+
+int prev_visible_sep(pt_menu menu, int index) // Return index of prev visible
+{
+ int ans;
+ if (index < 0)
+ ans = 0;
+ else if (index >= menu->numitems)
+ ans = menu->numitems - 1;
+ else
+ ans = index;
+ while ((ans > 0) && (menu->items[ans]->action == OPT_INVISIBLE))
+ ans--;
+ return ans;
+}
+
+int calc_visible(pt_menu menu, int first)
+{
+ int ans, i;
+
+ if (menu == NULL)
+ return 0;
+ ans = 0;
+ for (i = first; i < menu->numitems; i++)
+ if (menu->items[i]->action != OPT_INVISIBLE)
+ ans++;
+ return ans;
+}
+
+// is curr visible if first entry is first?
+int isvisible(pt_menu menu, int first, int curr)
+{
+ if (curr < first)
+ return 0;
+ return (calc_visible(menu, first) - calc_visible(menu, curr) <
+ menu->menuheight);
+}
+
+// Calculate the first entry to be displayed
+// so that curr is visible and make curr as late as possible
+int calc_first_late(pt_menu menu, int curr)
+{
+ int ans, i, nv;
+
+ nv = calc_visible(menu, 0);
+ if (nv <= menu->menuheight)
+ return 0;
+ // Start with curr and go back menu->menuheight times
+ ans = curr + 1;
+ for (i = 0; i < menu->menuheight; i++)
+ ans = prev_visible_sep(menu, ans - 1);
+ return ans;
+}
+
+// Calculate the first entry to be displayed
+// so that curr is visible and make curr as early as possible
+int calc_first_early(pt_menu menu, int curr)
+{
+ int ans, i, nv;
+
+ nv = calc_visible(menu, 0);
+ if (nv <= menu->menuheight)
+ return 0;
+ // Start with curr and go back till >= menu->menuheight
+ // items are visible
+ nv = calc_visible(menu, curr); // Already nv of them are visible
+ ans = curr;
+ for (i = 0; i < menu->menuheight - nv; i++)
+ ans = prev_visible_sep(menu, ans - 1);
+ return ans;
+}
+
+// Create a new menu and return its position
+uchar add_menu(const char *title, int maxmenusize)
+{
+ int num, i;
+ pt_menu m;
+
+ num = ms->nummenus;
+ if (num >= MAXMENUS)
+ return -1;
+ m = NULL;
+ m = (pt_menu) malloc(sizeof(t_menu));
+ if (m == NULL)
+ return -1;
+ ms->menus[num] = m;
+ m->numitems = 0;
+ m->name = NULL;
+ m->row = 0xFF;
+ m->col = 0xFF;
+ if (maxmenusize < 1)
+ m->maxmenusize = MAXMENUSIZE;
+ else
+ m->maxmenusize = maxmenusize;
+ m->items = (pt_menuitem *) malloc(sizeof(pt_menuitem) * (m->maxmenusize));
+ for (i = 0; i < m->maxmenusize; i++)
+ m->items[i] = NULL;
+
+ m->title = (char *)malloc(MENULEN + 1);
+ if (title) {
+ if (strlen(title) > MENULEN - 2)
+ strcpy(m->title, TITLELONG);
+ else
+ strcpy(m->title, title);
+ } else
+ strcpy(m->title, EMPTYSTR);
+ m->menuwidth = strlen(m->title);
+ ms->nummenus++;
+ return ms->nummenus - 1;
+}
+
+void set_menu_name(const char *name) // Set the "name" of this menu
+{
+ pt_menu m;
+
+ m = ms->menus[ms->nummenus - 1];
+ if (m->name) // Free up previous name
+ {
+ free(m->name);
+ m->name = NULL;
+ }
+
+ if (name) {
+ m->name = (char *)malloc(strlen(name) + 1);
+ strcpy(m->name, name);
+ }
+}
+
+// Create a new named menu and return its position
+uchar add_named_menu(const char *name, const char *title, int maxmenusize)
+{
+ add_menu(title, maxmenusize);
+ set_menu_name(name);
+ return ms->nummenus - 1;
+}
+
+void set_menu_pos(uchar row, uchar col) // Set the position of this menu.
+{
+ pt_menu m;
+
+ m = ms->menus[ms->nummenus - 1];
+ m->row = row;
+ m->col = col;
+}
+
+pt_menuitem add_sep(void) // Add a separator to current menu
+{
+ pt_menuitem mi;
+ pt_menu m;
+
+ m = (ms->menus[ms->nummenus - 1]);
+ mi = NULL;
+ mi = (pt_menuitem) malloc(sizeof(t_menuitem));
+ if (mi == NULL)
+ return NULL;
+ m->items[(unsigned int)m->numitems] = mi;
+ mi->handler = NULL; // No handler
+ mi->item = mi->status = mi->data = NULL;
+ mi->action = OPT_SEP;
+ mi->index = m->numitems++;
+ mi->parindex = ms->nummenus - 1;
+ mi->shortcut = 0;
+ mi->helpid = 0;
+ return mi;
+}
+
+// Add item to the "current" menu
+pt_menuitem add_item(const char *item, const char *status, t_action action,
+ const char *data, uchar itemdata)
+{
+ pt_menuitem mi;
+ pt_menu m;
+ const char *str;
+ uchar inhlite = 0; // Are we inside hlite area
+
+ m = (ms->menus[ms->nummenus - 1]);
+ mi = NULL;
+ mi = (pt_menuitem) malloc(sizeof(t_menuitem));
+ if (mi == NULL)
+ return NULL;
+ m->items[(unsigned int)m->numitems] = mi;
+ mi->handler = NULL; // No handler
+
+ // Allocate space to store stuff
+ mi->item = (char *)malloc(MENULEN + 1);
+ mi->status = (char *)malloc(STATLEN + 1);
+ mi->data = (char *)malloc(ACTIONLEN + 1);
+
+ if (item) {
+ if (strlen(item) > MENULEN) {
+ strcpy(mi->item, ITEMLONG);
+ } else {
+ strcpy(mi->item, item);
+ }
+ if (strlen(mi->item) > m->menuwidth)
+ m->menuwidth = strlen(mi->item);
+ } else
+ strcpy(mi->item, EMPTYSTR);
+
+ if (status) {
+ if (strlen(status) > STATLEN) {
+ strcpy(mi->status, STATUSLONG);
+ } else {
+ strcpy(mi->status, status);
+ }
+ } else
+ strcpy(mi->status, EMPTYSTR);
+
+ mi->action = action;
+ str = mi->item;
+ mi->shortcut = 0;
+ mi->helpid = 0xFFFF;
+ inhlite = 0; // We have not yet seen an ENABLEHLITE char
+ // Find the first char in [A-Za-z0-9] after ENABLEHLITE and not arg to control char
+ while (*str) {
+ if (*str == ENABLEHLITE) {
+ inhlite = 1;
+ }
+ if (*str == DISABLEHLITE) {
+ inhlite = 0;
+ }
+ if ((inhlite == 1) &&
+ (((*str >= 'A') && (*str <= 'Z')) ||
+ ((*str >= 'a') && (*str <= 'z')) ||
+ ((*str >= '0') && (*str <= '9')))) {
+ mi->shortcut = *str;
+ break;
+ }
+ ++str;
+ }
+ if ((mi->shortcut >= 'A') && (mi->shortcut <= 'Z')) // Make lower case
+ mi->shortcut = mi->shortcut - 'A' + 'a';
+
+ if (data) {
+ if (strlen(data) > ACTIONLEN) {
+ strcpy(mi->data, ACTIONLONG);
+ } else {
+ strcpy(mi->data, data);
+ }
+ } else
+ strcpy(mi->data, EMPTYSTR);
+
+ switch (action) {
+ case OPT_SUBMENU:
+ mi->itemdata.submenunum = itemdata;
+ break;
+ case OPT_CHECKBOX:
+ mi->itemdata.checked = itemdata;
+ break;
+ case OPT_RADIOMENU:
+ mi->itemdata.radiomenunum = itemdata;
+ if (mi->data)
+ free(mi->data);
+ mi->data = NULL; // No selection made
+ break;
+ default: // to keep the compiler happy
+ break;
+ }
+ mi->index = m->numitems++;
+ mi->parindex = ms->nummenus - 1;
+ return mi;
+}
+
+// Set the shortcut key for the current item
+void set_item_options(uchar shortcut, int helpid)
+{
+ pt_menuitem mi;
+ pt_menu m;
+
+ m = (ms->menus[ms->nummenus - 1]);
+ if (m->numitems <= 0)
+ return;
+ mi = m->items[(unsigned int)m->numitems - 1];
+
+ if (shortcut != 0xFF)
+ mi->shortcut = shortcut;
+ if (helpid != 0xFFFF)
+ mi->helpid = helpid;
+}
+
+// Free internal datasutructures
+void close_menusystem(void)
+{
+}
+
+// append_line_helper(pt_menu menu,char *line)
+void append_line_helper(int menunum, char *line)
+{
+ pt_menu menu;
+ pt_menuitem mi, ri;
+ char *app;
+ int ctr;
+
+ menu = ms->menus[menunum];
+ for (ctr = 0; ctr < (int)menu->numitems; ctr++) {
+ mi = menu->items[ctr];
+ app = NULL; //What to append
+ switch (mi->action) {
+ case OPT_CHECKBOX:
+ if (mi->itemdata.checked)
+ app = mi->data;
+ break;
+ case OPT_RADIOMENU:
+ if (mi->data) { // Some selection has been made
+ ri = (pt_menuitem) (mi->data);
+ app = ri->data;
+ }
+ break;
+ case OPT_SUBMENU:
+ append_line_helper(mi->itemdata.submenunum, line);
+ break;
+ default:
+ break;
+ }
+ if (app) {
+ strcat(line, " ");
+ strcat(line, app);
+ }
+ }
+}
+
+// Generate string based on state of checkboxes and radioitem in given menu
+// Assume line points to large enough buffer
+void gen_append_line(const char *menu_name, char *line)
+{
+ int menunum;
+
+ menunum = find_menu_num(menu_name);
+ if (menunum < 0)
+ return; // No such menu
+ append_line_helper(menunum, line);
+}