From 87b3f74737b0bd776643cc6b2bf5b22f083fdf48 Mon Sep 17 00:00:00 2001 From: hpa Date: Thu, 17 Jun 2004 15:14:28 +0000 Subject: [PATCH] New menu highlight/hotkey interface. --- menu/README | 25 ++++- menu/biosio.c | 32 ++++++- menu/biosio.h | 13 ++- menu/complex.c | 70 ++++++++------ menu/menu.c | 290 ++++++++++++++++++++++++++++++++++++++++++++++++--------- menu/menu.h | 92 ++++++++++++++---- menu/simple.c | 1 - 7 files changed, 421 insertions(+), 102 deletions(-) diff --git a/menu/README b/menu/README index 05917f6..dd22142 100644 --- a/menu/README +++ b/menu/README @@ -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 ------- diff --git a/menu/biosio.c b/menu/biosio.c index 55cdd01..d1fa38f 100644 --- a/menu/biosio.c +++ b/menu/biosio.c @@ -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); diff --git a/menu/biosio.h b/menu/biosio.h index c17cc8f..ebba214 100644 --- a/menu/biosio.h +++ b/menu/biosio.h @@ -17,6 +17,11 @@ #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. diff --git a/menu/complex.c b/menu/complex.c index aa8828e..6c099b8 100644 --- a/menu/complex.c +++ b/menu/complex.c @@ -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[] = "etwork [none]"; +static char dhcpnet[]="etwork [dhcp]"; +static char statnet[]="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("one","Dont start network",OPT_RADIOITEM,"no ",0); + dhcp = add_item("hcp","Use DHCP",OPT_RADIOITEM,"dhcp ",0); + stat = add_item("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("emory Test","Perform extensive memory testing",OPT_RUN, "memtest",0); + add_item("nvisible","You dont see this",OPT_INVISIBLE,"junk",0); + add_item("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("inux Rescue","linresc",OPT_RUN,"linresc",0); + add_item("os Rescue","dosresc",OPT_RUN,"dosresc",0); + add_item("indows Rescue","winresc",OPT_RUN,"winresc",0); + add_item("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("aseurl by IP?","Specify gui baseurl by IP address",OPT_CHECKBOX,"baseurl",0); + mountcd = add_item("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 indows","Re-install the windows side of a dual boot setup",OPT_CHECKBOX,"winrepair",0); + linrep = add_item("Reinstall 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("un prep now","Execute prep with the above options",OPT_RUN,"prep",0); + add_item("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("

repare","prep",OPT_RUN,"prep",0); + add_item("

rep options...","Options for prep image",OPT_SUBMENU,NULL,PREP); + add_item("escue options...","Troubleshoot a system",OPT_SUBMENU,NULL,RESCUE); + add_item("esting...","Options to test hardware",OPT_SUBMENU,NULL,TESTING); + add_item("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; } diff --git a/menu/menu.c b/menu/menu.c index deb3805..bec6872 100644 --- a/menu/menu.c +++ b/menu/menu.c @@ -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; +} diff --git a/menu/menu.h b/menu/menu.h index e1564bd..070e745 100644 --- a/menu/menu.h +++ b/menu/menu.h @@ -22,6 +22,27 @@ #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 @@ -38,18 +59,30 @@ #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 @@ -112,7 +145,7 @@ #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(); diff --git a/menu/simple.c b/menu/simple.c index 86c7007..61748dd 100644 --- a/menu/simple.c +++ b/menu/simple.c @@ -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); -- 2.7.4