home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / VISUAL_B / REFERENC / LEARNVBX / MULTI.C < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-25  |  10.6 KB  |  405 lines

  1. /**
  2. .File
  3.     Multi.c
  4. .Name
  5.     MultiSelect ListBox for Visual Basic 1
  6. .Author
  7.     Dan Rogers
  8.     Big Dog Software
  9.     25 Shirley Parkway
  10.     Piscataway, N.J. 08854
  11.  
  12. .What
  13.     custom visual basic control for multi select list box
  14.     This control was written to provide multiple select and other
  15.     goodies to a VB1 program.  It is posted for the sake of 
  16.     education by example.  Later versions of VB require more 
  17.     standard properties (hwnd, for one) that are simulated here,
  18.     and probably need to be accounted for.
  19.  
  20. .How
  21.     Read-On, man...
  22.     The code for this list-box was derived from the subclassing examples
  23.     in the CDK documentation shipped with visual basic professional.
  24.     The CDK is required to do custom control development for visual
  25.     basic.  The reading is not straight forward, and some of the 
  26.     concepts illustrated here were gained by experience.  I hope you
  27.     benefit from my learning.
  28. **/
  29. /* the next line causes parts of windows.h to be left out */
  30. #define  NOCOMM
  31. #include <windows.h>
  32.  
  33. #include <vbapi.h>
  34.  
  35. /* the next line, #define, causes variable space to be declared when
  36.    this module is compiled.  Other modules compiled against multi.h
  37.    will not declare space because you will not define CTL_DATA.  In
  38.    any C program, it is crucial that space be allocated once and only
  39.    once. */
  40. #define  CTL_DATA           // To declare static data in multi.h
  41.  
  42. #include "multi.h"
  43.  
  44.  
  45. /*
  46. Standard Error Values
  47. */
  48. #define ERR_None              0
  49. #define ERR_InvPropVal      380     /* Error$(380) = "Invalid property value" */
  50.  
  51.  
  52. /* Local Prototypes */
  53. /* none! */
  54.  
  55. /* Event Procedure Parameter Profiles */
  56. /* the tagPARAMS3 structure sets up the space for the arguments to the fire-back
  57.    event processing.  The fireback event will be called for each selection
  58.    made in the list at run-time.
  59.  */
  60. typedef struct tagPARAMS3  /* used in fireback */
  61. {
  62.     LPLONG  Value;
  63.     LPINT   Offset;
  64.     HLSTR   ClickString;
  65.     LPVOID  Index;          /* Reserve space for index parameter to array ctl */
  66. } PARAMS3;
  67.  
  68. typedef struct tagPARAMS4    /* used in click */
  69. {
  70.     LPLONG  Value;
  71.     LPINT   Selected;
  72.     LPINT   Offset;
  73.     HLSTR   ClickString;
  74.     LPVOID  Index;          /* Reserve space for index parameter to array ctl */
  75. } PARAMS4;
  76.  
  77.  
  78. /****************************************/
  79. /* MultiSelectListBox Control Procedure */
  80. /****************************************/
  81. LONG FAR PASCAL _export MultiCtlProc
  82. (
  83.     HCTL        hctl,
  84.     HWND        hwnd,
  85.     USHORT      msg,
  86.     USHORT      wp,
  87.     LONG        lp
  88. )
  89. {
  90.     LONG        lResult;
  91.     LPLONG      longer;
  92.     LONG        args;
  93.     LONG        index;
  94.     PARAMS3 params3;
  95.     HSZ         item;
  96.     LPSTR       string;
  97.     LONG        ret;
  98.     ERR    err;
  99.     int i;
  100.     LPSTR  lpstr;
  101.     
  102.  
  103.     /* Message pre-processing */
  104.     switch (msg)
  105.     {
  106.     
  107.     /* setproperty messages sent by VB whenever programmer changes a property value */
  108.     case VBM_SETPROPERTY:
  109.         /* add property setting here for the properties that            */
  110.         /* require support of messages (list, sellist, selcnt, listcnt) */
  111.         switch (wp)
  112.         {
  113.             /* SELCOUNT property : unselect by setting to zero */
  114.             case IPROP_MULTI_SELCOUNT:
  115.             /* may zero the selected list */
  116.             if ( (int)lp )
  117.                 return(380);
  118.             SendMessage(hwnd, LB_SETSEL, 0, MAKELPARAM(-1,0));
  119.             break;
  120.             
  121.             /* LISTCOUNT property : empty the list by setting to zero */
  122.             case IPROP_MULTI_LISTCOUNT:
  123.             /* may zero the selected list */
  124.             if ( (int)lp )
  125.                 return(380);
  126.             SendMessage(hwnd, LB_RESETCONTENT, 0, 0L);
  127.             break;
  128.  
  129.             case IPROP_MULTI_FIREBACK:
  130.             /* setting this property causes the SelectDump event */
  131.             /* to be fired for each occurence of selected items  */
  132.             {
  133.             int count;
  134.             long dval;
  135.             int loop;
  136.             int top, len;
  137.             int hunt;                       
  138.             HGLOBAL hg;
  139.  
  140.             count = LOWORD(SendMessage(hwnd, LB_GETSELCOUNT, 0, 0L));
  141.             if ( count == 0 )
  142.                 return(0);
  143.             params3.Value = &dval;
  144.             params3.Offset = &loop;
  145.             for (i = 1; i <= count; i++)
  146.             {
  147.                 top = LOWORD(SendMessage(hwnd, LB_GETCOUNT, 0, 0L));
  148.                 hunt = 0;
  149.                 if (i > top)
  150.                     return(0);
  151.                 for ( loop = 0; loop < top; loop ++)
  152.                 {
  153.                     len = LOWORD(SendMessage(hwnd, LB_GETSEL, (WPARAM)loop, 0L));
  154.                     if ( len > 0 )
  155.                     {
  156.                         hunt ++;
  157.                         if (hunt == i )
  158.                             break;
  159.                     }
  160.                 }
  161.                 if (loop >= top)
  162.                     return(382); 
  163.                 len = LOWORD(SendMessage(hwnd, LB_GETTEXTLEN, (WPARAM)loop, 0L));
  164.                 hg = GlobalAlloc(GMEM_FIXED, (DWORD)(count + 1));
  165.                 if (!hg)
  166.                     return(383);
  167.                 lpstr = (LPSTR) GlobalLock(hg);
  168.                 SendMessage(hwnd, LB_GETTEXT, (WPARAM)loop, (LONG) lpstr);
  169.                 params3.ClickString = VBCreateHlstr((LPVOID)lpstr, (USHORT)len);
  170.                 if (params3.ClickString == NULL)
  171.                     return(384);
  172.                 dval = SendMessage(hwnd, LB_GETITEMDATA, (WPARAM)loop, 0L);
  173.                 err = VBFireEvent(hctl, EVENT_MULTI_SELECTDUMP, ¶ms3);
  174.                 if (params3.ClickString != NULL)
  175.                     VBDestroyHlstr(params3.ClickString);
  176.                 GlobalUnlock(hg);
  177.                 GlobalFree (hg); 
  178.             }
  179.             }
  180.             break;
  181.  
  182.             case IPROP_MULTI_LIST:
  183.             /* can be used as additem alternative */
  184.             /* this string array property uses no actual     */
  185.             /* data, but rather stores items in the list box */
  186.             {
  187.             int count;
  188.             int i;
  189.             LPDATASTRUCT lpDs = (LPDATASTRUCT)lp;
  190.  
  191.             i = (int)lpDs->index[0].data;
  192.             count = LOWORD(SendMessage(hwnd,LB_GETCOUNT, 0, 0L));
  193.             if (i > count || i < 0 )
  194.                 i = -1;
  195.             SendMessage(hwnd, LB_INSERTSTRING, (WPARAM)i, (LONG) (lpDs->data));
  196.             }
  197.             break;
  198.             
  199.             /* listdata property: long integer parallel array:  same
  200.                as the VB2/3 ItemData property
  201.             /*
  202.             case IPROP_MULTI_LISTDATA:
  203.             {
  204.             int count;
  205.             int i;
  206.             LPDATASTRUCT lpDs = (LPDATASTRUCT)lp;
  207.  
  208.             i = (int)lpDs->index[0].data;
  209.             count = LOWORD(SendMessage(hwnd,LB_GETCOUNT, 0, 0L));
  210.             if (i > count || i < 0 )
  211.                 return (381);
  212.             SendMessage(hwnd, LB_SETITEMDATA, (WPARAM)i, (LONG) (lpDs->data));
  213.             }
  214.             break;
  215.  
  216.  
  217.             /* SELLIST can be used to set selections (like selected property today)*/
  218.             case IPROP_MULTI_SELLIST:
  219.             {
  220.             int count;
  221.             int i;
  222.  
  223.             LPDATASTRUCT lpDs = (LPDATASTRUCT)lp;
  224.  
  225.             i = (int)lpDs->index[0].data;
  226.             count = LOWORD(SendMessage(hwnd,LB_GETCOUNT, 0, 0L));
  227.             if (i > (int)count || i < 0 )
  228.                 return (381);
  229.             if (*(LPSTR)lpDs->data)
  230.                 count = -1;
  231.             else
  232.                 count = 0;
  233.             SendMessage(hwnd, LB_SETSEL, (WPARAM)count, lpDs->index[0].data );
  234.             }
  235.             break;
  236.  
  237.                     
  238.             /* for moving the caret (the dashed line) */
  239.             case IPROP_MULTI_CARET:
  240.             {
  241.             int count;
  242.             int i;
  243.             i = (int)lp;
  244.             count = LOWORD(SendMessage(hwnd,LB_GETCOUNT, 0, 0L));
  245.             if (i > count || i < 0 )
  246.                 return (381);
  247.             SendMessage(hwnd, LB_SETCARETINDEX, (WPARAM)i, 0L);
  248.             }
  249.             break;
  250.         }
  251.         break;
  252.  
  253.  
  254.     /* Getproperty messages sent by vb when a value is requested by the programmer */
  255.     case VBM_GETPROPERTY:
  256.         switch(wp)
  257.         {
  258.         /* selection count (number of items selected)*/
  259.         case IPROP_MULTI_SELCOUNT:
  260.             *((int far *)lp) = (int)SendMessage(hwnd, LB_GETSELCOUNT, 0, 0L);
  261.             break;
  262.  
  263.         /* number of items in list */
  264.         case IPROP_MULTI_LISTCOUNT:
  265.             *((int far *)lp) = (int)SendMessage(hwnd, LB_GETCOUNT, 0, 0L);
  266.             break;
  267.         
  268.         /* caret position */
  269.         case IPROP_MULTI_CARET:
  270.             *((int far *)lp) = (int)SendMessage(hwnd, LB_GETCARETINDEX, 0, 0L);
  271.             break;
  272.         
  273.         /* window handle for control */
  274.         case IPROP_MULTI_HWND:
  275.             *((int far*)lp) = hwnd;
  276.             break;
  277.  
  278.         /* string contents as an array */
  279.         case IPROP_MULTI_LIST:
  280.         {
  281.             long count;
  282.             LONG i;
  283.             HGLOBAL hg;
  284.             LPDATASTRUCT lpDs = (LPDATASTRUCT)lp;
  285.             i = lpDs->index[0].data;
  286.             count = SendMessage(hwnd,LB_GETCOUNT, 0, 0L);
  287.             if (i > count || i < 0 )
  288.                 return(381);
  289.             count = SendMessage(hwnd, LB_GETTEXTLEN, (WPARAM)i, 0L);
  290.             /* using global memory (its easy) for string transfer */
  291.             hg = GlobalAlloc(GMEM_FIXED, (DWORD)(count + 1));
  292.             if (!hg)
  293.                 return(380);
  294.             lpstr = (LPSTR) GlobalLock(hg);
  295.             SendMessage(hwnd, LB_GETTEXT, (WPARAM)i, (LONG) lpstr);
  296.             /* when calling back into VB, must have the temp memory */
  297.             /* locked : VB may cause movement of memory during re-entrance */
  298.             lpDs->data = (LONG)VBCreateHsz((_segment)hctl, lpstr);
  299.             GlobalUnlock(hg);
  300.             GlobalFree (hg); 
  301.         }
  302.         break;
  303.             
  304.         /* get the nth selected item */
  305.         case IPROP_MULTI_SELLIST:
  306.         {
  307.             int count;
  308.             int loop, i;
  309.             int top, len;
  310.             int hunt;                       
  311.             HGLOBAL hg;
  312.             LPDATASTRUCT lpDs = (LPDATASTRUCT)lp;
  313.             i = (int)lpDs->index[0].data;
  314.             count = LOWORD(SendMessage(hwnd,LB_GETSELCOUNT, 0, 0L));
  315.             if (i > count || i < 1 )
  316.                 return(381);
  317.             top = LOWORD(SendMessage(hwnd, LB_GETCOUNT, 0, 0L));
  318.             hunt = 0;
  319.             for ( loop = 0; loop < top; loop ++)
  320.             {
  321.                 len = LOWORD(SendMessage(hwnd, LB_GETSEL, (WPARAM)loop, 0L));
  322.                 if ( len )
  323.                 {
  324.                     hunt ++;
  325.                     if (hunt == i )
  326.                         break;
  327.                 }
  328.             }
  329.             if (loop == top)
  330.                 return(381); 
  331.             len = LOWORD(SendMessage(hwnd, LB_GETTEXTLEN, (WPARAM)loop, 0L));
  332.             hg = GlobalAlloc(GMEM_FIXED, (DWORD)(count + 1));
  333.             if (!hg)
  334.                 return(380);
  335.             lpstr = (LPSTR) GlobalLock(hg);
  336.             SendMessage(hwnd, LB_GETTEXT, (WPARAM)loop, (LONG) lpstr);
  337.             lpDs->data = (LONG)VBCreateHsz((_segment)hctl, lpstr);
  338.             GlobalUnlock(hg);
  339.             GlobalFree (hg); 
  340.         }
  341.         break;
  342.  
  343.         
  344.         /* get the long int at the nth position */    
  345.         case IPROP_MULTI_LISTDATA:
  346.         {
  347.             int count;
  348.             int i;
  349.             LPDATASTRUCT lpDs = (LPDATASTRUCT)lp;
  350.             i = (int)lpDs->index[0].data;
  351.             count = LOWORD(SendMessage(hwnd,LB_GETCOUNT, 0, 0L));
  352.             if (i > count || i < 0 )
  353.                 return(381);
  354.             lpDs->data = SendMessage(hwnd, LB_GETITEMDATA, (WPARAM)i, 0L);
  355.         }
  356.         break;
  357.             
  358.         }
  359.         break;
  360.  
  361.     /* code to handle the optional methods for controls... */
  362.     case VBM_METHOD:
  363.         switch ( wp )
  364.         {
  365.         /* AddItem method: add item to list */
  366.         case METH_ADDITEM:
  367.             longer = (LPLONG )lp;
  368.             args = longer[0];
  369.             item = (HSZ)longer[1];
  370.             if (args > 2 )
  371.                 index = longer[2];
  372.             else
  373.                 index = -1;
  374.             string = VBDerefHsz(item);
  375.             
  376.             /* later on, if sorted property is set, use      */
  377.             /* the addstring method - honors sorted property */
  378.             SendMessage ( hwnd, LB_INSERTSTRING, (WPARAM)index, (LPARAM)(LPCSTR)string);
  379.             return (0);
  380.             
  381.         /* Method to remove specific item from the list */
  382.         case METH_REMOVEITEM:
  383.             longer = (LPLONG )lp;
  384.             args = longer[0];
  385.             index = longer[1];
  386.             ret = SendMessage ( hwnd, LB_DELETESTRING, (WPARAM)index, (LPARAM)0);
  387.             return (0);
  388.             
  389.             default:
  390.             break;
  391.         }
  392.         break;
  393.  
  394.     case VBN_DRAWITEM:
  395.         break;
  396.     
  397.     }
  398.  
  399.     /* Default processing: Pass thru to let VB handle it - talks to control */
  400.     lResult = VBDefControlProc(hctl, hwnd, msg, wp, lp);
  401.  
  402.     
  403.     return lResult;
  404. }
  405.