Jump to content

Improving _ArrayDisplay speed


Recommended Posts

I haven't reviewed the current ArrayDisplay code.

I don't think that automatic sizing of columns works in a virtual listview. Automatic sizing of columns is based on the column widths being calculated when item/subitem texts are inserted into listview structures. But in a virtual listview, the texts aren't inserted into any structures. Therefore, the column widths cannot be calculated.

Of course, all code based on item/subitem texts being stored in listview structures doesn't work immediately in a virtual listview.

Depending on how much you want to make out of it, you can perform many of the calculations yourself directly on the basis of the data source. Since these calculations may be time consuming, they must be performed before the ArrayDisplay function is called.

Also note that the code in the post at top of the previous page are simple examples, which was what I could find time for on the last day of the Easter holiday.

Edited by LarsJ
Link to comment
Share on other sites

@pixelsearch I don't think I will change the default column size as the code try to fit the length of the data or of the header.

I agree that using array range the propose sizing does not follow this rule I don't want to make an extra code for array range that will certainly invove Font calculation.

You did not report on perf overall regarding the non virtual listview. Did you find some drawback?

Cheers

Link to comment
Share on other sites

Just an idea.  Transform $iMax_Colwidth into a variant where user can define each column size with "|" separator in between.  If user provide single number, it stays as it is now.  If user provide a string delimited with "|" then use the individual values apply for each column.

Link to comment
Share on other sites

6 hours ago, jpm said:

You did not report on perf overall regarding the non virtual listview. Did you find some drawback?

No drawback at all, on the contrary (even on my antique computer)
I tested with 10000, 20000 and 30000 rows (6 cols) array always shuffled.

* Virtual view always displays at the speed of sound (after the index is sorted) so there's nothing to compare with a regular view, concerning the moment LV is displayed the 1st time.

* Concerning the sorting time : I tested with 3 scripts :
- original beta 3.3.15.3 (regular LV)
- my reworked script (regular LV speeded)
- your actual script (virtual LV)

We already tested (thanks Nine) that my reworked script sorts 3 times faster than the original beta
3.3.15.3

Additional tests this morning show that your actual script (virtual LV) sorts between 2 or 3 times faster than my reworked script (2 times faster when array got 10000 rows, 3 times faster when it has 30000 rows)

Edit: just re-tested on a 20000 rows LV : your actual script (virtual LV) sorts and displays the results 6 times faster than the original beta 3.3.15.3 (this matches the tests & explanations of preceding lines)

Edited by pixelsearch
Link to comment
Share on other sites

Hi Jpm :)
I notice you implemented the Array range & transpose in your Virtual listview code, bravo.
Also I read this in your script :

Case $idListView ; sort
    If $sArrayRange Then ContinueLoop ; no sorting when Array range selected

After several tries, I was able to sort a virtual listview even when it got an Array row range. Here is a functional script :

Opt( "MustDeclareVars", 1 )

#include <GuiListView.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include "RandomArray.au3" ; LarsJ
#include "IndexSort.au3"   ; ditto

Global $iRows = 10000, $iCols = 6, $aArray, $aIndex, $aIndexes[$iCols], $iSortDir
Global $hListView, $aCols = [ "Strings", "Integers", "Floats", "Dates", "Times", "R/C" ], $aWidths = [ 230, 60, 120, 70, 60, 60 ]

;====================
; Comment / Uncomment 1 of these 2 lines for No row range / row range
;~ Global $iItem_Start = 0, $iItem_End = $iRows - 1 ; No row range
Global $iItem_Start = 500, $iItem_End = 599 ; row range
;====================

Global $iRows2 = $iItem_End - $iItem_Start + 1 ; line not to be modified

Example()

Func Example()
    ; Check range
    Local $sMsg = ""
    Select
        Case $iItem_Start > $iItem_End ; "minor" fatal error
            $sMsg = "$iItem_Start > $iItem_End"
        Case $iRows2 > $iRows ; "serious" fatal error (Autoit3.exe keeps running in the background)
            $sMsg = "$iRows2 > $iRows"
        Case $iItem_End >= $iRows ; "serious" fatal error
            $sMsg = "$iItem_End >= $iRows"
        Case $iItem_Start < 0 ;  ; "serious" fatal error
            $sMsg = "$iItem_Start < 0"
    EndSelect
    If $sMsg Then Exit MsgBox($MB_TOPMOST, $sMsg, "Please fix variables")

    ; Generate Array & Indexes
    _Generate_All($aArray, $aIndex, $aIndexes)

    ; Create GUI
    Local $hGui = GUICreate( "Virtual ListView + Sort when Array range (2a)", 630+20, 788+30+20 )
    ; WinSetOnTop ( $hGui, "", 1 ) ; 1 = $WINDOWS_ONTOP , 0 = $WINDOWS_NOONTOP

    ; Create ListView
    Local $idListView = GUICtrlCreateListView( "", 10, 40, 630, 788, $LVS_OWNERDATA, $WS_EX_CLIENTEDGE )
    _GUICtrlListView_SetExtendedListViewStyle( $idListView, $LVS_EX_DOUBLEBUFFER + $LVS_EX_FULLROWSELECT )
    $hListView = GUICtrlGetHandle( $idListView )
    Local $hHeader = _GUICtrlListView_GetHeader( $idListView )
    For $i = 0 To $iCols - 1
        _GUICtrlListView_AddColumn( $idListView, $aCols[$i], $aWidths[$i] )
    Next

    ; Sorting information
    $iSortDir = $HDF_SORTUP
    Local $iColumn = 5, $iColumnPrev = 5 ; col "R/C"
    _GUICtrlHeader_SetItemFormat( $hHeader, $iColumn, $HDF_STRING + $iSortDir )

    ; Register WM_NOTIFY message handler through subclassing
    Local $pNotifyHandler = DllCallbackGetPtr( DllCallbackRegister( "NotifyHandler", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) )
    DllCall( "comctl32.dll", "bool", "SetWindowSubclass", "hwnd", $hGui, "ptr", $pNotifyHandler, "uint_ptr", 0, "dword_ptr", 0 ) ; $iSubclassId = 0, $pData = 0

    ; Set the virtual number of items in a virtual list-view control
    ; GUICtrlSendMsg( $idListView, $LVM_SETITEMCOUNT, $iRows, 0 )
    GUICtrlSendMsg( $idListView, $LVM_SETITEMCOUNT, $iRows2, 0 )

    ; Show GUI
    GUISetState( @SW_SHOW )

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $idListView ; when this Case is 1st triggered, $aIndex corresponds to last col ("R/C")
                $iColumn = GUICtrlGetState( $idListView )
                If $iColumn <> $iColumnPrev Then
                    _GUICtrlHeader_SetItemFormat( $hHeader, $iColumnPrev, $HDF_STRING ) ; remove sort arrow from previous column
                    $aIndex = $aIndexes[$iColumn]
                EndIf
                $iSortDir = (($iColumn = $iColumnPrev) ? ($iSortDir = $HDF_SORTUP ? $HDF_SORTDOWN : $HDF_SORTUP) : ($HDF_SORTUP))
                _GUICtrlHeader_SetItemFormat(  $hHeader, $iColumn, $HDF_STRING + $iSortDir )
                GUICtrlSendMsg( $idListView, $LVM_SETSELECTEDCOLUMN, $iColumn, 0 )

                ; GUICtrlSendMsg( $idListView, $LVM_SETITEMCOUNT, $iRows, 0 )
                GUICtrlSendMsg( $idListView, $LVM_SETITEMCOUNT, $iRows2, 0 )

                $iColumnPrev = $iColumn
        EndSwitch
    WEnd

    ; Cleanup
    DllCall( "comctl32.dll", "bool", "RemoveWindowSubclass", "hwnd", $hGui, "ptr", $pNotifyHandler, "uint_ptr", 0 ) ; $iSubclassId = 0
    GUIDelete( $hGui )
EndFunc

;========================================================================
Func NotifyHandler( $hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData )

    If $iMsg = 0x004E Then ; 0x004E = $WM_NOTIFY
        Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )
        Switch HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) )
            Case $hListView
                Switch DllStructGetData( $tNMHDR, "Code" )
                    Case $LVN_GETDISPINFOW
                        Local $tNMLVDISPINFO = DllStructCreate( $tagNMLVDISPINFO, $lParam )
                        Local Static $tText = DllStructCreate( "wchar[50]" ), $pText = DllStructGetPtr( $tText )

;~                      ; LarsJ 1-liner nearly original line (watch out : $iRows changed to $iRows2 +++)
;~                      DllStructSetData( $tText, 1, $aArray[( $iSortDir=$HDF_SORTUP?$aIndex[DllStructGetData($tNMLVDISPINFO,"Item")]:$aIndex[$iRows2-1-DllStructGetData($tNMLVDISPINFO,"Item")] )][DllStructGetData($tNMLVDISPINFO,"SubItem")] )

                        ; Reorganized preceding 1-liner to study it (watch out : $iRows2 +++)
                        If $iSortDir = 0x0400 Then ; 0x0400 = $HDF_SORTUP
                            DllStructSetData($tText, 1, $aArray[$aIndex[$tNMLVDISPINFO.Item]][$tNMLVDISPINFO.SubItem])
                        Else
                            DllStructSetData($tText, 1, $aArray[$aIndex[$iRows2 -1 -$tNMLVDISPINFO.Item]][$tNMLVDISPINFO.SubItem])
                        EndIf

                        DllStructSetData( $tNMLVDISPINFO, "Text", $pText )
                        Return
                EndSwitch
        EndSwitch
    EndIf
    Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]
    #forceref $iSubclassId, $pData
EndFunc

;========================================================================
Func _Generate_All(ByRef $aArray, ByRef $aIndex, Byref $aIndexes)

    ; Generate array
    ConsoleWrite( "$iRows = " & $iRows & "   $iRows2 = " & $iRows2 & @CRLF )
    Local $hTimer = TimerInit()
    $aArray = FAS_Random2DArrayAu3( $iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz" )
    ConsoleWrite( "Generating array = " & TimerDiff( $hTimer ) & @CRLF & @CRLF)
;~  _ArrayDisplay($aArray, "Initial $aArray")

    ; Sort string column (0)
    ; ---------------------
;~  Dim $aIndex[$iRows]
    Dim $aIndex[$iRows2]

    Local $aCompare = [ 0, 1 ]
    ConsoleWrite( "Sorting array by column " & 0 & " (string)" & @CRLF )
    $hTimer = TimerInit()
    Local $tIndex = FAS_Sort2DArrayAu3( $aArray, $aCompare )
;~  For $i = 0 To $iRows - 1
;~      $aIndex[$i] = DllStructGetData( $tIndex, 1, $i+1 )
;~  Next

    Local $k = - 1, $iIdx
    For $i = 0 To $iRows - 1
        $iIdx = DllStructGetData( $tIndex, 1, $i+1 )
        If $iIdx >= $iItem_Start And $iIdx <= $iItem_End Then
            $k += 1
            $aIndex[$k] = $iIdx
        EndIf
    Next

    $aIndexes[0] = $aIndex
    ConsoleWrite( "Sorting array = " & TimerDiff( $hTimer ) & @CRLF )
;~  _ArrayDisplay($aIndex, "$aIndex (0)")

    ; Sort number columns
    ; -------------------
    $aCompare[1] = 0
    For $i = 1 To $iCols-2
        $aCompare[0] = $i
        ConsoleWrite( "Sorting array by column " & $i & @CRLF )
        $hTimer = TimerInit()
        $tIndex = FAS_Sort2DArrayAu3( $aArray, $aCompare )
;~      For $j = 0 To $iRows - 1
;~          $aIndex[$j] = DllStructGetData( $tIndex, 1, $j+1 )
;~      Next

        $k = - 1
        For $j = 0 To $iRows - 1
            $iIdx = DllStructGetData( $tIndex, 1, $j+1 )
            If $iIdx >= $iItem_Start And $iIdx <= $iItem_End Then
                $k += 1
                $aIndex[$k] = $iIdx
            EndIf
        Next

        $aIndexes[$i] = $aIndex
        ConsoleWrite( "Sorting array = " & TimerDiff( $hTimer ) & @CRLF )
;~      _ArrayDisplay($aIndex, "$aIndex (" & $i & ")")
    Next

    ; Sort array by last column
    ; -------------------------
    ConsoleWrite( "Sorting array by column " & $iCols-1 & " (R/C)" & @CRLF )
    $hTimer = TimerInit()
;~  For $i = 0 To $iRows - 1
;~      $aIndex[$i] = $i
;~  Next

    For $i = $iItem_Start To $iItem_End
        $aIndex[$i - $iItem_Start] = $i
    Next

    $aIndexes[$iCols-1] = $aIndex
    ConsoleWrite( "Sorting array = " & TimerDiff( $hTimer ) & @CRLF & @CRLF)
;~  _ArrayDisplay($aIndex, "$aIndex (" & $iCols-1 & ")")
;~  _ArrayDisplay($aIndexes, "$aIndexes")
EndFunc

The preceding code is based on a reworked LarsJ script "ArrayDisplay2.au3" found  in this link . Users who wanna try the precedent code will need both include files :

https://www.autoitscript.com/forum/applications/core/interface/file/attachment.php?id=67836
#include "RandomArray.au3" ; LarsJ
#include "IndexSort.au3"   ; ditto

In case someone would like to share ideas (or finds a bug in the precedent code), please indicate it in this thread. Thanks !

Updates
04/22/2021 : added a minimal range error checking, to avoid fatal errors when user indicates a bad range.

04/24/2021 : Version 2a (in GUI title)

Edited by pixelsearch
Link to comment
Share on other sites

I made an update in my previous script just above (added a minimal range checking. The checking seems much more important when the ListView is Virtual)

1 hour ago, jpm said:

I don't remember why I had this line.

Probably because actually column indexes don't match the items when there's a range.
The following code shows the issue :

ArrayDisplay2-new.au3

Example_range1D()

Func Example_range1D()
    Local $sRange = "1:"
     Local $aArray[10] = [8, 6, 7, 1, 9, 4, 0, 5, 3, 2]
    __ArrayDisplay_Share__($aArray, "range", $sRange)
EndFunc   ;==>Example_range1D

ArrayDisplayInternals2.au3

Case $idListView ; sort
    ; If $sArrayRange Then ContinueLoop ; no sorting when Array range selected

Row 0 ( value 8 ) shouldn't appear when Col 0 is clicked for sorting, but it will appear.

Same issue would be found if the Array was 2D and ran with Example(), then a click on any of 5 headers (String, Integers, Floats, Dates, Times) would wrongly display Row 0  ( 9 times out of 10 )

ArrayDisplay2-new.au3

Example()

Func Example()
    Local $iRows = 10
    Local $sRange = "1:"
    ...

 

Edited by pixelsearch
Link to comment
Share on other sites

@jpm & @LarsJ : I would like to modify a sort function found in Jpm's script (based on LarsJ's script) but I don't know how exactly to do it (tests weren't successful). Could you please help ?

The goal is, when a row range is selected by the user. to avoid creating structures having the same number of elements as the original array number of rows. Creating indexes could then be more faster.

Here is how I solved successfully the creating of filtered indexes in my script above :
* Original array : 10.000 rows ($iRows)
* Selected range : 100 rows ($iRows2, from row  $iItem_Start = 500  To  $iItem_End = 599)
* Create all indexes using structures (DllStructCreate) by calling FAS_Sort2DArrayAu3()

* As there are 10.000 elements in the structure, I did not create the final filtered index like this :

;~  For $i = 0 To $iRows - 1 ; 10000 times
;~      $aIndex[$i] = DllStructGetData( $tIndex, 1, $i+1 )
;~  Next

* Instead, I created a filtered index of 100 elements like that :

Local $k = - 1, $iIdx
For $i = 0 To $iRows - 1 ; 10000 times
    $iIdx = DllStructGetData( $tIndex, 1, $i+1 )
    If $iIdx >= $iItem_Start And $iIdx <= $iItem_End Then
        $k += 1
        $aIndex[$k] = $iIdx
    EndIf
Next

* Though it works very fine, I would like the script to be even more performant and be able to create range structures having only 100 elements each (1st time gain +++) then I could create each final filtered index like that (2nd time gain) :

For $i = 0 To $iRows2 - 1 ; 100 times
    $aIndex[$i] = DllStructGetData( $tIndex, 1, $i+1 )
Next

* Let's have a look at Jpm's function __ArrayDisplay_SortArrayStruct() based on LarsJ's function FAS_Sort2DArrayFunc()

Func __ArrayDisplay_SortArrayStruct(Const ByRef $aArray, $iCol)
    Local $iRows = UBound($aArray, $UBOUND_ROWS)
    Local $iDims = UBound($aArray, $UBOUND_DIMENSIONS)
    Local $tIndex = DllStructCreate("uint[" & $iRows & "]")
    Local $pIndex = DllStructGetPtr($tIndex)
    Static $hDll = DllOpen("kernel32.dll")
    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)
            If $iDims = 1 Then
                $r = DllCall($hDllComp, 'int', 'StrCmpLogicalW', 'wstr', $aArray[$i], 'wstr', $aArray[DllStructGetData($tIndex, 1, $mi + 1)])[0]
            Else
                $r = DllCall($hDllComp, 'int', 'StrCmpLogicalW', 'wstr', $aArray[$i][$iCol], 'wstr', $aArray[DllStructGetData($tIndex, 1, $mi + 1)][$iCol])[0]
            EndIf
            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   ;==>__ArrayDisplay_SortArrayStruct

Some lines of the function could be changed like this :

; Local $tIndex = DllStructCreate("uint[" & $iRows & "]") ; 10000 elements
Local $tIndex = DllStructCreate("uint[" & $iRows2 & "]") ; 100 elements
; For $i = 1 To $iRows - 1 ; 10000 - 1 times
For $i = $iItem_Start + 1 To $iItem_End ; 100 - 1 times

But what about those terrible variables in the function ($lo, $hi, especially $mi), the 'RtlMoveMemory' message (it includes $i) etc... How should this be scripted no matter the user selects a row range or not ?

The 2 optional parameters could be $iItem_Start and $iItem_End, knowing that $iRows2 = $iItem_End - $iItem_Start + 1 (though in my script all these 3 variables are declared Global)

If you guys think it's worth a try, thanks for sharing your solution :)

Edited by pixelsearch
Link to comment
Share on other sites

You can use an aRange index as shown here. Not tested. If this code is to work when there is no range and all rows are included, you must use an aRange index that includes all rows. But it'll be more performance optimal to use two sets of code with and without row ranges. 

The aRange index must of course be added to many different places in the code.

Local $iItem_Start = 500, $iItem_End = 599, $iRange = $iItem_End - $iItem_Start + 1, $aRange[$iRange]
For $i = 0 To $iRange - 1
  $aRange[$i] = $iItem_Start + $i
Next

Func __ArrayDisplay_SortArrayStruct(Const ByRef $aArray, $iCol)
  ;Local $iRows = UBound($aArray, $UBOUND_ROWS)
  Local $iDims = UBound($aArray, $UBOUND_DIMENSIONS)
  Local $tIndex = DllStructCreate("uint[" & $iRange & "]")
  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 $iRange - 1
    $lo = 0
    $hi = $i - 1
    Do
      $mi = Int(($lo + $hi) / 2)
      If $iDims = 1 Then
        $r = DllCall($hDllComp, 'int', 'StrCmpLogicalW', 'wstr', $aArray[$aRange[$i]], 'wstr', $aArray[$aRange[DllStructGetData($tIndex, 1, $mi + 1)]])[0]
      Else
        $r = DllCall($hDllComp, 'int', 'StrCmpLogicalW', 'wstr', $aArray[$aRange[$i]][$iCol], 'wstr', $aArray[$aRange[DllStructGetData($tIndex, 1, $mi + 1)]][$iCol])[0]
      EndIf
      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   ;==>__ArrayDisplay_SortArrayStruct

 

Link to comment
Share on other sites

@LarsJ your function was challenging. It helped me this evening to achieve my initial goal... and it worked :)

Here is a script without any aRange index and using "For $iItem = $iItem_Start + 1 To $iItem_End" in Func __ArrayDisplay_SortArrayStruct() . It works with or without range and there should be no noticeable performance penalty :

Opt( "MustDeclareVars", 1 )

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

Global $iRows = 10000, $iCols = 6, $aArray, $aIndex, $aIndexes[$iCols], $iSortDir
Global $hListView, $aCols = [ "Strings", "Integers", "Floats", "Dates", "Times", "R/C" ], $aWidths = [ 230, 60, 120, 70, 60, 60 ]

;====================
; Comment / Uncomment 1 of these 2 lines for No row range / row range
;~ Global $iItem_Start = 0, $iItem_End = $iRows - 1 ; No row range
Global $iItem_Start = 500, $iItem_End = 599 ; row range
;====================

Global $iRange = $iItem_End - $iItem_Start + 1 ; line not to be modified

Example()

Func Example()
    ; Check range
    Local $sMsg = ""
    Select
        Case $iItem_Start < 0
            $sMsg = "$iItem_Start < 0"
        Case $iItem_End >= $iRows
            $sMsg = "$iItem_End >= $iRows"
        Case $iItem_Start > $iItem_End
            $sMsg = "$iItem_Start > $iItem_End"
    EndSelect
    If $sMsg Then Exit MsgBox($MB_TOPMOST, $sMsg, "Please fix variables")

    ; Generate Array & Indexes
    _Generate_All($aArray, $aIndex, $aIndexes)

    ; Create GUI
    Local $hGui = GUICreate( "Virtual ListView + Sort when Array range (2b)", 630+20, 788+30+20 )

    ; Create ListView
    Local $idListView = GUICtrlCreateListView( "", 10, 40, 630, 788, $LVS_OWNERDATA, $WS_EX_CLIENTEDGE )
    _GUICtrlListView_SetExtendedListViewStyle( $idListView, $LVS_EX_DOUBLEBUFFER + $LVS_EX_FULLROWSELECT )
    $hListView = GUICtrlGetHandle( $idListView )
    Local $hHeader = _GUICtrlListView_GetHeader( $idListView )
    For $i = 0 To $iCols - 1
        _GUICtrlListView_AddColumn( $idListView, $aCols[$i], $aWidths[$i] )
    Next

    ; Sorting information
    $iSortDir = $HDF_SORTUP
    Local $iColumn = 5, $iColumnPrev = 5 ; col "R/C"
    _GUICtrlHeader_SetItemFormat( $hHeader, $iColumn, $HDF_STRING + $iSortDir )

    ; Register WM_NOTIFY message handler through subclassing
    Local $pNotifyHandler = DllCallbackGetPtr( DllCallbackRegister( "NotifyHandler", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) )
    DllCall( "comctl32.dll", "bool", "SetWindowSubclass", "hwnd", $hGui, "ptr", $pNotifyHandler, "uint_ptr", 0, "dword_ptr", 0 ) ; $iSubclassId = 0, $pData = 0

    ; Set the virtual number of items in a virtual list-view control
    GUICtrlSendMsg( $idListView, $LVM_SETITEMCOUNT, $iRange, 0 )

    ; Show GUI
    GUISetState( @SW_SHOW )

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $idListView ; sort
                $iColumn = GUICtrlGetState( $idListView )
                If $iColumn <> $iColumnPrev Then
                    _GUICtrlHeader_SetItemFormat( $hHeader, $iColumnPrev, $HDF_STRING )
                    $aIndex = $aIndexes[$iColumn]
                EndIf
                $iSortDir = (($iColumn = $iColumnPrev) ? ($iSortDir = $HDF_SORTUP ? $HDF_SORTDOWN : $HDF_SORTUP) : ($HDF_SORTUP))
                _GUICtrlHeader_SetItemFormat(  $hHeader, $iColumn, $HDF_STRING + $iSortDir )
                GUICtrlSendMsg( $idListView, $LVM_SETSELECTEDCOLUMN, $iColumn, 0 )
                GUICtrlSendMsg( $idListView, $LVM_SETITEMCOUNT, $iRange, 0 )

                $iColumnPrev = $iColumn
        EndSwitch
    WEnd

    ; Cleanup
    DllCall( "comctl32.dll", "bool", "RemoveWindowSubclass", "hwnd", $hGui, "ptr", $pNotifyHandler, "uint_ptr", 0 ) ; $iSubclassId = 0
    GUIDelete( $hGui )
EndFunc

;========================================================================
Func NotifyHandler( $hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData )

    If $iMsg = 0x004E Then ; 0x004E = $WM_NOTIFY
        Local $tNMHDR = DllStructCreate( $tagNMHDR, $lParam )
        Switch HWnd( DllStructGetData( $tNMHDR, "hWndFrom" ) )
            Case $hListView
                Switch DllStructGetData( $tNMHDR, "Code" )
                    Case $LVN_GETDISPINFOW
                        Local $tNMLVDISPINFO = DllStructCreate( $tagNMLVDISPINFO, $lParam )
                        Local Static $tText = DllStructCreate( "wchar[50]" ), $pText = DllStructGetPtr( $tText )

;~                      ; LarsJ 1-liner nearly original line (watch out : $iRows changed to $iRange +++)
;~                      DllStructSetData( $tText, 1, $aArray[( $iSortDir=$HDF_SORTUP?$aIndex[DllStructGetData($tNMLVDISPINFO,"Item")]:$aIndex[$iRange-1-DllStructGetData($tNMLVDISPINFO,"Item")] )][DllStructGetData($tNMLVDISPINFO,"SubItem")] )

                        ; Reorganized preceding 1-liner to study it
                        If $iSortDir = 0x0400 Then ; 0x0400 = $HDF_SORTUP
                            DllStructSetData($tText, 1, $aArray[$aIndex[$tNMLVDISPINFO.Item]][$tNMLVDISPINFO.SubItem])
                        Else
                            DllStructSetData($tText, 1, $aArray[$aIndex[$iRange -1 -$tNMLVDISPINFO.Item]][$tNMLVDISPINFO.SubItem])
                        EndIf

                        DllStructSetData( $tNMLVDISPINFO, "Text", $pText )
                        Return
                EndSwitch
        EndSwitch
    EndIf
    Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]
    #forceref $iSubclassId, $pData
EndFunc

;========================================================================
Func _Generate_All(ByRef $aArray, ByRef $aIndex, Byref $aIndexes)

    ; Generate array
    ConsoleWrite( "$iRows = " & $iRows & "   $iRange = " & $iRange & @CRLF )
    Local $hTimer = TimerInit()
    $aArray = FAS_Random2DArrayAu3( $iRows, "sifdtr", "abcdefghijklmnopqrstuvwxyz" )
    ConsoleWrite( "Generating array = " & TimerDiff( $hTimer ) & @CRLF & @CRLF)
;~  _ArrayDisplay($aArray, "Initial $aArray")

    ; Prepare indexes for all columns (except last column "R/C")
    ; ---------------------------------------------------------
    Dim $aIndex[$iRange]
    Local $tIndex
    For $iCol = 0 To $iCols - 2
        ConsoleWrite( "Sorting array by column " & $iCol & @CRLF )
        $hTimer = TimerInit()
        $tIndex = __ArrayDisplay_SortArrayStruct( $aArray, $iCol )
        For $i = 0 To $iRange - 1
            $aIndex[$i] = DllStructGetData( $tIndex, 1, $i+1 ) + $iItem_Start
        Next
        $aIndexes[$iCol] = $aIndex
        ConsoleWrite( "Sorting array = " & TimerDiff( $hTimer ) & @CRLF )
;~      _ArrayDisplay($aIndex, "$aIndex (" & $iCol & ")")
    Next

    ; Sort array by last column
    ; -------------------------
    ConsoleWrite( "Sorting array by column " & $iCols-1 & " (R/C)" & @CRLF )
    $hTimer = TimerInit()
    For $i = $iItem_Start To $iItem_End
        $aIndex[$i - $iItem_Start] = $i
    Next
    $aIndexes[$iCols-1] = $aIndex
    ConsoleWrite( "Sorting array = " & TimerDiff( $hTimer ) & @CRLF & @CRLF)
;~  _ArrayDisplay($aIndex, "$aIndex (" & $iCols-1 & ")")
;~  _ArrayDisplay($aIndexes, "$aIndexes")
EndFunc

;========================================================================
Func __ArrayDisplay_SortArrayStruct(Const ByRef $aArray, $iCol)
    
    ; $iItem_Start & $iItem_End & $iRange are Global variables in this script
    Local $iDims = UBound($aArray, $UBOUND_DIMENSIONS)
    Local $tIndex = DllStructCreate("uint[" & $iRange & "]")
    Local $pIndex = DllStructGetPtr($tIndex)
    Static $hDll = DllOpen("kernel32.dll")
    Static $hDllComp = DllOpen("shlwapi.dll")

    Local $lo, $hi, $mi, $r, $i = 0

    ; Sorting by one column
    For $iItem = $iItem_Start + 1 To $iItem_End
        $i += 1
        $lo = 0
        $hi = $i - 1
        Do
            $mi = Int(($lo + $hi) / 2)
            If $iDims = 1 Then
                $r = DllCall($hDllComp, 'int', 'StrCmpLogicalW', 'wstr', $aArray[$iItem], 'wstr', $aArray[DllStructGetData($tIndex, 1, $mi + 1) + $iItem_Start])[0]
            Else
                $r = DllCall($hDllComp, 'int', 'StrCmpLogicalW', 'wstr', $aArray[$iItem][$iCol], 'wstr', $aArray[DllStructGetData($tIndex, 1, $mi + 1) + $iItem_Start][$iCol])[0]
            EndIf
            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   ;==>__ArrayDisplay_SortArrayStruct

Update 04/24/2021
* Call __ArrayDisplay_SortArrayStruct() with 2 Params only, as $iItem_Start & $iItem_End & $iRange are Global variables in this script.
* Simplified array range checking
* Version 2b (in GUI title)

Edited by pixelsearch
Link to comment
Share on other sites

@jpm good news concerning ArrayDisplayInternals2.au3 . I found a way to totally get rid of Case -7 ; $NM_SETFOCUS and place all its code in a separate function (badly) named _Focus . This will reduce the code in the subclassing function, also $_g_ArrayDisplay_bFirstFocus isn't needed anymore.

This is a part of what stays in Main code (some lines have been moved and placed just after the subclassing is triggered)

...    
    ; Register WM_NOTIFY message handler through subclassing
    Local $p__ArrayDisplay_NotifyHandler = DllCallbackGetPtr(DllCallbackRegister("__ArrayDisplay_NotifyHandler", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr"))
    DllCall("comctl32.dll", "bool", "SetWindowSubclass", "hwnd", $_g_ArrayDisplay_hGUI, "ptr", $p__ArrayDisplay_NotifyHandler, "uint_ptr", 0, "dword_ptr", 0)   ; $iSubclassId = 0, $pData = 0

    Local $iColFill = $_g_ArrayDisplay_nCols + $_g_ArrayDisplay_iDisplayRow

    ; Align columns if required - $iColAlign = 2 for Right and 4 for Center
    If $iColAlign Then
        ; Loop through columns
        For $i = 0 To $iColFill - 1
            __ArrayDisplay_JustifyColumn($idListView, $i, $iColAlign / 2)
        Next
    EndIf

    GUICtrlSendMsg($idListView, (0x1000 + 47), $iItemCount, 0) ; $LVM_SETITEMCOUNT (test : has to be placed b4 "Get Row Height" as Jpm did)

    ; Get row height
    Local $tRECT = DllStructCreate("struct; long Left;long Top;long Right;long Bottom; endstruct") ; $tagRECT
    DllCall("user32.dll", "struct*", "SendMessageW", "hwnd", $_g_ArrayDisplay_hListView, "uint", $_ARRAYCONSTANT_LVM_GETITEMRECT, "wparam", 0, "struct*", $tRECT)
    ; Set required GUI height
    Local $aiWin_Pos = WinGetPos($_g_ArrayDisplay_hGUI)
    Local $aiLV_Pos = ControlGetPos($_g_ArrayDisplay_hGUI, "", $idListView)
    $iHeight = (($iItemCount + 3) * (DllStructGetData($tRECT, "Bottom") - DllStructGetData($tRECT, "Top"))) + $aiWin_Pos[3] - $aiLV_Pos[3]
    ; Check min/max height
    If $iHeight > @DesktopHeight - 100 Then
        $iHeight = @DesktopHeight - 100
    ElseIf $iHeight < $iMinSize Then
        $iHeight = $iMinSize
    EndIf

    Local $iWidth = _Focus() ; <===========

    ; Display and resize dialog
    GUISetState(@SW_HIDE, $_g_ArrayDisplay_hGUI)
    WinMove($_g_ArrayDisplay_hGUI, "", (@DesktopWidth - $iWidth) / 2, (@DesktopHeight - $iHeight) / 2, $iWidth, $iHeight)
    GUISetState(@SW_SHOW, $_g_ArrayDisplay_hGUI)
    ...

_Focus function :

Func _Focus()
    Local $bColDisplay = 1 ; Jpm will know better what to do with that variable value

    ; Adjust dialog width
    Local $iColFill = $_g_ArrayDisplay_iSubItem_End + $_g_ArrayDisplay_iDisplayRow
    Local $iWidth = 40, $iColWidth = 0, $aiColWidth[$iColFill + 1], $iMin_ColWidth = 55
    ; Get required column widths to fit items
    Local $iColWidthHeader
    For $i = 0 To $iColFill
        GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 30), $i, -1) ; $LVM_SETCOLUMNWIDTH $LVSCW_AUTOSIZE
        $iColWidth = GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 29), $i, 0) ; $LVM_GETCOLUMNWIDTH
        If $iColWidth = 0 Then ExitLoop
        GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 30), $i, -2) ; $LVM_SETCOLUMNWIDTH $LVSCW_AUTOSIZE_USEHEADER
        $iColWidthHeader = GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 29), $i, 0) ; $GETCOLUMNWIDTH
        ; Set minimum if required
        If $iColWidth < $iMin_ColWidth And $iColWidthHeader < $iMin_ColWidth Then
            GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 30), $i, $iMin_ColWidth) ; $LVM_SETCOLUMNWIDTH
            $iColWidth = $iMin_ColWidth
        ElseIf $iColWidthHeader < $iColWidth Then
            GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 30), $i, $iColWidth) ; $LVM_SETCOLUMNWIDTH
        Else
            $iColWidth = $iColWidthHeader
        EndIf
        If IsInt($bColDisplay) Then
            ; Add to total width
            $iWidth += $iColWidth
        EndIf
        ; Store  value
        $aiColWidth[$i] = $iColWidth
    Next

    ; Now check max size
    If $iWidth > @DesktopWidth - 100 Then
        ; Apply max col width limit to reduce width
        $iWidth = 40
        For $i = 0 To $iColFill
            If True Or $i = 0 Or ($i >= $_g_ArrayDisplay_iSubItem_Start And $i <= $_g_ArrayDisplay_iSubItem_End + 1) Then
                If $aiColWidth[$i] > $_g_ArrayDisplay_iMax_ColWidth Then
                    ; Reset width
                    GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 30), $i, $_g_ArrayDisplay_iMax_ColWidth) ; $LVM_SETCOLUMNWIDTH
                    $iWidth += $_g_ArrayDisplay_iMax_ColWidth
                Else
                    ; Retain width
                    $iWidth += $aiColWidth[$i]
                EndIf
                ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $iWidth = ' & $iWidth & " $i = " & $i & @CRLF & '>Error code: ' & @error & '    Extended code: ' & @extended & ' (0x' & Hex(@extended) & ')' & @CRLF)  ;### Debug Console
            EndIf
        Next
    EndIf

    Local $aWinPos = WinGetPos($_g_ArrayDisplay_hGUI)
    ; Check max/min width
    If $iWidth > @DesktopWidth - 100 Then
        $iWidth = @DesktopWidth - 100
    ElseIf $iWidth > $_g_ArrayDisplay_iMax_ColWidth * $_g_ArrayDisplay_nCols Then
        $iWidth = $_g_ArrayDisplay_iMax_ColWidth * $_g_ArrayDisplay_nCols
    EndIf
    ; Allow for borders with vertical scrollbar
    If $aWinPos[3] = (@DesktopHeight - 100) Then $iWidth += 15

    ; not needed anymore
    ; WinMove($_g_ArrayDisplay_hGUI, "", (@DesktopWidth - $iWidth) / 2, $aWinPos[1], $iWidth, $aWinPos[3])

    Return  $iWidth
EndFunc

I'm pretty sure that most of the "Main" code above could be transferred from Main into _Focus() using the correct parameters. Of course, it seems also that all the content of _Focus() could be placed back in Main, but imho it seems more pleasant to have it all in a separate function, you'll decide.

Anyway, display is correct on my computer with the code above (columns size, GUI width & height etc...)

Edited by pixelsearch
Link to comment
Share on other sites

I already did the same.

Now I face the transpose with arrayrange which imply to sort the whole as the indexes are relative to the complete array. Perhaps I will do as for the transpose a local subset array

Thanks for the help

Link to comment
Share on other sites

Hey Jpm, we're here nearly at same time :)
Glad you already did same. As I prepared some update a few minutes ago, I'll post it anyway, just as it was prepared :

Update 04/25/2021
"Forget the code just above. 2 separate functions make it clearer : 1 for calculating GUI Width, 1 for calculating GUI Height as shown below"

...
; Create GUI ; no need of $iOrgWidth, changed it to $iWidth in both lines :
Local $iWidth = 210, $iHeight = 200, $iMinSize = 250
$_g_ArrayDisplay_hGUI = GUICreate($sTitle, $iWidth, $iHeight, Default, Default, BitOR($_ARRAYCONSTANT_WS_SIZEBOX, $_ARRAYCONSTANT_WS_MINIMIZEBOX, $_ARRAYCONSTANT_WS_MAXIMIZEBOX))

...
;~ Local Const $_ARRAYCONSTANT_LVM_GETITEMRECT = (0x1000 + 14)

...
; Register WM_NOTIFY message handler through subclassing
Local $p__ArrayDisplay_NotifyHandler = DllCallbackGetPtr(DllCallbackRegister("__ArrayDisplay_NotifyHandler", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr"))
DllCall("comctl32.dll", "bool", "SetWindowSubclass", "hwnd", $_g_ArrayDisplay_hGUI, "ptr", $p__ArrayDisplay_NotifyHandler, "uint_ptr", 0, "dword_ptr", 0)   ; $iSubclassId = 0, $pData = 0

GUICtrlSendMsg($idListView, (0x1000 + 47), $iItemCount, 0) ; $LVM_SETITEMCOUNT

$iWidth = __ArrayDisplay_GuiWidth() ; <==========
$iHeight = __ArrayDisplay_GuiHeight($idListView, $iItemCount, $iMinSize) ; <==========

If $iVerbose Then SplashOff()
#EndRegion GUI generation

; Display and resize dialog
GUISetState(@SW_HIDE, $_g_ArrayDisplay_hGUI)
WinMove($_g_ArrayDisplay_hGUI, "", (@DesktopWidth - $iWidth) / 2, (@DesktopHeight - $iHeight) / 2, $iWidth, $iHeight)
GUISetState(@SW_SHOW, $_g_ArrayDisplay_hGUI)
...

;================================================================
Func __ArrayDisplay_GuiWidth()
    Local $bColDisplay = 1 ; Jpm will know better what to do with that variable value

    ; Adjust dialog width
    Local $iColFill = $_g_ArrayDisplay_iSubItem_End + $_g_ArrayDisplay_iDisplayRow
    Local $iWidth = 40, $iColWidth = 0, $aiColWidth[$iColFill + 1], $iMin_ColWidth = 55
    ; Get required column widths to fit items
    Local $iColWidthHeader
    For $i = 0 To $iColFill
        GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 30), $i, -1) ; $LVM_SETCOLUMNWIDTH $LVSCW_AUTOSIZE
        $iColWidth = GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 29), $i, 0) ; $LVM_GETCOLUMNWIDTH
        If $iColWidth = 0 Then ExitLoop
        GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 30), $i, -2) ; $LVM_SETCOLUMNWIDTH $LVSCW_AUTOSIZE_USEHEADER
        $iColWidthHeader = GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 29), $i, 0) ; $GETCOLUMNWIDTH
        ; Set minimum if required
        If $iColWidth < $iMin_ColWidth And $iColWidthHeader < $iMin_ColWidth Then
            GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 30), $i, $iMin_ColWidth) ; $LVM_SETCOLUMNWIDTH
            $iColWidth = $iMin_ColWidth
        ElseIf $iColWidthHeader < $iColWidth Then
            GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 30), $i, $iColWidth) ; $LVM_SETCOLUMNWIDTH
        Else
            $iColWidth = $iColWidthHeader
        EndIf
        If IsInt($bColDisplay) Then
            ; Add to total width
            $iWidth += $iColWidth
        EndIf
        ; Store  value
        $aiColWidth[$i] = $iColWidth
    Next

    ; Now check max size
    If $iWidth > @DesktopWidth - 100 Then
        ; Apply max col width limit to reduce width
        $iWidth = 40
        For $i = 0 To $iColFill
            If True Or $i = 0 Or ($i >= $_g_ArrayDisplay_iSubItem_Start And $i <= $_g_ArrayDisplay_iSubItem_End + 1) Then
                If $aiColWidth[$i] > $_g_ArrayDisplay_iMax_ColWidth Then
                    ; Reset width
                    GUICtrlSendMsg($_g_ArrayDisplay_idListView, (0x1000 + 30), $i, $_g_ArrayDisplay_iMax_ColWidth) ; $LVM_SETCOLUMNWIDTH
                    $iWidth += $_g_ArrayDisplay_iMax_ColWidth
                Else
                    ; Retain width
                    $iWidth += $aiColWidth[$i]
                EndIf
                ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $iWidth = ' & $iWidth & " $i = " & $i & @CRLF & '>Error code: ' & @error & '    Extended code: ' & @extended & ' (0x' & Hex(@extended) & ')' & @CRLF)  ;### Debug Console
            EndIf
        Next
    EndIf

    Local $aWinPos = WinGetPos($_g_ArrayDisplay_hGUI)
    ; Check max/min width
    If $iWidth > @DesktopWidth - 100 Then
        $iWidth = @DesktopWidth - 100
    ElseIf $iWidth > $_g_ArrayDisplay_iMax_ColWidth * $_g_ArrayDisplay_nCols Then
        $iWidth = $_g_ArrayDisplay_iMax_ColWidth * $_g_ArrayDisplay_nCols
    EndIf
    ; Allow for borders with vertical scrollbar
    If $aWinPos[3] = (@DesktopHeight - 100) Then $iWidth += 15

    ; not needed anymore
    ; WinMove($_g_ArrayDisplay_hGUI, "", (@DesktopWidth - $iWidth) / 2, $aWinPos[1], $iWidth, $aWinPos[3])

    Return  $iWidth
EndFunc   ;==>__ArrayDisplay_GuiWidth()

;================================================================
Func __ArrayDisplay_GuiHeight($idListView, $iItemCount, $iMinSize)

    ; Get row height
    Local $tRECT = DllStructCreate("struct; long Left;long Top;long Right;long Bottom; endstruct") ; $tagRECT
    DllCall("user32.dll", "struct*", "SendMessageW", "hwnd", $_g_ArrayDisplay_hListView, "uint", (0x1000 + 14), "wparam", 0, "struct*", $tRECT) ; $LVM_GETITEMRECT
    ; Set required GUI height
    Local $aiWin_Pos = WinGetPos($_g_ArrayDisplay_hGUI)
    Local $aiLV_Pos = ControlGetPos($_g_ArrayDisplay_hGUI, "", $idListView)
    Local $iHeight = (($iItemCount + 3) * (DllStructGetData($tRECT, "Bottom") - DllStructGetData($tRECT, "Top"))) + $aiWin_Pos[3] - $aiLV_Pos[3]
    ; Check min/max height
    If $iHeight > @DesktopHeight - 100 Then
        $iHeight = @DesktopHeight - 100
    ElseIf $iHeight < $iMinSize Then
        $iHeight = $iMinSize
    EndIf

    Return  $iHeight
EndFunc   ;==>__ArrayDisplay_GuiHeight()

All portions of code not indicated after subclassing  is ON are still placed just before it, for example :

; Align columns if required - $iColAlign = 2 for Right and 4 for Center (b4 subclassing is ok)
(code)

; Sorting information (b4 subclassing is ok)
(code)

I'll try to study a bit the transpose part (though I never transposed anything until now !)

Edited by pixelsearch
Link to comment
Share on other sites

8 hours ago, jpm said:

Now I face the transpose with arrayrange which imply to sort the whole as the indexes are relative to the complete array. Perhaps I will do as for the transpose a local subset array

Well... as you already scripted it for a full transpose with your function __ArrayDisplay_Transpose, then it seems coherent to do same when the transpose includes an array range (there will be less elements in the subset array)

On 4/9/2021 at 6:44 AM, jpm said:

I think that I will reject  the transpose when number of rows if more than  100 or LESS as to visualize the info it is needed  to use still a lot of horizontal shift

perhaps 100 is a litle small but what can be a better value?

I read this in an interesting LarsJ's link :

On 10/12/2019 at 7:23 AM, LarsJ said:

If the number of columns is limited to a few hundred, the built-in listview header can support the columns[...]

TooManyCols.au3 shows what happens if the listview contains more columns than the built-in header is able to support. Drag the thumb in the horizontal scrollbar all the way to the right to see that the header items are simply missing.

A test I just made indicates that the rectangle of all built-in ListView headers shouldn't be wider than 32767 pixels, this is about 327 cols x 100 pixels each (column width). If there are additional columns then headers will be missing (unless I guess the listview got the LVS_OWNERDRAWFIXED style but that's not our case). It's not the number of columns that indicates if a header will appear or not, but it's the total of columns width (in pixels) that has to be <= 32767

So when using virtual listviews, it seems difficult to know in advance if you should allow the user to transpose (with or without array range) as you don't know how many initial rows will have headers after they become columns (in case there are a few hundred initials rows... or much more as we're talking about virtual listviews)

Another point to think of : it shouldn't be a problem to display 300 cols of 100 pixels each, but what about the final number of rows (the ex-columns) ?

If there are only a few final rows, then it's ok, but if there are a few hundreds...
Now good luck with all these calculations before accepting to transpose or not.

HeaderWidth.png.492076923348e511cfc0c6a939c0ee31.png

Edit: here is a pic with 327 cols of 100 pixels each => 32700 pixels (i.e. the "row" col + cols from 0 to 325 => 327 cols displaying a full header)
The 67 remaining pixels are displayed in a truncated header ("Col 326")

Edited by pixelsearch
Link to comment
Share on other sites

my final touch. I hope ...

I don't except to work on too many cols

Cheers

Edited: see below for correct upload

Edited by jpm
to fix array 1D with range
Link to comment
Share on other sites

Hi Jpm :)
Great job! If you don't mind, I'll indicate in this post which problems I encounter with your most recent upload. Please feel free to re-upload a new version when the problem is serious, so we always test the most recent version. An example of serious problem with the last upload in preceding post

Example_range1D()

Func Example_range1D()
    Local $sRange = "1:"
    Local $aArray[2] = [0, 1]
    _DebugArrayDisplay($aArray, "range", $sRange)
    $g_hArrayDisplay_Share($aArray, "range", $sRange)
EndFunc   ;==>Example_range1D
"ArrayDisplayInternals2.au3" (829) : ==> Array variable has incorrect number of subscripts or subscript dimension range exceeded.:
$aTemp[$iRow][$iCol] = $_g_ArrayDisplay_aArray[$i][$j]
$aTemp[$iRow][$iCol] = ^ ERROR
>Exit code: 1

As the original array is 1D, then $_g_ArrayDisplay_aArray[$i][$j] will generate an error in Func __ArrayDisplay_CreateSubArray()

Apologies if I was mistaken.

Edit #1 : line 636

Dim $_g_ArrayDisplay_aIndexes[$_g_ArrayDisplay_nCols + 2]

; Shouldn't it be :
Dim $_g_ArrayDisplay_aIndexes[$_g_ArrayDisplay_nCols + 1]

Edit #2 : some cosmetic changes if you like...

line 824
Local $iRow = ($_g_ArrayDisplay_iSortDir = 0x00000400) ? $_g_ArrayDisplay_aIndex[$iItem] : $_g_ArrayDisplay_aIndex[$_g_ArrayDisplay_nRows - 1 - DllStructGetData($tNMLVDISPINFO, "Item")]

could be :
Local $iRow = ($_g_ArrayDisplay_iSortDir = 0x00000400) ? $_g_ArrayDisplay_aIndex[$iItem] : $_g_ArrayDisplay_aIndex[$_g_ArrayDisplay_nRows - 1 - $iItem]
3 same lines 703 - 711 - 725 :
DllStructSetData($tNMLVDISPINFO, "Text", $pText)

could be replaced by 1 line only (728)
DllStructSetData($tNMLVDISPINFO, "Text", $pText)
Return

 

Edited by pixelsearch
Link to comment
Share on other sites

Hi Jpm,
It seems ok now that you fixed it all.
I just had an idea. Pics being better than words...

1926197125_Transposewithheaders.png.160f397441d2da6708d5c023988ab449.png

When transposing, it allows the user to see in "Row" column the original headers (if any) before transposing so they are not lost anymore. Sorting on the "Row" column keeps working. Changes could be these (dirty code quickly tested) :

; actual
If $_g_ArrayDisplay_iTranspose Then
    DllStructSetData($tText, 1, "Col " & $iRow + $_g_ArrayDisplay_iItem_Start)
Else


; becomes
If $_g_ArrayDisplay_iTranspose Then
    Local $sCaptionCplt = ""
    If $iRow + $_g_ArrayDisplay_iItem_Start < Ubound($asHeader) _
    And StringStripWS($asHeader[$iRow + $_g_ArrayDisplay_iItem_Start], 1 + 2) <> "" Then
        $sCaptionCplt = " (" & StringStripWS($asHeader[$iRow + $_g_ArrayDisplay_iItem_Start], 1 + 2) & ")"
    EndIf
    DllStructSetData($tText, 1, "Col " & ($iRow + $_g_ArrayDisplay_iItem_Start) & $sCaptionCplt)
Else

Though there are several lines of code, this part of the message handler doesn't happen a lot (it requires 3 conditions : "row " column to be displayed +  "row" column being actually tested + transpose active.

Edited by pixelsearch
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...