Jump to content

Incremental search in owner and custom drawn ListViews


LarsJ
 Share

Recommended Posts

I am pleased with the appearance of a new version.

Unfortunately, it has the same features (for me the problem) as the previous one.

1. How to make a button to align the first column on the left-mid-right?

2. I added the context menu on the LV to get text using Func _GetText from pixelsearch.

How to prohibit the context menu call on the header of the LV to leave only the "choice of column to search" option?

Thank you.

Edited by Lion66
Link to comment
Share on other sites

6 hours ago, Lion66 said:

How to prohibit the context menu call on the header of the LV to leave only the "choice of column to search" option?

I'd re-use the same coords test as found in this script
Applied to the present script, it would end like this :

...
$g_hHeader = _GUICtrlListView_GetHeader($g_idListView)
Local $iHeaderHeight = _WinAPI_GetWindowHeight($g_hHeader) ; <========== added line
...

    Case $GUI_EVENT_SECONDARYDOWN
        Local $aMPos = MouseGetPos()
        Local $aLVPos = WinGetPos($g_hListView)
        Local $aLVCli = WinGetClientSize($g_hListView) ; includes LV headers, excludes both scrollbars (if any), great !
        If $aMPos[0] > $aLVPos[0] + 1 AND $aMPos[0] <= $aLVPos[0] + $aLVCli[0] + 1 _
            AND $aMPos[1] > $aLVPos[1] + $iHeaderHeight + 1 AND $aMPos[1] <= $aLVPos[1] + $aLVCli[1] + 1 Then
            Local $iSelected = GUICtrlSendMsg($g_idListView, $LVM_GETNEXTITEM, -1, $LVNI_SELECTED)
            If $iSelected  > - 1 Then ; if an item is selected
                Local $sGetText = _GetText($iSelected, 0)
                MsgBox($MB_TOPMOST, "Item = " & $iSelected, "Text col 0 = " & $sGetText)
            EndIf
        EndIf
...

;========================================================================
Func _GetText($iItem, $iSubItem)

    Local $sGetText
    If $g_iSortDir = 0x0400 Then ; 0x0400 = $HDF_SORTUP
        $sGetText = $g_aSubArray[$g_tIndex.arr($iItem + 1)][$iSubItem]
    Else
        $sGetText = $g_aSubArray[$g_tIndex.arr($g_iSearch - $iItem)][$iSubItem]
    EndIf
    Return $sGetText
EndFunc   ;==>_GetText

 

Edited by pixelsearch
Link to comment
Share on other sites

An interesting attempt.
And although the message is not issued when clicking on the header, but the context menu still appears.
It is necessary to somehow solve in the same style as Larsj forbade the change in the column width.

Link to comment
Share on other sites

56 minutes ago, Lion66 said:

but the context menu still appears.

As you don't share your code, then I had no idea that you created a real Context Menu !

I simply thought that you called "Context Menu" the fact that the right click I indicated (Case $GUI_EVENT_SECONDARYDOWN) was retrieving infos and the last script I presented is enough to prevent a right click in LV header.

Now if you want to see how to prevent a real Context Menu to appear, then you need to take care of the message  WM_CONTEXTMENU as shown in this thread where we exchanged some good ideas with Nine. Then you could mix it with the coords to exclude of the preceding script.

Good luck :)

Link to comment
Share on other sites

The pixelsearch made a very good (complete) example in which the search for columns is done.
What do you have to do so that the search in the entire list is carried out in all columns at the same time?
For example, if I wrote the number 7 so that this number would appear in all columns.

Link to comment
Share on other sites

Hi pixelsearch.

I tried to block the context menu by using your tip.
The menu is blocked if it is set to all GUI, and is not blocked if the menu is set to LV.

And in this case, the WM_CONTEXTMENU sends messages when right-clicking in any part of the GUI, and not only in LV.
And in general, I do not like the use of coordinates.
In my Windows 7, click in the bottom of the header is worked as in the inside LV (and not as on header).
I assume that need change the offsets that, with different window themes and different OS, there will be different.

#include <ComboConstants.au3>
#include <EditConstants.au3>
#include <GuiListView.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include "RandomArray.au3" ; LarsJ

Opt("MustDeclareVars", 1)

Global $g_iRows = 1000, $g_iCols = 6, $g_iLeftLV = 10, $g_iTopLV = 40, $g_hGui, $g_hListView, $g_hHeader, $g_hEdit
Global $g_aCols = ["Strings", "Integers", "Floats", "Dates", "Times", "R/C"], $g_aWidths = [230, 61, 124, 70, 60, 60]
Global $g_idListView, $g_idMarker, $g_idComboCol, $g_idComboColDummy, $g_idEditDummy
Global $g_sSearch, $g_iSearchCol, $g_iSortDir, $g_iSearch = $g_iRows
Global $g_aArray, $g_aSubArray, $g_tDefaultIndex, $g_tIndex = DllStructCreate("uint arr[" & $g_iRows & "]")
Global $g_aIndex[$g_iCols], $g_aIndexTemp[$g_iCols] ; VarGetType's : $g_aIndex => "Array", $g_aIndex[0] => "String"
Global $idDummy_CancelCrop

Example()

Func Example()
    ; Generate array & one index
    _Generate_All($g_aArray)
    $g_aSubArray = $g_aArray
    $g_tDefaultIndex = $g_tIndex

    ; Create GUI
    $g_hGui = GUICreate("Virtual ListView + Sort + Incremental search + CustomDraw (2k)", 630 + 20, 400 + 30 + 20)

    ; Create ComboBox control (how to search : RegEx or Normal ?)
    Local $idSearchHow = GUICtrlCreateCombo("RegEx search", 11, 9, 100, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST))
    Local $sSearchHow = "RegEx search", $sSearchHowPrev = $sSearchHow ; default way of searching (changeable)
    GUICtrlSetData($idSearchHow, "Normal search", $sSearchHow)

    Local $Left = GUICtrlCreateButton("Left", 120, 10, 60, 20)
    Local $Right = GUICtrlCreateButton("Right", 190, 10, 60, 20)

    ; Create Edit control (search) + dummy control
    Local $idEdit = GUICtrlCreateEdit("", 260, 10, 160, 20, BitXOR($GUI_SS_DEFAULT_EDIT, $WS_HSCROLL, $WS_VSCROLL))
    $g_hEdit = GUICtrlGetHandle($idEdit)
    $g_idEditDummy = GUICtrlCreateDummy()

    ; Create ComboBox control (column where to search) + dummy control
    GUICtrlCreateLabel("Col", 429, 10, 20, 20, BitOR($SS_CENTERIMAGE, $SS_CENTER))
    $g_idComboCol = GUICtrlCreateCombo("0", 452, 9, 41, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST))
    $g_iSearchCol = 0 ; default column where to search (changeable)
    Local $iSearchColPrev = $g_iSearchCol
    For $i = 1 To $g_iCols - 1
        GUICtrlSetData($g_idComboCol, $i & "|", $g_iSearchCol)
    Next
    $g_idComboColDummy = GUICtrlCreateDummy()

    ; Create Label control (number of matching results)
    Local $idResult = GUICtrlCreateLabel(" " & $g_iRows & " rows (no pattern)", 501, 10, 135, 20, BitOR($SS_CENTERIMAGE, $SS_SUNKEN))

    ; Create ListView
    $g_idListView = GUICtrlCreateListView("", $g_iLeftLV, $g_iTopLV, 630, 400, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_OWNERDATA), $WS_EX_CLIENTEDGE)
    _GUICtrlListView_SetExtendedListViewStyle($g_idListView, BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT))
    $g_hListView = GUICtrlGetHandle($g_idListView)
    $g_hHeader = _GUICtrlListView_GetHeader($g_idListView)
    Global $iHeaderHeight = _WinAPI_GetWindowHeight($g_hHeader)
    For $i = 0 To $g_iCols - 1
        _GUICtrlListView_AddColumn($g_idListView, $g_aCols[$i], $g_aWidths[$i])
    Next

    $idDummy_CancelCrop = GUICtrlCreateDummy()
    ;Local $idButtoncontext = GUICtrlCreateContextMenu()    ; for all GUI
    Local $idButtoncontext = GUICtrlCreateContextMenu($g_idListView)    ; for LV only
    Local $idGetText = GUICtrlCreateMenuItem("Get text", $idButtoncontext)

    ; Create Marker (an orange line placed above the header of the column being searched)
    $g_idMarker = GUICtrlCreateLabel("", 0, 0)
    GUICtrlSetBkColor(-1, 0xFFA060) ; orange
    _MoveMarker($g_iSearchCol)
    _GUICtrlListView_SetSelectedColumn($g_idListView, $g_iSearchCol)

    ; Sorting information
    $g_iSortDir = 0x0400 ; $HDF_SORTUP
    Local $iSortCol = -1, $iSortColPrev = -1

    GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
    GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")
    GUIRegisterMsg($WM_CONTEXTMENU, "WM_CONTEXTMENU")

    GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iRows, 0)

    GUISetState(@SW_SHOW)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $idDummy_CancelCrop
                ConsoleWrite("Code for Cancel Crop" & @CRLF)

            Case $idGetText
                Local $iSelected = GUICtrlSendMsg($g_idListView, $LVM_GETNEXTITEM, -1, $LVNI_SELECTED)
                If $iSelected > -1 Then ; an item is selected
                    Local $sGetText = _GetText($iSelected, 0)
                    MsgBox($MB_TOPMost, "Item = " & $iSelected, "Text col 0 = " & $sGetText)
                EndIf

            Case $Left
                _GUICtrlListView_JustifyColumn($g_idListView, 0, 0)

            Case $Right
                _GUICtrlListView_JustifyColumn($g_idListView, 0, 1)

            Case $g_idComboCol, $g_idComboColDummy, $idSearchHow
                $g_iSearchCol = GUICtrlRead($g_idComboCol)
                $sSearchHow = GUICtrlRead($idSearchHow)
                Select
                    Case $g_iSearchCol <> $iSearchColPrev
                        _MoveMarker($g_iSearchCol) ; Search column will be selected below, after ContinueCase
                        $iSearchColPrev = $g_iSearchCol
                    Case $sSearchHow <> $sSearchHowPrev
                        $sSearchHowPrev = $sSearchHow
                    Case Else ; no change in both Combo controls (same search column, same search way)
                        ContinueLoop
                EndSelect
                ContinueCase

            Case $g_idEditDummy
                _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortCol, $HDF_STRING)
                $g_sSearch = GUICtrlRead($idEdit)
                $g_tIndex = $g_tDefaultIndex
                If $g_sSearch = "" Then
                    ; Empty search string, display all rows
                    $g_aSubArray = $g_aArray
                    $g_iSearch = $g_iRows
                Else
                    ; Find rows matching the search string
                    $g_iSearch = 0
                    If $sSearchHow = "RegEx search" Then
                        For $i = 0 To $g_iRows - 1 ; duplicated For... Next for speed
                            If StringRegExp($g_aArray[$i][$g_iSearchCol], "(?i)" & $g_sSearch) Then
                                For $j = 0 To $g_iCols - 1
                                    $g_aSubArray[$g_iSearch][$j] = $g_aArray[$i][$j]
                                Next
                                $g_iSearch += 1
                            EndIf
                        Next
                    Else ; "Normal search"
                        For $i = 0 To $g_iRows - 1 ; duplicated For... Next for speed
                            If StringInStr($g_aArray[$i][$g_iSearchCol], $g_sSearch) Then
                                For $j = 0 To $g_iCols - 1
                                    $g_aSubArray[$g_iSearch][$j] = $g_aArray[$i][$j]
                                Next
                                $g_iSearch += 1
                            EndIf
                        Next
                    EndIf
                    ; Delete eventual temporary subindexes
                    For $i = 0 To $g_iCols - 1
                        If VarGetType($g_aIndexTemp[$i]) = "DLLStruct" Then $g_aIndexTemp[$i] = "" ; "String"
                    Next
                EndIf
                GUICtrlSetData($idResult, " " & $g_iSearch & ($g_sSearch = "" ? " rows (no pattern)" : " matching rows"))
                ;------
                ; _GUICtrlListView_SetSelectedColumn($g_hListView, $g_iSearchCol) ; depending on search, crashes script when placed here
                ; _GUICtrlListView_SetSelectedColumn($g_idListView, $g_iSearchCol) ; strange behavior here
                ; GUICtrlSendMsg($g_idListView, $LVM_SETSELECTEDCOLUMN, $g_iSearchCol, 0) ; strange behavior here
                ;------
                GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iSearch, 0)
                _GUICtrlListView_SetSelectedColumn($g_hListView, $g_iSearchCol) ; seems ok here (after $LVM_SETITEMCOUNT)

            Case $g_idListView ; Sort
                $iSortCol = GUICtrlGetState($g_idListView)
                If $iSortCol <> $iSortColPrev Then
                    _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortColPrev, $HDF_STRING)
                EndIf
                ; Set $g_tIndex + eventual update of $g_aIndexTemp[$iSortCol] OR $g_aIndex[$iSortCol]
                If GUICtrlRead($idEdit) Then
                    _UpdateIndex($g_aIndexTemp, $iSortCol)
                Else
                    _UpdateIndex($g_aIndex, $iSortCol)
                EndIf
                $g_iSortDir = (($iSortCol = $iSortColPrev) ? ($g_iSortDir = $HDF_SORTUP ? $HDF_SORTDOWN : $HDF_SORTUP) : ($HDF_SORTUP))
                _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortCol, $HDF_STRING + $g_iSortDir)
                GUICtrlSendMsg($g_idListView, $LVM_SETSELECTEDCOLUMN, $iSortCol, 0)
                GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iSearch, 0)
                $iSortColPrev = $iSortCol

            Case $GUI_EVENT_RESTORE ; needed, or Marker goes back in 0, 0 after Restore (why ?)
                _MoveMarker($g_iSearchCol)
        EndSwitch
    WEnd

    ; Cleanup
    GUIDelete($g_hGui)
EndFunc   ;==>Example

;========================================================================
Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)

    Switch HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
        Case $g_hListView
            Switch DllStructGetData($tNMHDR, "Code")
                Case $NM_CUSTOMDRAW
                    Local $tCustDraw = DllStructCreate($tagNMLVCUSTOMDRAW, $lParam)
                    Switch DllStructGetData($tCustDraw, "dwDrawStage") ; Holds a value that specifies the drawing stage
                        Case $CDDS_PREPAINT ; Before the paint cycle begins
                            Return $CDRF_NOTIFYITEMDRAW ; Notify the parent window of any item-related drawing operations
                        Case $CDDS_ITEMPREPAINT ; Before painting an item
                            Return $CDRF_NOTIFYSUBITEMDRAW ; Notify the parent window of any subitem-related drawing operations
                        Case $CDDS_ITEMPREPAINT + $CDDS_SUBITEM ; Before painting a subitem
                            If (DllStructGetData($tCustDraw, "iSubItem") = $g_iSearchCol) And $g_sSearch Then Return $CDRF_NOTIFYPOSTPAINT ; If $g_sSearch then paint $g_iSearchCol in postpaint stage
                            Return $CDRF_NEWFONT ; $CDRF_NEWFONT must be returned after changing font or colors          ; Paint all other columns in prepaint stage with default code
                        Case $CDDS_ITEMPOSTPAINT + $CDDS_SUBITEM ; After painting a subitem                                              ; Paints $g_iSearchCol ($g_iSearchCol only) if $g_sSearch
                            Local Static $tRect = DllStructCreate($tagRECT), $pRect = DllStructGetPtr($tRect), $tSize = DllStructCreate($tagSIZE)
                            Local Static $hBrushYellow = _WinAPI_CreateSolidBrush(0x00FFFF), $hBrushCyan = _WinAPI_CreateSolidBrush(0xFFFF00) ; Yellow and cyan, BGR
                            Local $iItem = DllStructGetData($tCustDraw, "dwItemSpec"), $hDC = DllStructGetData($tCustDraw, "hDC")

                            ; Subitem text and matching text
                            Local $sSubItemText = $g_iSortDir = 0x0400 ? $g_aSubArray[$g_tIndex.arr($iItem + 1)][$g_iSearchCol] _ ; 0x0400 = $HDF_SORTUP
                                     : $g_aSubArray[$g_tIndex.arr($g_iSearch - $iItem)][$g_iSearchCol]
                            Local $sMatch = StringRegExp($sSubItemText, "(?i)" & $g_sSearch, 1), $extended = @extended, $iLen = StringLen($sMatch[0])

                            ; Entire subitem rectangle
                            DllStructSetData($tRect, 2, $g_iSearchCol) ; Top
                            DllStructSetData($tRect, 1, $LVIR_BOUNDS) ; Left
                            GUICtrlSendMsg($g_idListView, $LVM_GETSUBITEMRECT, $iItem, $pRect)
                            DllStructSetData($tRect, 1, DllStructGetData($tRect, 1) + ($g_iSearchCol ? 6 : 3)) ; Left margin

                            ; Set transparent background for the subitem
                            DllCall("gdi32.dll", "int", "SetBkMode", "handle", $hDC, "int", $TRANSPARENT) ; _WinAPI_SetBkMode

                            ; Draw entire subitem text
                            DllStructSetData($tRect, 2, DllStructGetData($tRect, 2) + 2) ; Top  margin
                            DllCall("user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sSubItemText, "int", StringLen($sSubItemText), "struct*", $tRect, "uint", 0) ; _WinAPI_DrawText
                            DllStructSetData($tRect, 2, DllStructGetData($tRect, 2) - 2) ; Top  margin

                            ; Rectangle for matching substring
                            DllCall("gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sSubItemText, "int", $extended - $iLen - 1, "struct*", $tSize) ; _WinAPI_GetTextExtentPoint32
                            DllStructSetData($tRect, "Left", DllStructGetData($tRect, "Left") + DllStructGetData($tSize, "X"))
                            DllCall("gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tSize) ; _WinAPI_GetTextExtentPoint32
                            DllStructSetData($tRect, "Right", DllStructGetData($tRect, "Left") + DllStructGetData($tSize, "X"))

                            ; Fill matching rectangle with yellow or cyan background color
                            DllCall("user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", GUICtrlSendMsg($g_idListView, $LVM_GETITEMSTATE, $iItem, $LVIS_SELECTED) ? $hBrushYellow : $hBrushCyan) ; _WinAPI_FillRect

                            ; Redraw matching text on the yellow or cyan background
                            DllStructSetData($tRect, 2, DllStructGetData($tRect, 2) + 2) ; Top  margin
                            DllCall("gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", 0x000000) ; _WinAPI_SetTextColor
                            DllCall("user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tRect, "uint", 0) ; _WinAPI_DrawText
                            Return $CDRF_NEWFONT ; $CDRF_NEWFONT must be returned after changing font or colors
                    EndSwitch

                Case $LVN_GETDISPINFOW
                    Local $tNMLVDISPINFO = DllStructCreate($tagNMLVDISPINFO, $lParam)
                    If ($tNMLVDISPINFO.SubItem = $g_iSearchCol) And $g_sSearch Then Return ; Don't paint $g_iSearchCol if $g_sSearch
                    Local Static $tText = DllStructCreate("wchar[50]"), $pText = DllStructGetPtr($tText)
                    Local $s = $g_iSortDir = 0x0400 ? DllStructSetData($tText, 1, $g_aSubArray[$g_tIndex.arr($tNMLVDISPINFO.Item + 1)][$tNMLVDISPINFO.SubItem]) _ ; 0x0400 = $HDF_SORTUP
                             : DllStructSetData($tText, 1, $g_aSubArray[$g_tIndex.arr($g_iSearch - $tNMLVDISPINFO.Item)][$tNMLVDISPINFO.SubItem])
                    DllStructSetData($tNMLVDISPINFO, "Text", $pText)
            EndSwitch

        Case $g_hHeader
            Switch DllStructGetData($tNMHDR, "Code")
                Case $HDN_ENDTRACKW, $HDN_DIVIDERDBLCLICKW ; let's forget $HDN_TRACKW...
                    _MoveMarker(GUICtrlRead($g_idComboCol))

                Case $NM_RCLICK
                    Local $aHit = _GUICtrlListView_SubItemHitTest($g_hListView)
                    ; $aHit[1] : 0-based index of the LV subitem... i.e. the column in our case (may be -1 if right click on empty part of header)
                    If $aHit[1] > -1 Then ; valid column
                        GUICtrlSetData($g_idComboCol, $aHit[1])
                        GUICtrlSendToDummy($g_idComboColDummy)
                    EndIf
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

;========================================================================
Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)

    Local $hWndFrom = $lParam
    Local $iCode = BitShift($wParam, 16) ; High word
    Switch $hWndFrom
        Case $g_hEdit
            Switch $iCode
                Case $EN_CHANGE
                    GUICtrlSendToDummy($g_idEditDummy)
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_COMMAND

;========================================================================
Func _Generate_All(ByRef $g_aArray)

    ConsoleWrite("$g_iRows = " & $g_iRows & "    $g_iCols = " & $g_iCols & @CRLF)
    Local $hTimer = TimerInit()
    $g_aArray = FAS_Random2DArrayAu3($g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz")
    For $i = 0 To $g_iRows - 1
        $g_tIndex.arr($i + 1) = $i
    Next
    ConsoleWrite("Generating array & one index = " & TimerDiff($hTimer) & @CRLF & @CRLF)
EndFunc   ;==>_Generate_All

;========================================================================
Func _SortArrayStruct(Const ByRef $aArray, $iCol, $iRows)
    Local $tIndex = DllStructCreate("uint arr[" & $iRows & "]")
    Local $pIndex = DllStructGetPtr($tIndex)
    Local Static $hDll = DllOpen("kernel32.dll")
    Local Static $hDllComp = DllOpen("shlwapi.dll")

    Local $lo, $hi, $mi, $r

    ; Sorting by one column
    For $i = 1 To $iRows - 1
        $lo = 0
        $hi = $i - 1
        Do
            $mi = Int(($lo + $hi) / 2)
            $r = DllCall($hDllComp, 'int', 'StrCmpLogicalW', 'wstr', $aArray[$i][$iCol], 'wstr', $aArray[DllStructGetData($tIndex, 1, $mi + 1)][$iCol])[0]
            Switch $r
                Case -1
                    $hi = $mi - 1
                Case 1
                    $lo = $mi + 1
                Case 0
                    ExitLoop
            EndSwitch
        Until $lo > $hi
        DllCall($hDll, "none", "RtlMoveMemory", "struct*", $pIndex + ($mi + 1) * 4, "struct*", $pIndex + $mi * 4, "ulong_ptr", ($i - $mi) * 4)
        DllStructSetData($tIndex, 1, $i, $mi + 1 + ($lo = $mi + 1))
    Next

    Return $tIndex
EndFunc   ;==>_SortArrayStruct

;========================================================================
Func _UpdateIndex(ByRef $aIndex, $iCol)

    If VarGetType($aIndex[$iCol]) = "DLLStruct" Then
        $g_tIndex = $aIndex[$iCol]
    Else
        $g_tIndex = _SortArrayStruct($g_aSubArray, $iCol, $g_iSearch)
        $aIndex[$iCol] = $g_tIndex ; "DLLStruct" (or "Int32" when no match found +++)
    EndIf
EndFunc   ;==>_UpdateIndex

;========================================================================
Func _MoveMarker($iCol)

    Local $aRect = _GUICtrlHeader_GetItemRect($g_hHeader, $iCol)
    ControlMove($g_hGui, "", $g_idMarker, $g_iLeftLV + $aRect[0], $g_iTopLV - 3, $aRect[2] - $aRect[0] + 1, 3)
EndFunc   ;==>_MoveMarker

Func _GetText($iItem, $iSubItem)
    Local $sGetText
    If $g_iSortDir = 0x0400 Then ; 0x0400 = $HDF_SORTUP
        $sGetText = $g_aSubArray[$g_tIndex.arr($iItem + 1)][$iSubItem]
    Else
        $sGetText = $g_aSubArray[$g_tIndex.arr($g_iSearch - $iItem)][$iSubItem]
    EndIf
    Return $sGetText
EndFunc   ;==>_GetText

Func WM_CONTEXTMENU($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam
    #cs
    Local $aMPos = MouseGetPos()
    Local $aLVPos = WinGetPos($g_hListView)
    Local $aLVCli = WinGetClientSize($g_hListView) ; includes LV headers, excludes both scrollbars (if any), great !

    If $aMPos[0] > $aLVPos[0] + 1 And $aMPos[0] <= $aLVPos[0] + $aLVCli[0] + 1 _
            And $aMPos[1] > $aLVPos[1] + $iHeaderHeight + 1 And $aMPos[1] <= $aLVPos[1] + $aLVCli[1] + 1 Then
        Local $iSelected = GUICtrlSendMsg($g_idListView, $LVM_GETNEXTITEM, -1, $LVNI_SELECTED)
        If $iSelected > -1 Then ; if an item is selected
            Local $sGetText = _GetText($iSelected, 0)
            MsgBox($MB_TOPMOST, "Item = " & $iSelected, "Text col 0 = " & $sGetText)
        EndIf
        Return $GUI_RUNDEFMSG
    Else
    #ce
        GUICtrlSendToDummy($idDummy_CancelCrop)
        Return 0 ; prevents the context menu to appear

    ;EndIf

EndFunc   ;==>WM_CONTEXTMENU

 

Link to comment
Share on other sites

Column alignment

AdUnaLR.png

#AutoIt3Wrapper_Au3Check_Parameters=-d -w- 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

#AutoIt3Wrapper_UseX64=Y

Opt( "MustDeclareVars", 1 )

#include <ComboConstants.au3>
#include <EditConstants.au3>
#include <GuiListView.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include "RandomArray.au3"

Global $g_iRows = 1000, $g_iCols = 6, $g_aArray, $g_aSubArray, $g_tIndex = DllStructCreate( "uint arr[" & $g_iRows & "]" ), $g_tDefaultIndex, $g_aIndex[$g_iCols], $g_aIndexTemp[$g_iCols], $g_iSortDir
Global $g_aColNames[$g_iCols] = [ "Strings", "Integers", "Floats", "Dates", "Times", "R/C" ], $g_aColWidths[$g_iCols] = [ 230, 61, 124, 70, 60, 60 ], $g_aColAligns[$g_iCols] = [ $HDF_CENTER, $HDF_RIGHT, $HDF_RIGHT, $HDF_RIGHT, $HDF_RIGHT, $HDF_RIGHT ] ; $HDF_LEFT, $HDF_RIGHT, $HDF_CENTER
Global $g_hGui, $g_idListView, $g_hListView, $g_hHeader, $g_idMarker, $g_hEdit, $g_idEditDummy, $g_idComboCol, $g_idComboColDummy
Global $g_sSearch, $g_iSearchCol, $g_iSearch = $g_iRows

Example()

Func Example()
  ; Generate array & one index
  Generate_All( $g_aArray )
  $g_aSubArray = $g_aArray
  $g_tDefaultIndex = $g_tIndex

  ; Create GUI
  $g_hGui = GUICreate( "Virtual and Custom Drawn List View With Sorting and Incremental Search", 630 + 20, 788 + 30 + 20 )

  ; Create Edit control ( search ) + dummy control
  Local $idEdit = GUICtrlCreateEdit( "", 120, 10, 305, 20, BitXOR( $GUI_SS_DEFAULT_EDIT, $WS_HSCROLL, $WS_VSCROLL ) )
  $g_hEdit = GUICtrlGetHandle( $idEdit )
  $g_idEditDummy = GUICtrlCreateDummy()

  ; Create ComboBox control ( how to search : RegEx or Normal ? )
  Local $idSearchHow = GUICtrlCreateCombo( "RegEx search", 11, 9, 100, 20, BitOR( $GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST ) )
  Local $sSearchHow = "RegEx search", $sSearchHowPrev = $sSearchHow ; Default way of searching ( changeable )
  GUICtrlSetData( $idSearchHow, "Normal search", $sSearchHow )

  ; Create ComboBox control ( column where to search ) + dummy control
  GUICtrlCreateLabel( "Col", 429, 10, 20, 20, BitOR( $SS_CENTERIMAGE, $SS_CENTER ) )
  $g_idComboCol = GUICtrlCreateCombo( "0", 452, 9, 41, 20, BitOR( $GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST ) )
  $g_iSearchCol = 0 ; Default column where to search ( changeable )
  Local $iSearchColPrev = $g_iSearchCol
  For $i = 1 To $g_iCols - 1
    GUICtrlSetData( $g_idComboCol, $i & "|", $g_iSearchCol )
  Next
  $g_idComboColDummy = GUICtrlCreateDummy()

  ; Create Label control ( number of matching results )
  Local $idResult = GUICtrlCreateLabel( " " & $g_iRows & " rows ( no pattern )", 501, 10, 135, 20, BitOR( $SS_CENTERIMAGE, $SS_SUNKEN ) )

  ; Create ListView
  $g_idListView = GUICtrlCreateListView( "", 10, 40, 630, 788, BitOr( $GUI_SS_DEFAULT_LISTVIEW, $LVS_OWNERDATA ), $WS_EX_CLIENTEDGE )
  _GUICtrlListView_SetExtendedListViewStyle( $g_idListView, BitOr( $LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT ) )
  $g_hListView = GUICtrlGetHandle( $g_idListView )
  $g_hHeader = _GUICtrlListView_GetHeader( $g_idListView )
  For $i = 0 To $g_iCols - 1
    _GUICtrlListView_AddColumn( $g_idListView, $g_aColNames[$i], $g_aColWidths[$i] )
    _GUICtrlHeader_SetItemFormat( $g_hHeader, $i, $HDF_STRING + $g_aColAligns[$i] )
  Next

  ; No ListView column resizing by dragging header dividers
  ;_WinAPI_SetWindowLong( $hHeader, $GWL_STYLE, _WinAPI_GetWindowLong( $hHeader, $GWL_STYLE ) + $HDS_NOSIZING ) ; AutoIt 3.3.14.5 issue
  DllCall( "user32.dll", "long_ptr", @AutoItX64 ? "SetWindowLongPtrW" : "SetWindowLongW", "hwnd", $g_hHeader, "int", $GWL_STYLE, "long_ptr", _
  DllCall( "user32.dll", "long_ptr", @AutoItX64 ? "GetWindowLongPtrW" : "GetWindowLongW", "hwnd", $g_hHeader, "int", $GWL_STYLE )[0] + $HDS_NOSIZING )

  ; Create Marker ( an orange line placed above the header of the column being searched )
  $g_idMarker = GUICtrlCreateLabel( "", 0, 0 )
  GUICtrlSetBkColor( -1, 0xFFA060 ) ; Orange
  MoveMarker( $g_iSearchCol )
  GUICtrlSendMsg( $g_idListView, $LVM_SETSELECTEDCOLUMN, $g_iSearchCol, 0 )

  ; Sorting information
  $g_iSortDir = $HDF_SORTUP
  Local $iSortCol = 0, $iSortColPrev = 0

  ; Register message handlers
  GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" )
  GUIRegisterMsg( $WM_COMMAND, "WM_COMMAND" )

  ; Allocate memory for ListView rows
  GUICtrlSendMsg( $g_idListView, $LVM_SETITEMCOUNT, $g_iRows, 0 )

  ; Show GUI
  GUISetState( @SW_SHOW )

  ; Main loop
  While 1
    Switch GUIGetMsg()
      Case $g_idComboCol, $g_idComboColDummy, $idSearchHow
        $g_iSearchCol = GUICtrlRead( $g_idComboCol ) + 0 ; Return int
        $sSearchHow = GUICtrlRead( $idSearchHow )
        Select
          Case $g_iSearchCol <> $iSearchColPrev
            MoveMarker( $g_iSearchCol ) ; Search column will be selected below, after ContinueCase
            $iSearchColPrev = $g_iSearchCol
          Case $sSearchHow <> $sSearchHowPrev
            $sSearchHowPrev = $sSearchHow
          Case Else ; No change in both Combo controls ( same search column, same search way )
            ContinueLoop
        EndSelect
        ContinueCase

      Case $g_idEditDummy
        _GUICtrlHeader_SetItemFormat( $g_hHeader, $iSortCol, $HDF_STRING + $g_aColAligns[$iSortCol] )
        $g_sSearch = GUICtrlRead( $idEdit )
        $g_tIndex = $g_tDefaultIndex
        If $g_sSearch = "" Then
          ; Empty search string, display all rows
          $g_aSubArray = $g_aArray
          $g_iSearch = $g_iRows
        Else
          ; Find rows matching the search string
          $g_iSearch = 0
          If $sSearchHow = "RegEx search" Then
            For $i = 0 To $g_iRows - 1 ; Duplicated For... Next for speed
              If StringRegExp( $g_aArray[$i][$g_iSearchCol], "(?i)" & $g_sSearch ) Then
                For $j = 0 To $g_iCols - 1
                  $g_aSubArray[$g_iSearch][$j] = $g_aArray[$i][$j]
                Next
                $g_iSearch += 1
              EndIf
            Next
          Else ; "Normal search"
            For $i = 0 To $g_iRows - 1 ; Duplicated For... Next for speed
              If StringInStr( $g_aArray[$i][$g_iSearchCol], $g_sSearch ) Then
                For $j = 0 To $g_iCols - 1
                  $g_aSubArray[$g_iSearch][$j] = $g_aArray[$i][$j]
                Next
                $g_iSearch += 1
              EndIf
            Next
          EndIf
          ; Delete eventual temporary subindexes
          For $i = 0 To $g_iCols - 1
            If VarGetType( $g_aIndexTemp[$i] ) = "DLLStruct" Then $g_aIndexTemp[$i] = ""
          Next
        EndIf
        GUICtrlSendMsg( $g_idListView, $LVM_SETITEMCOUNT, $g_iSearch, 0 )
        GUICtrlSendMsg( $g_idListView, $LVM_SETSELECTEDCOLUMN, $g_iSearchCol, 0 )
        GUICtrlSetData( $idResult, " " & $g_iSearch & ( $g_sSearch = "" ? " rows ( no pattern )" : " matching rows" ) )

      Case $g_idListView ; Sort
        $iSortCol = GUICtrlGetState( $g_idListView ) + 0
        If $iSortCol <> $iSortColPrev Then _GUICtrlHeader_SetItemFormat( $g_hHeader, $iSortColPrev, $HDF_STRING + $g_aColAligns[$iSortColPrev] )
        ; Set $g_tIndex + eventual update of $g_aIndexTemp[$iSortCol] OR $g_aIndex[$iSortCol]
        $i = GUICtrlRead( $idEdit ) ? UpdateIndex( $g_aIndexTemp, $iSortCol ) : UpdateIndex( $g_aIndex, $iSortCol )
        $g_iSortDir = ( ( $iSortCol = $iSortColPrev ) ? ( $g_iSortDir = $HDF_SORTUP ? $HDF_SORTDOWN : $HDF_SORTUP ) : ( $HDF_SORTUP ) )
        _GUICtrlHeader_SetItemFormat( $g_hHeader, $iSortCol, $HDF_STRING + $g_aColAligns[$iSortCol] + $g_iSortDir )
        GUICtrlSendMsg( $g_idListView, $LVM_SETSELECTEDCOLUMN, $iSortCol, 0 )
        GUICtrlSendMsg( $g_idListView, $LVM_SETITEMCOUNT, $g_iSearch, 0 )
        $iSortColPrev = $iSortCol

      Case $GUI_EVENT_RESTORE ; Needed, or Marker goes back in 0, 0 after Restore ( why ? )
        MoveMarker( $g_iSearchCol )

      Case $GUI_EVENT_CLOSE
        ExitLoop
    EndSwitch
  WEnd

  ; Cleanup
  GUIDelete( $g_hGui )
EndFunc

;========================================================================
Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam )
  Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )

  Switch HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) )
    Case $g_hListView
      Switch DllStructGetData( $tNMHDR, "Code" )
        Case $NM_CUSTOMDRAW
          Local $tCustDraw = DllStructCreate( $tagNMLVCUSTOMDRAW, $lParam )
          Switch DllStructGetData( $tCustDraw, "dwDrawStage" ) ; Holds a value that specifies the drawing stage
            Case $CDDS_PREPAINT                      ; Before the paint cycle begins
              Return $CDRF_NOTIFYITEMDRAW            ; Notify the parent window of any item-related drawing operations
            Case $CDDS_ITEMPREPAINT                  ; Before painting an item
              Return $CDRF_NOTIFYSUBITEMDRAW         ; Notify the parent window of any subitem-related drawing operations
            Case $CDDS_ITEMPREPAINT + $CDDS_SUBITEM  ; Before painting a subitem
              If ( DllStructGetData( $tCustDraw, "iSubItem" ) = $g_iSearchCol ) And $g_sSearch Then Return $CDRF_NOTIFYPOSTPAINT ; If $g_sSearch then paint $g_iSearchCol in postpaint stage
              Return $CDRF_NEWFONT                   ; $CDRF_NEWFONT must be returned after changing font or colors              ; Paint all other columns in prepaint stage with default code
            Case $CDDS_ITEMPOSTPAINT + $CDDS_SUBITEM ; After painting a subitem                                                  ; Paints $g_iSearchCol ( $g_iSearchCol only ) if $g_sSearch
              Local Static $tRect = DllStructCreate( $tagRECT ), $pRect = DllStructGetPtr( $tRect ), $tSize = DllStructCreate( $tagSIZE )
              Local Static $hBrushYellow = _WinAPI_CreateSolidBrush( 0x00FFFF ), $hBrushCyan = _WinAPI_CreateSolidBrush( 0xFFFF00 ) ; Yellow and cyan, BGR
              Local $iItem = DllStructGetData( $tCustDraw, "dwItemSpec" ), $hDC = DllStructGetData( $tCustDraw, "hDC" )

              ; Subitem text and matching text
              Local $sSubItemText = $g_iSortDir = $HDF_SORTUP ? $g_aSubArray[$g_tIndex.arr($iItem+1)][$g_iSearchCol] _
                                                              : $g_aSubArray[$g_tIndex.arr($g_iSearch-$iItem)][$g_iSearchCol]
              Local $sMatch = StringRegExp( $sSubItemText, "(?i)" & $g_sSearch, 1 ), $extended = @extended, $iLen = StringLen( $sMatch[0] )

              ; Entire subitem rectangle
              DllStructSetData( $tRect, 2, $g_iSearchCol ) ; Top
              DllStructSetData( $tRect, 1, $LVIR_BOUNDS )  ; Left
              GUICtrlSendMsg( $g_idListView, $LVM_GETSUBITEMRECT, $iItem, $pRect )
              DllStructSetData( $tRect, 1, DllStructGetData( $tRect, 1 ) + 6 ) ; Left margin
              If Not $g_iSearchCol Then DllStructSetData( $tRect, 3, DllStructGetData( $tRect, 1 ) + GUICtrlSendMsg( $g_idListView, $LVM_GETCOLUMNWIDTH, 0, 0 ) )
              ; If $g_iSearchCol = 0 (first column), the rectangle is calculated for the entire listview item.
              ; Compensate for this by setting the width of the rectangle to the width of the first column.

              ; Subitem rectangle for right and center aligned columns
              If $g_aColAligns[$g_iSearchCol] = $HDF_RIGHT Or $g_aColAligns[$g_iSearchCol] = $HDF_CENTER Then
                DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sSubItemText, "int", StringLen( $sSubItemText ), "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32
                Switch $g_aColAligns[$g_iSearchCol]
                  Case $HDF_RIGHT
                    DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Right" ) - DllStructGetData( $tSize, "X" ) - 6 )
                  Case $HDF_CENTER
                    DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + ( DllStructGetData( $tRect, "Right" ) - DllStructGetData( $tRect, "Left" ) - DllStructGetData( $tSize, "X" ) ) / 2 - 3 )
                EndSwitch
              EndIf

              ; Set transparent background for the subitem
              DllCall( "gdi32.dll", "int", "SetBkMode", "handle", $hDC, "int", $TRANSPARENT ) ; _WinAPI_SetBkMode

              ; Draw entire subitem text
              DllStructSetData( $tRect, 2, DllStructGetData( $tRect, 2 ) + 2 ) ; Top  margin
              DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sSubItemText, "int", StringLen( $sSubItemText ), "struct*", $tRect, "uint", 0 ) ; _WinAPI_DrawText
              DllStructSetData( $tRect, 2, DllStructGetData( $tRect, 2 ) - 2 ) ; Top  margin

              ; Rectangle for matching substring
              DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sSubItemText, "int", $extended - $iLen - 1, "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32
              DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + DllStructGetData( $tSize, "X" ) )
              DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32
              DllStructSetData( $tRect, "Right", DllStructGetData( $tRect, "Left" ) + DllStructGetData( $tSize, "X" ) )

              ; Fill matching rectangle with yellow or cyan background color
              DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", GUICtrlSendMsg( $g_idListView, $LVM_GETITEMSTATE, $iItem, $LVIS_SELECTED ) ? $hBrushYellow : $hBrushCyan ) ; _WinAPI_FillRect

              ; Redraw matching text on the yellow or cyan background
              DllStructSetData( $tRect, 2, DllStructGetData( $tRect, 2 ) + 2 ) ; Top  margin
              DllCall( "gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", 0x000000 ) ; _WinAPI_SetTextColor
              DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tRect, "uint", 0 ) ; _WinAPI_DrawText
              Return $CDRF_NEWFONT                   ; $CDRF_NEWFONT must be returned after changing font or colors
          EndSwitch

        Case $LVN_GETDISPINFOW
          Local $tNMLVDISPINFO = DllStructCreate( $tagNMLVDISPINFO, $lParam )
          If ( $tNMLVDISPINFO.SubItem = $g_iSearchCol ) And $g_sSearch Then Return ; Don't paint $g_iSearchCol if $g_sSearch
          Local Static $tText = DllStructCreate( "wchar[50]" ), $pText = DllStructGetPtr( $tText )
          Local $s = $g_iSortDir = 0x0400 ? DllStructSetData( $tText, 1, $g_aSubArray[$g_tIndex.arr($tNMLVDISPINFO.Item+1)][$tNMLVDISPINFO.SubItem] ) _ ; 0x0400 = $HDF_SORTUP
                                          : DllStructSetData( $tText, 1, $g_aSubArray[$g_tIndex.arr($g_iSearch-$tNMLVDISPINFO.Item)][$tNMLVDISPINFO.SubItem] )
          DllStructSetData( $tNMLVDISPINFO, "Text", $pText )
      EndSwitch

    Case $g_hHeader
      Switch DllStructGetData( $tNMHDR, "Code" )
        Case $NM_RCLICK
          Local $aHit = _GUICtrlListView_SubItemHitTest( $g_hListView )
          If $aHit[1] > - 1 Then ; Valid column
            GUICtrlSetData( $g_idComboCol, $aHit[1] )
            GUICtrlSendToDummy( $g_idComboColDummy )
          EndIf
      EndSwitch
  EndSwitch

  Return $GUI_RUNDEFMSG
  #forceref $hWnd, $iMsg, $wParam, $s
EndFunc

Func WM_COMMAND( $hWnd, $iMsg, $wParam, $lParam )
  Switch $lParam ; $hWndFrom
    Case $g_hEdit
      Switch BitShift( $wParam, 16 ) ; $iCode = High word
        Case $EN_CHANGE
          GUICtrlSendToDummy( $g_idEditDummy )
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
  #forceref $hWnd, $iMsg
EndFunc

Func Generate_All( ByRef $g_aArray )
  $g_aArray = FAS_Random2DArrayAu3( $g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz" )
  For $i = 0 To $g_iRows - 1
    $g_tIndex.arr( $i + 1 ) = $i
  Next
EndFunc

Func SortArrayStruct( $aArray, $iCol, $iRows )
  Local Static $hDll = DllOpen( "kernel32.dll" ), $hDllComp = DllOpen( "shlwapi.dll" )
  Local $tIndex = DllStructCreate( "uint arr[" & $iRows & "]" ), $pIndex = DllStructGetPtr( $tIndex )

  ; Sorting by one column
  Local $lo, $hi, $mi
  For $i = 1 To $iRows - 1
    $lo = 0
    $hi = $i - 1
    Do
      $mi = Int( ( $lo + $hi ) / 2 )
      Switch DllCall( $hDllComp, 'int', 'StrCmpLogicalW', 'wstr', $aArray[$i][$iCol], 'wstr', $aArray[DllStructGetData($tIndex,1,$mi+1)][$iCol] )[0]
        Case -1
          $hi = $mi - 1
        Case 1
          $lo = $mi + 1
        Case 0
          ExitLoop
      EndSwitch
    Until $lo > $hi
    DllCall( $hDll, "none", "RtlMoveMemory", "struct*", $pIndex + ( $mi + 1 ) * 4, "struct*", $pIndex + $mi * 4, "ulong_ptr", ( $i - $mi ) * 4 )
    DllStructSetData( $tIndex, 1, $i, $mi + 1 + ( $lo = $mi + 1 ) )
  Next

  Return $tIndex
EndFunc

Func UpdateIndex( ByRef $aIndex, $iCol )
  If VarGetType( $aIndex[$iCol] ) = "DLLStruct" Then
    $g_tIndex = $aIndex[$iCol]
  Else
    $g_tIndex = SortArrayStruct( $g_aSubArray, $iCol, $g_iSearch )
    $aIndex[$iCol] = $g_tIndex
  EndIf
EndFunc

Func MoveMarker( $iCol )
  Local $aRect = _GUICtrlHeader_GetItemRect( $g_hHeader, $iCol )
  ControlMove( $g_hGui, "", $g_idMarker, 10 + $aRect[0], 40 - 3, $aRect[2] - $aRect[0] + 1, 3 )
EndFunc

The first column is centered. Other columns are right aligned. Note that alignment is set with the function _GUICtrlHeader_SetItemFormat().

ColAlign.7z

Virtual listview

Edited by LarsJ
Link to comment
Share on other sites

Gonna look at LarsJ's last post very soon, it's sure very interesting.

@Lion66 I'll post a "light" script in a new thread (in 10 min) to show how to use the Context Menu in a Listview without interfering at all with its headers. It's just courtesy towards LarsJ as it's not related to Virtual LV any more. See you there.

@Norm73 It should be doable, but there are 2 ways to think of it :
1) A row should be visible as soon as at least 1 column got a match in it (which means some rows will be hidden)

2) All rows should appear constantly, even the rows having no match in them. Of course the matches will be colored (personnaly I'd prefer option 1)

Thank you LarsJ for your patience & explanations :)

Edit: nice code in your Column alignment script, so easy when you think of it... first the entire subitem rectangle, then the smaller text rectangle (never heard of GetTextExtentPoint32W before) then the smaller matching rectangle, just like a set of matryoshkas... and Left became Right :D

Case $HDF_RIGHT
    DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Right" ) - DllStructGetData( $tSize, "X" ) - 6 )

 

Edited by pixelsearch
Link to comment
Share on other sites

6 hours ago, pixelsearch said:

1) A row should be visible as soon as at least 1 column got a match in it (which means some rows will be hidden)

I realized the search like this:

Spoiler
Else ; "Normal search"
            Local $nM
            For $i = 0 To $g_iRows - 1 ; duplicated For... Next for speed
              $nM = 0
              For $j = 0 To $g_iCols - 1
                If StringInStr($g_aArray[$i][$j], $g_sSearch) Then
                  $nM = 1
                  ExitLoop
                EndIf
              Next
              If $nM = 1 Then
                  For $j = 0 To $g_iCols - 1
                    $g_aSubArray[$g_iSearch][$j] = $g_aArray[$i][$j]
                  Next
                  $g_iSearch += 1
              EndIf
            Next
          EndIf

 

I can't figure out how to mark the found rows in all columns at the same time.

At this point my autoit knowledge is unfortunately at an end. :mad2:

Link to comment
Share on other sites

On 5/22/2021 at 12:07 PM, Norm73 said:

For example, if I wrote the number 7 so that this number would appear in all columns.

Here we go, version "2m"

1466493031_searchallcolumns.png.4a7579ab2a03257632c8b6ee45478208.png

#include <ComboConstants.au3>
#include <EditConstants.au3>
#include <GuiListView.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>

#include "RandomArray.au3" ; LarsJ
#include "DrawItem.au3"    ;  "

Opt("MustDeclareVars", 1)

Global $g_iRows = 1000, $g_iCols = 6, $g_iLeftLV = 10, $g_iTopLV = 40, $g_hGui, $g_hListView, $g_hHeader, $g_hEdit
Global $g_aCols = ["Strings", "Integers", "Floats", "Dates", "Times", "R/C"], $g_aWidths = [230, 61, 124, 70, 60, 60]
Global $g_idListView, $g_idMarker, $g_idComboCol, $g_idComboColDummy, $g_idEditDummy
Global $g_sSearch, $g_iSearchCol, $g_iSortDir, $g_iSearch = $g_iRows
Global $g_aArray, $g_aSubArray, $g_tDefaultIndex, $g_tIndex = DllStructCreate("uint arr[" & $g_iRows & "]")
Global $g_aIndex[$g_iCols], $g_aIndexTemp[$g_iCols] ; VarGetType's : $g_aIndex => "Array", $g_aIndex[0] => "String"
Global $fListViewHasFocus = 1 ; trying this for now, we'll see...

Example()

Func Example()
  ; Generate array & one index
  _Generate_All($g_aArray)
  $g_aSubArray = $g_aArray
  $g_tDefaultIndex = $g_tIndex

  ; Create GUI
  $g_hGui = GUICreate("Virtual ListView + match all columns (2m)", 630 + 20, 788 + 30 + 20)

  ; Create Edit control (search) + dummy control
  Local $idEdit = GUICtrlCreateEdit("", 120, 10, 305, 20, BitXOR($GUI_SS_DEFAULT_EDIT, $WS_HSCROLL, $WS_VSCROLL))
  $g_hEdit = GUICtrlGetHandle($idEdit)
  $g_idEditDummy = GUICtrlCreateDummy()

  ; Create ComboBox control (how to search : RegEx or Normal ?)
  Local $idSearchHow = GUICtrlCreateCombo("RegEx search", 11, 9, 100, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST))
  Local $sSearchHow = "RegEx search", $sSearchHowPrev = $sSearchHow ; default way of searching (changeable)
  GUICtrlSetData($idSearchHow, "Normal search", $sSearchHow)

  ; Create ComboBox control (column where to search) + dummy control
  GUICtrlCreateLabel("Col", 429, 10, 20, 20, BitOR($SS_CENTERIMAGE, $SS_CENTER))
  $g_idComboCol = GUICtrlCreateCombo("0", 452, 9, 41, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST))
  $g_iSearchCol = 0 ; default column where to search (changeable)
  Local $iSearchColPrev = $g_iSearchCol
  For $i = 1 To $g_iCols - 1
    GUICtrlSetData($g_idComboCol, $i & "|", $g_iSearchCol)
  Next
  $g_idComboColDummy = GUICtrlCreateDummy()

  ; Create Label control (number of matching results)
  Local $idResult = GUICtrlCreateLabel(" " & $g_iRows & " rows (no pattern)", 501, 10, 135, 20, BitOR($SS_CENTERIMAGE, $SS_SUNKEN))

  ; Create ListView
  $g_idListView = GUICtrlCreateListView("", $g_iLeftLV, $g_iTopLV, 630, 788, BitOr($LVS_OWNERDATA, $LVS_OWNERDRAWFIXED), $WS_EX_CLIENTEDGE)
  _GUICtrlListView_SetExtendedListViewStyle($g_idListView, BitOr($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT))
  $g_hListView = GUICtrlGetHandle($g_idListView)
  $g_hHeader = _GUICtrlListView_GetHeader($g_idListView)
  For $i = 0 To $g_iCols - 1
    _GUICtrlListView_AddColumn($g_idListView, $g_aCols[$i], $g_aWidths[$i])
  Next

  ; Create Marker (an orange line placed above the header of the column being searched)
  $g_idMarker = GUICtrlCreateLabel("", 0, 0)
  GUICtrlSetBkColor(-1, 0xFFA060) ; orange
  _MoveMarker($g_iSearchCol)
  _GUICtrlListView_SetSelectedColumn($g_idListView, $g_iSearchCol)

  ; Sorting information
  $g_iSortDir = 0x0400 ; $HDF_SORTUP
  Local $iSortCol = -1, $iSortColPrev = -1

  GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM")
  GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY") ; for LV header only
  GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")

  GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iRows, 0)

  GUISetState(@SW_SHOW)

  While 1
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        ExitLoop

      Case $g_idComboCol, $g_idComboColDummy, $idSearchHow
        $g_iSearchCol = GUICtrlRead($g_idComboCol)
        $sSearchHow = GUICtrlRead($idSearchHow)
        Select
          Case $g_iSearchCol <> $iSearchColPrev
            _MoveMarker($g_iSearchCol) ; Search column will be selected below, after ContinueCase
            $iSearchColPrev = $g_iSearchCol
          Case $sSearchHow <> $sSearchHowPrev
            $sSearchHowPrev = $sSearchHow
          Case Else ; no change in both Combo controls (same search column, same search way)
            ContinueLoop
        EndSelect
        ContinueCase

      Case $g_idEditDummy
        _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortCol, $HDF_STRING)
        $g_sSearch = GUICtrlRead($idEdit)
        $g_tIndex = $g_tDefaultIndex
        If $g_sSearch = "" Then
          ; Empty search string, display all rows
          $g_aSubArray = $g_aArray
          $g_iSearch = $g_iRows
        Else
          ; Find rows matching the search string
          $g_iSearch = 0
          If $sSearchHow = "Normal search" Then ; all searches use RegEx => escape 12 + 1 metacharacters
            $g_sSearch = StringRegExpReplace($g_sSearch, "(\\|\.|\^|\$|\||\[|\(|\{|\*|\+|\?|\#|\))" , "\\$1")
          EndIf
          ; ConsoleWrite("$sSearchHow = " & $sSearchHow & @TAB & "$g_sSearch = " & $g_sSearch & @lf)
          For $i = 0 To $g_iRows - 1
            For $j = 0 To $g_iCols - 1
              If StringRegExp($g_aArray[$i][$j], "(?i)" & $g_sSearch) Then
                For $k = 0 To $g_iCols - 1
                  $g_aSubArray[$g_iSearch][$k] = $g_aArray[$i][$k]
                Next
                $g_iSearch += 1
                ContinueLoop 2
              EndIf
            Next
          Next
          ; Delete eventual temporary subindexes
          For $i = 0 To $g_iCols - 1
            If VarGetType($g_aIndexTemp[$i]) = "DLLStruct" Then $g_aIndexTemp[$i] = "" ; "String"
          Next
        EndIf
        GUICtrlSetData($idResult, " " & $g_iSearch & ($g_sSearch = "" ? " rows (no pattern)" : " matching rows"))
        GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iSearch, 0)
        _GUICtrlListView_SetSelectedColumn($g_hListView, $g_iSearchCol) ; seems ok here (after $LVM_SETITEMCOUNT)

      Case $g_idListView ; Sort
        $iSortCol = GUICtrlGetState($g_idListView)
        If $iSortCol <> $iSortColPrev Then
          _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortColPrev, $HDF_STRING)
        EndIf
        ; Set $g_tIndex + eventual update of $g_aIndexTemp[$iSortCol] OR $g_aIndex[$iSortCol]
        If GUICtrlRead($idEdit) Then
          _UpdateIndex($g_aIndexTemp, $iSortCol)
        Else
          _UpdateIndex($g_aIndex, $iSortCol)
        EndIf
        $g_iSortDir = (($iSortCol = $iSortColPrev) ? ($g_iSortDir = $HDF_SORTUP ? $HDF_SORTDOWN : $HDF_SORTUP) : ($HDF_SORTUP))
        _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortCol, $HDF_STRING + $g_iSortDir)
        GUICtrlSendMsg($g_idListView, $LVM_SETSELECTEDCOLUMN, $iSortCol, 0)
        GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iSearch, 0)
        $iSortColPrev = $iSortCol

      Case $GUI_EVENT_RESTORE ; needed, or Marker goes back in 0, 0 after Restore (why ?)
        _MoveMarker($g_iSearchCol)
    EndSwitch
  WEnd

  ; Cleanup
  GUIDelete($g_hGui)
EndFunc   ;==>Example

;========================================================================
Func WM_DRAWITEM( $hWnd, $iMsg, $wParam, $lParam )
  ; Display items in an owner drawn ListView

  Local Static $tRect = DllStructCreate( $tagRECT ), $pRect = DllStructGetPtr( $tRect ), $tSize = DllStructCreate( $tagSIZE )
  Local Static $hBrushYellow = _WinAPI_CreateSolidBrush( 0xFFFF00 ), $hBrushCyan = _WinAPI_CreateSolidBrush( 0x00FFFF ) ; Yellow and cyan, BGR
  Local Static $hBrushHighLight = _WinAPI_GetSysColorBrush( $COLOR_HIGHLIGHT ), $hBrushButtonFace = _WinAPI_GetSysColorBrush( $COLOR_BTNFACE )

  ; We can optimize code by removing Switch statements because the ListView is the only ownerdrawn control and only $ODA_DRAWENTIRE actions are present

  Local $tDrawItem = DllStructCreate( $tagDRAWITEM, $lParam ), $itemID = DllStructGetData( $tDrawItem, "itemID" ), $iState = DllStructGetData( $tDrawItem, "itemState" ), $hDC = DllStructGetData( $tDrawItem, "hDC" ), $sItemText

  ; Loop through columns ($i is the column index)
  For $i = 0 To $g_iCols - 1
    ; Subitem rectangle
    DllStructSetData( $tRect, 2, $i ) ; Top
    DllStructSetData( $tRect, 1, $LVIR_BOUNDS ) ; Left
    GUICtrlSendMsg( $g_idListView, $LVM_GETSUBITEMRECT, $itemID, $pRect )
    DllStructSetData( $tRect, 1, DllStructGetData( $tRect, 1 ) + 6 ) ; Left margin
    DllStructSetData( $tRect, 2, DllStructGetData( $tRect, 2 ) + 2 ) ; Top  margin

    ; Subitem background and text color
    If BitAND( $iState, $ODS_SELECTED ) Then DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $fListViewHasFocus = 1 ? $hBrushHighLight : $hBrushButtonFace ) ; _WinAPI_FillRect
    DllCall( "gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", BitAND( $iState, $ODS_SELECTED ) ? $fListViewHasFocus = 1 ? 0xFFFFFF : 0x000000 : 0x000000 ) ; _WinAPI_SetTextColor

    ; Draw subitem text
    If $g_iSortDir = 0x0400 Then ; 0x0400 = $HDF_SORTUP
      $sItemText = $g_aSubArray[$g_tIndex.arr($itemID + 1)][$i]
    Else
      $sItemText = $g_aSubArray[$g_tIndex.arr($g_iSearch - $itemID)][$i]
    EndIf
    DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sItemText, "int", StringLen( $sItemText ), "struct*", $tRect, "uint", 0 ) ; _WinAPI_DrawText

    ; Mark matching substring if found in ANY column
    If $g_sSearch Then
      Local $sMatch = StringRegExp( $sItemText, "(?i)" & $g_sSearch, 1 )
      If Not @error Then ; match found
        Local $extended = @extended, $iLen = StringLen( $sMatch[0] )

        ; Rectangle for matching substring
        DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sItemText, "int", $extended - $iLen - 1, "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32
        DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + DllStructGetData( $tSize, "X" ) )
        DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32
        DllStructSetData( $tRect, "Right", DllStructGetData( $tRect, "Left" ) + DllStructGetData( $tSize, "X" ) )

        ; Fill rectangle with yellow or cyan (selected) background color
        DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", BitAND( $iState, $ODS_SELECTED ) ? $hBrushCyan : $hBrushYellow ) ; _WinAPI_FillRect

        ; Draw matching substring in rectangle
        DllCall( "gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", 0x000000 ) ; _WinAPI_SetTextColor
        DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tRect, "uint", 0 ) ; _WinAPI_DrawText
      EndIf
    EndIf
  Next

  Return $GUI_RUNDEFMSG
  #forceref $hWnd, $iMsg, $wParam
EndFunc

;========================================================================
Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)

  Local $tNMHEADER = DllStructCreate($tagNMHEADER, $lParam)
  Switch HWnd(DllStructGetData($tNMHEADER, "hWndFrom"))
    Case $g_hHeader
      Local $iCode = DllStructGetData($tNMHEADER, "Code")
      Switch $iCode
        Case $HDN_ENDTRACKW
          _MoveMarker(GUICtrlRead($g_idComboCol))

        Case $HDN_DIVIDERDBLCLICKW
          Local $iCol = DllStructGetData($tNMHEADER, "Item")
          _GUICtrlListView_SetColumnWidth($g_idListView, $iCol, $g_aWidths[$iCol]) ; initial size
          _MoveMarker(GUICtrlRead($g_idComboCol))

        Case $NM_RCLICK
          Local $aHit = _GUICtrlListView_SubItemHitTest($g_hListView)
          ; $aHit[1] : 0-based index of the LV subitem... i.e. the column in our case (may be -1 if right click on empty part of header)
          If $aHit[1] > - 1 Then ; valid column
            GUICtrlSetData($g_idComboCol, $aHit[1])
            GUICtrlSendToDummy($g_idComboColDummy)
          EndIf
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

;========================================================================
Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)

  Local $hWndFrom = $lParam
  Local $iCode = BitShift($wParam, 16) ; High word
  Switch $hWndFrom
    Case $g_hEdit
      Switch $iCode
        Case $EN_CHANGE
          GUICtrlSendToDummy($g_idEditDummy)
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_COMMAND

;========================================================================
Func _Generate_All(ByRef $g_aArray)

  ConsoleWrite("$g_iRows = " & $g_iRows & "    $g_iCols = " & $g_iCols & @CRLF)
  Local $hTimer = TimerInit()
  $g_aArray = FAS_Random2DArrayAu3($g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz")
  ; $g_aArray = FAS_Random2DArrayAu3($g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz" & "\.^$|[({*+?#)") ; 12 + 1 RegEx metacharacters
  For $i = 0 To $g_iRows - 1
    $g_tIndex.arr($i + 1) = $i
  Next
  ConsoleWrite("Generating array & one index = " & TimerDiff($hTimer) & @CRLF & @CRLF)
EndFunc   ;==>_Generate_All

;========================================================================
Func _SortArrayStruct(Const ByRef $aArray, $iCol, $iRows)
  Local $tIndex = DllStructCreate("uint arr[" & $iRows & "]")
  Local $pIndex = DllStructGetPtr($tIndex)
  Local Static $hDll = DllOpen("kernel32.dll")
  Local Static $hDllComp = DllOpen("shlwapi.dll")

  Local $lo, $hi, $mi, $r

  ; Sorting by one column
  For $i = 1 To $iRows - 1
    $lo = 0
    $hi = $i - 1
    Do
      $mi = Int(($lo + $hi) / 2)
      $r = DllCall($hDllComp, 'int', 'StrCmpLogicalW', 'wstr', $aArray[$i][$iCol], 'wstr', $aArray[DllStructGetData($tIndex, 1, $mi + 1)][$iCol])[0]
      Switch $r
        Case -1
          $hi = $mi - 1
        Case 1
          $lo = $mi + 1
        Case 0
          ExitLoop
      EndSwitch
    Until $lo > $hi
    DllCall($hDll, "none", "RtlMoveMemory", "struct*", $pIndex + ($mi + 1) * 4, "struct*", $pIndex + $mi * 4, "ulong_ptr", ($i - $mi) * 4)
    DllStructSetData($tIndex, 1, $i, $mi + 1 + ($lo = $mi + 1))
  Next

  Return $tIndex
EndFunc   ;==>_SortArrayStruct

;========================================================================
Func _UpdateIndex(ByRef $aIndex, $iCol)

  If VarGetType($aIndex[$iCol]) = "DLLStruct" Then
    $g_tIndex = $aIndex[$iCol]
  Else
    $g_tIndex = _SortArrayStruct($g_aSubArray, $iCol, $g_iSearch)
    $aIndex[$iCol] = $g_tIndex ; "DLLStruct" (or "Int32" when no match found +++)
  EndIf
EndFunc   ;==>_UpdateIndex

;========================================================================
Func _MoveMarker($iCol)

  Local $aRect = _GUICtrlHeader_GetItemRect($g_hHeader, $iCol)
  ControlMove($g_hGui, "", $g_idMarker, $g_iLeftLV + $aRect[0], $g_iTopLV - 3, $aRect[2] - $aRect[0] + 1, 3)
EndFunc   ;==>_MoveMarker

Many controls become irrelevant (combo to switch columns, orange marker, right click on headers etc...) because the number of matched rows is now the same, no matter the searched column. Anyway I'm keeping all controls untouched, better have more than less :)

More important : something has to be improved in all these scripts (even LarsJ got the same problem, just tested). I'll prepare a new post to indicate the issue.

 

Edit: update May 24th 2021 : fixed 'Normal Search' which includes RegEx metacharacters, for example a "." search in Normal search now returns correct matches (pic below)

1892934699_fixedmatchall.png.895115fd2447f7027c6810cc56a94381.png

Edited by pixelsearch
Script updated on May 24th, 2021
Link to comment
Share on other sites

@LarsJ I notice an issue in all these scripts, here is a way to reproduce it in one of yours (post titled "Virtual listview" on page before) :

* Please right click on "Floats" header (or use Combo) . "Floats" will become the "search column"
* Search Field, type  .
* All 1st characters of the Float column will have a cyan background (because of  .  in RegEx search) and this is correct.
* Now change the Search method to "Normal Search" and a wrong result appears :

534091781_wrongdotresult.png.8761405269ec831e69a2550ed9e70606.png

Dots in "Floats" column should have a cyan background instead of the 1st character.
This is because StringRegExp is used in all cases (in the handler function) even when user wants a Normal search. Then all RegEx metacharacters behave badly during a Normal search (if not escaped in StringRegExp)

\ . ^ $ | [ ( { * + ? #

Escaping them seems challenging to script, in case the user wants a Normal search and these characters are found in the Search Field.

Another simpler way could be, during the message treatment ($NM_CUSTOMDRAW or $WM_DRAWITEM) to use the same test that was used for the search (i.e. StringRegExp Or StringInStr), though StringInStr could be slower in this case (?)

This RegEx line will be easy to adapt to StringInStr

Local $extended = @extended, $iLen = StringLen( $sMatch[0] )

I faced this issue while testing the script adapted to all columns :

970455375_wrongmatchall.png.5e1f7142fb3e5b206c17dba0eb73f5ce.png

Link to comment
Share on other sites

Turn $sSearchHow into a global variable. And replace these lines:

; Subitem text and matching text
Local $sSubItemText = $g_iSortDir = 0x0400 ? $g_aSubArray[$g_tIndex.arr($iItem + 1)][$g_iSearchCol] _ ; 0x0400 = $HDF_SORTUP
                                           : $g_aSubArray[$g_tIndex.arr($g_iSearch - $iItem)][$g_iSearchCol]
Local $sMatch = StringRegExp( $sSubItemText, "(?i)" & $g_sSearch, 1 ), $extended = @extended, $iLen = StringLen( $sMatch[0] )

with these lines:

; Subitem text and matching text
Local $sSubItemText = $g_iSortDir = 0x0400 ? $g_aSubArray[$g_tIndex.arr($iItem + 1)][$g_iSearchCol] _ ; 0x0400 = $HDF_SORTUP
                                           : $g_aSubArray[$g_tIndex.arr($g_iSearch - $iItem)][$g_iSearchCol], $sMatch, $extended, $iLen
If $g_sSearchHow = "RegEx search" Then
  Local $aMatch = StringRegExp( $sSubItemText, "(?i)" & $g_sSearch, 1 )
  $extended = @extended
  $sMatch = $aMatch[0]
Else
  $iLen = StringLen( $g_sSearch )
  Local $iPos = StringInStr( $sSubItemText, $g_sSearch )
  $sMatch = StringMid( $sSubItemText, $iPos, $iLen )
  $extended = $iPos + $iLen
EndIf
$iLen = StringLen( $sMatch )

 

Link to comment
Share on other sites

Thank you LarsJ and Pixelsearch
@Pixelsearch I used the correction from LarsJ and now everything works great. :cheer:

18 hours ago, pixelsearch said:

Anyway I'm keeping all controls untouched, better have more than less

You are right, you don't have to do without it. Make the search even more flexible.    All | 0 | 1 | 2 | 3 | 4 ....

I have one more request.
Your virtual ListView with the search can be used in many cases.
But there are many lists where the content may not be changed. For example, a log protocol.
How can you do this in such a way that the lines are not deleted, but the first one found is scrolled up?
Similar to _GUICtrlListView_EnsureVisible.
For example, if I sort your list in column 0 and then search in RegExp mode like this ^ u. In this case, the list should not get smaller, but should be scrolled up.

It won't be hard to realize in order for the lines to stay in place. I have no idea how to implement the analog of the _GUICtrlListView_EnsureVisible() function.

 

Edited by Norm73
Link to comment
Share on other sites

@Norm73 I'm glad the changes indicated by LarsJ fixed the issue.

As I like challenges, then I just scripted the fix in another way (no more StringInStr, all searches are done using RegEx, even when user chooses "Normal Search") and without touching $NM_CUSTOMDRAW or $WM_DRAWITEM code. Below is the code found in amended script "2m" which has just been updated accordingly. Other older scripts should be amended soon.

Old code :

; Find rows matching the search string
          $g_iSearch = 0
          If $sSearchHow = "RegEx search" Then
            For $i = 0 To $g_iRows - 1 ; duplicated For... Next for speed
              For $j = 0 To $g_iCols - 1
                If StringRegExp($g_aArray[$i][$j], "(?i)" & $g_sSearch) Then
                  For $k = 0 To $g_iCols - 1
                    $g_aSubArray[$g_iSearch][$k] = $g_aArray[$i][$k]
                  Next
                  $g_iSearch += 1
                  ContinueLoop 2
                EndIf
              Next
            Next
          Else ; "Normal search"
            For $i = 0 To $g_iRows - 1 ; duplicated For... Next for speed
              For $j = 0 To $g_iCols - 1
                If StringInStr($g_aArray[$i][$j], $g_sSearch) Then
                  For $k = 0 To $g_iCols - 1
                    $g_aSubArray[$g_iSearch][$k] = $g_aArray[$i][$k]
                  Next
                  $g_iSearch += 1
                  ContinueLoop 2
                EndIf
              Next
            Next
          EndIf

New code :

; Find rows matching the search string
          $g_iSearch = 0
          If $sSearchHow = "Normal search" Then ; all searches use RegEx => escape 12 + 1 metacharacters
            $g_sSearch = StringRegExpReplace($g_sSearch, "(\\|\.|\^|\$|\||\[|\(|\{|\*|\+|\?|\#|\))" , "\\$1")
          EndIf
          ; ConsoleWrite("$sSearchHow = " & $sSearchHow & @TAB & "$g_sSearch = " & $g_sSearch & @lf)
          For $i = 0 To $g_iRows - 1
            For $j = 0 To $g_iCols - 1
              If StringRegExp($g_aArray[$i][$j], "(?i)" & $g_sSearch) Then
                For $k = 0 To $g_iCols - 1
                  $g_aSubArray[$g_iSearch][$k] = $g_aArray[$i][$k]
                Next
                $g_iSearch += 1
                ContinueLoop 2
              EndIf
            Next
          Next

The fix is based, when user chooses Normal search, in escaping all (eventual) 12 metacharacters indicated in AutoIt help file (topic StringRegExp) which are :

\.^$|[({*+?#

But I notice a 13th metacharacter indicated on PCRE web site (Perl Compatible Regular Expressions) and AutoIt uses the PCRE engine. This 13th meta is ")" and it's really a metacharacter that you can check after uncommenting a ConsoleWrite line in "2m" and create the array using the alternate line (also found in "2m") :

; ConsoleWrite("$sSearchHow = " & $sSearchHow & @TAB & "$g_sSearch = " & $g_sSearch & @lf)
...

$g_aArray = FAS_Random2DArrayAu3($g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz")
; $g_aArray = FAS_Random2DArrayAu3($g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz" & "\.^$|[({*+?#)") ; 12 + 1 RegEx metacharacters
...

Now if you search for ")" , without quotes of course, then no matches are found with "RegEx Search", which indicates (at least for me) that we just faced a metacharacter, because if you switch to "Normal Search", then ")" will appear in the results. Here is what ConsoleWrite will show :

$sSearchHow = RegEx search    $g_sSearch = )
$sSearchHow = Normal search    $g_sSearch = \)

The "." search (that revealed the issue) now works normally in amended "2m"

1824871853_fixedmatchall.png.9f4de6ce7fbbd790c1ec6ff47ae651ef.png

ConsoleWrite :

$sSearchHow = RegEx search    $g_sSearch = .
$sSearchHow = Normal search    $g_sSearch = \.

Concerning your other questions, I'll see what's doable when I have some time.
Dinner time :)

Edited by pixelsearch
typo
Link to comment
Share on other sites

9 hours ago, Lion66 said:
20 hours ago, Norm73 said:

Make the search even more flexible.    All | 0 | 1 | 2 | 3 | 4 ....


I support the request 😃

Done in script "2p" below (while everything is still fresh !)
* Note how the orange marker is placed above all headers when user wants to search all columns.
* Right-click a header acts same as in preceding scripts (search in this column only + appropriate orange marker)

Though we read "All" in the ComboBox, the corresponding global variable $g_iSearchCol = -1 in this case (by design) . It's important to remember this point in case someone reworks the script :
- 1 means search in all columns, while 0, 1, 2... means search in one column only (col 0, 1, 2)

I wonder if all these scripts should be zipped together and placed I don't know where... but as they all concern  "Incremental search in owner and custom drawn ListViews" (LarsJ new title for this thread) well... maybe they are at their right place :)

351531487_match1orallcolumns.png.e1a472f995c6cb30cfae38b8b0c31fea.png

#include <ComboConstants.au3>
#include <EditConstants.au3>
#include <GuiListView.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include "RandomArray.au3" ; LarsJ
#include "DrawItem.au3"    ;  "

Opt("MustDeclareVars", 1)

Global $g_iRows = 1000, $g_iCols = 6, $g_hGui, $g_hListView, $g_hHeader, $g_hEdit
Global $g_aCols = ["Strings", "Integers", "Floats", "Dates", "Times", "R/C"], $g_aWidths = [230, 61, 124, 70, 60, 60]
Global $g_idListView, $g_idMarker, $g_idComboCol, $g_idComboColDummy, $g_idEditDummy
Global $g_sSearch, $g_iSearchCol, $g_iSortDir, $g_iSearch = $g_iRows
Global $g_aArray, $g_aSubArray, $g_tDefaultIndex, $g_tIndex = DllStructCreate("uint arr[" & $g_iRows & "]")
Global $g_aIndex[$g_iCols], $g_aIndexTemp[$g_iCols] ; VarGetType's : $g_aIndex => "Array", $g_aIndex[0] => "String"
Global $fListViewHasFocus = 1 ; trying this for now, we'll see...

Example()

Func Example()
  ; Generate array & one index
  _Generate_All($g_aArray)
  $g_aSubArray = $g_aArray
  $g_tDefaultIndex = $g_tIndex

  ; Create GUI
  $g_hGui = GUICreate("Virtual ListView + match 1 or All columns (2p)", 630 + 20, 788 + 30 + 20)

  ; Create Edit control (search) + dummy control
  Local $idEdit = GUICtrlCreateEdit("", 120, 10, 305, 20, BitXOR($GUI_SS_DEFAULT_EDIT, $WS_HSCROLL, $WS_VSCROLL))
  $g_hEdit = GUICtrlGetHandle($idEdit)
  $g_idEditDummy = GUICtrlCreateDummy()

  ; Create ComboBox control (how to search : RegEx or Normal ?)
  Local $idSearchHow = GUICtrlCreateCombo("RegEx search", 11, 9, 100, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST))
  Local $sSearchHow = "RegEx search", $sSearchHowPrev = $sSearchHow ; default way of searching (changeable)
  GUICtrlSetData($idSearchHow, "Normal search", $sSearchHow)

  ; Create ComboBox control (column where to search, possible All) + dummy control
  GUICtrlCreateLabel("Col", 429, 10, 20, 20, BitOR($SS_CENTERIMAGE, $SS_CENTER))
  $g_idComboCol = GUICtrlCreateCombo("All", 452, 9, 41, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST))
  Local $sSearchCol = "All" ; default column where to search (changeable) => becomes $g_iSearchCol
  $g_iSearchCol = ($sSearchCol == "All" ? - 1 : Number($sSearchCol)) ; - 1 means search All columns
  Local $iSearchColPrev = $g_iSearchCol
  For $i = 0 To $g_iCols - 1
    GUICtrlSetData($g_idComboCol, $i & "|", $g_iSearchCol)
  Next
  $g_idComboColDummy = GUICtrlCreateDummy()

  ; Create Label control (number of matching results)
  Local $idResult = GUICtrlCreateLabel(" " & $g_iRows & " rows (no pattern)", 501, 10, 135, 20, BitOR($SS_CENTERIMAGE, $SS_SUNKEN))

  ; Create ListView
  $g_idListView = GUICtrlCreateListView("", 10, 40, 630, 788, BitOr($LVS_OWNERDATA, $LVS_OWNERDRAWFIXED), $WS_EX_CLIENTEDGE)
  _GUICtrlListView_SetExtendedListViewStyle($g_idListView, BitOr($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT))
  $g_hListView = GUICtrlGetHandle($g_idListView)
  $g_hHeader = _GUICtrlListView_GetHeader($g_idListView)
  For $i = 0 To $g_iCols - 1
    _GUICtrlListView_AddColumn($g_idListView, $g_aCols[$i], $g_aWidths[$i])
  Next

  ; No ListView column resizing by dragging header dividers (LarsJ)
  ;_WinAPI_SetWindowLong( $hHeader, $GWL_STYLE, _WinAPI_GetWindowLong( $hHeader, $GWL_STYLE ) + $HDS_NOSIZING ) ; AutoIt 3.3.14.5 issue
  DllCall( "user32.dll", "long_ptr", @AutoItX64 ? "SetWindowLongPtrW" : "SetWindowLongW", "hwnd", $g_hHeader, "int", $GWL_STYLE, "long_ptr", _
  DllCall( "user32.dll", "long_ptr", @AutoItX64 ? "GetWindowLongPtrW" : "GetWindowLongW", "hwnd", $g_hHeader, "int", $GWL_STYLE )[0] + $HDS_NOSIZING )

  ; Create Marker (an orange line placed above the header of the column being searched)
  $g_idMarker = GUICtrlCreateLabel("", 0, 0)
  GUICtrlSetBkColor(-1, 0xFFA060) ; orange
  _MoveMarker($g_iSearchCol)
  _GUICtrlListView_SetSelectedColumn($g_idListView, $g_iSearchCol)

  ; Sorting information
  $g_iSortDir = 0x0400 ; $HDF_SORTUP
  Local $iSortCol = -1, $iSortColPrev = -1

  GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM")
  GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY") ; for LV header only
  GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")

  GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iRows, 0)

  GUISetState(@SW_SHOW)

  While 1
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        ExitLoop

      Case $g_idComboCol, $g_idComboColDummy, $idSearchHow
        $sSearchCol = GUICtrlRead($g_idComboCol)
        $g_iSearchCol = ($sSearchCol == "All" ? - 1 : Number($sSearchCol)) ; - 1 means search All columns
        $sSearchHow = GUICtrlRead($idSearchHow)
        Select
          Case $g_iSearchCol <> $iSearchColPrev
            _MoveMarker($g_iSearchCol) ; Search column will be selected below, after ContinueCase
            $iSearchColPrev = $g_iSearchCol
          Case $sSearchHow <> $sSearchHowPrev
            $sSearchHowPrev = $sSearchHow
          Case Else ; no change in both Combo controls (same search column, same search way)
            ContinueLoop
        EndSelect
        ContinueCase

      Case $g_idEditDummy
        _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortCol, $HDF_STRING)
        $g_sSearch = GUICtrlRead($idEdit)
        $g_tIndex = $g_tDefaultIndex
        If $g_sSearch = "" Then
          ; Empty search string, display all rows
          $g_aSubArray = $g_aArray
          $g_iSearch = $g_iRows
        Else
          ; Find rows matching the search string
          $g_iSearch = 0
          If $sSearchHow = "Normal search" Then ; all searches use RegEx => escape 12 + 1 metacharacters
            $g_sSearch = StringRegExpReplace($g_sSearch, "(\\|\.|\^|\$|\||\[|\(|\{|\*|\+|\?|\#|\))" , "\\$1")
          EndIf
          ; ConsoleWrite("$sSearchHow = " & $sSearchHow & @TAB & "$g_sSearch = " & $g_sSearch & @lf)
          If $g_iSearchCol = - 1 Then ; - 1 means search All columns
            For $i = 0 To $g_iRows - 1
              For $j = 0 To $g_iCols - 1
                If StringRegExp($g_aArray[$i][$j], "(?i)" & $g_sSearch) Then
                  For $k = 0 To $g_iCols - 1
                    $g_aSubArray[$g_iSearch][$k] = $g_aArray[$i][$k]
                  Next
                  $g_iSearch += 1
                  ContinueLoop 2
                EndIf
              Next
            Next
          Else ; Search only in 1 column
            For $i = 0 To $g_iRows - 1
              If StringRegExp($g_aArray[$i][$g_iSearchCol], "(?i)" & $g_sSearch) Then
                For $j = 0 To $g_iCols - 1
                  $g_aSubArray[$g_iSearch][$j] = $g_aArray[$i][$j]
                Next
                $g_iSearch += 1
              EndIf
            Next
          EndIf
          ; Delete eventual temporary subindexes
          For $i = 0 To $g_iCols - 1
            If VarGetType($g_aIndexTemp[$i]) = "DLLStruct" Then $g_aIndexTemp[$i] = "" ; "String"
          Next
        EndIf
        GUICtrlSetData($idResult, " " & $g_iSearch & ($g_sSearch = "" ? " rows (no pattern)" : " matching rows"))
        GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iSearch, 0)
        _GUICtrlListView_SetSelectedColumn($g_hListView, $g_iSearchCol) ; seems ok here (after $LVM_SETITEMCOUNT)

      Case $g_idListView ; Sort
        $iSortCol = GUICtrlGetState($g_idListView)
        If $iSortCol <> $iSortColPrev Then
          _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortColPrev, $HDF_STRING)
        EndIf
        ; Set $g_tIndex + eventual update of $g_aIndexTemp[$iSortCol] OR $g_aIndex[$iSortCol]
        If GUICtrlRead($idEdit) Then
          _UpdateIndex($g_aIndexTemp, $iSortCol)
        Else
          _UpdateIndex($g_aIndex, $iSortCol)
        EndIf
        $g_iSortDir = (($iSortCol = $iSortColPrev) ? ($g_iSortDir = $HDF_SORTUP ? $HDF_SORTDOWN : $HDF_SORTUP) : ($HDF_SORTUP))
        _GUICtrlHeader_SetItemFormat($g_hHeader, $iSortCol, $HDF_STRING + $g_iSortDir)
        GUICtrlSendMsg($g_idListView, $LVM_SETSELECTEDCOLUMN, $iSortCol, 0)
        GUICtrlSendMsg($g_idListView, $LVM_SETITEMCOUNT, $g_iSearch, 0)
        $iSortColPrev = $iSortCol

      Case $GUI_EVENT_RESTORE ; needed, or Marker goes back in 0, 0 after Restore (why ?)
        _MoveMarker($g_iSearchCol)
    EndSwitch
  WEnd

  ; Cleanup
  GUIDelete($g_hGui)
EndFunc   ;==>Example

;========================================================================
Func WM_DRAWITEM( $hWnd, $iMsg, $wParam, $lParam )
  ; Display items in an owner drawn ListView

  Local Static $tRect = DllStructCreate( $tagRECT ), $pRect = DllStructGetPtr( $tRect ), $tSize = DllStructCreate( $tagSIZE )
  Local Static $hBrushYellow = _WinAPI_CreateSolidBrush( 0xFFFF00 ), $hBrushCyan = _WinAPI_CreateSolidBrush( 0x00FFFF ) ; Yellow and cyan, BGR
  Local Static $hBrushHighLight = _WinAPI_GetSysColorBrush( $COLOR_HIGHLIGHT ), $hBrushButtonFace = _WinAPI_GetSysColorBrush( $COLOR_BTNFACE )

  ; We can optimize code by removing Switch statements because the ListView is the only ownerdrawn control and only $ODA_DRAWENTIRE actions are present

  Local $tDrawItem = DllStructCreate( $tagDRAWITEM, $lParam ), $itemID = DllStructGetData( $tDrawItem, "itemID" ), $iState = DllStructGetData( $tDrawItem, "itemState" ), $hDC = DllStructGetData( $tDrawItem, "hDC" ), $sItemText

  ; Loop through columns ($i is the column index)
  For $i = 0 To $g_iCols - 1
    ; Subitem rectangle
    DllStructSetData( $tRect, 2, $i ) ; Top
    DllStructSetData( $tRect, 1, $LVIR_BOUNDS ) ; Left
    GUICtrlSendMsg( $g_idListView, $LVM_GETSUBITEMRECT, $itemID, $pRect )
    DllStructSetData( $tRect, 1, DllStructGetData( $tRect, 1 ) + 6 ) ; Left margin
    DllStructSetData( $tRect, 2, DllStructGetData( $tRect, 2 ) + 2 ) ; Top  margin

    ; Subitem background and text color
    If BitAND( $iState, $ODS_SELECTED ) Then DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $fListViewHasFocus = 1 ? $hBrushHighLight : $hBrushButtonFace ) ; _WinAPI_FillRect
    DllCall( "gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", BitAND( $iState, $ODS_SELECTED ) ? $fListViewHasFocus = 1 ? 0xFFFFFF : 0x000000 : 0x000000 ) ; _WinAPI_SetTextColor

    ; Draw subitem text
    If $g_iSortDir = 0x0400 Then ; 0x0400 = $HDF_SORTUP
      $sItemText = $g_aSubArray[$g_tIndex.arr($itemID + 1)][$i]
    Else
      $sItemText = $g_aSubArray[$g_tIndex.arr($g_iSearch - $itemID)][$i]
    EndIf
    DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sItemText, "int", StringLen( $sItemText ), "struct*", $tRect, "uint", 0 ) ; _WinAPI_DrawText

    ; Skip column if search for 1 column only and $i is not the searched column
    If $g_iSearchCol > - 1 And $i <> $g_iSearchCol Then ContinueLoop ; > - 1 means search for 1 column (0, 1, 2...)

    ; Mark matching substring if found in this column
    If $g_sSearch Then
      Local $sMatch = StringRegExp( $sItemText, "(?i)" & $g_sSearch, 1 )
      If Not @error Then ; match found
        Local $extended = @extended, $iLen = StringLen( $sMatch[0] )

        ; Rectangle for matching substring
        DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sItemText, "int", $extended - $iLen - 1, "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32
        DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + DllStructGetData( $tSize, "X" ) )
        DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32
        DllStructSetData( $tRect, "Right", DllStructGetData( $tRect, "Left" ) + DllStructGetData( $tSize, "X" ) )

        ; Fill rectangle with yellow or cyan (selected) background color
        DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", BitAND( $iState, $ODS_SELECTED ) ? $hBrushCyan : $hBrushYellow ) ; _WinAPI_FillRect

        ; Draw matching substring in rectangle
        DllCall( "gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", 0x000000 ) ; _WinAPI_SetTextColor
        DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sMatch[0], "int", $iLen, "struct*", $tRect, "uint", 0 ) ; _WinAPI_DrawText
      EndIf
    EndIf
  Next

  Return $GUI_RUNDEFMSG
  #forceref $hWnd, $iMsg, $wParam
EndFunc

;========================================================================
Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)

  Local $tNMHEADER = DllStructCreate($tagNMHEADER, $lParam)
  Switch HWnd(DllStructGetData($tNMHEADER, "hWndFrom"))
    Case $g_hHeader
      Local $iCode = DllStructGetData($tNMHEADER, "Code")
      Switch $iCode
        Case $NM_RCLICK
          Local $aHit = _GUICtrlListView_SubItemHitTest($g_hListView)
          ; $aHit[1] : 0-based index of the LV subitem... i.e. the column in our case (may be -1 if right click on empty part of header)
          If $aHit[1] > - 1 Then ; valid column
            GUICtrlSetData($g_idComboCol, $aHit[1])
            GUICtrlSendToDummy($g_idComboColDummy)
          EndIf
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

;========================================================================
Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)

  Local $hWndFrom = $lParam
  Local $iCode = BitShift($wParam, 16) ; High word
  Switch $hWndFrom
    Case $g_hEdit
      Switch $iCode
        Case $EN_CHANGE
          GUICtrlSendToDummy($g_idEditDummy)
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_COMMAND

;========================================================================
Func _Generate_All(ByRef $g_aArray)

  ConsoleWrite("$g_iRows = " & $g_iRows & "    $g_iCols = " & $g_iCols & @CRLF)
  Local $hTimer = TimerInit()
  $g_aArray = FAS_Random2DArrayAu3($g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz")
  ; $g_aArray = FAS_Random2DArrayAu3($g_iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz" & "\.^$|[({*+?#)") ; 12 + 1 RegEx metacharacters
  For $i = 0 To $g_iRows - 1
    $g_tIndex.arr($i + 1) = $i
  Next
  ConsoleWrite("Generating array & one index = " & TimerDiff($hTimer) & @CRLF & @CRLF)
EndFunc   ;==>_Generate_All

;========================================================================
Func _SortArrayStruct(Const ByRef $aArray, $iCol, $iRows)
  Local $tIndex = DllStructCreate("uint arr[" & $iRows & "]")
  Local $pIndex = DllStructGetPtr($tIndex)
  Local Static $hDll = DllOpen("kernel32.dll")
  Local Static $hDllComp = DllOpen("shlwapi.dll")

  Local $lo, $hi, $mi, $r

  ; Sorting by one column
  For $i = 1 To $iRows - 1
    $lo = 0
    $hi = $i - 1
    Do
      $mi = Int(($lo + $hi) / 2)
      $r = DllCall($hDllComp, 'int', 'StrCmpLogicalW', 'wstr', $aArray[$i][$iCol], 'wstr', $aArray[DllStructGetData($tIndex, 1, $mi + 1)][$iCol])[0]
      Switch $r
        Case -1
          $hi = $mi - 1
        Case 1
          $lo = $mi + 1
        Case 0
          ExitLoop
      EndSwitch
    Until $lo > $hi
    DllCall($hDll, "none", "RtlMoveMemory", "struct*", $pIndex + ($mi + 1) * 4, "struct*", $pIndex + $mi * 4, "ulong_ptr", ($i - $mi) * 4)
    DllStructSetData($tIndex, 1, $i, $mi + 1 + ($lo = $mi + 1))
  Next

  Return $tIndex
EndFunc   ;==>_SortArrayStruct

;========================================================================
Func _UpdateIndex(ByRef $aIndex, $iCol)

  If VarGetType($aIndex[$iCol]) = "DLLStruct" Then
    $g_tIndex = $aIndex[$iCol]
  Else
    $g_tIndex = _SortArrayStruct($g_aSubArray, $iCol, $g_iSearch)
    $aIndex[$iCol] = $g_tIndex ; "DLLStruct" (or "Int32" when no match found +++)
  EndIf
EndFunc   ;==>_UpdateIndex

;========================================================================
Func _MoveMarker($iCol)

  If $iCol = - 1 Then ; All columns
    ControlMove($g_hGui, "", $g_idMarker, 10, 40 - 3, 630, 3) ; 10 / 40 / 630 are LV coords
  Else ; 1 column (0, 1, 2...)
    Local $aRect = _GUICtrlHeader_GetItemRect($g_hHeader, $iCol)
    ControlMove($g_hGui, "", $g_idMarker, 10 + $aRect[0], 40 - 3, $aRect[2] - $aRect[0] + 1, 3)
  EndIf
EndFunc   ;==>_MoveMarker

 

Link to comment
Share on other sites

Hollo together

I couldn't change the column size of the virtual ListView.

When I deleted this block, it worked again. Maybe it was also because of the operating system? I have Windows 8.1 Pro 64 bit.

; No ListView column resizing by dragging header dividers (LarsJ)
  ;_WinAPI_SetWindowLong( $hHeader, $GWL_STYLE, _WinAPI_GetWindowLong( $hHeader, $GWL_STYLE ) + $HDS_NOSIZING ) ; AutoIt 3.3.14.5 issue
  DllCall( "user32.dll", "long_ptr", @AutoItX64 ? "SetWindowLongPtrW" : "SetWindowLongW", "hwnd", $g_hHeader, "int", $GWL_STYLE, "long_ptr", _
  DllCall( "user32.dll", "long_ptr", @AutoItX64 ? "GetWindowLongPtrW" : "GetWindowLongW", "hwnd", $g_hHeader, "int", $GWL_STYLE )[0] + $HDS_NOSIZING )

I also tried to integrate the LatsJ's ColAlign.au3 script into the last version, but unfortunately it didn't work. I couldn't cope with it.

; Subitem rectangle for right and center aligned columns
If $g_aColAligns[$g_iSearchCol] = $HDF_RIGHT Or $g_aColAligns[$g_iSearchCol] = $HDF_CENTER Then
    DllCall( "gdi32.dll", "bool", "GetTextExtentPoint32W", "handle", $hDC, "wstr", $sSubItemText, "int", StringLen( $sSubItemText ), "struct*", $tSize ) ; _WinAPI_GetTextExtentPoint32
    Switch $g_aColAligns[$g_iSearchCol]
        Case $HDF_RIGHT
            DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Right" ) - DllStructGetData( $tSize, "X" ) - 6 )
        Case $HDF_CENTER
            DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + ( DllStructGetData( $tRect, "Right" ) - DllStructGetData( $tRect, "Left" ) - DllStructGetData( $tSize, "X" ) ) / 2 - 3 )
        EndSwitch
EndIf

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...