www.pudn.com > commutil.zip > MENU.CPP


// ******************************************************************** // 
//                                                                      // 
//      MENU.CPP                                                        // 
//      Copyright (c) 1993, Michael Holmes and Bob Flanders             // 
//      C++ Communication Utilities                                     // 
//                                                                      // 
//      Chapter 7: Receiving a FAX                                      // 
//      Last changed in chapter 2                                       // 
//                                                                      // 
//      This file contains the definition and interface for             // 
//      the menu class.                                                 // 
//                                                                      // 
// ******************************************************************** // 
 
 
 
#define ALLOW_ALT   1                       // allow alt key in get_key() 
#define NO_ALT      0                       // ..and supress alt key 
 
 
/* ******************************************************************** * 
 * 
 *  Globals 
 * 
 * ******************************************************************** */ 
 
int     menu_cn = MIX(WHITE, CYAN),         // default normal menu colors 
        menu_cr = MIX(WHITE, BLUE);         // ..and reverse colors 
 
int     NotYet(int c, int r);               // null routine definition 
char    get_scan(unsigned char);            // get scan code for character 
 
 
 
/* ******************************************************************** * 
 * 
 *  Menu class definition 
 * 
 * ******************************************************************** */ 
 
class Menu 
    { 
    public: 
        Menu(char *s,                       // create first menu entry 
             int  (*f)(int, int) = NotYet,  // ..function to call 
             char c = '\0');                // ..special key character 
        Menu(Menu *m,                       // add a menu entry to list 
             char *s,                       // ..label to add 
             int  (*f)(int, int) = NotYet,  // ..function to call 
             int  t = 0,                    // ..submenu flag 
             char c = '\0');                // ..special key character 
        void SetColors(int cn, int cr),     // new norm and rev colors 
             Display(int c);                // process a main menu 
        Menu *ValidateKey(int c);           // validate key in menu 
       ~Menu();                             // destructor 
 
    private: 
        void EntryInit(char *s,             // initialize a menu entry 
                int  (*f)(int c, int r),    // ..with runtime routine 
                char c);                    // ..special key character 
        int  DisplayMenu(Menu *n,           // display menu bar 
                Window *w),                 // ..highlighting an entry 
             DisplaySub(int c, int r),      // process a submenu 
             DisplaySubMenu(Menu *n,        // display submenu column 
                Window *w),                 // ..and handle keystrokes 
             DoMenuAction(Menu *m,          // process menu entry 
                int c, int r);              // ..using column and row 
        Menu *Find(char c),                 // find entry by char 
             *FindAlt(char alt_c),          // find entry by alt char 
             *Left(Menu *m),                // find an entry's left 
             *Right(Menu *m);               // ..and its right 
        int  Count(void),                   // count the entries 
             MaxWidth(void);                // find the max width label 
        char *item,                         // menu item label 
              key,                          // normal selection character 
              alt_key;                      // ..and alt selection char 
        int  (*fnc)(int c, int r);          // runtime menu entry fnc 
        Menu *next,                         // next item pointer 
             *sub;                          // submenu pointer 
    }; 
 
 
 
/* ******************************************************************** * 
 * 
 *  Menu -- build the first menu entry 
 * 
 * ******************************************************************** */ 
 
Menu::Menu(char *s,                         // new menu item 
           int  (*f)(int c, int r),         // runtime routine 
           char c)                          // special key character 
{ 
 
EntryInit(s, f, c);                         // initialize new instance 
 
} 
 
 
 
/* ******************************************************************** ** 
 * 
 *  Menu -- add an entry to the menu list 
 * 
 * ******************************************************************** */* 
 
Menu::Menu(Menu *m,                         // menu to chain into 
           char *s,                         // new menu item 
           int  (*f)(int c, int r),         // runtime routine 
           int  t,                          // type, 0 = at same level 
                                            //       1 = submenu 
           char c)                          // special key character 
{ 
 
EntryInit(s, f, c);                         // build base instance 
 
if (t)                                      // q. submenu definition? 
    m->sub = this;                          // a. yes .. store in parent 
 else 
    { 
    while (m->next)                         // loop thru and .. 
        m = m->next;                        // ..then end of the list 
 
    m->next = this;                         // put this at the end 
    } 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  EntryInit -- initialize menu entry instance 
 * 
 * ******************************************************************** */ 
 
void Menu::EntryInit(char *s,               // menu label to set up 
                     int  (*f)(int, int),   // runtime function 
                     char c)                // special key character 
{ 
 
item = new char[strlen(s) + 1];             // get memory for label 
strcpy(item, s);                            // ..and copy into instance 
 
key = c ? c : *s;                           // ASCII selection key 
alt_key = get_scan(key);                    // alt selection key 
fnc = f ? f : NotYet;                       // runtime function 
 
next = sub = 0;                             // clear forward pointers 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Display -- display and process a menu at the top of the screen 
 * 
 * ******************************************************************** */ 
 
void    Menu::Display(int c)                // initial keystroke 
{ 
int  col,                                   // offset of selected entry 
     k;                                     // keystroke 
Menu *m, *n;                                // work menu pointer 
 
 
NOCURSOR();                                 // no cursor while in menu 
Window w(1, 1, 80, 3, menu_cn, menu_cr);    // define menu window 
w.Open(double_line);                        // open window 
 
if ((m = ValidateKey(c)) != 0)              // q. find initial selection? 
    k = (c == 0x100) ? 0 : CR;              // a. yes .. set up for entry 
 else 
    { 
    m = this;                               // else .. use first entry 
    k = 0;                                  // ..in menu and clear key 
    } 
 
for (;;)                                    // loop 'til exit requested 
    { 
    col = DisplayMenu(m, &w);               // display and highlight 
 
    if (NOT k)                              // q. need a new key? 
        while ((k = get_key(NO_ALT)) == 0)  // a. yes .. wait for a key 
            ; 
 
    switch (k)                              // handle user's keystroke 
        { 
        case CR:                            // carriage return 
            k = DoMenuAction(m, col, 2);    // process menu entry 
 
            if (k < 0)                      // q. need to exit the menu? 
                { 
                CURSOR();                   // a. yes .. set cursor back 
                return;                     // ..to normal and return 
                } 
 
            break;                          // else .. wait for next key 
 
        case LEFT:                          // left arrow 
            m = Left(m);                    // get entry to the left 
            k = 0;                          // clear keystroke 
            break;                          // ..then wait for next key 
 
        case RIGHT:                         // right arrow 
            m = Right(m);                   // get entry to the right 
            k = 0;                          // clear keystroke 
            break;                          // ..then wait for next key 
 
        case ESC:                           // escape key 
            CURSOR();                       // set cursor back to normal 
            return;                         // ..exit loop and return 
 
        default:                            // error case 
            if ((n = ValidateKey(k)) != 0)  // q. valid menu key? 
                { 
                m = n;                      // a. yes .. set up as current 
                k = CR;                     // ..and force a  
                } 
             else 
                { 
                printf(BELL);               // else .. ring bell 
                k = 0;                      // finally, clear keystroke 
                } 
        } 
    } 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  DisplayMenu -- write out a menu's entries 
 * 
 * ******************************************************************** */ 
 
int     Menu::DisplayMenu(Menu *n,          // entry to highlight 
                          Window *w)        // window to display in 
{ 
int  w_offset = 2,                          // offset in menu bar 
     s_offset;                              // offset of selected entry 
Menu *m = this;                             // work menu pointer 
 
 
w->GotoXY(1, 1);                            // start from the beginning 
 
for (;;) 
    { 
    w->Display("  ");                       // put some space out 
 
    if (m == n)                             // q. find entry? 
        { 
        w->DisplayReverse(m->item);         // a. yes .. highlight it 
        s_offset = w_offset;                // ..and save field offset 
        } 
     else 
        w->Display(m->item);                // else .. display normally 
 
    w_offset += strlen(m->item) + 2;        // get offset of next item 
 
    if ((m = m->next) == 0)                 // q. end of list? 
        break;                              // a. yes .. exit loop 
    } 
 
return(s_offset);                           // return with entry's offset 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  DisplaySub -- display a submenu 
 * 
 * ******************************************************************** */ 
 
int     Menu::DisplaySub(int c, int r)      // upper left coordinates 
{ 
int     k = 0,                              // keystroke 
        r_current;                          // current row 
Menu   *m = this,                           // current menu entry 
       *n;                                  // work menu pointer 
 
 
Window w(c, r, c + 3 + MaxWidth(),          // define menu window 
            r + 2 + Count(),                // ..to hold whole submenu 
            menu_cn, menu_cr);              // ..using default colors 
w.Open(single_line);                        // open submenu window 
 
for (;;)                                    // loop 'til exit requested 
    { 
    r_current = DisplaySubMenu(m, &w);      // display and highlight 
 
    if (NOT k)                              // q. need a new key? 
        while ((k = get_key(NO_ALT)) == 0)  // a. yes .. wait for a key 
            ; 
 
    switch (k)                              // handle user's keystroke 
        { 
        case CR:                            // carriage return 
            k = DoMenuAction(m, c,          // process menu entry 
                    r + r_current); 
 
            if (k != 0)                     // q. need to exit the menu? 
                return(k);                  // a. yes .. rtn w/keystroke 
 
            break;                          // else .. wait for next key 
 
        case UP:                            // up arrow 
            m = Left(m);                    // get entry above this one 
            k = 0;                          // clear keystroke 
            break;                          // ..then wait for next key 
 
        case DOWN:                          // down arrow 
            m = Right(m);                   // get entry beneath 
            k = 0;                          // clear keystroke 
            break;                          // ..then wait for next key 
 
        case LEFT:                          // left arrow 
            return(LEFT);                   // ..then return w/left key 
 
        case RIGHT:                         // right arrow 
            return(RIGHT);                  // ..then return w/right key 
 
        case ESC:                           // escape key 
            return(0);                      // ..then return one level 
 
        default:                            // error case 
            if ((n = ValidateKey(k)) != 0)  // q. valid menu key? 
                { 
                m = n;                      // a. yes .. set up as current 
                k = CR;                     // ..and force a  
                } 
             else 
                { 
                printf(BELL);               // else .. ring bell 
                k = 0;                      // finally, clear keystroke 
                } 
        } 
    } 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  DisplaySubMenu -- write out a submenu's entries 
 * 
 * ******************************************************************** */ 
 
int     Menu::DisplaySubMenu(Menu *n,       // entry to highlight 
                             Window *w)     // window to display in 
{ 
int  w_row = 1,                             // work row in menu bar 
     s_row;                                 // row of selected entry 
Menu *m = this;                             // work menu pointer 
 
 
for (;;)                                    // loop 'til all done 
    { 
    w->AtSay(1, w_row, " ");                // put some space out 
 
    if (m == n)                             // q. find entry? 
        { 
        w->DisplayReverse(m->item);         // a. yes .. highlight it 
        s_row = w_row;                      // ..and save row number 
        } 
     else 
        w->Display(m->item);                // else .. display normally 
 
    w_row++;                                // next row number 
 
    if ((m = m->next) == 0)                 // q. end of list? 
        break;                              // a. yes .. exit loop 
    } 
 
return(s_row);                              // return with entry's row 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  DoMenuAction -- process menu entry 
 * 
 * ******************************************************************** */ 
 
int     Menu::DoMenuAction(Menu *m,         // selected menu entry 
                           int c, int r)    // column and row 
{ 
 
c += 2;                                     // new column number 
r++;                                        // ..and row number 
 
if (m->sub == 0)                            // q. submenu present? 
    {                                       // a. no .. continue 
    if (m->fnc != 0)                        // q. function available? 
        return((*(m->fnc))(c, r));          // a. yes .. call it 
     else 
        return(0);                          // else .. just return 
    } 
 
 else 
    return(m->sub->DisplaySub(c, r));       // else .. do submenu 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Find -- find a menu entry by key 
 * 
 * ******************************************************************** */ 
 
Menu *Menu::Find(char c)                    // key to search for 
{ 
Menu *m = this;                             // work menu pointer 
 
c = toupper(c);                             // force uppercase search 
 
for (;;)                                    // loop thru the list 
    { 
    if (toupper(m->key) == c)               // q. find the entry? 
        return(m);                          // a. yes .. quit here 
 
    if ((m = m->next) == 0)                 // q. end of list? 
        break;                              // a. yes .. exit loop 
    } 
 
return(0);                                  // else return empty-handed 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  FindAlt -- find a menu entry by alt character (scan code) 
 * 
 * ******************************************************************** */ 
 
Menu *Menu::FindAlt(char alt_c)             // scan code to search 
{ 
Menu *m = this;                             // work menu pointer 
 
 
for (;;)                                    // loop thru the list 
    { 
    if (m->alt_key == alt_c)                // q. find the entry? 
        return(m);                          // a. yes .. quit here 
 
    if ((m = m->next) == 0)                 // q. end of list? 
        break;                              // a. yes .. exit loop 
    } 
 
return(0);                                  // else return empty-handed 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Left -- find a menu entry's left 
 * 
 * ******************************************************************** */ 
 
Menu *Menu::Left(Menu *m)                   // source menu entry 
{ 
Menu *t = this,                             // target menu pointer 
     *last;                                 // last processed entry 
 
for (;;)                                    // loop thru the list 
    { 
    if (t->next == m)                       // q. find the entry? 
        return(t);                          // a. yes .. quit here 
 
    last = t;                               // save last one 
 
    if ((t = t->next) == 0)                 // q. end of list? 
        return(last);                       // a. yes .. exit w/last one 
    } 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Right -- find a menu entry's right 
 * 
 * ******************************************************************** */ 
 
Menu *Menu::Right(Menu *m)                  // source menu entry 
{ 
 
return(m->next ? m->next : this);           // either next or 1st in list 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  MaxWidth -- find the widest menu label 
 * 
 * ******************************************************************** */ 
 
int     Menu::MaxWidth(void) 
{ 
int     x = 0,                              // max width 
        w;                                  // working width 
Menu   *m = this;                           // work pointer 
 
 
for (;;)                                    // loop thru the list 
    { 
    w = strlen(m->item);                    // get length of this entry 
 
    if (x < w)                              // q. find a larger one? 
        x = w;                              // a. yes .. save larger 
 
    if ((m = m->next) == 0)                 // q. end of list? 
        return(x);                          // a. yes .. exit loop 
    } 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Count -- find the count of menu items 
 * 
 * ******************************************************************** */ 
 
int     Menu::Count(void) 
{ 
int     i;                                  // loop counter 
Menu   *m = this;                           // work pointer 
 
 
for (i = 0; m->next; i++, m = m->next)      // count number of entries 
    ; 
 
return(i);                                  // ..and return w/count 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  SetColors -- set global menu colors 
 * 
 * ******************************************************************** */ 
 
void    Menu::SetColors(int cn,             // new normal color combo 
                        int cr)             // ..and reverse color combo 
{ 
 
menu_cn = cn;                               // set up new global 
menu_cr = cr;                               // ..color scheme 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  ValidateKey -- validate key for a menu 
 * 
 * ******************************************************************** */ 
 
Menu   *Menu::ValidateKey(int c)            // char to check 
{ 
 
 
if (c == 0x100)                             // q. just alt key? 
    return(this);                           // a. yes .. use first entry 
 
if (c > 0x100)                              // q. alt key? 
    return(FindAlt(c));                     // a. yes .. check alt list 
 else 
    return(Find(c));                        // else .. check regular list 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  ~Menu -- object destructor 
 * 
 * ******************************************************************** */ 
 
Menu::~Menu() 
{ 
 
delete item;                                // de-allocate string memory 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  get_key() -- get a key (including function keys) 
 * 
 * ******************************************************************** */ 
 
int     get_key(int alt_key)                // nonzero = allow alt_key 
{ 
static 
int     k;                                  // local key variable 
 
 
if ((k = bioskey(1)) != 0)                  // q. key available? 
    {                                       // a. yes .. process it 
    if (k == -1)                            // q. control break? 
        { 
        k = 0;                              // a. yes .. clear key, 
        wait(1);                            // ..wait a tick, then return 
        } 
     else 
        { 
        k = bioskey(0);                     // else .. get waiting key 
 
        if (NOT (k & 0xff))                 // q. fnc or extended key? 
            k = 0x100 + (k >> 8);           // a. yes .. show special key 
         else 
            k &= 0xff;                      // else .. force regular key 
        } 
    } 
 else if (alt_key &&                        // q. allowing alt key? 
         (_bios_keybrd(_KEYBRD_SHIFTSTATUS) // ..and one pressed? 
            & 0x08)) 
    k = 0x100;                              // a. yes .. special key 
 else 
    k = 0;                                  // else .. nothing available 
 
return(k);                                  // return w/key if available 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  get_scan() -- get scan code for a printable character 
 * 
 * ******************************************************************** */ 
 
char    get_scan(unsigned char c)           // ASCII character to convert 
{ 
static 
char    scan_codes[] =                      // scan codes for ! thru ~ 
    { 
    0x02, 0x28, 0x04, 0x05, 0x06, 0x08, 0x28, 0x0a, 0x0b, 0x09, 0x0d, 
    0x33, 0x0c, 0x34, 0x35, 0x0b, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
    0x08, 0x09, 0x0a, 0x27, 0x27, 0x33, 0x0d, 0x34, 0x35, 0x03, 0x1e, 
    0x30, 0x2e, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 
    0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11, 
    0x2d, 0x15, 0x2c, 0x1a, 0x2b, 0x1b, 0x07, 0x0c, 0x29, 0x1e, 0x30, 
    0x2e, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 
    0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11, 0x2d, 
    0x15, 0x2c, 0x1a, 0x2b, 0x1b, 0x29 
    }; 
 
return((c >= '!' && c <= '~') ?             // if valid rtn scan code 
            scan_codes[c - '!'] : 0);       // ..else return a zero 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  NotYet -- null routine for incomplete menu entries 
 * 
 * ******************************************************************** */ 
 
int     NotYet(int c, int r)                // column and row of window 
{ 
Window  ny_win(c, r, c + 28, r + 3,         // define not yet window 
            menu_cn, menu_cr);              // ..using default colors 
 
 
ny_win.Open(single_line);                   // open window with a border 
ny_win.Display(" ** Not Yet Implemented **" //display the not yet message 
               "\n\r" 
               " Press any key to continue"); 
 
while (NOT get_key(NO_ALT))                 // wait for a key 
    ;                                       // ..before closing down 
 
return(0);                                  // return to menu system 
 
}