home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / msysjour / vol05 / 03 / vlb / vlb.c < prev    next >
Text File  |  1990-05-01  |  33KB  |  1,064 lines

  1. /*
  2. **  vlb.c     Virtual List Box Source Code file
  3. **
  4. **  Author: Robert A. Wood
  5. **          Executive Micro Systems
  6. **          1716 Azurite Trail
  7. **          Plano, TX 75075
  8. **
  9. **  Microsoft C version 5.1 / medium memory model
  10. **  Microsoft Windows SDK version 2.1
  11. **  Runtime: Windows 286 version 2.1
  12. **
  13. **    Date: 03/19/90
  14. */
  15.  
  16.  
  17. #include <windows.h>
  18. #undef   min
  19. #undef   max
  20. #include <stdlib.h>
  21. #include <stdio.h>
  22. #include "vlb.h"
  23. #include "lmem.h"
  24.  
  25. static   char     StringBuffer[ VLBSTRLEN + 1 ];
  26. static   char     szVLBPropName[]   =  "VLB";
  27. static   FARPROC  lpfnVLBProc =  NULL;
  28.  
  29. LPSTR FAR PASCAL lstrcpy (LPSTR, LPSTR) ;
  30. WORD FAR PASCAL Rpad( LPSTR str, WORD length );
  31.  
  32. /*
  33. **  Virtual List Box initialization function
  34. ********************************************************************/
  35. BOOL FAR PASCAL InitVLB( HANDLE hInstance, HWND hDlg,
  36.                          WORD ListBoxId, VLBPROC VLBCallBack )
  37. {
  38.    int         x;
  39.    HANDLE      hCtl;
  40.    HANDLE      hVLB;
  41.    LPVLB       lpVLB;
  42.    HDC         hDC;
  43.    TEXTMETRIC  tm;            // need info about character sizes
  44.    LONG  FAR * lpStringIds;
  45.  
  46.    // check that a call back function address was passed in
  47.    if( ! VLBCallBack )
  48.    {
  49.       MessageBeep( 0 );
  50.       MessageBox( hDlg, "Invalid VLBCallBack", NULL,
  51.                   MB_ICONHAND | MB_OK );
  52.       return( FALSE );
  53.    }
  54.  
  55.    // check for existence of ListBoxId in specified dialog box
  56.    if( ( hCtl = GetDlgItem( hDlg, ListBoxId ) ) == NULL )
  57.    {
  58.       MessageBeep( 0 );
  59.       MessageBox( hDlg, "Invalid List Box Id", NULL,
  60.                   MB_ICONHAND | MB_OK );
  61.       return( FALSE );
  62.    }
  63.  
  64.    // allocate VLB control information structure
  65.    if( hVLB = GlobalAlloc( GHND, (LONG)sizeof( VLB ) ) )
  66.       lpVLB = (LPVLB)GlobalLock( hVLB );
  67.    else
  68.    {
  69.       SendMessage( hDlg, WM_COMMAND, ListBoxId,
  70.                    MAKELONG( hCtl, VLBN_ERRSPACE ) );
  71.       return( FALSE );
  72.    }
  73.  
  74.    hDC = GetDC( hCtl );
  75.    GetTextMetrics( hDC, &tm );
  76.    ReleaseDC( hCtl, hDC );
  77.  
  78.    lpVLB->ListBoxId = ListBoxId;
  79.    lpVLB->MultiSelection = (BOOL)
  80.                 (GetWindowLong( hCtl, GWL_STYLE ) & LBS_MULTIPLESEL);
  81.    GetClientRect( hCtl, &lpVLB->ClientRect );
  82.    lpVLB->CharWidth = tm.tmAveCharWidth;
  83.    lpVLB->CharHeight = tm.tmHeight + tm.tmExternalLeading;
  84.    lpVLB->DisplayStrings = (BYTE)
  85.                         lpVLB->ClientRect.bottom / lpVLB->CharHeight;
  86.    lpVLB->DisplayChars = (BYTE)
  87.                           lpVLB->ClientRect.right / lpVLB->CharWidth;
  88.    lpVLB->FocusString = 0;
  89.    lpVLB->FirstDisplayString = 0;
  90.    lpVLB->TotalSelectedStrings = 0;
  91.    lpVLB->MaxSelectedStrings = lpVLB->DisplayStrings;
  92.    VLB_CALLBACK = VLBCallBack;
  93.    lpVLB->SelectedStringId = -1;
  94.  
  95.    // allocate buffer for displayed strings
  96.    if( !(lpVLB->hDisplayBuffer = GlobalAlloc( GHND, (LONG)
  97.            (lpVLB->DisplayStrings * (lpVLB->DisplayChars + 1) ) ) ) )
  98.    {
  99.       GlobalUnlock( hVLB );
  100.       GlobalFree( hVLB );
  101.       SendMessage( hDlg, WM_COMMAND, ListBoxId,
  102.                    MAKELONG( hCtl, VLBN_ERRSPACE ) );
  103.       return( FALSE );
  104.    }
  105.  
  106.    // allocate array of longs for the string Ids of displayed strings
  107.    if( !(lpVLB->hStringIds = GlobalAlloc( GHND, (LONG)
  108.                       (lpVLB->DisplayStrings * sizeof( LONG ) ) ) ) )
  109.    {
  110.       GlobalUnlock( lpVLB->hDisplayBuffer );
  111.       GlobalFree( lpVLB->hDisplayBuffer );
  112.       GlobalUnlock( hVLB );
  113.       GlobalFree( hVLB );
  114.       SendMessage( hDlg, WM_COMMAND, ListBoxId,
  115.                    MAKELONG( hCtl, VLBN_ERRSPACE ) );
  116.       return( FALSE );
  117.    }
  118.  
  119.    // initialize string Ids to -1
  120.    lpStringIds = (LONG FAR *) GlobalLock( lpVLB->hStringIds );
  121.    for( x = 0; x < lpVLB->DisplayStrings; x++, lpStringIds[x] = -1L )
  122.       ;
  123.    GlobalUnlock( lpVLB->hStringIds );
  124.  
  125.    // setup for a multiselection list box
  126.    if( lpVLB->MultiSelection )
  127.    {
  128.       LONG  FAR * lpSelectedStringIds;
  129.  
  130.       // allocate array of longs for the selected string Id's
  131.       if( !(lpVLB->hSelectedStringIds = GlobalAlloc( GHND, (LONG)
  132.                   (lpVLB->MaxSelectedStrings * sizeof( LONG ) ) ) ) )
  133.       {
  134.          GlobalUnlock( lpVLB->hStringIds );
  135.          GlobalFree( lpVLB->hStringIds );
  136.          GlobalUnlock( lpVLB->hDisplayBuffer );
  137.          GlobalFree( lpVLB->hDisplayBuffer );
  138.          GlobalUnlock( hVLB );
  139.          GlobalFree( hVLB );
  140.          SendMessage( hDlg, WM_COMMAND, ListBoxId,
  141.                                    MAKELONG( hCtl, VLBN_ERRSPACE ) );
  142.          return( FALSE );
  143.       }
  144.  
  145.       // initialize selected string Ids to -1
  146.       lpSelectedStringIds = (LONG FAR *)
  147.                              GlobalLock( lpVLB->hSelectedStringIds );
  148.       for( x = 0; x < lpVLB->DisplayStrings;
  149.            x++, lpSelectedStringIds[x] = -1L )
  150.          ;
  151.       GlobalUnlock( lpVLB->hSelectedStringIds );
  152.    }
  153.  
  154.    if( !lpfnVLBProc )
  155.       lpfnVLBProc = MakeProcInstance( (FARPROC) VLBProc, hInstance );
  156.  
  157.    // subclass VLB
  158.    SetWindowLong( hCtl, GWL_WNDPROC, (LONG) lpfnVLBProc );
  159.  
  160.    // store handle to VLB structure in the control's property list
  161.    SetProp( hCtl, szVLBPropName, hVLB );
  162.  
  163.    // send create messages to control and to the VLB callback func
  164.    SendMessage( hCtl, WM_CREATE, 0, 0L );
  165.    VLB_CALLBACK( ListBoxId, VCB_CREATE, 0, 0L );
  166.  
  167.    // get the vitual list box length and set the scroll range
  168.    lpVLB->TotalStrings = VLB_CALLBACK( ListBoxId, VCB_LENGTH, 0, 0L);
  169.    SetScrollRange( hCtl, SB_VERT, 0, (int)
  170.              lpVLB->TotalStrings - lpVLB->DisplayStrings - 1, TRUE );
  171.  
  172.    // check for horizontally scrolling
  173.    if( GetWindowLong( hCtl, GWL_STYLE ) & WS_HSCROLL )
  174.    {
  175.       // get the vitual list box width and set the scroll range
  176.       lpVLB->TotalWidth = VLB_CALLBACK( ListBoxId, VCB_WIDTH, 0, 0L);
  177.       SetScrollRange( hCtl, SB_HORZ, 0, (int)
  178.                                        lpVLB->TotalWidth - 1, TRUE );
  179.    }
  180.  
  181.    // load list box
  182.    VLB_CALLBACK( ListBoxId, VCB_SETFOCUS, 0, 0L );
  183.    SendMessage( hCtl, VLB_RELOAD, RELOAD_STRINGPOS, 0L );
  184.    VLB_CALLBACK( ListBoxId, VCB_KILLFOCUS, 0, 0L );
  185.  
  186.    // I'm out of here
  187.    GlobalUnlock( hVLB );
  188.    return( TRUE );
  189. }
  190.  
  191. /*
  192. **  Virtual List Box Procedure
  193. ********************************************************************/
  194. LONG FAR PASCAL VLBProc( HWND hCtl, unsigned message,
  195.                          WORD wParam, LONG lParam )
  196. {
  197.    HANDLE   hVLB;
  198.    LPVLB    lpVLB;
  199.    LONG     lThumb;
  200.  
  201.    switch ( message )
  202.    {
  203.    case  WM_CHAR:
  204.       break;
  205.  
  206.    case  WM_SETCURSOR:            // override setting cursor
  207.       break;
  208.  
  209.    case WM_GETDLGCODE:            // don't let dialog manager have
  210.       return( DLGC_WANTARROWS );  // his way with the arrow keys
  211.  
  212.    case WM_CREATE:                // sent just after being subclassed
  213.       return( 1L );
  214.  
  215.    case VLB_RELOAD:               // reload the list box
  216.       LoadVLB( hCtl, wParam, lParam );
  217.       return( 1L );
  218.  
  219.    case WM_PAINT:                 // this is where it all takes shape
  220.       hVLB = GetProp( hCtl, szVLBPropName );
  221.       lpVLB = (LPVLB) GlobalLock( hVLB );
  222.  
  223.       PaintVLB( hCtl, lpVLB );
  224.  
  225.       GlobalUnlock( hVLB );
  226.       break;
  227.  
  228.    case WM_SETFOCUS:
  229.       HideCaret(hCtl);
  230.  
  231.       hVLB = GetProp( hCtl, szVLBPropName );
  232.       lpVLB = (LPVLB) GlobalLock( hVLB );
  233.  
  234.       VLB_CALLBACK( lpVLB->ListBoxId, VCB_SETFOCUS, 0, 0L );
  235.       FrameFocusString( hCtl, lpVLB, TRUE );
  236.  
  237.       GlobalUnlock( hVLB );
  238.       break;
  239.  
  240.    case WM_KILLFOCUS:
  241.       hVLB = GetProp( hCtl, szVLBPropName );
  242.       lpVLB = (LPVLB) GlobalLock( hVLB );
  243.  
  244.       FrameFocusString( hCtl, lpVLB, FALSE );
  245.       InvertSelectedStrings( hCtl, lpVLB, (int)( lpVLB->FocusString -
  246.                                        lpVLB->FirstDisplayString ) );
  247.  
  248.       VLB_CALLBACK( lpVLB->ListBoxId, VCB_KILLFOCUS, 0, 0L );
  249.  
  250.       GlobalUnlock( hVLB );
  251.       break;
  252.  
  253.    case WM_VSCROLL:
  254.       switch( wParam )
  255.       {
  256.       case SB_THUMBPOSITION:
  257.          LoadVLB( hCtl, RELOAD_STRINGPOS, (LONG)LOWORD( lParam ) );
  258.          break;
  259.  
  260.       case SB_LINEDOWN:
  261.       case SB_LINEUP:
  262.       case SB_PAGEDOWN:
  263.       case SB_PAGEUP:
  264.          ScrollVLB( hCtl, wParam, NULL );
  265.          break;
  266.       }
  267.       break;
  268.  
  269.    case WM_HSCROLL:
  270.       hVLB = GetProp( hCtl, szVLBPropName );
  271.       lpVLB = (LPVLB) GlobalLock( hVLB );
  272.  
  273.       lThumb = -1;
  274.  
  275.       switch( wParam )
  276.       {
  277.       case SB_THUMBPOSITION:
  278.          lThumb = (LONG)LOWORD( lParam );
  279.          lThumb = VLB_CALLBACK( lpVLB->ListBoxId, VCB_HTHUMB,
  280.                                 &lThumb, 0L );
  281.          LoadVLB( hCtl, RELOAD_STRINGS, 0L );
  282.          break;
  283.  
  284.       case SB_LINEDOWN:
  285.          lThumb = VLB_CALLBACK( lpVLB->ListBoxId, VCB_RIGHT, 0, 0L );
  286.          LoadVLB( hCtl, RELOAD_STRINGS, 0L );
  287.          break;
  288.  
  289.       case SB_LINEUP:
  290.          lThumb = VLB_CALLBACK( lpVLB->ListBoxId, VCB_LEFT, 0, 0L );
  291.          LoadVLB( hCtl, RELOAD_STRINGS, 0L );
  292.          break;
  293.  
  294.       case SB_PAGEDOWN:
  295.          lThumb = VLB_CALLBACK( lpVLB->ListBoxId,VCB_PAGERIGHT,0,0L);
  296.          LoadVLB( hCtl, RELOAD_STRINGS, 0L );
  297.          break;
  298.  
  299.       case SB_PAGEUP:
  300.          lThumb = VLB_CALLBACK( lpVLB->ListBoxId, VCB_PAGELEFT,0,0L);
  301.          LoadVLB( hCtl, RELOAD_STRINGS, 0L );
  302.          break;
  303.       }
  304.  
  305.       if( lThumb >= 0 )
  306.       {
  307.          lThumb = (LONG)min( (int)lpVLB->TotalWidth - 1,
  308.                         max( 0, (int)lThumb ) );
  309.          SetScrollPos( hCtl, SB_HORZ, (int)lThumb, TRUE );
  310.       }
  311.  
  312.       GlobalUnlock( hVLB );
  313.       break;
  314.  
  315.    case WM_MOUSEMOVE:
  316.       break;
  317.  
  318.    case WM_LBUTTONDBLCLK:
  319.          SendMessage( GetParent( hCtl ), WM_COMMAND,
  320.              GetWindowWord( hCtl, GWW_ID ),
  321.              MAKELONG( hCtl, VLBN_DBLCLK ) );
  322.       break;
  323.  
  324.    case WM_LBUTTONDOWN:
  325.    {
  326.       LONG lFocusString;
  327.  
  328.       hVLB = GetProp( hCtl, szVLBPropName );
  329.       lpVLB = (LPVLB) GlobalLock( hVLB );
  330.  
  331.       lpVLB->FocusString = lpVLB->FirstDisplayString +
  332.                      ( HIWORD( lParam ) / lpVLB->CharHeight );
  333.  
  334.       SetSelectedString( hCtl, NULL, lpVLB );
  335.  
  336.       InvalidateRect( hCtl, NULL, TRUE );
  337.  
  338.       GlobalUnlock( hVLB );
  339.       break;
  340.    }
  341.  
  342.    case WM_KEYDOWN:
  343.    {
  344.       BOOL  CtrlKey = HIBYTE( GetKeyState( VK_CONTROL ) );
  345.       hVLB = GetProp( hCtl, szVLBPropName );
  346.       lpVLB = (LPVLB) GlobalLock( hVLB );
  347.  
  348.       switch( wParam )
  349.       {
  350.       case ' ':
  351.       {
  352.          SetSelectedString( hCtl, wParam, lpVLB );
  353.  
  354.          InvalidateRect( hCtl, NULL, TRUE );
  355.          break;
  356.       }
  357.  
  358.       case VK_DOWN:
  359.          if( lpVLB->FocusString == lpVLB->FirstDisplayString +
  360.                                     lpVLB->DisplayStrings - 1 )
  361.             ScrollVLB( hCtl, SB_LINEDOWN, NULL );
  362.  
  363.          SetFocusString( wParam, lpVLB );
  364.  
  365.          if( ! CtrlKey )
  366.             SetSelectedString( hCtl, wParam, lpVLB );
  367.  
  368.          InvalidateRect( hCtl, NULL, TRUE );
  369.          PaintVLB( hCtl, lpVLB );
  370.          break;
  371.  
  372.       case VK_UP:
  373.          if( lpVLB->FocusString == lpVLB->FirstDisplayString )
  374.             ScrollVLB( hCtl, SB_LINEUP, NULL );
  375.  
  376.          SetFocusString( wParam, lpVLB );
  377.  
  378.          if( ! CtrlKey )
  379.             SetSelectedString( hCtl, wParam, lpVLB );
  380.  
  381.          InvalidateRect( hCtl, NULL, TRUE );
  382.          PaintVLB( hCtl, lpVLB );
  383.          break;
  384.  
  385.       case VK_NEXT:
  386.          if( lpVLB->FocusString == lpVLB->FirstDisplayString +
  387.                                     lpVLB->DisplayStrings - 1 )
  388.             ScrollVLB( hCtl, SB_PAGEDOWN, NULL );
  389.  
  390.          SetFocusString( wParam, lpVLB );
  391.  
  392.          if( ! CtrlKey )
  393.             SetSelectedString( hCtl, wParam, lpVLB );
  394.  
  395.          InvalidateRect( hCtl, NULL, TRUE );
  396.          PaintVLB( hCtl, lpVLB );
  397.          break;
  398.  
  399.       case VK_PRIOR:
  400.          if( lpVLB->FocusString == lpVLB->FirstDisplayString )
  401.             ScrollVLB( hCtl, SB_PAGEUP, NULL );
  402.  
  403.          SetFocusString( wParam, lpVLB );
  404.  
  405.          if( ! CtrlKey )
  406.             SetSelectedString( hCtl, wParam, lpVLB );
  407.  
  408.          InvalidateRect( hCtl, NULL, TRUE );
  409.          PaintVLB( hCtl, lpVLB );
  410.          break;
  411.  
  412.       case VK_HOME:
  413.          LoadVLB( hCtl, RELOAD_STRINGPOS, 0L );
  414.  
  415.          SetFocusString( wParam, lpVLB );
  416.  
  417.          if( ! CtrlKey )
  418.             SetSelectedString( hCtl, wParam, lpVLB );
  419.  
  420.          InvalidateRect( hCtl, NULL, TRUE );
  421.          break;
  422.  
  423.       case VK_END:
  424.          LoadVLB( hCtl, RELOAD_STRINGPOS, lpVLB->TotalStrings - 1 );
  425.  
  426.          SetFocusString( wParam, lpVLB );
  427.  
  428.          if( ! CtrlKey )
  429.             SetSelectedString( hCtl, wParam, lpVLB );
  430.  
  431.          InvalidateRect( hCtl, NULL, TRUE );
  432.          break;
  433.  
  434.       case VK_RIGHT:
  435.       case VK_LEFT:
  436.       {
  437.          int message;
  438.  
  439.          if( wParam == VK_RIGHT )
  440.          {
  441.             if( CtrlKey )
  442.                message = VCB_PAGERIGHT;
  443.             else
  444.                message = VCB_RIGHT;
  445.          }
  446.          else
  447.          {
  448.             if( CtrlKey )
  449.                message = VCB_PAGELEFT;
  450.             else
  451.                message = VCB_LEFT;
  452.          }
  453.  
  454.          lThumb = VLB_CALLBACK( lpVLB->ListBoxId, message, 0, 0L );
  455.          lThumb = (LONG)min( (int)lpVLB->TotalWidth - 1,
  456.                         max( 0, (int)lThumb ) );
  457.          SetScrollPos( hCtl, SB_HORZ, (int)lThumb, TRUE );
  458.          LoadVLB( hCtl, RELOAD_STRINGS, 0L );
  459.  
  460.          InvalidateRect( hCtl, NULL, TRUE );
  461.          break;
  462.       }
  463.       }
  464.       GlobalUnlock( hVLB );
  465.       break;
  466.    }
  467.  
  468.    case VLB_SETCURSEL:     // WM_USER+6   select specified StringId
  469.    case VLB_SETSEL:        // WM_USER+7   sets selection of StringId
  470.       break;
  471.  
  472.    case VLB_GETCOUNT:      // WM_USER+1   get total virtual strings
  473.    case VLB_GETSELCOUNT:   // WM_USER+2   get total selected strings
  474.    case VLB_GETSTRLEN:     // WM_USER+3   get total string length
  475.    case VLB_GETSELSTR:     // WM_USER+4   get the selected String
  476.    case VLB_GETSELID:      // WM_USER+5   get selected StringId(s)
  477.   {
  478.       LONG lret = 0;
  479.       hVLB = GetProp( hCtl, szVLBPropName );
  480.       lpVLB = (LPVLB) GlobalLock( hVLB );
  481.  
  482.       if( message == VLB_GETCOUNT )
  483.          lret = lpVLB->TotalStrings;
  484.       else if( message == VLB_GETSELCOUNT )
  485.          lret = lpVLB->TotalSelectedStrings;
  486.       else if( message == VLB_GETSTRLEN )
  487.          lret = lpVLB->CharWidth;
  488.       else if( message == VLB_GETSELSTR )
  489.       {
  490.          if( lpVLB->TotalSelectedStrings )
  491.          {
  492.             if( lpVLB->MultiSelection )
  493.             {
  494.                LONG FAR *lpSelectedStringIds =
  495.                   (LONG FAR *)GlobalLock( lpVLB->hSelectedStringIds);
  496.  
  497.                VLB_CALLBACK( lpVLB->ListBoxId, VCB_FULLSTRING,
  498.                        lpSelectedStringIds + wParam, StringBuffer );
  499.  
  500.                GlobalUnlock( lpVLB->hSelectedStringIds );
  501.             }
  502.             else
  503.                VLB_CALLBACK( lpVLB->ListBoxId, VCB_FULLSTRING,
  504.                        &lpVLB->SelectedStringId, StringBuffer );
  505.          }
  506.          else
  507.             *StringBuffer = 0;
  508.  
  509.          lret = (LONG)(LPSTR)StringBuffer;
  510.       }
  511.       else if( message == VLB_GETSELID )
  512.       {
  513.          if( lpVLB->MultiSelection )
  514.             lret = lpVLB->hSelectedStringIds;
  515.          else
  516.             lret = lpVLB->SelectedStringId;
  517.       }
  518.  
  519.       GlobalUnlock( hVLB );
  520.       return( lret );
  521.    }
  522.  
  523.    case WM_DESTROY:
  524.       hVLB = GetProp( hCtl, szVLBPropName );
  525.       lpVLB = (LPVLB) GlobalLock( hVLB );
  526.  
  527.       VLB_CALLBACK( lpVLB->ListBoxId, VCB_DESTROY, 0, 0L );
  528.  
  529.       GlobalFree( lpVLB->hDisplayBuffer );
  530.       GlobalFree( lpVLB->hStringIds );
  531.       if( lpVLB->MultiSelection )
  532.          GlobalFree( lpVLB->hSelectedStringIds );
  533.       GlobalUnlock( hVLB );
  534.       GlobalFree( hVLB );
  535.  
  536.       RemoveProp( hCtl, szVLBPropName );
  537.       break;
  538.  
  539.    default:
  540.        return( DefWindowProc( hCtl, message, wParam, lParam ) );
  541.    }
  542.  
  543.    return( 0L );
  544. }
  545.  
  546. //*******************************************************************
  547. VOID FrameFocusString( HWND hCtl, LPVLB lpVLB, BOOL draw )
  548. {
  549.    RECT  Rect;
  550.    HDC   hDC;
  551.  
  552.    // is the focus string visible
  553.    if( lpVLB->FocusString >= lpVLB->FirstDisplayString  &&
  554.        lpVLB->FocusString < lpVLB->FirstDisplayString +
  555.                            lpVLB->DisplayStrings )
  556.    {
  557.       hDC = GetDC( hCtl );
  558.  
  559.       Rect.left  = lpVLB->ClientRect.left + 1;
  560.       Rect.right = lpVLB->ClientRect.right;
  561.       Rect.top = (WORD) ( (lpVLB->FocusString -
  562.                  lpVLB->FirstDisplayString) * lpVLB->CharHeight) + 1;
  563.       Rect.bottom = Rect.top + lpVLB->CharHeight;
  564.  
  565.       if( draw )
  566.          FrameRect( hDC, &Rect, GetStockObject( GRAY_BRUSH ) );
  567.       else  // redraw string without frame
  568.       {
  569.          LPSTR lpString = (LPSTR)GlobalLock( lpVLB->hDisplayBuffer );
  570.          WORD nLine = (WORD)
  571.                   ( lpVLB->FocusString - lpVLB->FirstDisplayString );
  572.  
  573.          // move display buffer pointer to focus string
  574.          lpString += nLine * ( lpVLB->DisplayChars + 1 );
  575.  
  576.          TextOut ( hDC, 1, ( nLine * lpVLB->CharHeight ) + 1,
  577.                    lpString, lpVLB->DisplayChars );
  578.  
  579.          GlobalUnlock( lpVLB->hDisplayBuffer );
  580.       }
  581.       ReleaseDC( hCtl, hDC );
  582.    }
  583. }
  584.  
  585. //*******************************************************************
  586. VOID InvertSelectedStrings( HDC hCtl, LPVLB lpVLB, int StringPos )
  587. {
  588.    int  x;
  589.    LONG  y;
  590.    RECT  Rect;
  591.    int FirstString, LastString;
  592.    LONG FAR *lpStringIds;
  593.    HDC   hDC;
  594.  
  595.    hDC = GetDC( hCtl );
  596.  
  597.    lpStringIds = (LONG FAR *)GlobalLock( lpVLB->hStringIds );
  598.  
  599.    Rect.left  = lpVLB->ClientRect.left + 1;
  600.    Rect.right = lpVLB->ClientRect.right;
  601.  
  602.    if( StringPos < 0 )      // process all displayed strings
  603.    {
  604.       FirstString = 0;
  605.       LastString = (int)lpVLB->DisplayStrings - 1;
  606.    }
  607.    else
  608.       FirstString = LastString = StringPos;
  609.  
  610.    if( lpVLB->MultiSelection )
  611.    {
  612.       LONG FAR *lpSelectedStringIds =
  613.                (LONG FAR *)GlobalLock( lpVLB->hSelectedStringIds );
  614.  
  615.       for( x = FirstString; x <= LastString; x++ )
  616.       {
  617.          for( y = 0; y < lpVLB->TotalSelectedStrings; y++ )
  618.          {
  619.             if( lpStringIds[x] == lpSelectedStringIds[ y ] )
  620.             {
  621.                Rect.top = ( x * lpVLB->CharHeight ) + 1;
  622.                Rect.bottom = Rect.top + lpVLB->CharHeight;
  623.                InvertRect( hDC, &Rect );
  624.                break;
  625.             }
  626.          }
  627.       }
  628.       GlobalUnlock( lpVLB->hSelectedStringIds );
  629.    }
  630.    else  // single selection
  631.    {
  632.       for( x = FirstString; x <= LastString; x++ )
  633.       {
  634.          if( lpStringIds[x] == lpVLB->SelectedStringId )
  635.          {
  636.             Rect.top = ( x * lpVLB->CharHeight ) + 1;
  637.             Rect.bottom = Rect.top + lpVLB->CharHeight;
  638.             InvertRect( hDC, &Rect );
  639.             break;
  640.          }
  641.       }
  642.    }
  643.    ReleaseDC( hCtl, hDC );
  644.    GlobalUnlock( lpVLB->hStringIds );
  645. }
  646.  
  647. //*******************************************************************
  648. WORD FAR PASCAL Rpad( LPSTR str, WORD length )
  649. {
  650.    LPSTR cp = str;                  // pointer to string
  651.    int x;                           // current string position
  652.    for( x = 0; *cp; x++, cp++ )     // skip to end of string
  653.       ;
  654.    for( ; x < length; x++, *cp++ = ' ' )  // pad string with spaces
  655.       ;
  656.    *cp = 0;                         // NULL terminate string
  657.    return( length );                // return new string length
  658. }
  659.  
  660. //*******************************************************************
  661. BOOL FAR PASCAL LoadVLB( HWND hCtl, WORD wParam, LONG lParam )
  662. {
  663.    int x;
  664.    HANDLE hVLB = GetProp( hCtl, szVLBPropName );
  665.    LPVLB lpVLB = (LPVLB)GlobalLock( hVLB );
  666.    LPSTR lpString = (LPSTR)GlobalLock( lpVLB->hDisplayBuffer );
  667.    LONG FAR *lpStringIds = (LONG FAR *)GlobalLock(lpVLB->hStringIds);
  668.    LONG l;
  669.  
  670.    // initialize the DisplayBuffer and StringIds
  671.    lmemset( lpString, 0,
  672.                  lpVLB->DisplayStrings * (lpVLB->DisplayChars + 1) );
  673.    for( x = 0; x < lpVLB->DisplayStrings; x++, lpStringIds[x] = -1L )
  674.       ;
  675.  
  676.    if( wParam == RELOAD_STRINGS )
  677.       VLB_CALLBACK( lpVLB->ListBoxId, VCB_STRING,
  678.                     &lpStringIds[0], StringBuffer );
  679.    else if( wParam == RELOAD_STRINGPOS )
  680.    {
  681.       lpStringIds[0] = min( lParam,
  682.                        lpVLB->TotalStrings - lpVLB->DisplayStrings );
  683.       lpVLB->FirstDisplayString = lpStringIds[0];
  684.       VLB_CALLBACK( lpVLB->ListBoxId, VCB_VTHUMB,
  685.                     &lpStringIds[0], StringBuffer );
  686.    }
  687.    else if( wParam == RELOAD_STRINGID )
  688.    {
  689.       lpStringIds[0] = lParam;
  690.       VLB_CALLBACK( lpVLB->ListBoxId, VCB_STRING,
  691.                     &lpStringIds[0], StringBuffer );
  692.    }
  693.  
  694.    // load first DisplayString
  695.    StringBuffer[ lpVLB->DisplayChars ] = 0; // Null @ Display width
  696.    lstrcpy( lpString, StringBuffer );       // advance to next string
  697.    Rpad( lpString, lpVLB->DisplayChars );   // pad with spaces
  698.  
  699.    for( x = 1; x < lpVLB->DisplayStrings; x++ )
  700.    {
  701.       l = lpStringIds[x - 1];
  702.       if( VLB_CALLBACK( lpVLB->ListBoxId, VCB_NEXT,
  703.                         &l, StringBuffer ) )
  704.       {
  705.          StringBuffer[ lpVLB->DisplayChars ] = 0;  // Null Terminate
  706.          lpString += lpVLB->DisplayChars + 1;      // go to next str
  707.          lstrcpy( lpString, StringBuffer );        // copy buf to str
  708.          Rpad( lpString, lpVLB->DisplayChars );    // pad with spaces
  709.          lpStringIds[x] = l;
  710.       }
  711.       else
  712.          break;
  713.    }
  714.  
  715.    InvalidateRect( hCtl, NULL, TRUE );      // Force WM_PAINT message
  716.  
  717.    // set new scroll bar thumb postion
  718.    SetScrollPos( hCtl, SB_VERT, (int)lpVLB->FirstDisplayString,TRUE);
  719.  
  720.    GlobalUnlock( lpVLB->hDisplayBuffer );
  721.    GlobalUnlock( lpVLB->hStringIds );
  722.    GlobalUnlock( hVLB );
  723.    return( TRUE );
  724. }
  725.  
  726. //*******************************************************************
  727. BOOL FAR PASCAL ScrollVLB( HWND hCtl, WORD wParam, int Scroll )
  728. {
  729.    int x, scroll, ret = TRUE;
  730.    HANDLE hVLB = GetProp( hCtl, szVLBPropName );
  731.    LPVLB lpVLB = (LPVLB)GlobalLock( hVLB );
  732.    LPSTR lpString = (LPSTR)GlobalLock( lpVLB->hDisplayBuffer );
  733.    LONG FAR *lpStringIds = (LONG FAR *)GlobalLock(lpVLB->hStringIds);
  734.    LONG l;
  735.    LPSTR lpGoodStrings;
  736.    int nGoodStrings;
  737.  
  738.    // Check if scroll request is possible
  739.    if( wParam == SB_LINEDOWN )
  740.    {  // at the end of the total strings
  741.       if( lpVLB->FirstDisplayString + lpVLB->DisplayStrings >
  742.                                             lpVLB->TotalStrings - 1 )
  743.          ret = FALSE;
  744.       else
  745.          scroll = 1;
  746.       lpVLB->ScrollWindow = 1;
  747.    }
  748.    else if( wParam == SB_LINEUP )
  749.    {
  750.       if( lpVLB->FirstDisplayString == 0 )
  751.          ret = FALSE;
  752.       else
  753.          scroll = 1;
  754.       lpVLB->ScrollWindow = -1;
  755.    }
  756.    else if( wParam == SB_PAGEDOWN )
  757.    {
  758.       if( lpVLB->FirstDisplayString + lpVLB->DisplayStrings >
  759.                                             lpVLB->TotalStrings - 1 )
  760.          ret = FALSE;
  761.       else
  762.          scroll = min( (int)lpVLB->DisplayStrings - 1,
  763.             (int)(lpVLB->TotalStrings - ( lpVLB->FirstDisplayString +
  764.                                          lpVLB->DisplayStrings ) ) );
  765.       lpVLB->ScrollWindow = scroll;
  766.    }
  767.    else if( wParam == SB_PAGEUP )
  768.    {
  769.       if( lpVLB->FirstDisplayString == 0 )
  770.          ret = FALSE;
  771.       else
  772.          scroll = min( (int)lpVLB->DisplayStrings - 1,
  773.                        (int)lpVLB->FirstDisplayString );
  774.       lpVLB->ScrollWindow = scroll * -1;
  775.    }
  776.    else         // scroll number of the Scroll 3rd paramater
  777.    {
  778.       lpVLB->ScrollWindow = Scroll;
  779.       if( Scroll < 0 )
  780.       {
  781.          Scroll = abs( Scroll );         // absolute scroll amount
  782.          wParam = SB_LINEUP;             // scroll up
  783.       }
  784.       else if( Scroll > 0 )
  785.          wParam = SB_LINEDOWN;           // scroll down
  786.       else //  if( Scroll == 0 )         // no scroll amt specified
  787.          ret = FALSE;
  788.  
  789.       if( Scroll >= lpVLB->DisplayStrings )  // scroll less than #
  790.          ret = FALSE;                        // of displayed strings
  791.       else
  792.          scroll = Scroll;
  793.    }
  794.  
  795.    if( ret == FALSE )
  796.    {
  797.       lpVLB->ScrollWindow = FALSE;
  798.       GlobalUnlock( lpVLB->hDisplayBuffer );
  799.       GlobalUnlock( lpVLB->hStringIds );
  800.       GlobalUnlock( hVLB );
  801.       return( FALSE );
  802.    }
  803.  
  804.    // pointer to strings that will still be displayed
  805.    lpGoodStrings = lpString + ( (lpVLB->DisplayChars + 1) * scroll );
  806.    // number of strings that will still be displayed
  807.    nGoodStrings = ( lpVLB->DisplayStrings - scroll );
  808.  
  809.    // adjust strings that will still be displayed and get new strings
  810.    if( wParam == SB_LINEUP  ||  wParam == SB_PAGEUP )
  811.    {
  812.       lpVLB->FirstDisplayString -= scroll;
  813.  
  814.       // push good strings & StringIds down in their buffers
  815.       lmemmove( lpGoodStrings, lpString,
  816.                           nGoodStrings * (lpVLB->DisplayChars + 1) );
  817.       lmemmove( lpStringIds + scroll, lpStringIds,
  818.                                      nGoodStrings * sizeof( LONG ) );
  819.  
  820.       // move Display string pointer to last string to get
  821.       lpString += ( scroll - 1 ) * (lpVLB->DisplayChars + 1);
  822.  
  823.       // get the previous scroll number of strings
  824.       for( x = 0; x < scroll; x++ )
  825.       {
  826.          // get the StringId of the last string read
  827.          l = lpStringIds[ scroll - x  ];
  828.  
  829.          if( VLB_CALLBACK( lpVLB->ListBoxId, VCB_PREV,
  830.                            &l, StringBuffer ) )
  831.          {
  832.             StringBuffer[ lpVLB->DisplayChars ] = 0;// Null Terminate
  833.             lstrcpy( lpString, StringBuffer );     // copy buf to str
  834.             Rpad( lpString, lpVLB->DisplayChars ); // pad with spaces
  835.             lpString -= lpVLB->DisplayChars + 1;   // go to next str
  836.             lpStringIds[ ( scroll - x ) - 1 ] = l; // save StringId
  837.          }
  838.          else
  839.             break;
  840.       }
  841.    }
  842.    else     // if( wParam == SB_LINEDOWN  ||  wParam == SB_PAGEDOWN )
  843.    {
  844.       lpVLB->FirstDisplayString += scroll;
  845.  
  846.       // move good strings & StringIds to the beginning of their bufs
  847.       lmemmove( lpString, lpGoodStrings,
  848.                           nGoodStrings * (lpVLB->DisplayChars + 1) );
  849.       lmemmove( lpStringIds, lpStringIds + scroll,
  850.                                      nGoodStrings * sizeof( LONG ) );
  851.  
  852.       // move Display string pointer to first string to get
  853.       lpString += nGoodStrings * (lpVLB->DisplayChars + 1);
  854.  
  855.       // get the next scroll number of strings
  856.       for( x = 0; x < scroll; x++ )
  857.       {
  858.          // get the StringId of the last string read
  859.          l = lpStringIds[ ( nGoodStrings + x ) - 1 ];
  860.  
  861.          if( VLB_CALLBACK( lpVLB->ListBoxId, VCB_NEXT,
  862.                            &l, StringBuffer ) )
  863.          {
  864.             StringBuffer[ lpVLB->DisplayChars ] = 0;// Null Terminate
  865.             lstrcpy( lpString, StringBuffer );     // copy buf to str
  866.             Rpad( lpString, lpVLB->DisplayChars ); // pad with spaces
  867.             lpString += lpVLB->DisplayChars + 1;   // go to next str
  868.             lpStringIds[ nGoodStrings + x ] = l;   // save StringId
  869.          }
  870.          else
  871.             break;
  872.       }
  873.    }
  874.  
  875.    InvalidateRect( hCtl, NULL, TRUE );      // Force WM_PAINT message
  876.  
  877.    // set new scroll bar thumb postion
  878.    SetScrollPos( hCtl, SB_VERT, (int)lpVLB->FirstDisplayString,TRUE);
  879.  
  880.    GlobalUnlock( lpVLB->hDisplayBuffer );
  881.    GlobalUnlock( lpVLB->hStringIds );
  882.    GlobalUnlock( hVLB );
  883.    return( TRUE );
  884. }
  885.  
  886. //*******************************************************************
  887. VOID FAR PASCAL SetSelectedString( HWND hCtl, WORD wParam,
  888.                                    LPVLB lpVLB )
  889. {
  890.    LONG  FAR *lpStringIds;
  891.    LONG  FAR *lpSelectedStringId;
  892.  
  893.    lpStringIds = (LONG FAR *)GlobalLock( lpVLB->hStringIds );
  894.  
  895.    if( lpVLB->MultiSelection )
  896.    {
  897.       LONG  FAR *lpSelectedStringIds;
  898.  
  899.       if( ! HIBYTE( GetKeyState( VK_SHIFT ) ) )
  900.          lpVLB->TotalSelectedStrings = 0;
  901.       else
  902.       {
  903.          if(lpVLB->TotalSelectedStrings == lpVLB->MaxSelectedStrings)
  904.          {
  905.             lpVLB->MaxSelectedStrings += lpVLB->DisplayStrings;
  906.             if( ! ( lpVLB->hSelectedStringIds =
  907.                GlobalReAlloc( lpVLB->hSelectedStringIds,
  908.                lpVLB->MaxSelectedStrings * sizeof( LONG ), GHND ) ) )
  909.             {
  910.                lpVLB->MaxSelectedStrings -= lpVLB->DisplayStrings;
  911.                SendMessage( GetParent( hCtl ), WM_COMMAND,
  912.                  lpVLB->ListBoxId, MAKELONG( hCtl, VLBN_ERRSPACE ) );
  913.                return;
  914.             }
  915.          }
  916.       }
  917.       lpSelectedStringIds = (LONG FAR *)
  918.                              GlobalLock( lpVLB->hSelectedStringIds );
  919.       lpSelectedStringId =
  920.                  lpSelectedStringIds + lpVLB->TotalSelectedStrings++;
  921.    }
  922.    else
  923.    {
  924.       lpVLB->TotalSelectedStrings = 1;
  925.       lpSelectedStringId = &lpVLB->SelectedStringId;
  926.    }
  927.  
  928.    switch( wParam )
  929.    {
  930.    case NULL:              // mouse left button click
  931.    case ' ':               // space bar select focus string
  932.    case VK_DOWN:
  933.    case VK_UP:
  934.       *lpSelectedStringId =
  935.          lpStringIds[lpVLB->FocusString - lpVLB->FirstDisplayString];
  936.       break;
  937.  
  938.    case VK_NEXT:
  939.    case VK_END:
  940.       *lpSelectedStringId = lpStringIds[lpVLB->DisplayStrings-1];
  941.       break;
  942.  
  943.    case VK_PRIOR:
  944.    case VK_HOME:
  945.       *lpSelectedStringId = lpStringIds[0];
  946.       break;
  947.    }
  948.  
  949.       SendMessage( GetParent( hCtl ), WM_COMMAND, lpVLB->ListBoxId,
  950.                                   MAKELONG( hCtl, VLBN_SELCHANGE ) );
  951.  
  952.    if( lpVLB->MultiSelection )
  953.       GlobalUnlock( lpVLB->hSelectedStringIds );
  954.    GlobalUnlock( lpVLB->hStringIds );
  955. }
  956.  
  957. //*******************************************************************
  958. VOID FAR PASCAL SetFocusString( WORD wParam, LPVLB lpVLB )
  959. {
  960.    switch( wParam )
  961.    {
  962.    case VK_DOWN:
  963.    case VK_UP:
  964.       if( lpVLB->FocusString < lpVLB->FirstDisplayString )
  965.          lpVLB->FocusString = lpVLB->FirstDisplayString;
  966.       else if( lpVLB->FocusString >
  967.                lpVLB->FirstDisplayString +lpVLB->DisplayStrings - 1 )
  968.          lpVLB->FocusString =
  969.                lpVLB->FirstDisplayString + lpVLB->DisplayStrings - 1;
  970.       else
  971.       {
  972.          if( wParam == VK_DOWN )
  973.          {
  974.             if( lpVLB->FocusString < lpVLB->TotalStrings - 1 )
  975.               lpVLB->FocusString++;
  976.          }
  977.          else
  978.             if( lpVLB->FocusString )
  979.                lpVLB->FocusString--;
  980.       }
  981.       break;
  982.  
  983.    case VK_NEXT:
  984.       lpVLB->FocusString =
  985.                lpVLB->FirstDisplayString + lpVLB->DisplayStrings - 1;
  986.       break;
  987.  
  988.    case VK_PRIOR:
  989.       lpVLB->FocusString = lpVLB->FirstDisplayString;
  990.       break;
  991.  
  992.    case VK_HOME:
  993.       lpVLB->FocusString = 0;
  994.       break;
  995.  
  996.    case VK_END:
  997.       lpVLB->FocusString = lpVLB->TotalStrings - 1;
  998.       break;
  999.    }
  1000. }
  1001.  
  1002. //*******************************************************************
  1003. VOID FAR PASCAL PaintVLB( HWND hCtl, LPVLB lpVLB )
  1004. {
  1005.    int         x, y, first, last;
  1006.    HDC         hDC;
  1007.    PAINTSTRUCT ps;
  1008.    LPSTR       lpString;
  1009.    RECT        Rect;
  1010.    LONG  FAR * lpStringIds;
  1011.  
  1012.    // let's get the display string buffer and each string's Id
  1013.    lpString = (LPSTR)GlobalLock( lpVLB->hDisplayBuffer );
  1014.    lpStringIds = (LONG FAR *)GlobalLock( lpVLB->hStringIds );
  1015.  
  1016.    hDC = BeginPaint( hCtl, &ps );
  1017.  
  1018.    GetVLBColors( hCtl, hDC );
  1019.  
  1020.    first = 0;
  1021.    last = lpVLB->DisplayStrings - 1;
  1022.  
  1023.    for( x = first, y = 1; x <= last; x++, y += lpVLB->CharHeight )
  1024.    {
  1025.       if( lpStringIds[x] >= 0 )
  1026.       {
  1027.          TextOut ( hDC, 1, y, lpString, lpVLB->DisplayChars );
  1028.          lpString += lpVLB->DisplayChars + 1;
  1029.       }
  1030.       else
  1031.       {
  1032.          lmemset( lpString, ' ', lpVLB->DisplayChars );
  1033.          TextOut ( hDC, 1, y, lpString, lpVLB->DisplayChars );
  1034.       }
  1035.    }
  1036.  
  1037.    EndPaint( hCtl, &ps );
  1038.  
  1039.    if( lpVLB->TotalSelectedStrings )
  1040.       InvertSelectedStrings( hCtl, lpVLB, -1 );
  1041.  
  1042.    if( hCtl == GetFocus() )
  1043.       FrameFocusString( hCtl, lpVLB, TRUE );
  1044.  
  1045.    lpVLB->ScrollWindow = FALSE;
  1046.    GlobalUnlock( lpVLB->hDisplayBuffer );
  1047.    GlobalUnlock( lpVLB->hStringIds );
  1048. }
  1049.  
  1050. //*******************************************************************
  1051. VOID FAR PASCAL GetVLBColors( HWND hCtl, HANDLE hDC )
  1052. {
  1053.    HANDLE hBrush;
  1054.  
  1055.    SetBkColor( hDC, GetSysColor( COLOR_WINDOW ) );
  1056.    SetTextColor( hDC, GetSysColor( COLOR_WINDOWTEXT ) );
  1057.  
  1058.    if( hBrush = (HANDLE)SendMessage( GetParent( hCtl ), WM_CTLCOLOR,
  1059.                           hDC, MAKELONG( hCtl, CTLCOLOR_LISTBOX ) ) )
  1060.       SelectObject( hDC, hBrush );
  1061. }
  1062.  
  1063. //*** END OF VLB.C **************************************************
  1064.