From 719e4dd2a2731a1849dc5444870850882b05e8b1 Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 18 May 2004 03:52:24 +0000 Subject: [PATCH] Menu improvements from Murali --- NEWS | 1 + menu/README | 39 ++- menu/biosio.c | 200 +++++++++--- menu/biosio.h | 11 +- menu/complex.c | 104 ++++--- menu/heap.c | 2 +- menu/menu.c | 942 +++++++++++++++++++++++++++++++++++---------------------- menu/menu.h | 96 +++--- menu/simple.c | 4 +- 9 files changed, 903 insertions(+), 496 deletions(-) diff --git a/NEWS b/NEWS index a89b153..59f0bcf 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ Changes in 2.10: gzip. Some Windows-based image tools apparently generate these kinds of images by default. Patch by Patrick LoPresti. + * Major menu improvement from Murali Ganapathy. Changes in 2.09: * SYSLINUX: Remove residual setuid crap from diff --git a/menu/README b/menu/README index a343a3c..05917f6 100644 --- a/menu/README +++ b/menu/README @@ -14,7 +14,14 @@ The resulting code runs both under DOS as well as SYSLINUX. A trivial memory allocation routine is implemented, to reduce the memory footprint of this utility. -Currently, menu items, submenus and checkboxes are supported. +Features currently supported are: +* menu items, +* submenus, +* disabled items, +* checkboxes, +* invisible items (useful for dynamic menus), and +* Radio menus + The keys used are: @@ -23,10 +30,36 @@ The keys used are: * Enter to choose the item * Escape to exit from it +Features +-------- +This is a general purpose menu system implemented using only BIOS calls, +so it can be executed in a COMBOOT environment as well. It is highly +customizable. Some features include: + +* Status line + Display any help information associated with each menu item. +* Window + Specify a window within which the menu system draws all its menu's. + It is upto the user to ensure that the menu's fit within the window. +* Positioning submenus + By default, each submenu is positioned just below the corresponding + entry of the parent menu. However, the user may position each menu + at a specific location of his choice. This is useful, when the menu's + have lots of options. +* Registering handlers for each menu item + This is mainly used for checkboxes, where selecting a checkbox, may + result in disabling another menu item, or de-selecting another + checkbox. +* Global Handler + This is called every time the menu is redrawn. The user can display + additional information (usually outside the window where the menu is + being displayed). See the complex.c for an example, where the global + handler is used to display the choices made so far. + Credits ------- -* The Watcom developers and Peter Anvin for figuring out the - startup code. +* The Watcom developers and Peter Anvin for figuring out an OS + independent startup code. - Murali (gmurali+guicd@cs.uchicago.edu) diff --git a/menu/biosio.c b/menu/biosio.c index 6c573fa..33f5593 100644 --- a/menu/biosio.c +++ b/menu/biosio.c @@ -38,7 +38,6 @@ void setdisppage(char num) // Set the display page to specified number asm_setdisppage(num); } - static inline char asm_getdisppage(void) { char page; @@ -54,39 +53,21 @@ char getdisppage() // Get current display page return asm_getdisppage(); } -static inline void asm_putchar(char x, char page) -{ - asm volatile("movb %1,%%bh ; movb $0x0e,%%ah ; int $0x10" - : "+a" (x) - : "g" (page) - : "ebx", "ebp"); -} - -/* Print a C string (NUL-terminated) */ -void csprint(const char *str) -{ - char page = asm_getdisppage(); - - while ( *str ) { - asm_putchar(*str, page); - str++; - } -} - -void clearwindow(char top, char left, char bot, char right, char page, char fillchar, char fillattr) +static inline void asm_getpos(char *row, char *col, char page) { - char x; - for (x=top; x < bot+1; x++) - { - gotoxy(x,left,page); - asm_cprint(fillchar,fillattr,right-left+1,page); - } + asm("movb %2,%%bh ; " + "movb $0x03,%%ah ; " + "int $0x10 ; " + "movb %%dh,%0 ; " + "movb %%dl,%1" + : "=m" (*row), "=m" (*col) + : "g" (page) + : "eax", "ebx", "ecx", "edx"); } -void cls(void) +void getpos(char * row, char * col, char page) { - gotoxy(0,0,getdisppage()); - asm_cprint(' ',0x07,getnumrows()*getnumcols(),getdisppage()); + asm_getpos(row,col,page); } static inline void asm_gotoxy(char row,char col, char page) @@ -103,21 +84,118 @@ void gotoxy(char row,char col, char page) asm_gotoxy(row,col,page); } -static inline void asm_getpos(char *row, char *col, char page) +static inline unsigned char asm_sleep(unsigned int milli) +{ // ah = 86, int 15, cx:dx = microseconds + // mul op16 : dx:ax = ax * op16 + unsigned char ans; + asm volatile ("mul %%cx; " + "xchg %%dx, %%ax; " + "movw %%ax, %%cx; " + "movb $0x86, %%ah;" + "int $0x15;" + "setnc %0" + : "=r" (ans) + : "a" (milli), "c" (1000) + : "edx"); + return ans; +} + +unsigned char sleep(unsigned int msec) { - asm("movb %2,%%bh ; " - "movb $0x03,%%ah ; " - "int $0x10 ; " - "movb %%dh,%0 ; " - "movb %%dl,%1" - : "=m" (*row), "=m" (*col) - : "g" (page) - : "eax", "ebx", "ecx", "edx"); + return asm_sleep(msec); } -void getpos(char * row, char * col, char page) +void asm_beep() { - asm_getpos(row,col,page); + // For a beep the page number (bh) does not matter, so set it to zero + asm volatile("movb $0x0E07, %%ax;" + "xor %%bh,%%bh;" + "int $0x10" + : : : "eax","ebx"); +} + +void beep() +{ + asm_beep(); +} + +static inline void asm_putchar(char x, char attr,char page) +{ + asm volatile("movb %1,%%bh;" + "movb %2,%%bl;" + "movb $0x09,%%ah;" + "movw $0x1, %%cx;" + "int $0x10" + : "+a" (x) + : "g" (page), "g" (attr) + : "ebx", "ecx", "ebp"); +} + +static inline void scrollup() +{ + asm volatile("movw $0x0601, %%ax;" + "movb $0x07, %%bh;" + "xor %%cx, %%cx;" + "int $0x10" + : "+d"((getnumrows()<< 8) + getnumcols()) + : : "eax","ebx","ecx"); +} + +/* Print a C string (NUL-terminated) */ +void csprint(const char *str,char attr) +{ + char page = asm_getdisppage(); + char row,col; + + asm_getpos(&row,&col,page); + while ( *str ) { + switch (*str) + { + case '\b': + --col; + break; + case '\n': + ++row; + break; + case '\r': + col=0; + break; + case 0x07: // Bell Char + asm_beep(); + break; + default: + asm_putchar(*str, attr, page); + ++col; + } + if (col > getnumcols()) + { + ++row; + col=0; + } + if (row > getnumrows()) + { + scrollup(); + row= getnumrows(); + } + asm_gotoxy(row,col,page); + str++; + } +} + +void clearwindow(char top, char left, char bot, char right, char page, char fillchar, char fillattr) +{ + char x; + for (x=top; x < bot+1; x++) + { + gotoxy(x,left,page); + asm_cprint(fillchar,fillattr,right-left+1,page); + } +} + +void cls(void) +{ + gotoxy(0,0,getdisppage()); + asm_cprint(' ',0x07,getnumrows()*getnumcols(),getdisppage()); } char asm_inputc(char *scancode) @@ -168,13 +246,16 @@ static inline char asm_getchar(void) return v; } +#define GETSTRATTR 0x07 + // Reads a line of input from stdin. Replace CR with NUL byte void getstring(char *str, unsigned int size) { char c; char *p = str; char page = asm_getdisppage(); - + char row,col; + while ( (c = asm_getchar()) != '\r' ) { switch (c) { case '\0': /* Extended char prefix */ @@ -183,25 +264,27 @@ void getstring(char *str, unsigned int size) case '\b': if ( p > str ) { p--; - csprint("\b \b"); + csprint("\b \b",GETSTRATTR); } break; case '\x15': /* Ctrl-U: kill input */ while ( p > str ) { p--; - csprint("\b \b"); + csprint("\b \b",GETSTRATTR); } break; default: if ( c >= ' ' && (unsigned int)(p-str) < size-1 ) { - *p++ = c; - asm_putchar(c, page); + *p++ = c; + asm_getpos(&row,&col,page); + asm_putchar(c, GETSTRATTR, page); + asm_gotoxy(row,col+1,page); } break; } } *p = '\0'; - csprint("\r\n"); + csprint("\r\n",GETSTRATTR); } static inline void asm_setvideomode(char mode) @@ -216,3 +299,26 @@ void setvideomode(char mode) { asm_setvideomode(mode); } + +static inline unsigned char asm_checkkbdbuf() +{ + unsigned char ans; + + asm volatile("movb $0x11, %%ah;" + "int $0x16 ;" + "setnz %0;" + : "=r" (ans) + : + : "%eax"); + return ans; +} + +unsigned char checkkbdbuf() +{ + return asm_checkkbdbuf(); +} + +void clearkbdbuf() +{ + while (asm_checkkbdbuf()) asm_inputc(NULL); +} diff --git a/menu/biosio.h b/menu/biosio.h index c7da3ff..e9bd504 100644 --- a/menu/biosio.h +++ b/menu/biosio.h @@ -19,7 +19,7 @@ /* BIOS Assisted output routines */ -void csprint(const char *str); // Print a C str (NUL-terminated) +void csprint(const char *str, char attr); // Print a C str (NUL-terminated) void cprint(char chr,char attr,int times,char disppage); // Print a char @@ -58,6 +58,7 @@ static inline char getnumrows() { return readbiosb(0x484); } + static inline char getnumcols(void) { return readbiosb(0x44a); @@ -65,4 +66,12 @@ static inline char getnumcols(void) void setvideomode(char mode); // Set the video mode. +unsigned char sleep(unsigned int msec); // Sleep for specified time + +void beep(); // A Bell + +unsigned char checkkbdbuf(); // Check to see if there is kbd buffer is non-empty? + +void clearkbdbuf(); // Clear the kbd buffer (how many chars removed?) + #endif diff --git a/menu/complex.c b/menu/complex.c index dcdba8a..aa8828e 100644 --- a/menu/complex.c +++ b/menu/complex.c @@ -22,18 +22,23 @@ /* Global variables */ char infoline[160]; +// Different network options +static char nonet[] = "network [none]"; +static char dhcpnet[]="network [dhcp]"; +static char statnet[]="network [static]"; + struct { unsigned int baseurl : 1; // Do we need to specify by url unsigned int mountcd : 1; // Should we mount the cd - unsigned int network : 1; // want network? - unsigned int dhcp : 1; // want dhcp / static ip unsigned int winrep : 1; // Want to repair windows? unsigned int linrep : 1; // Want to repair linux? } flags; -t_menuitem *baseurl,*mountcd,*network,*dhcp,*runprep,*winrep,*linrep; +t_menuitem *baseurl,*mountcd,*network,*runprep,*winrep,*linrep; +// Some menu options +t_menuitem * stat,*dhcp,*none; // all the menus we are going to declare -char TESTING,RESCUE,MAIN,PREP; +char TESTING,RESCUE,MAIN,PREP,NETMENU; /* End globals */ @@ -42,6 +47,7 @@ char TESTING,RESCUE,MAIN,PREP; void msys_handler(t_menusystem *ms, t_menuitem *mi) { char nc; + void *v; nc = getnumcols(); // Get number of columns if (mi->parindex != PREP) // If we are not in the PREP MENU @@ -53,40 +59,42 @@ void msys_handler(t_menusystem *ms, t_menuitem *mi) strcpy (infoline," "); if (flags.baseurl) strcat(infoline,"baseurl=http://192.168.11.12/gui "); if (flags.mountcd) strcat(infoline,"mountcd=yes "); - if (!flags.network) - strcat(infoline,"network=no "); - else if (!flags.dhcp) strcat(infoline,"network=static "); + v = (void *)network->data; + if (v!=NULL) // Some network option specified + { + strcat(infoline,"network="); + strcat(infoline,(char *)(((t_menuitem *)v)->data)); + } if (flags.winrep) strcat(infoline,"repair=win "); if (flags.linrep) strcat(infoline,"repair=lin "); + gotoxy(INFLINE,0,ms->menupage); cprint(' ',0x07,nc,ms->menupage); gotoxy(INFLINE+1,0,ms->menupage); cprint(' ',0x07,nc,ms->menupage); gotoxy(INFLINE,0,ms->menupage); - csprint("Kernel Arguments:"); + csprint("Kernel Arguments:",0x07); gotoxy(INFLINE,17,ms->menupage); - csprint(infoline); + csprint(infoline,0x07); +} + +void network_handler(t_menusystem *ms, t_menuitem *mi) +{ + // mi=network since this is handler only for that. + (void)ms; // Unused + + if (mi->data == (void *)none) mi->item = nonet; + if (mi->data == (void *)stat) mi->item = statnet; + if (mi->data == (void *)dhcp) mi->item = dhcpnet; } void checkbox_handler(t_menusystem *ms, t_menuitem *mi) { - (void)ms; /* Unused */ + (void)ms; /* Unused */ if (mi->action != OPT_CHECKBOX) return; if (strcmp(mi->data,"baseurl") == 0) flags.baseurl = (mi->itemdata.checked ? 1 : 0); - if (strcmp(mi->data,"network") == 0) { - if (mi->itemdata.checked) - { - flags.network = 1; - dhcp->action = OPT_CHECKBOX; - } - else - { - flags.network = 0; - dhcp->action = OPT_INACTIVE; - } - } if (strcmp(mi->data,"winrepair") == 0) { if (mi->itemdata.checked) { @@ -112,7 +120,25 @@ void checkbox_handler(t_menusystem *ms, t_menuitem *mi) } } if (strcmp(mi->data,"mountcd") == 0) flags.mountcd = (mi->itemdata.checked ? 1 : 0); - if (strcmp(mi->data,"dhcp") == 0) flags.dhcp = (mi->itemdata.checked ? 1 : 0); +} + +/* + Clears keyboard buffer and then + wait for stepsize*numsteps milliseconds for user to press any key + checks for keypress every stepsize milliseconds. + Returns: 1 if user pressed a key (not read from the buffer), + 0 if time elapsed +*/ +int checkkeypress(int stepsize, int numsteps) +{ + int i; + clearkbdbuf(); + for (i=0; i < numsteps; i++) + { + if (checkkbdbuf()) return 1; + sleep(stepsize); + } + return 0; } int menumain(char *cmdline) @@ -139,10 +165,16 @@ int menumain(char *cmdline) // Register the menusystem handler reg_handler(&msys_handler); - + + NETMENU = add_menu(" Init Network "); + none = add_item("None","Dont start network",OPT_RADIOITEM,"no ",0); + dhcp = add_item("dhcp","Use DHCP",OPT_RADIOITEM,"dhcp ",0); + stat = add_item("static","Use static IP I will specify later",OPT_RADIOITEM,"static ",0); + TESTING = add_menu(" Testing "); - set_menu_pos(5,60); + set_menu_pos(5,55); add_item("Memory Test","Perform extensive memory testing",OPT_RUN, "memtest",0); + add_item("Invisible","You dont see this",OPT_INVISIBLE,"junk",0); add_item("Exit this menu","Go one level up",OPT_EXITMENU,"exit",0); RESCUE = add_menu(" Rescue Options "); @@ -154,9 +186,7 @@ int menumain(char *cmdline) PREP = add_menu(" Prep options "); baseurl = add_item("baseurl by IP?","Specify gui baseurl by IP address",OPT_CHECKBOX,"baseurl",0); mountcd = add_item("mountcd?","Mount the cdrom drive?",OPT_CHECKBOX,"mountcd",0); - add_sep(); - network = add_item("network?","Try to initialise network device?",OPT_CHECKBOX,"network",1); - dhcp = add_item("dhcp?","Use dhcp to get ipaddr?",OPT_CHECKBOX,"dhcp",1); + network = add_item(dhcpnet,"How to initialise network device?",OPT_RADIOMENU,NULL,NETMENU); add_sep(); winrep = add_item("Reinstall windows","Re-install the windows side of a dual boot setup",OPT_CHECKBOX,"winrepair",0); linrep = add_item("Reinstall linux","Re-install the linux side of a dual boot setup",OPT_CHECKBOX,"linrepair",0); @@ -165,14 +195,11 @@ int menumain(char *cmdline) add_item("Exit this menu","Go up one level",OPT_EXITMENU,"exitmenu",0); baseurl->handler = &checkbox_handler; mountcd->handler = &checkbox_handler; - network->handler = &checkbox_handler; - dhcp->handler = &checkbox_handler; winrep->handler = &checkbox_handler; linrep->handler = &checkbox_handler; + network->handler = &network_handler; flags.baseurl = 0; flags.mountcd = 0; - flags.network = 1; - flags.dhcp = 1; flags.winrep = 0; flags.linrep = 0; @@ -183,6 +210,13 @@ int menumain(char *cmdline) add_item("Testing...","Options to test hardware",OPT_SUBMENU,NULL,TESTING); add_item("Exit to prompt", "Exit the menu system", OPT_EXITMENU, "exit", 0); + csprint("Press any key within 5 seconds to show menu...",0x07); + if (!checkkeypress(200,25)) // Granularity of 200 milliseconds + { + csprint("Sorry! Time's up.\r\n",0x07); + return 1; + } + else clearkbdbuf(); // Just in case user pressed something important curr = showmenus(MAIN); if (curr) { @@ -193,9 +227,9 @@ int menumain(char *cmdline) if (curr == runprep) { strcat(cmd,infoline); - if (flags.network && !flags.dhcp) // We want static + if (network->data == (void *)stat) // We want static { - csprint("Enter IP address (last two octets only): "); + csprint("Enter IP address (last two octets only): ",0x07); getstring(ip, sizeof ip); strcat(cmd,"ipaddr=192.168."); strcat(cmd,ip); @@ -203,7 +237,7 @@ int menumain(char *cmdline) } if (syslinux) runcommand(cmd); - else csprint(cmd); + else csprint(cmd,0x07); return 1; } } diff --git a/menu/heap.c b/menu/heap.c index 2c90a45..aa95454 100644 --- a/menu/heap.c +++ b/menu/heap.c @@ -31,7 +31,7 @@ static inline void _checkheap(void) { if (currsp() < heap_curr) // Heap corrupted { - csprint("\r\nHeap overflow, aborting!\r\n"); + csprint("\r\nHeap overflow, aborting!\r\n",0x07); asm volatile("int $0x21" : : "a" (0x4C7f)); /* Exit with error */ return; } diff --git a/menu/menu.c b/menu/menu.c index 35288c1..deb3805 100644 --- a/menu/menu.c +++ b/menu/menu.c @@ -34,232 +34,428 @@ static char EMPTYSTR[] = ""; void drawbox(char top, char left, char bot, char right,char attr, char page) { - char x; + char x; - // Top border - gotoxy(top,left,page); - cprint(TOPLEFT,attr,1,page); - gotoxy(top,left+1,page); - cprint(TOP,attr,right-left,page); - gotoxy(top,right,page); - cprint(TOPRIGHT,attr,1,page); - // Bottom border - gotoxy(bot,left,page); - cprint(BOTLEFT,attr,1,page); - gotoxy(bot,left+1,page); - cprint(BOT,attr,right-left,page); - gotoxy(bot,right,page); - cprint(BOTRIGHT,attr,1,page); - // Left & right borders - for (x=top+1; x < bot; x++) + // Top border + gotoxy(top,left,page); + cprint(TOPLEFT,attr,1,page); + gotoxy(top,left+1,page); + cprint(TOP,attr,right-left,page); + gotoxy(top,right,page); + cprint(TOPRIGHT,attr,1,page); + // Bottom border + gotoxy(bot,left,page); + cprint(BOTLEFT,attr,1,page); + gotoxy(bot,left+1,page); + cprint(BOT,attr,right-left,page); + gotoxy(bot,right,page); + cprint(BOTRIGHT,attr,1,page); + // Left & right borders + for (x=top+1; x < bot; x++) { - gotoxy(x,left,page); - cprint(LEFT,attr,1,page); - gotoxy(x,right,page); - cprint(RIGHT,attr,1,page); + gotoxy(x,left,page); + cprint(LEFT,attr,1,page); + gotoxy(x,right,page); + cprint(RIGHT,attr,1,page); } } +int next_visible(pt_menu menu, int index) // Return index of next visible +{ + 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 next 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; +} + void printmenu(pt_menu menu, int curr, char top, char left) { - int x; - 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 - char attr; // all in the attribute attr - char sep[MENULEN];// and inbetween the item or a seperator is printed - pt_menuitem ci; - - numitems = menu->numitems; - 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->normalattr,ms->menupage); - memset(sep,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); - csprint(menu->title); - for (x=0; x < numitems; x++) + 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 + char attr; // all in the attribute attr + char sep[MENULEN];// and inbetween the item or a seperator is printed + pt_menuitem ci; + + calc_visible(menu); + numitems = menu->numvisible; + 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->normalattr, ms->menupage); + memset(sep,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); + csprint(menu->title,ms->normalattr); + row = -1; // 1 less than inital value of x + for (x=0; x < menu->numitems; x++) + { + ci = menu->items[x]; + if (ci->action == OPT_INVISIBLE) continue; + row++; + // 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] = LTRT; fchar[2] = HORIZ; fchar[3] = HORIZ; fchar[4] = 0; + lchar[0] = HORIZ; lchar[1] = RTLT; lchar[3] = 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,menuwidth+2,ms->menupage); // Wipe area with spaces + gotoxy(top+row,left-2,ms->menupage); + csprint(fchar,attr); // Print first part + gotoxy(top+row,left,ms->menupage); + csprint(str,attr); // Print main part + gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any + csprint(lchar,attr); // Print last part + } + 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, char top, char left) +{ + 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 + char attr; // all in the attribute attr + char sep[MENULEN];// and inbetween the item or a seperator is printed + pt_menuitem ci; + + calc_visible(menu); + numitems = menu->numvisible; + 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->normalattr, ms->menupage); + memset(sep,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); + csprint(menu->title,ms->normalattr); + row = -1; // 1 less than inital value of x + for (x=0; x < menu->numitems; x++) { - // Setup the defaults now - lchar[0] = fchar[0] = ' '; - lchar[1] = fchar[1] = '\0'; // fchar and lchar are just spaces - ci = menu->items[x]; - 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 + ci = menu->items[x]; + if (ci->action == OPT_INVISIBLE) continue; + row++; + // 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 = (x==curr? ms->revinactattr : ms->inactattr); - break; - case OPT_SUBMENU: - lchar[0] = SUBMENUCHAR; 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] = LTRT; fchar[2] = HORIZ; fchar[3] = HORIZ; fchar[4] = 0; - lchar[0] = HORIZ; lchar[1] = RTLT; lchar[3] = 0; - str = sep; - break; - case OPT_EXITMENU: - fchar[0] = EXITMENUCHAR; fchar[1] = 0; - //default: + case OPT_INACTIVE: + attr = ms->inactattr; + break; + case OPT_SEP: + fchar[0] = '\b'; fchar[1] = LTRT; fchar[2] = HORIZ; fchar[3] = HORIZ; fchar[4] = 0; + lchar[0] = HORIZ; lchar[1] = RTLT; lchar[3] = 0; + str = sep; + break; + default: // To keep the compiler happy + break; } - gotoxy(top+x,left-2,ms->menupage); - cprint(ms->spacechar,attr,menuwidth+2,ms->menupage); // Wipe area with spaces - gotoxy(top+x,left-2,ms->menupage); - csprint(fchar); // Print first part - gotoxy(top+x,left,ms->menupage); - csprint(str); // Print main part - gotoxy(top+x,left+menuwidth-1,ms->menupage); // Last char if any - csprint(lchar); // Print last part + gotoxy(top+row,left-2,ms->menupage); + cprint(ms->spacechar,attr,menuwidth+2,ms->menupage); // Wipe area with spaces + gotoxy(top+row,left-2,ms->menupage); + csprint(fchar,attr); // Print first part + gotoxy(top+row,left,ms->menupage); + csprint(str,attr); // Print main part + gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any + csprint(lchar,attr); // Print last part } - if (ms->handler) ms->handler(ms,menu->items[curr]); + if (ms->handler) ms->handler(ms,menu->items[curr]); } void cleanupmenu(pt_menu menu, char top,char left) { - clearwindow(top,left-2,top+menu->numitems+1,left+menu->menuwidth+4,ms->menupage,ms->fillchar,ms->fillattr); // Clear the shadow - clearwindow(top-1,left-3,top+menu->numitems,left+menu->menuwidth+3,ms->menupage,ms->fillchar,ms->fillattr); // clear the main window + clearwindow(top,left-2, top+menu->numvisible+1, left+menu->menuwidth+4, + ms->menupage, ms->fillchar, ms->fillattr); // Clear the shadow + clearwindow(top-1, left-3, top+menu->numvisible, left+menu->menuwidth+3, + ms->menupage, ms->fillchar, ms->fillattr); // main window +} + +/* Handle a radio menu */ +pt_menuitem getradiooption(pt_menu menu, char top, char left, char startopt) + // Return item chosen or NULL if ESC was hit. +{ + int curr,i; + char asc,scan; + char numitems; + pt_menuitem ci; // Current item + + calc_visible(menu); + numitems = menu->numvisible; + // Setup status line + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + cprint(ms->spacechar,ms->reverseattr,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,ms->numcols,1); + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + csprint(menu->items[curr]->status,ms->statusattr); + while (1) // Forever + { + printradiomenu(menu,curr,top,left); + ci = menu->items[curr]; + asc = inputc(&scan); + switch (scan) + { + case HOMEKEY: + curr = next_visible(menu,0); + break; + case ENDKEY: + curr = prev_visible(menu,numitems-1); + break; + case PAGEDN: + for (i=0; i < 5; i++) curr = next_visible(menu,curr+1); + break; + case PAGEUP: + for (i=0; i < 5; i++) curr = prev_visible(menu,curr-1); + break; + case UPARROW: + curr = prev_visible(menu,curr-1); + break; + case DNARROW: + curr = next_visible(menu,curr+1); + 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; + } + // Adjust within range + //if (curr < 0) curr=next_visible(menu,0); + //if (curr >= numitems) curr = prev_visible(menu,numitems -1); + // Update status line + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + cprint(ms->spacechar,ms->statusattr,ms->numcols,ms->menupage); + csprint(menu->items[curr]->status,ms->statusattr); + } + return NULL; // Should never come here } /* Handle one menu */ pt_menuitem getmenuoption( pt_menu menu, char top, char left, char startopt) -// Return item chosen or NULL if ESC was hit. + // Return item chosen or NULL if ESC was hit. { - int curr; - char asc,scan; - char numitems; - pt_menuitem ci; // Current item + int curr,i; + char asc,scan; + char numitems; + pt_menuitem ci; // Current item - numitems = menu->numitems; - // Setup status line - gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); - cprint(ms->spacechar,ms->reverseattr,ms->numcols,ms->menupage); - - // Initialise current menu item - curr = startopt; - gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); - cprint(ms->spacechar,ms->statusattr,ms->numcols,1); - gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); - csprint(menu->items[curr]->status); - while (1) // Forever + calc_visible(menu); + numitems = menu->numvisible; + // Setup status line + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + cprint(ms->spacechar,ms->reverseattr,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,ms->numcols,1); + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + csprint(menu->items[curr]->status,ms->statusattr); + while (1) // Forever { - printmenu(menu,curr,top,left); - ci = menu->items[curr]; - asc = inputc(&scan); - switch (scan) + printmenu(menu,curr,top,left); + ci = menu->items[curr]; + asc = inputc(&scan); + switch (scan) { - case HOMEKEY: - curr = 0; - break; - case ENDKEY: - curr = numitems -1; - break; - case PAGEDN: - curr += 5; - break; - case PAGEUP: - curr -= 5; - break; - case UPARROW: - while((curr > 0) && (menu->items[--curr]->action == OPT_SEP)) ; - break; - case DNARROW: - while((curr < numitems-1) && (menu->items[++curr]->action == OPT_SEP)) ; - 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 - return ci; - break; - case SPACEKEY: - if (ci->action != OPT_CHECKBOX) break; - ci->itemdata.checked = !ci->itemdata.checked; - // Call handler to see it anything needs to be done - if (ci->handler != NULL) ci->handler(ms,ci); - break; + case HOMEKEY: + curr = next_visible(menu,0); + break; + case ENDKEY: + curr = prev_visible(menu,numitems-1); + break; + case PAGEDN: + for (i=0; i < 5; i++) curr = next_visible(menu,curr+1); + break; + case PAGEUP: + for (i=0; i < 5; i++) curr = prev_visible(menu,curr-1); + break; + case UPARROW: + curr = prev_visible(menu,curr-1); + break; + case DNARROW: + curr = next_visible(menu,curr+1); + 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 + return ci; + break; + case SPACEKEY: + if (ci->action != OPT_CHECKBOX) break; + ci->itemdata.checked = !ci->itemdata.checked; + // Call handler to see it anything needs to be done + if (ci->handler != NULL) ci->handler(ms,ci); + break; } - // Adjust within range - if (curr < 0) curr=0; - if (curr >= numitems) curr = numitems -1; - // Update status line - gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); - cprint(ms->spacechar,ms->statusattr,ms->numcols,ms->menupage); - csprint(menu->items[curr]->status); + // Adjust within range + //if (curr < 0) curr=next_visible(menu,0); + //if (curr >= numitems) curr = prev_visible(menu,numitems -1); + // Update status line + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + cprint(ms->spacechar,ms->statusattr,ms->numcols,ms->menupage); + csprint(menu->items[curr]->status,ms->statusattr); } - return NULL; // Should never come here + return NULL; // Should never come here } /* Handle the entire system of menu's. */ -pt_menuitem runmenusystem(char top, char left, pt_menu cmenu) -/* - * cmenu - * Which menu should be currently displayed - * top,left - * What is the position of the top,left corner of the menu - * - * Return Value: - * Returns a pointer to the final item chosen, or NULL if nothing chosen. - */ +pt_menuitem runmenusystem(char top, char left, pt_menu cmenu, char startopt, char 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; - int numitems; - char startopt,row,col; - - startopt = 0; - if (cmenu == NULL) return NULL; -startover: - numitems = cmenu->numitems; + pt_menuitem opt,choice; + char startat,mt; + char row,col; + + if (cmenu == NULL) return NULL; + startover: + if (menutype == NORMALMENU) opt = getmenuoption(cmenu,top,left,startopt); - if (opt == NULL) + else // menutype == RADIOMENU + opt = getradiooption(cmenu,top,left,startopt); + + if (opt == NULL) { - // User hit Esc - cleanupmenu(cmenu,top,left); - return NULL; + // User hit Esc + cleanupmenu(cmenu,top,left); + return NULL; } - if (opt->action != OPT_SUBMENU) // We are done with the menu system + // Are we done with the menu system? + if ((opt->action != OPT_SUBMENU) && (opt->action != OPT_RADIOMENU)) { - cleanupmenu(cmenu,top,left); - return opt; // parent cleanup other menus + cleanupmenu(cmenu,top,left); + return opt; // parent cleanup other menus } - if (opt->itemdata.submenunum >= ms->nummenus) // This is Bad.... + // 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("Invalid submenu requested. Ask administrator to correct this."); - cleanupmenu(cmenu,top,left); - return NULL; // Pretend user hit esc + gotoxy(12,12,ms->menupage); // Middle of screen + csprint("ERROR: Invalid submenu requested.",0x07); + cleanupmenu(cmenu,top,left); + 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[opt->itemdata.submenunum]->row; - col = ms->menus[opt->itemdata.submenunum]->col; - if (row == 0xFF) row = top+opt->index+2; - if (col == 0xFF) col = left+3+(cmenu->menuwidth >> 1); - choice = runmenusystem(row, col, ms->menus[opt->itemdata.submenunum]); - if (choice==NULL) // User hit Esc in submenu + // 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) { - // Startover - startopt = opt->index; - goto startover; + if (choice != NULL) opt->data = (void *)choice; // store choice in data field + if (opt->handler != NULL) opt->handler(ms,opt); // Call handler + choice = NULL; // Pretend user hit esc } - else + if (choice==NULL) // User hit Esc in submenu { - cleanupmenu(cmenu,top,left); - return choice; + // Startover + startopt = opt->index; + goto startover; + } + else + { + cleanupmenu(cmenu,top,left); + return choice; } } @@ -267,241 +463,257 @@ startover: pt_menuitem showmenus(char startmenu) { - pt_menuitem rv; - char 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; // To 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); - - cursoroff(); // Doesn't seem to work? - - // Go - rv = runmenusystem(ms->minrow+MENUROW, ms->mincol+MENUCOL, ms->menus[startmenu]); - - // Hide the garbage we left on the screen - cursoron(); - if (oldpage == ms->menupage) cls(); else setdisppage(oldpage); - - // Return user choice - return rv; + pt_menuitem rv; + char 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; } void init_menusystem(const char *title) { - char i; + int i; - ms = NULL; - ms = (pt_menusystem) malloc(sizeof(t_menusystem)); - if (ms == NULL) return; - ms->nummenus = 0; - // Initialise all menu pointers - for (i=0; i < MAXMENUS; i++) ms->menus[i] = NULL; + ms = NULL; + ms = (pt_menusystem) malloc(sizeof(t_menusystem)); + if (ms == NULL) return; + ms->nummenus = 0; + // Initialise all menu pointers + for (i=0; i < MAXMENUS; i++) ms->menus[i] = NULL; - if (title == NULL) - ms->title = TITLESTR; // Copy pointers - else ms->title = title; - - ms->normalattr = NORMALATTR; - ms->reverseattr= REVERSEATTR; - ms->inactattr = INACTATTR; - ms->revinactattr = REVINACTATTR; - - ms->statusattr = STATUSATTR; - ms->statline = STATLINE; - ms->tfillchar= TFILLCHAR; - ms->titleattr= TITLEATTR; + if (title == NULL) + ms->title = TITLESTR; // Copy pointers + else ms->title = title; + + ms->normalattr = NORMALATTR; + ms->reverseattr= REVERSEATTR; + ms->inactattr = INACTATTR; + ms->revinactattr = REVINACTATTR; + + ms->statusattr = STATUSATTR; + 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 - ms->handler = NULL; // No handler function - - // 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; + ms->fillchar = FILLCHAR; + ms->fillattr = FILLATTR; + ms->spacechar= SPACECHAR; + ms->shadowattr = SHADOWATTR; + + ms->menupage = MENUPAGE; // Usually no need to change this at all + ms->handler = NULL; // No handler function + + // 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; } void set_normal_attr(char normal, char selected, char inactivenormal, char inactiveselected) { - if (normal != 0xFF) ms->normalattr = normal; - if (selected != 0xFF) ms->reverseattr = selected; - if (inactivenormal != 0xFF) ms->inactattr = inactivenormal; - if (inactiveselected != 0xFF) ms->revinactattr = inactiveselected; + if (normal != 0xFF) ms->normalattr = normal; + if (selected != 0xFF) ms->reverseattr = selected; + if (inactivenormal != 0xFF) ms->inactattr = inactivenormal; + if (inactiveselected != 0xFF) ms->revinactattr = inactiveselected; } void set_status_info(char statusattr, char statline) { - if (statusattr != 0xFF) ms->statusattr = statusattr; - // statline is relative to minrow - if (statline >= ms->numrows) statline = ms->numrows - 1; - ms->statline = statline; // relative to ms->minrow, 0 based + if (statusattr != 0xFF) ms->statusattr = statusattr; + // 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(char tfillchar, char titleattr) { - if (tfillchar != 0xFF) ms->tfillchar = tfillchar; - if (titleattr != 0xFF) ms->titleattr = titleattr; + if (tfillchar != 0xFF) ms->tfillchar = tfillchar; + if (titleattr != 0xFF) ms->titleattr = titleattr; } void set_misc_info(char fillchar, char fillattr,char spacechar, char 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; + 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_window_size(char top, char left, char bot, char right) // Set the window which menusystem should use { - char 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 + char 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_menusystem_handler handler) { - ms->handler = handler; + ms->handler = handler; } void unreg_handler() { - ms->handler = NULL; + ms->handler = NULL; +} + +void calc_visible(pt_menu menu) +{ + int ans,i; + + if (menu == NULL) return; + ans = 0; + for (i=0; i < menu->numitems; i++) + if (menu->items[i]->action != OPT_INVISIBLE) ans++; + menu->numvisible = ans; } char add_menu(const char *title) // Create a new menu and return its position { - char num,i; - pt_menu m; - - if (num >= MAXMENUS) return -1; - num = ms->nummenus; - 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; - for (i=0; i < MAXMENUSIZE; i++) m->items[i] = NULL; + 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; + for (i=0; i < MAXMENUSIZE; i++) m->items[i] = NULL; - if (title) - { - if (strlen(title) > MENULEN - 2) - m->title = TITLELONG; - else m->title = title; - } - else m->title = EMPTYSTR; - m ->menuwidth = strlen(m->title); - ms->nummenus ++; - return ms->nummenus - 1; + if (title) + { + if (strlen(title) > MENULEN - 2) + m->title = TITLELONG; + else m->title = title; + } + else m->title = EMPTYSTR; + m ->menuwidth = strlen(m->title); + ms->nummenus ++; + return ms->nummenus - 1; } void set_menu_pos(char row,char col) // Set the position of this menu. { -pt_menu m; + pt_menu m; - m = ms->menus[ms->nummenus-1]; - m->row = row; - m->col = col; + 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[m->numitems] = mi; - mi->handler = NULL; // No handler - mi->item = mi->status = mi->data = EMPTYSTR; - mi->action = OPT_SEP; - mi->index = m->numitems++; - mi->parindex = ms->nummenus-1; - return mi; + 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 = EMPTYSTR; + mi->action = OPT_SEP; + mi->index = m->numitems++; + mi->parindex = ms->nummenus-1; + return mi; } -pt_menuitem add_item(const char *item, const char *status, t_action action, const char *data, char itemdata) // Add item to the "current" menu +// Add item to the "current" menu +pt_menuitem add_item(const char *item, const char *status, t_action action, + const char *data, char itemdata) { - 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[m->numitems] = mi; - mi->handler = NULL; // No handler - if (item) { - if (strlen(item) > MENULEN - 2) { - mi->item = ITEMLONG; - } else { - mi->item = item; - if (strlen(item) > m->menuwidth) m->menuwidth = strlen(item); - } - } else mi->item = EMPTYSTR; - - if (status) { - if (strlen(status) > STATLEN - 2) { - mi->status = STATUSLONG; - } else { + 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 + if (item) { + if (strlen(item) > MENULEN - 2) { + mi->item = ITEMLONG; + } else { + mi->item = item; + if (strlen(item) > m->menuwidth) m->menuwidth = strlen(item); + } + } else mi->item = EMPTYSTR; + + if (status) { + if (strlen(status) > STATLEN - 2) { + mi->status = STATUSLONG; + } else { mi->status = status; - } - } else mi->status = EMPTYSTR; + } + } else mi->status = EMPTYSTR; - mi->action = action; + mi->action = action; - if (data) { - if (strlen(data) > ACTIONLEN - 2) { - mi->data = ACTIONLONG; - } else { - mi->data = data; - } - } else mi->data = EMPTYSTR; + if (data) { + if (strlen(data) > ACTIONLEN - 2) { + mi->data = ACTIONLONG; + } else { + mi->data = data; + } + } else mi->data = EMPTYSTR; - switch (action) + switch (action) { - case OPT_SUBMENU: - mi->itemdata.submenunum = itemdata; - break; - case OPT_CHECKBOX: - mi->itemdata.checked = itemdata; - break; - case OPT_RADIOBTN: - mi->itemdata.choice = itemdata; - break; + 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; + mi->index = m->numitems++; + mi->parindex = ms->nummenus-1; + return mi; } - - diff --git a/menu/menu.h b/menu/menu.h index 291787a..e1564bd 100644 --- a/menu/menu.h +++ b/menu/menu.h @@ -99,18 +99,26 @@ #define STATLINE 23 // Line number where status line starts (relative to window) // Other Chars -#define SUBMENUCHAR 175 // This is >> symbol -#define EXITMENUCHAR 174 // This is << symbol -#define CHECKED 251 // Check mark -#define UNCHECKED 250 // Light bullet +#define SUBMENUCHAR 175 // This is >> symbol +#define RADIOMENUCHAR '>' // > symbol for radio menu? +#define EXITMENUCHAR 174 // This is << symbol +#define CHECKED 251 // Check mark +#define UNCHECKED 250 // Light bullet +#define RADIOSEL '.' // Current Radio Selection +#define RADIOUNSEL ' ' // Radio option not selected + +// Types of menu's +#define NORMALMENU 1 +#define RADIOMENU 2 typedef enum {OPT_INACTIVE, OPT_SUBMENU, OPT_RUN, OPT_EXITMENU, OPT_CHECKBOX, - OPT_RADIOBTN, OPT_EXIT, OPT_SEP} t_action; + OPT_RADIOMENU, OPT_EXIT, OPT_SEP, OPT_INVISIBLE, + OPT_RADIOITEM} t_action; typedef union { - char submenunum; - char checked; // For check boxes - char choice; // For Radio buttons + char submenunum; // For submenu's + char checked; // For check boxes + char radiomenunum; // Item mapping to a radio menu } t_itemdata; struct s_menuitem; @@ -121,50 +129,51 @@ typedef void (*t_item_handler)(struct s_menusystem *, struct s_menuitem *); typedef void (*t_menusystem_handler)(struct s_menusystem *, struct s_menuitem *); typedef struct s_menuitem { - const char *item; - const char *status; - const char *data; - void * extra_data; // Any other data user can point to - t_item_handler handler; // Pointer to function of type menufn - char active; // Is this item active or not - t_action action; - t_itemdata itemdata; // Data depends on action value - char index; // Index within the menu array - char parindex; // Index of the menu in which this item appears. + const char *item; + const char *status; + const char *data; // string containing kernel to run.. but... + // for radio menu's this is a pointer to the item selected or NULL (initially) + void * extra_data; // Any other data user can point to + t_item_handler handler; // Pointer to function of type menufn + t_action action; + t_itemdata itemdata; // Data depends on action value + char index; // Index within the menu array + char parindex; // Index of the menu in which this item appears. } t_menuitem; typedef t_menuitem *pt_menuitem; // Pointer to type menuitem typedef struct s_menu { - pt_menuitem items[MAXMENUSIZE]; - const char *title; - char numitems; - char menuwidth; - char row,col; // Position where this menu should be displayed + pt_menuitem items[MAXMENUSIZE]; + const char *title; + char numitems; + char numvisible; + char menuwidth; + char row,col; // Position where this menu should be displayed } t_menu; typedef t_menu *pt_menu; // Pointer to type menu typedef struct s_menusystem { - pt_menu menus[MAXMENUS]; - const char *title; - t_menusystem_handler handler; // Handler function called every time a menu is re-printed. - char nummenus; - char normalattr; - char reverseattr; - char inactattr; - char revinactattr; - char statusattr; - char fillchar; - char fillattr; - char spacechar; - char tfillchar; - char titleattr; - char shadowattr; - char statline; - char menupage; - char maxrow,minrow,numrows; // Number of rows in the window - char maxcol,mincol,numcols; // Number of columns in the window + pt_menu menus[MAXMENUS]; + const char *title; + t_menusystem_handler handler; // Handler function called every time a menu is re-printed. + char nummenus; + char normalattr; + char reverseattr; + char inactattr; + char revinactattr; + char statusattr; + char fillchar; + char fillattr; + char spacechar; + char tfillchar; + char titleattr; + char shadowattr; + char statline; + char menupage; + char maxrow,minrow,numrows; // Number of rows in the window + char maxcol,mincol,numcols; // Number of columns in the window } t_menusystem; typedef t_menusystem *pt_menusystem; // Pointer to type menusystem @@ -214,6 +223,9 @@ void set_menu_pos(char row,char col); // Set the position of this menu. // Add a separator to the "current" menu pt_menuitem add_sep(); +// Calculate the number of visible items +void calc_visible(pt_menu menu); + // Main function for the user's config file int menumain(char *cmdline); diff --git a/menu/simple.c b/menu/simple.c index b23da42..86c7007 100644 --- a/menu/simple.c +++ b/menu/simple.c @@ -72,10 +72,10 @@ int menumain(char *cmdline) if (curr->action == OPT_RUN) { if (syslinux) runcommand(curr->data); - else csprint(curr->data); + else csprint(curr->data,0x07); return 1; } - csprint("Error in programming!"); + csprint("Error in programming!",0x07); } return 0; } -- 2.7.4