home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 328_02 / wpick.c < prev    next >
C/C++ Source or Header  |  1991-03-24  |  11KB  |  581 lines

  1. /* WPICKLIST.C
  2.  *
  3.  *    General-purpose menu.
  4.  *
  5.  *    displays a menu, which may be multiple pages (scrolling)
  6.  *    gets user input.
  7.  *
  8.  *
  9.  *
  10.  *    PARAMETERS:
  11.  *        char *title = title for menu.
  12.  *        char *list[] = array of string pointers to choices.
  13.  *                terminated by NULL
  14.  *
  15.  *    RETURNS:
  16.  *        N, the number of the choice, ranges from 0 to last on list
  17.  *        if user hit ESCAPE, N will indicate the terminal NULL
  18.  *
  19.  *    NOTE: in the large memory model, be sure you've NORMALIZED the list
  20.  *        otherwise list[N] may be garbage.
  21.  *
  22.  */
  23.  
  24.  
  25. #include "wsys.h"
  26.  
  27.  
  28.  
  29. #define MAX_WIDTH    20
  30. #define MIN_WIDTH    5
  31. #define MAX_N        10
  32.  
  33.  
  34. static void W_NEAR draw_menu ( char **list, int nfirst, int nlast, int npick );
  35.  
  36.  
  37. /* draw the  '#nnn/nnnn' at bottom */
  38. static void W_NEAR draw_npick ( int width, int npick );
  39.  
  40.  
  41.  
  42. /* draw one line of the menu
  43.  */
  44. static void W_NEAR draw_one_line (char **list, int nfirst, int n,
  45.         unsigned char attr );
  46.  
  47.  
  48. int wpicklist ( char *title, char **list )
  49.     {
  50.     int     more   =1,
  51.         width  =MIN_WIDTH,    /* menu choices, screen dimensions */
  52.         n, w,        /* scratch */
  53.         ntotal,      /* total # of choices */
  54.         nfirst =0,    /* number of first on-screen choice */
  55.         nlast,         /* number of last on-screen choice */
  56.         nvisible,    /* number of choices onscreen  */
  57.         npick  =0,    /* number of currently highlighted choice */
  58.         newpick=0,    /* number of next choice */
  59.         up_buttons =0,    /* are PageUp buttons being displayed ? */
  60.         dn_buttons =0,    /* are PageDn ...            */
  61.         redraw    =0,    /* need to rewrite list of choices     */
  62.         have_scrollbar =0;    /* have ascrollbar onscreen */
  63.  
  64.  
  65.     WINDOW *bigW, *smallW;        /* onscreen windows */
  66.     int l,t, ncols ;        /* window postions  */
  67.     int extra =0;            /* extra columns on right ? */
  68.  
  69.     WBUTTON *scrollbar;        /* mouse-able scrollbar */
  70.  
  71.     int key;    /* user input */
  72.  
  73.  
  74.     /* letter typing buffer for matching to strings.
  75.      * note that initializing type_len to MAX_WIDTH
  76.      * forces matching routine to initialize the buffer.
  77.      */
  78.     int     type_len = MAX_WIDTH;
  79.     char     typematch [ MAX_WIDTH+1 ];
  80.     int     type_cnt;
  81.  
  82.     _NORMALIZE (list);
  83.  
  84.  
  85.     /* count lines and find widest line 
  86.      */
  87.     for ( ntotal = 0;  list[ntotal] != NULL; ++ntotal )
  88.         {
  89.         w = strlen ( list[ntotal] );
  90.  
  91.         width = max ( width, w );
  92.         }
  93.  
  94.     if ( title )
  95.         {
  96.         width = max  (width, strlen (title) +2 );
  97.         }
  98.  
  99.  
  100.  
  101.     width = min ( width, MAX_WIDTH ) +2;    /* extra for frames R */
  102.  
  103.  
  104.  
  105.     /* decide if menu is shorter than 1 page...
  106.      */
  107.     if ( ntotal <= MAX_N )
  108.         {
  109.         nlast = ntotal;
  110.         }
  111.     else
  112.         {
  113.         nlast = MAX_N;
  114.         extra = 4;    /* enable extra for right-side buttons */
  115.         }
  116.     nvisible = nlast;
  117.  
  118.  
  119.     /* pick window position
  120.      */
  121.     ncols = width + extra ;
  122.     wlocate ( &l, &t, ncols, nlast );
  123.  
  124.  
  125.  
  126.     bigW = wopen ( l, t, ncols, nlast, wmenuattr,
  127.             DOUBLE_BORDER, wmenuattr, 1 );
  128.     w0-> winputstyle = (0xFF - WPUTWRAP - WPUTSCROLL );
  129.  
  130.         
  131.     /* create scrollbar
  132.      */
  133.     if ( nlast > 4 )
  134.         {
  135.         have_scrollbar =1;
  136.         scrollbar = wscrollbar_add ( -1, width-1, 0, nlast-1, ntotal-1, 0 );
  137.         }
  138.  
  139.  
  140.     /* actual number of text columns is width -1 (for border)
  141.      */
  142.     smallW = wopen ( l,t, width-1, nlast, wmenuattr, NO_BORDER, 0, 0);
  143.     w0-> winputstyle = (0xFF - WPUTWRAP - WPUTSCROLL );
  144.  
  145.  
  146.  
  147.  
  148.  
  149.  
  150.     wreopen ( bigW );
  151.     if ( title )
  152.         {
  153.         wtitle ( title );
  154.         }
  155.  
  156.     if ( extra )
  157.         {
  158.         wbutton_add ("END ", width,     nvisible-1, 5, END,    0);
  159.         wbutton_add ("HOME", width,     0,          5, HOME,   0);
  160.         }
  161.  
  162.     newpick = npick;
  163.     redraw = ON;
  164.  
  165.  
  166.     /* loop thru user input.
  167.       */
  168.     while ( more )
  169.         {
  170.         /*next line added for protection from wmsdrag() hotkey
  171.          *        mouse may move the bigW during a wgetc() call,
  172.          *        so may need to re-align positions of the 2 windows. 
  173.          */
  174.         smallW->winleft = bigW->winleft;     smallW->wintop  = bigW->wintop;
  175.         wreopen (smallW);
  176.  
  177.         if ( redraw )
  178.             {
  179.             draw_menu ( list, nfirst, nlast, newpick );
  180.             redraw = OFF;
  181.             }
  182.         else
  183.         if ( npick != newpick )
  184.             {
  185.             /* only thing changed is line# picked
  186.              * the other lines haven't changed
  187.              * so redraw old line, draw new one highlighted.
  188.              */
  189.             draw_one_line(list, nfirst, npick,  wmenuattr);
  190.             draw_one_line(list, nfirst, newpick,wbuttonattr);
  191.             }
  192.         wreopen (bigW);
  193.  
  194.         if ( npick != newpick )
  195.             {
  196.             npick = newpick;
  197.             if ( have_scrollbar )
  198.                 {
  199.                 wscrollbar_reset ( scrollbar, npick );
  200.                 }
  201.             }
  202.  
  203.  
  204.  
  205.         if ( extra )
  206.             {
  207.             draw_npick ( width, npick ); /* '#nnn' */
  208.  
  209.             /* handle the 'dn' buttons, if need to change them
  210.              */
  211.             if ( nlast != ntotal )
  212.                 {
  213.                 if ( ! dn_buttons )
  214.                     {
  215.                     /* not at bottom, so draw 'down' buttons
  216.                      */
  217.                     wbutton_add ("PgDn", width,
  218.                        nvisible-2,5,PAGE_DN, 0);
  219.                     wbutton_add ("Dn 1", width,
  220.                        nvisible-3,5,CTRL_PAGE_DN,0);
  221.                     dn_buttons = ON;
  222.                     }
  223.                 }
  224.             else
  225.                 {
  226.                 /* (nlast == ntotal )
  227.                  */
  228.                 if ( dn_buttons )
  229.                     {
  230.                     /* if reached end of list,
  231.                      * and dn_buttons are on,
  232.                      * then need to erase them
  233.                      */
  234.                     wbutton_delete ( PAGE_DN, 0 );
  235.                     wbutton_delete ( CTRL_PAGE_DN, 0 );
  236.                     dn_buttons = OFF;
  237.                     }
  238.                 }
  239.  
  240.             /* now the  'up' buttons, if need to change them
  241.              */
  242.             if ( nfirst > 0 )
  243.                 {
  244.                 if ( ! up_buttons )
  245.                     {
  246.                     /* not at bottom, so draw 'down' buttons
  247.                      */
  248.                     wbutton_add ("PgUp", width, 1,  5,
  249.                             PAGE_UP, 0 );
  250.                     wbutton_add ("Up 1", width, 2,  5,
  251.                             CTRL_PAGE_UP, 0 );
  252.                     up_buttons = ON;
  253.                     }
  254.                 }
  255.             else
  256.                 {
  257.                 /* (nfirst == 0 )
  258.                  */
  259.                 if ( up_buttons )
  260.                     {
  261.                     /* if reached top of list,
  262.                      * and up_buttons are on,
  263.                      * then need to erase them
  264.                      */
  265.                     wbutton_delete ( PAGE_UP, 0 );
  266.                     wbutton_delete ( CTRL_PAGE_UP, 0 );
  267.                     up_buttons = OFF;
  268.                     }
  269.                 }
  270.             }
  271.  
  272.  
  273.  
  274.         key = wgetc ();
  275.  
  276.  
  277.         /* did user type value to match ?
  278.          */
  279.         if ( isascii(key) && isprint(key) )
  280.             {
  281.             if ( type_len == MAX_WIDTH )
  282.                 {
  283.                 /* filled type match buffer, restart from 0
  284.                  */
  285.                 memset (typematch, 0, sizeof (typematch) );
  286.                 type_len = 0;
  287.                 }
  288.             /* insert typed char in match buffer
  289.              * & add one to length of buffer.
  290.              * start search from current position
  291.              */
  292.             typematch [type_len++] = key;
  293.             for (type_cnt =npick; type_cnt < ntotal; ++type_cnt)
  294.                 {
  295.                 if (0==memicmp
  296.                     (list[type_cnt],typematch, type_len))
  297.                     {
  298.                     newpick = type_cnt;
  299.                     redraw  = ON;
  300.                     nfirst  = max (0,newpick-1);
  301.                     nlast   = nfirst + nvisible;
  302.                     if ( nlast > ntotal)
  303.                         {
  304.                         nlast  = ntotal;
  305.                         nfirst = nlast - nvisible;
  306.                         }
  307.                     break;  /* break from for(...) */
  308.                     }
  309.                 }
  310.  
  311.  
  312.             }    /* end of printable char match */
  313.         else
  314.         if ( type_len > 0 )
  315.             {
  316.             /* match buffer has characters in it,
  317.              * but user is no longer trying to match
  318.              */
  319.             memset (typematch, 0, sizeof (typematch) );
  320.             type_len = 0;
  321.             }
  322.  
  323.  
  324.  
  325.         /* used mouse to make a selection on menu ?
  326.          */
  327.         if (  ( key == MOUSE )
  328.            && ( wmouse.wms_inwindow )
  329.            && ( wmouse.wms_x < width )            /* in a line l to r*/
  330.            && ( wmouse.wms_y < (nlast-nfirst) ) /* on a line t to b*/
  331.            )
  332.            {
  333.            n = nfirst + wmouse.wms_y;    /* menu line # chosen */
  334.            if ( n != npick )
  335.             {
  336.             newpick = n;
  337.  
  338.             }
  339.            else
  340.             {
  341.             /* double_click means accept */
  342.             if ( wmouse.wms_used & WMS_LEFT_RLS )
  343.                 {
  344.                 key = ENTER;
  345.                 }
  346.             }
  347.            }
  348.         else
  349.         if ( key == UP_ARROW )
  350.             {
  351.             /* no on_screen buttons for up arrow
  352.              * so have to handle it here
  353.              */
  354.             --newpick;
  355.             if ( newpick < 0 )
  356.                 {
  357.                 newpick =0;
  358.                 }
  359.  
  360.             if ( newpick < nfirst )
  361.                 {
  362.                 /* have moved offscreen */
  363.                 nfirst = newpick;
  364.                 nlast  = nfirst + nvisible;
  365.                 redraw = ON;
  366.                 }
  367.             }
  368.         else
  369.         if ( key == DN_ARROW )
  370.             {
  371.             /* no on_screen buttons for dnup arrow
  372.              */
  373.             ++newpick;
  374.             if ( newpick >= ntotal )
  375.                 {
  376.                 newpick = ntotal-1;
  377.                 }
  378.             if ( newpick >= nlast )
  379.                 {
  380.                 /* have moved down offscreen */
  381.                 nlast  = newpick +1;
  382.                 nfirst = nlast-nvisible;
  383.                 redraw = ON;
  384.                 }
  385.  
  386.             }
  387.  
  388.  
  389.  
  390.  
  391.         switch ( key )
  392.             {
  393.             /* NOTE ESCAPE falls through to ENTER,
  394.              *      there is no break
  395.              */
  396.         case (ESCAPE):
  397.             npick = ntotal;    /* offset to NULL string */
  398.         case (ENTER):
  399.             more = 0;
  400.             break;
  401.  
  402.         case ( HOME ):
  403.             nfirst = 0;
  404.             nlast  = nvisible;
  405.             newpick= 0;
  406.             redraw = ON;
  407.             break;
  408.  
  409.         case ( END ):
  410.             nlast  = ntotal;
  411.             nfirst = nlast - nvisible;
  412.             newpick= nlast -1;
  413.             redraw = ON;
  414.             break;
  415.  
  416.         case ( PAGE_DN ):
  417.             if ( dn_buttons )
  418.                 {
  419.                 /* dn_buttons are onscreen,
  420.                  * implies valid choice
  421.                  */
  422.                 nlast += nvisible;
  423.                 if ( nlast > ntotal )
  424.                     {
  425.                     nlast = ntotal;
  426.                     }
  427.                 nfirst  = nlast - nvisible;
  428.                 newpick = nfirst;
  429.                 redraw  = ON;
  430.                 }
  431.             break;
  432.  
  433.         case ( PAGE_UP ):
  434.             if ( up_buttons )
  435.                 {
  436.                 nfirst -= nvisible;
  437.                 if ( nfirst < 0 )
  438.                     {
  439.                     nfirst = 0;
  440.                     }
  441.                 nlast   = nfirst + nvisible;
  442.                 newpick = nlast -1;
  443.                 redraw  = ON;
  444.                 }
  445.             break;
  446.  
  447.  
  448.  
  449.  
  450.         case ( CTRL_PAGE_UP ):
  451.             if ( up_buttons )
  452.                 {
  453.                 nfirst -= 1;
  454.                 if ( nfirst < 0 )
  455.                     {
  456.                     nfirst = 0;
  457.                     }
  458.                 nlast   = nfirst + nvisible;
  459.  
  460.                 if ( npick  >= nlast )
  461.                      {
  462.                      newpick = nlast-1;
  463.                      }
  464.     
  465.                 redraw  = ON;
  466.                 }
  467.             break;
  468.  
  469.  
  470.         case ( CTRL_PAGE_DN ):
  471.             if ( dn_buttons )
  472.                 {
  473.                 nlast += 1;
  474.                 if ( nlast >ntotal )
  475.                     {
  476.                     nlast = ntotal;
  477.                     }
  478.                 nfirst   = nlast - nvisible;
  479.                 if ( npick  < nfirst )
  480.                      {
  481.                      newpick = nfirst;
  482.                      }
  483.                 redraw  = ON;
  484.                 }
  485.             break;
  486.  
  487.  
  488.         case ( -1 ):
  489.             /* scrollbar was selected.
  490.              */
  491.             newpick = wscrollbar_scroll ( scrollbar );
  492.             if ( newpick < nfirst )
  493.                 {
  494.                 redraw  = ON;
  495.                 nfirst  = newpick;
  496.                 nlast   = nfirst + nvisible;
  497.                 }
  498.             else
  499.             if ( newpick >= nlast )
  500.                 {
  501.                 redraw = ON;
  502.                 nlast  = newpick +1;
  503.                 nfirst = nlast  - nvisible;
  504.                 }
  505.             break;
  506.  
  507.             }    /* end switch on key */
  508.  
  509.  
  510.  
  511.         }    /* end while ... more */
  512.  
  513.  
  514.  
  515.  
  516.  
  517.  
  518.     /* close the small window first, then the big window
  519.      */
  520.     wreopen ( smallW );
  521.     wabandon ();
  522.     wclose ();
  523.     
  524.     return (npick);    /* wmenu */
  525.     }
  526.  
  527.  
  528.  
  529. static void W_NEAR draw_npick ( int width, int npick )
  530.     {
  531.     wgoto   ( width, (w0->winymax)/2 );
  532.     wprintf ( "#%-3.3i" , npick+1 );
  533.     return;
  534.     }
  535.  
  536.  
  537.  
  538. static void W_NEAR draw_menu ( char **list, int nfirst, int nlast, int npick )
  539.     {
  540.     int     n;
  541.     unsigned char attr;
  542.  
  543.     for ( n= nfirst; n<nlast; ++n )
  544.         {
  545.         if ( n==npick )
  546.             {
  547.             attr = wbuttonattr;
  548.             }
  549.         else
  550.             {
  551.             attr = wmenuattr;
  552.             }
  553.  
  554.         draw_one_line ( list, nfirst, n, attr );
  555.         }
  556.  
  557.     return;
  558.     }
  559.  
  560.  
  561.  
  562.  
  563. /* draw one line of the menu */
  564. static void W_NEAR draw_one_line (char **list, int nfirst, int n,
  565.     unsigned char attr )
  566.     {
  567.  
  568.     wgoto ( 0, n-nfirst );
  569.     wsetattr ( attr );
  570.     wclearline ();
  571.     wputs ( list[n] );
  572.     wsetattr (wmenuattr);
  573.  
  574.     return;
  575.     }
  576.  
  577.  
  578.  
  579.  
  580. /*--------------------------- end of WPICK.C -------------------------*/
  581.