home *** CD-ROM | disk | FTP | other *** search
/ Programming Tool Box / SIMS_2.iso / code / design / lbtabs / dulist.bas next >
BASIC Source File  |  1995-01-06  |  14KB  |  468 lines

  1. Option Explicit
  2.  
  3. Const WM_USER = 1024
  4. Const LB_SETTABSTOPS = WM_USER + 19
  5. Const EM_SETTABSTOPS = WM_USER + 27
  6. Const CB_SELECTSTRING = WM_USER + 13
  7. Const LB_SELECTSTRING = WM_USER + 13
  8. Const LB_SETHORIZONTALEXTENT = WM_USER + 21
  9.  
  10. Const nSEARCH_FROM_TOP = -1
  11.  
  12. Declare Function dulist_nlSetTabstops Lib "User" Alias "SendMessage" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long
  13. Declare Function dulist_nlSelectString Lib "User" Alias "SendMessage" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As String) As Long
  14. Declare Function dulist_nlGetTextExtent Lib "GDI" Alias "GetTextExtent" (ByVal hDC As Integer, ByVal lpString As String, ByVal nCount As Integer) As Long
  15. Declare Function dulist_nlGetDialogBaseUnits Lib "User" Alias "GetDialogBaseUnits" () As Long
  16. Declare Function dulist_nlSetHorizScrollBar Lib "User" Alias "SendMessage" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long
  17.  
  18. Sub dulist_AddHorizScrollBar (ctlListControl As Control, fVirtualWidthRatio As Single)
  19.  
  20. Dim nlRC As Long
  21. Dim fMultiplier As Single
  22.  
  23.  
  24. If fVirtualWidthRatio <= 1 Then
  25.    fMultiplier = 2  'default 2x wider
  26. Else
  27.    fMultiplier = fVirtualWidthRatio
  28. End If
  29.  
  30. nlRC = dulist_nlSetHorizScrollBar(ctlListControl.hWnd, LB_SETHORIZONTALEXTENT, (ctlListControl.Width * fMultiplier) \ Screen.TwipsPerPixelX, 0)
  31.  
  32. End Sub
  33.  
  34. Function dulist_sGetColumn (sColData As String, nColID As Integer) As String
  35.  
  36. Dim sTAB As String
  37. Dim sColString As String
  38. Dim nNbrListboxCols As Integer
  39. Dim nInStart As Integer, nTabPos As Integer
  40.  
  41.  
  42. dulist_sGetColumn = ""
  43.  
  44. If Len(sColData) = 0 Or nColID <= 0 Then
  45.    Exit Function
  46. End If
  47.  
  48. sTAB = Chr$(9)
  49. nNbrListboxCols = 1
  50.  
  51. nInStart = 1
  52. Do
  53.    nTabPos = InStr(nInStart, sColData, sTAB)
  54.  
  55.    If nTabPos > 0 Then
  56.       sColString = Mid$(sColData, nInStart, nTabPos - nInStart)
  57.    Else
  58.       sColString = Mid$(sColData, nInStart, Len(sColData) - nInStart + 1)
  59.    End If
  60.  
  61.    If nNbrListboxCols = nColID Then
  62.       dulist_sGetColumn = RTrim$(sColString)
  63.       Exit Do
  64.    End If
  65.  
  66.    If nTabPos > 0 Then
  67.       nNbrListboxCols = nNbrListboxCols + 1
  68.  
  69.       If nTabPos < Len(sColData) Then
  70.          nInStart = nTabPos + 1
  71.       Else
  72.          Exit Do
  73.       End If
  74.    Else
  75.       Exit Do
  76.    End If
  77. Loop
  78.  
  79. End Function
  80.  
  81. Function dulist_tfSelectListItem (ctlListControl As Control, sSelectString As String) As Integer
  82.  
  83. Dim nMsgID As Integer
  84. Dim nlRC As Long
  85.  
  86.  
  87. '===================
  88. SelectListItem_Main:
  89. '===================
  90. dulist_tfSelectListItem = True
  91.  
  92. GoSub SelectListItem_VerifyControls
  93. GoSub SelectListItem_UpdateControls
  94.  
  95. Exit Function
  96.  
  97.  
  98. '=============================
  99. SelectListItem_VerifyControls:
  100. '=============================
  101. If TypeOf ctlListControl Is ListBox Then
  102.    nMsgID = LB_SELECTSTRING
  103. Else
  104.    If TypeOf ctlListControl Is ComboBox Then
  105.       nMsgID = CB_SELECTSTRING
  106.    Else
  107.       dulist_tfSelectListItem = False
  108.       Exit Function
  109.    End If
  110. End If
  111.  
  112. If Len(sSelectString) = 0 Then
  113.    dulist_tfSelectListItem = False
  114.    Exit Function
  115. End If
  116.  
  117. Return
  118.  
  119. '=============================
  120. SelectListItem_UpdateControls:
  121. '=============================
  122. nlRC = dulist_nlSelectString(ctlListControl.hWnd, nMsgID, nSEARCH_FROM_TOP, sSelectString)
  123.  
  124. Return
  125.  
  126. End Function
  127.  
  128. Function dulist_tfSetListCols (ctlListControl As Control, ctlTextControl As Control, tfUseHeadingWidthsOnly As Integer, tfSetDefaultTabstops As Integer) As Integer
  129.  
  130. 'This function automatically calculates and sets appropriate
  131. 'tabstops for a multi-column listbox, based on the actual data
  132. 'in the listbox.  You do not have to tell the function how many
  133. 'columns you want, nor figure out how wide each column should be;
  134. 'the actual data placed into the listbox determines that.
  135.  
  136. 'In addition to the listbox, the function also sets identical
  137. 'tabstops in an accompanying, multi-line textbox.  This textbox
  138. 'provides the data for the column headings.
  139.  
  140. 'tfUseHeadingWidthsOnly:
  141. '  True -  Tabstops are calculated based only on the
  142. '          widths of the column headings. This option
  143. '          is must faster, but you're gambling that the
  144. '          actual data will always be narrower than the
  145. '          headings.
  146. '
  147. '  False - Tabstops are calculated based on the widest
  148. '          entry in each column; both the headings and
  149. '          the data are examined.  This option is slower
  150. '          because each entry in the listbox must be
  151. '          parsed, but it eliminates the guesswork.
  152.  
  153. 'tfSetDefaultTabstops:
  154. '  True -  Tabstops are reset to Windows' default intervals
  155. '          of 8 dialog units.
  156. '
  157. '  False - Tabstops are calculated based on the actual
  158. '          data in the listbox/textbox.
  159. '
  160. '
  161. 'The function itself returns FALSE if any of the control
  162. 'verification tests fail; otherwise it returns TRUE.
  163.  
  164.  
  165. Dim sTAB As String
  166. Dim sColHeadings As String, sColData As String, sColString As String
  167. Dim sParentFontName As String, fParentFontSize As Single
  168. Dim tfParentFontBold As Integer, tfParentFontItalic As Integer
  169. Dim nColCount As Integer, nDataWidth As Integer, nSpaceBetweenCols As Integer
  170. Dim nMaxListboxCols As Integer, nNbrListboxCols As Integer, nNbrTabstops As Integer
  171. Dim nInStart As Integer, nTabPos As Integer
  172. Dim nListSub As Integer, nTabSub As Integer
  173. Dim nlRC As Long
  174. Dim nListFontAvgWidth As Integer, nSystemFontAvgWidth As Integer
  175. Dim fListFontPixelsPerDlgUnit As Single, fFontRatio As Single
  176.  
  177. Dim nColWidth() As Integer  'measured column widths
  178. Dim nTabstop() As Integer   'calculated WinAPI tabstops
  179.  
  180. '================
  181. SetListCols_Main:
  182. '================
  183. dulist_tfSetListCols = True
  184.  
  185. GoSub SetListCols_VerifyControls
  186. GoSub SetListCols_Initialize
  187.  
  188. If tfSetDefaultTabstops Then
  189.    nNbrTabstops = 0
  190.    GoSub SetListCols_UpdateControls
  191. Else
  192.    'Since VB provides an hDC property for forms, but
  193.    'not for controls, we must temporarily set the parent
  194.    'form's font characteristics equal to the listbox's
  195.    'font characteristics.  Doing this ensures that all
  196.    'text measurements made using the form's DC will be
  197.    'accurate for the listbox.
  198.  
  199.    sParentFontName = ctlListControl.Parent.FontName
  200.    fParentFontSize = ctlListControl.Parent.FontSize
  201.    tfParentFontBold = ctlListControl.Parent.FontBold
  202.    tfParentFontItalic = ctlListControl.Parent.FontItalic
  203.    ctlListControl.Parent.FontName = ctlListControl.FontName
  204.    ctlListControl.Parent.FontSize = ctlListControl.FontSize
  205.    ctlListControl.Parent.FontBold = ctlListControl.FontBold
  206.    ctlListControl.Parent.FontItalic = ctlListControl.FontItalic
  207.  
  208.    'Identify and measure the width of the column headings
  209.    'present in the textbox.
  210.  
  211.    GoSub SetListCols_MeasureColHeadingWidths
  212.  
  213.    'Measure the width of the column data values present
  214.    'in the listbox.
  215.  
  216.    If Not tfUseHeadingWidthsOnly Then
  217.       GoSub SetListCols_MeasureColDataWidths
  218.    End If
  219.  
  220.    'Calculate and set the necessary tabstop values, based
  221.    'on the maximum width of each column.
  222.  
  223.    GoSub SetListCols_UpdateControls
  224.  
  225.    'Reset the parent form's font characteristics to their
  226.    'original values.
  227.  
  228.    ctlListControl.Parent.FontName = sParentFontName
  229.    ctlListControl.Parent.FontSize = fParentFontSize
  230.    ctlListControl.Parent.FontBold = tfParentFontBold
  231.    ctlListControl.Parent.FontItalic = tfParentFontItalic
  232. End If
  233.  
  234. Exit Function
  235.  
  236.  
  237. '==========================
  238. SetListCols_VerifyControls:
  239. '==========================
  240. 'Make sure both controls are of the proper type,
  241. 'and that the necessary property values are set.
  242.  
  243. If TypeOf ctlListControl Is ListBox Then
  244. Else
  245.    dulist_tfSetListCols = False
  246.    Exit Function
  247. End If
  248.  
  249. If TypeOf ctlTextControl Is TextBox Then
  250. Else
  251.    dulist_tfSetListCols = False
  252.    Exit Function
  253. End If
  254.  
  255. If ctlListControl.Columns <> 0 Then
  256.    dulist_tfSetListCols = False
  257.    Exit Function
  258. End If
  259.  
  260. If ctlTextControl.MultiLine = False Then
  261.    dulist_tfSetListCols = False
  262.    Exit Function
  263. End If
  264.  
  265. If ctlTextControl.BorderStyle <> 0 Then
  266.    dulist_tfSetListCols = False
  267.    Exit Function
  268. End If
  269.  
  270. If Len(ctlTextControl.Text) = 0 Then
  271.    dulist_tfSetListCols = False
  272.    Exit Function
  273. End If
  274.  
  275. Return
  276.            
  277. '======================
  278. SetListCols_Initialize:
  279. '======================
  280. 'A little extra space between columns helps
  281. 'to mitigate the inevitable rounding errors
  282. 'that will occur in the tabstop calculations.
  283.  
  284. nSpaceBetweenCols = 2
  285.  
  286. nMaxListboxCols = 10
  287. ReDim nColWidth(nMaxListboxCols)
  288.  
  289. sTAB = Chr$(9)
  290.  
  291. Return
  292.  
  293. '===================================
  294. SetListCols_MeasureColHeadingWidths:
  295. '===================================
  296. 'Search for TAB characters in the column heading
  297. 'text.  For each column found, measure the width
  298. 'of the heading text.
  299.  
  300. sColHeadings = ctlTextControl.Text
  301. nNbrListboxCols = 1
  302.  
  303. nInStart = 1
  304. Do
  305.    nTabPos = InStr(nInStart, sColHeadings, sTAB)
  306.  
  307.    If nTabPos > 0 Then
  308.       sColString = Mid$(sColHeadings, nInStart, nTabPos - nInStart)
  309.    Else
  310.       sColString = Mid$(sColHeadings, nInStart, Len(sColHeadings) - nInStart + 1)
  311.    End If
  312.  
  313.    'Measure the length of the string, in pixels;
  314.    'this value is the current "column width".
  315.    
  316.    sColString = sColString + Space$(nSpaceBetweenCols)
  317.    nColWidth(nNbrListboxCols) = dulist_nlGetTextExtent(ctlListControl.Parent.hDC, sColString, Len(sColString)) Mod 65536
  318.  
  319.    If nTabPos > 0 Then
  320.       nNbrListboxCols = nNbrListboxCols + 1
  321.  
  322.       'Allocate space for more columns, if necessary
  323.  
  324.       If nNbrListboxCols > nMaxListboxCols Then
  325.          nMaxListboxCols = nNbrListboxCols
  326.          ReDim Preserve nColWidth(nMaxListboxCols)
  327.       End If
  328.  
  329.       If nTabPos < Len(sColHeadings) Then
  330.          nInStart = nTabPos + 1
  331.       Else
  332.          Exit Do
  333.       End If
  334.    Else
  335.       Exit Do
  336.    End If
  337. Loop
  338.  
  339. nNbrTabstops = nNbrListboxCols - 1
  340.  
  341. Return
  342.  
  343. '================================
  344. SetListCols_MeasureColDataWidths:
  345. '================================
  346. 'Search for TAB characters in the listbox data.
  347. 'For each column found, measure the width of
  348. 'the data.
  349.  
  350. For nListSub = 0 To ctlListControl.ListCount - 1
  351.    If Len(ctlListControl.List(nListSub)) > 0 Then
  352.       sColData = ctlListControl.List(nListSub)
  353.       nColCount = 1
  354.  
  355.       nInStart = 1
  356.       Do
  357.          nTabPos = InStr(nInStart, sColData, sTAB)
  358.  
  359.          If nTabPos > 0 Then
  360.             sColString = Mid$(sColData, nInStart, nTabPos - nInStart)
  361.          Else
  362.             sColString = Mid$(sColData, nInStart, Len(sColData) - nInStart + 1)
  363.          End If
  364.  
  365.          'Measure the length of the string, in pixels
  366.    
  367.          sColString = sColString + Space$(nSpaceBetweenCols)
  368.          nDataWidth = dulist_nlGetTextExtent(ctlListControl.Parent.hDC, sColString, Len(sColString)) Mod 65536
  369.  
  370.          'Ignore data columns for which there is no heading.
  371.  
  372.          If nColCount <= nNbrListboxCols Then
  373.             'If any data value is wider than the current column width,
  374.             'it becomes the new column width.
  375.  
  376.             If nDataWidth > nColWidth(nColCount) Then
  377.                nColWidth(nColCount) = nDataWidth
  378.             End If
  379.          End If
  380.  
  381.          If nTabPos > 0 Then
  382.             nColCount = nColCount + 1
  383.  
  384.             If nTabPos < Len(sColData) Then
  385.                nInStart = nTabPos + 1
  386.             Else
  387.                Exit Do
  388.             End If
  389.          Else
  390.             Exit Do
  391.          End If
  392.       Loop
  393.    End If
  394. Next
  395.  
  396. Return
  397.  
  398. '==========================
  399. SetListCols_UpdateControls:
  400. '==========================
  401. 'Set the textbox font characteristics equal
  402. 'to the listbox font characteristics.
  403.  
  404. ctlTextControl.Enabled = False
  405. ctlTextControl.FontName = ctlListControl.FontName
  406. ctlTextControl.FontSize = ctlListControl.FontSize
  407. ctlTextControl.FontBold = ctlListControl.FontBold
  408. ctlTextControl.FontItalic = ctlListControl.FontItalic
  409. ctlTextControl.Move ctlListControl.Left, ctlListControl.Top - ctlTextControl.Height, ctlListControl.Width, ctlTextControl.Height
  410.  
  411. ReDim nTabstop(nNbrTabstops)
  412.  
  413. 'Calculate tabstop values for each column, in "dialog units"
  414.  
  415. If nNbrTabstops > 0 Then
  416.    'Get the average character widths, in pixels, of the
  417.    'listbox font and the system font.
  418.  
  419.    nListFontAvgWidth = (dulist_nlGetTextExtent(ctlListControl.Parent.hDC, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52) Mod 65536) / 52
  420.    nSystemFontAvgWidth = dulist_nlGetDialogBaseUnits() Mod 65536
  421.  
  422.    'A "dialog unit" is defined as 1/4 of the average
  423.    'character width of the system font, in pixels.
  424.    'We've already measured the width of each column,
  425.    'in pixels, but it's not accurate enough to simply
  426.    'divide one value into the other.
  427.  
  428.    'Note that errors in precision will start to creep in
  429.    'at this point, due to integer rounding and intermediate
  430.    'calculation results.  Experience shows that a little
  431.    'extra white space between the data columns helps to
  432.    'compensate (see "nSpaceBetweenCols").
  433.  
  434.    'Since a dialog unit is based on the system font,
  435.    'not the font we're actually using in the listbox,
  436.    'we must factor in the difference between the two
  437.    'average character widths.  Thus, a more accurate
  438.    'divisor is calculated as follows.
  439.  
  440.    fFontRatio = nListFontAvgWidth / nSystemFontAvgWidth
  441.    fListFontPixelsPerDlgUnit = (nSystemFontAvgWidth * fFontRatio) / 4
  442.  
  443.    'Set a tabstop at the dialog unit closest to the
  444.    'right-hand boundary (width) of each column.
  445.  
  446.    nTabstop(0) = nColWidth(1) / fListFontPixelsPerDlgUnit
  447.    For nTabSub = 2 To nNbrTabstops
  448.       nTabstop(nTabSub - 1) = nTabstop(nTabSub - 2) + nColWidth(nTabSub) / fListFontPixelsPerDlgUnit
  449.    Next
  450. Else
  451.    nTabstop(0) = 0
  452. End If
  453.  
  454. 'Activate the tabstops.
  455.  
  456. nlRC = dulist_nlSetTabstops(ctlTextControl.hWnd, EM_SETTABSTOPS, nNbrTabstops, nTabstop(0))
  457. nlRC = dulist_nlSetTabstops(ctlListControl.hWnd, LB_SETTABSTOPS, nNbrTabstops, nTabstop(0))
  458.  
  459. 'Redraw the controls.
  460.  
  461. ctlTextControl.Refresh
  462. ctlListControl.Refresh
  463.  
  464. Return
  465.  
  466. End Function
  467.  
  468.