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

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.

Related

Why autohotkey deos not send Ctrl-Space where Space is an input

Consider the following assignment:
When I type - followed by a key, the result is Ctrl-key. This work for ordinary keys.
But when the key is whitespace, it does not work.
Any idea why this happens? And how to fix the code?
-::
Input, key, L1,{LCtrl}
send, ^{%key%}
return
Edit.
Try to run the above script a program which has Ctrl-Space as a shortcut to see that it does not work. In fact, if you press - followed by Space, the script is suppose to call Ctrl-Space but it is not the case. For example:
In Microsoft Excel or in Libreoffice Calc, Ctrl-Space can select the current column.
In Emacs Ctrl-Space is reserved for setting a Mark.
Use SendInput instead.
Tested in Excel to mimic ^a, ^x, ^v, ^space
-::
Input, key, L1,{LCtrl}
SendInput, ^%key%
Return
If you want to handle "special" keys, add those keys to the list of endkeys using this syntax
Input [, OutputVar, Options, EndKeys, MatchList]
And then check to see which endkey was pressed
Tested in Firefox to mimic ^PgDn, ^PgUp
Input, key, L1,{LCtrl}{PgUp}{PgDn}
If (ErrorLevel = "EndKey:PgUp") {
SendInput ^{PgUp}
}
Else If (ErrorLevel = "EndKey:PgDn") {
SendInput ^{PgDn}
}
Else If (ErrorLevel = "EndKey:LCtrl") {
Return ;assumed you just want to abort input with the control key
}
Else {
SendInput, ^%key%
}
Return

AutoHotkey - send hotkeys to nested folders

Using Autohotkey, I can send "ctrl+" to Windows File Explorer, to auto adjust column width.
Manually, it's ctrl+ (the + in the numpad).
This code below works, but only for first level folders, not if I open a folder within a folder.
Is there a way to send "ctrl+" again for each subfolders I might open?
Gui, +LastFound
DllCall("RegisterShellHookWindow", UInt, WinExist())
MsgNum := DllCall("RegisterWindowMessage", Str, "SHELLHOOK")
OnMessage(MsgNum, "ShellMessage")
Return
ShellMessage(wParam, lParam) {
wTitle = ahk_id %lParam%
WinGet, pname, ProcessName, %wTitle%
If (wParam != 1 || pname != "Explorer.exe")
Return
WinActivate, %wTitle%
Send ^{NumpadAdd} ;ctrl+ (numpad)
}
Seems like a pretty questionable approach.
I would rather see about somehow setting this to be the default behavior, or automating this with COM instead of sending a hotkey.
Anyway, for the hotkey approach, this seems to do the trick:
;No need to create a gui, A_ScriptHwnd is used for this
DllCall("RegisterShellHookWindow", UInt, A_ScriptHwnd)
MsgNum := DllCall("RegisterWindowMessage", Str, "SHELLHOOK")
OnMessage(MsgNum, "ShellMessage")
Return
ShellMessage(wParam, lParam)
{
static _time := 0
if (wParam = 6 && A_TickCount - _time > 100 && WinActive("A") = lParam)
{
_time := A_TickCount
WinGet, pname, ProcessName, % "ahk_id " lParam
if (pname = "explorer.exe")
{
ControlFocus, DirectUIHWND2, % "ahk_id " lParam
SendInput, ^{NumpadAdd}
}
}
}
So, first ditched the legacy way of getting a hwnd for the current script, and used A_ScriptHwnd (docs).
Also, ditched legacy syntax overall.
Then switched over to the HSHELL_REDRAW (docs) event to check for window title changes.
And the timing stuff is to filter out duplicate shell messages. When the title changes, we actually receive like 10+ of those messages at once. Only need to run the hotkey once.
So a simple 100ms cooldown does the trick.
A_TickCount (docs) is used for this.
Also made the checking order smarter.
No need to get the process name if we didn't even receive the correct shell message.
And finally before sending the hotkey, activate the correct control so the hotkey will work. This part is likely going to be wrong for you if you're on some older or future Windows version.
Also switched over to SendInput (docs) due to it being the recommended faster and more reliable send mode.

How to keep original keybind aswell as well as a new one in AHK?

So i have an ahk script to write letters with macrons for Latin words. When you press tilde and a vowel; it types that vowel with a macron. My only problem is removes the function of the tilde key, except typing macrons. adding a ~ to keep the original keybind makes it so when i hit tilde and a vowel, it types tilde and a long vowel. How do i fix this? (this is supposed to replicate the Maori keyboard, but that was too tedious)
` & a::
send, ā
return
` & e::
send, ē
return
` & i::
send, ī
return
` & o::
send, ō
return
` & u::
send, ū
return
Add `::` to your script and backtick (`) will fire when you release the key and tilde (~) will fire on press.
https://www.autohotkey.com/docs/Hotkeys.htm#combo
Alternatively, you can use
Hotstrings
aa := Chr(257)
:?O:`a::
Send, %aa%
Return
Typing `athen pressing an ending character (like Space) creates ā.
• The "O" is an Option which omits the ending character (such as an unwanted Space).
• The "?" is an Option which allows the hotstring to be triggered inside of words, so you can type pra` then a Space and it will become prā.
• Using Hotstrings instead of Hotkeys will keep the functionality of the Tilde key unchanged.
• By default, the backtick (`) is used as the AHK escape character. For this Hotstring to work properly, the escape character should be changed. Example:
#EscapeChar \
My version of your script:
#EscapeChar \
; lower case graphemes
aa := Chr(257)
ee := Chr(275)
ii := Chr(299)
oo := Chr(333)
uu := Chr(363)
yy := Chr(563)
ae := Chr(230)
; upper case graphemes
upper_aa := Chr(256)
upper_ee := Chr(274)
upper_ii := Chr(298)
upper_oo := Chr(332)
upper_uu := Chr(362)
upper_yy := Chr(562)
upper_ae := Chr(198)
; Hotstrings
:?O:`a::
Send, %aa%
Return
:?O:`a`::
Send, %upper_aa%
Return
:?O:`e::
Send, %ee%
Return
:?O:`e`::
Send, %upper_ee%
Return
:?O:`i::
Send, %ii%
Return
:?O:`i`::
Send, %upper_ii%
Return
:?O:`o::
Send, %oo%
Return
:?O:`o`::
Send, %upper_oo%
Return
:?O:`u::
Send, %uu%
Return
:?O:`u`::
Send, %upper_uu%
Return
:?O:`y::
Send, %yy%
Return
:?O:`y`::
Send, %upper_yy%
Return
:?O:`ae::
Send, %ae%
Return
:?O:`ae`::
Send, %upper_ae%
Return
>
The above snippet saves the true ASCII values of the characters to variables (as to avoid compatibility issues), rather than trying to print the characters directly through "Send".
Other Options (and more information on Hotstrings) can be found in the Hotstrings AutoHotkey documentation.

Paste the text from clipboard char by char

I'm trying to create a paste function that simulate a write "character by character" inside an input field, but my code doesn't work.
Here is my code:
^+V::
Loop, parse, clipboard, `n, `r
{
SendRaw, %clipboard%
}
return
You're trying too hard. Send command will already send key by key of whatever content you may pass into it.
; Use this in case of delaying each key press.
; SetKeyDelay, Delay
^+V::
SendRaw, %clipboard%
Return
^v::
ClipBucket5 := Clipboard
ClipBucket5 := RegExReplace(ClipBucket5, "\r\n?|\n\r?", "`n")
Loop, parse, ClipBucket5
{
SendRaw, %A_Loopfield%
Sleep, 100
}
return

Autohotkey: Toggle caps lock on/off on activating certain windows

I want to write a script that will turn on caps lock when I activate a window containing particular keyword in its title(like SQL). I also want the caps lock to be turned off when I switch to a window whose title does not contain any of the keywords that I have specified.
How can I do it? I have considered #Persistent with a timer to periodically check active window. But, I think there should be a better way.
check answers at: http://www.reddit.com/r/AutoHotkey/comments/1qjf83/force_specific_program_to_use_caps/. Especially G33kDude's answer. It's a clever and efficient solution: check of current window is binded only to windows activation.
=======================
Edit: Code inserted below.
Please note that it's not a complete solution, you'll need to make some edits for your needs. Not a big ones, though.
#Persistent ; Don't close when the auto-execute ends
SetTitleMatchMode, 2 ; Partial title matching
WinGet, myHwnd, ID, Notepad ; Get the handle to the your window
; Listen for activation messages to all windows
DllCall("CoInitialize", "uint", 0)
if (!hWinEventHook := DllCall("SetWinEventHook", "uint", 0x3, "uint", 0x3, "uint", 0, "uint", RegisterCallback("HookProc"), "uint", 0, "uint", 0, "uint", 0))
{
MsgBox, Error creating shell hook
Exitapp
}
;MsgBox, Hook made
;DllCall("UnhookWinEvent", "uint", hWinEventHook) ; Remove the message listening hook
return
; Handle the messages we hooked on to
HookProc(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime)
{
global myHwnd
static lastHwnd
WinGetTitle, title, ahk_id %hwnd%
if (hwnd == myHwnd) ; If our window was just activated
{
tooltip, Gained focus
}
else if (lastHwnd == myHwnd) ; If our window was just deactivated
{
tooltip, Lost focus
}
lastHwnd := hwnd
}
If you want to do this without using SetTimer, the best way would be to use context-sensitive hotkeys. For Example:
SetTitleMatchMode, 2
#If WinActive("SQL") or WinActive("Notepad")
a::A
b::B
c::C
d::D
e::E
;; etc.
You could also use the WinActive function with Window Groups instead of the title if you wanted to avoid a very long #If line.
EDIT: Case-Insensitive Example
SetTitleMatchMode, Regex
GroupAdd, Editor, (?i).*sql ; Regular expression for window title
GroupAdd, Editor, (?i).*ahk
#IfWinActive ahk_group Editor
a::A
b::B
c::C
d::D
e::E
;; etc.
Since you have a Autoit in your tag, here is how it is done easy in autoit.
Opt("SendCapslockMode", 0)
While 1
Sleep(200)
$title = WinGetTitle("", ""); will get the title of the active window
If StringInStr($title, "sql") Then
Send("{CAPSLOCK ON}")
Else
Send("{CAPSLOCK OFF}")
EndIf
WEnd
Milos' answer is quite straight forward, but it misses a vital point. You need to set SendCapslockMode to 0. Else the effect of the Send command will be useless, because after the command the original state will be restored.
The next thing is, you don't need to use an infinite loop with a Sleep which will execute the complete loop body every few milliseconds, but you can wait for the active window to not be active any more, which is less CPU intensive. So a fully working solution in AutoIt is:
Opt("SendCapslockMode", 0)
While True
$win = WinGetHandle("[ACTIVE]")
If StringInStr(WinGetTitle($win), "sql") Then
Send("{CAPSLOCK ON}")
Else
Send("{CAPSLOCK OFF}")
EndIf
WinWaitNotActive($win)
WEnd
#Persistent
SetTitleMatchMode, 2 ; use RegEx for finer control
Loop
{
WinWaitActive, Notepad
{
WinGet, opVar, ID
SetCapsLockState, On
}
WinWaitNotActive, ahk_id %opVar%
SetCapsLockState, Off
}
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
; This script solves the problem of forgetting to turn on or off the capslock when using specific programs
; I for one ALWAYS use capital letters when typing things in CAD. My problem was when switching to another
; program, usually e-mail, I'd start typing and then have to erase it. Problem solved!
; The beauty of this script is that you don't lose the ability to manually turn the capslock on or off
; My first few interations of this script would turn it on when in the CAD window but then
; I'd never be able to turn it on wihtout it being forced off again.
Loop {
state := GetKeyState("Capslock", "T") ; check toggle state, save in variable 'state'
if state = 0
status = Off ; this block converts the 1 or 0 of variable 'state'
if state = 1 ; into the word ON or OFF and stores it in variable 'status'
status = On
Beginsub:
WinGetTitle, Title, A ; retreive current window title, store in Title
if title contains AutoCAD 2012 ; if title contains "AutoCAD" turn on capslock
; then go back to the BeginSub subroutine and see if the title
{ SetCapsLockState, on ; still matches "AutoCAD 2012" if it does not...\/\/\/
goto, BeginSub
}
SetCapsLockState, %status% ; ...Put caps lock back to the state it was when we started.
; 'SetCapsLockState' doesn't recognize a 1/0 variable
; therefore the use of 'status' and ON/OFF words
Sleep, 1000 ; only check every second
}