home *** CD-ROM | disk | FTP | other *** search
- /**
- .File
- Multi.c
- .Name
- MultiSelect ListBox for Visual Basic 1
- .Author
- Dan Rogers
- Big Dog Software
- 25 Shirley Parkway
- Piscataway, N.J. 08854
-
- .What
- custom visual basic control for multi select list box
- This control was written to provide multiple select and other
- goodies to a VB1 program. It is posted for the sake of
- education by example. Later versions of VB require more
- standard properties (hwnd, for one) that are simulated here,
- and probably need to be accounted for.
-
- .How
- Read-On, man...
- The code for this list-box was derived from the subclassing examples
- in the CDK documentation shipped with visual basic professional.
- The CDK is required to do custom control development for visual
- basic. The reading is not straight forward, and some of the
- concepts illustrated here were gained by experience. I hope you
- benefit from my learning.
- **/
- /* the next line causes parts of windows.h to be left out */
- #define NOCOMM
- #include <windows.h>
-
- #include <vbapi.h>
-
- /* the next line, #define, causes variable space to be declared when
- this module is compiled. Other modules compiled against multi.h
- will not declare space because you will not define CTL_DATA. In
- any C program, it is crucial that space be allocated once and only
- once. */
- #define CTL_DATA // To declare static data in multi.h
-
- #include "multi.h"
-
-
- /*
- Standard Error Values
- */
- #define ERR_None 0
- #define ERR_InvPropVal 380 /* Error$(380) = "Invalid property value" */
-
-
- /* Local Prototypes */
- /* none! */
-
- /* Event Procedure Parameter Profiles */
- /* the tagPARAMS3 structure sets up the space for the arguments to the fire-back
- event processing. The fireback event will be called for each selection
- made in the list at run-time.
- */
- typedef struct tagPARAMS3 /* used in fireback */
- {
- LPLONG Value;
- LPINT Offset;
- HLSTR ClickString;
- LPVOID Index; /* Reserve space for index parameter to array ctl */
- } PARAMS3;
-
- typedef struct tagPARAMS4 /* used in click */
- {
- LPLONG Value;
- LPINT Selected;
- LPINT Offset;
- HLSTR ClickString;
- LPVOID Index; /* Reserve space for index parameter to array ctl */
- } PARAMS4;
-
-
- /****************************************/
- /* MultiSelectListBox Control Procedure */
- /****************************************/
- LONG FAR PASCAL _export MultiCtlProc
- (
- HCTL hctl,
- HWND hwnd,
- USHORT msg,
- USHORT wp,
- LONG lp
- )
- {
- LONG lResult;
- LPLONG longer;
- LONG args;
- LONG index;
- PARAMS3 params3;
- HSZ item;
- LPSTR string;
- LONG ret;
- ERR err;
- int i;
- LPSTR lpstr;
-
-
- /* Message pre-processing */
- switch (msg)
- {
-
- /* setproperty messages sent by VB whenever programmer changes a property value */
- case VBM_SETPROPERTY:
- /* add property setting here for the properties that */
- /* require support of messages (list, sellist, selcnt, listcnt) */
- switch (wp)
- {
- /* SELCOUNT property : unselect by setting to zero */
- case IPROP_MULTI_SELCOUNT:
- /* may zero the selected list */
- if ( (int)lp )
- return(380);
- SendMessage(hwnd, LB_SETSEL, 0, MAKELPARAM(-1,0));
- break;
-
- /* LISTCOUNT property : empty the list by setting to zero */
- case IPROP_MULTI_LISTCOUNT:
- /* may zero the selected list */
- if ( (int)lp )
- return(380);
- SendMessage(hwnd, LB_RESETCONTENT, 0, 0L);
- break;
-
- case IPROP_MULTI_FIREBACK:
- /* setting this property causes the SelectDump event */
- /* to be fired for each occurence of selected items */
- {
- int count;
- long dval;
- int loop;
- int top, len;
- int hunt;
- HGLOBAL hg;
-
- count = LOWORD(SendMessage(hwnd, LB_GETSELCOUNT, 0, 0L));
- if ( count == 0 )
- return(0);
- params3.Value = &dval;
- params3.Offset = &loop;
- for (i = 1; i <= count; i++)
- {
- top = LOWORD(SendMessage(hwnd, LB_GETCOUNT, 0, 0L));
- hunt = 0;
- if (i > top)
- return(0);
- for ( loop = 0; loop < top; loop ++)
- {
- len = LOWORD(SendMessage(hwnd, LB_GETSEL, (WPARAM)loop, 0L));
- if ( len > 0 )
- {
- hunt ++;
- if (hunt == i )
- break;
- }
- }
- if (loop >= top)
- return(382);
- len = LOWORD(SendMessage(hwnd, LB_GETTEXTLEN, (WPARAM)loop, 0L));
- hg = GlobalAlloc(GMEM_FIXED, (DWORD)(count + 1));
- if (!hg)
- return(383);
- lpstr = (LPSTR) GlobalLock(hg);
- SendMessage(hwnd, LB_GETTEXT, (WPARAM)loop, (LONG) lpstr);
- params3.ClickString = VBCreateHlstr((LPVOID)lpstr, (USHORT)len);
- if (params3.ClickString == NULL)
- return(384);
- dval = SendMessage(hwnd, LB_GETITEMDATA, (WPARAM)loop, 0L);
- err = VBFireEvent(hctl, EVENT_MULTI_SELECTDUMP, ¶ms3);
- if (params3.ClickString != NULL)
- VBDestroyHlstr(params3.ClickString);
- GlobalUnlock(hg);
- GlobalFree (hg);
- }
- }
- break;
-
- case IPROP_MULTI_LIST:
- /* can be used as additem alternative */
- /* this string array property uses no actual */
- /* data, but rather stores items in the list box */
- {
- int count;
- int i;
- LPDATASTRUCT lpDs = (LPDATASTRUCT)lp;
-
- i = (int)lpDs->index[0].data;
- count = LOWORD(SendMessage(hwnd,LB_GETCOUNT, 0, 0L));
- if (i > count || i < 0 )
- i = -1;
- SendMessage(hwnd, LB_INSERTSTRING, (WPARAM)i, (LONG) (lpDs->data));
- }
- break;
-
- /* listdata property: long integer parallel array: same
- as the VB2/3 ItemData property
- /*
- case IPROP_MULTI_LISTDATA:
- {
- int count;
- int i;
- LPDATASTRUCT lpDs = (LPDATASTRUCT)lp;
-
- i = (int)lpDs->index[0].data;
- count = LOWORD(SendMessage(hwnd,LB_GETCOUNT, 0, 0L));
- if (i > count || i < 0 )
- return (381);
- SendMessage(hwnd, LB_SETITEMDATA, (WPARAM)i, (LONG) (lpDs->data));
- }
- break;
-
-
- /* SELLIST can be used to set selections (like selected property today)*/
- case IPROP_MULTI_SELLIST:
- {
- int count;
- int i;
-
- LPDATASTRUCT lpDs = (LPDATASTRUCT)lp;
-
- i = (int)lpDs->index[0].data;
- count = LOWORD(SendMessage(hwnd,LB_GETCOUNT, 0, 0L));
- if (i > (int)count || i < 0 )
- return (381);
- if (*(LPSTR)lpDs->data)
- count = -1;
- else
- count = 0;
- SendMessage(hwnd, LB_SETSEL, (WPARAM)count, lpDs->index[0].data );
- }
- break;
-
-
- /* for moving the caret (the dashed line) */
- case IPROP_MULTI_CARET:
- {
- int count;
- int i;
- i = (int)lp;
- count = LOWORD(SendMessage(hwnd,LB_GETCOUNT, 0, 0L));
- if (i > count || i < 0 )
- return (381);
- SendMessage(hwnd, LB_SETCARETINDEX, (WPARAM)i, 0L);
- }
- break;
- }
- break;
-
-
- /* Getproperty messages sent by vb when a value is requested by the programmer */
- case VBM_GETPROPERTY:
- switch(wp)
- {
- /* selection count (number of items selected)*/
- case IPROP_MULTI_SELCOUNT:
- *((int far *)lp) = (int)SendMessage(hwnd, LB_GETSELCOUNT, 0, 0L);
- break;
-
- /* number of items in list */
- case IPROP_MULTI_LISTCOUNT:
- *((int far *)lp) = (int)SendMessage(hwnd, LB_GETCOUNT, 0, 0L);
- break;
-
- /* caret position */
- case IPROP_MULTI_CARET:
- *((int far *)lp) = (int)SendMessage(hwnd, LB_GETCARETINDEX, 0, 0L);
- break;
-
- /* window handle for control */
- case IPROP_MULTI_HWND:
- *((int far*)lp) = hwnd;
- break;
-
- /* string contents as an array */
- case IPROP_MULTI_LIST:
- {
- long count;
- LONG i;
- HGLOBAL hg;
- LPDATASTRUCT lpDs = (LPDATASTRUCT)lp;
- i = lpDs->index[0].data;
- count = SendMessage(hwnd,LB_GETCOUNT, 0, 0L);
- if (i > count || i < 0 )
- return(381);
- count = SendMessage(hwnd, LB_GETTEXTLEN, (WPARAM)i, 0L);
- /* using global memory (its easy) for string transfer */
- hg = GlobalAlloc(GMEM_FIXED, (DWORD)(count + 1));
- if (!hg)
- return(380);
- lpstr = (LPSTR) GlobalLock(hg);
- SendMessage(hwnd, LB_GETTEXT, (WPARAM)i, (LONG) lpstr);
- /* when calling back into VB, must have the temp memory */
- /* locked : VB may cause movement of memory during re-entrance */
- lpDs->data = (LONG)VBCreateHsz((_segment)hctl, lpstr);
- GlobalUnlock(hg);
- GlobalFree (hg);
- }
- break;
-
- /* get the nth selected item */
- case IPROP_MULTI_SELLIST:
- {
- int count;
- int loop, i;
- int top, len;
- int hunt;
- HGLOBAL hg;
- LPDATASTRUCT lpDs = (LPDATASTRUCT)lp;
- i = (int)lpDs->index[0].data;
- count = LOWORD(SendMessage(hwnd,LB_GETSELCOUNT, 0, 0L));
- if (i > count || i < 1 )
- return(381);
- top = LOWORD(SendMessage(hwnd, LB_GETCOUNT, 0, 0L));
- hunt = 0;
- for ( loop = 0; loop < top; loop ++)
- {
- len = LOWORD(SendMessage(hwnd, LB_GETSEL, (WPARAM)loop, 0L));
- if ( len )
- {
- hunt ++;
- if (hunt == i )
- break;
- }
- }
- if (loop == top)
- return(381);
- len = LOWORD(SendMessage(hwnd, LB_GETTEXTLEN, (WPARAM)loop, 0L));
- hg = GlobalAlloc(GMEM_FIXED, (DWORD)(count + 1));
- if (!hg)
- return(380);
- lpstr = (LPSTR) GlobalLock(hg);
- SendMessage(hwnd, LB_GETTEXT, (WPARAM)loop, (LONG) lpstr);
- lpDs->data = (LONG)VBCreateHsz((_segment)hctl, lpstr);
- GlobalUnlock(hg);
- GlobalFree (hg);
- }
- break;
-
-
- /* get the long int at the nth position */
- case IPROP_MULTI_LISTDATA:
- {
- int count;
- int i;
- LPDATASTRUCT lpDs = (LPDATASTRUCT)lp;
- i = (int)lpDs->index[0].data;
- count = LOWORD(SendMessage(hwnd,LB_GETCOUNT, 0, 0L));
- if (i > count || i < 0 )
- return(381);
- lpDs->data = SendMessage(hwnd, LB_GETITEMDATA, (WPARAM)i, 0L);
- }
- break;
-
- }
- break;
-
- /* code to handle the optional methods for controls... */
- case VBM_METHOD:
- switch ( wp )
- {
- /* AddItem method: add item to list */
- case METH_ADDITEM:
- longer = (LPLONG )lp;
- args = longer[0];
- item = (HSZ)longer[1];
- if (args > 2 )
- index = longer[2];
- else
- index = -1;
- string = VBDerefHsz(item);
-
- /* later on, if sorted property is set, use */
- /* the addstring method - honors sorted property */
- SendMessage ( hwnd, LB_INSERTSTRING, (WPARAM)index, (LPARAM)(LPCSTR)string);
- return (0);
-
- /* Method to remove specific item from the list */
- case METH_REMOVEITEM:
- longer = (LPLONG )lp;
- args = longer[0];
- index = longer[1];
- ret = SendMessage ( hwnd, LB_DELETESTRING, (WPARAM)index, (LPARAM)0);
- return (0);
-
- default:
- break;
- }
- break;
-
- case VBN_DRAWITEM:
- break;
-
- }
-
- /* Default processing: Pass thru to let VB handle it - talks to control */
- lResult = VBDefControlProc(hctl, hwnd, msg, wp, lp);
-
-
- return lResult;
- }