Page 1 of 1

Is ControlClick dependent on a certain Window Class?

Posted: 11 Oct 2014, 06:28
by DavidP
I am asking because I can't get Controlclick to function in a certain program that does not use standard Windows classes and controls. The program is called PDF-XChange Editor and looks something like this:

Image

Clicking works with Click (interestingly without comma):

Code: Select all

Click, 1031 41
...but not with ControlClick (does nothing):

Code: Select all

ControlClick, X1031 Y41, PDF-XChange Editor
What could be wrong here?

Re: Is Controlclick dependent on a certain Window Class?

Posted: 11 Oct 2014, 18:42
by lexikos
ControlClick sends a click message to the window you specify. That menu is probably not in the window you specify, but a popup window which appears in front of it.

Re: Is Controlclick dependent on a certain Window Class?

Posted: 12 Oct 2014, 14:05
by DavidP
Sorry I was not clear enough. Actually I am trying to click on a dropdown triangle besides a toolbar button of the main program window, toolbar button and triangle being visible all the time:

Image

Do I have to specify anything else than the click location and the window title in order to make Controlclick work?

Code: Select all

ControlClick, X1031 Y41, PDF-XChange Editor
Thanks again,
Regards David.P

Re: Is Controlclick dependent on a certain Window Class?

Posted: 12 Oct 2014, 17:15
by lexikos
No. However, make sure you have used SetTitleMatchMode 2, since the window title does not start with "PDF-XChange Editor".

Re: Is Controlclick dependent on a certain Window Class?

Posted: 13 Oct 2014, 06:41
by autocart
lexikos, is it not true that the control-functions of autohotkey only work with standard windows-controls?
judging from PDF-XChange-Reader, I assume that also the editor uses custom controls.
if I am right then ControlClick will never work with PDF-XChange-Reader/Editor as long as it is employing custom controls.

Click on the other hand, DavidP, is a very general function that simply sends a click to the specified coordinates (really simulating a normal mouse click), not worrying about the target, therefore it works but is less reliable regarding changes of the active window.

BTW, welcome to the forum, DavidP.

Re: Is Controlclick dependent on a certain Window Class?

Posted: 13 Oct 2014, 19:35
by lexikos
No. ControlClick works with any window or control which responds to mouse messages. DavidP was identifying a position on the window, not a control. (Whether it was the right position, I don't know...)

ControlClick is used very heavily with some games, like World of Warcraft, which have no actual controls in the Windows sense, let alone standard ones.

Re: Is Controlclick dependent on a certain Window Class?

Posted: 14 Oct 2014, 07:04
by autocart
lexikos wrote:No. ControlClick works with any window or control which responds to mouse messages. DavidP was identifying a position on the window, not a control. (Whether it was the right position, I don't know...)

ControlClick is used very heavily with some games, like World of Warcraft, which have no actual controls in the Windows sense, let alone standard ones.
ok, thx for the explaination, Lexikos
glad to have learned something

Re: Is Controlclick dependent on a certain Window Class?

Posted: 14 Oct 2014, 08:25
by DavidP
OK thanks very much guys. I've tried everything I could think of, but it still only works with Click instead of ControlClick.

This is the dropdown and color picker that needs to be clicked on the toolbar:

Image

I shall post my script below:

Code: Select all

;###########################
; Header 
;###########################
 
SetTitleMatchMode, 2 ; A window's title can contain WinTitle anywhere inside it to be a match 
CoordMode, Mouse, Window ; Sets coordinate mode for Mouse to be relative to the active window 

;###########################
; Change Textmarker Color in PDF-XChange Editor -- this works using CLICK
;###########################

#IfWinActive, PDF-XChange Editor 
b:: ; make selected highlight blue -- replace "b" with your favorite hotkey
If (A_Cursor != "IBeam") ; only act when not editing text, e.g. don't act when editing a sticky note
{
	Click, 1031 41 ; replace with your coordinates of the little black triangle besides the "Fill Color" toolbar button, PDF-XChange Editor toolbar called "properties" must be set to visible, use the Autohotkey Window Spy tool to find the coordinates
	Click, 1017 298 ; replace with coordinates of the desired blue color that is shown on the picker when clicking on the "Fill Color" toolbar button
}
Else
	Send, b ; send the actual letter if editing text, e.g. in a sticky note
Return
#IfWinActive

;###########################
; Change Textmarker Color in PDF-XChange Editor -- this doesn't work using ControlClick
;###########################

#IfWinActive, PDF-XChange Editor 
g:: ; make selected highlight green -- replace "g" with your favorite hotkey
If (A_Cursor != "IBeam") ; only act when not editing text, e.g. don't act when editing a sticky note
{
	ControlClick, x1031 y41, PDF-XChange Editor,,,, Pos ; replace with your coordinates of the little black triangle besides the "Fill Color" toolbar button, PDF-XChange Editor toolbar called "properties" must be set to visible, use the Autohotkey Window Spy tool to find the coordinates
	ControlClick, x1047 y298, PDF-XChange Editor,,,, Pos ; replace with coordinates of the desired green color that is shown on the picker when clicking on the "Fill Color" toolbar button
	; ControlClick, x1047 y298 ; this variant also does not work, not even when [#IfWinActive, PDF-XChange Editor/#IfWinActive] is also deleted
}
Else
	Send, g ; send the actual letter if editing text, e.g. in a sticky note
Return
#IfWinActive

;###########################

; repeat the above for more colors
Thanks again for any clues why Click :thumbup: works but ControlClick :thumbdown: doesn't

Cheers
David

Re: Is ControlClick dependent on a certain Window Class?

Posted: 14 Oct 2014, 19:05
by lexikos
This is the dropdown and color picker that needs to be clicked on the toolbar
So... which window do you need to click? Your latest screenshot shows two more windows which are separate from the main "PDF-XChange Editor" window.

Re: Is ControlClick dependent on a certain Window Class?

Posted: 15 Oct 2014, 05:59
by DavidP
lexikos wrote:So... which window do you need to click? Your latest screenshot shows two more windows which are separate from the main "PDF-XChange Editor" window.
True, but they are only shown for visualization. The window title and class as reported by Window Spy always stays the same:

Code: Select all

- PDF-XChange Editor 
ahk_class PXE:{C5309AD3-73E4-4707-B1E1-2940D8AF3B9D}
...no matter if the PDF document itself, or the above shown undocked toolbar, or the color picker itself has the focus.

Not even the first click is carried out, the one that is supposed to click on the (here yellow) toolbar button (that opens the color picker), and not even if the toolbar is docked:

Image

:?: :eh: :?:

What could be the reason that the click is not executed?

Re: Is ControlClick dependent on a certain Window Class?

Posted: 15 Oct 2014, 21:31
by lexikos
I have installed an evaluation version of PDF-XChange Editor and confirmed that it does respond to ControlClick, but the mouse cursor must be over the appropriate button. Basically, when this program receives a click message (WM_LBUTTONDOWN), it ignores the position specified by the message and instead queries for the current mouse position (equivalent to MouseGetPos). If the window is not active (and you prevent activation by using the NA option), it will not display the colour picker, but clicking some of the other buttons will work. This might make sense if you consider that the colour picker is normally hidden automatically if the main window loses focus.

In short, this program will not respond to ControlClick in the way that you intend.
The window title and class as reported by Window Spy always stays the same:
The tool window clearly has a different title, so I presume the main window remains active even after you click the tool window; however, when I activate the tool window, Window Spy shows the title of the tool window.

With standard popup menus, usually some other window has the focus, and keyboard messages are automatically routed to the menu. For instance, AutoHotkey's Menu command focuses the script's hidden main window. Thus, Window Spy will not show the title of the menu itself. The same could be true of custom popups such as this program's colour picker.

Re: Is ControlClick dependent on a certain Window Class?

Posted: 16 Oct 2014, 01:03
by guest3456
lexikos wrote:I have installed an evaluation version of PDF-XChange Editor and confirmed that it does respond to ControlClick, but the mouse cursor must be over the appropriate button. Basically, when this program receives a click message (WM_LBUTTONDOWN), it ignores the position specified by the message and instead queries for the current mouse position (equivalent to MouseGetPos).
If the problem is indeed what Lexikos has found above, then this type of ControlClick bug/issue has been mentioned in the past. A suggested solution is here:
http://www.autohotkey.com/board/topic/9 ... k-improve/

@DavidP, look at that thread. Basically what you can try is to send a WM_MOUSEMOVE message first, and then WM_LBUTTONDOWN (which is what ControlClick already does). I've actually wrapped my own ControlClick function which does just that, similar to whats shown in the thread. Of course, this is if you want to avoid activating/focusing the window first, which I assume thats what you want. Otherwise, you could just use Click

@Lexikos, dunno if it matters but I'd like to repeat my wishlist request from that thread to add a new option to ControlClick, something like "MM" (similar to how there is "NA"), which would send the WM_MOUSEMOVE message in addition.

It could read something like this:
hypothetical ControlClick docs wrote: Options

NA [v1.0.45+]: May improve reliability. See reliability below.
MM [v1.1.17|2.0]: May improve reliability. See reliability below.



Reliability

"NA" avoids marking the target window as active and avoids merging its input processing with that of the script, which may prevent physical movement of the mouse from interfering (but usually only when the target window is not active). However, this method might not work for all types of windows and controls.

"MM" will send a WM_MOUSEMOVE message prior to the WM_[L|R]BUTTONDOWN click message. Some rare applications do not respect the coordinates that are sent in the BUTTONDOWN message and instead use the cursor position. Sending this message will simulate the cursor position and allow the clicks to be recognized in those applications.
;)

Re: Is ControlClick dependent on a certain Window Class?

Posted: 16 Oct 2014, 01:32
by lexikos
guest3456 wrote:If the problem is indeed what Lexikos has found above, then this type of ControlClick bug/issue has been mentioned in the past.
No, that's a different issue.

In this case, the program knows where the mouse is even if it was never sent a WM_MOUSEMOVE message - i.e. because there is another window obscuring it. For this reason, I didn't test sending it a WM_MOUSEMOVE message; I don't believe it would make any difference. The mouse doesn't have to be pointing directly at the window; it just has to line up with the button.

Re: Is ControlClick dependent on a certain Window Class?

Posted: 16 Oct 2014, 07:13
by DavidP
Thanks gentlemen, but *phew* I fear that this goes a little over my head.

So is there something simple that I could add to my lines like

Code: Select all

ControlClick, x1031 y41, PDF-XChange Editor,,,, Pos
...or am I better off to just use "Click" as I do at present?

I'd only prefer ControlClick because it is more elegant and doesn't actually move the pointer.

Regards David.P

Re: Is ControlClick dependent on a certain Window Class?

Posted: 16 Oct 2014, 10:23
by guest3456
DavidP wrote:

Code: Select all

ControlClick, x1031 y41, PDF-XChange Editor,,,, Pos
I'd only prefer ControlClick because it is more elegant and doesn't actually move the pointer.
Yeah that's the main benefit. I suppose if the WM_MOUSEMOVE message won't work like Lexikos said, you could try this wrapper for Click which will quickly move the mouse back:

Code: Select all

WinGet, PDFXhwnd, ID, PDF-XChange Editor
BasicClick(1031, 41, PDFXhwnd)

BasicClick(x, y, id)     ;// input coords relative to a full window (including borders and caption)
{
   CoordMode, Mouse, Screen
   MouseGetPos, oldx, oldy
   WinActivate, ahk_id %id%   
   BlockInput, MouseMove
   CoordMode, Mouse, Relative
   Click %x%, %y%
   CoordMode, Mouse, Screen
   MouseMove, %oldx%, %oldy%, 0
   BlockInput, MouseMoveOff
}

Re: Is ControlClick dependent on a certain Window Class?

Posted: 18 Oct 2014, 10:43
by DavidP
Sorry but I don't understand where and how I am to put the "WM_MOUSEMOVE" command in connection with the ControlClick command.

Btw., I am already using a similar "wrapper" for "Click".

Regards David.P

Re: Is ControlClick dependent on a certain Window Class?

Posted: 18 Oct 2014, 15:21
by guest3456
DavidP wrote:Sorry but I don't understand where and how I am to put the "WM_MOUSEMOVE" command in connection with the ControlClick command.
try this from the thread linked above, (prob will only work on 32bit ahk)

Code: Select all


ControlClick2(1031, 41, "PDF-XChange Editor")


;// required functions below, add these to the very bottom of your script

ControlClick2(X, Y, WinTitle="", WinText="", ExcludeTitle="", ExcludeText="")  
{  
  hwnd:=ControlFromPoint(X, Y, WinTitle, WinText, cX, cY, ExcludeTitle, ExcludeText)  
  PostMessage, 0x200, 0, cX&0xFFFF | cY<<16,, ahk_id %hwnd% ; WM_MOUSEMOVE
  PostMessage, 0x201, 1, cX&0xFFFF | cY<<16,, ahk_id %hwnd% ; WM_LBUTTONDOWN  
  PostMessage, 0x202, 0, cX&0xFFFF | cY<<16,, ahk_id %hwnd% ; WM_LBUTTONUP  
}


; Retrieves the control at the specified point.
; X         [in]    X-coordinate relative to the top-left of the window.
; Y         [in]    Y-coordinate relative to the top-left of the window.
; WinTitle  [in]    Title of the window whose controls will be searched.
; WinText   [in]
; cX        [out]   X-coordinate relative to the top-left of the control.
; cY        [out]   Y-coordinate relative to the top-left of the control.
; ExcludeTitle [in]
; ExcludeText  [in]
; Return Value:     The hwnd of the control if found, otherwise the hwnd of the window.
ControlFromPoint(X, Y, WinTitle="", WinText="", ByRef cX="", ByRef cY="", ExcludeTitle="", ExcludeText="")
{
    static EnumChildFindPointProc=0
    if !EnumChildFindPointProc
        EnumChildFindPointProc := RegisterCallback("EnumChildFindPoint","Fast")
    
    if !(target_window := WinExist(WinTitle, WinText, ExcludeTitle, ExcludeText))
        return false
    
    VarSetCapacity(rect, 16)
    DllCall("GetWindowRect","uint",target_window,"uint",&rect)
    VarSetCapacity(pah, 36, 0)
    NumPut(X + NumGet(rect,0,"int"), pah,0,"int")
    NumPut(Y + NumGet(rect,4,"int"), pah,4,"int")
    DllCall("EnumChildWindows","uint",target_window,"uint",EnumChildFindPointProc,"uint",&pah)
    control_window := NumGet(pah,24) ? NumGet(pah,24) : target_window
    DllCall("ScreenToClient","uint",control_window,"uint",&pah)
    cX:=NumGet(pah,0,"int"), cY:=NumGet(pah,4,"int")
    return control_window
}

; Ported from AutoHotkey::script2.cpp::EnumChildFindPoint()
EnumChildFindPoint(aWnd, lParam)
{
    if !DllCall("IsWindowVisible","uint",aWnd)
        return true
    VarSetCapacity(rect, 16)
    if !DllCall("GetWindowRect","uint",aWnd,"uint",&rect)
        return true
    pt_x:=NumGet(lParam+0,0,"int"), pt_y:=NumGet(lParam+0,4,"int")
    rect_left:=NumGet(rect,0,"int"), rect_right:=NumGet(rect,8,"int")
    rect_top:=NumGet(rect,4,"int"), rect_bottom:=NumGet(rect,12,"int")
    if (pt_x >= rect_left && pt_x <= rect_right && pt_y >= rect_top && pt_y <= rect_bottom)
    {
        center_x := rect_left + (rect_right - rect_left) / 2
        center_y := rect_top + (rect_bottom - rect_top) / 2
        distance := Sqrt((pt_x-center_x)**2 + (pt_y-center_y)**2)
        update_it := !NumGet(lParam+24)
        if (!update_it)
        {
            rect_found_left:=NumGet(lParam+8,0,"int"), rect_found_right:=NumGet(lParam+8,8,"int")
            rect_found_top:=NumGet(lParam+8,4,"int"), rect_found_bottom:=NumGet(lParam+8,12,"int")
            if (rect_left >= rect_found_left && rect_right <= rect_found_right
                && rect_top >= rect_found_top && rect_bottom <= rect_found_bottom)
                update_it := true
            else if (distance < NumGet(lParam+28,0,"double")
                && (rect_found_left < rect_left || rect_found_right > rect_right
                 || rect_found_top < rect_top || rect_found_bottom > rect_bottom))
                 update_it := true
        }
        if (update_it)
        {
            NumPut(aWnd, lParam+24)
            DllCall("RtlMoveMemory","uint",lParam+8,"uint",&rect,"uint",16)
            NumPut(distance, lParam+28, 0, "double")
        }
    }
    return true
}

Re: Is ControlClick dependent on a certain Window Class?

Posted: 21 Oct 2014, 15:22
by DavidP
Thanks for the code, but in this case :o (this seems to be way over my head) I think I shall simply stick with the "Click" command, which works somewhat clumsy, but works.

Re: Is ControlClick dependent on a certain Window Class?

Posted: 08 Feb 2021, 08:31
by BeamN6784
I hate to bump such an old post, but I'm curious as to whether or not this code could be adapted to permit ControlClickDrag?
Looking for a solution to the infamous "draw a picture in MsPaint" problem.
guest3456 wrote:
18 Oct 2014, 15:21
DavidP wrote:Sorry but I don't understand where and how I am to put the "WM_MOUSEMOVE" command in connection with the ControlClick command.
try this from the thread linked above, (prob will only work on 32bit ahk)

Code: Select all


ControlClick2(1031, 41, "PDF-XChange Editor")


;// required functions below, add these to the very bottom of your script

ControlClick2(X, Y, WinTitle="", WinText="", ExcludeTitle="", ExcludeText="")  
{  
  hwnd:=ControlFromPoint(X, Y, WinTitle, WinText, cX, cY, ExcludeTitle, ExcludeText)  
  PostMessage, 0x200, 0, cX&0xFFFF | cY<<16,, ahk_id %hwnd% ; WM_MOUSEMOVE
  PostMessage, 0x201, 1, cX&0xFFFF | cY<<16,, ahk_id %hwnd% ; WM_LBUTTONDOWN  
  PostMessage, 0x202, 0, cX&0xFFFF | cY<<16,, ahk_id %hwnd% ; WM_LBUTTONUP  
}


; Retrieves the control at the specified point.
; X         [in]    X-coordinate relative to the top-left of the window.
; Y         [in]    Y-coordinate relative to the top-left of the window.
; WinTitle  [in]    Title of the window whose controls will be searched.
; WinText   [in]
; cX        [out]   X-coordinate relative to the top-left of the control.
; cY        [out]   Y-coordinate relative to the top-left of the control.
; ExcludeTitle [in]
; ExcludeText  [in]
; Return Value:     The hwnd of the control if found, otherwise the hwnd of the window.
ControlFromPoint(X, Y, WinTitle="", WinText="", ByRef cX="", ByRef cY="", ExcludeTitle="", ExcludeText="")
{
    static EnumChildFindPointProc=0
    if !EnumChildFindPointProc
        EnumChildFindPointProc := RegisterCallback("EnumChildFindPoint","Fast")
    
    if !(target_window := WinExist(WinTitle, WinText, ExcludeTitle, ExcludeText))
        return false
    
    VarSetCapacity(rect, 16)
    DllCall("GetWindowRect","uint",target_window,"uint",&rect)
    VarSetCapacity(pah, 36, 0)
    NumPut(X + NumGet(rect,0,"int"), pah,0,"int")
    NumPut(Y + NumGet(rect,4,"int"), pah,4,"int")
    DllCall("EnumChildWindows","uint",target_window,"uint",EnumChildFindPointProc,"uint",&pah)
    control_window := NumGet(pah,24) ? NumGet(pah,24) : target_window
    DllCall("ScreenToClient","uint",control_window,"uint",&pah)
    cX:=NumGet(pah,0,"int"), cY:=NumGet(pah,4,"int")
    return control_window
}

; Ported from AutoHotkey::script2.cpp::EnumChildFindPoint()
EnumChildFindPoint(aWnd, lParam)
{
    if !DllCall("IsWindowVisible","uint",aWnd)
        return true
    VarSetCapacity(rect, 16)
    if !DllCall("GetWindowRect","uint",aWnd,"uint",&rect)
        return true
    pt_x:=NumGet(lParam+0,0,"int"), pt_y:=NumGet(lParam+0,4,"int")
    rect_left:=NumGet(rect,0,"int"), rect_right:=NumGet(rect,8,"int")
    rect_top:=NumGet(rect,4,"int"), rect_bottom:=NumGet(rect,12,"int")
    if (pt_x >= rect_left && pt_x <= rect_right && pt_y >= rect_top && pt_y <= rect_bottom)
    {
        center_x := rect_left + (rect_right - rect_left) / 2
        center_y := rect_top + (rect_bottom - rect_top) / 2
        distance := Sqrt((pt_x-center_x)**2 + (pt_y-center_y)**2)
        update_it := !NumGet(lParam+24)
        if (!update_it)
        {
            rect_found_left:=NumGet(lParam+8,0,"int"), rect_found_right:=NumGet(lParam+8,8,"int")
            rect_found_top:=NumGet(lParam+8,4,"int"), rect_found_bottom:=NumGet(lParam+8,12,"int")
            if (rect_left >= rect_found_left && rect_right <= rect_found_right
                && rect_top >= rect_found_top && rect_bottom <= rect_found_bottom)
                update_it := true
            else if (distance < NumGet(lParam+28,0,"double")
                && (rect_found_left < rect_left || rect_found_right > rect_right
                 || rect_found_top < rect_top || rect_found_bottom > rect_bottom))
                 update_it := true
        }
        if (update_it)
        {
            NumPut(aWnd, lParam+24)
            DllCall("RtlMoveMemory","uint",lParam+8,"uint",&rect,"uint",16)
            NumPut(distance, lParam+28, 0, "double")
        }
    }
    return true
}