home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / C++-7 / DISK4 / SAMPLES / GRAPHICS / MENU.C$ / MENU
Encoding:
Text File  |  1992-03-09  |  13.2 KB  |  409 lines

  1. /* MENU - Module of functions to put menus on the screen and handle keyboard
  2.  * input. To use it, include the MENU.H file in your program. The following
  3.  * functions are public:
  4.  *
  5.  *   Menu       -   Puts a menu on screen and reads input for it
  6.  *   Box        -   Puts a box on screen (fill it yourself)
  7.  *   GetKey     -   Gets ASCII or function key
  8.  *   _outchar   -   Displays character using current text position and color
  9.  *
  10.  * The following structures are defined:
  11.  *
  12.  *   MENU       -   Defines menu colors, box type, and centering
  13.  *   ITEM       -   Defines text of menu item and index of highlight character
  14.  *
  15.  * The global variable "mnuAtrib" has type MENU. Change this variable to
  16.  * change menu appearance.
  17.  */
  18.  
  19. #include <string.h>
  20. #include <stddef.h>
  21. #include <ctype.h>
  22. #include <graph.h>
  23. #include <bios.h>
  24. #include "menu.h"
  25. #include "mouse.h"
  26.  
  27. /* Prototype for internal function */
  28. static void Itemize( int row, int col, int fCur, ITEM itm, int cBlank );
  29.  
  30. /* Default menu attribute. The default works for color or B&W. You can
  31.  * override the default value by defining your own MENU variable and
  32.  * assigning it to mnuAtrib. Or you can modify specific fields at
  33.  * run time. For example, you could use a different attribute for color
  34.  * than for black and white.
  35.  */
  36. MENU mnuAtrib =
  37. {
  38.     _TBLACK, _TBLACK, _TWHITE, _TBRIGHTWHITE, _TBRIGHTWHITE,
  39.     _TWHITE, _TWHITE, _TBLACK, _TWHITE, _TBLACK,
  40.     TRUE,
  41.     (unsigned char)'┌', (unsigned char)'┐', (unsigned char)'┘', (unsigned char)'└',
  42.     (unsigned char)'│', (unsigned char)'─'
  43. };
  44.  
  45.  
  46. /* Menu - Puts menu on screen and reads menu input from keyboard. When a
  47.  * highlighted hot key or ENTER is pressed, returns the index of the
  48.  * selected menu item.
  49.  *
  50.  * Params: row and col - If "fCentered" attribute of "mnuAtrib" is true,
  51.  *           center row and column of menu; otherwise top left of menu
  52.  *         aItem - array of structure containing the text of each item
  53.  *           and the index of the highlighted hot key
  54.  *         iCur - index of the current selection--pass 0 for first item,
  55.  *           or maintain a static value
  56.  *
  57.  * Return: The index of the selected item
  58.  *
  59.  * Uses:   mnuAtrib
  60.  */
  61. int Menu( int row, int col, ITEM aItem[], int iCur )
  62. {
  63.     int i;
  64.     int cItem, cchItem = 2; /* Counts of items and chars per item       */
  65.     int acchItem[MAXITEM];  /* Array of counts of character in items    */
  66.     char achHilite[36];     /* Array for highlight characters           */
  67.     long bgColor;           /* Screen color, position, and cursor       */
  68.     short fgColor;
  69.     struct rccoord rc;
  70.     unsigned fCursor;
  71.  
  72.     /* Save screen information. */
  73.     fCursor = _displaycursor( _GCURSOROFF );
  74.     bgColor = _getbkcolor();
  75.     fgColor = _gettextcolor();
  76.     rc = _gettextposition();
  77.  
  78.     /* Count items, find longest, and put count of each in array. Also,
  79.      * put the highlighted character from each in a string.
  80.      */
  81.     for( cItem = 0; aItem[cItem].achItem[0]; cItem++ )
  82.     {
  83.         acchItem[cItem] = strlen( aItem[cItem].achItem );
  84.         cchItem = (acchItem[cItem] > cchItem) ? acchItem[cItem] : cchItem;
  85.         i = aItem[cItem].iHilite;
  86.         achHilite[cItem] = aItem[cItem].achItem[i];
  87.     }
  88.     cchItem += 2;
  89.     achHilite[cItem] = 0;          /* Null-terminate and lowercase string  */
  90.     strlwr( achHilite );
  91.  
  92.     /* Adjust if centered, and draw menu box. */
  93.     if( mnuAtrib.fCentered )
  94.     {
  95.         row -= cItem / 2;
  96.         col -= cchItem / 2;
  97.     }
  98.     Box( row++, col++, cItem, cchItem );
  99.  
  100.     /* Put items on menu. */
  101.     for( i = 0; i < cItem; i++ )
  102.     {
  103.         if( i == iCur )
  104.             Itemize( row + i, col, TRUE,  aItem[i], cchItem - acchItem[i] );
  105.         else
  106.             Itemize( row + i, col, FALSE, aItem[i], cchItem - acchItem[i] );
  107.     }
  108.     SetPtrPos( col + (cchItem / 2), row + iCur );
  109.  
  110.     iCur = EventLoop( row, col, aItem, iCur, cItem, cchItem,
  111.                       acchItem, achHilite );
  112.     _setbkcolor( bgColor );
  113.     _settextcolor( fgColor );
  114.     _settextposition( rc.row, rc.col );
  115.     _displaycursor( fCursor );
  116.     return iCur;
  117. }
  118.  
  119. int EventLoop( int row, int col, ITEM aItem[], int iCur, int cItem,
  120.                int cchItem, int acchItem[], char achHilite[] )
  121. {
  122.     unsigned    uKey;       /* Unsigned key code                        */
  123.     int         iPrev;      /* Previous index                           */
  124.     EVENT       meEvent;
  125.     char        *pchT;      /* Temporary character pointer              */
  126.     static      int fBtnDown = FALSE;
  127.  
  128.     while( TRUE )
  129.     {
  130.         uKey = GetKey( NO_WAIT );
  131.         if( uKey )
  132.         {
  133.             switch( uKey )
  134.             {
  135.                 case U_UP:              /* Up key       */
  136.                     iPrev = iCur;
  137.                     iCur = (iCur > 0) ? (--iCur % cItem) : cItem - 1;
  138.                     break;
  139.                 case U_DN:              /* Down key     */
  140.                     iPrev = iCur;
  141.                     iCur = (iCur < cItem) ? (++iCur % cItem) : 0;
  142.                     break;
  143.                 default:
  144.                     if( uKey > 256 )    /* Ignore unknown function key  */
  145.                         continue;
  146.                     pchT = strchr( achHilite, (char)tolower( uKey ) );
  147.                     if( pchT != NULL )  /* If in highlight string, evaluate */
  148.                         iCur = pchT - achHilite;/* and fall through  */
  149.                     else
  150.                         continue;       /* Ignore unkown ASCII key      */
  151.                 case ENTER:
  152.                     return iCur;
  153.             }
  154.         }
  155.         else if( GetMouseEvent( &meEvent ) )
  156.         {
  157.             SetPtrVis( SHOW );
  158.  
  159.             /* If mouse is on the menu, respond to various events. */
  160.             if( (meEvent.x >= col) && (meEvent.x < (col + cchItem)) &&
  161.                 (meEvent.y >= row) && (meEvent.y < (row + cItem)) )
  162.             {
  163.                 /* If button is down, drag selection. */
  164.                 if( meEvent.fsBtn & LEFT_DOWN )
  165.                 {
  166.                     fBtnDown = TRUE;
  167.                     iPrev = iCur;
  168.                     iCur = meEvent.y - row;
  169.                 }
  170.                 /* If button goes up from down, select current. */
  171.                 else if( fBtnDown && !(meEvent.fsBtn & LEFT_DOWN) )
  172.                 {
  173.                     fBtnDown = FALSE;
  174.                     iCur = meEvent.y - row;
  175.                     return iCur;
  176.                 }
  177.                 /* Ignore if no button has been pushed. */
  178.                 else
  179.                     continue;
  180.             }
  181.             /* Ignore if off menu. */
  182.             else
  183.                 continue;
  184.         }
  185.         else
  186.             continue;
  187.  
  188.         /* Redisplay current and previous if we get here through arrow
  189.          * move or drag.
  190.          */
  191.         Itemize( row + iCur,  col, TRUE,  aItem[iCur],
  192.                  cchItem - acchItem[iCur] );
  193.         Itemize( row + iPrev, col, FALSE, aItem[iPrev],
  194.                  cchItem - acchItem[iPrev] );
  195.     }
  196. }
  197.  
  198. /* Box - Draw menu box, filling interior with blanks of the border color.
  199.  *
  200.  * Params: row and col - upper left of box
  201.  *         rowLast and colLast - height and width
  202.  *
  203.  * Return: None
  204.  *
  205.  * Uses:   mnuAtrib
  206.  */
  207. void Box( int row, int col, int rowLast, int colLast )
  208. {
  209.     int i;
  210.     char achT[MAXITEM + 2];         /* Temporary array of characters */
  211.  
  212.     /* Set color and position. */
  213.     _settextposition( row, col );
  214.     _settextcolor( mnuAtrib.fgBorder );
  215.     _setbkcolor( mnuAtrib.bgBorder );
  216.  
  217.     /* Draw box top. */
  218.     achT[0] = mnuAtrib.chNW;
  219.     memset( achT + 1, mnuAtrib.chEW, colLast );
  220.     achT[colLast + 1] = mnuAtrib.chNE;
  221.     achT[colLast + 2] = 0;
  222.     _outtext( achT );
  223.  
  224.     /* Draw box sides and center. */
  225.     achT[0] = mnuAtrib.chNS;
  226.     memset( achT + 1, ' ', colLast );
  227.     achT[colLast + 1] = mnuAtrib.chNS;
  228.     achT[colLast + 2] = 0;
  229.     for( i = 1; i <= rowLast; ++i )
  230.     {
  231.         _settextposition( row + i, col );
  232.         _outtext( achT );
  233.     }
  234.  
  235.     /* Draw box bottom. */
  236.     _settextposition( row + rowLast + 1, col );
  237.     achT[0] = mnuAtrib.chSW;
  238.     memset( achT + 1, mnuAtrib.chEW, colLast );
  239.     achT[colLast + 1] = mnuAtrib.chSE;
  240.     achT[colLast + 2] = 0;
  241.     _outtext( achT );
  242. }
  243.  
  244. /* Itemize - Display one selection (item) of a menu. This function
  245.  * is normally only used internally by Menu.
  246.  *
  247.  * Params: row and col - top left of menu
  248.  *         fCur - flag set if item is current selection
  249.  *         itm - structure containing item text and index of highlight
  250.  *         cBlank - count of blanks to fill
  251.  *
  252.  * Return: none
  253.  *
  254.  * Uses:   mnuAtrib
  255.  */
  256. void Itemize( int row, int col, int fCur, ITEM itm, int cBlank )
  257. {
  258.     int i;
  259.     char achT[MAXITEM];             /* Temporary array of characters */
  260.  
  261.     /* Set text position and color. */
  262.     _settextposition( row, col );
  263.     if( fCur )
  264.     {
  265.         _settextcolor( mnuAtrib.fgSelect );
  266.         _setbkcolor( mnuAtrib.bgSelect );
  267.     }
  268.     else
  269.     {
  270.         _settextcolor( mnuAtrib.fgNormal );
  271.         _setbkcolor( mnuAtrib.bgNormal );
  272.     }
  273.  
  274.     /* Display item and fill blanks. */
  275.     strcat( strcpy( achT, " " ), itm.achItem );
  276.     _outtext( achT );
  277.     memset( achT, ' ', cBlank-- );
  278.     achT[cBlank] = 0;
  279.     _outtext( achT );
  280.  
  281.     /* Set position and color of highlight character, then display it. */
  282.     i = itm.iHilite;
  283.     _settextposition( row, col + i + 1 );
  284.     if( fCur )
  285.     {
  286.         _settextcolor( mnuAtrib.fgSelHilite );
  287.         _setbkcolor( mnuAtrib.bgSelHilite );
  288.     }
  289.     else
  290.     {
  291.         _settextcolor( mnuAtrib.fgNormHilite );
  292.         _setbkcolor( mnuAtrib.bgNormHilite );
  293.     }
  294.     _outchar( itm.achItem[i] );
  295. }
  296.  
  297. /* GetKey - Gets a key from the keyboard. This routine distinguishes
  298.  * between ASCII keys and function or control keys with different shift
  299.  * states. It also accepts a flag to return immediately if no key is
  300.  * available.
  301.  *
  302.  * Params: fWait - Code to indicate how to handle keyboard buffer:
  303.  *   NO_WAIT     Return 0 if no key in buffer, else return key
  304.  *   WAIT        Return first key if available, else wait for key
  305.  *   CLEAR_WAIT  Throw away any key in buffer and wait for new key
  306.  *
  307.  * Return: One of the following:
  308.  *
  309.  *   Keytype                                High Byte    Low Byte
  310.  *   -------                                ---------    --------
  311.  *   No key available (only with NO_WAIT)       0           0
  312.  *   ASCII value                                0        ASCII code
  313.  *   Unshifted function or keypad               1        scan code
  314.  *   Shifted function or keypad                 2        scan code
  315.  *   CTRL function or keypad                    3        scan code
  316.  *   ALT function or keypad                     4        scan code
  317.  *
  318.  * Note:   getkey cannot return codes for keys not recognized by BIOS
  319.  *         int 16, such as the CTRL-UP or the 5 key on the numeric keypad.
  320.  */
  321. unsigned GetKey( int fWait )
  322. {
  323.     unsigned uKey, uShift;
  324.  
  325.     /* If CLEAR_WAIT, drain the keyboard buffer. */
  326.     if( fWait == CLEAR_WAIT )
  327.         while( _bios_keybrd( _KEYBRD_READY ) )
  328.             _bios_keybrd( _KEYBRD_READ );
  329.  
  330.     /* If NO_WAIT, return 0 if there is no key ready. */
  331.     if( !fWait && !_bios_keybrd( _KEYBRD_READY ) )
  332.         return FALSE;
  333.  
  334.     /* Get key code. */
  335.     uKey = _bios_keybrd( _KEYBRD_READ );
  336.  
  337.     /* If low byte is not zero, it's an ASCII key. Check scan code to see
  338.      * if it's on the numeric keypad. If not, clear high byte and return.
  339.      */
  340.     if( uKey & 0x00ff )
  341.         if( (uKey >> 8) < 69 )
  342.             return( uKey & 0x00ff );
  343.  
  344.     /* For function keys and numeric keypad, put scan code in low byte
  345.      * and shift state codes in high byte.
  346.      */
  347.     uKey >>= 8;
  348.     uShift = _bios_keybrd( _KEYBRD_SHIFTSTATUS ) & 0x000f;
  349.     switch( uShift )
  350.     {
  351.         case 0:
  352.             return( 0x0100 | uKey );  /* None (1)    */
  353.         case 1:
  354.         case 2:
  355.         case 3:
  356.             return( 0x0200 | uKey );  /* Shift (2)   */
  357.         case 4:
  358.             return( 0x0300 | uKey );  /* Control (3) */
  359.         case 8:
  360.             return( 0x0400 | uKey );  /* Alt (4)     */
  361.     }
  362. }
  363.  
  364. /* _outchar - Display a character. This is the character equivalent of
  365.  * _outtext. It is affected by _settextposition, _settextcolor, and
  366.  * _setbkcolor. It should not be used in loops. Build strings and then
  367.  * _outtext to show multiple characters.
  368.  *
  369.  * Params: ch - character to be displayed
  370.  *
  371.  * Return: none
  372.  */
  373. void _outchar( char ch )
  374. {
  375.     static char ach[2] = " ";      /* Temporary array of characters */
  376.  
  377.     ach[0] = ch;
  378.     _outtext( ach );
  379. }
  380.  
  381.  
  382. /* ClickOrPress - Checks to see if a key has been pressed or a mouse
  383.  * button clicked. A click is defined as pressing and then releasing.
  384.  *
  385.  * Params: none
  386.  *
  387.  * Return: TRUE or FALSE
  388.  */
  389. int ClickOrPress()
  390. {
  391.     EVENT ev;
  392.     int i = 0;
  393.  
  394.     /* Check for press. */
  395.     if( GetKey( NO_WAIT ) )
  396.         return TRUE;
  397.  
  398.     /* Check for click. If button is down, wait until it is released. */
  399.     if( !GetMouseEvent( &ev ) )
  400.         return 0;
  401.     if( ev.fsBtn )
  402.     {
  403.         while( TRUE )
  404.             if( GetMouseEvent( &ev ) && !ev.fsBtn )
  405.                 return TRUE;
  406.     }
  407.     return FALSE;
  408. }
  409.