Page 1 of 1

PB4B: Unicode & WinAPI

Posted: Mon Feb 27, 2006 10:24 pm
by Xombie
I'm not sure whether to call this a bug or what but I'm getting some oddball behavior from this code. Basically I'm building on some code by Danilo, et al to create a listicon sorter for pb4.

The problem is, well, it doesn't work for one thing so I'd appreciate some extra eyes. The sorting routine contains the same value for lParam1 and lParam2 when it should be different. The UpdatelParam() procedure doesn't even seem to be correctly setting the lParam value since the SendMessage_() is returning 0 for a failure.

The other strange-ish behavior is in the callback, the #WM_NOTIFY message is intercepting #HDN_ITEMCLICKW rather than #HDN_ITEMCLICKA even though it's being compiled as non-unicode.

Here's the code (saved as xSort.pb)

Code: Select all

;- Information
; -   Coded by Xombie 2/23/2006
; -   Sorting information/code stolen from Danilo ( http://forums.purebasic.com/english/viewtopic.php?t=9113 )
;- To Do
; -   Update Control\ColumnCount when columns are added/deleted - test for these in the callback.  Use the updatelparam then too.
;- Constants
#LVM_GETHEADER = 4127
#MCN_SELCHANGE = #MCN_FIRST + 1 
#MCN_SELECT = #MCN_FIRST + 4 
;- Enumerations
Enumeration ; Sort Types
   #x_Sort_TypeText
   #x_Sort_TypeInteger
   #x_Sort_TypeDecimal
   #x_Sort_TypeDate
EndEnumeration
;- Structures
Structure HDHITTESTINFO
   pt.POINT
   flags.l
   iItem.l
EndStructure
Structure s_x_Sort_Columns
   ; Structure used to store information about the columns in a listview control.
   Column.l
   ; The zero based index of the column being sorted.
   SortType.b
   ; The type of sorting used for the column.
EndStructure
Structure s_x_Sort_Controls
   ; Structure used to store information about the listview being sorted.
   Gadget.l
   ; The PB gadget id of the control.
   Handle.l
   ; The handle of the listview control.
   HandleHeader.l
   ; The handle to the header for the listview.
   Columns.l
   ; Pointer to array of sorted column types.
   SortedColumn.l
   ; The zero based index of the column currently being sorted.
   ColumnCount.l
   ; The number of columns in the control.
   Callback.l
   ; The old callback for the listview control.
   Size.l
   ; The size of the sorted column array.
   Count.l
   ; The count of items in the column array.
EndStructure
Structure s_x_Sort_Main
   ; The structure used to library information.
   Controls.l
   ; Pointer to 'array' of s_x_Sort_Control structures.
   Size.l
   ; The size (in bytes) of the control 'array'.
   Count.l
   ; The count of items in the control 'array'.
EndStructure
;- Library Variables
Global _xSortMain.s_x_Sort_Main
; Structure used to store information about the library.
;- Utility Functions
; Loword (a & $FFFF) - HiWord (a >> 16 & $FFFF)
Procedure.b x_Sort_IsListIcon(Handle.l) 
   ; Return the gadget type based on the classname used in CreateWindowExW_() 
   Define.s HoldString
   ; This will store the length of the wide character string, in characters. 
   Define.l lCount
   ; Used to store the number of character copied. 
   HoldString = Space(255) 
   ; Allocate size for our string. 
   GetClassName_(Handle, @HoldString, 255) 
   ; Call our function to retrieve the classname. 
   If HoldString = "SysListView32" : ProcedureReturn #True : Else : ProcedureReturn #False : EndIf
   ; Check if the control type is a listview.
EndProcedure 
Procedure.l x_Sort_ControlFromHandle(Handle.l)
   ;
   Define.l *Position
   ;
   Define.s_x_Sort_Controls *HoldControl
   ; 
   *Position = _xSortMain\Controls
   While *Position - _xSortMain\Controls < _xSortMain\Size
      ;
      *HoldControl = PeekL(*Position)
      ;
      If *HoldControl\Handle = Handle : ProcedureReturn *HoldControl : EndIf
      ;
      *Position + 4
      ;
   Wend
   ;
EndProcedure
Procedure.l x_Sort_UpdatelParam(Handle.l, ColumnCount.l)
   ; 
   ; PureArea.net CodeArchiv, by unknown 
   ; 
   ; modified by Danilo, 11.01.2004 
   ;
   ; Modified again (extremely slightly) by Xombie 2/23/2006
   ;
   Define.l ItemCount
   ;
   Define.l SubItem
   ;
   Define.LV_ITEM HoldItem
   ;
   ItemCount = SendMessage_(Handle, #LVM_GETITEMCOUNT, 0, 0) 
   ;
   HoldItem\mask = #LVIF_PARAM
   HoldItem\iItem = 0
   ;
   While ItemCount > 0
      ;
      HoldItem\lParam = HoldItem\iItem 
      ;
      For SubItem = 1 To ColumnCount
         ;
         HoldItem\iSubItem = SubItem
         ;
         Debug Str(HoldItem\iItem) + " : " + Str(HoldItem\iSubItem) + " : " + Str(HoldItem\lParam)
         ;
         SendMessage_(Handle, #LVM_SETITEM, 0, @HoldItem) 
         ;
      Next SubItem
      ;
      HoldItem\iItem + 1 
      ;
      ItemCount - 1 
      ;
   Wend 
   ;
EndProcedure 
;- Comparison Functions
Procedure.l x_Sort_CompareInteger(lParam1.l, lParam2.l, lParamSort.l)
   ;
   Define.l lResult
   ;
   Define.l Column
   ;
   Define.l Handle
   ;
   Define.s Item01, Item02
   ;
   Define.LV_ITEM HoldItem
   ;
   Column = lParamSort & $FFFF
   ;
   Handle = GadgetID(lParamSort >> 16 & $FFFF)
   ;
   Debug "Start Sort"
   Debug Str(lParam1) + " : " + Str(lParam2)
   ;
   Item01 = Space(255) : Item02 = Space(255)
   ; Create space used to store the two items to compare.
   HoldItem\iSubItem = Column
   ; Store the column used in sorting.
   HoldItem\mask = #LVIF_TEXT
   HoldItem\cchTextMax = 255
   HoldItem\pszText = @Item01
   ; Store the address to the first item.
   SendMessage_(Handle, #LVM_GETITEMTEXT, lParam1, @HoldItem) 
   ; Store the first text item being compared in the column.
   Debug Item01
   HoldItem\pszText = @Item02
   ; Set the address to the second item.
   SendMessage_(Handle, #LVM_GETITEMTEXT, lParam2, @HoldItem) 
   ; Store the second text item being compared.
   Debug Item02 : Debug " "
   ;
   If Item01 = Item02 : ProcedureReturn 0 : EndIf
   ; Check if the items are equal and return 0 if so.
   If Item01 < Item02 : ProcedureReturn -1 : Else : ProcedureReturn 1 : EndIf
   ;
   ProcedureReturn 0
   ;
EndProcedure
;- Callbacks
Procedure.l x_Sort_MainCallback(HandleWindow.l, Message.l, wParam.l, lParam.l) 
   ;
   Define.l lResult
   ;
   Define.l HiLo
   ;
   Define.s_x_Sort_Controls *HoldControl
   ;
   Define.NMHDR *pnmh
   ;
   Define.NMHEADER *phdr
   ;
   *HoldControl = x_Sort_ControlFromHandle(HandleWindow)
   ; Retrieve information about the control based on it's handle.
   If Message = #WM_NOTIFY
      ;
      *pnmh = lParam
      ;
      If *pnmh\code = #HDN_ITEMCLICKW : MessageRequester("Check HDN_ItemClickW Flag", "Using Unicode Version...") : EndIf
      ;
      If *pnmh\code = #HDN_ITEMCLICKW Or *pnmh\code = #HDN_ITEMCLICKA
         ;
         *phdr = lParam 
         ; Fill the header structure with information about the clicked header.
         *HoldControl\SortedColumn = *phdr\iItem
         ; Store the column clicked by the user.
         HiLo = *HoldControl\SortedColumn | (*HoldControl\Gadget << 16)
         ; The sorting procedure will need to know the column being sorted as well as the handle of the control. (hi / lo)
         SendMessage_(HandleWindow, #LVM_SORTITEMS, HiLo, @x_Sort_CompareInteger())
         ; Sort the listview.
         x_Sort_UpdatelParam(*HoldControl\Handle, *HoldControl\ColumnCount)
         ;
      EndIf
      ;
   EndIf
   ;
   lResult = CallWindowProc_(*HoldControl\Callback, HandleWindow, Message, wParam, lParam)
   ; Pass the message to the default message handler.
   ProcedureReturn lResult
   ;
EndProcedure
;- Private Functions
Procedure x_Sort_AddSort(*Control.s_x_Sort_Controls)
   ;
   Define.l *Position
   ;
   Define.l HoldArray
   ;
   Define.s_x_Sort_Controls *HoldControl
   ;
   If _xSortMain\Controls
      ; Will be non-zero if controls exist within the 'array'.
      
      ;
   Else
      ; No controls exist within the array yet.
      _xSortMain\Controls = AllocateMemory(4)
      ; Allocate 4 bytes to store the pointer to the control structure.
      PokeL(_xSortMain\Controls, *Control)
      ; Store the pointer to the listicon sort structure.
      _xSortMain\Count = 1 : _xSortMain\Size = 4
      ; Set the size and count of the control array.
   EndIf
   ;
EndProcedure
;- Library functions
Procedure AllowListIconSort(Gadget.l)
   ;
   Define.l Handle
   ;
   Define.l *Position
   ;
   Define.s_x_Sort_Controls *HoldControl
   ;
   If IsGadget(Gadget) = #False : ProcedureReturn : EndIf
   ; Ensure the passed gadget identifier is valid.
   Handle = GadgetID(Gadget)
   ; Store the handle of the control.
   If x_Sort_IsListIcon(Handle) = #False : ProcedureReturn : EndIf
   ; Check if the control is a listview.
   ;{ Test for an existing control.
   If _xSortMain\Controls
      ; Will be non-zero if controls exist within the 'array'.
      *Position = _xSortMain\Controls
      ;
      While *Position - _xSortMain\Controls < _xSortMain\Size
         ;
         *HoldControl = PeekL(*Position)
         ;
         If Handle = *HoldControl\Handle : ProcedureReturn : EndIf
         ; Exit if the control already exists in the area.
         *Position + 4
         ;
      Wend
      ;
   EndIf
   ;}
   *HoldControl = AllocateMemory(SizeOf(s_x_Sort_Controls))
   ; Allocate space for a new control.
   *HoldControl\Gadget = Gadget
   ; Store the gadget identifier for the control.
   *HoldControl\Handle = Handle
   ; Store the handle for the control.
   *HoldControl\SortedColumn = -1
   ; No column is currently sorted.
   x_Sort_AddSort(*HoldControl)
   ; Add the control to the main control array.
   *HoldControl\HandleHeader = SendMessage_(Handle, #LVM_GETHEADER, 0, 0)
   ; Get the handle to the listview control header.
   *HoldControl\ColumnCount = SendMessage_(*HoldControl\HandleHeader, #HDM_GETITEMCOUNT, 0, 0)
   ; Get the number of columns in the listview.
   *HoldControl\Callback = SetWindowLong_(Handle, #GWL_WNDPROC, @x_Sort_MainCallback())
   ; Set the custom callback for the control.
   x_Sort_UpdatelParam(*HoldControl\Handle, *HoldControl\ColumnCount)
   ; Update the lParam value for the columns.
EndProcedure
Procedure AutoSortListIcon(Gadget.l, AutoSort.b)
   ; Enable or disable autosorting a listicon as new entries are added.
   ;
EndProcedure
Procedure RemoveListIconSort(Gadget.l)
   ;
   ;
EndProcedure
Procedure DestroyListIconSort()
   ; Removes all listview control sorting information.
   Define.l *Position
   ;
   Define.s_x_Sort_Controls *HoldControl
   ;
   If _xSortMain\Controls
      ; No need to destroy anything if no controls exist in the array.
      *Position = _xSortMain\Controls
      While *Position - _xSortMain\Controls < _xSortMain\Size
         ;
         *HoldControl = PeekL(*Position)
         ;
         SetWindowLong_(*HoldControl\Handle, #GWL_WNDPROC, *HoldControl\Callback)
         ; Restore the callback for the listview control.
         FreeMemory(*HoldControl)
         ; Destroy the memory used by the listview control.
         *Position + 4
         ;
      Wend
      ;
   EndIf
   ;
EndProcedure
;- Library End
And the test program (saved as Main.pb) ...

Code: Select all

;
EnableExplicit
;
CompilerIf #PB_Compiler_Unicode = #True
   MessageRequester("Check Unicode Flag", "Unicode!")
CompilerEndIf
;
;- Enumerations
Enumeration ; Window IDs
   #WindowMain
EndEnumeration
Enumeration ; Gadget IDs
   #ListMain
   #ButtonClose
EndEnumeration
;- Global Variables
;- Includes
XIncludeFile "xSort.pb"
;- Main Program
Define.l lRet
;
Define.b DoQuit
;
Define.l EventID
;
lRet = OpenWindow(#WindowMain, 0, 0, 400, 300, #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_TitleBar, "Test")
;
If lRet
   ;
   If CreateGadgetList(lRet)
      ;
      ListIconGadget(#ListMain, 0, 0, WindowWidth(#WindowMain), WindowHeight(#WindowMain) - 20 - 1, "One", 60, #PB_ListIcon_AlwaysShowSelection | #PB_ListIcon_FullRowSelect | #PB_ListIcon_GridLines)
      ;
      ButtonGadget(#ButtonClose, WindowWidth(#WindowMain) - 60 - 1, GadgetY(#ListMain) + GadgetHeight(#ListMain) + 1, 60, 20, "Close")
      ;
   Else
      ;
      MessageRequester("Program Error", "Cannot create controls!")
      ;
      End
      ;
   EndIf
   ;
Else
   ;
   MessageRequester("Program Error", "Can not create main window!")
   ;
EndIf
;
AddGadgetColumn(#ListMain, 1, "Two", 60)
AddGadgetColumn(#ListMain, 2, "Three", 60)
AddGadgetColumn(#ListMain, 3, "Four", 60)
AddGadgetColumn(#ListMain, 4, "Five", 60)
;
AddGadgetItem(#ListMain, -1, "Jack"+Chr(10)+"be"+Chr(10)+"Nimble"+Chr(10)+"jack"+Chr(10)+"Be")
AddGadgetItem(#ListMain, -1, "quick"+Chr(10)+"Jack"+Chr(10)+"jumped"+Chr(10)+"Over"+Chr(10)+"the")
AddGadgetItem(#ListMain, -1, "Candle"+Chr(10)+"stick"+Chr(10)+"Little"+Chr(10)+"miss"+Chr(10)+"Muffet")
AddGadgetItem(#ListMain, -1, "sat"+Chr(10)+"On"+Chr(10)+"her"+Chr(10)+"Tuffet"+Chr(10)+"eating")
AddGadgetItem(#ListMain, -1, "Her"+Chr(10)+"curds"+Chr(10)+"And"+Chr(10)+"whey"+Chr(10)+"Along")
;
AllowListIconSort(#ListMain)
;
DoQuit = #False
;
;{ Event Loop
Repeat
   ;
   EventID = WaitWindowEvent()
   ;
   If EventID = #PB_Event_CloseWindow
      ; Close the program.
      DoQuit = #True
      ;
   ElseIf EventID = #PB_Event_Gadget
      ;
      If EventGadget() = #ButtonClose
         ;
         If EventType() = #PB_EventType_LeftClick : DoQuit = #True : EndIf
         ;
      EndIf
      ;
   EndIf
   ;
Until DoQuit = #True
;}
;
DestroyListIconSort()
; Remove any existing listicon sorting.
End
;