New menu highlight/hotkey interface.
authorhpa <hpa>
Thu, 17 Jun 2004 15:14:28 +0000 (15:14 +0000)
committerhpa <hpa>
Thu, 17 Jun 2004 15:14:28 +0000 (15:14 +0000)
menu/README
menu/biosio.c
menu/biosio.h
menu/complex.c
menu/menu.c
menu/menu.h
menu/simple.c

index 05917f6..dd22142 100644 (file)
@@ -14,14 +14,13 @@ 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.
 
-Features currently supported are:
+Menu Features currently supported are:
 * menu items, 
 * submenus, 
 * disabled items,
 * checkboxes,
 * invisible items (useful for dynamic menus), and
 * Radio menus
 
 The keys used are:
 
@@ -29,6 +28,7 @@ The keys used are:
 * Space to switch state of a checkbox
 * Enter to choose the item
 * Escape to exit from it
+* Shortcut keys
 
 Features
 --------
@@ -47,14 +47,29 @@ customizable. Some features include:
     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.
+    This is mainly used for checkboxes and radiomenu's, where a selection may
+    result in disabling other menu items/checkboxes 
 * 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.
+* Shortcut Keys
+    With each item one can register a shortcut key from [A-Za-z0-9]. 
+    Pressing a key within that range, will take you to the next item
+    with that shortcut key (so you can have multiple items with the
+    same shortcut key). The default shortcut key for each item, is 
+    the lower case version of the first char of the item in the range 
+    [A-Za-z0-9].
+* Escape Keys
+    Each item entry can have a substring enclosed in < and >. This part
+    is highlighted. Can be used to highlight the shortcut keys. By default
+    if an item has a <, then the first char inside < and > in the range
+    [A-Za-z0-9] is converted to lower case and set as the shortcut key.
+* Ontimeout handler
+    The user can register an ontimeout handler, which gets called if 
+    no key has been pressed for a user specific amount of time (default 5 min).
+    For an example see the complex.c file.
 
 Credits
 -------
index 55cdd01..d1fa38f 100644 (file)
@@ -138,7 +138,12 @@ static inline void asm_putchar(char x, char attr,char page)
                 : "ebx", "ecx", "ebp");
 }
 
-static inline void scrollup()
+void putch(char x, char attr, char page)
+{
+  asm_putchar(x,attr,page);
+}
+
+void scrollup()
 {
   unsigned short dx = (getnumrows()<< 8) + getnumcols();
   
@@ -154,6 +159,7 @@ static inline void scrollup()
 void csprint(const char *str,char attr)
 {
     char page = asm_getdisppage();
+    char newattr=0,cha,chb;
     char row,col;
 
     asm_getpos(&row,&col,page);
@@ -169,9 +175,29 @@ void csprint(const char *str,char attr)
        case '\r':
          col=0;
          break;
-       case 0x07: // Bell Char
+       case BELL: // Bell Char
          asm_beep();
          break;
+       case CHRELATTR: // change attribute (relatively)
+       case CHABSATTR: // change attribute (absolute)
+         cha = *(str+1);
+         chb = *(str+2);
+         if ((((cha >= '0') && (cha <= '9')) || 
+              ((cha >= 'A') && (cha <= 'F'))) &&
+             (((chb >= '0') && (chb <= '9')) || 
+              ((chb >= 'A') && (chb <= 'F')))) // Next two chars are legal
+           {
+             if ((cha >= 'A') && (cha <= 'F'))
+               cha = cha - 'A'+10;
+             else cha = cha - '0';
+             if ((chb >= 'A') && (chb <= 'F'))
+               chb = chb - 'A'+10;
+             else chb = chb - '0';
+             newattr = (cha << 4) + chb;
+             attr = (*str == CHABSATTR ? newattr : attr ^ newattr);
+             str += 2; // Will be incremented again later
+           }
+         break;
        default:
          asm_putchar(*str, attr, page);
          ++col;
@@ -231,7 +257,7 @@ static inline void asm_cursorshape(char start, char end)
     asm volatile("movb $0x01,%%ah ; int $0x10"
                 : : "c" ((start << 8) + end) : "eax");
 }
-   
+
 void cursoroff(void)
 {
     asm_cursorshape(32,32);
index c17cc8f..ebba214 100644 (file)
 #define NULL ((void *)0)
 #endif
 
+#define BELL 0x07
+// CHRELATTR = ^N, CHABSATTR = ^O
+#define CHABSATTR 15 
+#define CHRELATTR 14
+
 /* BIOS Assisted output routines */
 
 void csprint(const char *str, char attr); // Print a C str (NUL-terminated)
@@ -27,7 +32,8 @@ void setdisppage(char num); // Set the display page to specified number
 
 char getdisppage(); // Get current display page 
 
-void clearwindow(char top,char left,char bot,char right, char page,char fillchar, char fillattr);
+void clearwindow(char top, char left, char bot, char right, 
+                char page, char fillchar, char fillattr);
 
 void cls(void);                        /* Clears the entire current screen page */
 
@@ -37,6 +43,9 @@ void getpos(char * row, char * col, char page);
 
 char inputc(char * scancode); // Return ASCII char by val, and scancode by reference
 
+
+void putch(char x, char attr, char page); // Print one char
+
 void cursoroff(void);          /* Turns on cursor */
 
 void cursoron(void);           /* Turns off cursor */
@@ -63,6 +72,8 @@ static inline char getnumcols(void)
 {
     return readbiosb(0x44a);
 }
+
+void scrollup(); //Scroll up display screen by one line
  
 void setvideomode(char mode); // Set the video mode.
 
index aa8828e..6c099b8 100644 (file)
@@ -23,9 +23,9 @@
 char infoline[160];
 
 // Different network options
-static char nonet[] = "network [none]";
-static char dhcpnet[]="network [dhcp]";
-static char statnet[]="network [static]";
+static char nonet[] = "<n>etwork [none]";
+static char dhcpnet[]="<n>etwork [dhcp]";
+static char statnet[]="<n>etwork [static]";
 
 struct {
     unsigned int baseurl : 1; // Do we need to specify by url
@@ -42,6 +42,14 @@ char TESTING,RESCUE,MAIN,PREP,NETMENU;
 
 /* End globals */
 
+
+TIMEOUTCODE ontimeout()
+{
+  beep();
+  return CODE_WAIT;
+}
+
+
 #define INFLINE 22
 
 void msys_handler(t_menusystem *ms, t_menuitem *mi)
@@ -165,34 +173,36 @@ int menumain(char *cmdline)
 
   // Register the menusystem handler
   reg_handler(&msys_handler);
+  // Register the ontimeout handler, with a time out of 10 seconds
+  reg_ontimeout(ontimeout,1000,0);
 
   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);
+  none = add_item("<N>one","Dont start network",OPT_RADIOITEM,"no ",0);
+  dhcp = add_item("<d>hcp","Use DHCP",OPT_RADIOITEM,"dhcp ",0);
+  stat = add_item("<s>tatic","Use static IP I will specify later",OPT_RADIOITEM,"static ",0);
 
   TESTING = add_menu(" Testing ");
   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);
+  add_item("<M>emory Test","Perform extensive memory testing",OPT_RUN, "memtest",0);
+  add_item("<I>nvisible","You dont see this",OPT_INVISIBLE,"junk",0);
+  add_item("<E>xit this menu","Go one level up",OPT_EXITMENU,"exit",0);
 
   RESCUE = add_menu(" Rescue Options ");
-  add_item("Linux Rescue","linresc",OPT_RUN,"linresc",0);
-  add_item("Dos Rescue","dosresc",OPT_RUN,"dosresc",0);
-  add_item("Windows Rescue","winresc",OPT_RUN,"winresc",0);
-  add_item("Exit this menu","Go one level up",OPT_EXITMENU,"exit",0);
+  add_item("<L>inux Rescue","linresc",OPT_RUN,"linresc",0);
+  add_item("<D>os Rescue","dosresc",OPT_RUN,"dosresc",0);
+  add_item("<W>indows Rescue","winresc",OPT_RUN,"winresc",0);
+  add_item("<E>xit this menu","Go one level up",OPT_EXITMENU,"exit",0);
 
   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);
+  baseurl = add_item("<b>aseurl by IP?","Specify gui baseurl by IP address",OPT_CHECKBOX,"baseurl",0);
+  mountcd = add_item("<m>ountcd?","Mount the cdrom drive?",OPT_CHECKBOX,"mountcd",0);
   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);
+  winrep  = add_item("Reinstall <w>indows","Re-install the windows side of a dual boot setup",OPT_CHECKBOX,"winrepair",0);
+  linrep  = add_item("Reinstall <l>inux","Re-install the linux side of a dual boot setup",OPT_CHECKBOX,"linrepair",0);
   add_sep();
-  runprep = add_item("Run prep now","Execute prep with the above options",OPT_RUN,"prep",0);
-  add_item("Exit this menu","Go up one level",OPT_EXITMENU,"exitmenu",0);
+  runprep = add_item("<R>un prep now","Execute prep with the above options",OPT_RUN,"prep",0);
+  add_item("<E>xit this menu","Go up one level",OPT_EXITMENU,"exitmenu",0);
   baseurl->handler = &checkbox_handler;
   mountcd->handler = &checkbox_handler;
   winrep->handler = &checkbox_handler;
@@ -204,14 +214,14 @@ int menumain(char *cmdline)
   flags.linrep = 0;
 
   MAIN = add_menu(" Main Menu ");  
-  add_item("Prepare","prep",OPT_RUN,"prep",0);
-  add_item("Prep options...","Options for prep image",OPT_SUBMENU,NULL,PREP);
-  add_item("Rescue options...","Troubleshoot a system",OPT_SUBMENU,NULL,RESCUE);
-  add_item("Testing...","Options to test hardware",OPT_SUBMENU,NULL,TESTING);
-  add_item("Exit to prompt", "Exit the menu system", OPT_EXITMENU, "exit", 0);
+  add_item("<P>repare","prep",OPT_RUN,"prep",0);
+  add_item("<P>rep options...","Options for prep image",OPT_SUBMENU,NULL,PREP);
+  add_item("<R>escue options...","Troubleshoot a system",OPT_SUBMENU,NULL,RESCUE);
+  add_item("<T>esting...","Options to test hardware",OPT_SUBMENU,NULL,TESTING);
+  add_item("<E>xit 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
+  if (!checkkeypress(100,50)) // Granularity of 100 milliseconds
     {
       csprint("Sorry! Time's up.\r\n",0x07);
       return 1;
@@ -220,7 +230,6 @@ int menumain(char *cmdline)
   curr = showmenus(MAIN);
   if (curr)
   {
-        if (curr->action == OPT_EXIT) return 0;
         if (curr->action == OPT_RUN)
         {
             strcpy(cmd,curr->data);
@@ -238,9 +247,16 @@ int menumain(char *cmdline)
             if (syslinux)
                runcommand(cmd);
             else csprint(cmd,0x07);
-            return 1;
+            return 1; // Should not happen when run from SYSLINUX
         }
   }
+  // If user quits the menu system, control comes here
+  // If you want to execute some specific command uncomment the next two lines
+
+  // if (syslinux) runcommand(YOUR_COMMAND_HERE);
+  // else csprint(YOUR_COMMAND_HERE,0x07);
+
+  // Return to prompt
   return 0;
 }
 
index deb3805..bec6872 100644 (file)
@@ -32,6 +32,88 @@ static char EMPTYSTR[] = "";
 
 /* Basic Menu routines */
 
+// This is same as inputc except it honors the ontimeout handler
+// and calls it when needed. For the callee, there is no difference
+// as this will not return unless a key has been pressed.
+char getch(char *scan)
+{
+  unsigned long i;
+  TIMEOUTCODE c;
+
+  // Wait until keypress if no handler specified
+  if (ms->ontimeout==NULL) return inputc(scan);
+
+  while (1) // Forever do
+    {
+      for (i=0; i < ms->tm_numsteps; i++)
+       {
+         if (checkkbdbuf()) return inputc(scan);
+         sleep(ms->tm_stepsize);
+       }
+      c = ms->ontimeout();
+      switch(c)
+       {
+       case CODE_ENTER: // Pretend user hit enter
+         *scan = ENTERA;
+         return '\015'; // \015 octal = 13
+       case CODE_ESCAPE: // Pretend user hit escape
+         *scan = ESCAPE;
+         return '\033'; // \033 octal = 27
+       default:
+         break;
+       }
+    }
+  return 0;
+}
+
+/* Print a menu item */
+/* attr[0] is non-hilite attr, attr[1] is highlight attr */
+void printmenuitem(const char *str,char* attr)
+{
+    char page = getdisppage();
+    char row,col;
+    int hlite=NOHLITE; // Initially no highlighting
+
+    getpos(&row,&col,page);
+    while ( *str ) {
+      switch (*str) 
+       {
+       case '\b':
+         --col;
+         break;
+       case '\n':
+         ++row;
+         break;
+       case '\r':
+         col=0;
+         break;
+       case BELL: // No Bell Char
+         break;
+       case ENABLEHLITE: // Switch on highlighting
+         hlite = HLITE;
+         break;
+       case DISABLEHLITE: // Turn off highlighting
+         hlite = NOHLITE;
+         break;
+       default:
+         putch(*str, attr[hlite], page);
+         ++col;
+       }
+      if (col > getnumcols())
+       {
+         ++row;
+         col=0;
+       }
+      if (row > getnumrows())
+       {
+         scrollup();
+         row= getnumrows();
+       }
+      gotoxy(row,col,page);
+      str++;
+    }
+}
+
 void drawbox(char top, char left, char bot, char right,char attr, char page)
 {
   char x;
@@ -86,13 +168,44 @@ int prev_visible(pt_menu menu, int index) // Return index of next visible
   return ans;
 }
 
+int find_shortcut(pt_menu menu,char shortcut, int index) 
+// Find the next index with specified shortcut key
+{
+  int ans;
+  pt_menuitem mi;
+
+  // Garbage in garbage out
+  if ((index <0) || (index >= menu->numitems)) return index; 
+  ans = index+1;
+  // Go till end of menu
+  while (ans < menu->numitems)  
+    {
+      mi = menu->items[ans];
+      if ((mi->action == OPT_INVISIBLE) || (mi->action == OPT_SEP)
+         || (mi->shortcut != shortcut))
+       ans ++;
+      else return ans;
+    }
+  // Start at the beginning and try again
+  ans = 0;
+  while (ans < index)
+    {
+      mi = menu->items[ans];
+      if ((mi->action == OPT_INVISIBLE) || (mi->action == OPT_SEP)
+         || (mi->shortcut != shortcut))
+       ans ++;
+      else return ans;
+    }
+  return index; // Sorry not found
+}
+
 void printmenu(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 *attr;  // attribute attr
   char sep[MENULEN];// and inbetween the item or a seperator is printed
   pt_menuitem ci;
   
@@ -102,13 +215,13 @@ void printmenu(pt_menu menu, int curr, char top, char left)
   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);
+         ms->normalattr[NOHLITE], 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);
+  printmenuitem(menu->title,ms->normalattr);
   row = -1; // 1 less than inital value of x
   for (x=0; x < menu->numitems; x++)
     {
@@ -137,7 +250,7 @@ void printmenu(pt_menu menu, int curr, char top, char left)
          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;
+         lchar[0] = HORIZ; lchar[1] = RTLT; lchar[2] = 0;
          str = sep;
          break;
        case OPT_EXITMENU:
@@ -147,13 +260,13 @@ void printmenu(pt_menu menu, int curr, char top, char left)
          break;
         }
       gotoxy(top+row,left-2,ms->menupage);
-      cprint(ms->spacechar,attr,menuwidth+2,ms->menupage); // Wipe area with spaces
+      cprint(ms->spacechar,attr[NOHLITE],menuwidth+2,ms->menupage); // Wipe area with spaces
       gotoxy(top+row,left-2,ms->menupage);
-      csprint(fchar,attr); // Print first part
+      csprint(fchar,attr[NOHLITE]); // Print first part
       gotoxy(top+row,left,ms->menupage);
-      csprint(str,attr); // Print main part
+      printmenuitem(str,attr); // Print main part
       gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any
-      csprint(lchar,attr); // Print last part
+      csprint(lchar,attr[NOHLITE]); // Print last part
     }
   if (ms->handler) ms->handler(ms,menu->items[curr]);
 }
@@ -166,7 +279,7 @@ void printradiomenu(pt_menu menu, int curr, char top, char left)
   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 *attr;  // all in the attribute attr
   char sep[MENULEN];// and inbetween the item or a seperator is printed
   pt_menuitem ci;
   
@@ -176,13 +289,13 @@ void printradiomenu(pt_menu menu, int curr, char top, char left)
   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);
+         ms->normalattr[NOHLITE], 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);
+  printmenuitem(menu->title,ms->normalattr);
   row = -1; // 1 less than inital value of x
   for (x=0; x < menu->numitems; x++)
     {
@@ -209,13 +322,13 @@ void printradiomenu(pt_menu menu, int curr, char top, char left)
          break;
         }
       gotoxy(top+row,left-2,ms->menupage);
-      cprint(ms->spacechar,attr,menuwidth+2,ms->menupage); // Wipe area with spaces
+      cprint(ms->spacechar,attr[NOHLITE],menuwidth+2,ms->menupage); // Wipe area with spaces
       gotoxy(top+row,left-2,ms->menupage);
-      csprint(fchar,attr); // Print first part
+      csprint(fchar,attr[NOHLITE]); // Print first part
       gotoxy(top+row,left,ms->menupage);
-      csprint(str,attr); // Print main part
+      printmenuitem(str,attr); // Print main part
       gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any
-      csprint(lchar,attr); // Print last part
+      csprint(lchar,attr[NOHLITE]); // Print last part
     }
   if (ms->handler) ms->handler(ms,menu->items[curr]);
 }
@@ -241,20 +354,21 @@ pt_menuitem getradiooption(pt_menu menu, char top, char left, char startopt)
   numitems = menu->numvisible;
   // Setup status line
   gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
-  cprint(ms->spacechar,ms->reverseattr,ms->numcols,ms->menupage);
+  cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
 
   // Initialise current menu item
   curr = next_visible(menu,startopt);
 
   gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
-  cprint(ms->spacechar,ms->statusattr,ms->numcols,1);
+  cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,1);
   gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
-  csprint(menu->items[curr]->status,ms->statusattr);
+  printmenuitem(menu->items[curr]->status,ms->statusattr);
   while (1) // Forever
     {
       printradiomenu(menu,curr,top,left);
       ci = menu->items[curr];
-      asc = inputc(&scan);
+      
+      asc = getch(&scan);
       switch (scan)
         {
        case HOMEKEY:
@@ -286,14 +400,18 @@ pt_menuitem getradiooption(pt_menu menu, char top, char left, char startopt)
          if (ci->action == OPT_SEP) break;
          return ci;
          break;
+       default:
+         // Check if this is a shortcut key
+         if (((asc >= 'A') && (asc <= 'Z')) ||
+             ((asc >= 'a') && (asc <= 'z')) ||
+             ((asc >= '0') && (asc <= '9')))
+           curr = find_shortcut(menu,asc,curr);
+         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);
+      cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
+      printmenuitem(menu->items[curr]->status,ms->statusattr);
     }
   return NULL; // Should never come here
 }
@@ -311,20 +429,20 @@ pt_menuitem getmenuoption( pt_menu menu, char top, char left, char startopt)
   numitems = menu->numvisible;
   // Setup status line
   gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
-  cprint(ms->spacechar,ms->reverseattr,ms->numcols,ms->menupage);
+  cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
 
   // Initialise current menu item    
   curr = next_visible(menu,startopt);
 
   gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
-  cprint(ms->spacechar,ms->statusattr,ms->numcols,1);
+  cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,1);
   gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
-  csprint(menu->items[curr]->status,ms->statusattr);
+  printmenuitem(menu->items[curr]->status,ms->statusattr);
   while (1) // Forever
     {
       printmenu(menu,curr,top,left);
       ci = menu->items[curr];
-      asc = inputc(&scan);
+      asc = getch(&scan);
       switch (scan)
         {
        case HOMEKEY:
@@ -364,14 +482,18 @@ pt_menuitem getmenuoption( pt_menu menu, char top, char left, char startopt)
          // Call handler to see it anything needs to be done
          if (ci->handler != NULL) ci->handler(ms,ci); 
          break;
+       default:
+         // Check if this is a shortcut key
+         if (((asc >= 'A') && (asc <= 'Z')) ||
+             ((asc >= 'a') && (asc <= 'z')) ||
+             ((asc >= '0') && (asc <= '9')))
+           curr = find_shortcut(menu,asc,curr);
+         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);
+      cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
+      printmenuitem(menu->items[curr]->status,ms->statusattr);
     }
   return NULL; // Should never come here
 }
@@ -507,12 +629,25 @@ void init_menusystem(const char *title)
     ms->title = TITLESTR; // Copy pointers
   else ms->title = title;
 
-  ms->normalattr = NORMALATTR; 
-  ms->reverseattr= REVERSEATTR;
-  ms->inactattr = INACTATTR;
-  ms->revinactattr = REVINACTATTR;
+  // Timeout settings
+  ms->tm_stepsize = TIMEOUTSTEPSIZE;
+  ms->tm_numsteps = TIMEOUTNUMSTEPS;
+
+  ms->normalattr[NOHLITE] = NORMALATTR; 
+  ms->normalattr[HLITE] = NORMALHLITE;
+
+  ms->reverseattr[NOHLITE] = REVERSEATTR;
+  ms->reverseattr[HLITE] = REVERSEHLITE;
+
+  ms->inactattr[NOHLITE] = INACTATTR;
+  ms->inactattr[HLITE] = INACTHLITE;
+
+  ms->revinactattr[NOHLITE] = REVINACTATTR;
+  ms->revinactattr[HLITE] = REVINACTHLITE;
+
+  ms->statusattr[NOHLITE] = STATUSATTR;
+  ms->statusattr[HLITE] = STATUSHLITE;
 
-  ms->statusattr = STATUSATTR;
   ms->statline = STATLINE;
   ms->tfillchar= TFILLCHAR;
   ms->titleattr= TITLEATTR;
@@ -524,6 +659,7 @@ void init_menusystem(const char *title)
 
   ms->menupage = MENUPAGE; // Usually no need to change this at all
   ms->handler = NULL; // No handler function
+  ms->ontimeout=NULL; // No timeout handler
 
   // Figure out the size of the screen we are in now.
   // By default we use the whole screen for our menu
@@ -536,15 +672,25 @@ void init_menusystem(const char *title)
 
 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[0]   = normal;
+  if (selected != 0xFF)         ms->reverseattr[0]  = selected;
+  if (inactivenormal != 0xFF)   ms->inactattr[0]    = inactivenormal;
+  if (inactiveselected != 0xFF) ms->revinactattr[0] = inactiveselected;
 }
 
-void set_status_info(char statusattr, char statline)
+void set_normal_hlite(char normal, char selected, char inactivenormal, char inactiveselected)
 {
-  if (statusattr != 0xFF) ms->statusattr = statusattr;
+  if (normal != 0xFF)           ms->normalattr[1]   = normal;
+  if (selected != 0xFF)         ms->reverseattr[1]  = selected;
+  if (inactivenormal != 0xFF)   ms->inactattr[1]    = inactivenormal;
+  if (inactiveselected != 0xFF) ms->revinactattr[1] = inactiveselected;
+}
+
+
+void set_status_info(char statusattr, char statushlite, char statline)
+{
+  if (statusattr != 0xFF) ms->statusattr[NOHLITE] = statusattr;
+  if (statushlite!= 0xFF) ms->statusattr[HLITE] = statushlite;
   // statline is relative to minrow
   if (statline >= ms->numrows) statline = ms->numrows - 1;
   ms->statline = statline; // relative to ms->minrow, 0 based
@@ -592,6 +738,19 @@ void unreg_handler()
   ms->handler = NULL;
 }
 
+void reg_ontimeout(t_timeout_handler handler, unsigned int numsteps, unsigned int stepsize)
+{
+  ms->ontimeout = handler;
+  if (numsteps != 0) ms->tm_numsteps = numsteps;
+  if (stepsize != 0) ms->tm_stepsize = stepsize;
+}
+
+void unreg_ontimeout()
+{
+  ms->ontimeout = NULL;
+}
+
+
 void calc_visible(pt_menu menu)
 {
   int ans,i;
@@ -655,6 +814,7 @@ pt_menuitem add_sep() // Add a separator to current menu
   mi->action = OPT_SEP;
   mi->index = m->numitems++;
   mi->parindex = ms->nummenus-1;
+  mi->shortcut = 0;
   return mi;
 }
 
@@ -664,6 +824,8 @@ pt_menuitem add_item(const char *item, const char *status, t_action action,
 {
   pt_menuitem mi;
   pt_menu m;
+  const char *str;
+  char inhlite=0; // Are we inside hlite area
 
   m = (ms->menus[ms->nummenus-1]);
   mi = NULL;
@@ -689,6 +851,32 @@ pt_menuitem add_item(const char *item, const char *status, t_action action,
   } else mi->status = EMPTYSTR; 
     
   mi->action = action;
+  str = mi->item;
+  mi->shortcut = 0;
+  inhlite = 0; // We have not yet seen an ENABLEHLITE char
+  // Find the first char in [A-Za-z0-9] after ENABLEHLITE and not arg to control char
+  while (*str)
+    {
+      if (*str == ENABLEHLITE) 
+       {
+         inhlite=1;
+       }
+      if (*str == DISABLEHLITE)
+       {
+         inhlite = 0;
+       }
+      if ( (inhlite == 1) && 
+          (((*str >= 'A') && (*str <= 'Z')) || 
+           ((*str >= 'a') && (*str <= 'z')) ||
+           ((*str >= '0') && (*str <= '9'))))
+       {
+         mi->shortcut=*str;
+         break;
+       }
+      ++str;
+    }
+  if ((mi->shortcut >= 'A') && (mi->shortcut <= 'Z')) // Make lower case
+    mi->shortcut = mi->shortcut -'A'+'a';
 
   if (data) {
     if (strlen(data) > ACTIONLEN - 2) {
@@ -696,7 +884,7 @@ pt_menuitem add_item(const char *item, const char *status, t_action action,
     } else {
       mi->data = data; 
     }
-  } else mi->data = EMPTYSTR; 
+  } else mi->data = EMPTYSTR;
 
   switch (action)
     {
@@ -717,3 +905,15 @@ pt_menuitem add_item(const char *item, const char *status, t_action action,
   mi->parindex = ms->nummenus-1;
   return mi;
 }
+
+// Set the shortcut key for the current item
+void set_shortcut(char shortcut)
+{
+  pt_menuitem mi;
+  pt_menu m;
+
+  m = (ms->menus[ms->nummenus-1]); 
+  if (m->numitems <= 0) return;
+  mi = m->items[(unsigned int) m->numitems-1];
+  mi->shortcut = shortcut;
+}
index e1564bd..070e745 100644 (file)
 #include "biosio.h"
 #include "string.h"
 
+// TIMEOUT PARAMETERS
+/* If no key is pressed within TIMEOUTNUMSTEPS * TIMEOUTSTEPSIZE milliseconds
+   and if a timeout handler is registered, then that will be called.
+   The handler should either either take control from there on, or return without
+   producing any change in the current video settings.
+
+   For e.g. the handler could
+   * Could just quit the menu program
+   * beep and return.
+
+   TIMEOUTSTEPSIZE is the interval for which the program sleeps without checking for 
+   any keystroke. So increasing this will make the response of the system slow.
+   Decreasing this will make a lot of interrupt calls using up your CPU. Default
+   value of TIMEOUTSTEPSIZE of 0.1 seconds should be right in most cases.
+
+   TIMEOUTNUMSTEPS of 3000 corresponds to a wait time of 300 seconds or 5 minutes
+*/
+
+#define TIMEOUTSTEPSIZE 10  
+#define TIMEOUTNUMSTEPS 30000L
+
 // Scancodes of some keys
 #define ESCAPE     1
 #define ENTERA    28
 #define SPACEKEY 57 // Scan code for SPACE
 
 // Attributes
-#define NORMALATTR   0x17
-#define REVERSEATTR  0x70
-#define INACTATTR    0x18
-#define REVINACTATTR 0x78
-#define STATUSATTR   0x74
-#define FILLCHAR     177
-#define FILLATTR     0x01
-#define SHADOWATTR   0x00
-#define SPACECHAR    ' '
-
-#define TFILLCHAR    ' '
-#define TITLEATTR    0x70
+#define NORMALATTR    0x17
+#define NORMALHLITE   0x1F // Normal Highlight attribute
+#define REVERSEATTR   0x70
+#define REVERSEHLITE  0x78 // Reverse Hightlight attribute
+#define INACTATTR     0x18
+#define INACTHLITE    0x10 // Inactive Highlight attribute
+#define REVINACTATTR  0x78
+#define REVINACTHLITE 0x70 // Reverse Inactive Highlight attr
+
+#define STATUSATTR    0x74
+#define STATUSHLITE   0x7B // Status highlight
+
+#define FILLCHAR      177
+#define FILLATTR      0x01
+#define SHADOWATTR    0x00
+#define SPACECHAR     ' '
+
+#define TFILLCHAR     ' '
+#define TITLEATTR     0x70
+
+#define ENABLEHLITE   '<' // Char which turns on highlight
+#define DISABLEHLITE  '>' // Char which turns off highlight
+#define NOHLITE       0   // The offset into attrib array for non-hilite
+#define HLITE         1   // The offset for Hlite attrib
 
 // Single line Box drawing Chars
 
 #define RADIOMENU  2
 
 typedef enum {OPT_INACTIVE, OPT_SUBMENU, OPT_RUN, OPT_EXITMENU, OPT_CHECKBOX,
-             OPT_RADIOMENU, OPT_EXIT, OPT_SEP, OPT_INVISIBLE,
+             OPT_RADIOMENU, OPT_SEP, OPT_INVISIBLE,
              OPT_RADIOITEM} t_action;
 
 typedef union {
@@ -128,6 +161,11 @@ struct s_menusystem;
 typedef void (*t_item_handler)(struct s_menusystem *, struct s_menuitem *);
 typedef void (*t_menusystem_handler)(struct s_menusystem *, struct s_menuitem *);
 
+// TIMEOUT is the list of possible values which can be returned by the handler
+// instructing the menusystem what to do. The default is CODE_WAIT
+typedef enum {CODE_WAIT, CODE_ENTER, CODE_ESCAPE } TIMEOUTCODE;
+typedef TIMEOUTCODE (*t_timeout_handler)(void);
+
 typedef struct s_menuitem {
   const char *item;
   const char *status;
@@ -137,6 +175,7 @@ typedef struct s_menuitem {
   t_item_handler handler; // Pointer to function of type menufn
   t_action action;
   t_itemdata itemdata; // Data depends on action value
+  char shortcut; // one of [A-Za-z0-9] shortcut for this menu item
   char index; // Index within the menu array
   char parindex; // Index of the menu in which this item appears. 
 } t_menuitem;
@@ -158,12 +197,16 @@ 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.
+  t_timeout_handler ontimeout; // Timeout handler
+  unsigned int tm_stepsize; // Timeout step size (in milliseconds)
+  unsigned long tm_numsteps; // Time to wait for key press=numsteps * stepsize milliseconds
+
   char nummenus;
-  char normalattr
-  char reverseattr;
-  char inactattr;
-  char revinactattr;
-  char statusattr;
+  char normalattr[2]; // [0] is non-hlite attr, [1] is hlite attr
+  char reverseattr[2];
+  char inactattr[2];
+  char revinactattr[2];
+  char statusattr[2];
   char fillchar;
   char fillattr;
   char spacechar;
@@ -200,7 +243,9 @@ void init_menusystem(const char *title); // This pointer value is stored interna
 
 void set_normal_attr(char normal, char selected, char inactivenormal, char inactiveselected);
 
-void set_status_info(char statusattr, char statline);
+void set_normal_hlite(char normal, char selected, char inactivenormal, char inactiveselected);
+
+void set_status_info(char statusattr, char statushlite, char statline);
 
 void set_title_info(char tfillchar, char titleattr);
 
@@ -212,13 +257,20 @@ void reg_handler( t_menusystem_handler handler); // Register handler
 
 void unreg_handler(); 
 
+void reg_ontimeout(t_timeout_handler, unsigned int numsteps, unsigned int stepsize); 
+// Set timeout handler, set 0 for default values.
+// So stepsize=0 means numsteps is measured in centiseconds.
+void unreg_ontimeout();
+
 // Create a new menu and return its position
 char add_menu(const char *title); // This pointer value is stored internally
 
+void set_menu_pos(char row,char col); // Set the position of this menu.
+
 // Add item to the "current" menu // pointer values are stored internally
 pt_menuitem add_item(const char *item, const char *status, t_action action, const char *data, char itemdata);
 
-void set_menu_pos(char row,char col); // Set the position of this menu.
+void set_shortcut(char shortcut); // Set the shortcut key for the current item
 
 // Add a separator to the "current" menu
 pt_menuitem add_sep();
index 86c7007..61748dd 100644 (file)
@@ -68,7 +68,6 @@ int menumain(char *cmdline)
   curr = showmenus(MAIN); // Initial menu is the one with index MAIN
   if (curr)
   {
-        if (curr->action == OPT_EXIT) return 0;
         if (curr->action == OPT_RUN)
         {
             if (syslinux) runcommand(curr->data);