Menu improvements from Murali
authorhpa <hpa>
Tue, 18 May 2004 03:52:24 +0000 (03:52 +0000)
committerhpa <hpa>
Tue, 18 May 2004 03:52:24 +0000 (03:52 +0000)
NEWS
menu/README
menu/biosio.c
menu/biosio.h
menu/complex.c
menu/heap.c
menu/menu.c
menu/menu.h
menu/simple.c

diff --git a/NEWS b/NEWS
index a89b153..59f0bcf 100644 (file)
--- 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
index a343a3c..05917f6 100644 (file)
@@ -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)
 
index 6c573fa..33f5593 100644 (file)
@@ -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);
+}
index c7da3ff..e9bd504 100644 (file)
@@ -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
index dcdba8a..aa8828e 100644 (file)
 /* 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;
         }
   }
index 2c90a45..aa95454 100644 (file)
@@ -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;
     }
index 35288c1..deb3805 100644 (file)
@@ -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;
 }
-
-
index 291787a..e1564bd 100644 (file)
 #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);
 
index b23da42..86c7007 100644 (file)
@@ -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;
 }