This is a first attempt to make a properties list control self-contained so that it can be incorporated in an application with the miniumum of fuss. Having said, it is a single source file with a bit of a test harness included so that it can be run, marvelled at, and torn to shreds... It is not much tested and I have no doubt that versions will arise. If reposting the whole or major part, post here, else post comments in the Windows forum here
Code:
'-------------------------------------------------------------------------------------------- ' Module: PROPCON.Bas ' Desc: Properties List control ' Interface: ' The control is driven by WM_USER messages, WM_USER is offset as follows: ' %our_add_property = 100 ' %our_get_property = 102 ' %our_get_property_count = 103 ' This being a properties list, the content is unlikely to be changed dynamically ' so I have saved 30 minutes by not coding a "del_property" or "set_property" ' method. ' Loading the property sheet with properties and their default values is ' shown in InitializePropertyItems. ' Extracting the properties and their values is done in the DEBUG button handler. ' The developer also has to handle the WM_MEASUREITEM message as shown. ' Issues: ' 1. lack of thorough testing ' 2. WM_MEASUREITEM ' Purpose: a standalone control for managing a properties list ' History: Listbox handling code borrowed from Jules Marchildon's post of 2001 , not changed much ' Date: 7-FEB-2008 ' Name: Chris Holbrook ' '-------------------------------------------------------------------------------------------- #COMPILE EXE #INCLUDE "WIN32API.INC" #INCLUDE "COMDLG32.INC" '--- TYPE PROPERTYITEM propName AS ASCIIZ*225 curValue AS ASCIIZ*255 nItemType AS INTEGER cmbItems AS ASCIIZ*255 fptr AS propertyitem PTR END TYPE ' message types - WM_USER offsets %our_add_property = 100 %our_get_property = 101 %our_get_property_count = 102 %our_MEASUREITEM = 103 %our_drawitem = 104 %our_lb_selchange = 105 %our_msg_max = 106 ' must be highest WM_USER offset + 1 '--- %IDC_LISTBOX = 1000 %IDC_COMBO = 1001 %IDC_BUTTON = 1002 %IDC_EDIT = 1003 %IDC_UCC = 1004 %IDC_DEBUG_BN = 1005 'PIT = property item type, Button is default %PIT_COMBO = 0 %PIT_EDIT = 1 %PIT_COLOR = 2 %PIT_FONT = 3 %PIT_FILE = 4 '--- DECLARE SUB InitializePropertyItems( hOwner AS DWORD, lCtlId AS LONG) DECLARE SUB InitializeCustomColors( pColors AS DWORD) DECLARE SUB OnSelChange(hlist AS DWORD, hcombo AS DWORD, hbutton AS DWORD, hedit AS DWORD, _ nDivider AS INTEGER) DECLARE SUB OnButtonClicked(hlist AS DWORD, pColors AS DWORD) DECLARE SUB InvertLine( BYVAL hList AS DWORD, BYVAL xleft1 AS INTEGER ,BYVAL yFrom AS INTEGER, _ BYVAL xleft2 AS INTEGER ,BYVAL yTo AS INTEGER ) DECLARE FUNCTION MakeFont(BYVAL sFont AS STRING, BYVAL PointSize AS LONG) AS LONG '------------------------------------------------------------- ' common functions '------------------------------------------------------------------------------ ' borrowed from Semen Matusovski FUNCTION CreateSuperClass(OldClassName AS STRING, NewClassName AS STRING, lpfnNewWndProc AS LONG, cbWndExtra AS LONG) AS LONG LOCAL wc AS WNDCLASSEX LOCAL result AS LONG wc.cbSize = SIZEOF(wc) IF GetClassInfoEx(BYVAL 0&, BYVAL STRPTR(OldClassName), wc) THEN CallWindowProc lpfnNewWndProc, 0, 0, wc.lpfnWndProc, wc.cbWndExtra wc.hInstance = GetModuleHandle(BYVAL 0&) wc.lpszClassName = STRPTR(NewClassName) wc.lpfnWndProc = lpfnNewWndProc wc.cbWndExtra = wc.cbWndExtra + cbWndExtra result = RegisterClassEx(wc) FUNCTION = result END IF END FUNCTION '------------------------------------------------------------- ' WinMain: ' ' '------------------------------------------------------------- FUNCTION WINMAIN (BYVAL hInstance AS LONG, _ BYVAL hPrevInstance AS LONG, _ BYVAL lpCmdLine AS ASCIIZ PTR, _ BYVAL iCmdShow AS LONG) AS LONG LOCAL Msg AS tagMsg LOCAL wndclss AS WndClassEx LOCAL hWnd, result AS LONG LOCAL rct AS RECT LOCAL szpgmname AS ASCIIZ * 20 szpgmname = "PROPLIST" wndclss.cbSize = SIZEOF(WndClss) wndclss.style = %CS_HREDRAW OR %CS_VREDRAW OR %CS_DBLCLKS wndclss.lpfnWndProc = CODEPTR( LBWndProc ) wndclss.cbClsExtra = 0 wndclss.cbWndExtra = 0 wndclss.hInstance = hInstance wndclss.hIcon = LoadIcon( hInstance, "MAINICON" ) wndclss.hCursor = LoadCursor( %NULL, BYVAL %IDC_ARROW ) wndclss.hbrBackground = GetStockObject( %LTGRAY_BRUSH ) wndclss.lpszMenuName = %NULL wndclss.lpszClassName = VARPTR(szpgmname) wndclss.hIconSm = LoadIcon(hInstance, BYVAL %IDI_APPLICATION) result = RegisterClassEx (wndclss) IF result = 0 THEN result = getlasterror ERR = result ?ERROR$ END IF IF ISFALSE(CreateSuperClass("LISTBOX","OurLB", CODEPTR(ListBoxProc), 4)) THEN EXIT FUNCTION style& = %WS_OVERLAPPED OR _ %WS_CAPTION OR _ %WS_SYSMENU OR _ '%WS_SIZEBOX OR _ %WS_visible 'MINIMIZEBOX 'create the main window (my screen LCD,1024x768) hWnd = CreateWindowEx(0, _ szpgmname, _ "Control Properties:", _ style&, _ 210, 110, 650, 350, _ %HWND_DESKTOP, _ %NULL, _ hInstance, _ BYVAL %NULL) ' fail if window is not created IF ISFALSE hWnd THEN FUNCTION = %FALSE EXIT FUNCTION END IF ' Activate window ShowWindow hWnd, %SW_SHOW ' Paint client area UpdateWindow hWnd WHILE ISTRUE GetMessage(msg, BYVAL %NULL, 0, 0) IF ISFALSE IsDialogMessage(hWnd, msg) THEN TranslateMessage msg DispatchMessage msg END IF WEND FUNCTION = msg.wParam END FUNCTION '------------------------------------------------------------------------ ' ' MainWndProc() ' '------------------------------------------------------------------------ FUNCTION LBWndProc (BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, _ BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG STATIC hlist, hfont, hinstance, hwndBn AS DWORD LOCAL lpMs AS MEASUREITEMSTRUCT PTR LOCAL psz AS ASCIZ PTR LOCAL result, nrows, i AS LONG LOCAL sz AS ASCIZ * 255 LOCAL pitem AS PROPERTYITEM PTR LOCAL s AS STRING '--- SELECT CASE wMsg CASE %WM_CREATE hinstance = getmodulehandle(BYVAL 0) ' Create the OwnerDraw List box for our control properties... hList = CreateWindowEx(%WS_EX_CLIENTEDGE, "OurLB", "", _ %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %WS_BORDER OR _ %LBS_NOINTEGRALHEIGHT OR %LBS_HASSTRINGS OR _ %LBS_OWNERDRAWFIXED OR %LBS_NOTIFY OR %WS_VSCROLL, _ 10,10,300,306, _ hWnd, %IDC_LISTBOX, hInstance, BYVAL %NULL) hFont = MakeFont("Courier New", 9) CALL SendMessage( hList, %WM_SETFONT, hFont, 0 ) CALL InitializePropertyItems(hWnd, %IDC_LISTBOX) ' Create a label control hWndBN = CreateWindowEx(%NULL, "Static","DEBUG", _ %SS_LEFT OR %WS_VISIBLE OR %WS_CHILD OR %SS_NOTIFY, _ 335, 280, 50, 15, _ hWnd, %IDC_DEBUG_BN, hInstance, BYVAL %NULL) FUNCTION = %TRUE EXIT FUNCTION CASE %WM_MEASUREITEM lpMs = LPARAM 'arbitrary number for height @lpMs.itemHeight = 20 FUNCTION = %TRUE EXIT FUNCTION CASE %WM_DRAWITEM sendmessage (hlist, %WM_USER + %our_DRAWITEM, wparam, lparam) ' pass the message on to the superclassed LB control CASE %WM_COMMAND 'ListBox control notifications... SELECT CASE LOWRD(wParam) CASE %IDC_LISTBOX SELECT CASE HIWRD(wParam) CASE %LBN_SELCHANGE ' nudge the listbox control sendmessage(hlist, %WM_USER + %our_lb_selchange, 0, 0) END SELECT CASE %IDC_DEBUG_BN result = sendmessage(hlist, %WM_USER + %our_get_property_count,0,0) ? "there are" + STR$(result) + " properties in the list" ' work through listbox getting each property in turn, ' requesting its values & formatting them for display nrows = sendmessage(hlist, %LB_GETCOUNT,0,0) FOR i = 1 TO nrows CALL sendmessage(hlist, %LB_GETTEXT, i, VARPTR(sz)) pitem = sendmessage ( hlist, %WM_USER + %our_get_property, VARPTR(sz), 0) s = s + "name = " + TRIM$(@pitem.Propname) + "," + _ "CurValue = " + TRIM$(@pitem.CurValue) + "," SELECT CASE @pitem.nitemtype CASE %PIT_COMBO s = s + "nItemType = COMBO" CASE %PIT_EDIT s = s + "nItemType = EDIT" CASE %PIT_COLOR s = s + "nItemType = COLOR" CASE %PIT_FONT s = s + "nItemType = FONT" CASE %PIT_FILE s = s + "nItemType = FILE" END SELECT s = s + ",cmbitems = " + TRIM$(@pitem.CmbItems) + "," + _ "fptr = " + STR$( @pitem.fptr) + $CRLF NEXT ? s END SELECT CASE %WM_DESTROY deleteObject hfont END SELECT FUNCTION = DefWindowProc(hWnd, wMsg, wParam, lParam) END FUNCTION ' LBN_SELCHANGE: '---------------------------------------------------------------------------- SUB OnSelChange(hlist AS DWORD, hcombo AS DWORD, hbutton AS DWORD, hedit AS DWORD, _ nDivider AS INTEGER) LOCAL lBoxSelText AS STRING LOCAL rc AS RECT LOCAL cursel AS LONG LOCAL nlastbox AS LONG LOCAL pitem AS PROPERTYITEM PTR LOCAL sz AS ASCIZ * 255 curSel = SendMessage( hList, %LB_GETCURSEL, 0, 0 ) sendmessage ( hlist, %LB_GETTEXT, cursel, VARPTR(sz)) ' get selected text = propertyname lBoxSelText = sz pitem = sendmessage ( hList, %WM_USER + %our_get_property, VARPTR(sz), 0 ) ' get pointer to item structure CALL SendMessage( hList, %LB_GETITEMRECT, curSel, VARPTR(rc) ) rc.nleft = nDivider IF hCombo <> 0 THEN CALL ShowWindow( hCombo, %SW_HIDE ) IF hButton <> 0 THEN CALL ShowWindow( hButton, %SW_HIDE ) IF hEdit <> 0 THEN CALL ShowWindow( hEdit, %SW_HIDE ) IF @pItem.nItemType = %PIT_COMBO THEN 'display the combo box and move it to the new location nLastBox = 0 IF hCombo <> 0 THEN CALL MoveWindow( hCombo,rc.nleft-1,rc.ntop-2,rc.nright+2-rc.nleft,rc.nbottom+100,1 ) END IF 'add the choices for this particular property cmbItems$ = @pItem.cmbItems lBoxSelText = @pItem.curValue CALL SendMessage( hCombo,%CB_RESETCONTENT, 0,0 ) delimiter$ = "|" nCount& = TALLY(cmbItems$,delimiter$) FOR i& = 1 TO nCount& dataItem$ = PARSE$(cmbItems$,delimiter$,i&) 'add each string to the ComboBox CALL SendMessage(hCombo, %CB_ADDSTRING, i&-1, STRPTR(dataItem$) ) NEXT CALL ShowWindow( hCombo,%SW_SHOW ) CALL SetFocus( hCombo ) 'Call SetFocus( ghList ) 'jump to the property's current value in the combo box j& = SendMessage(hCombo,%CB_FINDSTRINGEXACT,0,STRPTR(lBoxSelText) ) IF j& <> %CB_ERR THEN CALL SendMessage( hCombo,%CB_SETCURSEL,j&,0 ) ELSE 'there is no current value, so default to first in list CALL SendMessage( hCombo,%CB_SETCURSEL,0,0 ) END IF ELSEIF @pItem.nItemType = %PIT_EDIT THEN 'display edit box nLastBox = 1 rc.nbottom = rc.nbottom - 3 IF hEdit <> 0 THEN CALL MoveWindow( hEdit,rc.nleft+1,rc.ntop+3,rc.nright-rc.nleft,rc.nbottom-rc.ntop,1 ) END IF lBoxSelText = @pItem.curValue CALL ShowWindow( hEdit, %SW_SHOW ) CALL SetFocus( hEdit ) 'Call SetFocus( ghList ) 'set the text in the edit box to the property's current value CALL SendMessage(hEdit, %WM_SETTEXT, 0, STRPTR(lBoxSelText) ) ELSE 'displays a button if the property is a Color/File/Font chooser nLastBox = 2 nWidth& = rc.nright - rc.nleft IF nWidth& > 25 THEN rc.nleft = rc.nright - 25 END IF rc.nbottom = rc.nbottom - 3 IF hButton <> 0 THEN CALL MoveWindow( hButton,rc.nleft,rc.ntop,rc.nRight -rc.nleft, rc.nBottom -rc.ntop, 1 ) END IF CALL ShowWindow( hButton, %SW_SHOW ) CALL SetFocus( hButton ) 'Call SetFocus( ghList ) '--- END IF END SUB '---------------------------------------------------------------------------- ' BN_CLICKED: '---------------------------------------------------------------------------- SUB OnButtonClicked(hlist AS DWORD, pColors AS DWORD) LOCAL lBoxSelText AS STRING LOCAL initClr AS DWORD LOCAL currClr AS ASCIIZ*255 LOCAL clrStr AS ASCIIZ*255 LOCAL ColorSpec AS CHOOSECOLORAPI LOCAL lResult, cursel AS LONG LOCAL lCounter AS LONG LOCAL lCustomColor() AS LONG LOCAL pitem AS PROPERTYITEM PTR LOCAL sz AS ASCIZ * 255 'display the appropriate common dialog depending on what type 'of chooser is associated with the property ' get current listbox selection cursel = sendmessage ( hlist, %LB_GETCURSEL,0,0) sendmessage ( hlist, %LB_GETTEXT, cursel, VARPTR(sz)) ' get selected text = propertyname lBoxSelText = TRIM$(sz) pitem = sendmessage ( hList, %WM_USER + %our_get_property, VARPTR(sz), 0 ) ' get pointer to item structure 'First check for the Choose Color dialog... SELECT CASE @pItem.nItemType CASE %PIT_COLOR currClr = @pItem.curValue IF currClr > "" THEN 'parse the property's current color value currClr = LTRIM$(currClr,"RGB(") currClr = RTRIM$(currClr,")") initClr = RGB(VAL(PARSE$(currClr,d$,1)), _ VAL(PARSE$(currClr,d$,2)), _ VAL(PARSE$(currClr,d$,3))) ELSE 'use a default instead initClr = RGB( 255,128,128 ) END IF ColorSpec.lStructSize = LEN(ColorSpec) ColorSpec.hwndOwner = hList ColorSpec.lpCustColors = pColors ColorSpec.rgbResult = initClr ColorSpec.Flags = ColorSpec.Flags OR %CC_RGBINIT lResult = ChooseColor(ColorSpec) IF lResult = 0 THEN 'check if user cancelled dialog ? EXIT SUB ELSE 'selClr = ColorSpec.rgbResult clrStr = HEX$(ColorSpec.rgbResult,6) 'Note order of RGB, COLORREF 0x00bbggrr clrStr = "RGB(" + TRIM$(STR$(VAL("&H" + RIGHT$(ClrStr,2)))) + _ "," + TRIM$(STR$(VAL("&H" + MID$ (ClrStr,3,2)))) + _ "," + TRIM$(STR$(VAL("&H" + LEFT$ (ClrStr,2)))) + ")" @pItem.curValue = clrStr 'Call ShowWindow( ghButton,%SW_HIDE ) CALL InvalidateRect( hList,BYVAL %NULL,1) CALL UpdateWindow ( hList ) END IF CASE %PIT_FILE 'Next check for the Open File Dialog... LOCAL SelectedFile AS ASCIIZ*255 LOCAL zTxt AS ASCIIZ * 255 zTxt = "All pictures (*.bmp,*.ico)|*.BMP;*.ICO|" zTxt = zTxt & "Bitmap (*.bmp)|*.BMP|" ZTxt = zTxt & "Icon (*.ico)|*.ICO|" currPath$ = @pItem.curValue IF currPath$ = "none" THEN fName$ = "" ELSE fName$ = currPath$ END IF 'use simple Open dialog for demo... tmp& = OpenFileDialog(getparent(hlist),"Select File:",fName$,CURDIR$,zTxt,"BMP", _ %OFN_FILEMUSTEXIST OR %OFN_HIDEREADONLY OR %OFN_LONGNAMES) IF tmp& THEN @pItem.curValue = fName$ CALL InvalidateRect(hList,BYVAL %NULL,1) CALL UpdateWindow ( hList ) END IF ' END IF '<remove later> 'Last, check for the ChooseFont() dialog... CASE %PIT_FONT DIM cf AS CHOOSEFONTAPI DIM lfFont AS LOGFONT cf.lStructSize = SIZEOF(cf) cf.hWndOwner = getparent(hlist) cf.lpLogFont = VARPTR(lfFont) cf.Flags = %CF_EFFECTS OR %CF_SCREENFONTS lResult = ChooseFont(cf) IF lResult = 0 THEN 'check if user cancelled dialog ? EXIT SUB ELSE faceName$ = lfFont.lfFaceName 'ToDo: Not included in orgional C++, get the Font height... @pItem.curValue = faceName$ CALL InvalidateRect( hList,BYVAL %NULL,1) CALL UpdateWindow ( hList ) END IF END SELECT END SUB '------------------------------------------------------------------------- ' Create a Font: '------------------------------------------------------------------------- FUNCTION MakeFont(BYVAL sFont AS STRING, BYVAL PointSize AS LONG) AS LONG LOCAL hDC AS LONG, CyPixels AS LONG hDC = GetDC(%HWND_DESKTOP) CyPixels = GetDeviceCaps(hDC, %LOGPIXELSY) ReleaseDC %HWND_DESKTOP, hDC PointSize = (PointSize * CyPixels) \ 72 FUNCTION = CreateFont(0 - PointSize, 0, 0, 0, %FW_NORMAL, 0, 0, 0, _ %ANSI_CHARSET, %OUT_TT_PRECIS, %CLIP_DEFAULT_PRECIS, _ %DEFAULT_QUALITY, %FF_DONTCARE, BYCOPY sFont) END FUNCTION '------------------------------------------------------------------------- '' SuperClassed ListBox Control Procedure: '------------------------------------------------------------------------- CALLBACK FUNCTION ListBoxProc LOCAL lBoxSelText AS STRING STATIC OldProc AS LONG, OffsetWndExtra AS LONG LOCAL sz AS ASCIZ * 64 LOCAL psz AS ASCIZ PTR LOCAL cursel, hDC, nindex AS LONG LOCAL r, rc, rc2, rectfull AS rect LOCAL pt AS POINTAPI STATIC hbrush, hCursorSize, hcursorarrow, hcombo, hedit, hbutton, hfont, hinst AS DWORD LOCAL p_item, q_item, r_item AS PROPERTYITEM PTR LOCAL h, i AS LONG STATIC pitem, ppty AS propertyitem PTR STATIC nDivider AS INTEGER STATIC nDivTop AS INTEGER STATIC nDivBtm AS INTEGER STATIC nOldDivX AS INTEGER STATIC nLastBox AS INTEGER STATIC bTracking AS LONG STATIC bDivIsSet AS INTEGER DIM Ccolor(0 TO 15) AS STATIC LONG LOCAL lpDs AS DRAWITEMSTRUCT PTR LOCAL pdw AS DWORD PTR LOCAL lcreating AS LONG ' ' message from CreateSuperClass to give us the WNDPROC for the base class IF CBHNDL = 0 THEN OldProc = CBWPARAM OffsetWndExtra = CBLPARAM EXIT FUNCTION END IF ' SELECT CASE CBMSG CASE %WM_CREATE lcreating = %true hCursorArrow = LoadCursor( %NULL, BYVAL %IDC_ARROW ) hinst = getmodulehandle(BYVAL 0) hCursorSize = LoadCursor( %NULL, BYVAL %IDC_SIZEWE ) bTracking = %FALSE nDivider = 0 bDivIsSet = %FALSE ' Create the controls that will need to edit the Properties with. ' They are all hidden for now and resized later. hButton = CreateWindowEx(0,"BUTTON","...", _ %WS_CHILD OR %WS_CLIPSIBLINGS OR %BS_PUSHBUTTON, _ 0,0,8,12, _ CBHNDL, %IDC_BUTTON, hInst, BYVAL %NULL) hCombo = CreateWindowEx(0,"COMBOBOX", "", _ %WS_CHILD OR %CBS_DROPDOWNLIST OR %CBS_NOINTEGRALHEIGHT, _ 0,0,10,100, _ CBHNDL, %IDC_COMBO, hInst, BYVAL %NULL) hEdit = CreateWindowEx(0,"EDIT", "True", _ %WS_CHILD OR %WS_CLIPSIBLINGS OR %ES_LEFT OR %ES_AUTOHSCROLL OR %WS_BORDER, _ 0,0,10,20, _ CBHNDL, %IDC_EDIT, hInst, BYVAL %NULL) CALL InitializeCustomColors( VARPTR(Ccolor(0))) lcreating = %false CASE %WM_SETFONT hfont = CBWPARAM CALL SendMessage( hEdit, %WM_SETFONT, hFont, 0 ) CALL SendMessage( hCombo, %WM_SETFONT, hFont, 0 ) ' NB SetFont does not return a value 'Catch the Combo,Edit,Button child control notifications here... CASE %WM_COMMAND cursel = sendmessage ( CBHNDL, %LB_GETCURSEL,0,0) sendmessage ( CBHNDL, %LB_GETTEXT, cursel, VARPTR(sz)) ' get selected text = propertyname lBoxSelText = TRIM$(sz) pitem = sendmessage ( CBHNDL, %WM_USER + %our_get_property, VARPTR(sz), 0 ) ' get pointer to item structure SELECT CASE CBCTL CASE %IDC_COMBO IF (CBCTLMSG = %CBN_SELCHANGE) THEN LOCAL selStr AS ASCIIZ*255 IF hCombo <> 0 THEN idx& = SendMessage( hCombo, %CB_GETCURSEL ,0 ,0 ) CALL SendMessage( hCombo, %CB_GETLBTEXT, idx& , VARPTR(selStr) ) @pItem.curValue = selStr END IF END IF CASE %IDC_EDIT IF (CBCTLMSG = %EN_CHANGE) THEN LOCAL newStr AS ASCIIZ*255 tLen& = SendMessage(hEdit, %WM_GETTEXTLENGTH, 0, 0) + 1 newStr = SPACE$(tLen&) CALL SendMessage(hEdit, %WM_GETTEXT, tLen&, VARPTR(newStr)) IF pitem <> 0 THEN ' no items exist during initialisation! @pItem.curValue = newStr END IF END IF CASE %IDC_BUTTON IF (CBCTLMSG = %BN_CLICKED) THEN cursel = sendmessage ( CBHNDL, %LB_GETCURSEL,0,0) CALL OnButtonClicked( CBHNDL, VARPTR(Ccolor(0))) END IF END SELECT CASE %WM_LBUTTONDOWN IF hCombo <> 0 THEN CALL ShowWindow( hCombo, %SW_HIDE ) IF hEdit <> 0 THEN CALL ShowWindow( hEdit, %SW_HIDE ) IF hButton <> 0 THEN CALL ShowWindow( hButton, %SW_HIDE ) '-----------------------[ Splitter ]----------------------- pt.x = LOWRD(CBLPARAM) pt.y = HIWRD(CBLPARAM) IF ((pt.x >= nDivider-5) AND (pt.x <= nDivider+5)) THEN 'if mouse clicked on divider line, then start resizing CALL SetCursor(hCursorSize) CALL GetWindowRect( CBHNDL ,rc ) rc.nleft = rc.nleft +10 rc.nright = rc.nright -20 'do not let mouse leave the list box boundary CALL ClipCursor( rc ) CALL GetClientRect( CBHNDL ,rc ) bTracking = %TRUE nDivTop = rc.ntop nDivBtm = rc.nbottom nOldDivX = pt.x CALL InvertLine( CBHNDL, nOldDivX ,nDivTop ,nOldDivX ,nDivBtm ) 'capture the mouse CALL SetCapture(CBHNDL) END IF CASE %WM_MOUSEMOVE pt.x = LOWRD(CBLPARAM) pt.y = HIWRD(CBLPARAM) 'move divider line to the mouse position 'if columns are currently being resized IF bTracking = %TRUE THEN 'remove old divider line CALL InvertLine( CBHNDL, nOldDivX ,nDivTop ,nOldDivX ,nDivBtm ) 'draw new divider line CALL InvertLine( CBHNDL, pt.x ,nDivTop ,pt.x ,nDivBtm ) nOldDivX = pt.x ELSE 'set the cursor to a sizing cursor if 'the cursor is over the row divider IF (pt.x >= nDivider-5) AND (pt.x <= nDivider+5) THEN CALL SetCursor(hCursorSize) END IF END IF CASE %WM_LBUTTONUP pt.x = LOWRD(CBLPARAM) pt.y = HIWRD(CBLPARAM) 'if columns were being resized then this indicates 'that mouse is up so resizing is done. Need to redraw 'columns to reflect their new widths. IF bTracking = %TRUE THEN bTracking = %FALSE CALL ReleaseCapture() CALL ClipCursor( BYVAL %NULL ) CALL InvertLine( CBHNDL, pt.x ,nDivTop ,pt.x ,nDivBtm ) 'set the divider position to the new value nDivider = pt.x 'Self paint the ListBox control... CALL InvalidateRect(CBHNDL ,BYVAL %NULL, 1) CALL UpdateWindow(CBHNDL) END IF CASE %WM_CAPTURECHANGED IF CBLPARAM <> getparent(CBHNDL) THEN IF bTracking = %TRUE THEN bTracking = %FALSE CALL ReleaseCapture() CALL ClipCursor( BYVAL %NULL ) CALL InvalidateRect(CBHNDL ,BYVAL %NULL, 1) CALL UpdateWindow(CBHNDL) END IF END IF '---------------------[ end of splitter ]------------------ '--- ' user messages CASE %WM_USER + %our_lb_selchange ' sent when LB_SELCHANGE rec'd by parent curSel = SendMessage( CBHNDL, %LB_GETCURSEL, 0, 0 ) CALL OnSelChange(CBHNDL, hcombo, hbutton, hedit, ndivider) CASE %WM_USER + %our_DRAWITEM ' parent has rec'd a WM_DRAWITEM msg lpDs = CBLPARAM IF @lpDs.itemID < 0 THEN EXIT SELECT END IF IF bTracking = %TRUE THEN EXIT FUNCTION rectFull = @lpDs.rcItem rc = rectFull IF nDivider = 0 THEN nDivider = (rc.nRight - rc.nLeft) \ 2 rc.nleft = nDivider rc2 = rectFull rc2.nright = rc.nleft - 1 nIndex = @lpDs.itemID sendmessage ( CBHNDL, %LB_GETTEXT, nIndex, VARPTR(sz)) ' get selected text = propertyname lBoxSelText = TRIM$(sz) pitem = sendmessage ( CBHNDL, %WM_USER + %our_get_property, VARPTR(sz), 0 ) ' get pointer to item structure IF nIndex > -1 THEN 'draw two rectangles, one for each row column hBrush = CreateSolidBrush( RGB(192,192,192) ) CALL FillRect(@lpDs.hDC , rc2, hBrush ) CALL DeleteObject(hBrush) CALL DrawEdge(@lpDs.hDC , rc2, %EDGE_SUNKEN,%BF_BOTTOMRIGHT ) CALL DrawEdge(@lpDs.hDC , rc, %EDGE_SUNKEN,%BF_BOTTOM ) CALL SetBkMode( @lpDs.hDC , %TRANSPARENT ) rc2.nleft = rc2.nleft + 3 rc2.ntop = rc2.ntop + 3 rc2.nright = rc2.nright - 3 rc2.nbottom = rc2.nbottom + 3 'write the Property Name in the first rectangle, left side CALL DrawText( @lpDs.hDC , @pItem.propName, _ LEN(@pItem.propName), _ rc2, %DT_LEFT OR %DT_SINGLELINE ) rc.nleft = rc.nleft + 3 rc.ntop = rc.ntop + 3 rc.nright = rc.nright + 3 rc.nbottom = rc.nbottom + 3 'write the initial property value in the second rectangle, right side CALL DrawText(@lpDs.hDC , @pItem.curValue, _ LEN(@pItem.curValue), _ rc, %DT_LEFT OR %DT_SINGLELINE ) END IF '<insert here> Add some kind of item focus rect FUNCTION = %TRUE EXIT FUNCTION CASE %WM_USER + %our_add_property BEEP pdw = CBLPARAM p_item = CBLPARAM ' grab memory to hold new property and ititialise to zeros h = GlobalAlloc ( %GPTR, SIZEOF(PropertyItem)) ' move property details into memory movememory ( BYVAL h, BYVAL pdw, SIZEOF(PROPERTYITEM)) ' add it into list at end IF ppty = %NULL THEN ' empty list ppty = h ELSE p_item = ppty WHILE @p_item.fptr <> %NULL p_item = @p_item.fptr WEND @p_item.fptr = h END IF q_item = h sz = @q_item.propname pitem = q_item ' pitem is static! CALL SendMessage(CBHNDL,%LB_ADDSTRING,0,VARPTR(sz)) EXIT FUNCTION CASE %WM_USER + %our_get_property ' wparam has a ptr to propertyname ' find property by name p_item = ppty IF p_item = %NULL THEN EXIT FUNCTION ' empty list psz = CBWPARAM WHILE TRIM$(@p_item.propname) <> TRIM$(@psz) p_item = @p_item.fptr IF p_item = %NULL THEN EXIT FUNCTION ' return null pointer WEND ' return msg with pointer to it FUNCTION = p_item ' return pointer to property EXIT FUNCTION 'sendmessage ( hParent, %WM_USER + %our_ispty, 0, p_item) CASE %WM_USER + %our_get_property_count ' count properties in list p_item = ppty IF p_item = %NULL THEN EXIT FUNCTION ' return zero WHILE %TRUE INCR i p_item = @p_item.fptr IF p_item = %NULL THEN FUNCTION = i EXIT FUNCTION END IF WEND ' CASE %WM_DESTROY ' deallocate memory in property linked list p_item = ppty WHILE p_item <> %null q_item = p_item p_item = @p_item.fptr globalfree( BYVAL q_item ) WEND ppty = %NULL ' CALL PostQuitMessage( 0 ) END SELECT ' if the message was received as a user message, it is not processed further IF (CBMSG > %WM_USER) AND ( CBMSG < %WM_USER + %our_msg_max) THEN EXIT FUNCTION ' FUNCTION = CallWindowProc(OldProc, CBHNDL, CBMSG, CBWPARAM, CBLPARAM) END FUNCTION '-------------------------------------------------------------------------- ' InvertLine() '-------------------------------------------------------------------------- SUB InvertLine( BYVAL hList AS DWORD, BYVAL xleft1 AS INTEGER ,BYVAL yFrom AS INTEGER, _ BYVAL xleft2 AS INTEGER ,BYVAL yTo AS INTEGER ) LOCAL hDC AS LONG LOCAL nDrawMode AS LONG LOCAL oldpt AS POINTAPI '*Get DC of ListBox hDC = GetDC(hList) '*Use the default Pen color and style... '*Set the GDI foreground mix mode nDrawMode = SetROP2(hDC, %R2_NOT) '*Draw the Line CALL MoveToEx( hDC ,xLeft1 ,yFrom ,oldpt) CALL LineTo( hDC ,xLeft2 ,yTo) '*Restore DC CALL SetROP2(hDC, nDrawMode) CALL ReleaseDC(hList, hDC) END SUB '-------------------------------------------------------------------------- ' User Designed Custom Control Properties: ' '-------------------------------------------------------------------------- ' This sub is outside the custom control so it needs to know where the CC is ' for which purpose the parent hWnd and control id are passed in SUB InitializePropertyItems( hOwner AS DWORD, lCtlId AS LONG) LOCAL pi AS propertyitem pi.propName = "ToolTip Text" pi.curValue = "Litte Red Riding Hood..." pi.nItemType = %PIT_EDIT pi.cmbItems = "" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Enabled" pi.curValue = "true" pi.nItemType = %PIT_COMBO pi.cmbItems = "true|false|" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Visible" pi.curValue = "true" pi.nItemType = %PIT_COMBO pi.cmbItems = "true|false|" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Fore. Color" pi.curValue = "RGB(255,255,0)" pi.nItemType = %PIT_COLOR pi.cmbItems = "" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Back. Color" pi.curValue = "RGB(255,0,128)" pi.nItemType = %PIT_COLOR pi.cmbItems = "" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Opaque" pi.curValue = "false" pi.nItemType = %PIT_COMBO pi.cmbItems = "true|false|" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Auto. Scroll" pi.curValue = "true" pi.nItemType = %PIT_COMBO pi.cmbItems = "true|false|" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Double Buffered" pi.curValue = "true" pi.nItemType = %PIT_COMBO pi.cmbItems = "true|false|" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Font" pi.curValue = "MS San Sarif" pi.nItemType = %PIT_FONT pi.cmbItems = "" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Text" pi.curValue = "Big Bad Wolf!" pi.nItemType = %PIT_EDIT pi.cmbItems = "" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Horiz. Align" pi.curValue = "CENTER" pi.nItemType = %PIT_COMBO pi.cmbItems = "CENTER|LEFT|RIGHT|" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Vert. Align" pi.curValue = "CENTER" pi.nItemType = %PIT_COMBO pi.cmbItems = "CENTER|TOP|BOTTOM|" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Bmp ,Icon" pi.curValue = "none" pi.nItemType = %PIT_FILE pi.cmbItems = "" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Border Painted" pi.curValue = "false" pi.nItemType = %PIT_COMBO pi.cmbItems = "true|false|" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Fill Content Area" pi.curValue = "true" pi.nItemType = %PIT_COMBO pi.cmbItems = "true|false|" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Focus Painted" pi.curValue = "true" pi.nItemType = %PIT_COMBO pi.cmbItems = "true|false|" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) pi.propName = "Horiz. Text Pos." pi.curValue = "RIGHT" pi.nItemType = %PIT_COMBO pi.cmbItems = "RIGHT|LEFT|CENTER|LEADING|TRAILING|" CALL SendMessage(getdlgitem(howner, lCtlId),%WM_USER + %our_add_property, 0,VARPTR(pi)) END SUB '-------------------------------------------------------------------------- ' Load 16 Custom colors up for our ChooseColor() Dialog '-------------------------------------------------------------------------- SUB InitializeCustomColors( pColors AS DWORD) LOCAL lCounter AS LONG LOCAL pl AS LONG PTR 'load your custom colors... RANDOMIZE TIMER pl = pColors FOR lCounter = 0 TO 15 'example colors, @pl = RND(0,16777215) INCR pl NEXT END SUB
Comment