GaryC Posted October 6, 2007 Posted October 6, 2007 Hi All, I want to store an index with each tree view item so that I can locate the information associated with each item. I am trying to do this by storing the index into an array in the LPARAM member of the TVITEM structure. I wrote GUICtrlTreeViewInsertItem2, a modification of _GUICtrlTreeViewInsertItem from GUITreeView.au3 to store this information, and wrote _GUICtrlTreeViewGetParam modeled after _GUICtrlTreeViewGetText. Well, I'm not getting my data back, I can't tell if the param data isn't being stored or isn't being retrieved. I also notice that _GUICtrlTreeViewGetText doesn't retrieve the text of the item. The platform SDK documentation for the TVM_GETITEM message indicates that the text buffer returned might not be the same as the one supplied, so the below function _GUICtrlTreeViewGetText2 attempts to deal with this with no change in my results. Also, I notice that the _GUICtrlTreeViewGetParentID UDF returns the content of the PARAM member, but I can't find anything that sets it. Does the standard AutoIt GUICtrlCreateTreeViewItem function store the control ID of the item in it? Is there a better way of linking the items in the tree view with data when the data for each item includes more than the text that appears in the displayed item? I thought I remembered some way to get the index of the focused item in the tree view as if it were a list but I can't find anything like that. What I am trying to do is convert HTML help (.chm) files to text. From the decompiled TOC (.hhc) file I generate the TOC in an array. I then want to display the TOC so that the user can select the node in the help to convert. Below is a test version based on my application that demonstrates what I am doing. The $gaNodes[$entry][$field] array contains 3 fields per entry: (0) the level number, (1) the item text, (2) a file name associated with the item, which this code does nothing with. I wrote push() and pop() because it didn't seem like the UDFs in array.au3 did exactly what I wanted. Steps to reproduce: Run this file. When GUI appears, arrow down to select an item, such as "item 3". Press TAB twice to reach Info button, press Spacebar to activate. This is supposed to get the stored index of the currently selected item and place the item text from the array ($gaNodes[$itm][1]) in the input box. Notice the output in the input box. Press ESC to terminate the GUI. Debug info has been written to the console. Can anyone help? Thanks. GaryC expandcollapse popup#Include <array.au3> #include <GUITreeView.au3> Global $ghForm2 = 0 Global $gaNodes[6][3] = [[0, "item 1", ""], [0, "item 2", ""], [1, "item 2.1", ""], [1, "item 2.2", ""], _ [0, "item 3", ""], [0, "item 4", ""]] Form2Create() Form2MsgLoop() GUIDelete() Exit(0) Func Form2Create() $ghForm2 = GUICreate("CHM to Text - Choose starting node", 342, 183, 341, 278, -1798701056, 256) $IDC_TREEVIEWLABEL = GUICtrlCreateLabel("Choose Node", 14, 14, 65, 16, 1342308608, 0) Global $IDC_TREEVIEW = GUICtrlCreateTreeView(85, 14, 200, 100, 1350631424, 0) Global $IDC_InfoInput = GUICtrlCreateInput("", 14, 128, 200, 16, 1342374016, 0) Global $IDC_InfoBtn = GUICtrlCreateButton("Info", 222, 128, 100, 16, 1342246656, 0) GUISetState(@SW_SHOW) BuildTreeView($IDC_TreeView, $gaNodes) GUICtrlSetState($IDC_TreeView, $GUI_Focus) GUICtrlSetState(GUICtrlSendMsg($IDC_TreeView, $TVM_GETNEXTITEM, $TVGN_ROOT, 0), $GUI_Focus) EndFunc ; Form2Create Func Form2MsgLoop() While 1 Local $msg = GUIGetMsg() Switch($msg) Case 0 ContinueLoop Case $GUI_EVENT_CLOSE ExitLoop Case $IDC_InfoBtn Local $itm = GUICtrlRead($IDC_TREEVIEW) ConsoleWrite("CtrlRead returned Selected item is " & $itm & @CRLF) ; debug ConsoleWrite(" Text = " & String(_GUICtrlTreeViewGetText2($IDC_TREEVIEW, $itm)) & @CRLF) ; debug ConsoleWrite(" param = " & String(_GUICtrlTreeViewGetParam($IDC_TREEVIEW, $itm)) & @CRLF) ; debug GUICtrlSetData($IDC_InfoInput, $gaNodes[_GUICtrlTreeViewGetParam($IDC_TREEVIEW, _ GUICtrlRead($IDC_TREEVIEW))][1]) EndSwitch ConsoleWrite(String($msg) & @CRLF) ; debug WEnd EndFunc ; Form2MsgLoop ; Build the contents of the tree view. ; $vID - ID of tree view ; $aNodes - Array of nodes, passed by reference for efficiency, should not be modified here. Func BuildTreeView($vID, ByRef $aNodes) Local $aParents = "" ; empty stack Local $iParent = 0; tell GUITreeViewInsertItem2 to start at the root Local $iLastItem ; last item generated Local $iLastLevel = $aNodes[0][0] ; start at root, we won't have a level change first time through for $iItem = 0 to UBound($aNodes) - 1 If ($aNodes[$iItem][0] > $iLastLevel) Then Push($aParents, $iParent) $iParent = $iLastItem $iLastLevel = $aNodes[$iItem][0] ElseIf ($aNodes[$iItem][0] < $iLastLevel) Then $iParent = Pop($aParents) $iLastLevel = $aNodes[$iItem][0] EndIf $iLastItem = _GUICtrlTreeViewInsertItem2($IDC_TREEVIEW, $aNodes[$iItem][1], $iParent, 0, $iItem) ; add TOC name to tree with item idx in PARAM data If (not $iLastItem) Then ConsoleWrite("BuildTreeView: GUITreeViewInsertItem2 returned 0 at $aNodes[" & $iItem & "], @error = " & @error & @CRLF) ; debug EndIf Next ; $iItem EndFunc; BuildTreeView ; Start the stack by passing a non-array variable for $a. It will be redefined to be an array. Func Push(ByRef $a, $v) If (not IsArray($a)) Then Local $ary[1] $a = $ary Else Redim $a[UBound($a) + 1] EndIf $a[UBound($a) - 1] = $v EndFunc ; Push Func Pop(ByRef $a) If (not IsArray($a)) Then Return SetError(1, @extend, "") EndIf Local $v = $a[UBound($a) - 1] If (UBound($a) > 1) Then ReDim $a[UBound($a) - 1] Else $a = "" ; last element returned EndIf Return $v EndFunc ; pop ; Same as GUITreeViewInsertItem but also stores value in the lparam field. Func _GUICtrlTreeViewInsertItem2($i_treeview, $s_itemtext, $h_item_parent = 0, $h_item_after = 0, $iData = 0) If Not _IsClassName ($i_treeview, "SysTreeView32") Then Return SetError(-1, -1, 0) Local $h_item_tmp Local $st_TVI = DllStructCreate("uint;uint;" & $s_TVITEMEX) If @error Then Return SetError(@error, @error, 0) Local $st_text = DllStructCreate("char[" & StringLen($s_itemtext) + 1 & "]") If @error Then Return SetError(@error, @error, 0) If $h_item_parent = 0 Then $h_item_parent = $TVI_ROOT Else $h_item_tmp = GUICtrlGetHandle($h_item_parent) If $h_item_tmp <> 0 Then $h_item_parent = $h_item_tmp EndIf If $h_item_after = 0 Or _ ($h_item_after <> $TVI_ROOT And _ $h_item_after <> $TVI_FIRST And _ $h_item_after <> $TVI_LAST And _ $h_item_after <> $TVI_SORT) Then $h_item_after = $TVI_LAST EndIf DllStructSetData($st_text, 1, $s_itemtext) Local $u_mask = BitOR($TVIF_TEXT, $TVIF_PARAM) Local $h_icon = _TreeViewGetImageListIconHandle($i_treeview, 0) If $h_icon <> 0 Then $u_mask = BitOR($u_mask, $TVIF_IMAGE) DllStructSetData($st_TVI, 9, 0) DllCall("user32.dll", "int", "DestroyIcon", "hwnd", $h_icon) EndIf $h_icon = _TreeViewGetImageListIconHandle($i_treeview, 1) If $h_icon <> 0 Then $u_mask = BitOR($u_mask, $TVIF_SELECTEDIMAGE) DllStructSetData($st_TVI, 10, 0) DllCall("user32.dll", "int", "DestroyIcon", "hwnd", $h_icon) EndIf DllStructSetData($st_TVI, 1, $h_item_parent) DllStructSetData($st_TVI, 2, $h_item_after) DllStructSetData($st_TVI, 3, $u_mask) DllStructSetData($st_TVI, 7, DllStructGetPtr($st_text)) DLLStructSetData( $st_TVI, 12, $iData) Return GUICtrlSendMsg($i_treeview, $TVM_INSERTITEM, 0, DllStructGetPtr($st_TVI)) EndFunc ;==>_GUICtrlTreeViewInsertItem2 ; Returns lparam field. Func _GUICtrlTreeViewGetParam($i_treeview, $h_item = 0) If Not _IsClassName ($i_treeview, "SysTreeView32") Then Return SetError(-1, -1, "") Local $iData = 0 ConsoleWrite("_GUICtrlTreeViewGetParam: Initial $h_item = " & $h_item & @CRLF) ; debug $h_item = _TreeViewGetItemHandle($i_treeview, $h_item) If $h_item = 0 Then Return SetError(1, 1, "") Local $st_TVITEM = DllStructCreate($s_TVITEMEX) If @error Then Return SetError(@error, @error, "") DllStructSetData($st_TVITEM, 1, $TVIF_PARAM) DllStructSetData($st_TVITEM, 2, $h_item) Local $iRtn = GUICtrlSendMsg($i_treeview, $TVM_GETITEM, 0, DllStructGetPtr($st_TVITEM)) Local $sDbg = "st_TVItem: " ; debug For $iDbg = 1 to 10 ; debug $sDbg &= @CRLF & $iDbg & ": " & DllStructGetData($st_TVItem, $iDbg) & " 0x" & Hex(DllStructGetData($st_TVItem, $iDbg)) ; debug Next ; $iDbg ; debug ConsoleWrite("_GUICtrlTreeViewGetParam: " & $sDbg & @CRLF) ; debug If ($iRtn) Then $iData = DllStructGetData($st_TVITEM, 10) Else Return SetError(1, @error, 0) EndIf Return $iData EndFunc ;==>_GUICtrlTreeViewGetParam ; GUITreeViewGetText with code to handle the case where the system returns text in a different buffer (see PSDK for TVM_GETITEM). Func _GUICtrlTreeViewGetText2($i_treeview, $h_item = 0) If Not _IsClassName ($i_treeview, "SysTreeView32") Then Return SetError(-1, -1, "") Local $s_text = "" $h_item = _TreeViewGetItemHandle($i_treeview, $h_item) If $h_item = 0 Then Return SetError(1, 1, "") Local $st_text = DllStructCreate("char[4096]"); create a text 'area' for receiving the text If @error Then Return SetError(@error, @error, "") Local $st_TVITEM = DllStructCreate($s_TVITEMEX) If @error Then Return SetError(@error, @error, "") DllStructSetData($st_TVITEM, 1, $TVIF_TEXT) DllStructSetData($st_TVITEM, 2, $h_item) $iOrigPtr = DllStructGetPtr($st_text) DllStructSetData($st_TVITEM, 5, $iOrigPtr) DllStructSetData($st_TVITEM, 6, DllStructGetSize($st_text)) If GUICtrlSendMsg($i_treeview, $TVM_GETITEM, 0, DllStructGetPtr($st_TVITEM)) Then $iNewPtr = DllStructGetData($st_TVItem, 5) If ($iOrigPtr <> $iNewPtr) Then ; The text was placed in a different buffer ConsoleWrite("GUITreeViewGetText2: new pointer" & @CRLF) ; debug $st_text = 0 $st_text = DllStructCreate("char[" & DllStructGetData($st_TVITEM, 6) & "]", $iNewPtr) EndIf $s_text = DllStructGetData($st_text, 1) EndIf ; sendMsg Return $s_text EndFunc ;==>_GUICtrlTreeViewGetText2
eltorro Posted October 7, 2007 Posted October 7, 2007 (edited) Maybe this is along the lines of what your trying to accomplish?expandcollapse popup#include <GuiConstants.au3> Global $gaNodes[9][3] = [[0, "item 1", "This is Item 1"], [0, "item 2", "This is Item 2"], [1, "item 2.1", "This is Item 2.1"], [1, "item 2.2", "This is Item 2.2"], _ [2, "item 2.2.1", "This is Item 2.2.1"],[0, "item 3", "Oh boy, Item 3"], [1, "item 3.1", "Hello from 3.1"], _ [1, "item 3.2", "3.2 Here"],[0, "item 4", "Last but maybe least: Item 4"]] Global $ghForm2, $IDC_TREEVIEW, $IDC_InfoInput, $IDC_InfoBtn, $gaTV_Items[2],$gaParents[1] $gaTV_Items[0] = 0 $ghForm2 = GUICreate("CHM to Text - Choose starting node", 342, 183, 341, 278, -1798701056, 256) $IDC_TREEVIEWLABEL = GUICtrlCreateLabel("Choose Node", 14, 14, 65, 16, 1342308608, 0) $IDC_TREEVIEW = GUICtrlCreateTreeView(85, 14, 200, 100, 1350631424, 0) $IDC_InfoInput = GUICtrlCreateInput("", 14, 128, 200, 16, 1342374016, 0) $IDC_InfoBtn = GUICtrlCreateButton("Info", 222, 128, 100, 16, 1342246656, 0) GUISetState(@SW_SHOW) BuildTreeView($IDC_TREEVIEW, $gaNodes) While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit Case $IDC_InfoBtn Local $iTV_Clicked = GUICtrlRead($IDC_TREEVIEW) ConsoleWrite("CtrlRead returned Selected item is " & $iTV_Clicked& @CRLF) $iItem = GetTVIndex($iTV_Clicked) If $iItem >= 0 Then GuiCtrlSetData($IDC_InfoInput,$gaNodes[$iItem][2]) EndIf EndSwitch Sleep(10) WEnd Func BuildTreeView($nTreeView,$aNodes) Local $iParent =0 Local $iLastLevel = 0 Local $iCurrent =0 Local $nNode = 0 For $x = 0 to UBound($aNodes,1)-1 $iCurrent = $aNodes[$x][0] Switch $iCurrent Case 0 $nNode = GUICtrlCreateTreeViewItem($aNodes[$x][1],$nTreeView) Push($iCurrent,$nNode) $iParent =$nNode Case Else If $iParent = 0 Then $iParent = $nTreeView Else $iParent = Peek($iCurrent-1) If $iParent > 0 Then $nNode = GUICtrlCreateTreeViewItem($aNodes[$x][1],$iParent) Push($iCurrent,$nNode) EndIf EndIf EndSwitch $iLast = $aNodes[$x][0] If $nNode <> 0 Then If $gaTV_Items[0]+1 = UBound($gaTV_Items) Then ReDim $gaTV_Items[UBound($gaTV_Items)+10] EndIf $gaTV_Items[$gaTV_Items[0]+1] = $nNode $gaTV_Items[0] += 1 EndIf Next EndFunc Func GetTVIndex($iTV_Clicked) Local $retval = -1 For $x = 1 to $gaTV_Items[0] If $gaTV_Items[$x] = $iTV_Clicked Then $retval = $x-1 ExitLoop EndIf Next Return $retval EndFunc Func Peek($iLevel) If $iLevel < 0 Then Return SetError(1,0,0) EndIf If $iLevel < UBound($gaParents) Then Return $gaParents[$iLevel] EndIf Return -1 EndFunc Func Push($iLevel,$iNode) If $iLevel < 0 Then Return SetError(1,0,0) EndIf If $iNode < 0 Then Return SetError(2,0,0) EndIf Select Case $iLevel < UBound($gaParents) $gaParents[$iLevel] = $iNode Case $iLevel = UBound($gaParents) ReDim $gaParents[$iLevel +1] $gaParents[$iLevel] = $iNode Case Else Return SetError(1,0,0) EndSelect ;ConsoleWrite(StringFormat("Level: %d Node: %d\n",$iLevel,$iNode)) Return 1 EndFuncEdit: Removed an un-needed line. Edited October 7, 2007 by eltorro Regards, [indent]ElTorro[/indent][font="Book"] Decide, Commit, Achieve[/font]_ConfigIO.au3Language Translation --uses Google(tm) MsgBox Move XML wrapper UDF XML2TreeView Zip functionality Split your GUI Save Print ScreenZipPluginEdit In Place listviewSome of my scripts on Google code
-Ultima- Posted October 8, 2007 Posted October 8, 2007 (edited) I was also looking for a method to store additional data in treeviews a long while back, but gave up. With the recent addition of ControlTreeView() in the betas, though, I've found that I could simply create an array in parallel with the treeview, then use ControlTreeView()'s "GetSelected" command to grab the index of the selected item, then look it up in the array. Treeview items with child nodes can be represented by nested arrays (I guess with the 0 element of that nested array containing data for that particular item that has children?). You'll need to remember to increment each index returned by ControlTreeView() by 1, but it can be easily done when you're parsing the ControlTreeView(). TreeItem1 TreeItem2 TreeItem2Sub1 TreeItem2Sub2 TreeItem3 would maybe have an accompanying array of [ 0, "Data associated with TreeItem1", [ "Data associated with TreeItem2", "Data associated with TreeItem2Sub1", "Data associated with TreeItem2Sub2" ], "Data associated with TreeItem3" ] The 0 is there since it would be easier to just increment every returned index by 1 instead of having to remember that the first index returned by ControlTreeView() doesn't need to be incremented. I'm not a big fan of this method, as it feels "unclean" to me, but it got the job done for me anyway, so I thought I'd just throw the idea out there in case you weren't aware of it Edited October 8, 2007 by -Ultima- [ WinINet.au3 | Array.au3 (Optimized) | _UnixTimeParse() ]
GaryC Posted October 9, 2007 Author Posted October 9, 2007 Thanks for your responses. I used something like eltorro's solution. I was not able to retrieve a number for the currently-focused item matching the number stored by _GUICtrlTreeViewInsertItem2 (should be the same as GUICtrlTreeViewInsertItem in the GUITreeView.au3 library), either by GUICtrlRead or by GUICtrlGetHandle(GUICtrlRead()). I replaced my _GUICtrlTreeViewInsertItem2 with GUICtrlCreateTreeViewItem and it worked. Because I forgot to disable my debug call to my _GUICtrlTreeViewGetParam function I found out that GUICtrlCreateTreeViewItem does store the item ID in the LPARAM value of the item structure. I also found out that the problem with my code for that is in the storing function (_GUICtrlTreeViewInsertItem2) since _GUICtrlTreeViewGetParam seems to work. Thanks again. GaryC
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now