home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / pctech / hlsrc / hlmenu.c < prev    next >
C/C++ Source or Header  |  1988-09-09  |  14KB  |  576 lines

  1. /*+
  2.     Name:    hlmenu.c
  3.     Date:    05-Jun-1988
  4.     Author:    Kent J. Quirk
  5.         (c) Copyright 1988 Ziff Communications Co.
  6.     Abstract:    Creates and manipulates a menu on the screen.
  7.     History:    09-Sep-88   kjq     Version 1.00
  8. -*/
  9.  
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <graph.h>
  14. #include <conio.h>
  15. #include <ctype.h>
  16.  
  17. #include "winmenu.h"
  18.  
  19. #define hbound(array) (sizeof(array)/sizeof(array[0]))
  20.  
  21. typedef struct {
  22.     int fg;
  23.     long bg;
  24. } TEXTCOLOR;
  25.  
  26. #define ATTRIB(c) (((char)c.bg << 4) + c.fg)
  27.  
  28. TEXTCOLOR orig;
  29. TEXTCOLOR normal      = { 14, 1L },
  30.       reverse     = {  0, 3L };
  31. TEXTCOLOR mononormal  = {  7, 0L },
  32.       monoreverse = {  0, 7L };
  33. TEXTCOLOR error       = { 15, 4L };
  34.  
  35. unsigned char box[2][6] = {
  36.     { 218, 191, 192, 217, 196, 179     },    /* SINGLE lines */
  37.     { 201, 187, 200, 188, 205, 186     },    /* DOUBLE lines */
  38. };
  39.  
  40. void (*menu_init)() = NULL;
  41. void (*menu_exit)() = NULL;
  42. void (*menu_line)(char *text, int item) = NULL;
  43.  
  44. #define UL 0
  45. #define UR 1
  46. #define LL 2
  47. #define LR 3
  48. #define HR 4
  49. #define VR 5
  50.  
  51. #define NL 0        /* use single-line pattern */
  52.  
  53. struct videoconfig config;
  54.  
  55. /**** x g e t c h ****
  56.     Like getch() except that it handles function keys properly by noticing
  57.     the prefix character, then setting the 0x100 bit if it's present. 
  58. ****************************/
  59. int xgetch()        /* getch with function key recognition */
  60. {
  61.     int c;
  62.     if ((c = getch()) == 0)
  63.     c = 0x100 + getch();
  64.     return(c);
  65. }
  66.  
  67. /**** d r a w _ t e x t ****
  68.     Given a position and an array of text, this draws the text at
  69.     that position.
  70. ****************************/
  71. void draw_text(int row, int col, char *menu[], int nrows, int width)
  72. {
  73.     int i;
  74.     char buf[200];
  75.  
  76.     for (i=0; i<nrows; i++)                         /* now the middle */
  77.     {
  78.     _settextposition(row+i, col);
  79.     sprintf(buf, "%c%-*s%c", box[NL][VR], width, menu[i], box[NL][VR]);
  80.     _outtext(buf);
  81.     }
  82. }
  83.  
  84. /**** d r a w _ m e n u ****
  85.     Draws a menu given the text for the menu and where to draw it.
  86. ****************************/
  87. WINDOW *draw_menu(char *menu[], char *toptitle, char *bottitle,
  88.         int nrows, int toprow, int leftcol, int maxlen)
  89. {
  90.     char buf[200];
  91.     int i;
  92.     WINDOW *w;
  93.  
  94.     _getvideoconfig(&config);           /* what is our video state? */
  95.     if (config.mode == _TEXTMONO)
  96.     {
  97.     normal = mononormal;
  98.     reverse = monoreverse;
  99.     }
  100.     
  101.     w = open_window(toprow-1, leftcol-1, toprow+nrows, leftcol+maxlen,
  102.             (char)((normal.bg << 4) + normal.fg));
  103.     _settextcolor(normal.fg);
  104.     _setbkcolor(normal.bg);
  105.  
  106.     buf[maxlen+2] = 0;
  107.  
  108.     buf[0] = box[NL][UL];                /* draw top line */
  109.     memset(buf+1, box[NL][HR], maxlen);
  110.     buf[maxlen+1] = box[NL][UR];
  111.     memmove(buf+1, toptitle, strlen(toptitle));
  112.     _settextposition(toprow-1, leftcol-1);
  113.     _outtext(buf);
  114.  
  115.     buf[0] = box[NL][LL];                /* and bottom line */
  116.     memset(buf+1, box[NL][HR], maxlen);
  117.     buf[maxlen+1] = box[NL][LR];
  118.     memmove(buf+1, bottitle, strlen(bottitle));
  119.     _settextposition(toprow+nrows, leftcol-1);
  120.     _outtext(buf);
  121.  
  122.     draw_text(toprow, leftcol-1, menu, nrows, maxlen);
  123.     return(w);
  124. }
  125.  
  126. /**** s c r o l l _ m e n u ****
  127.     Given a pointer to an array of strings, a title, and the corner
  128.     of a box on the screen, this draws a menu in that box and allows the
  129.     user to select items from it.  It returns when the user presses Enter
  130.     or ESC.  Returns -1 if ESC pressed, otherwise the index into the
  131.     menu items.  The box will scroll to allow for long menus.
  132.     
  133.     This routine calls the function pointed to by (*menu_line)() for every
  134.     line highlighted. It takes two arguments, a char * containing the
  135.     buffer text, and an integer corresponding to the item within menu[]
  136.     currently selected. It also calls (*menu_init)() upon entry to the
  137.     function, and (*menu_exit)() when leaving.    These take no arguments.
  138.     
  139.     The variable allow_select, if true, allows the user to pick a line. 
  140.     If false, this just becomes a browser for the array, and the return
  141.     value has little meaning.
  142. ****************************/
  143. int scroll_menu(char *menu[], char *toptitle, char *bottitle, 
  144.             int toprow, int leftcol, int nrows, int allow_select)
  145. {
  146.     int retval;
  147.     int maxlen = 0;
  148.     int maxrow;
  149.     int i;
  150.     int cursor = 0, firstrow = 0;
  151.     int c = 0;
  152.     unsigned char up[2], dn[2];
  153.     char buf[200];
  154.     WINDOW *w;
  155.  
  156.     up[1] = dn[1] = 0;
  157.     
  158.     if (menu_init)
  159.     (*menu_init)();
  160.     _getvideoconfig(&config);               /* what is our video state? */
  161.     orig.fg = _gettextcolor();               /* get default colors */
  162.     orig.bg = _getbkcolor();
  163.  
  164.     for (maxrow=0; menu[maxrow] != NULL; maxrow++)    /* scan menu */
  165.     {
  166.     if (maxlen < strlen(menu[maxrow]))
  167.         maxlen = strlen(menu[maxrow]);
  168.     }
  169.  
  170.     if (nrows > maxrow)
  171.     nrows = maxrow;
  172.     w = draw_menu(menu, toptitle, bottitle, nrows, toprow, leftcol, maxlen);
  173.  
  174.     for(;;)
  175.     {
  176.     up[0] = (firstrow > 0) ? (unsigned char)'\x18': box[NL][HR];
  177.     _settextposition(toprow-1, leftcol+maxlen-1);
  178.     _outtext(up);
  179.     dn[0] = (firstrow + nrows >= maxrow) ? box[NL][HR] : '\x19';
  180.     _settextposition(toprow + nrows, leftcol+maxlen-1);
  181.     _outtext(dn);
  182.     
  183.     if (allow_select)
  184.     {    
  185.         _settextposition(toprow+cursor, leftcol);
  186.         _settextcolor(reverse.fg);
  187.         _setbkcolor(reverse.bg);
  188.         sprintf(buf, "%-*s", maxlen, menu[cursor+firstrow]);
  189.         _outtext(buf);
  190.     }
  191.     if (menu_line)
  192.         (*menu_line)(buf, cursor+firstrow);     /* call user function */
  193.     
  194.     c = xgetch();
  195.     
  196.     if (allow_select)
  197.     {    
  198.         _settextposition(toprow+cursor, leftcol);    /* unhighlight */
  199.         _settextcolor(normal.fg);
  200.         _setbkcolor(normal.bg);
  201.         _outtext(buf);
  202.     }
  203.     
  204.     switch (c) {
  205.     case DOWN:
  206.     case ' ':
  207.     case TAB:
  208.         if ((allow_select == 0) || (cursor+1 >= nrows))
  209.         {
  210.         if (firstrow + nrows < maxrow)
  211.         {
  212.             ++firstrow;
  213.             draw_text(toprow, leftcol-1, menu+firstrow, nrows, maxlen);
  214.         }
  215.         }
  216.         else
  217.         ++cursor;
  218.         break;
  219.     case UP:
  220.     case 0x08:
  221.     case BACKTAB:
  222.         if ((allow_select == 0) || (cursor-1 < 0))
  223.         {
  224.         if (firstrow > 0)
  225.         {
  226.             --firstrow;
  227.             draw_text(toprow, leftcol-1, menu+firstrow, nrows, maxlen);
  228.         }
  229.         }
  230.         else
  231.         --cursor;
  232.         break;
  233.     case HOME:
  234.         cursor = 0;
  235.         firstrow = 0;
  236.         draw_text(toprow, leftcol-1, menu+firstrow, nrows, maxlen);
  237.         break;
  238.     case END:
  239.         cursor = nrows-1;
  240.         firstrow = maxrow - nrows;
  241.         draw_text(toprow, leftcol-1, menu+firstrow, nrows, maxlen);
  242.         break;
  243.     case PGUP:
  244.         if (firstrow == 0)
  245.         cursor = 0;
  246.         else
  247.         {
  248.         firstrow -= nrows;
  249.         if (firstrow < 0)
  250.             firstrow = 0;
  251.         }
  252.         draw_text(toprow, leftcol-1, menu+firstrow, nrows, maxlen);
  253.         break;
  254.     case PGDN:
  255.         if (firstrow + nrows == maxrow)
  256.         cursor = nrows - 1;
  257.         else
  258.         {
  259.         firstrow += nrows;
  260.         if (firstrow + nrows > maxrow)
  261.             firstrow = maxrow - nrows;
  262.         }
  263.         draw_text(toprow, leftcol-1, menu+firstrow, nrows, maxlen);
  264.         break;
  265.     case DOIT:
  266.     case ESC:
  267.         _settextcolor(orig.fg);
  268.         _setbkcolor(orig.bg);
  269.         close_window(w);
  270.         _settextposition(config.numtextrows, 1);
  271.         if (menu_exit)
  272.         (*menu_exit)();
  273.         if (c == DOIT)
  274.         return(firstrow+cursor);
  275.         else
  276.         return(-1);
  277.     default:
  278.         /* _settextposition(0,0);
  279.         printf("%02X", c); */
  280.         break;
  281.     }
  282.     }
  283. }
  284.  
  285. /**** d o _ m e n u ****
  286.     Given a pointer to an array of strings, a title, and a starting
  287. selection, this draws a menu centered on the screen and allows the
  288. user to select items from it.  It returns when the user presses Enter
  289. or ESC.  Returns -1 if ESC pressed, otherwise the index into the
  290. menu items.
  291.  
  292. If the starting selection is negative, this doesn't display the cursor or
  293. allow the user to pick anything.
  294. ****************************/
  295. int do_menu(char *menu[], char *toptitle, char *bottitle, int sel)
  296. {
  297.     int retval;
  298.     int maxlen = 0;
  299.     int leftcol, toprow, row;
  300.     int nrows, i;
  301.     int current_sel = sel, nextsel;
  302.     int c = 0;
  303.     char buf[200];
  304.     WINDOW *w;
  305.  
  306.     _getvideoconfig(&config);               /* what is our video state? */
  307.     orig.fg = _gettextcolor();               /* get default colors */
  308.     orig.bg = _getbkcolor();
  309.  
  310.     for (nrows=0; menu[nrows] != NULL; nrows++)    /* scan menu */
  311.     {
  312.     if (maxlen < strlen(menu[nrows]))
  313.         maxlen = strlen(menu[nrows]);
  314.     }
  315.  
  316.     leftcol = (config.numtextcols - maxlen)/2;  /* calc left column */
  317.     toprow = (config.numtextrows - nrows)/2;    /* and top row */
  318.  
  319.     w = draw_menu(menu, toptitle, bottitle, nrows, toprow, leftcol, maxlen);
  320.  
  321.     if (sel < 0)
  322.     current_sel = 0, nextsel = 0;
  323.     
  324.     for(;;)
  325.     {
  326.     if (sel >= 0)
  327.     {    
  328.         _settextposition(toprow+current_sel, leftcol);
  329.         _settextcolor(reverse.fg);
  330.         _setbkcolor(reverse.bg);
  331.         sprintf(buf, "%-*s", maxlen, menu[current_sel]);
  332.         _outtext(buf);
  333.         nextsel = current_sel;
  334.     }
  335.     
  336.     switch (c = xgetch()) {
  337.     case DOWN:
  338.     case ' ':
  339.     case TAB:
  340.         if (++nextsel >= nrows)
  341.         nextsel = 0;
  342.         break;
  343.     case UP:
  344.     case 0x08:
  345.     case BACKTAB:
  346.         if (--nextsel < 0)
  347.         nextsel = nrows - 1;
  348.         break;
  349.     case DOIT:
  350.         _settextcolor(orig.fg);
  351.         _setbkcolor(orig.bg);
  352.         close_window(w);
  353.         _settextposition(config.numtextrows, 1);
  354.         return(current_sel);
  355.     case ESC:
  356.         _settextcolor(orig.fg);
  357.         _setbkcolor(orig.bg);
  358.         close_window(w);
  359.         _settextposition(config.numtextrows, 1);
  360.         return(-1);
  361.     default:
  362.         /* _settextposition(0,0);
  363.         printf("%02X", c); */
  364.         break;
  365.     }
  366.     if (sel >= 0)
  367.     {
  368.         _settextposition(toprow+current_sel, leftcol);
  369.         _settextcolor(normal.fg);
  370.         _setbkcolor(normal.bg);
  371.         _outtext(buf);
  372.         current_sel = nextsel;
  373.     }
  374.     }
  375. }
  376.  
  377. /**** e d i t _ l i n e ****
  378.     Given a line of text and a field area, this 
  379.     allows the user to edit the text and returns the last key struck.
  380.     Edits terminate on UP, DOWN, Tab, Backtab, Enter or ESC.
  381. ****************************/
  382. int edit_line(char *text, int len, int row, int col)
  383. {
  384.     char buf[81];
  385.     int pos = 0, redraw = 1;
  386.     static int insert = 1;
  387.     int c;
  388.     char cbuf[2];
  389.     char *s;
  390.  
  391.     text[len] = 0;
  392.     memset(text+strlen(text), ' ', len-strlen(text));
  393.     text[len] = 0;
  394.     strcpy(buf, text);                 /* save it in case of ESC */
  395.     _settextposition(row, col);
  396.     orig.fg = _gettextcolor();               /* get default colors */
  397.     orig.bg = _getbkcolor();
  398.     _settextcolor(reverse.fg);
  399.     _setbkcolor(reverse.bg);
  400.     _outtext(text);
  401.     
  402.     pos = len-1;
  403.     while (isspace(text[pos]) && pos >= 0)
  404.     --pos;
  405.     ++pos;
  406.  
  407.     cbuf[1] = 0;
  408.     for(;;)
  409.     {
  410.     if (redraw)
  411.     {
  412.         _settextposition(row, col);
  413.         _outtext(text);       
  414.         redraw = 0;
  415.     }
  416.     _settextposition(row, col+pos);
  417.  
  418.     switch (c = xgetch()) {
  419.     default:        /* text */
  420.         if (pos >= len)
  421.         break;
  422.         if ((c & 0x100) == 0)
  423.         {
  424.         if (insert)
  425.         {
  426.             if (pos < len-1)
  427.             {
  428.             memmove(text+pos+1, text+pos, len-pos-1);
  429.             redraw = 1;
  430.             }
  431.         }
  432.         text[pos++] = cbuf[0] = (char)c;
  433.         _outtext(cbuf);
  434.         }
  435.         break;
  436.     case RIGHT:
  437.         if (pos < len-1)
  438.         ++pos;
  439.         break;
  440.     case LEFT:
  441.         if (pos > 0)
  442.         --pos;
  443.         break;
  444.     case 0x08:
  445.         if (pos <= 0)
  446.         break;
  447.         memmove(text+pos-1, text+pos, len-pos);
  448.         text[len-1] = ' ';
  449.         --pos;
  450.         redraw = 1;
  451.         break;
  452.     case DEL:
  453.         if (pos < 0)
  454.         break;
  455.         memmove(text+pos, text+pos+1, len-pos);
  456.         text[len-1] = ' ';
  457.         redraw = 1;
  458.         break;
  459.     case HOME:
  460.         pos = 0;
  461.         break;
  462.     case END:
  463.         pos = len-1;
  464.         while (isspace(text[pos]))
  465.         --pos;
  466.         ++pos;
  467.         break;
  468.     case INS:
  469.         insert = !insert;
  470.         break;
  471.     case UNDO:
  472.         strcpy(text, buf);
  473.         redraw = 1;
  474.         break;
  475.     case ESC:
  476.     case UP:
  477.     case DOWN:
  478.     case TAB:
  479.     case BACKTAB:
  480.     case DOIT:
  481.     case F10:
  482.         _settextcolor(orig.fg);
  483.         _setbkcolor(orig.bg);
  484.         _settextposition(row, col);
  485.         _outtext(text);
  486.         for (s=text+strlen(text)-1; isspace(*s) && s >= text; --s)
  487.         *s = 0;
  488.         return(c);
  489.     }
  490.     }
  491. }
  492.  
  493. /**** p o p _ e r r o r ****
  494.     Pops up an error message in red at the bottom of the screen.
  495.     Returns the character struct by the user as a response.
  496. ****************************/
  497. int pop_error(char *s)
  498. {
  499.     WINDOW *w;
  500.     int c;
  501.     
  502.     w = open_window(config.numtextrows-4, 1, 
  503.         config.numtextrows-1, config.numtextcols, ATTRIB(error));
  504.     _settextposition(config.numtextrows-3, (config.numtextcols-strlen(s))/2);
  505.     _settextcolor(error.fg);
  506.     _setbkcolor(error.bg);
  507.     _outtext(s);
  508.     
  509.     s = "Press any key";
  510.     _settextposition(config.numtextrows-2, (config.numtextcols-strlen(s))/2);
  511.     _outtext(s);
  512.     
  513.     c = xgetch();
  514.     close_window(w);
  515.     return(c);
  516. }
  517.  
  518. /*************************************
  519.     FOR DEBUGGING EDIT_LINE
  520. extmake:c cl /AS /Od /Zi hlmenu.c
  521.  
  522. main(argc, argv)
  523. int argc;
  524. char *argv[];
  525. {
  526.     char buf[100];
  527.     int c;
  528.     int len;
  529.  
  530.     len = atoi(argv[1]);
  531.     strcpy(buf, "This is a test of the buffer");
  532.     _clearscreen(_GCLEARSCREEN);
  533.     for (;;)
  534.     {
  535.     c = edit_line(buf, len, 10, 10);
  536.     _settextposition(0,0);
  537.     printf("%4x", c);
  538.     if (c == TAB)
  539.         break;
  540.     }
  541. }
  542.  
  543. */
  544. /*******************************************
  545.    Routine to test scroll_menu
  546.    ******************************************
  547. main(argc, argv)
  548. int argc;
  549. char *argv[];
  550. {
  551.     FILE *f;
  552.     char *bp, *bp2, buf[100];
  553.     char *text[100];
  554.     int i=0;
  555.  
  556.     if ((f = fopen(argv[1], "r")) == NULL)
  557.     exit(1);
  558.  
  559.     _clearscreen(_GCLEARSCREEN);
  560.     while ((bp = fgets(buf, sizeof(buf), f)) != NULL)
  561.     {
  562.     bp[strlen(bp)-1] = 0;
  563.     printf("%s", bp);
  564.     text[i++] = strdup(bp);
  565.     }
  566.     text[i] = NULL;
  567.  
  568.     i = scroll_menu(text, "Test", "Bottom", 5, 20, 10, 1);
  569.     if (i != -1)
  570.     printf("Line selected was %d, '%s'\n", i, text[i]);
  571.     else
  572.     printf("ESCape\n");
  573.     return(0);
  574. }
  575. */
  576.