;'Edit ListIcon'.
;-----------------
; Stephen Rodriguez.
; Created with Purebasic 4.02 for Windows (date: May 2007).
; Maintained with PB 4.2.
; Platforms: Windows (tested on XP and Vista 32-bit).
; Licence: DAYLike
; (Do As You Like with it! - No Warranties!)
; A credit to myself, whilst nice, is not absolutely necessary.
;*******************************************************************************************
;NOTES.
;------
; 1) Register a listicon gadget to have editable cells by using the command SetListIconEditable(listID).
; You MUST set up such listicons to have a column zero of zero width.
; 2) Cells are made editable by intercepting double-clicks, setting the #LVS_EDITLABELS style,
; repositioning the edit control which Windows uses to edit the labels in column zero and copying
; the resulting text to the listicon cell.
; 3) Cells can also be edited by means of the command EditCell().
;*******************************************************************************************
#LVM_SUBITEMHITTEST = #LVM_FIRST+57
#LVM_GETSUBITEMRECT = #LVM_FIRST+56
#LVM_GETHEADER = #LVM_FIRST+31
#HDI_ORDER = $80
#EC_RIGHTMARGIN = 2
EnableExplicit
CompilerIf Defined(HDITEM, #PB_Structure) = 0
Structure HDITEM
mask.l
cxy.l
pszText.l
hbm.l
cchTextMax.l
fmt.l
lParam.l
iImage.l
iOrder.l
EndStructure
CompilerEndIf
Structure _LIEdit
listOldProc.l
editHwnd.l
item.l
subitem.l
x.l
y.l
cx.l
cy.l
osVersion.b
blnIsXPThemes.b
EndStructure
Structure _LIEditGlobals
osVersion.b
blnIsXPThemes.b
EndStructure
Declare.l SetListIconEditable(listID)
Declare _LIEEditCell(*liedit._LIEdit, hWnd)
Declare.l _LIEwinProc(hWnd, uMsg, wParam, lParam)
Declare.l _LIEListProc(hWnd, uMsg, wParam, lParam)
Declare.l _LIEeditProc(hWnd, uMsg, wParam, lParam)
Global _LIEditGlobals._LIEditGlobals
;Returns zero if an error.
Procedure.l SetListIconEditable(listID)
Protected result, parenthWnd, *mem._LIEdit, hWnd, dlv.DLLVERSIONINFO, func, lib
;Set globals if appropriate.
If _LIEditGlobals\osVersion = 0
_LIEditGlobals\osVersion = OSVersion()
dlv\cbSize=SizeOf(DLLVERSIONINFO)
lib=OpenLibrary(#PB_Any,"comctl32.dll")
If lib
func=GetFunction(lib,"DllGetVersion")
If func
CallFunctionFast(func, dlv)
If dlv\dwMajorVersion >=6
_LIEditGlobals\blnIsXPThemes = #True
EndIf
EndIf
CloseLibrary(lib)
EndIf
EndIf
;Check that listID references a valid listicon.
If IsGadget(listID) And GadgetType(listID)=#PB_GadgetType_ListIcon
hWnd = GadgetID(listID)
;Is the listicon already registered?
If GetProp_(hWnd, "_LIEdit")=0 ;No!
;Allocate enough memory for a _LIEdit structure.
*mem=AllocateMemory(SizeOf(_LIEdit))
If *mem
SetWindowLong_(hWnd, #GWL_STYLE, GetWindowLong_(hWnd, #GWL_STYLE)&~#LVS_EDITLABELS)
;Set the fields of the _LIEedit structure.
*mem\listOldProc = SetWindowLong_(hWnd, #GWL_WNDPROC, @_LIEListProc())
;Store a pointer to this structure in a window property ofthe listicon.
SetProp_(hWnd, "_LIEdit", *mem)
;Subclass the parent window if not already through another listicon.
parenthWnd=GetParent_(hWnd)
If GetProp_(parenthWnd, "_LIEditOldProc")=0 ;No!
SetProp_(parenthWnd, "_LIEditOldProc", SetWindowLong_(parenthWnd, #GWL_WNDPROC, @_LIEwinProc()))
EndIf
result=1
EndIf
EndIf
EndIf
ProcedureReturn result
EndProcedure
;Sets the specified cell to be edited.
Procedure EditCell(listID, item, subitem)
Protected hWnd, *liedit._LIEdit, numrows, numcols
;Check that listID references a valid listicon.
If IsGadget(listID) And GadgetType(listID)=#PB_GadgetType_ListIcon
;Check that the listicon is registered as editable.
hWnd = GadgetID(listID)
*liedit = GetProp_(hWnd, "_LIEdit")
If *liedit
;Check parameters are in range.
numrows = CountGadgetItems(listID)
numcols = SendMessage_(SendMessage_(hWnd,#LVM_GETHEADER,0,0), #HDM_GETITEMCOUNT,0,0)
If item>=0 And item < numrows And subitem>0 And subitem < numcols
*liedit\item = item
*liedit\subitem = subitem
SetActiveGadget(listID)
_LIEEditCell(*liedit, hWnd)
EndIf
EndIf
EndIf
EndProcedure
Procedure _LIEEditCell(*liedit._LIEdit, hWnd)
Protected rc.RECT, clientrc.RECT, numCols, headerWnd
Protected Dim cols(0), i, blnFoundZeroColumn
Protected hdi.HDITEM
;Vista themes requires a cheat because it automatically scrolls the listicon to bring the item being edited
;into view.
If _LIEditGlobals\osVersion >= #PB_OS_Windows_Vista And _LIEditGlobals\blnIsXPThemes
headerWnd = SendMessage_(hWnd,#LVM_GETHEADER,0,0)
numCols = SendMessage_(headerWnd, #HDM_GETITEMCOUNT,0,0)
Dim cols(numCols-1)
SendMessage_(hWnd, #LVM_GETCOLUMNORDERARRAY, numCols, @cols())
For i = 0 To numcols-1
If cols(i)
If cols(i) = *liedit\subItem
Break
EndIf
Else
blnFoundZeroColumn = 1
EndIf
Next
i-blnFoundZeroColumn
Dim cols(0)
With hdi
\mask = #HDI_ORDER
\iOrder = i
EndWith
SendMessage_(headerWnd, #HDM_SETITEM, 0, hdi)
EndIf
;Scroll the listicon if the clicked cell is not entirely visible
;*****IF YOU WISH TO RESTRICT WHICH CELLS CAN BE EDITED, THEN PERFORM THE NECESSARY CHECKS HERE
;*****ON THE VALUES OF *liedit\item and *liedit\subitem (WHICH INDICATE WHICH CELL IS ABOUT
;*****TO BE EDITED) AND RUN THE FOLLOWING LINES FOR THOSE CELLS WHICH ARE TO BE EDITED.
rc\top = *liedit\subitem
rc\left = #LVIR_BOUNDS
SendMessage_(hWnd, #LVM_GETSUBITEMRECT, *liedit\item, rc)
GetClientRect_(hWnd, clientrc)
If rc\left < 0 Or (rc\right-rc\left)>=clientrc\right
SendMessage_(hWnd, #LVM_SCROLL,rc\left,0)
Else
If rc\right > clientrc\right
SendMessage_(hWnd, #LVM_SCROLL,rc\right-clientrc\right,0)
EndIf
EndIf
SetWindowLong_(hWnd, #GWL_STYLE, GetWindowLong_(hWnd, #GWL_STYLE)|#LVS_EDITLABELS)
SendMessage_(hWnd, #LVM_EDITLABEL, *liedit\item, 0)
EndProcedure
;Window proc of the ListIcon parent window.
Procedure.l _LIEwinProc(hWnd, uMsg, wParam, lParam)
Protected result, oldwinproc, *nmh. NMHDR, listhWnd, edithWnd, *liedit._LIEdit, *lvd.LV_DISPINFO, rc.RECT
Protected hdi.HDITEM, headerWnd
Static celltext$
;Retrieve the address of the old proc.
oldwinproc = GetProp_(hWnd, "_LIEditOldProc")
Select uMsg
Case #WM_NOTIFY
*nmh=lParam
Select *nmh\code
Case #LVN_BEGINLABELEDIT
listhWnd = *nmh\hwndFrom
;Retrieve the address of the LIEdit structure.
*liedit = GetProp_(listhWnd, "_LIEdit")
If *liedit ;Good to go!
*liedit\editHwnd=0
;Get the handle of the edit control used to edit the label.
edithWnd = SendMessage_(listhWnd, #LVM_GETEDITCONTROL,0,0)
;Subclass the edit control.
SetProp_(edithWnd, "_LIEditOldProc", SetWindowLong_(edithWnd, #GWL_WNDPROC, @_LIEeditProc()))
;Set text.
celltext$=GetGadgetItemText(*nmh\idFrom, *liedit\item, *liedit\subitem)
SendMessage_(edithWnd, #WM_SETTEXT, 0, celltext$)
SetGadgetItemText(*nmh\idFrom, *liedit\item, "",*liedit\subitem)
;Get bounding rectangle.
rc\top = *liedit\subitem
rc\left = #LVIR_BOUNDS
SendMessage_(listhWnd, #LVM_GETSUBITEMRECT, *liedit\item, rc)
*liedit\x=rc\left
*liedit\y=rc\top
*liedit\cx=SendMessage_(listhWnd, #LVM_GETCOLUMNWIDTH, *liedit\subitem,0)
*liedit\cy=rc\bottom-rc\top
EndIf
Case #LVN_ENDLABELEDIT
listhWnd = *nmh\hwndFrom
;Retrieve the address of the LIEdit structure.
*liedit = GetProp_(listhWnd, "_LIEdit")
If *liedit ;Good to go!
*lvd = lParam
If *lvd\item\pszText
SetGadgetItemText(*nmh\idFrom, *liedit\item, PeekS(*lvd\item\pszText), *liedit\subitem)
Else
SetGadgetItemText(*nmh\idFrom, *liedit\item, celltext$, *liedit\subitem)
EndIf
SetWindowLong_(listhWnd, #GWL_STYLE, GetWindowLong_(listhWnd, #GWL_STYLE)&~#LVS_EDITLABELS)
If _LIEditGlobals\osVersion >= #PB_OS_Windows_Vista And _LIEditGlobals\blnIsXPThemes
With hdi
\mask = #HDI_ORDER
\iOrder = 0
EndWith
headerWnd = SendMessage_(listhWnd,#LVM_GETHEADER,0,0)
SendMessage_(headerWnd, #HDM_SETITEM, 0, hdi)
EndIf
EndIf
Default
result=CallWindowProc_(oldwinproc, hWnd, uMsg, wParam, lParam)
EndSelect
Case #WM_NCDESTROY
result=CallWindowProc_(oldwinproc, hWnd, uMsg, wParam, lParam)
RemoveProp_(hWnd, "_LIEditOldProc")
Default
result=CallWindowProc_(oldwinproc, hWnd, uMsg, wParam, lParam)
EndSelect
ProcedureReturn result
EndProcedure
;Window proc of the ListIcon.
Procedure.l _LIEListProc(hWnd, uMsg, wParam, lParam)
Protected result, *liedit._LIEdit, PInfo.LVHITTESTINFO, *nmHEADER.HD_NOTIFY
;Retrieve the address of the LIEdit structure.
*liedit = GetProp_(hWnd, "_LIEdit")
Select uMsg
Case #WM_NOTIFY
*nmHEADER = lParam
Select *nmHEADER\hdr\code
Case #HDN_BEGINTRACK, #HDN_BEGINTRACKW ;Prevent column 0 from being resized.
If *nmHEADER\iItem=0
result=1
EndIf
Case #HDN_ENDTRACK, #HDN_ENDTRACKW
InvalidateRect_(hWnd,0,1)
Default
result=CallWindowProc_(*liedit\listOldProc, hWnd, uMsg, wParam, lParam)
EndSelect
Case #WM_LBUTTONDBLCLK
;Identify the clicked item
PInfo\pt\x = lParam&$ffff
PInfo\pt\y = (lParam>>16)&$ffff
SendMessage_(hwnd, #LVM_SUBITEMHITTEST, 0, PInfo)
If PInfo\iItem <> -1 ;A valid cell was clicked.
*liedit\item = PInfo\iItem
*liedit\subitem = PInfo\iSubItem
_LIEEditCell(*liedit, hWnd)
EndIf
Case #WM_NCDESTROY
result=CallWindowProc_(*liedit\listOldProc, hWnd, uMsg, wParam, lParam)
RemoveProp_(hWnd, "_LIEdit")
FreeMemory(*liedit)
Default
result=CallWindowProc_(*liedit\listOldProc, hWnd, uMsg, wParam, lParam)
EndSelect
ProcedureReturn result
EndProcedure
;Window proc of the edit control.
Procedure.l _LIEeditProc(hWnd, uMsg, wParam, lParam)
Protected result, oldwinproc, *liedit._LIEdit, *wpos.WINDOWPOS
;Retrieve the address of the old proc.
oldwinproc = GetProp_(hWnd, "_LIEditOldProc")
;Retrieve the address of the LIEdit structure.
*liedit = GetProp_(GetParent_(hWnd), "_LIEdit")
Select uMsg
Case #WM_ERASEBKGND
;A hack in order to clear the default selection of characters.
result=CallWindowProc_(oldwinproc, hWnd, uMsg, wParam, lParam)
If *liedit\editHwnd=0
*liedit\editHwnd = hWnd
;Set margins.
SendMessage_(hWnd, #EM_SETMARGINS, #EC_LEFTMARGIN|#EC_RIGHTMARGIN, 4)
SendMessage_(hWnd, #EM_SETSEL, -1,0)
EndIf
Case #WM_WINDOWPOSCHANGING
*wpos=lParam
*wpos\cx=*liedit\cx ;Comment this line to get an edit control which grows with the text.
*wpos\x=*liedit\x
If _LIEditGlobals\osVersion >= #PB_OS_Windows_Vista And _LIEditGlobals\blnIsXPThemes
*wpos\cy=*liedit\cy
*wpos\y=*liedit\y
Else
*wpos\cy=*liedit\cy+3
*wpos\y=*liedit\y-2
EndIf
result=CallWindowProc_(oldwinproc, hWnd, uMsg, wParam, lParam)
Case #WM_NCDESTROY
result=CallWindowProc_(oldwinproc, hWnd, uMsg, wParam, lParam)
RemoveProp_(hWnd, "_LIEditOldProc")
;InvalidateRect_(GetParent_(hWnd),0,0)
Default
result=CallWindowProc_(oldwinproc, hWnd, uMsg, wParam, lParam)
EndSelect
ProcedureReturn result
EndProcedure
DisableExplicit
LoadFont(1, "Arial",10)
If OpenWindow(0, 100, 100, 600, 600, "ListIcon Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
If CreateGadgetList(WindowID(0))
ListIconGadget(1, 5, 5, 490, 390, "", 0, #PB_ListIcon_GridLines|#PB_ListIcon_HeaderDragDrop)
SetGadgetFont(1, FontID(1))
For i = 1 To 30
AddGadgetColumn(1, i, "Col "+Str(i), 100)
Next
For row = 1 To 100
AddGadgetItem(1,-1,"")
For col = 2 To 10
SetGadgetItemText(1, row-1, "row "+Str(row-1)+", col "+Str(col), col)
Next
Next
SetListIconEditable(1)
; EditCell(1, 19, 5)
Repeat
Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
EndIf
EndIf