Adding function to AHK script - autohotkey

I want to add to this script so that during the disable period you ccan still use key 4 if you are holding alt shift or cntrl
The code I have disables the 4 key entirely for a duration after it's pressed.
4WasPressedAt:=A_TickCount-5200
~*4::4WasPressedAt:=A_TickCount
#if A_TickCount-4WasPressedAt<5200
*4::return
I would like to have it functional if another key is being held down during that time and then the same for the 3 key with a 4.8 second delay.

This can be done by adding more conditions to your #If. Please see my solution below. Note that a possible shortcoming is that the combinations of alt+4, shift+4, and ctrl+4 will only produce a 4. That is, the possible expected effect of typing those combinations, such as shift+4 to get $, will only produce a 4.
4WasPressedAt := A_TickCount - 5200
$*4::
4WasPressedAt := A_TickCount
Send , {4}
Return
#If (( A_TickCount - 4WasPressedAt ) < 5200 && !( GetKeyState( "Alt" ) || GetKeyState( "Shift" ) || GetKeyState( "Ctrl" )))
*4::Return
#If

Related

Autohotkey: replace key combination (but with timeout)

When I type, I want my script to replace:
"ab" to "x"
"ac" to "y"
"a[more than 300ms]b" - don't replace, just leave "ab"
So, it should replace only if time between "a" and "b"/"c" is less than 300ms
tried doing this:
a & b :: Send x
a & c :: Send y
But it obviously doesn't 'forgive' me any delay
Would be great to get any hints, thanks!
A hotstring is usually good for replacements. To add that delay, here's a simple thing I could come up with:
~*a::APressTime := A_TickCount
:*B0:ab::
if(A_TickCount - APressTime <= 300)
SendInput, % "{BS 2}x"
return
First a simple hotkey for A that just saves the current system time with A_TickCount.
Then a hotstring to which I set some options you're likely going to want to use:
* so you don't need to type an ending character.
B0 so it doesn't backspace the letters ab automatically.
You might also want to use the ? option so the hotstring also works while "inside of a word". Add it in if you want.
Then we compare the current system time to the time when a was last pressed. If it's 300ms or less, send backspace twice followed up by x.
({BS 2} is same as {Backspace 2} or {Backspace}{Backspace})
To do the same with ac, just copy paste the hotstring again and switch b with c, and x with y.
Alternatively, here's something a bit more fancy to put them in the same hotstring label with a ternary:
:*B0:ab::
:*B0:ac::
if(A_TickCount - APressTime <= 300)
SendInput, % "{BS 2}" (SubStr(A_ThisHotkey, 0) = "b" ? "x" : "y")
return

Setting a control name with concatenated text+variable for ControlSetText usage

TL;DR I created a new variable (destinationControl) by concatenating a string, a separate string variable, and then another string. I tried using the variable destinationControl with ControlSetText, but its not working. Can anyone tell me why?
Long Explanation:
I'm attempting to send some data from an excel spreadsheet into another application using AHK ControlSetText. My issue comes in when I need the script to detect which one of two possible programs is the active one (the detection part is working) and then based on the name of the program, set the destination control name is slightly different.
prog_A_segment := "abc"
prog_B_segment := "def"
;determine which program is open
IfInString, OpenProgram, ProgA
{
ctrlSegment := prog_A_segment
}
else
ctrlSegment := prog_B_segment
;set control variable
destinationControl := "WindowsForms10.EDIT.app.0." . ctrlSegment . "_r13_ad11"
;activate program
WinActivate, % OpenProgram
WinWaitActive, % OpenProgram,,3
;open vendor form
Sleep 300
Send ^o
Sleep 200
Send Vendors
sleep 200
Send {ENTER}
Sleep 2000
;This does not work:
;pass information to vendor form control
ControlSetText, %destinationControl%, %myNumber%, %OpenProgram%
I know I could just slightly more manually set them based on the open program but i have about 25 controls in total and the only difference is that center segment so I thought this would be a little more elegant and cleaner.
When I use the above method it doesn't appear AHK can find the control. I'm assuming it has something to do with how I combined a string and a variable. Is there some way to make this approach work without doing this instead:
IfInString, OpenProgram, ProgA
{
destinationControl1 := "WindowsForms10.EDIT.app.0.abc_r13_ad11"
....
destinationControl25 := "WindowsForms10.EDIT.app.0.abc_d52_ad11"
}
else
destinationControl1 := "WindowsForms10.EDIT.app.0.def_r13_ad11"
....
destinationControl25 := "WindowsForms10.EDIT.app.0.def_d52_ad11"
I agree with Josh Brobst that your first piece of code would work with the missing quote added.
Well, here's what you want to try anyways:
ctrlSegment := InStr(OpenProgram, ProgA) ? "abc" : "def"
Loop Parse, % "r13, ... ,d52", CSV
ControlSetText % "WindowsForms10.EDIT.app.0." ctrlSegment "_" A_LoopField "_ad11"
, % myNumber, % OpenProgram

AutoHotkey - Detect double press of AltGr

I want to detect double press on AltGr.
According to documentation:
; Example #4: Detects when a key has been double-pressed (similar to double-click).
; KeyWait is used to stop the keyboard's auto-repeat feature from creating an unwanted
; double-press when you hold down the RControl key to modify another key. It does this by
; keeping the hotkey's thread running, which blocks the auto-repeats by relying upon
; #MaxThreadsPerHotkey being at its default setting of 1.
; Note: There is a more elaborate script to distinguish between single, double, and
; triple-presses at the bottom of the SetTimer page.
~RControl::
if (A_PriorHotkey <> "~RControl" or A_TimeSincePriorHotkey > 400)
{
; Too much time between presses, so this isn't a double-press.
KeyWait, RControl
return
}
MsgBox You double-pressed the right control key.
return
AltGr is actually a combination of LControl & RAlt. So, for AltGr, script should be something like this:
~LControl & RAlt::
if (A_PriorHotkey <> "~LControl & RAlt" or A_TimeSincePriorHotkey > 400)
{
click
KeyWait, LControl & RAlt
return
}
click 2
return
But when I try to load this script, AutoHotkey gives an error:
Maybe there is a way to make an alias for key combinations.
As mentioned in the comments, KeyWait can only wait on one key (not hotkey) at a time. You only need to wait for RAlt to be released, not the combination of LCtrl and RAlt.
This works:
~LControl & RAlt::
if (A_PriorHotkey <> "~LControl & RAlt" or A_TimeSincePriorHotkey > 400)
{
KeyWait, RAlt
return
}
MsgBox Double-click
return
However, in this case KeyWait is only being used (in combination with the default #MaxThreadsPerHotkey setting of 1) to prevent key-repeat from activating the hotkey. You can remove KeyWait and it will still detect double-presses; but it will also activate if you hold AltGr down until it auto-repeats.
Note that in your case, double-pressing the hotkey would click three times: once on the first press and an additional two times on the second press.
If you just want to use AltGr as a mouse button and allow double-click, all you need is <^RAlt::Click.
If you want to perform two different actions depending on whether it is a single or double click, you must delay the response to the first click until you know whether there's a second click. For example:
<^RAlt::
KeyWait RAlt
KeyWait RAlt, D T0.4
if ErrorLevel
MsgBox Single
else
MsgBox Double
return

AHK: Assign hotkey only for one specific active window and not for others

I have just done a piece of code that does the following thing. When I make a selection by mouse in Firefox or EndNote, the script sents a Ctrl+c and checks the clipboard for a regex match. If there is a match, it changes the clipboard contents and shows a tooltip. It works fine for these two programs. Adobe Acrobat sometimes shows an error when a Ctrl+c is sent (even if a user presses a ctrl-c Acrobat sometimes shows famous "There was an error while copying to the Clipboard. An internal error occurred). So it decided to assign an F9 hotkey, but it works for all programs and not just for Acrobat. How do I assign an hotkey for only one window – Acrobat? Here's my code. I know it's lame – I am a newbie to programming in general, and in AHK in particular.
#If WinActive("ahk_exe firefox.exe") || WinActive("ahk_exe EndNote.exe") || WinActive("ahk_exe Acrobat.exe")
if WinActive("ahk_exe Acrobat.exe")
F9::
{
Clipboard:=""
send,^c
ClipWait, 1
ToolTip % Clipboard := RegExReplace(Clipboard, "\r\n", " ")
SetTimer, ToolTipOff, -1000
}
return
~LButton::
now := A_TickCount
while GetKeyState("LButton", "P")
continue
if (A_TickCount-now > 500 )
{
Send ^c
if WinActive("ahk_exe firefox.exe")
{
If RegExMatch(Clipboard, "[0-9]\.\s[A-Za-z,]*\s[A-Za-z]*")
{
regex := "[0-9]\.\s*|\s?\([^)]*\)|\."
replace := ""
}
else If RegExMatch(Clipboard,"[0-9]{2}[-\/][0-9]{2}[-\/][0-9]{4}")
{
Clipboard := RegExReplace(Clipboard, "^0", "")
regex := "\/"
replace := "."
}
else return
}
else if WinActive("ahk_exe EndNote.exe")
{
If RegExMatch(Clipboard, "[a-z]+\,\s[A-Z0-9‘“]")
{
regex := "\??!?\:|\?|!"
replace := "."
}
else return
}
ToolTip % Clipboard := RegExReplace(Clipboard, regex, replace)
SetTimer, ToolTipOff, -1000
}
return
#If
ToolTipOff:
ToolTip
return
I see some very fundamental problems in the first few lines. Let me explain...
There are two types of if-statements in AutoHotkey If and #If.
You usually always use the normal If-statements unless you are doing something with hotkeys and you want specific hotkeys to be context-sensitive.
Here are some important rules:
Normal If-statements have to use curly braces {} to mark the area of code that should be executed if the expression is true. If you don't use curly braces, the If-statement will work as if you had put curly braces around the first command directly under the If-statement.
Example:
If WinActive("Firefox") {
Send, Test
MsgBox, The script just typed "Test.
}
Another example:
If WinActive("Firefox")
MsgBox, Firefox is the active window.
Normal If-statements cannot be used around a hotkey definition, but only within it.
This is allowed:
F1::
If (A_OSVersion = "WIN_7") {
MsgBox, Your operating system is Windows 7 and you just pressed F1.
}
Return
This is NOT:
If (A_OSVersion = "WIN_7") {
F1::
MsgBox, Your operating system is Windows 7 and you just pressed F1.
Return
}
But there is a way around that and that is #If-statements.
#If-statements don't use curly braces ever.
They can only be used on hotkey definitions.
And they can only be closed by another #If-statement.
(It's very common to simply use an empty #If to close it.)
Examples:
#If (A_OSVersion = "WIN_7")
F1::
MsgBox, Your operating system is Windows 7 and you just pressed F1.
Return
#If
A more complex example:
#If (A_ScreenWidth >= 1920)
F1::
MsgBox, Your your screen is at least 1920 pixels wide.
Return
F2::
MsgBox, Your operating system is %A_OSVersion%.
Return
#If (A_ScreenWidth < 1920)
F1::
MsgBox, Your your screen width is smaller than 1920 pixels.
Return
#If
As you might have guessed by now, hotkey definitions are always started by a pattern like this hotkey:: and closed by a Return. Although you can define hotkeys on a single line.
Examples:
F1::MsgBox, Hello!
F2::a ;This will remap the F2 key to an a-key.
Hotkeys by themselves do never use curly braces! Though an If-statement within a hotkey still has to use them according to the before mentioned rules.

AutoHotkey repeated key sequences, detecting KEYUP and KEYDOWN for modifiers

Similar to what this question covers, I'm trying to bind two sequences of keys.
Ideally I'd like to bind Alt DOWN,-,-,-,Alt UP to an em-dash (—) and Alt DOWN,-,-,Alt UP to an en-dash (–).
What I have almost works for em-dashes but not quite:
; Em-dash
!-::
Input Key, L1
if Key=-
Input Key, L1
if Key=-
Send {ASC 0151}
return
; En-dash
;!-::
;Input Key, L1
;if Key=-
;Send {ASC 0150}
;return
The em-dash sequence works like Alt+-,-,-, instead of what I'm trying to match. I'm not sure how to only test for Alt DOWN and Alt UP. The en-dash sequence fails altogether to bind because !- has already been bound.
Have a look at this one:
dashCount := 0
!-::
dashCount++
if(dashCount = 1) {
SetTimer, WaitForAlt, -1
}
return
WaitForAlt:
KeyWait, Alt
if(dashCount = 2) {
Send {ASC 0150}
} else if(dashCount = 3) {
Send {ASC 0151}
}
dashCount := 0
return
It seems to do the job well. The code works by counting each time Alt + - gets pressed. Concurrently, a pseudo-thread is spawned that waits for Alt to be released and then sends the appropriate dash, depending on the counter.