This repository has been archived on 2023-08-20. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
RescueBootCD/extra/syslinux-3.09/menu/libmenu/menu.c

1149 lines
34 KiB
C

/* -*- 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 <stdlib.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.
char getch(char *scan)
{
unsigned long i;
TIMEOUTCODE c;
// Wait until keypress if no handler specified
if (ms->ontimeout==NULL) return inputc(scan);
while (1) // Forever do
{
for (i=0; i < ms->tm_numsteps; i++)
{
if (checkkbdbuf()) return inputc(scan);
sleep(ms->tm_stepsize);
}
c = ms->ontimeout();
switch(c)
{
case CODE_ENTER: // Pretend user hit enter
*scan = ENTERA;
return '\015'; // \015 octal = 13
case CODE_ESCAPE: // Pretend user hit escape
*scan = ESCAPE;
return '\033'; // \033 octal = 27
default:
break;
}
}
return 0;
}
/* Print a menu item */
/* attr[0] is non-hilite attr, attr[1] is highlight attr */
void printmenuitem(const char *str,uchar* attr)
{
uchar page = getdisppage();
uchar row,col;
int hlite=NOHLITE; // Initially no highlighting
getpos(&row,&col,page);
while ( *str ) {
switch (*str)
{
case '\b':
--col;
break;
case '\n':
++row;
break;
case '\r':
col=0;
break;
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], page);
++col;
}
if (col > getnumcols())
{
++row;
col=0;
}
if (row > getnumrows())
{
scrollup();
row= getnumrows();
}
gotoxy(row,col,page);
str++;
}
}
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
}
// print the menu starting from FIRST
// will print a maximum of menu->menuheight items
void printmenu(pt_menu menu, int curr, uchar top, uchar left, uchar first)
{
int x,row; // x = index, row = position from top
int numitems,menuwidth;
char fchar[5],lchar[5]; // The first and last char in for each entry
const char *str; // and inbetween the item or a seperator is printed
uchar *attr; // attribute attr
char sep[MENULEN];// and inbetween the item or a seperator is printed
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->menupage, ms->fillchar, ms->shadowattr);
drawbox(top-1,left-3,top+numitems,left+menuwidth,
ms->menupage,ms->normalattr[NOHLITE],ms->menubt);
memset(sep,ms->box_horiz,menuwidth); // String containing the seperator string
sep[menuwidth-1] = 0;
// Menu title
x = (menuwidth - strlen(menu->title) - 1) >> 1;
gotoxy(top-1,left+x,ms->menupage);
printmenuitem(menu->title,ms->normalattr);
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
// Setup the defaults now
lchar[0] = fchar[0] = ' ';
lchar[1] = fchar[1] = '\0'; // fchar and lchar are just spaces
str = ci->item; // Pointer to item string
attr = (x==curr ? ms->reverseattr : ms->normalattr); // Normal attributes
switch (ci->action) // set up attr,str,fchar,lchar for everything
{
case OPT_INACTIVE:
attr = (x==curr? ms->revinactattr : ms->inactattr);
break;
case OPT_SUBMENU:
lchar[0] = SUBMENUCHAR; lchar[1] = 0;
break;
case OPT_RADIOMENU:
lchar[0] = RADIOMENUCHAR; lchar[1] = 0;
break;
case OPT_CHECKBOX:
lchar[0] = (ci->itemdata.checked ? CHECKED : UNCHECKED);
lchar[1] = 0;
break;
case OPT_SEP:
fchar[0] = '\b'; fchar[1] = ms->box_ltrt; fchar[2] = ms->box_horiz; fchar[3] = ms->box_horiz; fchar[4] = 0;
lchar[0] = ms->box_horiz; lchar[1] = ms->box_rtlt; lchar[2] = 0;
str = sep;
break;
case OPT_EXITMENU:
fchar[0] = EXITMENUCHAR; fchar[1] = 0;
break;
default: // Just to keep the compiler happy
break;
}
gotoxy(top+row,left-2,ms->menupage);
cprint(ms->spacechar,attr[NOHLITE],menuwidth+2,ms->menupage); // Wipe area with spaces
gotoxy(top+row,left-2,ms->menupage);
csprint(fchar,attr[NOHLITE]); // Print first part
gotoxy(top+row,left,ms->menupage);
printmenuitem(str,attr); // Print main part
gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any
csprint(lchar,attr[NOHLITE]); // Print last part
}
// 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,ms->menupage);
cprint(MOREABOVE,ms->normalattr[NOHLITE],1,ms->menupage);
}
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,ms->menupage);
cprint(MOREBELOW,ms->normalattr[NOHLITE],1,ms->menupage);
}
// Add a scroll box
x = ((numitems-1)*curr)/(menu->numitems);
if ((x>0) && (row==1)) {
gotoxy(top+x,left+menuwidth,ms->menupage);
cprint(SCROLLBOX,ms->normalattr[NOHLITE],1,ms->menupage);
}
if (ms->handler) ms->handler(ms,menu->items[curr]);
}
// Difference between this and regular menu, is that only
// OPT_INVISIBLE, OPT_SEP are honoured
void printradiomenu(pt_menu menu, int curr, uchar top, uchar left, int first)
{
int x,row; // x = index, row = position from top
int numitems,menuwidth;
char fchar[5],lchar[5]; // The first and last char in for each entry
const char *str; // and inbetween the item or a seperator is printed
uchar *attr; // all in the attribute attr
char sep[MENULEN];// and inbetween the item or a seperator is printed
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->menupage, ms->fillchar, ms->shadowattr);
drawbox(top-1,left-3,top+numitems,left+menuwidth,
ms->menupage,ms->normalattr[NOHLITE],ms->menubt);
memset(sep,ms->box_horiz,menuwidth); // String containing the seperator string
sep[menuwidth-1] = 0;
// Menu title
x = (menuwidth - strlen(menu->title) - 1) >> 1;
gotoxy(top-1,left+x,ms->menupage);
printmenuitem(menu->title,ms->normalattr);
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;
// Setup the defaults now
fchar[0] = RADIOUNSEL; fchar[1]='\0'; // Unselected ( )
lchar[0] = '\0'; // Nothing special after
str = ci->item; // Pointer to item string
attr = ms->normalattr; // Always same attribute
fchar[0] = (x==curr ? RADIOSEL : RADIOUNSEL);
switch (ci->action) // set up attr,str,fchar,lchar for everything
{
case OPT_INACTIVE:
attr = ms->inactattr;
break;
case OPT_SEP:
fchar[0] = '\b'; fchar[1] = ms->box_ltrt; fchar[2] = ms->box_horiz; fchar[3] = ms->box_horiz; fchar[4] = 0;
lchar[0] = ms->box_horiz; lchar[1] = ms->box_rtlt; lchar[3] = 0;
str = sep;
break;
default: // To keep the compiler happy
break;
}
gotoxy(top+row,left-2,ms->menupage);
cprint(ms->spacechar,attr[NOHLITE],menuwidth+2,ms->menupage); // Wipe area with spaces
gotoxy(top+row,left-2,ms->menupage);
csprint(fchar,attr[NOHLITE]); // Print first part
gotoxy(top+row,left,ms->menupage);
printmenuitem(str,attr); // Print main part
gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any
csprint(lchar,attr[NOHLITE]); // Print last part
}
// 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,ms->menupage);
cprint(MOREABOVE,ms->normalattr[NOHLITE],1,ms->menupage);
}
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,ms->menupage);
cprint(MOREBELOW,ms->normalattr[NOHLITE],1,ms->menupage);
}
// Add a scroll box
x = ((numitems-1)*curr)/(menu->numitems);
if ((x > 0) && (row == 1))
{
gotoxy(top+x,left+menuwidth,ms->menupage);
cprint(SCROLLBOX,ms->normalattr[NOHLITE],1,ms->menupage);
}
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->menupage, ms->fillchar, ms->fillattr); // Clear the shadow
clearwindow(top-1, left-3, top+numitems, left+menu->menuwidth+3,
ms->menupage, ms->fillchar, ms->fillattr); // main window
}
/* Handle a radio menu */
pt_menuitem getradiooption(pt_menu menu, uchar top, uchar left, uchar startopt)
// Return item chosen or NULL if ESC was hit.
{
int curr,i,first,tmp;
uchar asc,scan;
uchar numitems;
pt_menuitem ci; // Current item
numitems = calc_visible(menu,0);
// Setup status line
gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
// Initialise current menu item
curr = next_visible(menu,startopt);
gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,1);
gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
printmenuitem(menu->items[curr]->status,ms->statusattr);
first = calc_first_early(menu,curr);
while (1) // Forever
{
printradiomenu(menu,curr,top,left,first);
ci = menu->items[curr];
asc = getch(&scan);
switch (scan)
{
case HOMEKEY:
curr = next_visible(menu,0);
first = calc_first_early(menu,curr);
break;
case ENDKEY:
curr = prev_visible(menu,numitems-1);
first = calc_first_late(menu,curr);
break;
case PAGEDN:
for (i=0; i < 5; i++) curr = next_visible(menu,curr+1);
first = calc_first_late(menu,curr);
break;
case PAGEUP:
for (i=0; i < 5; i++) curr = prev_visible(menu,curr-1);
first = calc_first_early(menu,curr);
break;
case UPARROW:
curr = prev_visible(menu,curr-1);
if (curr < first) first = calc_first_early(menu,curr);
break;
case DNARROW:
curr = next_visible(menu,curr+1);
if (! isvisible(menu,first,curr))
first = calc_first_late(menu,curr);
break;
case LTARROW:
case ESCAPE:
return NULL;
break;
case RTARROW:
case ENTERA:
case ENTERB:
if (ci->action == OPT_INACTIVE) break;
if (ci->action == OPT_SEP) break;
return ci;
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],(scan << 8) | asc);
}
break;
}
// Update status line
gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
printmenuitem(menu->items[curr]->status,ms->statusattr);
}
return NULL; // Should never come here
}
/* Handle one menu */
pt_menuitem getmenuoption(pt_menu menu, uchar top, uchar left, uchar startopt)
// Return item chosen or NULL if ESC was hit.
{
int curr,i,first,tmp;
uchar asc,scan;
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,ms->menupage);
cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
// Initialise current menu item
curr = next_visible(menu,startopt);
gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,1);
gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
printmenuitem(menu->items[curr]->status,ms->statusattr);
first = calc_first_early(menu,curr);
while (1) // Forever
{
printmenu(menu,curr,top,left,first);
ci = menu->items[curr];
asc = getch(&scan);
switch (scan)
{
case HOMEKEY:
curr = next_visible(menu,0);
first = calc_first_early(menu,curr);
break;
case ENDKEY:
curr = prev_visible(menu,numitems-1);
first = calc_first_late(menu,curr);
break;
case PAGEDN:
for (i=0; i < 5; i++) curr = next_visible(menu,curr+1);
first = calc_first_late(menu,curr);
break;
case PAGEUP:
for (i=0; i < 5; i++) curr = prev_visible(menu,curr-1);
first = calc_first_early(menu,curr);
break;
case UPARROW:
curr = prev_visible(menu,curr-1);
if (curr < first) first = calc_first_early(menu,curr);
break;
case DNARROW:
curr = next_visible(menu,curr+1);
if (! isvisible(menu,first,curr))
first = calc_first_late(menu,curr);
break;
case LTARROW:
case ESCAPE:
return NULL;
break;
case RTARROW:
case ENTERA:
case ENTERB:
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);
}
if (hr.valid) return ci;
}
else return ci;
break;
case SPACEKEY:
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);
}
}
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],(scan << 8) | asc);
}
break;
}
// Update status line
gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
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);
else // menutype == RADIOMENU
opt = getradiooption(cmenu,top,left,startopt);
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,ms->menupage); // 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;
}
}
/* User Callable functions */
pt_menuitem showmenus(uchar startmenu)
{
pt_menuitem rv;
uchar oldpage,tpos;
// Setup screen for menusystem
oldpage = getdisppage();
setdisppage(ms->menupage);
cls();
clearwindow(ms->minrow, ms->mincol, ms->maxrow, ms->maxcol,
ms->menupage, ms->fillchar, ms->fillattr);
tpos = (ms->numcols - strlen(ms->title) - 1) >> 1; // center it on line
gotoxy(ms->minrow,ms->mincol,ms->menupage);
cprint(ms->tfillchar,ms->titleattr,ms->numcols,ms->menupage);
gotoxy(ms->minrow,ms->mincol+tpos,ms->menupage);
csprint(ms->title,ms->titleattr);
cursoroff(); // Doesn't seem to work?
// 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();
if (oldpage == ms->menupage) cls(); else setdisppage(oldpage);
// 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
// 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
ms->minrow = ms->mincol = 0;
ms->numcols = getnumcols();
ms->numrows = getnumrows();
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;
// Set up the look of the box
set_box_type(MENUBOXTYPE);
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_box_type(boxtype bt)
{
uchar *bxc;
ms->menubt = bt;
bxc = getboxchars(bt);
ms->box_horiz = bxc[BOX_HORIZ]; // The char used to draw top line
ms->box_ltrt = bxc[BOX_LTRT];
ms->box_rtlt = bxc[BOX_RTLT];
}
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)
{
uchar nr,nc;
if ((top > bot) || (left > right)) return; // Sorry no change will happen here
nr = getnumrows();
nc = getnumcols();
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()
{
ms->ontimeout = 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->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_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() // 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;
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)
{
}