It's very, very far away
Sort ListIconGadget [Windows only]
Re: Sort ListIconGadget [Windows only]
Re: Sort ListIconGadget [Windows only]
You convinced me. I updated the code with your logic.ChrisR wrote: Tue Feb 25, 2025 6:40 pmYeah, it depends on how you see it:Zapman wrote: Tue Feb 25, 2025 6:04 pm I totally agree that the consequence (down arrow for ascending sorting) can seem inverted for certains. But an arrow pointing to the sky when values grow from up to down is too weird for me![]()
An arrow pointing to the sky.
Or an arrow smaller at top, larger at bottom for ascending sorting from smallest to largest value, as in Explorer
![]()
Re: Sort ListIconGadget [Windows only]
Just for the game...
This code is now Windows XP compatible.
Mesa.
This code is now Windows XP compatible.
Code: Select all
;*****************************************************************************
;
; ColumnSortedListIconGadget.pbi
; Zapman - Feb 2025-7 - Windows Only
;
; This file must be saved under the name 'ColumnSortedListIconGadget.pbi'
;
; This library provides functionality to add sorting capabilities
; to a ListIconGadget, including sorting based on ascending or descending
; order, and displaying arrows for sorting in the header.
; The library can also be used to remove accents from text for proper sorting.
;
;*****************************************************************************
;
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
CompilerEndIf
;
; To sort items by date, the local date format is needed.
; Adapt the following if necessary:
;
Global DateFormat$ = Space(255) ; Create a buffer to get the local date format:
GetLocaleInfo_(#LOCALE_USER_DEFAULT, #LOCALE_SSHORTDATE, @DateFormat$, Len(DateFormat$))
Global DateSeparator$ = Space(4)
GetLocaleInfo_(#LOCALE_USER_DEFAULT, #LOCALE_SDATE, @DateSeparator$, Len(DateSeparator$))
DateFormat$ = "%" + LCase(ReplaceString(DateFormat$, DateSeparator$, DateSeparator$ + "%"))
DateFormat$ = ReplaceString(DateFormat$, "%d" + DateSeparator$, "%dd" + DateSeparator$)
DateFormat$ = ReplaceString(DateFormat$, "%m" + DateSeparator$, "%mm" + DateSeparator$)
;
; To sort items by floating numbers, the local decimal separator is needed.
; Adapt the following if necessary:
;
Global DecimalSeparator$ = Space(2) ; Create a buffer to get the local decimal separator
GetLocaleInfo_(#LOCALE_USER_DEFAULT, #LOCALE_SDECIMAL, @DecimalSeparator$, Len(DecimalSeparator$))
;
; You can also decide how you want the sorts to be done:
;
Global RemoveAccentsWhenSorting = #True
Global NoCaseSorting = #True
;
; ****************************************************************************************
;
;- 1--- FIRST PART: MISCELLANEOUS FUNCTIONS (possibly reusable for other needs) ---
;
CompilerIf Not(Defined(RemoveAccents, #PB_Procedure))
Procedure.s RemoveAccents(Text$)
; Function to remove accents from a string. By Zapman.
If Text$
Protected length = Len(Text$) * 2
Protected char$, ct, Result$
Protected NormalizedText$ = Space(Length)
;
Length = FoldString_(#MAP_COMPOSITE, @Text$, - 1, @NormalizedText$, Length) - 1
;
If Length > 0 And Length <> Len(Text$)
For ct = 1 To Length
char$ = Mid(NormalizedText$, ct, 1)
If Asc(char$) < 128
Result$ + char$
EndIf
Next
Else
Result$ = Text$
EndIf
;
ProcedureReturn Result$
EndIf
EndProcedure
CompilerEndIf
;
; ****************************************************************************************
;
;- 2--- SECOND PART: SPECIALIZED FUNCTIONS FOR THIS LIBRARY ---
;
; Enumeration to define the sort type:
Enumeration CSLI_SortType
#CSLI_Descent ; Ascending sort.
#CSLI_Ascent ; Descending sort.
#CSLI_Unsorted ; No sorting (default).
EndEnumeration
;
Procedure CSLI_CreateListIconArrows(ArrowType)
;
; Function to create sorting arrow images (Up, Down, and Up-Down arrows).
;
Protected TSize = 16
Protected ASize = TSize / 4
Protected SX = TSize / 2 - ASize
Protected SY = TSize / 2
Protected NewImage = CreateImage(#PB_Any, TSize, TSize, 32, #PB_Image_Transparent)
Protected VASize, Color
;
If NewImage
If ArrowType = #CSLI_Ascent ; UpArrow.
VASize = -ASize
ElseIf ArrowType = #CSLI_Descent ; DownArrow.
VASize = ASize
SY - ASize + 1
ElseIf ArrowType = #CSLI_Unsorted ; UpDownArrow.
VASize = ASize
SY = TSize / 2 + 1
EndIf
If StartDrawing(ImageOutput(NewImage))
;
DrawingMode(#PB_2DDrawing_AlphaBlend)
Box(0, 0, TSize, TSize, 0) ; Transparent background.
;
If ArrowType = #CSLI_Unsorted
Color = RGBA(192, 192, 192, 255) ; Gray for unsorted.
Else
Color = RGBA(1, 1, 1, 255) ; Quasi-Black for sorted arrows.
; A 'Full-Black' (0, 0, 0, 255) is not used because the FillArea()
; function doesn't work with full-black.
EndIf
;
Line(SX, SY, ASize * 2 + 1, 1, Color) ; Draw the arrow shape.
Line(SX, SY, ASize, VASize, Color)
Line(SX + ASize, SY + VASize, ASize, - VASize, Color)
FillArea(SX + ASize, SY + VASize / 2, Color, Color)
If ArrowType = #CSLI_Unsorted
; A double arrow (UpDown) is drawn for #CSLI_Unsorted.
SY - ASize + 1
VASize = -ASize
Line(SX, SY, ASize * 2 + 1, 1, Color)
Line(SX, SY, ASize, VASize, Color)
Line(SX + ASize, SY + VASize, ASize, - VASize, Color)
FillArea(SX + ASize, SY + VASize / 2, Color, Color)
EndIf
StopDrawing()
EndIf
ProcedureReturn NewImage
EndIf
EndProcedure
;
Procedure CSLI_CreateListIconImgList(LIHandle)
;
; Function to create an image list for the ListIcon header.
;
Protected hHeader, hImgL, ct, Img
;
If IsGadget(LIHandle)
LIHandle = GadgetID(LIHandle) ; Get the control handle from the gadget's num.
EndIf
;
hHeader = SendMessage_(LIHandle, #LVM_GETHEADER, 0, 0)
If SendMessage_(hHeader, #HDM_GETIMAGELIST, 0, 0) = 0
hImgL = ImageList_Create_(16, 16, #ILC_COLOR32 | #ILC_MASK, 0, 0)
For ct = #CSLI_Descent To #CSLI_Unsorted
Img = CSLI_CreateListIconArrows(ct)
ImageList_Add_(hImgL, ImageID(Img), 0)
; Microsoft doc says to free the original image after adding it to the image list.
FreeImage(Img)
Next
SendMessage_(hHeader, #HDM_SETIMAGELIST, 0, hImgL)
EndIf
EndProcedure
;
Procedure CSLI_FreeImageListFromGadget(LIHandle)
;
; Destroy both image lists of a ListIconGadget.
;
; Retrieve and destroy the ImageList for list items (row icons)
Protected hImageList, hHeader, hHeaderImageList
;
hImageList = SendMessage_(LIHandle, #LVM_GETIMAGELIST, #LVSIL_NORMAL, 0)
If hImageList
ImageList_Destroy_(hImageList)
SendMessage_(LIHandle, #LVM_SETIMAGELIST, #LVSIL_NORMAL, 0)
EndIf
;
; Retrieve the handle of the header control
hHeader = SendMessage_(LIHandle, #LVM_GETHEADER, 0, 0)
If hHeader
; Retrieve and destroy the ImageList for the header (column icons)
hHeaderImageList = SendMessage_(hHeader, #HDM_GETIMAGELIST, 0, 0)
If hHeaderImageList
ImageList_Destroy_(hHeaderImageList)
SendMessage_(hHeader, #HDM_SETIMAGELIST, 0, 0)
EndIf
EndIf
EndProcedure
; Define structure for sorting information.
Structure SortInfoStruct
GadgetHandle.i
Column.l
AscentDescent.l
EndStructure
;
Procedure CSLI_ListIconSortCallback(*item1.long, *item2.long, *SortInfo.SortInfoStruct)
;
; Sorting callback function used by the ListIcon to compare items.
;
Protected A$ = Space(200), B$ = Space(200), A2$, A.q = 0, B.q = 0, lvi.LV_ITEM
Protected result = 0, AscentDescent
;
lvi\iSubItem = *SortInfo\Column
lvi\pszText = @A$
lvi\cchTextMax = 200
SendMessage_(*SortInfo\GadgetHandle, #LVM_GETITEMTEXT, *item1\l, @lvi)
lvi\pszText = @B$
SendMessage_(*SortInfo\GadgetHandle, #LVM_GETITEMTEXT, *item2\l, @lvi)
;
If A$ = B$
ProcedureReturn 0 ; Items are equal.
EndIf
;
; Determine if sorting is ascending or descending.
If *SortInfo\AscentDescent = #CSLI_Descent : AscentDescent = -1 : Else : AscentDescent = 1 : EndIf
;
; Sorting based on numeric or alphabetical values.
A$ = ReplaceString(A$, DecimalSeparator$, ".")
B$ = ReplaceString(B$, DecimalSeparator$, ".")
;
If FindString(A$, "0")
A2$ = ReplaceString(A$, "0", "")
If A2$ = "" Or A2$ = "." Or A2$ = "$"
A$ = "0"
EndIf
EndIf
;
If A$ = "0" Or Val(A$) > 0 Or Val(B$) > 0
A = Val(A$)
B = Val(B$)
EndIf
If ValF(A$) > A Or ValF(B$) > B
A = ValF(A$)
B = ValF(B$)
EndIf
If ValD(A$) > A Or ValD(B$) > B
A = ValD(A$)
B = ValD(B$)
EndIf
;
If CountString(A$, DateSeparator$) > 1 Or CountString(B$, DateSeparator$) > 1
If ParseDate(DateFormat$, A$) > ParseDate(DateFormat$, B$)
Result = AscentDescent
Else
Result = -AscentDescent
EndIf
ElseIf Abs(A) > 0 Or A$ = "0"
If A > B Or ValF(A$) > ValF(B$)
Result = AscentDescent
Else
Result = -AscentDescent
EndIf
Else
If RemoveAccentsWhenSorting
A$ = RemoveAccents(A$)
B$ = RemoveAccents(B$)
EndIf
If NoCaseSorting
A$ = LCase(A$)
B$ = LCase(B$)
EndIf
If A$ > B$
Result = AscentDescent
Else
Result = -AscentDescent
EndIf
EndIf
;
ProcedureReturn result
EndProcedure
;
Procedure CSLI_ListIconHideHeaderImages(LIHandle)
;
; Function to hide images in the ListIcon header (useful when arrows are hidden).
; It will keep the value stored in the columns by #LVM_SETCOLUMN
; (index of the current image for each column), but no image will be shown.
;
Protected NColumn, LVC.LVCOLUMN
;
If IsGadget(LIHandle)
LIHandle = GadgetID(LIHandle) ; Get the control handle from the gadget's num.
EndIf
;
While SendMessage_(LIHandle, #LVM_GETCOLUMN, NColumn, @LVC)
; Hide images
LVC\mask = #LVCF_FMT
LVC\fmt = #LVCFMT_COL_HAS_IMAGES
LVC\iImage = 0
SendMessage_(LIHandle, #LVM_SETCOLUMN, NColumn, @LVC)
NColumn + 1
Wend
EndProcedure
; Declare SortListIcon procedure:
Declare SortListIcon(LIHandle, column, AscentDescent)
;
Procedure CSLI_WinLIProc(hWnd, uMsg, wParam, lParam)
;
; Window callback procedure to handle ListIcon sorting interaction.
;
Protected NColumn, LVC.LVCOLUMN, LIHandle, AscentDescent
Protected *NMHDR.NMHDR, *NMLV.NMLISTVIEW
;
Select uMsg
Case #WM_NOTIFY
*NMHDR = lParam
If GetProp_(*NMHDR\hWndFrom, "CSLI_Sort")
; Handle sort operations on column click:
If *NMHDR\code = #LVN_COLUMNCLICK
*NMLV = lParam
LIHandle = *NMHDR\hWndFrom
; Retrieve actual image-index:
LVC\mask = #LVCF_IMAGE
SendMessage_(LIHandle, #LVM_GETCOLUMN, *NMLV\iSubItem, @LVC)
AscentDescent = LVC\iImage
; Switch the index:
If AscentDescent = #CSLI_Ascent
AscentDescent = #CSLI_Descent
Else
AscentDescent = #CSLI_Ascent
EndIf
;
; Set image-index to UpDown for all columns:
While SendMessage_(LIHandle, #LVM_GETCOLUMN, NColumn, @LVC)
LVC\iImage = #CSLI_Unsorted
SendMessage_(LIHandle, #LVM_SETCOLUMN, NColumn, @LVC)
NColumn + 1
Wend
;
If GetProp_(LIHandle, "CSLI_ShowArrow") = 0
; If CSLI_ShowArrow is not set, hide the images:
CSLI_ListIconHideHeaderImages(LIHandle)
EndIf
;
; Sort and update image-index for the clicked column.
; If CSLI_ShowArrow is set, this will update the shown image
; for the clicked column:
SortListIcon(LIHandle, *NMLV\iSubItem, AscentDescent)
;
ElseIf *NMHDR\code = #LVN_DELETEALLITEMS
; The ListIcon gadget is being destroyed.
; Clean the memory from images:
CSLI_FreeImageListFromGadget(*NMHDR\hWndFrom)
EndIf
EndIf
EndSelect
;
Protected CSLI_OldCallBack = GetProp_(hWnd, "CSLI_OldCallBack")
ProcedureReturn CallWindowProc_(CSLI_OldCallBack, hWnd, uMsg, wParam, lParam)
EndProcedure
;
; ****************************************************************************************
;
;- 3--- THIRD PART: MAIN FUNCTIONS OF THIS LIBRARY ---
;
; ****************************************************************************************
;
Procedure MakeListIconSortable(GadgetNum, TrueFalse = #True)
;
; Initialyze all the needed values of 'LIHandle' to allow it
; to be sortable.
;
Protected i.l, GadgetID, LVC.LVCOLUMN, NColumn, NItems
;
If IsGadget(GadgetNum)
Protected LIHandle = GadgetID(GadgetNum) ; Get the control handle from the gadget's num.
Else
LIHandle = GadgetNum
GadgetNum = GetProp_(LIHandle, "PB_ID") ; Get the gadget's num from its handle.
EndIf
;
If IsGadget(GadgetNum) And GadgetType(GadgetNum) = #PB_GadgetType_ListIcon ; Ensure it's a valid ListIcon gadget.
If TrueFalse ; Check if sorting should be enabled.
NItems = SendMessage_(LIHandle, #LVM_GETITEMCOUNT, 0, 0) ; Get the number of items in the ListIcon.
For i = 0 To NItems - 1 ; Loop through all the items:
SetGadgetItemData(GadgetNum, i, i) ; Set the item data to its index for sorting purposes.
Next
SetProp_(LIHandle, "CSLI_Sort", 1) ; Store the sorting state in the ListIcon handle property.
; This will allow to know that this gadget had been made sortable.
CSLI_CreateListIconImgList(LIHandle); Create the image list for sorting icons.
If GetProp_(LIHandle, "CSLI_ShowArrow") ; If the sorting arrow is enabled...
NColumn = 0
While SendMessage_(LIHandle, #LVM_GETCOLUMN, NColumn, @LVC)
LVC\mask = #LVCF_IMAGE ; Set the column to use image (for sorting arrows).
LVC\iImage = #CSLI_Unsorted ; Set the initial sorting state as unsorted.
SendMessage_(LIHandle, #LVM_SETCOLUMN, NColumn, @LVC) ; Set the column information.
NColumn + 1
Wend
Else
CSLI_ListIconHideHeaderImages(LIHandle) ; If CSLI_ShowArrow is not set, hide the header images.
EndIf
;
Protected hWnd = GetAncestor_(LIHandle, #GA_ROOT) ; Get the root window handle for the ListIcon.
Protected CSLI_OldCallBack = GetProp_(hWnd, "CSLI_OldCallBack") ; Retrieve the CSLI_OldCallBack procedure if any.
If CSLI_OldCallBack = 0 ; If there's no existing CSLI_OldCallBack, set it:
CSLI_OldCallBack = SetWindowLongPtr_(hWnd, #GWL_WNDPROC, @CSLI_WinLIProc()) ; Set a custom window procedure.
SetProp_(hWnd, "CSLI_OldCallBack", CSLI_OldCallBack) ; Store the Oldcallback procedure address.
EndIf
Else ; If sorting is disabled
CSLI_ListIconHideHeaderImages(LIHandle) ; Hide any header images.
CSLI_FreeImageListFromGadget(LIHandle) ; Free the image list from the gadget.
hWnd = GetAncestor_(LIHandle, #GA_ROOT) ; Get the root window handle for the ListIcon.
CSLI_OldCallBack = GetProp_(hWnd, "CSLI_OldCallBack") ; Retrieve the old callback procedure.
If CSLI_OldCallBack ; If there was a custom callback set earlier...
; Reset to standard management:
SetWindowLongPtr_(hWnd, #GWL_WNDPROC, CSLI_OldCallBack) ; Restore the original window procedure.
RemoveProp_(hWnd, "CSLI_OldCallBack") ; Remove the stored callback address.
EndIf
EndIf
EndIf
EndProcedure
;
;https://www.purebasic.fr/english/viewtopic.php?t=55085
Procedure LIG_SetSortIcon(ListGadget.i, Column.i, SortOrder.i)
; http://stackoverflow.com/questions/254129/how-To-i-display-a-sort-arrow-in-the-header-of-a-List-view-column-using-c
Protected ColumnHeader.i
Protected ColumnCount.i
Protected hditem.HD_ITEM
Protected Cnt.i
#NoSort = 0
#AscSort = 1
#DescSort = 2
ColumnHeader = SendMessage_(GadgetID(ListGadget), #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
EndProcedure
Procedure.b LIG_GetSortOrder(ListGadget.i, Column.i)
Protected ColumnHeader.i
Protected hditem.HD_ITEM
Protected RetVal.b
ColumnHeader = SendMessage_(GadgetID(ListGadget), #LVM_GETHEADER, 0, 0)
hditem\mask = #HDI_FORMAT
If SendMessage_(ColumnHeader, #HDM_GETITEM, Column, @hditem)
If (hditem\fmt & #HDF_SORTUP) = #HDF_SORTUP
RetVal = #AscSort
ElseIf (hditem\fmt & #HDF_SORTDOWN) = #HDF_SORTDOWN
RetVal = #DescSort
Else
RetVal = #NoSort
EndIf
Else
Debug "ERROR! LIG_GetSortOrder"
RetVal = - 1
EndIf
ProcedureReturn RetVal
EndProcedure
Procedure SortListIcon(GadgetNum, column, AscentDescent)
;
; Sort the whole content of a ListIconGadget from one column values.
;
; column : from 0 to the last column number.
; AscentDescent : #CSLI_Descent for ascending, #CSLI_Ascent for descending.
;,,
Protected SortInfo.SortInfoStruct, LVC.LVCOLUMN, Order
;
If IsGadget(GadgetNum)
Protected LIHandle = GadgetID(GadgetNum) ; Get the control handle from the gadget's num.
Else
LIHandle = GadgetNum
GadgetNum = GetProp_(LIHandle, "PB_ID") ; Get the gadget's num from its handle.
EndIf
If IsGadget(GadgetNum) And GadgetType(GadgetNum) = #PB_GadgetType_ListIcon ; Ensure it's a ListIcon gadget
MakeListIconSortable(GadgetNum) ; Initialize the ListIcon gadget with sorting enabled.
If OSVersion() = #PB_OS_Windows_XP
#NoSort = 0
#AscSort = 1
#DescSort = 2
Select LIG_GetSortOrder(GadgetNum, column)
Case #NoSort, #DescSort
Order = #AscSort
Case #AscSort
Order = #DescSort
EndSelect
LIG_SetSortIcon(GadgetNum, column, Order)
EndIf
SortInfo\GadgetHandle = LIHandle ; Store the gadget handle in the sorting structure.
SortInfo\Column = column ; Store the column to be sorted in the sorting structure.
SortInfo\AscentDescent = AscentDescent ; Store the sorting order (ascending or descending).
SendMessage_(LIHandle, #LVM_SORTITEMS, @SortInfo, @CSLI_ListIconSortCallback()) ; Perform the sorting using the callback function.
;
; Update the image index for the column to reflect the sorting order.
LVC\mask = #LVCF_IMAGE ; Set the column to use an image (for the sorting arrow).
LVC\iImage = AscentDescent ; Set the image index based on the sorting order.
SendMessage_(LIHandle, #LVM_SETCOLUMN, column, @LVC) ; Set the updated column information.
;
If GetProp_(LIHandle, "CSLI_ShowArrow") = 0 ; If CSLI_ShowArrow is not enabled...
; Hide images in the columns.
; This will keep the value stored in the column by the preceeding #LVM_SETCOLUMN message
; (index of the current image for the column), but no image will be shown.
LVC\mask = #LVCF_FMT ; Set the column format to hide images:
LVC\fmt = #LVCFMT_COL_HAS_IMAGES
LVC\iImage = 0 ; Hide the image.
SendMessage_(LIHandle, #LVM_SETCOLUMN, column, @LVC) ; Apply the changes to the column.
EndIf
ProcedureReturn #True ; Return True to indicate success.
EndIf
EndProcedure
;
Procedure ShowListIconSortingArrows(LIHandle, ShowHide = #True)
;
; Hide or show the sorting arrows in the header of 'LIHandle'.
;
Protected Order, Column = 0
If IsGadget(LIHandle) ; Check if the provided handle is a valid gadget.
LIHandle = GadgetID(LIHandle) ; Get the control handle from the gadget's num.
EndIf
SetProp_(LIHandle, "CSLI_ShowArrow", ShowHide) ; Store whether the sorting arrows should be shown or hidden.
MakeListIconSortable(LIHandle) ; Re-initialize the ListIcon with the updated sorting arrows setting.
EndProcedure
;
; ****************************************************************************************
;
;- 4--- FORTH PART: DEMO PROCEDURE ---
;
; ****************************************************************************************
;
CompilerIf #PB_Compiler_IsMainFile
; The following won't run when this file is used as 'Included'.
Define HWindow = OpenWindow(#PB_Any, 0, 0, 485, 260, "Sortable ListIconGadget demo", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
;
Define SortListIconGagdetRef = ListIconGadget(#PB_Any, 0, 0, WindowWidth(HWindow), 225, "", 0, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
;
Define MakeListIconSortableCheckBox = CheckBoxGadget(#PB_Any, 10, WindowHeight(HWindow) - 30, 150, 22, "Make ListIcon Sortable")
SetGadgetState(MakeListIconSortableCheckBox, 1)
;
Define ShowListIconSortingArrowsCheckBox = CheckBoxGadget(#PB_Any, 200, WindowHeight(HWindow) - 30, 180, 22, "Show ListIcon Sorting Arrows")
SetGadgetState(ShowListIconSortingArrowsCheckBox, 1)
ShowListIconSortingArrows(SortListIconGagdetRef) ; <-- This will automatically set the ListIconGagdet to 'Sortable'.
; If you just want to make ListIconGagdet 'Sortable' without showing the arrows in the header,
; you can call 'MakeListIconSortable()' instead of ShowListIconSortingArrows().
;
AddGadgetColumn(SortListIconGagdetRef, 0, "Column 1", 150)
AddGadgetColumn(SortListIconGagdetRef, 1, "Column 2", 100)
AddGadgetColumn(SortListIconGagdetRef, 2, "Column 3", 100)
AddGadgetColumn(SortListIconGagdetRef, 3, "Column 4", 110)
;
Define mDate.q = Date()
Define a, A$, B$, C$, D$, x
;
For a = 0 To 8
A$ = "COLUMN 1, Row " + RSet(Str(a), 3, "0") + Chr(10)
If a = 0
A$ = "élève " + A$
EndIf
If a = 1
A$ = "devant " + A$
EndIf
If a = 2
A$ = "front " + A$
EndIf
If a = 3
A$ = "Absent " + A$
EndIf
If a = 4
A$ = "Final " + A$
EndIf
x = Random($FFFF)
B$ = RSet(Str(x), 5, "0") + Chr(10)
x = Random($7FFFFFFF)
C$ = "$" + RSet(Hex(x), 8, "0") + Chr(10)
D$ = FormatDate(DateFormat$, mDate)
mDate + 100000
AddGadgetItem(SortListIconGagdetRef, - 1, A$ + B$ + C$ + D$)
Next
;
SortListIcon(SortListIconGagdetRef, 0, #CSLI_Ascent)
;
Repeat
Define Event = WaitWindowEvent()
If Event = #PB_Event_Gadget
Select EventGadget()
Case MakeListIconSortableCheckBox
Define TrueFalse = GetGadgetState(MakeListIconSortableCheckBox)
SetGadgetState(ShowListIconSortingArrowsCheckBox, TrueFalse)
If TrueFalse
ShowListIconSortingArrows(SortListIconGagdetRef) ; <-- This will automatically call MakeListIconSortable(SortListIconGagdetRef, #True).
Else
MakeListIconSortable(SortListIconGagdetRef, #False)
EndIf
Case ShowListIconSortingArrowsCheckBox
TrueFalse = GetGadgetState(ShowListIconSortingArrowsCheckBox)
If TrueFalse
SetGadgetState(MakeListIconSortableCheckBox, #True)
EndIf
ShowListIconSortingArrows(SortListIconGagdetRef, TrueFalse)
EndSelect
EndIf
Until Event = #PB_Event_CloseWindow
CompilerEndIf
Re: Sort ListIconGadget [Windows only]
Very nice work, Mesa!
Thank you for this suggestion which gives the Explorer's appearance to the sorted column, like the code of nalor did.
It can also work on later versions of Windows.
I'll work on it to better integrate this option (for example, it's not ideal to have to redefine the #NoSort, #AscSort and #DescSort constants...) and set a constant at the beginning of the program to allow to choose between to the types of appearance.
Re: Sort ListIconGadget [Windows only]
The code within the first message of this subject has been udated.
- Comments had been added to explain all the story about local date formats,
- Other comments had been added to facilitate code adaptation for special needs,
- There are now two possible manners to show the sorting arrows: on left or above the column titles.
- The demonstration code allows to choose between these two options:

- Comments had been added to explain all the story about local date formats,
- Other comments had been added to facilitate code adaptation for special needs,
- There are now two possible manners to show the sorting arrows: on left or above the column titles.
- The demonstration code allows to choose between these two options:
Re: Sort ListIconGadget [Windows only]
Hi @Zapman,
Thanks for the code update and the arrow options. I noticed one problem:
When either of the "show arrows" options are selected, clicking on the same column header alternates between ascending and descending sort, as expected.
However, if "don't show arrows" is selected, the first click sorts in ascending order, but the second (and subsequent) clicks on the same header don't do anything.
Thanks for the code update and the arrow options. I noticed one problem:
When either of the "show arrows" options are selected, clicking on the same column header alternates between ascending and descending sort, as expected.
However, if "don't show arrows" is selected, the first click sorts in ascending order, but the second (and subsequent) clicks on the same header don't do anything.
Re: Sort ListIconGadget [Windows only]
That's true.ebs wrote: Fri Feb 28, 2025 3:37 pm If "don't show arrows" is selected, the first click sorts in ascending order, but the second (and subsequent) clicks on the same header don't do anything.
I'm working on a new version to fix that and with many other improvements.
Thank you, ebs.
Re: Sort ListIconGadget [Windows only]
Waiting anxiously... 
Re: Sort ListIconGadget [Windows only]
You can relax
The code within the first message of this subject has been udated.
- Fixed: Sort works well (both ascending and descending) even when arrows are hidden.
- Fixed: The column justification format (left-center-right) was lost after sorts. It's OK now.
- Improved: The gadget item's data was used for sorting. This is no longer the case. You can therefore use the data for other things, if necessary (with SetGadgetItemData() and GetGadgetItemData()).
- Improved: Hours are sorted whatever the format used to represent them (see the last column of the demo).
Re: Sort ListIconGadget [Windows only]
I'm just curios:
Why is "6:00 PM" greater than "16h30mn"?
Isn't "6:00PM" same as "16h00mn"?
So "6:00PM" should be lower than "16h30mn".
Edit:
Just noticed, that if "Don't show arrows" is selected
after arrows were allowed and sorting was used,
the arrow remains visible.
Why is "6:00 PM" greater than "16h30mn"?
Isn't "6:00PM" same as "16h00mn"?
So "6:00PM" should be lower than "16h30mn".
Edit:
Just noticed, that if "Don't show arrows" is selected
after arrows were allowed and sorting was used,
the arrow remains visible.

- Mindphazer
- Enthusiast

- Posts: 487
- Joined: Mon Sep 10, 2012 10:41 am
- Location: Savoie
Re: Sort ListIconGadget [Windows only]
NopeLord wrote: Sat Mar 01, 2025 5:23 pm I'm just curios:
Why is "6:00 PM" greater than "16h30mn"?
Isn't "6:00PM" same as "16h00mn"?
So "6:00PM" should be lower than "16h30mn".
6:00PM is the same as 18h00, not 16h00
16h00 is equal to 4:00PM
So 6:00PM is indeed greater than 16h30 (which is equal to 4:30PM)
MacBook Pro 16" M4 Pro - 24 Gb - MacOS 15.4.1 - Iphone 15 Pro Max - iPad at home
...and unfortunately... Windows at work...
...and unfortunately... Windows at work...
Re: Sort ListIconGadget [Windows only]
Of course.
Sorry for this. I just was looking at the "30".
My fingers were faster than my brain.

Re: Sort ListIconGadget [Windows only]
Thanks again, Lord, for your accurate testing. This bug is fixed with the actual version.Lord wrote: Sat Mar 01, 2025 5:23 pmIf "Don't show arrows" is selected after arrows were allowed and sorting was used, the arrow remains visible.
And also:
- I have simplified the used of the library by reducing two functions in one. Now, simply call with four possible values for the 'Setting' parameter:
Code: Select all
MakeListIconSortable(ListIconGadget, setting)- #CSLI_NotSortable ; Reset the gadget to normal (not sortable).
- #CSLI_NoArrow ; Make the gadget sortable but don't show arrows.
- #CSLI_LeftArrows ; Show arrows on left side.
- #CSLI_TopArrows ; Show arrows above the column title.
- I've added a 'Sporty sorting' possibility, for times noted as in sport's competitions.
' for minute
'' for second
''' for hundredths of a second
so: 1'30''80''' means: one minute, 30 seconds and 80 hundredths of a second.
The last symbol is sometimes omitted, so 1'30 = 1'30'' and 1'30''80 = 1'30''80'''
Re: Sort ListIconGadget [Windows only]
Hi Zapman!
These are nice additions! And are working fine.
I now have some ideas for additional funkionality, but this would
blow up the code and make it more a extended LIG:
- different justification of columns and columns title
maybe user initiated by RMB
These are nice additions! And are working fine.
I now have some ideas for additional funkionality, but this would
blow up the code and make it more a extended LIG:
- different justification of columns and columns title
maybe user initiated by RMB

Re: Sort ListIconGadget [Windows only]
I haven't tested everything thoroughly in this sort module.
These format dates sort properly.
"%yyyy/%mm/%dd"
"%yyyy.%mm.%dd"
"%dd/%mm/%yyyy
But the following dates format don't sort properly.
"%yyyy-%mm-%dd"
"%dd-%mm-%yyyy"
"%dd.%mm.%yyyy"
Also list of dates, like.
25 Jan 2025
25 January 2025
25-Jan-2025
25/Jan/2025
or with any combination above, don't sort.
These format dates sort properly.
"%yyyy/%mm/%dd"
"%yyyy.%mm.%dd"
"%dd/%mm/%yyyy
But the following dates format don't sort properly.
"%yyyy-%mm-%dd"
"%dd-%mm-%yyyy"
"%dd.%mm.%yyyy"
Also list of dates, like.
25 Jan 2025
25 January 2025
25-Jan-2025
25/Jan/2025
or with any combination above, don't sort.
