LBEX - some functions for ListBoxes

Post your working scripts, libraries and tools
just me
Posts: 7419
Joined: 02 Oct 2013, 08:51
Location: Germany

LBEX - some functions for ListBoxes

02 Oct 2014, 04:20

As usual, the first release of a script will most likely contain bugs. So testers are welcome. ;)
Functions list
LBEX.ahk:

Code: Select all

; ======================================================================================================================
; Namespace:      LBEX
; Function:       Some functions to use with AHK GUI ListBox controls (LB).
; Tested with:    AHK 1.1.23.01 (A32/U32/U64)
; Tested on:      Win 10 Pro (x64)
; Changelog:
;     1.0.02.00/2020-08-19/just me - removes unused parameter Index from LBEX_GetTopIndex()
;     1.0.01.00/2016-02-14/just me - added LBEX_SetColumnTabs()
;     1.0.00.00/2014-10-01/just me - initial release
; Common function parameters (not mentioned in the parameter descriptions of functions):
;     HLB         -  Handle to the list box control.
;     Index       -  1-based index of a list box item (line).
; MSDN:           msdn.microsoft.com/en-us/library/bb775146(v=vs.85).aspx
; ======================================================================================================================
; Add             Adds a string to a list box.
;                 If the list box does not have the LBS_SORT style, the string is added to the end of the list.
;                 Otherwise, the string is inserted into the list and the list is sorted.
; Parameters:     String   -  String to be added.
; Return values:  Returns the index of the string in the list box, 0, if an error occurs, or -1, if there
;                 is insufficient space to store the new string.
; ======================================================================================================================
LBEX_Add(HLB, ByRef String) {
   Static LB_ADDSTRING := 0x0180
   SendMessage, % LB_ADDSTRING, 0, % &String, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; CalcIdealWidth  Calculates the ideal width of a list box needed to display the current content.
; Parameters:     HLB         -  Handle to the list box.
;                                If you want to determine the ideal width of an existing control specify its HWND;
;                                all other parameters are ignored in this case.
;                                Otherwise pass 0 and use the other parameters to specify the content and other options.
;                 ------------------------------------------------------------------------------------------------------
;                 Content     -  Content to be displayed in the list box.
;                 Delimiter   -  The field separator to be used.
;                 FontOptions -  The 'Options' parameter to be used for Gui, Font, ...
;                 FontName    -  The 'Name' parameter to be used for Gui, Font, ...
; Return values:  Returns the ideal width of the list box in respect to the content.
; ======================================================================================================================
LBEX_CalcIdealWidth(HLB, Content := "", Delimiter := "|", FontOptions := "", FontName := "") {
   DestroyGui := MaxW := 0
   If !(HLB) {
      If (Content = "")
         Return -1
      Gui, LB_EX_CalcContentWidthGui: Font, %FontOptions%, %FontName%
      Gui, LB_EX_CalcContentWidthGui: Add, ListBox, hwndHLB, %Content%
      DestroyGui := True
   }
   ControlGet, Content, List, , , ahk_id %HLB%
   Items := StrSplit(Content, "`n")
   SendMessage, 0x0031, 0, 0, , ahk_id %HLB% ; WM_GETFONT
   HFONT := ErrorLevel
   HDC := DllCall("User32.dll\GetDC", "Ptr", HLB, "UPtr")
   DllCall("Gdi32.dll\SelectObject", "Ptr", HDC, "Ptr", HFONT)
   VarSetCapacity(SIZE, 8, 0)
   For Each, Item In Items {
      DllCall("Gdi32.dll\GetTextExtentPoint32", "Ptr", HDC, "Ptr", &Item, "Int", StrLen(Item), "UIntP", Width)
      If (Width > MaxW)
         MaxW := Width
   }
   DllCall("User32.dll\ReleaseDC", "Ptr", HLB, "Ptr", HDC)
   If (DestroyGui)
      Gui, LB_EX_CalcContentWidthGui: Destroy
   Return MaxW + 8 ; + 8 for the margins
}
; ======================================================================================================================
; Delete          Deletes an item (row) in a list box.
; Return values:  The return value is a count of the items remaining in the list, or 0, if Index is greater than the
;                 number of items in the list.
; ======================================================================================================================
LBEX_Delete(HLB, Index) {
   Static LB_DELETESTRING := 0x0182
   SendMessage, % LB_DELETESTRING, % (Index - 1), 0, , % "ahk_id " . HLB
   Return ErrorLevel
}
; ======================================================================================================================
; DeleteAll       Removes all items from a list box.
; Return values:  Always True.
; ======================================================================================================================
LBEX_DeleteAll(HLB) {
   Static LB_RESETCONTENT := 0x0184
   SendMessage, % LB_RESETCONTENT, 0, 0, , % "ahk_id " . HLB
   Return True
}
; ======================================================================================================================
; Find            Finds the first string in a list box that begins with the specified string.
; Parameters:     String   -  String to search for.
;                 Index    -  The index of the item before the first item to be searched.
;                             Default: 0 - the entire list box is searched.
; Return values:  The return value is the index of the matching item, or LB_ERR if the search was unsuccessful.
; ======================================================================================================================
LBEX_Find(HLB, ByRef String, Index := 0) {
   Static LB_FINDSTRING := 0x018F
   SendMessage, % LB_FINDSTRING, % (Index - 1), % &String, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; FindExact       Finds the first list box string that exactly matches the specified string.
;                 The search is not case sensitive.
; Parameters:     String   -  String to search for.
;                 Index    -  The index of the item before the first item to be searched.
;                             Default: 0 - the entire list box is searched.
; Return values:  The return value is the index of the matching item, or LB_ERR if the search was unsuccessful.
; ======================================================================================================================
LBEX_FindExact(HLB, ByRef String, Index := 0) {
   Static LB_FINDSTRINGEXACT := 0x01A2
   SendMessage, % LB_FINDSTRINGEXACT, % (Index - 1), % &String, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; GetCount        Gets the number of items in a list box.
; Return values:  The return value is the number of items in the list box, or -1 if an error occurs.
; ======================================================================================================================
LBEX_GetCount(HLB) {
   Static LB_GETCOUNT := 0x018B
   SendMessage, % LB_GETCOUNT, 0, 0, , % "ahk_id " . HLB
   Return ErrorLevel
}
; ======================================================================================================================
; GetCurrentSel   Gets the index of the currently selected item, if any, in a single-selection list box.
; Return values:  In a single-selection list box, the return value is the index of the currently selected item.
;                 If there is no selection, the return value is 0.
; Remarks:        If sent to a multiple-selection list box, the function returns the index of the item that has the
;                 focus rectangle. If no items are selected, it returns zero.
; ======================================================================================================================
LBEX_GetCurrentSel(HLB) {
   Static LB_GETCURSEL := 0x0188
   SendMessage, % LB_GETCURSEL, 0, 0, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; GetData         Gets the application-defined value associated with the specified list box item.
; Return values:  The return value is the value associated with the item, or -1 if an error occurs.
; ======================================================================================================================
LBEX_GetData(HLB, Index) {
   Static LB_GETITEMDATA := 0x0199
   SendMessage, % LB_GETITEMDATA, % (Index - 1), 0, , % "ahk_id " . HLB
   Return ErrorLevel
}
; ======================================================================================================================
; GetFocus        Retrieves the index of the item that has the focus in a multiple-selection list box.
;                 The item may or may not be selected. This function can also be used to get the index of the item
;                 that is currently selected in a single-selection list box.
; Return values:  The return value is the index of the focused item, or 0, if no item has the focus.
; Remarks:        A multiple selection spans all items from the anchor item to the focused item.
; ======================================================================================================================
LBEX_GetFocus(HLB) {
   Static LB_GETCARETINDEX := 0x019F
   SendMessage, % LB_GETCARETINDEX, 0, 0, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; GetItemHeight   Gets the height of items in a list box.
; Return values:  The return value is the height, in pixels, of each item in the list box, or -1 if an error occurs.
; ======================================================================================================================
LBEX_GetItemHeight(HLB) {
   Static LB_GETITEMHEIGHT := 0x01A1
   SendMessage, % LB_GETITEMHEIGHT, 0, 0, , % "ahk_id " . HLB
   Return ErrorLevel
}
; ======================================================================================================================
; GetSelCount     Gets the total number of selected items in a multiple-selection list box.
; Return values:  The return value is the count of selected items in the list box.
;                 If the list box is a single-selection list box, the return value is -1.
; ======================================================================================================================
LBEX_GetSelCount(HLB) {
   Static LB_GETSELCOUNT := 0x0190
   SendMessage, % LB_GETSELCOUNT, 0, 0, , % "ahk_id " . HLB
   Return ErrorLevel
}
; ======================================================================================================================
; GetSelItems     Retrieves an array of selected items in a multiple-selection list box.
; Parameters:     ItemArray   -  Array to fill with the indices of the selected items.
;                 MaxItems    -  The maximum number of selected items whose item numbers are to be placed in the array.
;                                Default: 0 - all selected items.
; Return values:  The return value is the number of items placed in the buffer.
;                 If the list box is a single-selection list box, the return value is -1.
; ======================================================================================================================
LBEX_GetSelItems(HLB, ByRef ItemArray, MaxItems := 0) {
   Static LB_GETSELITEMS := 0x0191
   ItemArray := []
   If (MaxItems = 0)
      MaxItems := LBEX_GetSelCount(HLB)
   If (MaxItems < 1)
      Return MaxItems
   VarSetCapacity(Items, MaxItems * 4, 0)
   SendMessage, % LB_GETSELITEMS, % MaxItems, % &Items, , % "ahk_id " . HLB
   MaxItems := ErrorLevel
   If (MaxItems < 1)
      Return MaxItems
   Loop, % MaxItems
      ItemArray[A_Index] := NumGet(Items, (A_Index - 1) * 4, "UInt") + 1
   Return MaxItems
}
; ======================================================================================================================
; GetSelStart     Gets the index of the anchor item from which a multiple selection starts.
; Return values:  The return value is the index of the anchor item.
; Remarks:        A multiple selection spans all items from the anchor item to the caret item.
; ======================================================================================================================
LBEX_GetSelStart(HLB) {
   Static LB_GETANCHORINDEX := 0x019D
   SendMessage, % LB_GETANCHORINDEX, 0, 0, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; GetSelState     Gets the selection state of an item.
; Return values:  If the item is selected, the return value is greater than zero; otherwise, it is zero.
;                 If an error occurs, the return value is -1.
; ======================================================================================================================
LBEX_GetSelState(HLB, Index) {
   Static LB_GETSEL := 0x0187
   SendMessage, % LB_GETSEL, % (Index - 1), 0, , % "ahk_id " . HLB
   Return ErrorLevel
}
; ======================================================================================================================
; GetText         Gets a string from a list box.
; Return values:  The return value is the string contained in the item, or an empty string, if an error occurs.
; ======================================================================================================================
LBEX_GetText(HLB, Index) {
   Static LB_GETTEXT := 0x0189
   Len := LBEX_GetTextLen(HLB, Index)
   If (Len = -1)
      Return ""
   VarSetCapacity(Text, Len << !!A_IsUnicode, 0)
   SendMessage, % LB_GETTEXT, % (Index - 1), % &Text, , % "ahk_id " . HLB
   Return StrGet(&Text, Len)
}
; ======================================================================================================================
; GetTextLen      Gets the length of a string in a list box.
; Return values:  The return value is the length of the string, in characters, excluding the terminating null character.
;                 If an error occurs, the return value is -1.
; ======================================================================================================================
LBEX_GetTextLen(HLB, Index) {
   Static LB_GETTEXTLEN := 0x018A
   SendMessage, % LB_GETTEXTLEN, % (Index - 1), 0, , % "ahk_id " . HLB
   Return ErrorLevel
}
; ======================================================================================================================
; GetTopIndex     Gets the index of the first visible item in a list box.
; Return values:  The return value is the index of the first visible item in the list box.
; ======================================================================================================================
LBEX_GetTopIndex(HLB) {
   Static LB_GETTOPINDEX := 0x018E
   SendMessage, % LB_GETTOPINDEX, 0, 0, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; Insert          Inserts a string into a list box.
;                 Unlike LV_EX_Add(), this function does not cause a list box with the LBS_SORT style to be sorted.
; Parameters:     Index    -  The index of the position at which to insert the string.
;                             If this parameter is 0, the string is added to the end of the list.
;                 String   -  String to be added.
; Return values:  The return value is the index of the string in the list box, 0, if an error occurs, or -1, if there
;                 is insufficient space to store the new string.
; ======================================================================================================================
LBEX_Insert(HLB, Index, ByRef String) {
   Static LB_INSERTSTRING := 0x0181
   SendMessage, % LB_INSERTSTRING, % (Index - 1), % &String, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; ItemFromPoint   Gets the index of the item nearest the specified point in a list box.
; Parameters:     X  -  The X-coordinate, relative to the upper-left corner of the client area of the list box.
;                 Y  -  The Y-coordinate, relative to the upper-left corner of the client area of the list box..
; Return values:  The return value contains the index of the nearest item in the LOWORD. The HIWORD is zero if the
;                 specified point is in the client area of the list box, or one if it is outside the client area.
; ======================================================================================================================
LBEX_ItemFromPoint(HLB, X, Y) {
   Static LB_ITEMFROMPOINT := 0x01A9
   X &= 0xFFFF
   Y &= 0xFFFF
   SendMessage, % LB_ITEMFROMPOINT, 0, % (X | (Y << 16)), , % "ahk_id " . HLB
   Return ((ErrorLevel & 0xFFFF) + 1) | (ErrorLevel & 0xFFFF0000)
}
; ======================================================================================================================
; SelectRange     Selects or deselects one or more consecutive items in a multiple-selection list box.
; Parameters:     First    -  The index of the first item to select/deselect.
;                 Last     -  The index of the last item to select/deselect.
;                 Select   -  True to select the range of items, or False to deselect it.
;                             Default: True - select.
; Return values:  If an error occurs, the return value is False.
; Remarks:        Use this function only with multiple-selection list boxes. This function can select a range only
;                 within the first 65,536 items.
; ======================================================================================================================
LBEX_SelectRange(HLB, First, Last, Select := True) {
   Static LB_SELITEMRANGE := 0x019B
   First &= 0xFFFF
   Last &= 0xFFFF
   SendMessage, % LB_SELITEMRANGE, % !!Select, % (First - 1) | ((Last - 1) << 16), , % "ahk_id " . HLB
   Return (ErrorLevel > 0x7FFFFFFF ? False : True)
}
; ======================================================================================================================
; SelectString    Searches a list box for an item that begins with the characters in a specified string.
;                 If a matching item is found, the item is selected.
; Parameters:     String   -  String to search for.
;                 Index    -  The index of the item before the first item to be searched.
;                             Default: 0 - the entire list box is searched.
; Return values:  If the search is successful, the return value is the index of the selected item.
;                 If the search is unsuccessful, the return value is 0 and the current selection is not changed.
; Remarks:        The list box is scrolled, if necessary, to bring the selected item into view.
;                 Do not use this function with a list box that has the LBS_MULTIPLESEL or the LBS_EXTENDEDSEL styles.
; ======================================================================================================================
LBEX_SelectString(HLB, ByRef String, Index := 0) {
   Static LB_SELECTSTRING := 0x018C
   SendMessage, % LB_SELECTSTRING, % (Index - 1), % &String, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; SetColumnTabs   Sets the tab stop positions according to the columns of a list box.
; Parameters:     ColGap   -  The amount of average characters widths used to separate the columns as an integer or
;                             floating point number. The minimum is 1.
;                             Default: 2
; Return values:  True if successful; otherwise False.
; Remarks:        The list box must have been created with the LBS_USETABSTOPS style.
;                 Columns must be separated by exactly one tab.
; Credits:        Original idea by jballi - autohotkey.com/boards/viewtopic.php?p=69544#p69544
; ======================================================================================================================
LBEX_SetColumnTabs(HLB, ColGap := 2) {
   Static StrDBU := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
   Static LenDBU := StrLen(StrDBU)
   ; Get the items
   ControlGet, Items, List, , , ahk_id %HLB%
   If (Items = "") ; error or empty list box
      Return False
   ; Check ColGap parameter
   If ((ColGap + 0) < 1)
      ColGap := 1
   ; Get the font
   HFONT := DllCall("SendMessage", "Ptr", HLB, "UInt", 0x0031, "Ptr", 0, "Ptr", 0, "UPtr")
   ; Get the DC
   HDC := DllCall("GetDC", "Ptr", HLB, "UPtr")
   ; Select the font
   DllCall("SelectObject", "Ptr", HDC, "Ptr", HFONT)
   ; Get the horizontal dialog base units
   VarSetCapacity(SIZE, 8, 0)
   DllCall("GetTextExtentPoint32", "Ptr", HDC, "Str", StrDBU, "Int", LenDBU, "Ptr", &SIZE)
   HDBU := Round(NumGet(SIZE, "Int") / LenDBU)
   ; Calculate the tab stop units per column
   ColUnits := []
   Loop, Parse, Items, `n
   {
      Loop, Parse, A_LoopField, `t
      {
         If (ColUnits[A_Index] = "")
            ColUnits[A_Index] := 0
         If !(Len := StrLen(A_LoopField))
            Continue
         DllCall("GetTextExtentPoint32", "Ptr", HDC, "Str", A_LoopField, "Int", Len, "Ptr", &SIZE)
         Units := Round((NumGet(SIZE, 0, "Int") / HDBU * 4) + (4 * ColGap))
         If (Units > ColUnits[A_Index])
            ColUnits[A_Index] := Units
      }
   }
   ; Release the DC
   DllCall("ReleaseDC", "Ptr", HLB, "Ptr", HDC)
   ; If less than two columns were found, reset the tab stops to their default
   TabCount := ColUnits.Length()
   If (TabCount < 2)
      Return LBEX_SetTabStops(HLB, 0)
   ; Build the LB_SETTABSTOPS message array parameter
   VarSetCapacity(TabArray, TabCount * 4, 0)
   TabAddr := &TabArray
   TabPos := 0
   For Index, Units In ColUnits
      TabAddr := NumPut(TabPos += Units, TabAddr + 0, "UInt")
   ; Set the tab stops - LB_SETTABSTOPS = 0x0192
   Return DllCall("SendMessage", "Ptr", HLB, "UInt", 0x0192, "Ptr", TabCount, "Ptr", &TabArray, "UInt")
}
; ======================================================================================================================
; SetCurSel       Selects an item and scrolls it into view, if necessary.
; Parameters:     Index    -  If set to 0, the list box is set to have no selection.
; Return values:  If the function succeeds, the return value is True; otherwise 0.
; Remarks:        Use this function only with single-selection list boxes.
; ======================================================================================================================
LBEX_SetCurSel(HLB, Index) {
   Static LB_SETCURSEL := 0x0186
   SendMessage, % LB_SETCURSEL, % (Index - 1), 0, , % "ahk_id " . HLB
   Return (Index = 0 ? True : ErrorLevel + 1)
}
; ======================================================================================================================
; SetFocus        Sets the focus rectangle to the specified item in a multiple-selection list box.
;                 If the item is not visible, it is scrolled into view.
; Return values:  If the function succeeds, the return value is True; otherwise 0.
; Remarks:        A multiple selection spans all items from the anchor item to the caret item.
; ======================================================================================================================
LBEX_SetFocus(HLB, Index) {
   Static LB_SETCARETINDEX := 0x019E
   SendMessage, % LB_SETCARETINDEX, % (Index - 1), 0, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; SetItemData     Sets a value associated with the specified item in a list box.
; Parameters:     Data     -  Specifies the numeric value to be associated with the item.
; Return values:  If an error occurs, the return value is False.
; ======================================================================================================================
LBEX_SetItemData(HLB, Index, Data) {
   Static LB_SETITEMDATA := 0x019A
   SendMessage, % LB_SETITEMDATA, % (Index - 1), % Data, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; SetItemHeight   Sets the height, in pixels, of items in a list box.
; Parameters:     Height   -  Specifies the height, in pixels, of the item. The maximum height is 255 pixels.
; Return values:  If an error occurs, the return value is False.
; ======================================================================================================================
LBEX_SetItemHeight(HLB, Index, Height) {
   Static LB_SETITEMHEIGHT := 0x01A0
   SendMessage, % LB_SETITEMHEIGHT, % (Index - 1), % Height, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; SetSel          Selects an item in a multiple-selection list box and scrolls the item into view, if necessary.
; Parameters:     Index    -  If set to 0, the selection is added to or removed from all items.
;                 Select   -  True to select the items, or False to deselect them.
;                             Default: True - select.
; Return values:  If the function succeeds, the return value is True; otherwise 0.
; Remarks:        Use this function only with multiple-selection list boxes.
; ======================================================================================================================
LBEX_SetSel(HLB, Index, Select := True) {
   Static LB_SETSEL := 0x0185
   SendMessage, % LB_SETSEL, % Select, % (Index - 1), , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; SetSelStart     Sets the anchor item, that is, the item from which a multiple selection starts.
; Return values:  If the function succeeds, the return value is True; otherwise 0.
; Remarks:        A multiple selection spans all items from the anchor item to the caret item.
; ======================================================================================================================
LBEX_SetSelStart(HLB, Index) {
   Static LB_SETANCHORINDEX := 0x019C
   SendMessage, % LB_SETANCHORINDEX, % (Index - 1), 0, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
; ======================================================================================================================
; SetTabStops     Sets the tab-stop positions in a list box.
; Parameters:     TabArray -  Array of integers or floating point numbers containing the tab stops as character
;                             positions according to the average character width of the used font.
;                             The tab stops must be sorted in ascending order; backward tabs are not allowed.
;                             If only one value is passed, the list box will have tab stops separated by this distance.
;                             If TabArray is set to 0, the list box will be reset to the default tab stops.
; Return values:  If all the specified tabs are set, the return value is True; otherwise, it is False.
; Remarks:        The list box must have been created with the LBS_USETABSTOPS style.
; ======================================================================================================================
LBEX_SetTabStops(HLB, TabArray) {
   Static LB_SETTABSTOPS := 0x0192
   If (TabArray = 0) {
      TabCount := 0
      TabsAddr := 0
   }
   Else {
      TabCount := TabArray.MaxIndex()
      VarSetCapacity(Tabs, TabCount * 4, 0)
      For Each, TabPos in TabArray
         NumPut(TabPos * 4, Tabs, (A_Index - 1) * 4, "UInt")
      TabsAddr := &Tabs
   }
   SendMessage, % LB_SETTABSTOPS, % TabCount, % TabsAddr, , % "ahk_id " . HLB
   Return ErrorLevel
}
; ======================================================================================================================
; SetTopIndex     Ensures that the specified item in a list box is visible.
; Return values:  If the function succeeds, the return value is True; otherwise, it is 0.
; Remarks:        The system scrolls the list box contents so that either the specified item appears at the top of the
;                 list box or the maximum scroll range has been reached.
; ======================================================================================================================
LBEX_SetTopIndex(HLB, Index) {
   Static LB_SETTOPINDEX := 0x0197
   SendMessage, % LB_SETTOPINDEX, % (Index - 1), 0, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
:arrow: Short sample script by Joe_Glines_Joetazz.
:arrow: Additional function and example to auto-calculate tab stops by jballi (requires Fnt lib).

Always take a look at GitHub for new/fixed versions. I might forget to update the script in this post. ;)

:arrow: View on GitHub.
:arrow: Download from GitHub.
Last edited by just me on 19 Aug 2020, 05:11, edited 4 times in total.
User avatar
BGM
Posts: 496
Joined: 20 Nov 2013, 20:56
GitHub: bgmCoder
Contact:

Re: LBEX - some functions for ListBoxes

06 Nov 2014, 11:22

Just Me, is there a wrapper library for regular listview functions? If there was, they would operate on the hwnd handle instead of having to always make sure you set the proper listview as default before working on the control - which can be meddlesome when you have multiple listviews.
just me
Posts: 7419
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: LBEX - some functions for ListBoxes

07 Nov 2014, 03:44

This topic applies to ListBoxes. ;)

No, but you can write your own wrapper. The simplest way might be something like this:

Code: Select all

#NoEnv
Gui, Add, ListView, w600 r10 hwndHLV1, One|Two|Three,|Four|Five
Gui, Add, ListView, w600 r10 hwndHLV2, One|Two|Three,|Four|Five
Loop, 20
   LVEX_Add(A_Index & 1 ? HLV1 : HLV2, "", A_Index, A_Index, A_Index, A_Index, A_Index)
Gui, Show, , ListViews
Return
GuiClose:
ExitApp

LVEX_ADD(HLV, Options := "", Fields*) {
   LVEX_SetLV(HLV)
   LV_Add(Options, Fields*)
}
LVEX_SetLV(HLV) {
   HGUI := DllCall("USer32.dll\GetParent", "Ptr", HLV, "UPtr")
   Gui, %HGUI%:Default
   Gui, ListView, %HLV%
}
But it still might change the current default GUI.
User avatar
BGM
Posts: 496
Joined: 20 Nov 2013, 20:56
GitHub: bgmCoder
Contact:

Re: LBEX - some functions for ListBoxes

07 Nov 2014, 09:33

Just Me - oops, sorry - you're right, this topic does belong to listboxes. Didn't mean to hijack it.
Thanks for the snippet. I know how to write wrappers (I actually wrote a library for the treeview), I was just looking for some that were already done by the pros.

But thank you for the snippet samples. I'll put those to good use!
wdmodlin
Posts: 150
Joined: 16 Dec 2015, 02:42

LBEX_ItemFromPoint(HLB, X, Y)

14 Jan 2016, 14:38

This does not seem to work.
I tried it in a GuiContextMenu() function.

; Index := LBEX_ItemFromPoint(HLB,X,Y) & 0xFFFF

returns a value that changes with the position of the right click, but is too high. Clicking somewhere in the first iline returns 4 or 5, in the second line 6 or 7.

If i do an adhoc computation of the line as line := Floor((Y-30)/14)
then string := LBEX_GetText(HLB,line) works correctly.
Last edited by wdmodlin on 14 Jan 2016, 21:49, edited 1 time in total.
User avatar
Joe Glines
Posts: 701
Joined: 30 Sep 2013, 20:49
Facebook: https://www.facebook.com/theAutomatorGuru/
Google: https://plus.google.com/105328929654286634910
GitHub: joetazz
Location: Dallas
Contact:

Re: LBEX - some functions for ListBoxes

14 Jan 2016, 21:42

Nice functionality... I don't do much with ListBoxes so I wrote a short example playing with a few of the functions. I don't normally use a handle so I thought I'd go ahead and post my simple example here for others that aren't used to it either.

Code: Select all

Gui, Add, ListBox, +HwndHLB vColorChoice h100 w100, Green||Blue
gui, show 
Count:=LBEX_GetCount(HLB)
Sleep, 1000
LBEX_Add(HLB, "Added to " count " items")
    Sleep, 1000
LBEX_DeleteAll(HLB)
Sleep, 1000
LBEX_Add(HLB, "Added this back")

Find & Click AutoHotkey syntax writer Automate my Task :clap:
AHK Tutorials:Web Scraping | AHK Studio | Webservice APIs | AHK and Excel | Chrome | RegEx | Functions
Training: AHK Webinars Courses on AutoHotkey :ugeek:
Connect with me on LinkedIn :beer: | YouTube
How-to: Create a shortcut that automatically
logs in to any website

:thumbup: Quick Access Popup, the powerful Windows folders, apps and documents launcher!
Help support the AutoHotkey foundation
just me
Posts: 7419
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: LBEX - some functions for ListBoxes

15 Jan 2016, 03:14

@wdmodlin:
The passed coordinates have to be "relative to the upper-left corner of the client area of the list box.", are they? I'll post modified example using the current cursor coordinates in the related thread in "Ask For Help".

@Joe_Glines_Joetazz:
Thanks, I added a link to the OP.
User avatar
jballi
Posts: 625
Joined: 29 Sep 2013, 17:34

Re: LBEX - some functions for ListBoxes

28 Jan 2016, 23:47

Great library. I haven't found much use for it yet but it's in my arsenal of tools.

Minor inconsistency. Tab stops for the ListBox control are measured in dialog template units. They are sometimes simply called "Dialog Units". The LBEX_SetTabStops function is taking the value for tab stops and then multiplying that value by 4. Dialog template units are usually calculated values. In order to use the function, the developer has to calculate the required dialog template units, then divide the value by 4 (usually will result in a real value) so that the function will have the desired value after multiplying it by 4. Not a big deal but I thought I would mention it.
just me
Posts: 7419
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: LBEX - some functions for ListBoxes

29 Jan 2016, 02:15

Hi jballi,

the description in the MSDN is somewhat unclear about the units. The units in the array passed toLBEX_SetTabStops() are character positions. And the following example is working here:

Code: Select all

#NoEnv
#Include LBEX.ahk
Gui, Add, ListBox, w400 r10 hwndHLB +0x0080 ; LBS_USETABSTOPS
LBEX_SetTabStops(HLB, [4]) ; every 4 characters
GuiControl, , %HLB%, 1`t1
GuiControl, , %HLB%, 12`t1
GuiControl, , %HLB%, 123`t1
GuiControl, , %HLB%, 1234`t1
Gui, Show, , Tabbed LB
Return
GuiClose:
ExitApp
User avatar
jballi
Posts: 625
Joined: 29 Sep 2013, 17:34

Re: LBEX - some functions for ListBoxes

29 Jan 2016, 03:28

Not quite. A dialog template unit is a unit of measurement. It is based on the width of the average character of the font. Specifically, the average width of the characters in this string: "ABC..XYZabc...xyz". Your example happens to work but only by luck. One small change and a tab stop of 4 doesn't work anymore. For example:

Code: Select all

#NoEnv
#Include LBEX.ahk
Gui, Add, ListBox, w400 r10 hwndHLB +0x0080 ; LBS_USETABSTOPS
LBEX_SetTabStops(HLB, [4]) ; every 4 characters
GuiControl, , %HLB%, 1`t1
GuiControl, , %HLB%, 12`t1
GuiControl, , %HLB%, 123`t1
GuiControl, , %HLB%, ZZZZ`t1
Gui, Show, , Tabbed LB
Return
GuiClose:
ExitApp
If we change the 4 (16 dialog template units) to 4.75 (19 dialog template units), then the example works again.

Dialog template units are just smaller units of average character units. The documentation refers to average characters units (more correctly known as dialog base units) but the LB_SETTABSTOPS message uses dialog template units. Since the message uses it, then the function should use it as the parameter value.

Thank you for your consideration.
just me
Posts: 7419
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: LBEX - some functions for ListBoxes

29 Jan 2016, 05:36

I think the keyword here is average character width. All tabs based on an average character width will meet character positions exactly only for fixed-width fonts. Your value 4.75 won't work in all cases, too. Should I change the parameters description to 'avarage character positions'?
User avatar
jballi
Posts: 625
Joined: 29 Sep 2013, 17:34

Re: LBEX - some functions for ListBoxes

29 Jan 2016, 06:52

just me wrote:I think the keyword here is average character width.
I think "average character width" is a good mnemonic for understanding how the value is measured but as your example shows, it's misleading to use it as form of measurement. It's also too large a unit of measure.
just me wrote:Your value 4.75 won't work in all cases, too.
Yes, exactly. This should be measured value. Sure, you can do it with trial and error but the value goes to the crapper as soon as the font changes or the text in the column changes.
just me wrote:Should I change the parameters description to 'avarage character positions'?
I think you know my opinion on that one. Dialog Template Unit or DTU. That is the unit of measurement that the message uses.

One last pitch for dialog template units. The Edit control and Rich Edit control also use dialog template units to measure tab stops.

Them be my thoughts.
just me
Posts: 7419
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: LBEX - some functions for ListBoxes

29 Jan 2016, 10:15

ListBox Options wrote:Tn: The letter T may be used to set tab stops, which can be used to format the text into columns. If the letter T is not used, tab stops are set at every 32 dialog units (the width of each "dialog unit" is determined by the operating system). If the letter T is used only once, tab stops are set at every n units across the entire width of the control. For example, Gui, Add, ListBox, vMyListBox t64 would double the default distance between tab stops. To have custom tab stops, specify the letter T multiple times as in the following example: Gui, Add, ListBox, vMyListBox t8 t16 t32 t64 t128. One tab stop is set for each of the absolute column positions in the list, up to a maximum of 50 tab stops.

Source
I still remember my confusion when I read this 'description' the first time:
... (the width of each "dialog unit" is determined by the operating system) ...
did'nt tell me anything.

The same happened when I tried to set tab stops within an edit control with a fixed-width font. I had to do it with trial and error until i finally noticed that 12 dialog units are exactly 3 characters in this case. So for me 'character position' is more descriptive than 'dialog units', and to explain 'dialog units' more detailed, you have to use the term 'average character width' anyway. But I'll change the parameter description to

Code: Select all

; Parameters:     TabArray -  Array of integers or floating point numbers containing the tab stops as character
;                             positions according to the average character width of the used font.
User avatar
jballi
Posts: 625
Joined: 29 Sep 2013, 17:34

Re: LBEX - some functions for ListBoxes

31 Jan 2016, 19:16

I wrote an add-on function for the LBEX library that will automatically set the tab stops for the ListBox control. It requires the LBEX and Fnt libraries. I hope that someone finds it useful.

Code: Select all

;------------------------------
;
; Function: LBEX_AutoSetTabStops v0.1 (Preview)
;
; Description:
;
;   Automatically sets the tab stops for an ListBox control by examining the
;   tab-delimited data in the control and then setting the tabs stops so that
;   each column is wide enough to show all fields in the column.
;
; Type:
;
;   Add-on for the LBEX library.
;
; Parameters:
;
;   p_ColumnGap - The amount of empty space, in dialog template units, to
;       separate each column.  The minimum is 1.  The recommended minimum is 2.
;       The default is 6 which is equal to the width of 1.5 average characters
;       of the ListBox control's font.
;
; Returns:
;
;   Return value of LBEX_SetTabStops.
;
; Calls to other functions:
;
; * Fnt_GetFont
; * Fnt_Pixels2DialogTemplateUnits
; * LBEX_GetCount
; * LBEX_GetText
; * LBEX_SetTabStops
;
; Remarks:
;
;   If this function is used while the ListBox is showing, the control may need
;   to be redrawn in order to show the changes.  Two examples:
;
;       (start code)
;       Example1:
;       LBEX_AutoSetTabStops(hLB,4)
;       WinSet Redraw,,ahk_id %hLB%
;       return
;
;       Example2:
;       LBEX_AutoSetTabStops(hLB)
;       GUIControl +Redraw,MyLB
;       return
;       (end)
;
;-------------------------------------------------------------------------------
LBEX_AutoSetTabStops(hLB,p_ColumnGap=6)
    {
    Static Dummy0237
          ,HWND_DESKTOP:=0
          ,s_ColumnGap:=6
                ;-- Default column gap (in dialog template units)

    ;[==============]
    ;[  Initialize  ]
    ;[==============]
    ;-- Parameters
    if p_ColumnGap is not Integer
        p_ColumnGap:=s_ColumnGap

    ;-- If there are no ListBox items, set tab stops to the default and bounce
    if not l_LBCount:=LBEX_GetCount(hLB)
        Return LBEX_SetTabStops(hLB,0)

    ;[================]
    ;[  Process Prep  ]
    ;[================]
    ;-- Initialize
    ColumnWidth:=Object()
    VarSetCapacity(SIZE,8,0)

    ;-- Get the ListBox control's font
    hFont:=Fnt_GetFont(hLB)

    ;-- Select the font into the device context for the desktop
    hDC      :=DllCall("GetDC","Ptr",HWND_DESKTOP)
    hFont_Old:=DllCall("SelectObject","Ptr",hDC,"Ptr",hFont)

    ;[===========]
    ;[  Process  ]
    ;[===========]
    ;-- Determine the maximum width, in pixels, for each column
    l_NbrOfColumns:=0
    Loop %l_LBCount%
        {
        ;-- Get text
        l_Text:=LBEX_GetText(hLB,A_Index)
        Loop Parse,l_Text,%A_Tab%
            {
            ;-- Update number of columns?
            if (A_Index>l_NbrOfColumns)
                {
                l_NbrOfColumns:=A_Index

                ;-- Initialize ColumnWidth array value
                ColumnWidth[A_Index]:=0
                }

            ;-- Skip if null
            if not StrLen(A_LoopField)
                Continue

            ;-- Get the string size, in pixels
            DllCall("GetTextExtentPoint32"
                ,"Ptr",hDC                          ;-- hDC
                ,"Str",A_LoopField                  ;-- lpString
                ,"Int",StrLen(A_LoopField)          ;-- c (string length)
                ,"Ptr",&SIZE)                       ;-- lpSize

            l_Width:=NumGet(SIZE,0,"Int")

            ;-- Update the ColumnWidth array if needed
            if (l_Width>ColumnWidth[A_Index])
                ColumnWidth[A_Index]:=l_Width
            }
        }

    ;-- Release the objects needed by the "GetTextExtentPoint32" function
    DllCall("SelectObject","Ptr",hDC,"Ptr",hFont_Old)
        ;-- Necessary to avoid memory leak

    DllCall("ReleaseDC","Ptr",HWND_DESKTOP,"Ptr",hDC)

    ;-- If there is only 1 column, set tab stops to the default and bounce
    if (l_NbrOfColumns=1)
        Return LBEX_SetTabStops(hLB,0)

    ;-- Create an array of numbers that will used by LBEX_SetTabStops
    TSArray:=Object()
    l_TabStop:=0  ;-- In dialog template units
    Loop % l_NbrOfColumns-1
        {
        Fnt_Pixels2DialogTemplateUnits(hFont,ColumnWidth[A_Index],0,l_HorzDTUs)
        l_TabStop+=l_HorzDTUs+p_ColumnGap
        TSArray[A_Index]:=l_TabStop/4
        }

    ;-- Set tab stop(s)
    Return LBEX_SetTabStops(hLB,TSArray)
    }
Example of use:

Code: Select all

/*
    In this example, the LBEX_AutoSetTabStops function is used to calculate the
    width of the tab stops in an ListBox control so that each tab-delimited
    column contain enough room to display the data of each column. The LBEX
    library is used to perform all of the actions on the ListBox control.  See
    the documentation in the LBEX_AutoSetTabStops function for additional
    considerations.
*/
#NoEnv
#SingleInstance Force

;-- Generate list with random components
LBList:=""
Loop 10
    {
    if (A_Index>1)
        LBList.="|"

    Loop 3
        {
        Item:="Column " . A_Index . " Stuff"

        Random UC,1,3  ;-- 1 in 3 chance
        if (UC=1)
            StringUpper Item,Item
         else
            {
            Random LC,1,3  ;-- 1 in 3 chance (less since UC checked first)
            if (LC=1)
                StringLower Item,Item
            }

        ExtraStuff:=""
        Random Append,0,1
        if Append
            {
            Random Short,0,1
            if Short
                Random ExtraStuff,1,999
              else
                Random ExtraStuff,1000,2147483647
            }

        LBList.=Item
        if ExtraStuff
            LBList.=A_Space . ExtraStuff

        if (A_Index<3)
            LBList.="`t"
        }
    }

;-- Build GUI
gui -DPIScale -MinimizeBox
gui Font,Bold
gui Add,Button,xm           gAutoSetTabStops,%A_Space% &Auto Set %A_Space%
gui Font
gui Add,Button,x+5 wp       gResetTabStops,&Reset
gui Add,Button,x+40 Default gReload,%A_Space% Rebuild Example... %A_Space%
Random FontSize,5,12
gui Font,s%FontSize%
gui Add,ListBox,xm w800 r10 +hWndhLB vMyLB,%LBList%
gui Font
;;;;;LBEX_AutoSetTabStops(hLB)  ;-- Uncomment to auto-set the tab stops b4 the window is shown

;-- Show it
SplitPath A_ScriptName,,,,$ScriptTitle
gui Show,,%$ScriptTitle%
return

AutoSetTabStops:
LBEX_AutoSetTabStops(hLB)
GUIControl +Redraw,MyLB
return

GUIClose:
GUIEscape:
ExitApp

Reload:
Reload
return

ResetTabStops:
LBEX_SetTabStops(hLB,0)
WinSet Redraw,,ahk_id %hLB%
return

;*******************
;*                 *
;*    Functions    *
;*                 *
;*******************
#include Fnt.ahk
#include LBEX.ahk
#include LBEX_AutoSetTabStops.ahk
just me
Posts: 7419
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: LBEX - some functions for ListBoxes

01 Feb 2016, 02:40

Nice! I will not add it to the LBEX lib because it requires an additional library, but I added a link in the OT.
jnsm
Posts: 11
Joined: 27 Nov 2015, 06:49

Re: LBEX - some functions for ListBoxes

18 Oct 2016, 04:53

Looks very promising, thank you. Can this code be used to search inside big listboxes populated by delimited txt files?

I've been trying to make it work with following script:

Code: Select all

ListBox_Text = ;initialize empty variable
  Loop, Read, accounts.txt
    {
	StringSplit, listout, A_LoopReadLine, `,
	ListBox_Text .= listout1 "`t" "`t" listout2 "`t" "`t" listout3 "`t" "`t" "`t" listout4 "`t" listout5 "|" ;append a line of text
	}
I've used LBEX_Find, but don't understand how it works - I want to make a few keystrokes of an account I am searching for and the result should be focus on the correct line in listbox.

Code: Select all

Start:
  InputBox, srch, What are you looking for?, Type the searchstring
  LBEX_Find(listout, %srch%, Index := 0)
  ;LBEX_Find(listout, %srch%, A_LoopReadLine)
  ;LBEX_Find(HLB, %srch%, Index := 0)
  MsgBox, %LB_FINDSTRING%
Return
just me
Posts: 7419
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: LBEX - some functions for ListBoxes

18 Oct 2016, 05:29

The first parameter of all LBEX functions is the handle to the ListBox. You can easily get it, e.g.

Code: Select all

Gui, Add, Listbox, hwndHLB ...
The built-in search used for LB_FINDSTRING and LB_FINDSTRINGEXACT messages sent by LBEX_Find() and LBEX_FindExact() will only find items beginning with the search string (Find) or exactly matching the search string (FindExact).

Also, both functions will only return the index of the item on success. If you want to select the item you have to call LBEX_SetCurSel() passing the returned index.
jnsm
Posts: 11
Joined: 27 Nov 2015, 06:49

Re: LBEX - some functions for ListBoxes

19 Oct 2016, 07:32

Thank you very much just me!

I have corrected the handle that I misused for the first time. I didn't paste this line before:

Code: Select all

Gui, Add, ListBox, x6 y10 w355 h370 vMyListBox gDbleClick, %ListBox_Text%
If I understand correctly, the handle is vMyListBox and I don't need to pass the third parameter to function?

The thing giving trouble me is that I can't seem to find how the function returns the index which I should pass to LBEX_SetCurSel()..

I tried different combinations but I don't know how do I call the returned result and pass it further?

Code: Select all

Find:
  InputBox, srch, What are you looking for?, Type the searchstring 
  LBEX_Find(vMyListBox, %srch%)
  LBEX_SetCurSel(vMyListBox, Index)
Return
or

Code: Select all

Test := LBEX_Find(vMyListBox, BEGP)
MsgBox % Test
I hope I am not to annoying with my noobish questions. Wishing you great days!
just me
Posts: 7419
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: LBEX - some functions for ListBoxes

19 Oct 2016, 08:20

Code: Select all

Gui, Add, ListBox, x6 y10 w355 h370 vMyListBox hwndListBoxHandle gDbleClick, %ListBox_Text%
vMyListBox assigns the variable MyListBox to the control. hwndListBoxHandle stores the handle of the control in the variable ListBoxHandle. This variable can be passed as the first parameter of the LBEX functions.

Code: Select all

LBEX_Find(vMyListBox, %srch%)
Function calls are expressions. You must not enclose variable names in %-signs in this case. Also, the name of the control variable is MyListBox (w/o the leading v), but you cannot use it here.
A_AhkUser
Posts: 1147
Joined: 06 Mar 2017, 16:18
GitHub: AAhkUser
Location: France

Re: LBEX - some functions for ListBoxes

17 Aug 2020, 15:38

Hi just me,

This set of functions proved to be a a reliable work when using listbox in projects; this is a great work that helped me a lot. I have one question though, a doubt regarding the LBEX_GetTopIndex function:

Code: Select all

LBEX_GetTopIndex(HLB, Index) {
   Static LB_GETTOPINDEX := 0x018E
   SendMessage, % LB_GETTOPINDEX, 0, 0, , % "ahk_id " . HLB
   Return (ErrorLevel + 1)
}
I was suprised to be prompted to pass a second parameter when I first try to use it. At first, I thought I erroneously interchanged both the LBEX_GetTopIndex and the LBEX_SetTopIndex functions... Unless I'm mistaken, it seems the Index parameter, not being used inside the body of the function and not being a ByRef parameter is a typo.

Thanks again for your invaluable work.

A_AhkUser
my scripts

Return to “Scripts and Functions”

Who is online

Users browsing this forum: gwarble and 25 guests