/* -*- c -*- ------------------------------------------------------------- * * * Copyright 2004 Murali Krishnan Ganapathy - All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, Inc., 53 Temple Place Ste 330, * Boston MA 02111-1307, USA; either version 2 of the License, or * (at your option) any later version; incorporated herein by reference. * * ----------------------------------------------------------------------- */ /* This program can be compiled for DOS with the OpenWatcom compiler * (http://www.openwatcom.org/): * * wcl -3 -osx -mt getargs.c */ #include "biosio.h" #include "string.h" #include "menu.h" #include "heap.h" // Local Variables static pt_menusystem ms; // Pointer to the menusystem static char TITLESTR[] = "COMBOOT Menu System for SYSLINUX developed by Murali Krishnan Ganapathy"; static char TITLELONG[] = " TITLE too long "; static char ITEMLONG[] = " ITEM too long "; static char ACTIONLONG[] = " ACTION too long "; static char STATUSLONG[] = " STATUS too long "; 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; // 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); } } 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; } 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; // 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[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); printmenuitem(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[2] = 0; str = sep; break; case OPT_EXITMENU: fchar[0] = EXITMENUCHAR; fchar[1] = 0; break; default: // Just to keep the compiler happy break; } gotoxy(top+row,left-2,ms->menupage); cprint(ms->spacechar,attr[NOHLITE],menuwidth+2,ms->menupage); // Wipe area with spaces gotoxy(top+row,left-2,ms->menupage); csprint(fchar,attr[NOHLITE]); // Print first part gotoxy(top+row,left,ms->menupage); printmenuitem(str,attr); // Print main part gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any csprint(lchar,attr[NOHLITE]); // Print last part } 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[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); printmenuitem(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 fchar[0] = RADIOUNSEL; fchar[1]='\0'; // Unselected ( ) lchar[0] = '\0'; // Nothing special after str = ci->item; // Pointer to item string attr = ms->normalattr; // Always same attribute fchar[0] = (x==curr ? RADIOSEL : RADIOUNSEL); switch (ci->action) // set up attr,str,fchar,lchar for everything { case OPT_INACTIVE: attr = ms->inactattr; break; case OPT_SEP: fchar[0] = '\b'; fchar[1] = 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+row,left-2,ms->menupage); cprint(ms->spacechar,attr[NOHLITE],menuwidth+2,ms->menupage); // Wipe area with spaces gotoxy(top+row,left-2,ms->menupage); csprint(fchar,attr[NOHLITE]); // Print first part gotoxy(top+row,left,ms->menupage); printmenuitem(str,attr); // Print main part gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any csprint(lchar,attr[NOHLITE]); // Print last part } if (ms->handler) ms->handler(ms,menu->items[curr]); } void cleanupmenu(pt_menu menu, char top,char left) { 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->statusattr[NOHLITE],ms->numcols,ms->menupage); // Initialise current menu item curr = next_visible(menu,startopt); gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,1); gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); printmenuitem(menu->items[curr]->status,ms->statusattr); while (1) // Forever { printradiomenu(menu,curr,top,left); ci = menu->items[curr]; asc = getch(&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; 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; } // Update status line gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage); printmenuitem(menu->items[curr]->status,ms->statusattr); } return NULL; // Should never come here } /* Handle one menu */ pt_menuitem getmenuoption( pt_menu menu, 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->statusattr[NOHLITE],ms->numcols,ms->menupage); // Initialise current menu item curr = next_visible(menu,startopt); gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,1); gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); printmenuitem(menu->items[curr]->status,ms->statusattr); while (1) // Forever { printmenu(menu,curr,top,left); ci = menu->items[curr]; asc = getch(&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_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; 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; } // Update status line gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage); printmenuitem(menu->items[curr]->status,ms->statusattr); } return NULL; // Should never come here } /* Handle the entire system of menu's. */ pt_menuitem runmenusystem(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; char startat,mt; char row,col; if (cmenu == NULL) return NULL; startover: if (menutype == NORMALMENU) opt = getmenuoption(cmenu,top,left,startopt); else // menutype == RADIOMENU opt = getradiooption(cmenu,top,left,startopt); if (opt == NULL) { // User hit Esc cleanupmenu(cmenu,top,left); return NULL; } // 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 } // Either radiomenu or submenu // Do we have a valid menu number? The next hack uses the fact that // itemdata.submenunum = itemdata.radiomenunum (since enum data type) if (opt->itemdata.submenunum >= ms->nummenus) // This is Bad.... { gotoxy(12,12,ms->menupage); // Middle of screen csprint("ERROR: Invalid submenu requested.",0x07); cleanupmenu(cmenu,top,left); return NULL; // Pretend user hit esc } // Call recursively for submenu // Position the submenu below the current item, // covering half the current window (horizontally) row = ms->menus[(unsigned int)opt->itemdata.submenunum]->row; col = ms->menus[(unsigned int)opt->itemdata.submenunum]->col; if (row == 0xFF) row = top+opt->index+2; if (col == 0xFF) col = left+3+(cmenu->menuwidth >> 1); mt = (opt->action == OPT_SUBMENU ? NORMALMENU : RADIOMENU ); startat = 0; if ((opt->action == OPT_RADIOMENU) && (opt->data != NULL)) startat = ((t_menuitem *)opt->data)->index; choice = runmenusystem(row, col, ms->menus[(unsigned int)opt->itemdata.submenunum], startat, mt ); if (opt->action == OPT_RADIOMENU) { if (choice != NULL) opt->data = (void *)choice; // store choice in data field if (opt->handler != NULL) opt->handler(ms,opt); // Call handler choice = NULL; // Pretend user hit esc } if (choice==NULL) // User hit Esc in submenu { // Startover startopt = opt->index; goto startover; } else { cleanupmenu(cmenu,top,left); return choice; } } /* User Callable functions */ 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; // 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) { 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; if (title == NULL) ms->title = TITLESTR; // Copy pointers else ms->title = title; // Timeout settings ms->tm_stepsize = TIMEOUTSTEPSIZE; ms->tm_numsteps = TIMEOUTNUMSTEPS; ms->normalattr[NOHLITE] = NORMALATTR; ms->normalattr[HLITE] = NORMALHLITE; ms->reverseattr[NOHLITE] = REVERSEATTR; ms->reverseattr[HLITE] = REVERSEHLITE; ms->inactattr[NOHLITE] = INACTATTR; ms->inactattr[HLITE] = INACTHLITE; ms->revinactattr[NOHLITE] = REVINACTATTR; ms->revinactattr[HLITE] = REVINACTHLITE; ms->statusattr[NOHLITE] = STATUSATTR; ms->statusattr[HLITE] = STATUSHLITE; ms->statline = STATLINE; ms->tfillchar= TFILLCHAR; ms->titleattr= TITLEATTR; ms->fillchar = FILLCHAR; ms->fillattr = FILLATTR; ms->spacechar= SPACECHAR; ms->shadowattr = SHADOWATTR; ms->menupage = MENUPAGE; // Usually no need to change this at all 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 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[0] = normal; if (selected != 0xFF) ms->reverseattr[0] = selected; if (inactivenormal != 0xFF) ms->inactattr[0] = inactivenormal; if (inactiveselected != 0xFF) ms->revinactattr[0] = inactiveselected; } void set_normal_hlite(char normal, char selected, char inactivenormal, char inactiveselected) { if (normal != 0xFF) ms->normalattr[1] = normal; if (selected != 0xFF) ms->reverseattr[1] = selected; if (inactivenormal != 0xFF) ms->inactattr[1] = inactivenormal; if (inactiveselected != 0xFF) ms->revinactattr[1] = inactiveselected; } void set_status_info(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 } void set_title_info(char tfillchar, char 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; } 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 } void reg_handler( t_menusystem_handler handler) { ms->handler = handler; } 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; 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 { 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; } void set_menu_pos(char row,char col) // Set the position of this menu. { pt_menu m; m = ms->menus[ms->nummenus-1]; m->row = row; m->col = col; } pt_menuitem add_sep() // Add a separator to current menu { pt_menuitem mi; pt_menu m; m = (ms->menus[ms->nummenus-1]); mi = NULL; mi = (pt_menuitem) malloc(sizeof(t_menuitem)); if (mi == NULL) return NULL; m->items[(unsigned int)m->numitems] = mi; mi->handler = NULL; // No handler mi->item = mi->status = mi->data = EMPTYSTR; mi->action = OPT_SEP; mi->index = m->numitems++; mi->parindex = ms->nummenus-1; mi->shortcut = 0; return mi; } // Add item to the "current" menu pt_menuitem add_item(const char *item, const char *status, t_action action, const char *data, char itemdata) { 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; 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; 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) { mi->data = ACTIONLONG; } else { mi->data = data; } } else mi->data = EMPTYSTR; switch (action) { case OPT_SUBMENU: mi->itemdata.submenunum = itemdata; break; case OPT_CHECKBOX: mi->itemdata.checked = itemdata; break; case OPT_RADIOMENU: mi->itemdata.radiomenunum = itemdata; mi->data = NULL; // No selection made break; default: // to keep the compiler happy break; } mi->index = m->numitems++; mi->parindex = ms->nummenus-1; return mi; } // Set the shortcut key for the current item void set_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; }