1

I want to use AutoHotkey to scroll a document horizontally in Adobe Reader X. Sending wheel messages (0x20e) does not work, nor does sending scroll messages (0x114). The only method I could find is to send clicks to the scrollbar arrows, but that makes scrolling horizontally very slow, besides making any concurrent vertical scrolling lag badly. Also, I notice that my mouse driver (UltraNav) can scroll under an apparently modal dialog in Adobe Reader X such as the Open File dialog, whereas none of the above three methods can. So does anyone know what my mouse driver could be doing or have another way?

I just found a fourth method which works quite well for most applications for which the first two fails, which is to send arrow keys to the scrollbar. When it does respond properly, it also responds to {PgUp} and {PgDn} that scrolls by page. However, it still does not work underneath a modal dialog, so what mouse driver does remains a mystery to me, though it seems to scroll by the same amounts. Also, this method does not work in Windows Explorer (expectedly); the keys sent to the scrollbars get sent to the main area also. For example controlsend,%scrollbarname%,{Down},ahk_id %window% will successfully scroll the scrollbar but will also cause the current selection position to move down if possible. I cannot find any other way of controlling the horizontal scrollbar in Windows Explorer without sending mouse clicks.

Edit

See AutoHotkey scrolling & middle click & mouse acceleration which was my original goal, Adobe Reader being just one of many applications that does not understand the usual wheel messages.

4 Answers 4

2

Adobe Reader has a strange way of handling horizontal scrolling. This is what I use to fix the issue:

#IfWinActive, ahk_exe Acrobat.exe
F13::
  While GetKeyState("F13") {
    Send, {Shift down}{WheelUp}
    Sleep, 100
  }
  Send, {Shift up}
  Return

F14::
  While GetKeyState("F14") {
    Send, {Shift down}{WheelDown}
    Sleep, 100
  }
  Send, {Shift up}
  Return
#IfWinActive

Note: I changed the key assignment of my left and right mouse scroll to F keys so that they could be picked up in AutoHotKey since I also use Logitech SetPoint

3
  • Sorry I forgot to answer my own question after I found a complete solution on my own a few months later. See my answer for the details.
    – user21820
    Commented Jan 19, 2016 at 10:07
  • I don't have enough points to comment to your answer but just wanted to say thanks for sharing your solution.
    – Snake
    Commented Jan 21, 2016 at 5:53
  • Sure you're welcome! If you've any questions about my solution or my full script on the other question I've created, feel free to ask me. To notify a single user, put @<username> anywhere in your comment.
    – user21820
    Commented Jan 21, 2016 at 6:00
0

You can click (only push down left mouse button, dont reliase it yet) in the beginning of scroll bar, in the place where red dot is on the image below. After move mouse as down as possible, in the place where green dot is on the image below. Now release left mouse button. The scrolling speed should be good enough.

enter image description here

Here is complete AutoHotkey script code:

CoordMode, Mouse, Screen
InitX := 
InitY :=
DestX := 
DestY :=
Click, Left, Down, %InitX%, %InitY%
Mousemove, %DestX%, %DestY%, 0
Click, Left, Up

Variables InitX and InitY should hold coordinates (x and y respectively) initial points. Coordinates of red dot that is on the image above.

Variables DestX and DestY should hold coordinates (x and y respectively) for destination points. Coordinates of green dot that is on the image above.

EDITED:

May that can help you: http://ahkscript.org/boards/viewtopic.php?f=5&t=4028

Download new version of AutoHotkey from http://ahkscript.org/ (current version). AutoHotkey from autohotkey.com is outdated!

7
  • Well I didn't mentioned this method in my question, but it is not what I want since the scroll speed would vary with the width of the document and would not be smooth at all for wide documents. And I wanted horizontal scroll because vertical wheel messages work well for most applications.
    – user21820
    Commented Jul 16, 2014 at 1:49
  • By the way, I'm using AutoHotkey_L, which I believe (can't recall where I downloaded it from) is from ahkscript.org. =)
    – user21820
    Commented Jul 16, 2014 at 1:51
  • @user21820 My script will work with any kind of scroll bar (horizontal or vertical or any other ;)) just give it right coordinates (initial and destination). Scroll speed will be fast enough. If you need to make it smooth, you can play with mousemove speed. Currently it has value 0. It can go up to 100 (more value, less the speed). And yes, scroll speed will vary with the width of the document. Code updated.
    – vasili111
    Commented Jul 16, 2014 at 3:07
  • You don't understand. When the document is long, just dragging the scrollbar by 1 pixel will cause it to scroll by many pages. That is absolutely undesirable, which is why wheel messages are better for vertical scrolling. For horizontal scrolling it is usually not too bad, but I still do not want scroll speed varying with the width of the document. Anyway since my fourth method works perfectly with Adobe Reader, my only remaining question is what exactly UltraNav does. But thanks for sharing your method!
    – user21820
    Commented Jul 16, 2014 at 8:33
  • @user21820 At the end of my question check section EDITED. Maybe it can help you.
    – vasili111
    Commented Jul 21, 2014 at 17:18
0

Short answer

For horizontal scrolling in Adobe Reader X, send scroll messages to the parent of the scrollbar, as in sendscrolltoscrollbarparent in the code. Many other ways would not work properly. This method somehow gives very fast scrolling, even better than my original mouse driver.

Long answer

I had found my own answers, but had forgotten about this question. Basically I used an idiosyncratic method for each and every crazy application. Since there are too many, I've created a separate question and answer for the whole lot (AutoHotkey scrolling & middle click & mouse acceleration), and only give the parts relevant to Adobe Reader here.

The process should go like this. First you call gettarget, which assumes the mouse position is stored in mx,my and finds the correct target for the scroll events based on what is currently under the mouse. Then you repeatedly call scroll after adding the amount to scroll to sx,sy.

For Adobe Reader, even vertical scrolling depends on sending wheel messages to the right place, which is not consistent and hence I ended up hard-coding for the two main cases, which are scrolling the document display area and scrolling the bookmarks area. To find out which case it is, I check whether the parent of the control under the mouse has a descendant called AVL_AVView4 or not. If it does, then that's the right one to send vertical wheel messages to, performed by sendwheel. But for horizontal scrolling, it turns out that sending scroll messages to the parent control of the correct scrollbar works in both places, performed by sendscrolltoscrollbarparent. The correct scrollbar is the one called scrollbar1 that is the descendent of the parent of the control under the mouse.

Code

#commentflag // ; Change to C++ comment style

global mx,my
global sx:=0
global sy:=0
global ctrl,window,parent
global methodx
global methody
global scrollbarx
global scrollbary

global max16bit:=32767

gettarget()
{
    ctrl:=getctrlat(mx,my)
    window:=getwindow(ctrl)
    class:=getclass(window)
    parent:=getparent(ctrl)
    parentname:=getnameatroot(parent)
    if( class=="AcrobatSDIWindow" )
    {
        if( regexmatch(parentname,"AVL_AVView")==1 )
        {
            ctrl:=getdescendant(parent,"AVL_AVView4")
            if( ctrl=="" )
            {
                ctrl:=getdescendant(parent,"AVL_AVView1")
            }
            methodx:="scrolltoscrollbarparent"
            scrollbarx:="scrollbar1"
            methody:="wheel"
        }
    }
}

scroll:
    critical on
    tx:=sx
    ty:=sy
    sx-=tx
    sy-=ty
    rx:=0
    ry:=0
    if( tx!=0 )
    {
        txi:=rtoz(tx)
        rx:=tx-txi
        if( txi!=0 )
        {
            if( methodx=="scrolltoscrollbarparent" )
            {
                sendscrolltoscrollbarparent(scrollbarx,"h",txi)
            }
        }
    }
    if( ty!=0 )
    {
        if( methody=="wheel" )
        {
            sendwheel("v",-ty)
        }
    }
    sx:=rx
    sy:=ry
return

sendwheel(dir,amount)
{
    t:=a_tickcount
    msg:=( dir=="v" ? 0x20a : 0x20e )
    flags:=getkeystate("Ctrl")<<3|getkeystate("Shift")<<2
    amount*=120
    while( amount>max16bit )
    {
        sendmessage msg,max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit
        amount-=max16bit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
    while( amount<-max16bit )
    {
        sendmessage msg,-max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit
        amount+=max16bit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
    sendmessage msg,round(amount)<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit
}
sendscrolltoscrollbarparent(name,dir,amount)
{
    sb:=getdescendant(parent,name)
    sbp:=getparent(sb)
    t:=a_tickcount
    msg:=( dir=="v" ? 0x115 : 0x114 )
    flag:=( amount<0 ? 0 : 1 )
    loop % abs(amount)
    {
        sendmessage msg,flag,sb,,ahk_id %sbp%,,,,timelimit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
}

rtoz(r)
{
    return ( r>0 ? floor(r) : ceil(r) )
}
getparent(handle)
{
    return dllcall("GetParent","uint",handle)
}
getname(root,handle)
{
    local CH,CN,S,P
    WinGet, CH, ControlListHwnd, ahk_id %root%
    WinGet, CN, ControlList, ahk_id %root%
    setformat integerfast,h
    handle+=0
    handle.=""
    setformat integerfast,d
    LF:= "`n",  CH:= LF CH LF, CN:= LF CN LF,  S:= SubStr( CH, 1, InStr( CH, LF handle LF ) )
    StringReplace, S, S,`n,`n, UseErrorLevel
    StringGetPos, P, CN, `n, L%ErrorLevel%
    Return SubStr( CN, P+2, InStr( CN, LF, 0, P+2 ) -P-2 )
}
getdescendant(handle,name)
{
    local CH,CN,S,P
    WinGet, CH, ControlListHwnd, ahk_id %handle%
    WinGet, CN, ControlList, ahk_id %handle%
    setformat integerfast,h
    handle+=0
    handle.=""
    setformat integerfast,d
    LF:= "`n",  CH:= LF CH LF, CN:= LF CN LF,  S:= SubStr( CN, 1, InStr( CN, LF name LF ) )
    StringReplace, S, S,`n,`n, UseErrorLevel
    StringGetPos, P, CH, `n, L%ErrorLevel%
    Return SubStr( CH, P+2, InStr( CH, LF, 0, P+2 ) -P-2 )*1
}
getnameatroot(handle)
{
    return getname(dllcall("GetAncestor","uint",handle,"uint",2),handle)
}
getnameaschild(handle)
{
    return getname(getparent(handle),handle)
}
getclass(handle)
{
    local class
    wingetclass class,ahk_id %handle%
    return class
}
getwindow(handle)
{
    return dllcall("GetAncestor","uint",handle,"uint",2)
}
getctrlat2(x,y,first,current)
{
    /*
        Pushes the following invisible container controls to the back because they are in front of their contents for no reason
            SysTabControl32 : The usual class that contains tabbed panes ( Mouse properties , ... )
            Static : A class occasionally used to contain tabbed panes ( Programmer's Notepad Options > Fonts and Colours > Advanced , ... )
            Button : A typical class used to contain a List Box ( Outlook Contact > Properties > General > Members , ... )
        Executes WindowFromPoint again to access the contents of such container controls
    */
    local handle,class,style
    class:=getclass(current)
    winget style,style,ahk_id %current%
    if( class=="SysTabControl32" or class=="Static" or ( class=="Button" and (style&0x7)==0x7 ) )
    {
        dllcall("SetWindowPos","uint",current,"uint",1,"int",0,"int",0,"int",0,"int",0,"uint",0x3)  // push it to the back where it belongs
        handle:=dllcall("WindowFromPoint","int",x,"int",y)
        //handle:=DllCall( "WindowFromPoint", "int64", (my << 32) | (mx & 0xFFFFFFFF), "Ptr") // for negative 64-bit
        if( handle==first )
        {
            return first
        }
        return getctrlat2(x,y,first,handle)
    }
    return current
}
getctrlat(x,y)
{
    local handle
    handle:=dllcall("WindowFromPoint","int",x,"int",y)
    //handle:=DllCall( "WindowFromPoint", "int64", (my << 32) | (mx & 0xFFFFFFFF), "Ptr") // for negative 64-bit
    return getctrlat2(x,y,handle,handle)
}
0

My solution:

#IfWinActive, ahk_exe Acrobat.exe
WheelLeft::
    Send,{shift down}
    sleep,20
    Send,{WheelUp}
    sleep,20
    Send,{shift up}
    return
WheelRight::
    Send,{shift down}
    sleep,20
    Send,{WheelDown}
    sleep,20
    Send,{shift up}
    return
#IfWinActive

You can increase the number 20 to scroll slower

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .