Sign in to follow this  
Followers 0
-Ultima-

Overriding double-click on treeview...

4 posts in this topic

#1 ·  Posted (edited)

So, I've got this treeview for which I'm handling NM_DBLCLK through WM_NOTIFY... The thing is, I don't actually want a treeview item to be expanded when I double-click it. In AutoIt 3.2.10.x, it was enough to return some unexpected value in the NM_DBLCLK callback function, but since 3.2.11.x, I haven't been able to override the double-click behavior -- only augment it. Does anyone know how I can work around this, short of toggling the expanded state every time WM_NOTIFY/NM_DBLCLK is called?

Here's a test script:

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <TreeViewConstants.au3>
#include <StructureConstants.au3>
#include <Misc.au3>
;#include <SendMessage.au3> ; NEEDED FOR 3.2.11.x, WON'T WORK WITH 3.2.10.x

Opt("GUIOnEventMode", 1)

Global $GUIMain_iWidth = 150
Global $GUIMain_iHeight = 100

; GUI/Controls
Global $GUIMain = GUICreate(@AutoItVersion, $GUIMain_iWidth, $GUIMain_iHeight)

Global $TreeView = GUICtrlCreateTreeView(5, 5, $GUIMain_iWidth - 10, $GUIMain_iHeight - 10, $TVS_HASBUTTONS + $TVS_HASLINES + $TVS_LINESATROOT + $TVS_NONEVENHEIGHT, $WS_EX_CLIENTEDGE)
Global $TreeView_hWnd = GUICtrlGetHandle($TreeView)
Global $TreeView_Root = GUICtrlCreateTreeViewItem("ITEM", $TreeView)
GUICtrlCreateTreeViewItem("SUBITEM", $TreeView_Root)

; Miscellaneous
GUISetOnEvent($GUI_EVENT_CLOSE, "mainExit", $GUIMain)
GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
GUISetState()

; Main Loop
While 1
    Sleep(1000)
WEnd

; Functions
Func mainExit()
    Exit
EndFunc

Func NM_DBLCLK()
;   _GUICtrlTreeView_ToggleItem($TreeView_hWnd)
    Return 1
EndFunc

Func WM_NOTIFY($hWnd, $Msg, $wParam, $lParam)
    Local $tNMHDR = DLLStructCreate($tagNMHDR, $lParam)

    Switch DllStructGetData($tNMHDR, "hWndFrom")
        Case $TreeView_hWnd
            Switch DllStructGetData($tNMHDR, "Code")
                Case $NM_DBLCLK
                    Return NM_DBLCLK()
            EndSwitch
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc

Func _GUICtrlTreeView_ToggleItem($hWnd)
    _SendMessage($hWnd, $TVM_EXPAND, $TVE_TOGGLE, _SendMessage($hWnd, $TVM_GETNEXTITEM, $TVGN_CARET, 0, 0, "wparam", "hwnd", "hwnd"), 0, "wparam", "hwnd")
EndFunc
Edited by -Ultima-

[ WinINet.au3 | Array.au3 (Optimized) | _UnixTimeParse() ]

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

Hm, it looks to me like it just might be a bug/regression with the AutoIt betas... Take this script as an example:

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <TreeViewConstants.au3>
#include <StructureConstants.au3>

Opt("GUIOnEventMode", 1)

Global $GUIMain_iWidth = 150
Global $GUIMain_iHeight = 100

; GUI/Controls --------------
Global $GUIMain = GUICreate(@AutoItVersion, $GUIMain_iWidth, $GUIMain_iHeight)

Global $TreeView = GUICtrlCreateTreeView(5, 5, $GUIMain_iWidth - 10, $GUIMain_iHeight - 10, $TVS_HASBUTTONS + $TVS_HASLINES + $TVS_LINESATROOT + $TVS_NONEVENHEIGHT, $WS_EX_CLIENTEDGE)
Global $TreeView_hWnd = GUICtrlGetHandle($TreeView)

GUICtrlCreateTreeViewItem("ITEM", $TreeView)
GUICtrlCreateTreeViewItem("SUBITEM", -1)

; Miscellaneous -------------
GUISetOnEvent($GUI_EVENT_CLOSE, "mainExit", $GUIMain)
GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
GUISetState()

; Main Loop -----------------
While 1
    Sleep(1000)
WEnd

; Functions -----------------
Func mainExit()
    Exit
EndFunc

Func WM_NOTIFY($hWnd, $Msg, $wParam, $lParam)
    Local $tNMHDR = DLLStructCreate($tagNMHDR, $lParam)
    If (DllStructGetData($tNMHDR, "hWndFrom") = $TreeView_hWnd) And (DllStructGetData($tNMHDR, "Code") = $NM_DBLCLK) Then
        Return 1+Chain1()
    EndIf

    Return $GUI_RUNDEFMSG
EndFunc

; Callback Function Chain ---
Func Chain1()
    For $i = 0 To 10000
        ConsoleWrite("")
    Next
    Chain2()
EndFunc

Func Chain2()
    For $i = 0 To 10000
        ConsoleWrite("")
    Next
    Chain3()
EndFunc

Func Chain3()
    For $i = 0 To 10000
        ConsoleWrite("")
    Next
    Chain4()
EndFunc

Func Chain4()
    For $i = 0 To 10000
        ConsoleWrite("")
    Next
    Chain5()
EndFunc

Func Chain5()
    For $i = 0 To 10000
        ConsoleWrite("")
    Next
;   Return 1
EndFunc

As is, it behaves correctly, and the treeview root can't be expanded by double-click. If you modify Chain5() to return *anything*, the whole thing falls apart, and AutoIt decides it wants to handle the message (and expands the treeview root). In fact, if you do return anything, anywhere in the callback function chain, AutoIt decides it wants to handle the message, regardless of what is returned.

Regarding the function chain, ignore the For loops. I only added them to demonstrate the fact that it isn't because the function chain is taking too long to return that causes AutoIt to behave wonkily (it's something I considered at first).

Can anyone confirm that this a bug? If so, I'll report it to Trac.

Aside: The GUIRegisterMsg() documentation needs updating. It's difficult to determine what return value would cause AutoIt to stop handling the message internally.

By using 'Return' without any return value the AutoIt internal message handler (if there is one for this message) will NOT be proceed!

Yet, an empty return *does* allow AutoIt to continue handling the message. Returning 0 does the same. It seems that it's only when you return a non-zero integer that it stops handling things internally. Edited by -Ultima-

[ WinINet.au3 | Array.au3 (Optimized) | _UnixTimeParse() ]

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

I can confirm that something weird is going on with user return values from callbacks in v3.2.11 betas.

For example, I believe that this http://www.autoitscript.com/forum/index.ph...amp;hl=listview (posts #3-#7) is related with the problem you describe here. That custom drawn listview example worked in v.3.2.10.0, doesn't work for betas. I thought I figured out the problem, - it looked like internal message proc simply overrides user return value of item prepaint notification with one of it's own for whatever reason - subclassing main GUI "fixed" the problem. Then after some time I decided to submit the bug, and started stripping down the example to the bare minimum. And suddenly, it started "working" again, which got me confused, so I put whole thing aside for a while.

Example:

#include <GuiListView.au3>
#include <ListViewConstants.au3>
#include <WindowsConstants.au3>
#include <GuiConstantsEx.au3>

;custom draw constants for v3.2.10.0
;~ Global Const $CDDS_ITEM = 0x10000, $CDDS_PREPAINT = 0x1, $CDDS_SUBITEM = 0x20000, $CDDS_ITEMPREPAINT = BitOR($CDDS_ITEM,$CDDS_PREPAINT), _
;~          $CDRF_NEWFONT = 0x2, $CDRF_NOTIFYITEMDRAW = 0x20, $CDRF_NOTIFYSUBITEMDRAW = 0x20


$hGUI = GUICreate("Listview Custom Draw", 400, 300)

$cListView = GUICtrlCreateListView("1|2|3", 2, 2, 394, 268, -1, BitOR($LVS_EX_GRIDLINES, $LVS_EX_FULLROWSELECT))
GUICtrlCreateListViewItem("a|b|c",$cListView)

GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
GUISetState()

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE

Func WM_NOTIFY($hWnd, $Msg, $wParam, $lParam)
    Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR

    $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    $hWndFrom = DllStructGetData($tNMHDR, "hWndFrom")
    $iIDFrom = DllStructGetData($tNMHDR, "IDFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")
    Switch $iIDFrom
        Case $cListView
            Switch $iCode
                Case $NM_CUSTOMDRAW
;~                  If Not _GUICtrlListView_GetViewDetails($cListView) Then Return $GUI_RUNDEFMSG
                    Local $tCustDraw = DllStructCreate('hwnd hwndFrom;int idFrom;int code;' & _
                                        'dword DrawStage;hwnd hdc;long rect[4];dword ItemSpec;int ItemState;dword Itemlparam;' & _
                                        'dword clrText;dword clrTextBk;int SubItem;' & _
                                        'dword ItemType;dword clrFace;int IconEffect;int IconPhase;int PartID;int StateID;long rectText[4];int Align', _;winxp or later
                                        $lParam), $iDrawStage
                    $iDrawStage = DllStructGetData($tCustDraw, 'DrawStage')
                    Switch $iDrawStage
                        Case $CDDS_PREPAINT
                            ConsoleWrite("$CDDS_PREPAINT" & @CRLF)
                            Return $CDRF_NOTIFYITEMDRAW
                        Case $CDDS_ITEMPREPAINT
                            ConsoleWrite("$CDDS_ITEMPREPAINT" & @CRLF)
                            Return $CDRF_NOTIFYSUBITEMDRAW
                        Case BitOR($CDDS_ITEMPREPAINT, $CDDS_SUBITEM)
                            ConsoleWrite("$CDDS_ITEMPREPAINT+$CDDS_SUBITEM" & @CRLF)
                            Return $CDRF_NEWFONT
                        Case Else
                    EndSwitch                   
            EndSwitch
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc;==>WM_NOTIFY

Works with betas - Case BitOR($CDDS_ITEMPREPAINT, $CDDS_SUBITEM) is executed. Then uncomment the line "If Not _GUICtrlListView_GetViewDetails($cListView) Then" and the subitem case isn't getting executed anymore.

Edited by Siao

"be smart, drink your wine"

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0