PB Windows sort ListIconGadget items at labelclick

Share your advanced PureBasic knowledge/code with the community.
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by El_Choni.

Hi, here's some code to allow items sorting by clicking on the column label of a ListIconGadget. I know the code looks quite ugly with all that mixing of Asm/PB (and some of the Asm is not really needed, it's there only for speed and practicing), but it works.

It can be easily adapted to sort ListView gadgets internally (ListView and ListIcon are quite the same thing for Windows). If someone wants to translate this mess into something human-readable, you're welcome (Fred, is there any other way to use a custom callback function? I'm talking about the CompareFunc, not the Window callback, procedure won't work as arguments are not retrieved if not called from the PB code).

Code: Select all

#LVM_SETEXTENDEDLISTVIEWSTYLE = #LVM_FIRST + 54
#LVM_GETEXTENDEDLISTVIEWSTYLE = #LVM_FIRST + 55
#LVS_EX_GRIDLINES = 1
#LVS_EX_FULLROWSELECT = $20

Global ListIconGadget.l, Buffer1.l, Buffer2.l, lvi.LV_ITEM, lParamSort.l, updown.l, lastcol.l

Buffer1 = AllocateMemory(0, 128, 0)
Buffer2 = AllocateMemory(1, 128, 0)
returnaddress.l
item1.l
item2.l

!jmp jmpproc
CompareFunc:
!   pop dword [v_returnaddress]
!   pop dword [v_item1]
!   pop dword [v_item2]
!   pop dword [v_lParamSort]
  lvi\iSubItem = lParamSort
  lvi\pszText = Buffer1
  lvi\cchTextMax = 128
  lvi\mask = #LVIF_TEXT
  SendMessage_(ListIconGadget, #LVM_GETITEMTEXT, item1, @lvi)
  lvi\pszText = Buffer2
  SendMessage_(ListIconGadget, #LVM_GETITEMTEXT, item2, @lvi)
! compare:
!   push ecx
!   mov eax, [v_Buffer1]
!   mov edx, [v_Buffer2]
! .upcase1:
!   mov cl, [eax]
!   cmp cl, "a"
!   jb .upcase2
!   cmp cl, "z"
!   ja .upcase2
!   sub cl, 32
! .upcase2:
!   mov ch, [edx]
!   cmp ch, "a"
!   jb .upcased
!   cmp ch, "z"
!   ja .upcased
!   sub ch, 32
! .upcased:
!   cmp cl, ch
!   ja .above
!   jb .below
!   inc eax
!   inc edx
!   Or cl, ch
!   jnz .upcase1
!   xor eax, eax
!   jmp .endcompare
! .above:
!   mov eax, [v_updown]
!   jmp .endcompare
! .below:
!   mov eax, [v_updown]
!   neg eax
! .endcompare:
!   pop ecx
!   jmp [v_returnaddress]
!jmpproc:

Procedure UpdatelParam()
  ItemCount = SendMessage_(ListIconGadget, #LVM_GETITEMCOUNT, 0, 0)
  lvi\mask = #LVIF_PARAM
  lvi\iItem = 0
  While ItemCount>0
    lvi\lParam = lvi\iItem
    For SubItem = 0 To 3
      lvi\iSubItem = SubItem 
      SendMessage_(ListIconGadget, #LVM_SETITEM, 0, @lvi)
    Next SubItem
    lvi\iItem = lvi\iItem+1
    ItemCount = ItemCount-1
  Wend
EndProcedure

Procedure ColumnClickCallback(WindowID, Message, wParam, lParam)
  If Message = #WM_NOTIFY
    *msg.NMHDR = lParam
    If *msg\hwndFrom = ListIconGadget And *msg\code = #LVN_COLUMNCLICK
      *pnmv.NM_LISTVIEW = lParam
      If lastcol*pnmv\iSubItem
        updown = 1
      EndIf
      SendMessage_(ListIconGadget, #LVM_SORTITEMS, *pnmv\iSubItem, ?CompareFunc)
      UpdatelParam()
      UpdateWindow_(ListIconGadget)
      lastcol = *pnmv\iSubItem
      updown = -updown
    EndIf
  EndIf
EndProcedure

InitGadget(1)

If OpenWindow(0, #CW_USEDEFAULT, #CW_USEDEFAULT, #CW_USEDEFAULT, #CW_USEDEFAULT, #PB_Window_SystemMenu|#PB_Window_MinimizeGadget, "ListIconGadget sort example")

  LVWidth = WindowWidth()-4
  LVCWidth = Int(LVWidth/4-1)

  If CreateGadgetList(WindowID())
    ListIconGadget = ListIconGadget(0, 0, 0, LVWidth, WindowHeight()-30, "Column 0", LVCWidth)
    AddGadgetColumn(0, 1, "Column 1", LVCWidth)
    AddGadgetColumn(0, 2, "Column 2", LVCWidth)
    AddGadgetColumn(0, 3, "Column 3", LVCWidth)
  EndIf

  SendMessage_(ListIconGadget, #LVM_SETEXTENDEDLISTVIEWSTYLE, #LVS_EX_GRIDLINES, -1) ; show grid lines
  SendMessage_(ListIconGadget, #LVM_SETEXTENDEDLISTVIEWSTYLE, #LVS_EX_FULLROWSELECT, -1) ; the entire row is highlighted when the item is selected

  AddListIconGadgetItem(0, 0, "Aaa 1"+Chr(10)+"Bcc 3"+Chr(10)+"Cdd 2"+Chr(10)+"Eee 3"+Chr(10), 0)
  AddListIconGadgetItem(0, 1, "Aab 2"+Chr(10)+"Bbc 2"+Chr(10)+"Ddd 3"+Chr(10)+"Dde 1"+Chr(10), 0)
  AddListIconGadgetItem(0, 2, "Abb 3"+Chr(10)+"Baa 1"+Chr(10)+"Ccd 1"+Chr(10)+"Dee 2"+Chr(10), 0)

  updown = 1
  lastcol = 0
  UpdatelParam()
  SetWindowCallback(@ColumnClickCallback())

  Repeat

    EventID.l = WaitWindowEvent()

    Select EventID
      Case #PB_EventCloseWindow
        Quit = 1
    EndSelect
  Until Quit = 1
EndIf

End
Hope it's useful. Bye,

El_Choni
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by MrVainSCL.

Hi El_Choni!
Very nice example... works fine on my system... keep on your work...


PIII450, 256MB Ram, 6GB HD, RivaTNT, DirectX8.1, SB AWE64, Win98SE + Updates...

greetz
MrVainSCL! aka Thorsten
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by fred.

Modified for PureBasic v3.20 and better

Code: Select all

#LVM_SETEXTENDEDLISTVIEWSTYLE = #LVM_FIRST + 54
#LVM_GETEXTENDEDLISTVIEWSTYLE = #LVM_FIRST + 55

Global ListIconGadget.l, Buffer1.l, Buffer2.l, lvi.LV_ITEM, lParamSort.l, updown.l, lastcol.l

Buffer1 = AllocateMemory(0, 128, 0)
Buffer2 = AllocateMemory(1, 128, 0)
returnaddress.l
item1.l
item2.l

!jmp jmpproc
CompareFunc:
!   pop dword [v_returnaddress]
!   pop dword [v_item1]
!   pop dword [v_item2]
!   pop dword [v_lParamSort]
  lvi\iSubItem = lParamSort
  lvi\pszText = Buffer1
  lvi\cchTextMax = 128
  lvi\mask = #LVIF_TEXT
  SendMessage_(ListIconGadget, #LVM_GETITEMTEXT, item1, @lvi)
  lvi\pszText = Buffer2
  SendMessage_(ListIconGadget, #LVM_GETITEMTEXT, item2, @lvi)
! compare:
!   push ecx
!   mov eax, [v_Buffer1]
!   mov edx, [v_Buffer2]
! .upcase1:
!   mov cl, [eax]
!   cmp cl, "a"
!   jb .upcase2
!   cmp cl, "z"
!   ja .upcase2
!   sub cl, 32
! .upcase2:
!   mov ch, [edx]
!   cmp ch, "a"
!   jb .upcased
!   cmp ch, "z"
!   ja .upcased
!   sub ch, 32
! .upcased:
!   cmp cl, ch
!   ja .above
!   jb .below
!   inc eax
!   inc edx
!   Or cl, ch
!   jnz .upcase1
!   XOr eax, eax
!   jmp .endcompare
! .above:
!   mov eax, [v_updown]
!   jmp .endcompare
! .below:
!   mov eax, [v_updown]
!   neg eax
! .endcompare:
!   pop ecx
!   jmp [v_returnaddress]
!jmpproc:

Procedure UpdatelParam()
  ItemCount = SendMessage_(ListIconGadget, #LVM_GETITEMCOUNT, 0, 0)
  lvi\mask = #LVIF_PARAM
  lvi\iItem = 0
  While ItemCount>0
    lvi\lParam = lvi\iItem
    For SubItem = 0 To 3
      lvi\iSubItem = SubItem 
      SendMessage_(ListIconGadget, #LVM_SETITEM, 0, @lvi)
    Next SubItem
    lvi\iItem = lvi\iItem+1
    ItemCount = ItemCount-1
  Wend
EndProcedure

Procedure ColumnClickCallback(WindowID, Message, wParam, lParam)
  Result = #PB_ProcessPureBasicEvents

  If Message = #WM_NOTIFY
    *msg.NMHDR = lParam
    If *msg\hwndFrom = ListIconGadget And *msg\code = #LVN_COLUMNCLICK
      *pnmv.NM_LISTVIEW = lParam
      If lastcol*pnmv\iSubItem
        updown = 1
      EndIf
      SendMessage_(ListIconGadget, #LVM_SORTITEMS, *pnmv\iSubItem, ?CompareFunc)
      UpdatelParam()
      UpdateWindow_(ListIconGadget)
      lastcol = *pnmv\iSubItem
      updown = -updown
    EndIf
  EndIf
  
  Procedurereturn Result
EndProcedure


If OpenWindow(0, 200, 200, 500, 500, #PB_Window_SystemMenu|#PB_Window_MinimizeGadget, "ListIconGadget sort example")

  LVWidth = WindowWidth()-4
  LVCWidth = Int(LVWidth/4-1)

  If CreateGadgetList(WindowID())
    ListIconGadget = ListIconGadget(0, 0, 0, LVWidth, WindowHeight()-30, "Column 0", LVCWidth, #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect)
    AddGadgetColumn(0, 1, "Column 1", LVCWidth)
    AddGadgetColumn(0, 2, "Column 2", LVCWidth)
    AddGadgetColumn(0, 3, "Column 3", LVCWidth)
  EndIf

  AddGadgetItem(0, 0, "Aaa 1"+Chr(10)+"Bcc 3"+Chr(10)+"Cdd 2"+Chr(10)+"Eee 3"+Chr(10), 0)
  AddGadgetItem(0, 1, "Aab 2"+Chr(10)+"Bbc 2"+Chr(10)+"Ddd 3"+Chr(10)+"Dde 1"+Chr(10), 0)
  AddGadgetItem(0, 2, "Abb 3"+Chr(10)+"Baa 1"+Chr(10)+"Ccd 1"+Chr(10)+"Dee 2"+Chr(10), 0)

  updown = 1
  lastcol = 0
  UpdatelParam()
  SetWindowCallback(@ColumnClickCallback())

  Repeat

    EventID.l = WaitWindowEvent()

    Select EventID
      Case #PB_EventCloseWindow
        Quit = 1
    EndSelect
  Until Quit = 1
EndIf

End

Fred - AlphaSND
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by fweil.

...,

Congrats. It's more than nice and you answer so to both a feature I was trying to put in one of my pieces of code and also giving a good assembly sample.

Really nice.

Francois Weil
14, rue Douer
F64100 Bayonne
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by El_Choni.

Here, an asm-free version with window size handling :)

Code: Select all

#LVM_SETEXTENDEDLISTVIEWSTYLE = #LVM_FIRST + 54
#LVM_GETEXTENDEDLISTVIEWSTYLE = #LVM_FIRST + 55

Global ListIconGadget.l, Buffer1.l, Buffer2.l, lvi.LV_ITEM, updown.l, lastcol.l

Buffer1 = AllocateMemory(128)
Buffer2 = AllocateMemory(128)

Procedure CompareFunc(item1, item2, lParamSort)
  result = 0
  lvi\iSubItem = lParamSort
  lvi\pszText = Buffer1
  lvi\cchTextMax = 128
  lvi\mask = #LVIF_TEXT
  SendMessage_(ListIconGadget, #LVM_GETITEMTEXT, item1, @lvi)
  lvi\pszText = Buffer2
  SendMessage_(ListIconGadget, #LVM_GETITEMTEXT, item2, @lvi)
  Seeker1 = Buffer1
  Seeker2 = Buffer2
  done = 0
  While done=0
    char1 = Asc(UCase(Chr(PeekB(Seeker1))))
    char2 = Asc(UCase(Chr(PeekB(Seeker2))))
    result = (char1-char2)*updown
    If result0 Or (Seeker1-Buffer1)>127
      done = 1
    EndIf
    Seeker1+1
    Seeker2+1
  Wend
  ProcedureReturn result
EndProcedure

Procedure UpdatelParam()
  ItemCount = SendMessage_(ListIconGadget, #LVM_GETITEMCOUNT, 0, 0)
  lvi\mask = #LVIF_PARAM
  lvi\iItem = 0
  While ItemCount>0
    lvi\lParam = lvi\iItem
    For SubItem = 0 To 3
      lvi\iSubItem = SubItem 
      SendMessage_(ListIconGadget, #LVM_SETITEM, 0, @lvi)
    Next SubItem
    lvi\iItem = lvi\iItem+1
    ItemCount = ItemCount-1
  Wend
EndProcedure

Procedure ColumnClickCallback(hWnd, uMsg, wParam, lParam)
  result = #PB_ProcessPureBasicEvents
  Select uMsg
    Case #WM_NOTIFY
      *msg.NMHDR = lParam
      If *msg\hwndFrom = ListIconGadget And *msg\code = #LVN_COLUMNCLICK
        *pnmv.NM_LISTVIEW = lParam
        If lastcol*pnmv\iSubItem
          updown = 1
        EndIf
        SendMessage_(ListIconGadget, #LVM_SORTITEMS, *pnmv\iSubItem, @CompareFunc())
        UpdatelParam()
        UpdateWindow_(ListIconGadget)
        lastcol = *pnmv\iSubItem
        updown = -updown
      EndIf
    
    Case #WM_SIZE
      If hWnd = WindowID() And IsIconic_(hWnd)=0
        WindowWidth = lParam & $ffff
        WindowHeight = lParam>>16
        ResizeGadget(0, 0, 0, WindowWidth, WindowHeight)
        result = 1
      EndIf
  EndSelect
  
  ProcedureReturn result
EndProcedure

If OpenWindow(0, 384, 288, 640, 480, #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget, "ListIconGadget sort example")
  LVWidth = WindowWidth()
  LVCWidth = Int(LVWidth/4)-1
  If CreateGadgetList(WindowID())
    ListIconGadget = ListIconGadget(0, 0, 0, LVWidth, WindowHeight(), "Column 0", LVCWidth, #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect)
    AddGadgetColumn(0, 1, "Column 1", LVCWidth)
    AddGadgetColumn(0, 2, "Column 2", LVCWidth)
    AddGadgetColumn(0, 3, "Column 3", LVCWidth)
  EndIf

  AddGadgetItem(0, 0, "Aaa 1"+Chr(10)+"Bcc 3"+Chr(10)+"Cdd 2"+Chr(10)+"Eee 3"+Chr(10), 0)
  AddGadgetItem(0, 1, "Aab 2"+Chr(10)+"Bbc 2"+Chr(10)+"Ddd 3"+Chr(10)+"Dde 1"+Chr(10), 0)
  AddGadgetItem(0, 2, "Abb 3"+Chr(10)+"Baa 1"+Chr(10)+"Ccd 1"+Chr(10)+"Dee 2"+Chr(10), 0)
  updown = 1
  lastcol = 0
  UpdatelParam()
  SetWindowCallback(@ColumnClickCallback())

  Repeat
    EventID = WaitWindowEvent()
  Until EventID = #PB_EventCloseWindow
EndIf
End
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by Fangbeast.

Thanks El_Choni, your code is fun and just what I was looking for since a friend asked me "Why can't I sort the stuff the way I want".

Friends make you program all sorts of things without sleep and food:)

Fangles
Post Reply