at the prompting of stevezen, I have incorporated multiclicking; rocking with more than 2 mouse buttons; and rocking mixed with gestures. to achieve this, the gesture tracking had to be much more flexible, so large chunks have been re-written. would appreciate any feedback re use/difficulties/bugs etc.
Code:
multiclicktime=250 ; set to 0 if you don't want double/triple/multi clicking. otherwise set as fast as you can double-click see http://www.autohotkey.com/forum/viewtopic.php?t=41411
rockinstant:=false ; if true, rocking action fires on downclick (which is faster) but stops further tracking
sensitivity=4 ; adjust if required. if too low, a simple "down/up" (V) might be recorded as "down/right/up" (U)
demomode:=true ; demonstration mode on/off
coordmode,mouse,screen
winget,windowD,id,ahk_class Progman ;windowD is the desktop
gosub cleanup
return
#MaxThreadsPerHotkey 5 ; needed when multiclicking
browser_search:: ; "find" key on logitech mx620 mouse, used here as an example
lbutton::
rbutton::
mbutton:: ; list whatever mouse buttons you want tracked.
xbutton1:: ; works best if all available listed
xbutton2::
critical
thishotkey:=a_thishotkey="xbutton2" ? "f" : substr(a_thishotkey,1,1) ;buttons named from initial except xbutton2
buttonsdown.=thishotkey . (getkeystate("lbutton","p") ? "l" : "")
gosub multiclick
gcalled=0 ; records if gesture has been called to avoid unintended double-click double calls
winget,windowA,id,A ; windowA is the active window, useful for some gestures
hotkey,lbutton,on ; disables normal left click during gesture
hotkey,lbutton up,off ; prevents next line of code from calling on lbutton up
click up ; prevents drag box appearing when left/right rocking and gesturing eg. gl/r(d)
hotkey,lbutton up,on
if (window1<>windowD)
window2:=window1 ; used in some gestures, window2 is the window the previous gesture acted on
mousegetpos xpos1,ypos1,window1 ; window1 is the window underneath the current gesture
if mousetracking
{
gosub trackassign
record.=(getkeystate("lbutton","p") and not record ? "l/" : "") . (record and thishotkey<>substr(record,0,1) ? "/" : "") . thishotkey
settimer,mousetrack,10 ; "/" is the rocking symbol
}
else
{
gosub parserecord ; finds position of last "/" in record for next line of code
record:=regexreplace(record,"/\w*","/" . thishotkey . multitrack,position,1,position) ; allows rocking with different buttons
} ; why "\w*"? \w* may represent a single, double or multiclick, but not a parenthesis eg /r, /rr, /rrr, as in gl/rrr(ud)
if instr(record,thishotkey . "/") or not instr(buttonsdown,substr(record,1,1))
{
gosub cleanup ; avoids possible errors when different buttons hit nearly simultaneously
return
}
if rock()="rocking" and rockinstant
{ ; calls action before mouse button released (rockinstant is user defined)
rockdown:=thishotkey ; advantage - faster action
goto rbutton up ; disadvantage - gesture called so no more tracking
}
rockdown=
return
browser_search up::
rbutton up::
lbutton up::
mbutton up::
xbutton1 up::
xbutton2 up::
thishotkey:=a_thishotkey="xbutton2" ? "f" : substr(a_thishotkey,1,1)
if a_thishotkey contains up
{
buttonsdown:=regexreplace(buttonsdown,thishotkey,"")
if (rockdown=thishotkey)
return
}
%thishotkey%count=0 ; in a double-click, there are 2 "button up" calls. we only want one action however
multiclick:=false
if not mousetrack ; no need for delay to check for multiclick if button released after mouse tracking
sleep % multiclicktime-a_tickcount+gtime1 ;delay to check for multiclick
thishotkey:=a_thishotkey="xbutton2" ? "f" : substr(a_thishotkey,1,1)
%thishotkey%count+=1 ; if multiple threads are waking from (above) sleep, only 1 is let through
if (%thishotkey%count>1 or multiclick) ; muticlick can only be true if mouse clicked during
return ; multiclicktime sleep
mousetracking:=false ; no more tracking of the mouse once a mouse button has been released (unless multiclicking)
gosub trackassign
cleanup:=(rock()<>"rocking") ? true : false ; this allows for repeat rocking
callgesture:=not wheel and record and not (rock()="rockover" and gcalled) ? true : false
if cleanup ; these lines allow for cleanup before the gesture action is called.
gosub cleanup ; (note that the gesture itself is not erased by the cleanup). this prevents a called
if callgesture ; action that takes a significant time stopping further tracking.
gosub callgesture
return
#maxthreadsperhotkey 1 ; autohotkey default setting. set to your usual setting
callgesture:
gcalled:=1
gesture.=(wheel ? "-" : "") . wheel
if demomode
msgbox,,Demo,%gesture%, % (wheel ? .5 : strlen(gesture)/5+1)
else
if islabel(gesture) ; checks if gesture is labelled
gosub %gesture%
return
cleanup:
hotkey,lbutton,off ; restore normal left mouse button function
hotkey,lbutton up,off
settimer,mousetrack,off
record=
wheel=
mousetracking:=true
mousetrack=
return
mousetrack:
critical off ; don't want this getting in way of double-click checking
mousegetpos xpos2,ypos2
track:=(abs(ypos1-ypos2)>=abs(xpos1-xpos2)) ? (ypos1>ypos2 ? "u" : "d") : (xpos1>xpos2 ? "l" : "r") ;determines up down left right
if (abs(ypos1-ypos2)>sensitivity or abs(xpos1-xpos2)>sensitivity) and (track<>SubStr(mousetrack, 0, 1)) ;track if x or y changing and direction changing
mousetrack.=track
xpos1:=xpos2
ypos1:=ypos2
return
parserecord: ; script allows for rocking more than once eg gr/l/x:
loop,parse,record ; finds position of last "/"
if a_loopfield=/
position:=a_index
return
multiclick:
gtime2:=gtime1 ; why use time difference and not keywait? in the time between the 2 clicks, you don't want to miss any
gtime1:=a_tickcount ; mousetracking. the gesture gr(u) can be all over in less time than a double click takes
if (thishotkey=substr(record,0,1) and gtime1-gtime2<multiclicktime)
{
multiclick:=true
multitrack.=thishotkey
}
else
{ ; avoids rocking errors when time between clicks is too short
multiclick:=false
multitrack=
}
return
trackassign:
settimer,mousetrack,off
if mousetrack ; letters enclosed in parentheses
record.="(" . mousetrack . ")" ; are gesture directions
mousetrack=
gesture:="g" . record
return
esc::
exitapp
return
wheelup::
wheeldown::
wheel:=a_thishotkey
if record and mousetracking
{
gosub trackassign
gosub callgesture
}
if not record
sendinput {%wheel%}
return
rock()
{
global
if record contains / ; "/" is the rocking symbol
{
gosub parserecord ; "position+1" is the first letter after the forward slash
if (thishotkey=substr(record,position+1,1))
return "rocking"
else
return "rockover" ; in the button up section, if the button down and
; the button up don't match, rocking is finished
}
}
gr: ;normal right click
click right,xpos1,ypos1
return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;; Examples ;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;; wheel examples ;;;;;;;;;;
gr-wheelup: ;change volume with wheel - hold right button down and use wheel
Send {Volume_Up}
return
gr-wheeldown:
Send {Volume_Down}
return
;;;;;;;; multi click examples ;;;;;;;;;;
grr:
msgbox,,,double right click,5
return
grr(l):
msgbox,,,double right click then gesture left ,5
return
grr/ll:
msgbox,,,double right click then rock with double right ,5
return
gr/l/m:
msgbox,,,right click then rock with left and then middle ,5
return
;;;;;;;; rocker examples ;;;;;;;;;;
gr/l:
msgbox,,,keep right held down and click left will repeat action,5
return
gl/r:
msgbox,,,rocker left then right. careful where you click,5
return
gr(l)/l:
msgbox,,,you gestured left and clicked,5
return
;;;;;;;;;;;; 4th mouse button example ;;;;;;;;;;
gx(l):
msgbox,,,you've gestures left with the 4th mouse button,5
return
gx/r:
msgbox,,,rocker 4th button then right,5
return
gr/x:
msgbox,,,rocker right then 4th button,5
return
;;;;;;;; other examples ;;;;;;;;;;;;;;
gr(u): ;; gesture up ;; maximize window under cursor. maximize last window if cursor over desktop
if (window1=windowD)
{
if (window2<>"")
winmaximize, ahk_id %window2%
}
else
winmaximize ahk_id %window1%
return
grd: ;minimize window under cursor
if (window1<>windowD) ;don't minimize the desktop
winminimize, ahk_id %window1%
return
gr(l): ;browser back
winactivate ahk_id %window1%
sendinput {Browser_Back}
return
gr(r): ;browser forward
winactivate ahk_id %window1%
sendinput {Browser_Forward}
return
gr(dr): ;restore last window.
winrestore ahk_id %window2%
return
gr(ldr): ;close window under cursor (draw a C)
if (window1<>windowD)
winclose ahk_id %window1%
return