ListIconGadget with Sort and Arrow Icons

Share your advanced PureBasic knowledge/code with the community.
User avatar
Bisonte
Addict
Addict
Posts: 1232
Joined: Tue Oct 09, 2007 2:15 am

Re: ListIconGadget with Sort and Arrow Icons

Post by Bisonte »

to understand the new module feature, I tried this to made one.
I hope that I made it right ;)

Code: Select all

; ==================================================================================================
; --- ListIconGadget Extras & Sort
; --- ----------------------------------------------------------------------------------------------
; --- File            : module_nalorLIG.pbi
; --- OriginalAuthor  : nalor
; --- modified        : bisonte (change to new module system of pb)
; --- Link            : http://purebasic.fr/english/viewtopic.php?f=12&t=55085    
; --- Date            : June 23, 2013
; --- Compiler        : PureBasic 5.20b2 (Windows - x64)
; --- Target OS       : Windows
; --- Version         : 1.0a
; ==================================================================================================
; --- Module : nalorLIG
; --- Remark : If WindowsXP is used - Disable Debugger or use Unicode Flag only
CompilerIf #PB_Compiler_Version => 520
  
  EnableExplicit
  
  CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
    MessageRequester("PureBasic", "Windows only! Sorry.") : End
  CompilerEndIf 
  
  DeclareModule nalorLIG
    
    ; --- Original by : nalor
    ; --- Link        : http://purebasic.fr/english/viewtopic.php?f=12&t=55085    
    ; --- mod.by      : bisonte (change to module) - 23.06.2013
    
    Enumeration ; Type of Column Sort
      #SortString
      #SortNumeric
      #SortFloat
      #SortDate
      #SortAutoDetect
    EndEnumeration
    Enumeration ; Column Sort States
      #NoSort   ; keine Sortierung
      #AscSort  ; Aufsteigende Sortierung
      #DescSort ; Absteigende Sortierung
    EndEnumeration
    
    Structure LVWSORT
      hWndListView.i ; Fensterhandle des ListView-Controls
      SortKey.i ; Spalte, die sortiert werden soll
      SortType.b ; Typ der zu sortierenden Daten
      SortOrder.b ; Sortierrichtung
      DateFormat.s ; Mask for 'ParseDate'
    EndStructure
    
    Declare   AlignColumn(Gadget, Index, Format)
    Declare   SetColumnWidth(Gadget, Index, New_Width)
    Declare   SetSortIcon(Gadget, Column, SortOrder)
    Declare.b GetSortOrder(Gadget, Column)
    Declare   EnsureVisible(Gadget, Line)
    
    Declare   Enable()
    
  EndDeclareModule
  Module        nalorLIG
    
    ; --- Original by : nalor
    ; --- Link        : http://purebasic.fr/english/viewtopic.php?f=12&t=55085    
    ; --- mod.by      : bisonte (change to module) - 23.06.2013
        
    Procedure   GethWnd(ID)
      
      Protected hWnd = #False
      
      If IsGadget(ID)
        hWnd = GadgetID(ID)
      Else
        If IsWindow_(ID)
          hWnd = ID
        EndIf
      EndIf 
      
      ProcedureReturn hWnd
      
    EndProcedure
    
    ;- ++++++ ListIconGadget Tools Start ++++++
    
    Procedure   AlignColumn(Gadget, Index, Format)
      
      ; by Danilo, 15.12.2003 - english chat (for 'Karbon')
      ; 20130615..nalor..modified
      ; change text alignment for columns
      ; #LVCFMT_LEFT / #LVCFMT_CENTER / #LVCFMT_RIGHT
      
      Protected lvc.LV_COLUMN, hWnd = GethWnd(Gadget)
      
      If hWnd
        
        lvc\mask = #LVCF_FMT
        lvc\fmt = Format
        
        SendMessage_(hWnd, #LVM_SETCOLUMN, Index, @lvc)
        
        ProcedureReturn #True
        
      EndIf
      
      ProcedureReturn #False
      
    EndProcedure
    Procedure   SetColumnWidth(Gadget, Index, New_Width)
      
      ; by Danilo, 15.12.2003 - english chat (for 'Karbon')
      ;
      ; change column header width
      ;
      
      Protected hWnd = GethWnd(Gadget)
      
      If hWnd
        
        SendMessage_(hWnd, #LVM_SETCOLUMNWIDTH, Index, New_Width)
        ProcedureReturn #True
        
      EndIf
      
      ProcedureReturn #False
      
    EndProcedure
    Procedure   SetSortIcon(Gadget, Column, SortOrder)
      
      ; http://stackoverflow.com/questions/254129/how-To-i-display-a-sort-arrow-in-the-header-of-a-List-view-column-using-c   
      
      Protected ColumnHeader
      Protected ColumnCount
      Protected hditem.HD_ITEM
      Protected Cnt
      Protected hWnd = GethWnd(Gadget)
      
      If hWnd
        
        ColumnHeader = SendMessage_(hWnd, #LVM_GETHEADER, 0, 0)
        ColumnCount  = SendMessage_(ColumnHeader, #HDM_GETITEMCOUNT, 0, 0)
        
        For Cnt = 0 To ColumnCount - 1
          
          hditem\mask=#HDI_FORMAT
          
          If SendMessage_(ColumnHeader, #HDM_GETITEM, Cnt, @hditem) = 0
            Debug "ERROR! LIG_SetSortIcon 1"
          EndIf
          
          hditem\mask=#HDI_FORMAT
          
          If (Cnt = Column And SortOrder <> #NoSort)
            
            Select SortOrder
                
              Case #AscSort ; wenn aufsteigend sortiert werden soll
                hditem\fmt& ~#HDF_SORTDOWN
                hditem\fmt|#HDF_SORTUP
                Debug "sortup"
                
              Case #DescSort
                hditem\fmt& ~#HDF_SORTUP
                hditem\fmt|#HDF_SORTDOWN               
                Debug "sortdown"
                
            EndSelect
            
          Else
            
            hditem\fmt& ~#HDF_SORTUP
            hditem\fmt& ~#HDF_SORTDOWN
            
          EndIf
          
          If (SendMessage_(ColumnHeader, #HDM_SETITEM, Cnt, @hditem) = 0)
            Debug "ERROR! LIG_SetSortIcon 2"
          EndIf
          
        Next cnt
        
        ProcedureReturn #True
        
      EndIf
      
      ProcedureReturn #False
      
    EndProcedure
    Procedure.b GetSortOrder(Gadget, Column)
      
      Protected ColumnHeader
      Protected hditem.HD_ITEM
      Protected RetVal.b = -1
      Protected hWnd = GethWnd(Gadget)
      
      If hWnd
        
        ColumnHeader = SendMessage_(hWnd, #LVM_GETHEADER, 0, 0)
        
        hditem\mask=#HDI_FORMAT
        
        If SendMessage_(ColumnHeader, #HDM_GETITEM, Column, @hditem)
          
          If (hditem\fmt&#HDF_SORTUP)=#HDF_SORTUP
            
            Debug "sortup"
            RetVal=#AscSort
            
          ElseIf (hditem\fmt&#HDF_SORTDOWN)=#HDF_SORTDOWN
            
            Debug "sortdown"
            RetVal=#DescSort
            
          Else
            
            Debug "keine sortierung"
            RetVal=#NoSort
            
          EndIf
          
        Else
          
          Debug "ERROR! LIG_GetSortOrder"
          RetVal=-1
          
        EndIf
        
      EndIf
      
      ProcedureReturn RetVal
      
    EndProcedure
    Procedure   EnsureVisible(Gadget, Line)
      ; makes sure the line is visible
      
      Protected hWnd = GethWnd(Gadget)
      If hWnd
        SendMessage_(hWnd, #LVM_ENSUREVISIBLE, Line, #True)
        ProcedureReturn #True  
      EndIf
      ProcedureReturn #False
      
    EndProcedure
    
    ;- ##### ListIconGadget Tools Ende #####
    
    ;- ++++++ ListIconGadget Sort Start ++++++
    
    ; http://msdn.microsoft.com/de-de/library/bb979183.aspx   
    ; Die Struktur LVWSORT enthält Informationen über das zu sortierende ListView-Steuerelement, die Spalte,
    ; nach der sortiert werden soll, sowie die gewünschte Sortierrichtung.
    
    Procedure.b IsNumChar(*Text, Position = 1)
      Select Asc(PeekS(*Text+(Position-1)*SizeOf(Character), 1))
        Case 48 To 57
          ProcedureReturn #True
        Default
          ProcedureReturn #False
      EndSelect
    EndProcedure
    Procedure   CompareStrings(*sEntry1, *sEntry2, SortOrder.b)
      ; ' -----------------------------------------------------
      ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
      ; ' Elemente nach Maßgabe des Parameters SortOrder größer
      ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
      ; ' aufsteigender Sortierung) als das zweite Element ist.
      ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
      ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
      ; ' -----------------------------------------------------
      ; ' Rückgabewert je nach erwünschter Sortierung:
      
      If SortOrder = #AscSort
        ; Aufsteigende Sortierung zweier unterschiedlicher Strings
        If CompareMemoryString(*sEntry1, *sEntry2, #PB_String_NoCase) = #PB_String_Lower
          ProcedureReturn -1
        Else
          ProcedureReturn 1
        EndIf
      Else ; Absteigende Sortierung
        If CompareMemoryString(*sEntry1, *sEntry2, #PB_String_NoCase) = #PB_String_Greater
          ProcedureReturn -1
        Else
          ProcedureReturn 1
        EndIf
      EndIf
      
    EndProcedure
    Procedure   CompareNumbers(sEntry1.s, sEntry2.s, SortOrder.b)
      ; ' -----------------------------------------------------
      ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
      ; ' Elemente nach Maßgabe des Parameters SortOrder größer
      ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
      ; ' aufsteigender Sortierung) als das zweite Element ist.
      ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
      ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
      ; ' -----------------------------------------------------
      ; ' Rückgabewert je nach erwünschter Sortierung:
      
      If SortOrder = #AscSort
        ; Aufsteigende Sortierung zweier unterschiedlicher Zahlen
        If Val(sEntry1) < Val(sEntry2)
          ProcedureReturn -1
        Else
          ProcedureReturn 1
        EndIf
      Else ; Absteigende Sortierung
        If Val(sEntry1) > Val(sEntry2)
          ProcedureReturn -1
        Else
          ProcedureReturn 1
        EndIf
      EndIf
      
    EndProcedure
    Procedure   CompareFloat(sEntry1.s, sEntry2.s, SortOrder.b)
      ; ' -----------------------------------------------------
      ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
      ; ' Elemente nach Maßgabe des Parameters SortOrder größer
      ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
      ; ' aufsteigender Sortierung) als das zweite Element ist.
      ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
      ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
      ; ' -----------------------------------------------------
      ; ' Rückgabewert je nach erwünschter Sortierung:
      
      ReplaceString(sEntry1, ",", ".", #PB_String_InPlace, 1, 1) ; ersetze Dezimalkomma durch Punkt, damit ValF korrekt arbeitet
      ReplaceString(sEntry2, ",", ".", #PB_String_InPlace, 1, 1)
      
      If SortOrder = #AscSort
        ; Aufsteigende Sortierung zweier unterschiedlicher Zahlen
        If ValF(sEntry1) < ValF(sEntry2)
          ProcedureReturn -1
        Else
          ProcedureReturn 1
        EndIf
      Else ; Absteigende Sortierung
        If ValF(sEntry1) > ValF(sEntry2)
          ProcedureReturn -1
        Else
          ProcedureReturn 1
        EndIf
      EndIf         
      
    EndProcedure
    Procedure   CompareDate(sEntry1.s, sEntry2.s, SortOrder.b, sDateMask.s)
      ; ' -----------------------------------------------------
      ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
      ; ' Elemente nach Maßgabe des Parameters SortOrder größer
      ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
      ; ' aufsteigender Sortierung) als das zweite Element ist.
      ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
      ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
      ; ' -----------------------------------------------------
      ; ' Rückgabewert je nach erwünschter Sortierung:
      
      If SortOrder = #AscSort
        ; Aufsteigende Sortierung zweier unterschiedlicher Zahlen
        If ParseDate(sDateMask, sEntry1) < ParseDate(sDateMask, sEntry2)
          ProcedureReturn -1
        Else
          ProcedureReturn 1
        EndIf
      Else ; Absteigende Sortierung
        If ParseDate(sDateMask, sEntry1) > ParseDate(sDateMask, sEntry2)
          ProcedureReturn -1
        Else
          ProcedureReturn 1
        EndIf
      EndIf         
      
    EndProcedure
    Procedure.s LvwGetText(*ListViewSort.LVWSORT, lParam)
      ; ' -----------------------------------------------------
      ; ' Ermittelt aus dem Fensterhandle des ListView-
      ; ' Steuerelements, der in ListViewSort.SortKey
      ; ' angegebenen (nullbasierten) Spalte im ListView
      ; ' und der an CompareFunc übergebenen Werte lParam1/2
      ; ' die davon repräsentierten Zelleninhalte.
      ; ' -----------------------------------------------------
      Protected udtFindInfo.LV_FINDINFO, udtLVItem.LV_ITEM
      Protected lngIndex, *baBuffer, lngLength, RetVal.s = ""
      
      *baBuffer = AllocateMemory(512)
      
      If *baBuffer
        
        ; Auf Basis des Index den Text der Zelle auslesen:
        udtLVItem\mask=#LVIF_TEXT
        udtLVItem\iSubItem=*ListViewSort\SortKey
        udtLVItem\pszText=*baBuffer
        udtLVItem\cchTextMax=(512/SizeOf(Character))-1
        
        lngLength = SendMessage_(*ListViewSort\hWndListView, #LVM_GETITEMTEXT, lParam, @udtLVItem)
        
        ; Byte-Array in passender Länge als String-Rückgabewert kopieren:
        
        If lngLength > 0
          RetVal = PeekS(*baBuffer, lngLength)
        EndIf
        FreeMemory(*baBuffer) ; thx LittleJohn 
        
      EndIf
    
      ProcedureReturn RetVal
      
    EndProcedure
    Procedure   CompareFunc(lParam1, lParam2, lParamSort)
      ; ' -----------------------------------------------------
      ; ' Vergleichsfunktion CompareFunc
      ; ' -----------------------------------------------------
      ; ' Verglichen werden jeweils zwei Elemente der zu
      ; ' sortierenden Spalte des ListView-Steuerelements,
      ; ' die über lParam1 und lParam2 angegeben werden.
      ; ' Hierbei wird über den Rückgabewert der Funktion
      ; ' bestimmt, welches der beiden Elemente als größer
      ; ' gelten soll (hier für Aufwärtssortierung):
      ; ' * Element 1 < Element 2: Rückgabewert < 0
      ; ' * Element 1 = Element 2: Rückgabewert = 0
      ; ' * Element 1 > Element 2: Rückgabewert > 0
      ; ' -----------------------------------------------------
      Protected *ListViewSort.LVWSORT
      Protected sEntry1.s
      Protected sEntry2.s
      Protected vCompare1.s ; As Variant
      Protected vCompare2.s ; As Variant
      
      ; In lParamSort von SortListView als Long-Pointer übergebene LVWSORT-Struktur abholen, um auf deren
      ; Werte zugreifen zu können:
      
      *ListViewSort=lParamSort
      
      ; Die Werte der zu vergleichenden Elemente werden mithilfe der privaten Funktion LvwGetText aus
      ; den Angaben lParam1 und lParam2 ermittelt:
      sEntry1 = LvwGetText(*ListViewSort, lParam1)
      sEntry2 = LvwGetText(*ListViewSort, lParam2)
      
      ; Sind die Elemente gleich, kann die Funktion sofort mit dem aktuellen Rückgabewert 0
      ; verlassen werden:
      If sEntry1 = sEntry2
        ProcedureReturn 0
      EndIf
      
      ; Für die Sortierung wird unterschieden zwischen Zahlen, Fließkommazahlen und allgemeinen Strings. Hierfür
      ; steht jeweils eine separate, private Vergleichsfunktion zur Verfügung.
      
      Select *ListViewSort\SortType
        Case #SortNumeric ; ' Spalteninhalte sind Zahlen
          ProcedureReturn CompareNumbers(sEntry1, sEntry2, *ListViewSort\SortOrder)
        Case #SortFloat ; ' Spalteninhalte sind Zahlen mit Nachkommastellen
          ProcedureReturn CompareFloat(sEntry1, sEntry2, *ListViewSort\SortOrder)
        Case #SortString;  ' Spalteninhalte sind Strings
          ProcedureReturn CompareStrings(@sEntry1, @sEntry2, *ListViewSort\SortOrder)
        Case #SortDate
          ProcedureReturn CompareDate(sEntry1, sEntry2, *ListViewSort\SortOrder, *ListViewSort\DateFormat)
      EndSelect
    EndProcedure
    Procedure.s GetDateFormat(Date.s)
      
      Debug "GetDateFormat >"+Date+"<"
      
      Protected Diff.i
      
      Diff=Len(Date)-CountString(Date, "0")-CountString(Date, "1")-CountString(Date, "2")-CountString(Date, "3")-CountString(Date, "4")-CountString(Date, "5")-CountString(Date, "6")-CountString(Date, "7")-CountString(Date, "8")-CountString(Date, "9")   
      
      Select Diff
        Case 2
          If Len(Date)=10 ; Date 'dd.mm.yyyy', 'mm.dd.yyyy' or 'yyyy.mm.dd'
            
            If (Not IsNumChar(@Date, 5) And Not IsNumChar(@Date, 8)) ; yyyy.mm.dd
              ProcedureReturn "" ; faster to sort as string
              
            ElseIf (Not IsNumChar(@Date, 3) And Not IsNumChar(@Date, 6)) ; dd.mm.yyyy or mm.dd.yyyy
              If Val(Mid(Date, 4, 2))>12 ; is it mm.dd.yyyy?
                ProcedureReturn "%mm"+Mid(Date, 3, 1)+"%dd"+Mid(Date, 6, 1)+"%yyyy"
              Else ; default is dd.mm.yyyy
                ProcedureReturn "%dd"+Mid(Date, 3, 1)+"%mm"+Mid(Date, 6, 1)+"%yyyy"
              EndIf
              
            Else
              ProcedureReturn "" ; not a date - sort as string
            EndIf
          Else
            ProcedureReturn "" ; not a date - sort as string
          EndIf
          
        Case 4
          If Len(Date)=16 ;yyyy-mm-dd hh:mm, dd-mm-yyyy hh:mm or mm-dd-yyyy hh:mm
            
            If (Not IsNumChar(@Date, 5) And Not IsNumChar(@Date, 8)) ; yyyy.mm.dd xxxxx
              ProcedureReturn "" ; faster to sort as string
              
            ElseIf (Not IsNumChar(@Date, 3) And Not IsNumChar(@Date, 6)) ; dd.mm.yyyy hh:mm or mm.dd.yyyy hh:mm
              If Val(Mid(Date, 4, 2))>12 ; is it mm.dd.yyyy?
                ProcedureReturn "%mm"+Mid(Date, 3, 1)+"%dd"+Mid(Date, 6, 1)+"%yyyy"+Mid(Date, 11, 1)+"%hh"+Mid(Date, 14, 1)+"%mm"
              Else ; default is dd.mm.yyyy
                ProcedureReturn "%dd"+Mid(Date, 3, 1)+"%mm"+Mid(Date, 6, 1)+"%yyyy"+Mid(Date, 11, 1)+"%hh"+Mid(Date, 14, 1)+"%mm"
              EndIf
              
            Else
              ProcedureReturn "" ; not a date - sort as string
            EndIf
          Else
            ProcedureReturn "" ; not a date - sort as string
          EndIf            
          
        Case 5 ; 5 other chars, possibly DateTime?
          
          If Len(Date)=19 ;yyyy-mm-dd hh:mm, dd-mm-yyyy hh:mm or mm-dd-yyyy hh:mm
            
            If (Not IsNumChar(@Date, 5) And Not IsNumChar(@Date, 8)) ; yyyy.mm.dd xxxxx
              ProcedureReturn "" ; faster to sort as string
              
            ElseIf (Not IsNumChar(@Date, 3) And Not IsNumChar(@Date, 6)) ; dd.mm.yyyy hh:mm or mm.dd.yyyy hh:mm
              If Val(Mid(Date, 4, 2))>12 ; is it mm.dd.yyyy?
                ProcedureReturn "%mm"+Mid(Date, 3, 1)+"%dd"+Mid(Date, 6, 1)+"%yyyy"+Mid(Date, 11, 1)+"%hh"+Mid(Date, 14, 1)+"%mm"+Mid(Date, 17, 1)+"%ss"
              Else ; default is dd.mm.yyyy
                ProcedureReturn "%dd"+Mid(Date, 3, 1)+"%mm"+Mid(Date, 6, 1)+"%yyyy"+Mid(Date, 11, 1)+"%hh"+Mid(Date, 14, 1)+"%mm"+Mid(Date, 17, 1)+"%ss"
              EndIf
              
            Else
              ProcedureReturn "" ; not a date - sort as string
            EndIf
          Else
            ProcedureReturn "" ; not a date - sort as string
          EndIf
          
        Default
          ProcedureReturn ""
      EndSelect
      
    EndProcedure
    Procedure   SortListView(hWndListView, SortKey, SortType.b, SortOrder.b)
      ; ' -----------------------------------------------------
      ; ' Öffentlich aufzurufende Prozedur SortListView, die
      ; ' für die individuelle Sortierung einer ListView-Spalte
      ; ' sorgt.
      ; ' -----------------------------------------------------
      ; ' hWndListView: Fensterhandle des ListView-Steuerelements
      ; ' SortKey:      Spalte (nullbasiert), die sortiert werden
      ; '               soll (= Spaltennummer - 1).
      ; ' SortType:     stString, um Strings zu sortieren (Standardwert)
      ; '               stDate, um Datumsangaben zu sortieren
      ; '               stNumeric, um Zahlen zu sortieren
      ; ' SortOrder:    lvwAscending für aufsteigende Sortierung (Std.)
      ; '               lvwDescending für absteigende Sortierung
      ; ' -----------------------------------------------------
      
      Protected udtLVWSORT.LVWSORT
      Protected sDateFormat.s, sTemp.s, GadId.i
      
      If SortType = #SortDate
        GadId = GetDlgCtrlID_(hWndListView)
        sDateFormat = GetDateFormat(GetGadgetItemText(GadId, 0, SortKey))
        
        If sDateFormat = ""
          SortType = #SortString
        Else
          sTemp = GetDateFormat(GetGadgetItemText(GadId, CountGadgetItems(GadId)-1, SortKey))
          If sTemp=""
            SortType=#SortString
          Else
            If sTemp<>sDateFormat
              If Left(sTemp, 3)="%mm" ; new format starts with %mm (.dd.yyyy) - if this US format is detected it has higher prio
                sDateFormat=sTemp
              EndIf
            EndIf
            sTemp=GetDateFormat(GetGadgetItemText(GadId, CountGadgetItems(GadId)/2, SortKey))
            If sTemp=""
              SortType=#SortString
            Else
              If sTemp<>sDateFormat
                If Left(sTemp, 3)="%mm" ; new format starts with %mm (.dd.yyyy) - if this US format is detected it has higher prio
                  sDateFormat=sTemp
                EndIf
              EndIf
            EndIf   
          EndIf         
        EndIf
        udtLVWSORT\DateFormat=sDateFormat
        Debug "Final DateFormat >"+sDateFormat+"<"
      EndIf
      
      ; Übergebene Informationen in einer LVWSORT-Struktur zusammenfassen:
      udtLVWSORT\hWndListView=hWndListView
      udtLVWSORT\SortKey=SortKey
      udtLVWSORT\SortOrder=SortOrder
      udtLVWSORT\SortType=SortType   
      
      ; Eigene Sortierfunktionalität in der Funktion CompareFunc verwenden: Die Informationen der
      ; LVWSORT-Struktur wird mithilfe eines Zeigers auf die Variable udtLVWSORT beigegeben:
      SendMessage_(hWndListView, #LVM_SORTITEMSEX, @udtLVWSORT, @CompareFunc())
      
    EndProcedure
    Procedure.b DetectOrderType(sText.s)
      
      Protected Diff
      
      Diff=Len(sText)-CountString(sText, "0")-CountString(sText, "1")-CountString(sText, "2")-CountString(sText, "3")-CountString(sText, "4")-CountString(sText, "5")-CountString(sText, "6")-CountString(sText, "7")-CountString(sText, "8")-CountString(sText, "9")   
      
      Select Diff
        Case 0 ; es sind nur Ziffern
          ProcedureReturn #SortNumeric
          
        Case 1 ; nur 1 anderes Zeichen
          If (CountString(sText, ",")>0 Or CountString(sText, ".")>0)
            ProcedureReturn #SortFloat
          ElseIf (Left(sText, 1)="$" Or Left(sText, 1)="%") ; es ist eine HEX oder Binär Zahl
            ProcedureReturn #SortNumeric
          Else
            ProcedureReturn #SortString
          EndIf
          
        Case 2 ; 2 andere Zeichen - evtl. Datum?
          
          If (Len(sText)=10 And
              Not IsNumChar(@sText, 3) And Not IsNumChar(@sText, 6))
            ; dd-mm-yyyy or mm-dd-yyyy
            ProcedureReturn #SortDate
          Else
            ; yyyy-mm-dd
            ProcedureReturn #SortString
          EndIf
          
        Case 4 ; 4 other chars, possibly DateTime?
          
          If (Len(sText)=16 And
              Not IsNumChar(@sText, 3) And Not IsNumChar(@sText, 6) And
              Not IsNumChar(@sText, 11) And Not IsNumChar(@sText, 14))
            ;dd-mm-yyyy hh:mm or mm-dd-yyyy hh:mm
            ProcedureReturn #SortDate
          Else
            ProcedureReturn #SortString
          EndIf
          
        Case 5 ; 5 other chars, possibly DateTime?
          
          If (Len(sText)=19 And
              Not IsNumChar(@sText, 3) And Not IsNumChar(@sText, 6) And
              Not IsNumChar(@sText, 11) And Not IsNumChar(@sText, 14) And Not IsNumChar(@sText, 17))
            ;dd-mm-yyyy hh:mm:ss or mm-dd-yyyy hh:mm:ss
            ProcedureReturn #SortDate
          Else
            ProcedureReturn #SortString
          EndIf         
          
        Default
          ProcedureReturn #SortString
          
      EndSelect
      
    EndProcedure
    Procedure   SortColumn(Gadget, Column, OrderType.b = #SortAutoDetect)
      
      Protected ColCnt, Order, iStartT, iEndT, Temp.b
      
      If Not IsGadget(Gadget) : ProcedureReturn #False : EndIf
      
      Debug "LIG_SortColumn >"+Str(Gadget)+"< Spalte >"+Str(Column)+"<"
      
      Select GetSortOrder(Gadget, Column)
        Case #NoSort, #DescSort
          Order = #AscSort
        Case #AscSort
          Order = #DescSort
      EndSelect
      
      iStartT=ElapsedMilliseconds()
      
      If OrderType = #SortAutoDetect ; detect it automatically - check first, last and middle item of list
        OrderType = DetectOrderType(GetGadgetItemText(Gadget, 0, Column))
        If (OrderType = DetectOrderType(GetGadgetItemText(Gadget, CountGadgetItems(Gadget)-1, Column)))
          If (OrderType <> DetectOrderType(GetGadgetItemText(Gadget, CountGadgetItems(Gadget)/2, Column)))
            Debug "Different OrderType - use SortString 2"
            OrderType = #SortString
          EndIf
        Else
          Debug "Different OrderType - use SortString"
          OrderType = #SortString
        EndIf
      EndIf   
      
      SortListView(GadgetID(Gadget), Column, OrderType, Order)
      
      iEndT  =ElapsedMilliseconds()
      
      Debug "Dauer >"+StrF( (iEndT-iStartT)/1000, 2)+"<"
      
      SetSortIcon(Gadget, Column, Order)
      
      If (GetGadgetState(Gadget) > -1)
        EnsureVisible(Gadget, GetGadgetState(Gadget))
      EndIf
      
    EndProcedure
    
    ;- ##### ListIconGadget Sort Ende ######
    
    Procedure ColumnClickCallback(hWnd, uMsg, wParam, lParam)
      
      Protected *msg.NM_LISTVIEW
      
      If uMsg = #WM_NOTIFY
        *msg = lParam
        If *msg\hdr\code = #LVN_COLUMNCLICK                     
          SortColumn(GetDlgCtrlID_(*msg\hdr\hwndfrom), *msg\iSubItem)
        EndIf
      EndIf
      
      ProcedureReturn #PB_ProcessPureBasicEvents
      
    EndProcedure
    
    Procedure Enable()
      SetWindowCallback(@ColumnClickCallback())  
    EndProcedure
    
  EndModule
  
  DisableExplicit
  
CompilerElse
  
  MessageRequester("PureBasic", "PureBasic V5.20 or higher needed!") : End
  
CompilerEndIf
; --- Example
CompilerIf #PB_Compiler_IsMainFile
  
Enumeration #PB_Compiler_EnumerationValue
  #MainWin
EndEnumeration
Enumeration #PB_Compiler_EnumerationValue
  #ListIcon
EndEnumeration

Procedure OpenMainWin()
  OpenWindow(#MainWin, 0, 0, 850, 500, "ListIconGadget-SortExample", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  
  ListIconGadget(#ListIcon, 10, 10, 830, 480, "COL 0", 150, #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect)
  AddGadgetColumn(#ListIcon, 1, "COL 1", 100)
  AddGadgetColumn(#ListIcon, 2, "COL 2", 100)
  AddGadgetColumn(#ListIcon, 3, "COL 3 (NUM)", 100)
  AddGadgetColumn(#ListIcon, 4, "COL 4 (FLOAT)", 100)
  AddGadgetColumn(#ListIcon, 5, "COL 5 (DATE)", 100)
  AddGadgetColumn(#ListIcon, 6, "COL 6 (DATETIME)", 150)
EndProcedure

Procedure MainWin_Events(event)
  Select event
    Case #PB_Event_CloseWindow
      ProcedureReturn #False
      
  EndSelect
  ProcedureReturn #True
EndProcedure


Define iEvent.i
Define iEventWindow.i
Define iCloseAll.i
Define a.i, x.i
Define A$, B$, C$, D$, E$, F$, G$

OpenMainWin()

; generate Test Values:

For a = 0 To 1000
  
  A$ = "COL 1, Row "+RSet(Str(  a  ),6,"0")+Chr(10)
  
  B$ =RSet(Str(Random($FFFF)),5,"0")+Chr(10)
  
  C$ ="$"+RSet(Hex(Random($7FFFFFFF)),8,"0")+Chr(10)
  
  Select Random(5, 1)
    Case 1
      D$=Str(Random(9, 0))
    Case 2
      D$=Str(Random(99, 10))
    Case 3
      D$=Str(Random(999, 100))
    Case 4
      D$=Str(Random(9999, 1000))
    Case 5
      D$=Str(Random(99999, 10000))
  EndSelect         
  D$+Chr(10)
  
  Select Random(5, 1)
    Case 1
      E$=Str(Random(9, 0))+","+Str(Random(99, 0))
    Case 2
      E$=Str(Random(99, 10))+","+Str(Random(99, 0))
    Case 3
      E$=Str(Random(999, 100))+","+Str(Random(99, 0))
    Case 4
      E$=Str(Random(9999, 1000))+","+Str(Random(99, 0))
    Case 5
      E$=Str(Random(99999, 10000))+","+Str(Random(99, 0))
  EndSelect
  E$+Chr(10)
  
  F$=FormatDate("%dd.%mm.%yyyy", Random(Date(), 0))+Chr(10)
  
  G$=FormatDate("%mm-%dd-%yyyy %hh:%mm:%ss", Random(Date(), 0))
  
  AddGadgetItem(#ListIcon, a, A$+B$+C$+D$+E$+F$+G$)
  
Next

nalorLIG::Enable()

Repeat
  
  iEvent = WaitWindowEvent()
  iEventWindow=EventWindow()
  
  Select iEventWindow
    Case #MainWin
      If (Not MainWin_Events(iEvent))
        iCloseAll=#True
      EndIf
  EndSelect
  
Until iCloseAll=#True 

CompilerEndIf
PureBasic 6.10 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
English is not my native language... (I often use DeepL to translate my texts.)
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5353
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: ListIconGadget with Sort and Arrow Icons

Post by Kwai chang caine »

Me too, i have always the problem with ASCII and XP who crash the compiler in 5.11 :(
But in 5.20 with your code that work
Thanks BISONTE and also obviously NALOR 8)
ImageThe happiness is a road...
Not a destination
nalor
Enthusiast
Enthusiast
Posts: 115
Joined: Thu Apr 02, 2009 9:48 pm

Re: ListIconGadget with Sort and Arrow Icons

Post by nalor »

Fixed a small bug - updated code in the first post and also added a link to my enhanced version in another post!
linkerstorm
User
User
Posts: 47
Joined: Sun Feb 18, 2007 11:57 am

Re: ListIconGadget with Sort and Arrow Icons

Post by linkerstorm »

Hi.

Very good sort implementation.

Using it, a bug exists : in the "GetDateFormat" procedure, be aware to use "%ii" for minutes, instead of "%mm" actually, that stands for monthes.

Anyway, thanks for the great module.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5353
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: ListIconGadget with Sort and Arrow Icons

Post by Kwai chang caine »

Works great W7 v5.23
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
Little John
Addict
Addict
Posts: 4527
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: ListIconGadget with Sort and Arrow Icons

Post by Little John »

@Bisonte:
I also wanted to have this as module.
I have nothing else but your function Enable() declared public, and it works fine. :-)
User avatar
Bisonte
Addict
Addict
Posts: 1232
Joined: Tue Oct 09, 2007 2:15 am

Re: ListIconGadget with Sort and Arrow Icons

Post by Bisonte »

Two years later.... with some more experience... yes you're right :mrgreen:
If I look over the source... I think a function to remove the callback must be written ;)
PureBasic 6.10 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
English is not my native language... (I often use DeepL to translate my texts.)
dige
Addict
Addict
Posts: 1252
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: ListIconGadget with Sort and Arrow Icons

Post by dige »

Since PB4.50 I can't use Gnozals PureLVSort Lib anymore and search
now for an replacement, to have the functionality of a column sort for the ListIconGadget().

@Nalor, Bisonte: Would you recommend to use your example or are there, from your point of view, still important things that need to be done?

Ciao dige
"Daddy, I'll run faster, then it is not so far..."
dige
Addict
Addict
Posts: 1252
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: ListIconGadget with Sort and Arrow Icons

Post by dige »

Now closer to PureLVSort ;-)

Bugfix: GetDateFormat()
Added: new SortType #SortFileSize, #SortNone
Added: SetSortOrderType ()
Added: DisableSort ()

Code: Select all

; ==================================================================================================
; --- ListIconGadget Extras & Sort
; --- ----------------------------------------------------------------------------------------------
; --- File            : module_nalorLIG.pbi
; --- OriginalAuthor  : nalor
; --- modified        : bisonte (change to new module system of pb)
; --- modified        : dige (add SetSortOrderType, bugfix parsedate, Add SortType FileSize, DisableSort)
; --- Link            : http://purebasic.fr/english/viewtopic.php?f=12&t=55085   
; --- Date            : Nov 12, 2015
; --- Compiler        : PureBasic 5.40 (Windows - x86)
; --- Target OS       : Windows
; --- Version         : 1.1
; ==================================================================================================
; --- Module : nalorLIG
; --- Remark : If WindowsXP is used - Disable Debugger or use Unicode Flag only
; --- Changed with edel's help to subclass
; ==================================================================================================
CompilerIf #PB_Compiler_Version => 520
  
  EnableExplicit
  
  CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
    MessageRequester("PureBasic", "Windows only! Sorry.") : End
  CompilerEndIf
  
  DeclareModule nalorLIG
  
  Enumeration ; Type of Column Sort
    #SortAutoDetect
    #SortString
    #SortNumeric
    #SortFloat
    #SortDate
    #SortFileSize
    #SortNone
  EndEnumeration
  Enumeration ; Column Sort States
    #NoSort   ; keine Sortierung
    #AscSort  ; Aufsteigende Sortierung
    #DescSort ; Absteigende Sortierung
  EndEnumeration
  
  Structure LVWSORT
    hWndListView.i ; Fensterhandle des ListView-Controls
    SortKey.i ; Spalte, die sortiert werden soll
    SortType.b ; Typ der zu sortierenden Daten
    SortOrder.b ; Sortierrichtung
    DateFormat.s ; Mask for 'ParseDate'
  EndStructure
  
  Declare   Enable(gadget)
  Declare   SetSortOrderType(gadget, Column, SortType)
  Declare   DisableSort (gadget, disable.b = #TRUE)
  
  Structure LVWSTATUS
    Activ.i
    SortOrderType.s
  EndStructure
  
  Global NewMap SortOrder.LVWSTATUS()
  
  EndDeclareModule
  Module        nalorLIG
  
  ;{--- Improvement by edel
  
  ; Subclass Window
  
  Import ""
    GetProcAddress(hmod, s.p-ascii)
  EndImport
  
  Prototype pSetWindowSubclass(hwnd, *Proc, *Id, *RefData)
  Prototype pDefSubclassProc(hwnd, msg, wParam, lParam)
  Prototype pRemoveWindowSubclass(hwnd, *Proc, *Id)
  Prototype pGetWindowSubclass(hwnd, *Proc, *Id, *RefData)
  
  Procedure SetWindowSubclass(hwnd, *Proc, *Id, *RefData)
    Protected Comctl32 = GetModuleHandle_("Comctl32.dll")   
    Protected func.pSetWindowSubclass = GetProcAddress(Comctl32, "SetWindowSubclass")
    ProcedureReturn func(hwnd, *Proc, *Id, *RefData)
  EndProcedure
  Procedure DefSubclassProc(hwnd, msg, wParam, lParam)
    Protected Comctl32 = GetModuleHandle_("Comctl32.dll")
    Protected func.pDefSubclassProc = GetProcAddress(Comctl32, "DefSubclassProc")
    ProcedureReturn func(hwnd, msg, wParam, lParam)
  EndProcedure
  Procedure RemoveWindowSubclass(hwnd, *Proc, *Id)
    Protected Comctl32 = GetModuleHandle_("Comctl32.dll")
    Protected func.pRemoveWindowSubclass = GetProcAddress(Comctl32, "RemoveWindowSubclass")
    ProcedureReturn func(hwnd, *Proc, *Id)
  EndProcedure
  Procedure GetWindowSubclass(hwnd, *Proc, *Id, *RefData)
    Protected Comctl32 = GetModuleHandle_("Comctl32.dll")
    Protected func.pGetWindowSubclass = GetProcAddress(Comctl32, "GetWindowSubclass")
    ProcedureReturn func(hwnd, *Proc, *Id, *RefData)
  EndProcedure
  ;}
  
  Procedure   GethWnd(id)
    
    Protected hwnd = #False
    
    If IsGadget(id)
      hwnd = GadgetID(id)
    Else
      If IsWindow_(id)
        hwnd = id
      EndIf
    EndIf
    
    ProcedureReturn hwnd
    
  EndProcedure
  
  ;- ++++++ ListIconGadget Tools Start ++++++
  
  Procedure   AlignColumn(gadget, index, Format)
    
    ; by Danilo, 15.12.2003 - english chat (for 'Karbon')
    ; 20130615..nalor..modified
    ; change text alignment for columns
    ; #LVCFMT_LEFT / #LVCFMT_CENTER / #LVCFMT_RIGHT
    
    Protected lvc.LV_COLUMN, hwnd = GethWnd(gadget)
    
    If hwnd
      
      lvc\Mask = #LVCF_FMT
      lvc\fmt = Format
      
      SendMessage_(hwnd, #LVM_SETCOLUMN, index, @lvc)
      
      ProcedureReturn #TRUE
      
    EndIf
    
    ProcedureReturn #False
    
  EndProcedure
  Procedure   SetColumnWidth(gadget, index, New_Width)
    
    ; by Danilo, 15.12.2003 - english chat (for 'Karbon')
    ;
    ; change column header width
    ;
    
    Protected hwnd = GethWnd(gadget)
    
    If hwnd
      
      SendMessage_(hwnd, #LVM_SETCOLUMNWIDTH, index, New_Width)
      ProcedureReturn #TRUE
      
    EndIf
    
    ProcedureReturn #False
    
  EndProcedure
  Procedure   SetSortIcon(gadget, Column, SortOrder)
    
    ; http://stackoverflow.com/questions/254129/how-To-i-display-a-sort-arrow-in-the-header-of-a-List-view-column-using-c   
    
    Protected ColumnHeader
    Protected ColumnCount
    Protected hditem.HD_ITEM
    Protected Cnt
    Protected hwnd = GethWnd(gadget)
    
    If hwnd
      
      ColumnHeader = SendMessage_(hwnd, #LVM_GETHEADER, 0, 0)
      ColumnCount  = SendMessage_(ColumnHeader, #HDM_GETITEMCOUNT, 0, 0)
      
      For Cnt = 0 To ColumnCount - 1
        
        hditem\Mask=#HDI_FORMAT
        
        If SendMessage_(ColumnHeader, #HDM_GETITEM, Cnt, @hditem) = 0
          Debug "ERROR! LIG_SetSortIcon 1"
        EndIf
        
        hditem\Mask=#HDI_FORMAT
        
        If (Cnt = Column And SortOrder <> #NoSort)
          
          Select SortOrder
              
            Case #AscSort ; wenn aufsteigend sortiert werden soll
              hditem\fmt& ~#HDF_SORTDOWN
              hditem\fmt|#HDF_SORTUP
              ;Debug "sortup"
              
            Case #DescSort
              hditem\fmt& ~#HDF_SORTUP
              hditem\fmt|#HDF_SORTDOWN               
              ;Debug "sortdown"
              
          EndSelect
          
        Else
          
          hditem\fmt& ~#HDF_SORTUP
          hditem\fmt& ~#HDF_SORTDOWN
          
        EndIf
        
        If (SendMessage_(ColumnHeader, #HDM_SETITEM, Cnt, @hditem) = 0)
          Debug "ERROR! LIG_SetSortIcon 2"
        EndIf
        
      Next Cnt
      
      ProcedureReturn #TRUE
      
    EndIf
    
    ProcedureReturn #False
    
  EndProcedure
  Procedure GetSortState (gadget)
    Protected Result.i
    
    If FindMapElement(SortOrder(), Str(gadget))
      Result = SortOrder()\Activ
    EndIf  
    
    ProcedureReturn Result
  EndProcedure
  Procedure.b GetSortOrder(gadget, Column)
    
    Protected ColumnHeader
    Protected hditem.HD_ITEM
    Protected RetVal.b = -1
    Protected hwnd = GethWnd(gadget)
    
    If hwnd
      
      ColumnHeader = SendMessage_(hwnd, #LVM_GETHEADER, 0, 0)
      
      hditem\Mask=#HDI_FORMAT
      
      If SendMessage_(ColumnHeader, #HDM_GETITEM, Column, @hditem)
        
        If (hditem\fmt&#HDF_SORTUP)=#HDF_SORTUP
          
          ;Debug "sortup"
          RetVal=#AscSort
          
        ElseIf (hditem\fmt&#HDF_SORTDOWN)=#HDF_SORTDOWN
          
          ;Debug "sortdown"
          RetVal=#DescSort
          
        Else
          
          ;Debug "keine sortierung"
          RetVal=#NoSort
          
        EndIf
        
      Else
        
        Debug "ERROR! LIG_GetSortOrder"
        RetVal=-1
        
      EndIf
      
    EndIf
    
    ProcedureReturn RetVal
    
  EndProcedure
  Procedure   EnsureVisible(gadget, Line)
    ; makes sure the line is visible
    
    Protected hwnd = GethWnd(gadget)
    If hwnd
      SendMessage_(hwnd, #LVM_ENSUREVISIBLE, Line, #TRUE)
      ProcedureReturn #TRUE
    EndIf
    ProcedureReturn #False
    
  EndProcedure
  
  ;- ##### ListIconGadget Tools Ende #####
  
  ;- ++++++ ListIconGadget Sort Start ++++++
  
  ; http://msdn.microsoft.com/de-de/library/bb979183.aspx   
  ; Die Struktur LVWSORT enthält Informationen über das zu sortierende ListView-Steuerelement, die Spalte,
  ; nach der sortiert werden soll, sowie die gewünschte Sortierrichtung.
  Procedure.q StringToBytes (sEntry.s)
    Protected Result.q
    
    Select UCase(Right(sEntry, 2))
      Case "TB" : Result = ValF(sEntry) * 1099511627776
      Case "GB" : Result = ValF(sEntry) * 1073741824
      Case "MB" : Result = ValF(sEntry) * 1048576
      Case "KB" : Result = ValF(sEntry) * 1024
      Default   : Result = Val(sEntry)
    EndSelect
    
    ProcedureReturn Result
  EndProcedure
  
  
  Procedure.b IsNumChar(*Text, Position = 1)
    Select Asc(PeekS(*Text+(Position-1)*SizeOf(character), 1))
      Case 48 To 57
        ProcedureReturn #TRUE
      Default
        ProcedureReturn #False
    EndSelect
  EndProcedure
  Procedure   CompareStrings(*sEntry1, *sEntry2, SortOrder.b)
    ; ' -----------------------------------------------------
    ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
    ; ' Elemente nach Maßgabe des Parameters SortOrder größer
    ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
    ; ' aufsteigender Sortierung) als das zweite Element ist.
    ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
    ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
    ; ' -----------------------------------------------------
    ; ' Rückgabewert je nach erwünschter Sortierung:
    
    If SortOrder = #AscSort
      ; Aufsteigende Sortierung zweier unterschiedlicher Strings
      If CompareMemoryString(*sEntry1, *sEntry2, #PB_String_NoCase) = #PB_String_Lower
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    Else ; Absteigende Sortierung
      If CompareMemoryString(*sEntry1, *sEntry2, #PB_String_NoCase) = #PB_String_Greater
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    EndIf
    
  EndProcedure
  Procedure   CompareNumbers(sEntry1.s, sEntry2.s, SortOrder.b)
    ; ' -----------------------------------------------------
    ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
    ; ' Elemente nach Maßgabe des Parameters SortOrder größer
    ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
    ; ' aufsteigender Sortierung) als das zweite Element ist.
    ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
    ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
    ; ' -----------------------------------------------------
    ; ' Rückgabewert je nach erwünschter Sortierung:
    
    If SortOrder = #AscSort
      ; Aufsteigende Sortierung zweier unterschiedlicher Zahlen
      If Val(sEntry1) < Val(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    Else ; Absteigende Sortierung
      If Val(sEntry1) > Val(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    EndIf
    
  EndProcedure
  Procedure   CompareFloat(sEntry1.s, sEntry2.s, SortOrder.b)
    ; ' -----------------------------------------------------
    ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
    ; ' Elemente nach Maßgabe des Parameters SortOrder größer
    ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
    ; ' aufsteigender Sortierung) als das zweite Element ist.
    ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
    ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
    ; ' -----------------------------------------------------
    ; ' Rückgabewert je nach erwünschter Sortierung:
    
    ReplaceString(sEntry1, ",", ".", #PB_String_InPlace, 1, 1) ; ersetze Dezimalkomma durch Punkt, damit ValF korrekt arbeitet
    ReplaceString(sEntry2, ",", ".", #PB_String_InPlace, 1, 1)
    
    If SortOrder = #AscSort
      ; Aufsteigende Sortierung zweier unterschiedlicher Zahlen
      If ValF(sEntry1) < ValF(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    Else ; Absteigende Sortierung
      If ValF(sEntry1) > ValF(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    EndIf         
    
  EndProcedure
  
  Procedure   CompareFileSize(sEntry1.s, sEntry2.s, SortOrder.b)
    ; ' -----------------------------------------------------
    ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
    ; ' Elemente nach Maßgabe des Parameters SortOrder größer
    ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
    ; ' aufsteigender Sortierung) als das zweite Element ist.
    ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
    ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
    ; ' -----------------------------------------------------
    ; ' Rückgabewert je nach erwünschter Sortierung:
    
    If SortOrder = #AscSort
      ; Aufsteigende Sortierung zweier unterschiedlicher Zahlen
      If StringToBytes(sEntry1) < StringToBytes(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    Else ; Absteigende Sortierung
      If StringToBytes(sEntry1) > StringToBytes(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    EndIf         
    
  EndProcedure
  
  Procedure   CompareDate(sEntry1.s, sEntry2.s, SortOrder.b, sDateMask.s)
    ; ' -----------------------------------------------------
    ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
    ; ' Elemente nach Maßgabe des Parameters SortOrder größer
    ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
    ; ' aufsteigender Sortierung) als das zweite Element ist.
    ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
    ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
    ; ' -----------------------------------------------------
    ; ' Rückgabewert je nach erwünschter Sortierung:
    
    If SortOrder = #AscSort
      ; Aufsteigende Sortierung zweier unterschiedlicher Zahlen
      If ParseDate(sDateMask, sEntry1) < ParseDate(sDateMask, sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    Else ; Absteigende Sortierung
      If ParseDate(sDateMask, sEntry1) > ParseDate(sDateMask, sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    EndIf         
    
  EndProcedure
  Procedure.s LvwGetText(*ListViewSort.LVWSORT, lParam)
    ; ' -----------------------------------------------------
    ; ' Ermittelt aus dem Fensterhandle des ListView-
    ; ' Steuerelements, der in ListViewSort.SortKey
    ; ' angegebenen (nullbasierten) Spalte im ListView
    ; ' und der an CompareFunc übergebenen Werte lParam1/2
    ; ' die davon repräsentierten Zelleninhalte.
    ; ' -----------------------------------------------------
    Protected udtFindInfo.LV_FINDINFO, udtLVItem.LV_ITEM
    Protected lngIndex, *baBuffer, lngLength, RetVal.s = ""
    
    *baBuffer = AllocateMemory(512)
    
    If *baBuffer
      
      ; Auf Basis des Index den Text der Zelle auslesen:
      udtLVItem\Mask=#LVIF_TEXT
      udtLVItem\iSubItem=*ListViewSort\SortKey
      udtLVItem\pszText=*baBuffer
      udtLVItem\cchTextMax=(512/SizeOf(character))-1
      
      lngLength = SendMessage_(*ListViewSort\hWndListView, #LVM_GETITEMTEXT, lParam, @udtLVItem)
      
      ; Byte-Array in passender Länge als String-Rückgabewert kopieren:
      
      If lngLength > 0
        RetVal = PeekS(*baBuffer, lngLength)
      EndIf
      FreeMemory(*baBuffer) ; thx LittleJohn
      
    EndIf
    
    ProcedureReturn RetVal
    
  EndProcedure
  Procedure   CompareFunc(lParam1, lParam2, lParamSort)
    ; ' -----------------------------------------------------
    ; ' Vergleichsfunktion CompareFunc
    ; ' -----------------------------------------------------
    ; ' Verglichen werden jeweils zwei Elemente der zu
    ; ' sortierenden Spalte des ListView-Steuerelements,
    ; ' die über lParam1 und lParam2 angegeben werden.
    ; ' Hierbei wird über den Rückgabewert der Funktion
    ; ' bestimmt, welches der beiden Elemente als größer
    ; ' gelten soll (hier für Aufwärtssortierung):
    ; ' * Element 1 < Element 2: Rückgabewert < 0
    ; ' * Element 1 = Element 2: Rückgabewert = 0
    ; ' * Element 1 > Element 2: Rückgabewert > 0
    ; ' -----------------------------------------------------
    Protected *ListViewSort.LVWSORT
    Protected sEntry1.s
    Protected sEntry2.s
    Protected vCompare1.s ; As Variant
    Protected vCompare2.s ; As Variant
    
    ; In lParamSort von SortListView als Long-Pointer übergebene LVWSORT-Struktur abholen, um auf deren
    ; Werte zugreifen zu können:
    
    *ListViewSort=lParamSort
    
    ; Die Werte der zu vergleichenden Elemente werden mithilfe der privaten Funktion LvwGetText aus
    ; den Angaben lParam1 und lParam2 ermittelt:
    sEntry1 = LvwGetText(*ListViewSort, lParam1)
    sEntry2 = LvwGetText(*ListViewSort, lParam2)
    
    ; Sind die Elemente gleich, kann die Funktion sofort mit dem aktuellen Rückgabewert 0
    ; verlassen werden:
    If sEntry1 = sEntry2
      ProcedureReturn 0
    EndIf
    
    ; Für die Sortierung wird unterschieden zwischen Zahlen, Fließkommazahlen und allgemeinen Strings. Hierfür
    ; steht jeweils eine separate, private Vergleichsfunktion zur Verfügung.
    
    Select *ListViewSort\SortType
      Case #SortNumeric ; ' Spalteninhalte sind Zahlen
        ProcedureReturn CompareNumbers(sEntry1, sEntry2, *ListViewSort\SortOrder)
      Case #SortFloat ; ' Spalteninhalte sind Zahlen mit Nachkommastellen
        ProcedureReturn CompareFloat(sEntry1, sEntry2, *ListViewSort\SortOrder)
      Case #SortString;  ' Spalteninhalte sind Strings
        ProcedureReturn CompareStrings(@sEntry1, @sEntry2, *ListViewSort\SortOrder)
      Case #SortDate
        ProcedureReturn CompareDate(sEntry1, sEntry2, *ListViewSort\SortOrder, *ListViewSort\DateFormat)
      Case #SortFileSize;  ' Spalteninhalte sind Floats und Strings
        ProcedureReturn CompareFileSize(sEntry1, sEntry2, *ListViewSort\SortOrder)

    EndSelect
  EndProcedure
  Procedure.s GetDateFormat(date.s)
    
    ;Debug "GetDateFormat >"+Date+"<"
    Protected Diff.i
    
    Diff=Len(date)-CountString(date, "0")-CountString(date, "1")-CountString(date, "2")-CountString(date, "3")-CountString(date, "4")-CountString(date, "5")-CountString(date, "6")-CountString(date, "7")-CountString(date, "8")-CountString(date, "9")   
    
    Select Diff
      Case 2
        If Len(date)=10 ; Date 'dd.mm.yyyy', 'mm.dd.yyyy' or 'yyyy.mm.dd'
          
          If (Not IsNumChar(@date, 5) And Not IsNumChar(@date, 8)) ; yyyy.mm.dd
            ProcedureReturn "" ; faster to sort as string
            
          ElseIf (Not IsNumChar(@date, 3) And Not IsNumChar(@date, 6)) ; dd.mm.yyyy or mm.dd.yyyy
            If Val(Mid(date, 4, 2))>12 ; is it mm.dd.yyyy?
              ProcedureReturn "%mm"+Mid(date, 3, 1)+"%dd"+Mid(date, 6, 1)+"%yyyy"
            Else ; default is dd.mm.yyyy
              ProcedureReturn "%dd"+Mid(date, 3, 1)+"%mm"+Mid(date, 6, 1)+"%yyyy"
            EndIf
            
          Else
            ProcedureReturn "" ; not a date - sort as string
          EndIf
        Else
          ProcedureReturn "" ; not a date - sort as string
        EndIf
        
      Case 4
        If Len(date)=16 ;yyyy-mm-dd hh:mm, dd-mm-yyyy hh:mm or mm-dd-yyyy hh:mm
          
          If (Not IsNumChar(@date, 5) And Not IsNumChar(@date, 8)) ; yyyy.mm.dd xxxxx
            ProcedureReturn "" ; faster to sort as string
            
          ElseIf (Not IsNumChar(@date, 3) And Not IsNumChar(@date, 6)) ; dd.mm.yyyy hh:mm or mm.dd.yyyy hh:mm
            If Val(Mid(date, 4, 2))>12 ; is it mm.dd.yyyy?
              ProcedureReturn "%mm"+Mid(date, 3, 1)+"%dd"+Mid(date, 6, 1)+"%yyyy"+Mid(date, 11, 1)+"%hh"+Mid(date, 14, 1)+"%ii"
            Else ; default is dd.mm.yyyy
              ProcedureReturn "%dd"+Mid(date, 3, 1)+"%mm"+Mid(date, 6, 1)+"%yyyy"+Mid(date, 11, 1)+"%hh"+Mid(date, 14, 1)+"%ii"
            EndIf
            
          Else
            ProcedureReturn "" ; not a date - sort as string
          EndIf
        Else
          ProcedureReturn "" ; not a date - sort as string
        EndIf           
        
      Case 5 ; 5 other chars, possibly DateTime?
        
        If Len(date)=19 ;yyyy-mm-dd hh:mm, dd-mm-yyyy hh:mm or mm-dd-yyyy hh:mm
          
          If (Not IsNumChar(@date, 5) And Not IsNumChar(@date, 8)) ; yyyy.mm.dd xxxxx
            ProcedureReturn "" ; faster to sort as string
            
          ElseIf (Not IsNumChar(@date, 3) And Not IsNumChar(@date, 6)) ; dd.mm.yyyy hh:mm or mm.dd.yyyy hh:mm
            If Val(Mid(date, 4, 2))>12 ; is it mm.dd.yyyy?
              ProcedureReturn "%mm"+Mid(date, 3, 1)+"%dd"+Mid(date, 6, 1)+"%yyyy"+Mid(date, 11, 1)+"%hh"+Mid(date, 14, 1)+"%ii"+Mid(date, 17, 1)+"%ss"
            Else ; default is dd.mm.yyyy
              ProcedureReturn "%dd"+Mid(date, 3, 1)+"%mm"+Mid(date, 6, 1)+"%yyyy"+Mid(date, 11, 1)+"%hh"+Mid(date, 14, 1)+"%ii"+Mid(date, 17, 1)+"%ss"
            EndIf
            
          Else
            ProcedureReturn "" ; not a date - sort as string
          EndIf
        Else
          ProcedureReturn "" ; not a date - sort as string
        EndIf
        
      Default
        ProcedureReturn ""
    EndSelect
    
  EndProcedure
  Procedure   SortListView(hWndListView, SortKey, SortType.b, SortOrder.b)
    ; ' -----------------------------------------------------
    ; ' Öffentlich aufzurufende Prozedur SortListView, die
    ; ' für die individuelle Sortierung einer ListView-Spalte
    ; ' sorgt.
    ; ' -----------------------------------------------------
    ; ' hWndListView: Fensterhandle des ListView-Steuerelements
    ; ' SortKey:      Spalte (nullbasiert), die sortiert werden
    ; '               soll (= Spaltennummer - 1).
    ; ' SortType:     stString, um Strings zu sortieren (Standardwert)
    ; '               stDate, um Datumsangaben zu sortieren
    ; '               stNumeric, um Zahlen zu sortieren
    ; ' SortOrder:    lvwAscending für aufsteigende Sortierung (Std.)
    ; '               lvwDescending für absteigende Sortierung
    ; ' -----------------------------------------------------
    
    Protected udtLVWSORT.LVWSORT
    Protected sDateFormat.s, sTemp.s, GadId.i
    
    If SortType = #SortDate
      GadId = GetDlgCtrlID_(hWndListView)
      sDateFormat = GetDateFormat(GetGadgetItemText(GadId, 0, SortKey))
      
      If sDateFormat = ""
        SortType = #SortString
      Else
        sTemp = GetDateFormat(GetGadgetItemText(GadId, CountGadgetItems(GadId)-1, SortKey))
        If sTemp=""
          SortType=#SortString
        Else
          If sTemp<>sDateFormat
            If Left(sTemp, 3)="%mm" ; new format starts with %mm (.dd.yyyy) - if this US format is detected it has higher prio
              sDateFormat=sTemp
            EndIf
          EndIf
          sTemp=GetDateFormat(GetGadgetItemText(GadId, CountGadgetItems(GadId)/2, SortKey))
          If sTemp=""
            SortType=#SortString
          Else
            If sTemp<>sDateFormat
              If Left(sTemp, 3)="%mm" ; new format starts with %mm (.dd.yyyy) - if this US format is detected it has higher prio
                sDateFormat=sTemp
              EndIf
            EndIf
          EndIf   
        EndIf         
      EndIf
      udtLVWSORT\DateFormat=sDateFormat
      ;Debug "Final DateFormat >"+sDateFormat+"<"
    EndIf
    
    ; Übergebene Informationen in einer LVWSORT-Struktur zusammenfassen:
    udtLVWSORT\hWndListView=hWndListView
    udtLVWSORT\SortKey=SortKey
    udtLVWSORT\SortOrder=SortOrder
    udtLVWSORT\SortType=SortType   
    
    ; Eigene Sortierfunktionalität in der Funktion CompareFunc verwenden: Die Informationen der
    ; LVWSORT-Struktur wird mithilfe eines Zeigers auf die Variable udtLVWSORT beigegeben:
    SendMessage_(hWndListView, #LVM_SORTITEMSEX, @udtLVWSORT, @CompareFunc())
    
  EndProcedure
  Procedure.b DetectOrderType(sText.s)
    
    Protected Diff
    
    Diff=Len(sText)-CountString(sText, "0")-CountString(sText, "1")-CountString(sText, "2")-CountString(sText, "3")-CountString(sText, "4")-CountString(sText, "5")-CountString(sText, "6")-CountString(sText, "7")-CountString(sText, "8")-CountString(sText, "9")   
    
    If Diff > 0
      If FindString(" KB MB GB TB", UCase(Right(sText, 3)))
        ProcedureReturn #SortFileSize
      EndIf
    EndIf
    
    Select Diff
      Case 0 ; es sind nur Ziffern
        ProcedureReturn #SortNumeric
        
      Case 1 ; nur 1 anderes Zeichen
        If (CountString(sText, ",")>0 Or CountString(sText, ".")>0)
          ProcedureReturn #SortFloat
        ElseIf (Left(sText, 1)="$" Or Left(sText, 1)="%") ; es ist eine HEX oder Binär Zahl
          ProcedureReturn #SortNumeric
        Else
          ProcedureReturn #SortString
        EndIf
        
      Case 2 ; 2 andere Zeichen - evtl. Datum?
        
        If (Len(sText)=10 And
          Not IsNumChar(@sText, 3) And Not IsNumChar(@sText, 6))
          ; dd-mm-yyyy or mm-dd-yyyy
          ProcedureReturn #SortDate
        Else
          ; yyyy-mm-dd
          ProcedureReturn #SortString
        EndIf

      Case 4 ; 4 other chars, possibly DateTime?
        
        If (Len(sText)=16 And
          Not IsNumChar(@sText, 3) And Not IsNumChar(@sText, 6) And
          Not IsNumChar(@sText, 11) And Not IsNumChar(@sText, 14))
          ;dd-mm-yyyy hh:mm or mm-dd-yyyy hh:mm
          ProcedureReturn #SortDate
        Else
          ProcedureReturn #SortString
        EndIf
        
      Case 5 ; 5 other chars, possibly DateTime?
        Debug Len(sText)
        If (Len(sText)=19 And
          Not IsNumChar(@sText, 3) And Not IsNumChar(@sText, 6) And
          Not IsNumChar(@sText, 11) And Not IsNumChar(@sText, 14) And Not IsNumChar(@sText, 17))
          ;dd-mm-yyyy hh:mm:ss or mm-dd-yyyy hh:mm:ss
          ProcedureReturn #SortDate
        Else
          ProcedureReturn #SortString
        EndIf         
        
      Default
        ProcedureReturn #SortString
        
    EndSelect
    
  EndProcedure
  Procedure.i GetSortOrderType(gadget, Column)

    Protected Result = #SortAutoDetect
    
    If FindMapElement(SortOrder(), Str(gadget))
      If ParseJSON(0, SortOrder()\SortOrderType)
        Dim SortTypes.i(0)
        ExtractJSONArray(JSONValue(0), SortTypes())
        
        If Column <= ArraySize(SortTypes())
          Result = SortTypes(Column)
        EndIf
        
        FreeJSON(0)
      EndIf
    EndIf
    
    ProcedureReturn Result
  EndProcedure
  
  
  Procedure   SortColumn(gadget, Column)
    
    Protected ColCnt, Order, iStartT, iEndT, Temp.b, OrderType.b
    
    If Not IsGadget(gadget) Or GetSortState(gadget) = 0 : ProcedureReturn #False : EndIf
    
    ;Debug "LIG_SortColumn >"+Str(Gadget)+"< Spalte >"+Str(Column)+"<"
    
    Select GetSortOrder(gadget, Column)
      Case #NoSort, #DescSort
        Order = #AscSort
      Case #AscSort
        Order = #DescSort
    EndSelect
    
    iStartT=ElapsedMilliseconds()
    
    OrderType = GetSortOrderType(gadget, Column)
    
    If OrderType = #SortAutoDetect ; detect it automatically - check first, last and middle item of list
      OrderType = DetectOrderType(GetGadgetItemText(gadget, 0, Column))
      
      If (OrderType = DetectOrderType(GetGadgetItemText(gadget, CountGadgetItems(gadget)-1, Column)))
        If (OrderType <> DetectOrderType(GetGadgetItemText(gadget, CountGadgetItems(gadget)/2, Column)))
          ;Debug "Different OrderType - use SortString 2"
          OrderType = #SortString
        EndIf
      Else
        ;Debug "Different OrderType - use SortString"
        OrderType = #SortString
      EndIf
      
    ElseIf OrderType = #SortNone
      ProcedureReturn #False
    EndIf   
    
    SortListView(GadgetID(gadget), Column, OrderType, Order)
    
    iEndT = ElapsedMilliseconds()
    
    ;Debug "Dauer >"+StrF( (iEndT-iStartT)/1000, 2)+"<"
    
    SetSortIcon(gadget, Column, Order)
    
    If (GetGadgetState(gadget) > -1)
      EnsureVisible(gadget, GetGadgetState(gadget))
    EndIf
    
  EndProcedure
  
  ;- ##### ListIconGadget Sort Ende ######
  
  Procedure ColumnClickCallback(hwnd, uMsg, wParam, lParam, id, *RefData)
    ; Edel
    Protected *msg.NM_LISTVIEW
    
    If uMsg = #WM_NOTIFY
      *msg = lParam
      If *msg\hdr\code = #LVN_COLUMNCLICK   
        SortColumn(GetDlgCtrlID_(*msg\hdr\hwndFrom), *msg\iSubItem)
      EndIf
    EndIf
    
    ProcedureReturn DefSubclassProc(hwnd, uMsg, wParam, lParam)
    
  EndProcedure
  Procedure ColumnClickCallback_SubClass(hwnd, uMsg, wParam, lParam, id, *RefData)
    ; --- Bisonte
    Protected *msg.NM_LISTVIEW
    
    Select uMsg
      Case #WM_DESTROY
        RemoveWindowSubclass(hwnd, @ColumnClickCallback_SubClass(), id)
        
      Case #WM_NOTIFY
        *msg = lParam
        If *msg\hdr\code = #HDN_ITEMCLICK
          SortColumn(GetDlgCtrlID_(hwnd), *msg\iItem) 
        EndIf
        
    EndSelect
    
    ProcedureReturn DefSubclassProc(hwnd, uMsg, wParam, lParam)
    
  EndProcedure   
  
  Procedure SetSortOrderType(gadget, Column, SortType)
    
    If FindMapElement(SortOrder(), Str(gadget))
      
      If ParseJSON(0, SortOrder()\SortOrderType)
        
        Dim SortTypes.i(0)
        ExtractJSONArray(JSONValue(0), SortTypes())
      
        If Column > ArraySize(SortTypes())
          Redim SortTypes.i(Column)
        EndIf
        
        FreeJSON(0)
      Else
        Dim SortTypes.i(Column)
      EndIf
      
      SortTypes(Column) = SortType
        
      If CreateJSON(0)
        InsertJSONArray(JSONValue(0), SortTypes())
        SortOrder()\SortOrderType = ComposeJSON(0)
        FreeJSON(0)
      EndIf
      
    EndIf
  EndProcedure
  
  Procedure DisableSort (gadget, disable.b = #TRUE)
    If FindMapElement(SortOrder(), Str(gadget))
      SortOrder()\Activ = disable!1
    EndIf  
  EndProcedure
  
  Procedure Enable(gadget)
    ; --- method by edel
    SetWindowSubclass(GetParent_(GadgetID(gadget)), @ColumnClickCallback(), 0, 0)
    If AddMapElement(SortOrder(), Str(gadget))
      SortOrder()\Activ = 1
    EndIf
    
    ; --- method by bisonte
    ;SetWindowSubclass(GadgetID(Gadget), @ColumnClickCallback_SubClass(), #Null, #Null)
  EndProcedure
  
  EndModule
  
  DisableExplicit
  
CompilerElse
  
  MessageRequester("PureBasic", "PureBasic V5.20 or higher needed!") : End
  
CompilerEndIf
CompilerIf #PB_Compiler_IsMainFile
  
  Enumeration #PB_Compiler_EnumerationValue
    #MainWin
  EndEnumeration
  Enumeration #PB_Compiler_EnumerationValue
    #ListIcon
  EndEnumeration
  
  Procedure OpenMainWin()
    OpenWindow(#MainWin, 0, 0, 850, 500, "ListIconGadget-SortExample", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    
    ListIconGadget(#ListIcon, 10, 10, 830, 480, "COL 0", 150, #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect)
    AddGadgetColumn(#ListIcon, 1, "COL 1", 100)
    AddGadgetColumn(#ListIcon, 2, "COL 2 (fSize)", 100)
    AddGadgetColumn(#ListIcon, 3, "COL 3 (NUM)", 100)
    AddGadgetColumn(#ListIcon, 4, "COL 4 (FLOAT)", 100)
    AddGadgetColumn(#ListIcon, 5, "COL 5 (DATE)", 100)
    AddGadgetColumn(#ListIcon, 6, "COL 6 (DATETIME)", 150)
  EndProcedure
  
  Procedure MainWin_Events(event)
    Select event
      Case #PB_Event_CloseWindow
        ProcedureReturn #False
        
    EndSelect
    ProcedureReturn #TRUE
  EndProcedure
  
  
  Define iEvent.i
  Define iEventWindow.i
  Define iCloseAll.i
  Define a.i, x.i
  Define A$, B$, C$, D$, E$, F$, G$
  
  OpenMainWin()
  
  ; generate Test Values:
  
  For a = 0 To 1000
    
    A$ = "COL 1, Row "+RSet(Str(  a  ),6,"0")+Chr(10)
    
    B$ = StrF(Random(1000)/Random(100, 1), 2)
    Select Random(4, 1)
      Case 1 : B$ + " KB"
      Case 2 : B$ + " MB"
      Case 3 : B$ + " GB"
      Case 4 : B$ + " TB"
    EndSelect
    B$ + Chr(10)
    
    C$ = "$"+RSet(Hex(Random($7FFFFFFF)),8,"0")+Chr(10)
    
    Select Random(5, 1)
      Case 1
        D$=Str(Random(9, 0))
      Case 2
        D$=Str(Random(99, 10))
      Case 3
        D$=Str(Random(999, 100))
      Case 4
        D$=Str(Random(9999, 1000))
      Case 5
        D$=Str(Random(99999, 10000))
    EndSelect         
    D$+Chr(10)
    
    Select Random(5, 1)
      Case 1
        E$=Str(Random(9, 0))+","+Str(Random(99, 0))
      Case 2
        E$=Str(Random(99, 10))+","+Str(Random(99, 0))
      Case 3
        E$=Str(Random(999, 100))+","+Str(Random(99, 0))
      Case 4
        E$=Str(Random(9999, 1000))+","+Str(Random(99, 0))
      Case 5
        E$=Str(Random(99999, 10000))+","+Str(Random(99, 0))
    EndSelect
    E$+Chr(10)
    
    F$=FormatDate("%dd.%mm.%yyyy", Random(Date(), 0))+Chr(10)
    
    G$=FormatDate("%mm-%dd-%yyyy %hh:%mm:%ss", Random(Date(), 0))
    
    AddGadgetItem(#ListIcon, a, A$+B$+C$+D$+E$+F$+G$)
    
  Next
  
  Enumeration ; Type of Column Sort
    #SortAutoDetect
    #SortString
    #SortNumeric
    #SortFloat
    #SortDate
    #SortFileSize
    #SortNone
  EndEnumeration
  
  
  nalorLIG::Enable(#ListIcon)
  
  nalorLIG::SetSortOrderType(#ListIcon, 0, #SortNone)
  nalorLIG::SetSortOrderType(#ListIcon, 1, #SortFileSize)
  nalorLIG::SetSortOrderType(#ListIcon, 2, #SortString)
  nalorLIG::SetSortOrderType(#ListIcon, 3, #SortNumeric)
  nalorLIG::SetSortOrderType(#ListIcon, 4, #SortFloat)
  nalorLIG::SetSortOrderType(#ListIcon, 5, #SortDate)
  nalorLIG::SetSortOrderType(#ListIcon, 6, #SortDate)
  
  ; nalorLIG::DisableSort (#ListIcon)
  
  
  Repeat
    
    iEvent = WaitWindowEvent()
    iEventWindow=EventWindow()
    
    Select iEventWindow
      Case #MainWin
        If (Not MainWin_Events(iEvent))
          iCloseAll=#TRUE
        EndIf
    EndSelect
    
  Until iCloseAll=#TRUE
  
CompilerEndIf
"Daddy, I'll run faster, then it is not so far..."
User avatar
VB6_to_PBx
Enthusiast
Enthusiast
Posts: 625
Joined: Mon May 09, 2011 9:36 am

Re: ListIconGadget with Sort and Arrow Icons

Post by VB6_to_PBx »

How do you go back and re-Sort Column 0
after you've tested sorting out Columns such as Columns 1 to 6

in others words ,
try testing sorting out Columns such as Columns 1 to 6
then go back and try to sort Column 0
and Column 0 will not sort correctly .

Column 0 only sorts out correctly at initial startup or Run
 
PureBasic .... making tiny electrons do what you want !

"With every mistake we must surely be learning" - George Harrison
dige
Addict
Addict
Posts: 1252
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: ListIconGadget with Sort and Arrow Icons

Post by dige »

Column 0 is set to #SortNone.

Code: Select all

nalorLIG::SetSortOrderType(#ListIcon, 0, #SortNone)
If you want to activate this column for sorting, just set an ordertype like:

Code: Select all

nalorLIG::SetSortOrderType(#ListIcon, 0, #SortString)
Did you mean this?
"Daddy, I'll run faster, then it is not so far..."
User avatar
VB6_to_PBx
Enthusiast
Enthusiast
Posts: 625
Joined: Mon May 09, 2011 9:36 am

Re: ListIconGadget with Sort and Arrow Icons

Post by VB6_to_PBx »

Code: Select all

nalorLIG::SetSortOrderType(#ListIcon, 0, #SortString)
#ListIcon, 0, #SortString

Thanks dige , that worked great ! , and thanks for sharing this Code !
 
PureBasic .... making tiny electrons do what you want !

"With every mistake we must surely be learning" - George Harrison
dige
Addict
Addict
Posts: 1252
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: ListIconGadget with Sort and Arrow Icons

Post by dige »

* Update * (see modified..)

Code: Select all

; ==================================================================================================
; --- ListIconGadget Extras & Sort
; --- ----------------------------------------------------------------------------------------------
; --- File            : module_nalorLIG.pbi
; --- OriginalAuthor  : nalor
; --- modified        : bisonte (change to new module system of pb)
; --- modified        : dige (add SetSortOrderType, bugfix parsedate, add SortType FileSize, DisableSort)
; --- modified        : dige (add SetColumnAlignment, add SortType SortPixel)
; --- Link            : http://purebasic.fr/english/viewtopic.php?f=12&t=55085   
; --- Date            : Nov 29, 2015
; --- Compiler        : PureBasic 5.40 (Windows - x86)
; --- Target OS       : Windows
; --- Version         : 1.1
; ==================================================================================================
; --- Module : nalorLIG
; --- Remark : If WindowsXP is used - Disable Debugger or use Unicode Flag only
; --- Changed with edel's help to subclass
; ==================================================================================================
CompilerIf #PB_Compiler_Version => 520
  
  EnableExplicit
  
  CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
    MessageRequester("PureBasic", "Windows only! Sorry.") : End
  CompilerEndIf

  ;{ Modul Deklaration
  DeclareModule nalorLIG
  
  Enumeration ; Type of Column Sort
    #SortAutoDetect
    #SortString
    #SortNumeric
    #SortFloat
    #SortDate
    #SortDateDDMMYYYYHHIISS
    #SortFileSize
    #SortPixel
    #SortNone
  EndEnumeration
  Enumeration ; Column Sort States
    #NoSort   ; keine Sortierung
    #AscSort  ; Aufsteigende Sortierung
    #DescSort ; Absteigende Sortierung
  EndEnumeration
  
  Structure LVWSORT
    hWndListView.i ; Fensterhandle des ListView-Controls
    SortKey.i ; Spalte, die sortiert werden soll
    SortType.b ; Typ der zu sortierenden Daten
    SortOrder.b ; Sortierrichtung
    DateFormat.s ; Mask for 'ParseDate'
  EndStructure
  
  Declare   Enable(gadget)
  Declare   SetSortOrderType(gadget, Column, SortType)
  Declare   SetColumnAlignment(gadget, Column, flags)
  Declare   DisableSort (gadget, disable.b = #TRUE)
  Declare   ClearSort (gadget)
  
  Structure LVWSTATUS
    activ.i
    SortOrderType.s
  EndStructure
  
  Global NewMap SortOrder.LVWSTATUS()
  
  EndDeclareModule
  ;}
  
  ;{ Modul ListIconSort
  Module        nalorLIG
  
  ;{--- Improvement by edel
  
  ; Subclass Window
  
  Import ""
    GetProcAddress(hmod, s.p-ascii)
  EndImport
  
  Prototype pSetWindowSubclass(hwnd, *Proc, *Id, *RefData)
  Prototype pDefSubclassProc(hwnd, msg, wParam, lParam)
  Prototype pRemoveWindowSubclass(hwnd, *Proc, *Id)
  Prototype pGetWindowSubclass(hwnd, *Proc, *Id, *RefData)
  
  Procedure SetWindowSubclass(hwnd, *Proc, *Id, *RefData)
    Protected Comctl32 = GetModuleHandle_("Comctl32.dll")   
    Protected func.pSetWindowSubclass = GetProcAddress(Comctl32, "SetWindowSubclass")
    ProcedureReturn func(hwnd, *Proc, *Id, *RefData)
  EndProcedure
  Procedure DefSubclassProc(hwnd, msg, wParam, lParam)
    Protected Comctl32 = GetModuleHandle_("Comctl32.dll")
    Protected func.pDefSubclassProc = GetProcAddress(Comctl32, "DefSubclassProc")
    ProcedureReturn func(hwnd, msg, wParam, lParam)
  EndProcedure
  Procedure RemoveWindowSubclass(hwnd, *Proc, *Id)
    Protected Comctl32 = GetModuleHandle_("Comctl32.dll")
    Protected func.pRemoveWindowSubclass = GetProcAddress(Comctl32, "RemoveWindowSubclass")
    ProcedureReturn func(hwnd, *Proc, *Id)
  EndProcedure
  Procedure GetWindowSubclass(hwnd, *Proc, *Id, *RefData)
    Protected Comctl32 = GetModuleHandle_("Comctl32.dll")
    Protected func.pGetWindowSubclass = GetProcAddress(Comctl32, "GetWindowSubclass")
    ProcedureReturn func(hwnd, *Proc, *Id, *RefData)
  EndProcedure
  ;}
  
  Procedure   GethWnd(id)
    
    Protected hwnd = #False
    
    If IsGadget(id)
      hwnd = GadgetID(id)
    Else
      If IsWindow_(id)
        hwnd = id
      EndIf
    EndIf
    
    ProcedureReturn hwnd
    
  EndProcedure
  
  ;- ++++++ ListIconGadget Tools Start ++++++
  
  Procedure   AlignColumn(gadget, index, Format)
    
    ; by Danilo, 15.12.2003 - english chat (for 'Karbon')
    ; 20130615..nalor..modified
    ; change text alignment for columns
    ; #LVCFMT_LEFT / #LVCFMT_CENTER / #LVCFMT_RIGHT
    
    Protected lvc.LV_COLUMN, hwnd = GethWnd(gadget)
    
    If hwnd
      
      lvc\Mask = #LVCF_FMT
      lvc\fmt = Format
      
      SendMessage_(hwnd, #LVM_SETCOLUMN, index, @lvc)
      
      ProcedureReturn #TRUE
      
    EndIf
    
    ProcedureReturn #False
    
  EndProcedure
  Procedure   SetColumnWidth(gadget, index, New_Width)
    
    ; by Danilo, 15.12.2003 - english chat (for 'Karbon')
    ;
    ; change column header width
    ;
    
    Protected hwnd = GethWnd(gadget)
    
    If hwnd
      
      SendMessage_(hwnd, #LVM_SETCOLUMNWIDTH, index, New_Width)
      ProcedureReturn #TRUE
      
    EndIf
    
    ProcedureReturn #False
    
  EndProcedure
  Procedure   SetSortIcon(gadget, Column, SortOrder)
    
    ; http://stackoverflow.com/questions/254129/how-To-i-display-a-sort-arrow-in-the-header-of-a-List-view-column-using-c   
    
    Protected ColumnHeader
    Protected ColumnCount
    Protected hditem.HD_ITEM
    Protected Cnt
    Protected hwnd = GethWnd(gadget)
    
    If hwnd
      
      ColumnHeader = SendMessage_(hwnd, #LVM_GETHEADER, 0, 0)
      ColumnCount  = SendMessage_(ColumnHeader, #HDM_GETITEMCOUNT, 0, 0)
      
      For Cnt = 0 To ColumnCount - 1
        
        hditem\Mask=#HDI_FORMAT
        
        If SendMessage_(ColumnHeader, #HDM_GETITEM, Cnt, @hditem) = 0
          Debug "ERROR! LIG_SetSortIcon 1"
        EndIf
        
        hditem\Mask=#HDI_FORMAT
        
        If (Cnt = Column And SortOrder <> #NoSort)
          
          Select SortOrder
              
            Case #AscSort ; wenn aufsteigend sortiert werden soll
              hditem\fmt& ~#HDF_SORTDOWN
              hditem\fmt|#HDF_SORTUP
              ;Debug "sortup"
              
            Case #DescSort
              hditem\fmt& ~#HDF_SORTUP
              hditem\fmt|#HDF_SORTDOWN               
              ;Debug "sortdown"
              
          EndSelect
          
        Else
          
          hditem\fmt& ~#HDF_SORTUP
          hditem\fmt& ~#HDF_SORTDOWN
          
        EndIf
        
        If (SendMessage_(ColumnHeader, #HDM_SETITEM, Cnt, @hditem) = 0)
          Debug "ERROR! LIG_SetSortIcon 2"
        EndIf
        
      Next Cnt
      
      ProcedureReturn #TRUE
      
    EndIf
    
    ProcedureReturn #False
    
  EndProcedure
  Procedure GetSortState (gadget)
    Protected Result.i
    
    If FindMapElement(SortOrder(), Str(gadget))
      Result = SortOrder()\activ
    EndIf  
    
    ProcedureReturn Result
  EndProcedure
  Procedure.b GetSortOrder(gadget, Column)
    
    Protected ColumnHeader
    Protected hditem.HD_ITEM
    Protected RetVal.b = -1
    Protected hwnd = GethWnd(gadget)
    
    If hwnd
      
      ColumnHeader = SendMessage_(hwnd, #LVM_GETHEADER, 0, 0)
      
      hditem\Mask=#HDI_FORMAT
      
      If SendMessage_(ColumnHeader, #HDM_GETITEM, Column, @hditem)
        
        If (hditem\fmt&#HDF_SORTUP)=#HDF_SORTUP
          
          ;Debug "sortup"
          RetVal=#AscSort
          
        ElseIf (hditem\fmt&#HDF_SORTDOWN)=#HDF_SORTDOWN
          
          ;Debug "sortdown"
          RetVal=#DescSort
          
        Else
          
          ;Debug "keine sortierung"
          RetVal=#NoSort
          
        EndIf
        
      Else
        
        Debug "ERROR! LIG_GetSortOrder"
        RetVal=-1
        
      EndIf
      
    EndIf
    
    ProcedureReturn RetVal
    
  EndProcedure
  
  Procedure.l CountGadgetColumns(gadget)
    Protected hHdr.l, lresult.l
    hHdr    = SendMessage_(GadgetID(gadget), $101F, #Null, #Null)
    lresult = SendMessage_(hHdr, #HDM_GETITEMCOUNT, #Null, #Null)
    ProcedureReturn lresult
  EndProcedure
  
  Procedure   EnsureVisible(gadget, Line)
    ; makes sure the line is visible
    
    Protected hwnd = GethWnd(gadget)
    If hwnd
      SendMessage_(hwnd, #LVM_ENSUREVISIBLE, Line, #TRUE)
      ProcedureReturn #TRUE
    EndIf
    ProcedureReturn #False
    
  EndProcedure
  
  ;- ##### ListIconGadget Tools Ende #####
  
  ;- ++++++ ListIconGadget Sort Start ++++++
  
  ; http://msdn.microsoft.com/de-de/library/bb979183.aspx   
  ; Die Struktur LVWSORT enthält Informationen über das zu sortierende ListView-Steuerelement, die Spalte,
  ; nach der sortiert werden soll, sowie die gewünschte Sortierrichtung.
  Procedure.q StringToBytes (sEntry.s)
    Protected Result.q
    
    Select UCase(Right(sEntry, 2))
      Case "TB" : Result = ValF(sEntry) * 1099511627776
      Case "GB" : Result = ValF(sEntry) * 1073741824
      Case "MB" : Result = ValF(sEntry) * 1048576
      Case "KB" : Result = ValF(sEntry) * 1024
      Default   : Result = Val(sEntry)
    EndSelect
    
    ProcedureReturn Result
  EndProcedure
  
  Procedure.q StringToPixels (sEntry.s)
    Protected Result.q
    
    Result = Val(StringField(sEntry, 1, "x")) * Val(StringField(sEntry, 2, "x"))
    
    ProcedureReturn Result
  EndProcedure
  
  
  Procedure.b IsNumChar(*Text, Position = 1)
    Select Asc(PeekS(*Text+(Position-1)*SizeOf(character), 1))
      Case 48 To 57
        ProcedureReturn #TRUE
      Default
        ProcedureReturn #False
    EndSelect
  EndProcedure
  Procedure   CompareStrings(*sEntry1, *sEntry2, SortOrder.b)
    ; ' -----------------------------------------------------
    ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
    ; ' Elemente nach Maßgabe des Parameters SortOrder größer
    ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
    ; ' aufsteigender Sortierung) als das zweite Element ist.
    ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
    ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
    ; ' -----------------------------------------------------
    ; ' Rückgabewert je nach erwünschter Sortierung:
    
    If SortOrder = #AscSort
      ; Aufsteigende Sortierung zweier unterschiedlicher Strings
      If CompareMemoryString(*sEntry1, *sEntry2, #PB_String_NoCase) = #PB_String_Lower
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    Else ; Absteigende Sortierung
      If CompareMemoryString(*sEntry1, *sEntry2, #PB_String_NoCase) = #PB_String_Greater
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    EndIf
    
  EndProcedure
  Procedure   CompareNumbers(sEntry1.s, sEntry2.s, SortOrder.b)
    ; ' -----------------------------------------------------
    ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
    ; ' Elemente nach Maßgabe des Parameters SortOrder größer
    ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
    ; ' aufsteigender Sortierung) als das zweite Element ist.
    ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
    ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
    ; ' -----------------------------------------------------
    ; ' Rückgabewert je nach erwünschter Sortierung:
    
    If SortOrder = #AscSort
      ; Aufsteigende Sortierung zweier unterschiedlicher Zahlen
      If Val(sEntry1) < Val(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    Else ; Absteigende Sortierung
      If Val(sEntry1) > Val(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    EndIf
    
  EndProcedure
  Procedure   CompareFloat(sEntry1.s, sEntry2.s, SortOrder.b)
    ; ' -----------------------------------------------------
    ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
    ; ' Elemente nach Maßgabe des Parameters SortOrder größer
    ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
    ; ' aufsteigender Sortierung) als das zweite Element ist.
    ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
    ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
    ; ' -----------------------------------------------------
    ; ' Rückgabewert je nach erwünschter Sortierung:
    
    ReplaceString(sEntry1, ",", ".", #PB_String_InPlace, 1, 1) ; ersetze Dezimalkomma durch Punkt, damit ValF korrekt arbeitet
    ReplaceString(sEntry2, ",", ".", #PB_String_InPlace, 1, 1)
    
    If SortOrder = #AscSort
      ; Aufsteigende Sortierung zweier unterschiedlicher Zahlen
      If ValF(sEntry1) < ValF(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    Else ; Absteigende Sortierung
      If ValF(sEntry1) > ValF(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    EndIf         
    
  EndProcedure
  
  Procedure   CompareFileSize(sEntry1.s, sEntry2.s, SortOrder.b)
    ; ' -----------------------------------------------------
    ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
    ; ' Elemente nach Maßgabe des Parameters SortOrder größer
    ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
    ; ' aufsteigender Sortierung) als das zweite Element ist.
    ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
    ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
    ; ' -----------------------------------------------------
    ; ' Rückgabewert je nach erwünschter Sortierung:
    
    If SortOrder = #AscSort
      ; Aufsteigende Sortierung zweier unterschiedlicher Zahlen
      If StringToBytes(sEntry1) < StringToBytes(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    Else ; Absteigende Sortierung
      If StringToBytes(sEntry1) > StringToBytes(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    EndIf         
    
  EndProcedure
  
  Procedure   ComparePixel(sEntry1.s, sEntry2.s, SortOrder.b)
    ; ' -----------------------------------------------------
    ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
    ; ' Elemente nach Maßgabe des Parameters SortOrder größer
    ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
    ; ' aufsteigender Sortierung) als das zweite Element ist.
    ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
    ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
    ; ' -----------------------------------------------------
    ; ' Rückgabewert je nach erwünschter Sortierung:
    
    If SortOrder = #AscSort
      ; Aufsteigende Sortierung zweier unterschiedlicher Zahlen
      If StringToPixels(sEntry1) < StringToPixels(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    Else ; Absteigende Sortierung
      If StringToPixels(sEntry1) > StringToPixels(sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    EndIf         
    
  EndProcedure
  
  Procedure   CompareDate(sEntry1.s, sEntry2.s, SortOrder.b, sDateMask.s)
    ; ' -----------------------------------------------------
    ; ' Gibt zurück, ob das erste der beiden unterschiedlichen
    ; ' Elemente nach Maßgabe des Parameters SortOrder größer
    ; ' (1 bei aufsteigender Sortierung) oder kleiner (-1 bei
    ; ' aufsteigender Sortierung) als das zweite Element ist.
    ; ' Gleiche Elemente wurden bereits in CompareFunc ausge-
    ; ' schlossen; für sie wäre sonst 0 zurückzugeben.
    ; ' -----------------------------------------------------
    ; ' Rückgabewert je nach erwünschter Sortierung:
    
    If SortOrder = #AscSort
      ; Aufsteigende Sortierung zweier unterschiedlicher Zahlen
      If ParseDate(sDateMask, sEntry1) < ParseDate(sDateMask, sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    Else ; Absteigende Sortierung
      If ParseDate(sDateMask, sEntry1) > ParseDate(sDateMask, sEntry2)
        ProcedureReturn -1
      Else
        ProcedureReturn 1
      EndIf
    EndIf         
    
  EndProcedure
  Procedure.s LvwGetText(*ListViewSort.LVWSORT, lParam)
    ; ' -----------------------------------------------------
    ; ' Ermittelt aus dem Fensterhandle des ListView-
    ; ' Steuerelements, der in ListViewSort.SortKey
    ; ' angegebenen (nullbasierten) Spalte im ListView
    ; ' und der an CompareFunc übergebenen Werte lParam1/2
    ; ' die davon repräsentierten Zelleninhalte.
    ; ' -----------------------------------------------------
    Protected udtFindInfo.LV_FINDINFO, udtLVItem.LV_ITEM
    Protected lngIndex, *baBuffer, lngLength, RetVal.s = ""
    
    *baBuffer = AllocateMemory(512)
    
    If *baBuffer
      
      ; Auf Basis des Index den Text der Zelle auslesen:
      udtLVItem\Mask=#LVIF_TEXT
      udtLVItem\iSubItem=*ListViewSort\SortKey
      udtLVItem\pszText=*baBuffer
      udtLVItem\cchTextMax=(512/SizeOf(character))-1
      
      lngLength = SendMessage_(*ListViewSort\hWndListView, #LVM_GETITEMTEXT, lParam, @udtLVItem)
      
      ; Byte-Array in passender Länge als String-Rückgabewert kopieren:
      
      If lngLength > 0
        RetVal = PeekS(*baBuffer, lngLength)
      EndIf
      FreeMemory(*baBuffer) ; thx LittleJohn
      
    EndIf
    
    ProcedureReturn RetVal
    
  EndProcedure
  Procedure   CompareFunc(lParam1, lParam2, lParamSort)
    ; ' -----------------------------------------------------
    ; ' Vergleichsfunktion CompareFunc
    ; ' -----------------------------------------------------
    ; ' Verglichen werden jeweils zwei Elemente der zu
    ; ' sortierenden Spalte des ListView-Steuerelements,
    ; ' die über lParam1 und lParam2 angegeben werden.
    ; ' Hierbei wird über den Rückgabewert der Funktion
    ; ' bestimmt, welches der beiden Elemente als größer
    ; ' gelten soll (hier für Aufwärtssortierung):
    ; ' * Element 1 < Element 2: Rückgabewert < 0
    ; ' * Element 1 = Element 2: Rückgabewert = 0
    ; ' * Element 1 > Element 2: Rückgabewert > 0
    ; ' -----------------------------------------------------
    Protected *ListViewSort.LVWSORT
    Protected sEntry1.s
    Protected sEntry2.s
    Protected vCompare1.s ; As Variant
    Protected vCompare2.s ; As Variant
    
    ; In lParamSort von SortListView als Long-Pointer übergebene LVWSORT-Struktur abholen, um auf deren
    ; Werte zugreifen zu können:
    
    *ListViewSort=lParamSort
    
    ; Die Werte der zu vergleichenden Elemente werden mithilfe der privaten Funktion LvwGetText aus
    ; den Angaben lParam1 und lParam2 ermittelt:
    sEntry1 = LvwGetText(*ListViewSort, lParam1)
    sEntry2 = LvwGetText(*ListViewSort, lParam2)
    
    ; Sind die Elemente gleich, kann die Funktion sofort mit dem aktuellen Rückgabewert 0
    ; verlassen werden:
    If sEntry1 = sEntry2
      ProcedureReturn 0
    EndIf
    
    ; Für die Sortierung wird unterschieden zwischen Zahlen, Fließkommazahlen und allgemeinen Strings. Hierfür
    ; steht jeweils eine separate, private Vergleichsfunktion zur Verfügung.
    
    Select *ListViewSort\SortType
      Case #SortNumeric ; ' Spalteninhalte sind Zahlen
        ProcedureReturn CompareNumbers(sEntry1, sEntry2, *ListViewSort\SortOrder)
      Case #SortFloat ; ' Spalteninhalte sind Zahlen mit Nachkommastellen
        ProcedureReturn CompareFloat(sEntry1, sEntry2, *ListViewSort\SortOrder)
      Case #SortString;  ' Spalteninhalte sind Strings
        ProcedureReturn CompareStrings(@sEntry1, @sEntry2, *ListViewSort\SortOrder)
      Case #SortDate, #SortDateDDMMYYYYHHIISS
        ProcedureReturn CompareDate(sEntry1, sEntry2, *ListViewSort\SortOrder, *ListViewSort\DateFormat)
      ; Case #SortDateDDMMYYYYHHIISS
        ; ProcedureReturn CompareDate(sEntry1, sEntry2, *ListViewSort\SortOrder, "%dd.%mm.%yyyy %hh:%ii:%ss")
      Case #SortFileSize;  ' Spalteninhalte sind Floats und Strings
        ProcedureReturn CompareFileSize(sEntry1, sEntry2, *ListViewSort\SortOrder)
      Case #SortPixel;  ' Spalteninhalte Numerische Werte XxY
        ProcedureReturn ComparePixel(sEntry1, sEntry2, *ListViewSort\SortOrder)
    EndSelect
  EndProcedure
  Procedure.s GetDateFormat(date.s)
    
    ;Debug "GetDateFormat >"+Date+"<"
    Protected Diff.i
    
    Diff=Len(date)-CountString(date, "0")-CountString(date, "1")-CountString(date, "2")-CountString(date, "3")-CountString(date, "4")-CountString(date, "5")-CountString(date, "6")-CountString(date, "7")-CountString(date, "8")-CountString(date, "9")   
    
    Select Diff
      Case 2
        If Len(date)=10 ; Date 'dd.mm.yyyy', 'mm.dd.yyyy' or 'yyyy.mm.dd'
          
          If (Not IsNumChar(@date, 5) And Not IsNumChar(@date, 8)) ; yyyy.mm.dd
            ProcedureReturn "" ; faster to sort as string
            
          ElseIf (Not IsNumChar(@date, 3) And Not IsNumChar(@date, 6)) ; dd.mm.yyyy or mm.dd.yyyy
            If Val(Mid(date, 4, 2))>12 ; is it mm.dd.yyyy?
              ProcedureReturn "%mm"+Mid(date, 3, 1)+"%dd"+Mid(date, 6, 1)+"%yyyy"
            Else ; default is dd.mm.yyyy
              ProcedureReturn "%dd"+Mid(date, 3, 1)+"%mm"+Mid(date, 6, 1)+"%yyyy"
            EndIf
            
          Else
            ProcedureReturn "" ; not a date - sort as string
          EndIf
        Else
          ProcedureReturn "" ; not a date - sort as string
        EndIf
        
      Case 4
        If Len(date)=16 ;yyyy-mm-dd hh:mm, dd-mm-yyyy hh:mm or mm-dd-yyyy hh:mm
          
          If (Not IsNumChar(@date, 5) And Not IsNumChar(@date, 8)) ; yyyy.mm.dd xxxxx
            ProcedureReturn "" ; faster to sort as string
            
          ElseIf (Not IsNumChar(@date, 3) And Not IsNumChar(@date, 6)) ; dd.mm.yyyy hh:mm or mm.dd.yyyy hh:mm
            If Val(Mid(date, 4, 2))>12 ; is it mm.dd.yyyy?
              ProcedureReturn "%mm"+Mid(date, 3, 1)+"%dd"+Mid(date, 6, 1)+"%yyyy"+Mid(date, 11, 1)+"%hh"+Mid(date, 14, 1)+"%ii"
            Else ; default is dd.mm.yyyy
              ProcedureReturn "%dd"+Mid(date, 3, 1)+"%mm"+Mid(date, 6, 1)+"%yyyy"+Mid(date, 11, 1)+"%hh"+Mid(date, 14, 1)+"%ii"
            EndIf
            
          Else
            ProcedureReturn "" ; not a date - sort as string
          EndIf
        Else
          ProcedureReturn "" ; not a date - sort as string
        EndIf           
        
      Case 5 ; 5 other chars, possibly DateTime?
        
        If Len(date)=19 ;yyyy-mm-dd hh:mm, dd-mm-yyyy hh:mm or mm-dd-yyyy hh:mm
          
          If (Not IsNumChar(@date, 5) And Not IsNumChar(@date, 8)) ; yyyy.mm.dd xxxxx
            ProcedureReturn "" ; faster to sort as string
            
          ElseIf (Not IsNumChar(@date, 3) And Not IsNumChar(@date, 6)) ; dd.mm.yyyy hh:mm or mm.dd.yyyy hh:mm
            If Val(Mid(date, 4, 2))>12 ; is it mm.dd.yyyy?
              ProcedureReturn "%mm"+Mid(date, 3, 1)+"%dd"+Mid(date, 6, 1)+"%yyyy"+Mid(date, 11, 1)+"%hh"+Mid(date, 14, 1)+"%ii"+Mid(date, 17, 1)+"%ss"
            Else ; default is dd.mm.yyyy
              ProcedureReturn "%dd"+Mid(date, 3, 1)+"%mm"+Mid(date, 6, 1)+"%yyyy"+Mid(date, 11, 1)+"%hh"+Mid(date, 14, 1)+"%ii"+Mid(date, 17, 1)+"%ss"
            EndIf
            
          Else
            ProcedureReturn "" ; not a date - sort as string
          EndIf
        Else
          ProcedureReturn "" ; not a date - sort as string
        EndIf
        
      Default
        ProcedureReturn ""
    EndSelect
    
  EndProcedure
  Procedure   SortListView(hWndListView, SortKey, SortType.b, SortOrder.b)
    ; ' -----------------------------------------------------
    ; ' Öffentlich aufzurufende Prozedur SortListView, die
    ; ' für die individuelle Sortierung einer ListView-Spalte
    ; ' sorgt.
    ; ' -----------------------------------------------------
    ; ' hWndListView: Fensterhandle des ListView-Steuerelements
    ; ' SortKey:      Spalte (nullbasiert), die sortiert werden
    ; '               soll (= Spaltennummer - 1).
    ; ' SortType:     stString, um Strings zu sortieren (Standardwert)
    ; '               stDate, um Datumsangaben zu sortieren
    ; '               stNumeric, um Zahlen zu sortieren
    ; ' SortOrder:    lvwAscending für aufsteigende Sortierung (Std.)
    ; '               lvwDescending für absteigende Sortierung
    ; ' -----------------------------------------------------
    
    Protected udtLVWSORT.LVWSORT
    Protected sDateFormat.s, sTemp.s, GadId.i
    
    If SortType = #SortDate
      GadId = GetDlgCtrlID_(hWndListView)
      sDateFormat = GetDateFormat(GetGadgetItemText(GadId, 0, SortKey))
      
      If sDateFormat = ""
        SortType = #SortString
      Else
        sTemp = GetDateFormat(GetGadgetItemText(GadId, CountGadgetItems(GadId)-1, SortKey))
        If sTemp=""
          SortType=#SortString
        Else
          If sTemp<>sDateFormat
            If Left(sTemp, 3)="%mm" ; new format starts with %mm (.dd.yyyy) - if this US format is detected it has higher prio
              sDateFormat=sTemp
            EndIf
          EndIf
          sTemp=GetDateFormat(GetGadgetItemText(GadId, CountGadgetItems(GadId)/2, SortKey))
          If sTemp=""
            SortType=#SortString
          Else
            If sTemp<>sDateFormat
              If Left(sTemp, 3)="%mm" ; new format starts with %mm (.dd.yyyy) - if this US format is detected it has higher prio
                sDateFormat=sTemp
              EndIf
            EndIf
          EndIf   
        EndIf         
      EndIf
      udtLVWSORT\DateFormat=sDateFormat
      ;Debug "Final DateFormat >"+sDateFormat+"<"
    ElseIf SortType = #SortDateDDMMYYYYHHIISS
      udtLVWSORT\DateFormat = "%dd.%mm.%yyyy %hh:%ii:%ss"
    EndIf
    
    ; Übergebene Informationen in einer LVWSORT-Struktur zusammenfassen:
    udtLVWSORT\hWndListView=hWndListView
    udtLVWSORT\SortKey=SortKey
    udtLVWSORT\SortOrder=SortOrder
    udtLVWSORT\SortType=SortType   
    
    ; Eigene Sortierfunktionalität in der Funktion CompareFunc verwenden: Die Informationen der
    ; LVWSORT-Struktur wird mithilfe eines Zeigers auf die Variable udtLVWSORT beigegeben:
    SendMessage_(hWndListView, #LVM_SORTITEMSEX, @udtLVWSORT, @CompareFunc())
    
  EndProcedure
  Procedure.b DetectOrderType(sText.s)
    
    Protected Diff
    
    Diff=Len(sText)-CountString(sText, "0")-CountString(sText, "1")-CountString(sText, "2")-CountString(sText, "3")-CountString(sText, "4")-CountString(sText, "5")-CountString(sText, "6")-CountString(sText, "7")-CountString(sText, "8")-CountString(sText, "9")   
    
    If Diff > 0
      If FindString(" KB MB GB TB", UCase(Right(sText, 3)))
        ProcedureReturn #SortFileSize
      EndIf
    EndIf
    
    Select Diff
      Case 0 ; es sind nur Ziffern
        ProcedureReturn #SortNumeric
        
      Case 1 ; nur 1 anderes Zeichen
        If (CountString(sText, ",")>0 Or CountString(sText, ".")>0)
          ProcedureReturn #SortFloat
        ElseIf (Left(sText, 1)="$" Or Left(sText, 1)="%") ; es ist eine HEX oder Binär Zahl
          ProcedureReturn #SortNumeric
        Else
          ProcedureReturn #SortString
        EndIf
        
      Case 2 ; 2 andere Zeichen - evtl. Datum?
        
        If (Len(sText)=10 And
          Not IsNumChar(@sText, 3) And Not IsNumChar(@sText, 6))
          ; dd-mm-yyyy or mm-dd-yyyy
          ProcedureReturn #SortDate
        Else
          ; yyyy-mm-dd
          ProcedureReturn #SortString
        EndIf

      Case 4 ; 4 other chars, possibly DateTime?
        
        If (Len(sText)=16 And
          Not IsNumChar(@sText, 3) And Not IsNumChar(@sText, 6) And
          Not IsNumChar(@sText, 11) And Not IsNumChar(@sText, 14))
          ;dd-mm-yyyy hh:mm or mm-dd-yyyy hh:mm
          ProcedureReturn #SortDate
        Else
          ProcedureReturn #SortString
        EndIf
        
      Case 5 ; 5 other chars, possibly DateTime?
        Debug Len(sText)
        If (Len(sText)=19 And
          Not IsNumChar(@sText, 3) And Not IsNumChar(@sText, 6) And
          Not IsNumChar(@sText, 11) And Not IsNumChar(@sText, 14) And Not IsNumChar(@sText, 17))
          ;dd-mm-yyyy hh:mm:ss or mm-dd-yyyy hh:mm:ss
          ProcedureReturn #SortDate
        Else
          ProcedureReturn #SortString
        EndIf         
        
      Default
        ProcedureReturn #SortString
        
    EndSelect
    
  EndProcedure
  Procedure.i GetSortOrderType(gadget, Column)

    Protected Result = #SortAutoDetect
    
    If FindMapElement(SortOrder(), Str(gadget))
      If ParseJSON(0, SortOrder()\SortOrderType)
        Dim SortTypes.i(0)
        ExtractJSONArray(JSONValue(0), SortTypes())
        
        If Column <= ArraySize(SortTypes())
          Result = SortTypes(Column)
        EndIf
        
        FreeJSON(0)
      EndIf
    EndIf
    
    ProcedureReturn Result
  EndProcedure
  
  
  Procedure   SortColumn(gadget, Column)
    
    Protected ColCnt, Order, iStartT, iEndT, Temp.b, OrderType.b
    
    If Not IsGadget(gadget) Or GetSortState(gadget) = 0 : ProcedureReturn #False : EndIf
    
    ;Debug "LIG_SortColumn >"+Str(Gadget)+"< Spalte >"+Str(Column)+"<"
    
    Select GetSortOrder(gadget, Column)
      Case #NoSort, #DescSort
        Order = #AscSort
      Case #AscSort
        Order = #DescSort
    EndSelect
    
    iStartT=ElapsedMilliseconds()
    
    OrderType = GetSortOrderType(gadget, Column)
    
    If OrderType = #SortAutoDetect ; detect it automatically - check first, last and middle item of list
      OrderType = DetectOrderType(GetGadgetItemText(gadget, 0, Column))
      
      If (OrderType = DetectOrderType(GetGadgetItemText(gadget, CountGadgetItems(gadget)-1, Column)))
        If (OrderType <> DetectOrderType(GetGadgetItemText(gadget, CountGadgetItems(gadget)/2, Column)))
          ;Debug "Different OrderType - use SortString 2"
          OrderType = #SortString
        EndIf
      Else
        ;Debug "Different OrderType - use SortString"
        OrderType = #SortString
      EndIf
      
    ElseIf OrderType = #SortNone
      ProcedureReturn #False
    EndIf   
    
    SortListView(GadgetID(gadget), Column, OrderType, Order)
    
    iEndT = ElapsedMilliseconds()
    
    ;Debug "Dauer >"+StrF( (iEndT-iStartT)/1000, 2)+"<"
    
    SetSortIcon(gadget, Column, Order)
    
    If (GetGadgetState(gadget) > -1)
      EnsureVisible(gadget, GetGadgetState(gadget))
    EndIf
    
  EndProcedure
  
  ;- ##### ListIconGadget Sort Ende ######
  
  Procedure ColumnClickCallback(hwnd, uMsg, wParam, lParam, id, *RefData)
    ; Edel
    Protected *msg.NM_LISTVIEW
    
    If uMsg = #WM_NOTIFY
      *msg = lParam
      If *msg\hdr\code = #LVN_COLUMNCLICK   
        SortColumn(GetDlgCtrlID_(*msg\hdr\hwndFrom), *msg\iSubItem)
      EndIf
    EndIf
    
    ProcedureReturn DefSubclassProc(hwnd, uMsg, wParam, lParam)
    
  EndProcedure
 
  Procedure SetColumnAlignment (gadget, Column, flags); 0-Left(Default), 1-Right, 2-Center
    Protected lvc.LV_COLUMN
    lvc\Mask = #LVCF_FMT
    Select flags
      Case 1
        lvc\fmt=#LVCFMT_RIGHT
      Case 2
        lvc\fmt=#LVCFMT_CENTER
      Default
        lvc\fmt=#LVCFMT_LEFT
    EndSelect
    ProcedureReturn SendMessage_(GadgetID(gadget),#LVM_SETCOLUMN,Column,@lvc)
  EndProcedure

  
  Procedure SetSortOrderType(gadget, Column, SortType)
    
    ; Debug "SetSortOrderType(gadget, Column, SortType)"
    ; Debug "Column: " + Str(Column) + " = " + Str(SortType)
    ; CallDebugger
    
    If FindMapElement(SortOrder(), Str(gadget))
      
      If ParseJSON(0, SortOrder()\SortOrderType)
        
        Dim SortTypes.i(0)
        ExtractJSONArray(JSONValue(0), SortTypes())
      
        If Column > ArraySize(SortTypes())
          Redim SortTypes.i(Column)
        EndIf
        
        FreeJSON(0)
      Else
        Dim SortTypes.i(Column)
      EndIf
      
      SortTypes(Column) = SortType
        
      If CreateJSON(0)
        InsertJSONArray(JSONValue(0), SortTypes())
        SortOrder()\SortOrderType = ComposeJSON(0)
        FreeJSON(0)
      EndIf
      
    EndIf
  EndProcedure
  
  Procedure ClearSort (gadget)
    Protected n
    For n = 0 To CountGadgetColumns(gadget)
      SetSortIcon(gadget, n, #NoSort)
    Next
  EndProcedure
  
  
  Procedure DisableSort (gadget, disable.b = #TRUE)
    If FindMapElement(SortOrder(), Str(gadget))
      SortOrder()\activ = disable!1
    EndIf  
  EndProcedure
  
  Procedure Enable(gadget)
    ; --- method by edel
    SetWindowSubclass(GetParent_(GadgetID(gadget)), @ColumnClickCallback(), 0, 0)
    If AddMapElement(SortOrder(), Str(gadget))
      SortOrder()\activ = 1
    EndIf
  EndProcedure
  
  EndModule
  ;}
  
  DisableExplicit
  
CompilerElse
  
  MessageRequester("PureBasic", "PureBasic V5.20 or higher needed!") : End
  
CompilerEndIf
CompilerIf #PB_Compiler_IsMainFile
  
  Enumeration #PB_Compiler_EnumerationValue
    #MainWin
  EndEnumeration
  Enumeration #PB_Compiler_EnumerationValue
    #ListIcon
  EndEnumeration
  
  Procedure OpenMainWin()
    OpenWindow(#MainWin, 0, 0, 850, 500, "ListIconGadget-SortExample", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    
    ListIconGadget(#ListIcon, 10, 10, 830, 480, "COL 0", 150, #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect)
    AddGadgetColumn(#ListIcon, 1, "COL 1 (Pixel)", 80)
    AddGadgetColumn(#ListIcon, 2, "COL 2 (fSize)", 100)
    AddGadgetColumn(#ListIcon, 3, "COL 3 (STRG)", 100)
    AddGadgetColumn(#ListIcon, 4, "COL 4 (NUM)", 80)
    AddGadgetColumn(#ListIcon, 5, "COL 5 (FLOAT)", 80)
    AddGadgetColumn(#ListIcon, 6, "COL 6 (DATE)", 90)
    AddGadgetColumn(#ListIcon, 7, "COL 7 (DATETIME)", 120)
  EndProcedure
  
  Procedure MainWin_Events(Event)
    Select Event
      Case #PB_Event_CloseWindow
        ProcedureReturn #False
        
    EndSelect
    ProcedureReturn #TRUE
  EndProcedure
  
  
  Define iEvent.i
  Define iEventWindow.i
  Define iCloseAll.i
  Define a.i, x.i
  Define Row$
  
  OpenMainWin()
  
  ; generate Test Values:
  
  For a = 0 To 1000
    
    Row$ = "COL 0, Row "+RSet(Str(  a  ),6,"0") + #LF$
    
    Row$ + Str(Random($2000)) + "x" + Str(Random($2000)) + #LF$
    
    Row$ + StrF(Random(1000)/Random(100, 1), 2)
    Select Random(4, 1)
      Case 1 : Row$ + " KB"
      Case 2 : Row$ + " MB"
      Case 3 : Row$ + " GB"
      Case 4 : Row$ + " TB"
    EndSelect
    Row$ + #LF$
    
    Row$ + "$"+RSet(Hex(Random($7FFFFFFF)),8,"0") + #LF$
    
    Select Random(5, 1)
      Case 1
        Row$+Str(Random(9, 0))
      Case 2
        Row$+Str(Random(99, 10))
      Case 3
        Row$+Str(Random(999, 100))
      Case 4
        Row$+Str(Random(9999, 1000))
      Case 5
        Row$+Str(Random(99999, 10000))
    EndSelect         
    Row$ + #LF$
    
    Select Random(5, 1)
      Case 1
        Row$+Str(Random(9, 0))+","+Str(Random(99, 0))
      Case 2
        Row$+Str(Random(99, 10))+","+Str(Random(99, 0))
      Case 3
        Row$+Str(Random(999, 100))+","+Str(Random(99, 0))
      Case 4
        Row$ + Str(Random(9999, 1000))+","+Str(Random(99, 0))
      Case 5
        Row$+Str(Random(99999, 10000))+","+Str(Random(99, 0))
    EndSelect
    Row$ + #LF$
    
    Row$ + FormatDate("%dd.%mm.%yyyy", Random(Date(), 0)) + #LF$
    
    Row$ + FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss", Random(Date(), 0)) + #LF$
    
    
    AddGadgetItem(#ListIcon, a, Row$ )
    
  Next
  
  Enumeration ; Type of Column Sort
    #SortAutoDetect
    #SortString
    #SortNumeric
    #SortFloat
    #SortDate
    #SortDateDDMMYYYYHHIISS
    #SortFileSize
    #SortPixel
    #SortNone
  EndEnumeration
  
  
  nalorLIG::Enable(#ListIcon)
  
  ;nalorLIG::SetSortOrderType(#ListIcon, 0, #SortNone)
  
  nalorLIG::SetSortOrderType(#ListIcon, 0, #SortString)
  nalorLIG::SetSortOrderType(#ListIcon, 1, #SortPixel)
  nalorLIG::SetSortOrderType(#ListIcon, 2, #SortFileSize)
  nalorLIG::SetSortOrderType(#ListIcon, 3, #SortString)
  nalorLIG::SetSortOrderType(#ListIcon, 4, #SortNumeric)
  nalorLIG::SetSortOrderType(#ListIcon, 5, #SortFloat)
  nalorLIG::SetSortOrderType(#ListIcon, 6, #SortDate)
  nalorLIG::SetSortOrderType(#ListIcon, 7, #SortDateDDMMYYYYHHIISS)
  
  nalorLIG::SetColumnAlignment(#ListIcon, 1, 1)
  nalorLIG::SetColumnAlignment(#ListIcon, 2, 2)
  nalorLIG::SetColumnAlignment(#ListIcon, 3, 1)
  nalorLIG::SetColumnAlignment(#ListIcon, 4, 1)
  nalorLIG::SetColumnAlignment(#ListIcon, 5, 1)
  nalorLIG::SetColumnAlignment(#ListIcon, 6, 2)
  nalorLIG::SetColumnAlignment(#ListIcon, 7, 1)
  
  ; nalorLIG::DisableSort (#ListIcon)
  
  
  
  Repeat
    
    iEvent = WaitWindowEvent()
    iEventWindow=EventWindow()
    
    Select iEventWindow
      Case #MainWin
        If (Not MainWin_Events(iEvent))
          iCloseAll=#TRUE
        EndIf
    EndSelect
    
  Until iCloseAll=#TRUE
  
CompilerEndIf
"Daddy, I'll run faster, then it is not so far..."
Olby
Enthusiast
Enthusiast
Posts: 461
Joined: Mon Jan 12, 2009 10:33 am
Contact:

Re: ListIconGadget with Sort and Arrow Icons

Post by Olby »

Excellent code! Works really well. Thanks.

Although the latest version by dige » Mon Nov 30, 2015 12:25 pm seems to have a bug in sorting by DATE. DATETIME works fine but DATE is messed up. I haven't yet had time to look into the code. Interestingly previous iterations seem to sporadically work with DATE or DATETIME but not both at the same time :)
Intel Core i7 Quad 2.3 Ghz, 8GB RAM, GeForce GT 630M 2GB, Windows 10 (x64)
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4749
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: ListIconGadget with Sort and Arrow Icons

Post by Fangbeast »

I know my reply is a little old but I've just started using this sort in my inventory program and I colour my gadget headers and align their text headings using the below callback.

Predictably, this kills the sort direction arrow used by the sort modules.

Is there any way to add this back in?

I set the callback before the sort routines are initialised.

Code: Select all

  Program\OldListIconCallback = SetWindowLongPtr_(GadgetID(#Gadget_Mystuff_Locations),  #GWL_WNDPROC, @ColourListIconGadgetHeading())
  Program\OldListIconCallback = SetWindowLongPtr_(GadgetID(#Gadget_Mystuff_Itemlist),   #GWL_WNDPROC, @ColourListIconGadgetHeading())

Code: Select all

; Coloured header control.By srod.Purebasic 4.Windows.

Procedure ColourListIconGadgetHeading(Handle.i,  Message.i,  Wparam.i,  Lparam.i)
  Protected hdi.hd_item
  ResultCode.i = CallWindowProc_(Program\OldListIconCallback,  Handle.i,  Message.i,  Wparam.i,  Lparam.i)
  Select Message.i
    Case #WM_NOTIFY
      *pnmh.NMHDR = Lparam.i
      ; Get handle to ListIcon header control
      If *pnmh\code = #NM_CUSTOMDRAW
        *pnmcd.NMCUSTOMDRAW = Lparam.i
        ; Determine drawing stage
        Select *pnmcd\dwDrawStage
          Case #CDDS_PREPAINT
            ResultCode.i = #CDRF_NOTIFYITEMDRAW
          Case #CDDS_ITEMPREPAINT
            TextString.s = GetGadgetItemText(GetDlgCtrlID_(Handle.i), -1, *pnmcd\dwItemSpec) ; Get header text.
            If *pnmcd\uItemState & #CDIS_SELECTED  ; Check button state.
              ; DrawFrameControl_(*pnmcd\hdc,  *pnmcd\rc,  #DFC_BUTTON,  #DFCS_BUTTONPUSH | #DFCS_PUSHED)
              ; DrawFrameControl_(*pnmcd\hdc,  *pnmcd\rc,  #DFC_BUTTON,  #DFCS_FLAT)
              DrawFrameControl_(*pnmcd\hdc,  *pnmcd\rc,  #DFC_BUTTON,  #DFC_MENU)
              InflateRect_(*pnmcd\rc, -1, -1) ; Offset text because of the selected button.
              ; BOOL DrawFrameControl(
              ;   _In_  HDC hdc,
              ;   _In_  LPRECT lprc,
              ;   _In_  UINT uType,
              ;   _In_  UINT uState
              ; );
            Else
              DrawFrameControl_(*pnmcd\hdc,  *pnmcd\rc,  #DFC_BUTTON,  #DFCS_BUTTONPUSH)
            EndIf
            *pnmcd\rc\bottom -2
            *pnmcd\rc\right - 2  ; Draw background.
            SetBkMode_(*pnmcd\hdc, #TRANSPARENT)
            FillRect_(*pnmcd\hdc,  *pnmcd\rc,  Program\BackBrush)
            SetTextColor_(*pnmcd\hdc,  Program\FrontBrush)
            *pnmcd\rc\top + 2
            InflateRect_(*pnmcd\rc, -5, 0)
            If *pnmcd\rc\right > *pnmcd\rc\left
              ;DrawText_(*pnmcd\hdc,  @TextString.s,  Len(TextString.s),  *pnmcd\rc,  #DT_CENTER | #DT_VCENTER | #DT_END_ELLIPSIS)
              ;DrawText_(*pnmcd\hdc,  @TextString.s,  Len(TextString.s),  *pnmcd\rc,  #DT_LEFT   | #DT_VCENTER | #DT_END_ELLIPSIS)
              If Handle.i = GadgetID(#Gadget_Mystuff_Itemlist)
                If FindString(TextString.s, "Item name", 1, #PB_String_NoCase)
                  DrawText_(*pnmcd\hdc,  @TextString.s,  Len(TextString.s),  *pnmcd\rc,  #DT_LEFT   | #DT_VCENTER | #DT_END_ELLIPSIS)
                Else
                  ;DrawText_(*pnmcd\hdc,  @TextString.s,  Len(TextString.s),  *pnmcd\rc,  #DT_RIGHT   | #DT_VCENTER | #DT_END_ELLIPSIS)
                  DrawText_(*pnmcd\hdc,  @TextString.s,  Len(TextString.s),  *pnmcd\rc,  #DT_LEFT   | #DT_VCENTER | #DT_END_ELLIPSIS)
                EndIf
              Else
                DrawText_(*pnmcd\hdc,  @TextString.s,  Len(TextString.s),  *pnmcd\rc,  #DT_LEFT   | #DT_VCENTER | #DT_END_ELLIPSIS)
              EndIf
            EndIf
            ResultCode.i = #CDRF_SKIPDEFAULT
        EndSelect
      EndIf
  EndSelect
  ProcedureReturn ResultCode.i
EndProcedure
Amateur Radio, D-STAR/VK3HAF
Post Reply