How to achieve visual-studio-like chained hotkeys using AHK? - autohotkey

I'm trying to achieve a visual-studio-like chaining of hotkeys for something at work.
Basically, when I hit Ctrl+Alt+F, I want to enter a sort of "Format mode" The next key I press will determine what text is injected. I'd like "Format mode" to cease as soon as one of the inner hotkeys is pressed. However I'd also like the option to have to cancel it manually just in case.
I've tried searching for the chaining of hotkeys, as well as attempted the following, rather naive code:
;
;; Format Mode
;
^+f::
; Bold
b::
Send "<b></b>"
Send {Left 4}
return
; Italics
i::
Send "<i></i>"
Send {Left 4}
return
; Bulleted List
u::
Send "<u></u>"
Send {Left 4}
return
; Numbered List
o::
Send "<o></o>"
Send {Left 4}
return
; List Item
l::
Send "<li></li>"
Send {Left 4}
return
; Line Break
r::
Send "<br/>"
return
return
I was pretty sure this wasn't going to work, but I figured I'd give it a shot so as to not make y'all think I'm just asking to be spoon fed.
I haven't worked with AHK a whole lot, but I've used it enough to be able to accomplish some stuff both at home and at work, but it's suuuuper messy - just as some background as to my experience with AHK.

With the #if command you are able to Switch/Toggle between action1/Enable and action2/Disable with the same Hotkeys.
You can test it out in Notepad. if you then type the key t
type Ctrl+Shift+f keys to toggle between two the same hotkeys.
Note: For your Hotkeys You Can Change the Code a Little Bit! you can Place all your hotkeys into #if Mode1 and then use no hotkeys into #if Mode2
Example1.ahk
; [+ = Shift] [! = Alt] [^ = Ctrl] [# = Win]
#SingleInstance force
a := 1
#If mode1 ; All hotkeys below this line will only work if mode1 is TRUE or 1
t::
send 1
; Here you can put your first hotkey code
return
; Here you can Place All your Hotkeys
#If
#If mode2 ; All hotkeys below this line will only work if mode2 is TRUE or 1
t::
send 2
; And here you can put your second hotkey code
return
#If
; toggle between [t::1] and [t::2]
;a = 1 => t::1
;a = 2 => t::2
;type Ctrl+Shift+f keys to toggle between two the same hotkeys
;you can test it out in notepad - if you then type the key t
^+f::
if (a=1)
{
mode1 = 1
mode2 = 0
a := 2
}else{
mode1 = 0
mode2 = 1
a := 1
}
return
esc::exitapp

Using #If is a good choice, as stevecody showed. Below is another solution that may work for you. It uses your ^+f hotkey to activate the specialty hotkeys. The specialty hotkeys will also deactivate themselves upon use. f1 is your option to cancel it manually.
^+f::
Hotkey , b , l_Bold , On
Hotkey , i , l_Italics , On
Hotkey , l , l_ListItem , On
Return
f1::
Hotkey , b , l_Bold , Off
Hotkey , i , l_Italics , Off
Hotkey , l , l_ListItem , Off
Return
l_Bold:
Hotkey , b , l_Bold , Off
Send , <b></b>{left 4}
Return
l_Italics:
Hotkey , i , l_Italics , Off
Send , <i></i>{left 4}
Return
l_Italics:
Hotkey , l , l_ListItem , Off
Send , <li></li>{left 5}
Return
Something else I was looking at, but it doesn't quite work right, is below. The problem is that it will still send the specialty key and you end up with something like <i>i</i> instead of <i></i>.
f1::
KeyBdHook := DllCall( "SetWindowsHookEx" , "int" , 13 , "uint" , RegisterCallback( "KeyBdProc" ) , "uint" , 0 , "uint" , 0 )
input
Return
KeyBdProc( nCode , wParam , lParam )
{
global KeyBdHook
Critical
If ( wParam = 0x100 )
{
DllCall( "UnhookWindowsHookEx" , "ptr" , KeyBdHook )
sKey := GetKeyName( Format( "vk{:x}" , NumGet( lParam + 0 , 0 , "int" ) ) )
If ( sKey = "i" )
Send , <i></i>{left 4}
Else
MsgBox , You pressed %sKey%
}
}

Related

How to toggle an AHK script on/off with a key?

I'm working on a Script that constrains the movement of the cursor to the horizontal direction only. I would like to activate and deactivate it using the same hotkey.
I'm using this code:
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
!s:: ; Hotkey will toggle status
Confine := !Confine
MouseGetPos, , SetY
ClipCursor( Confine, 0, SetY, A_ScreenWidth, SetY+1 )
return
!a::
Pause
Suspend
return
ClipCursor( Confine=True, x1=0 , y1=0, x2=1, y2=1 ) {
VarSetCapacity(R,16,0), NumPut(x1,&R+0),NumPut(y1,&R+4),NumPut(x2,&R+8),NumPut(y2,&R+12)
Return Confine ? DllCall( "ClipCursor", UInt,&R ) : DllCall( "ClipCursor" )
}
The code works, but when pressing ctrl + a the script doesn't stop.
Am I using incorrectly the pause and suspend commands? How could this task be done?
That's a neat function! I can definitely see some use for that. Anyway, you are correctly using Pause and Suspend, but it appears that !s was meant to toggle it on and off (so, no need for !a).
For some reason, though, it won't toggle off. In my testing, the function was correctly seeing the value of "Confine", but wasn't returning the false-portion of the ternary operation. It appears to be coded properly, but I suspect there may be an issue (possible bug?) with Return evaluating "Confine" properly.
Here are a few solutions:
By testing explicitly whether "Confine" is equal to True works.
Return ( Confine = True ) ? DllCall( "ClipCursor" , UInt , &R ) : DllCall( "ClipCursor" )
What I would do, however, is take the ternary operation out of the function and move it to your hotkey to avoid needless operations and assignments if it evaluates to false. To me, this is a bit cleaner.
!s:: ; Hotkey will toggle status
Confine := !Confine
MouseGetPos ,, SetY
Confine ? ClipCursor( 0 , SetY , A_ScreenWidth , SetY+1 ) : DllCall( "ClipCursor" )
Return
ClipCursor( x1=0 , y1=0 , x2=1 , y2=1 ) {
VarSetCapacity( R , 16 , 0 )
NumPut( x1 , &R + 0 )
NumPut( y1 , &R +4 )
NumPut( x2 , &R +8 )
NumPut( y2 , &R +12 )
Return DllCall( "ClipCursor" , UInt , &R )
}
If you just want to use !a to turn it off, you could just do this, !a::DllCall( "ClipCursor" ). If you decide to go this route, I would recommend removing all of the toggle portions of code from the hotkey and function.

How do you include a loop with modifiers?

I'm trying to get AHK to continue to press "2" until "2" is pressed a second time. If alt, ctrl, or shift is held it sends ^2, +2, !2 while held and then returns to spamming "2" once the modifier key is released.
This code works so far with modifiers I just need to figure out how to add the loop.
; Disable Alt+Tab
!Tab::Return
; Disable Windows Key + Tab
#Tab::Return
#ifWinActive World of Warcraft
{
$2::
$^2::
$+2::
$!2::
Loop
{
if not GetKeyState("2", "P")
break
if GetKeyState("LCtrl", "P")
Send ^2
else if GetKeyState("LShift", "P")
Send +2
else if GetKeyState("LAlt", "P")
Send !2
else
Send 2
sleep 135
}
return
}
I would recommend using SetTimer for your loop and to be able to toggle it on and off. Please see if the following works for you:
$2::
$<^2::
$<+2::
$<!2::
SetTimer , label_TwoLoop , % ( bT := !bT ) ? "135" : "Off"
Return
label_TwoLoop:
If GetKeyState( "LCtrl" , "P" )
Send , ^2
Else If GetKeyState( "LShift" , "P" )
Send , +2
Else If GetKeyState( "LAlt" , "P" )
Send , !2
Else
Send , 2
Return
https://autohotkey.com/docs/commands/SetTimer.htm
Note that I added the < to the hotkey definitions since the loop-portion is only looking for the left modifier keys. I figured this is the intended behavior.

ControlSend not sending to the non-topmost window

Is it possible to make ControlSend send / click to a background window?
This code finds a window by id / handler, but sends a click / keystroke only if the window is the topmost. ControlSend skips its action if the window is not topmost. ControlClick force brings window to top and then clicks.
#Singleinstance
DetectHiddenWindows, On
#SingleInstance Force
#MaxThreadsPerHotkey, 2
SendMode Input ; Recommended for new scripts
SetControlDelay -1
PostClick(x,y,win="A") {
lParam := x & 0xFFFF | (y & 0xFFFF) << 16
PostMessage, 0x201, , %lParam%, , %win% ;WM_LBUTTONDOWN
PostMessage, 0x202, , %lParam%, , %win% ;WM_LBUTTONUP
}
RunAsAdmin() {
Loop, %0% {
param := %A_Index% ; Fetch the contents of the variable whose name is contained in A_Index.
params .= A_Space . param
}
ShellExecute := A_IsUnicode ? "shell32\ShellExecute":"shell32\ShellExecuteA"
if not A_IsAdmin
{
If A_IsCompiled
DllCall(ShellExecute, uint, 0, str, "RunAs", str, A_ScriptFullPath, str, params , str, A_WorkingDir, int, 1)
Else
DllCall(ShellExecute, uint, 0, str, "RunAs", str, A_AhkPath, str, """" . A_ScriptFullPath . """" . A_Space . params, str, A_WorkingDir, int, 1)
ExitApp
}
}
RunAsAdmin()
F1::
Loop
{
; http://particle-clicker.web.cern.ch/particle-clicker/
WinGet, WinID, ID, Particle Clicker - Google Chrome
; Send keystrokes only if tab active and topmost, suspend if focus lost, continue when gain focue
; ControlSend, ahk_parent, {Space}, ahk_id %WinID%
; send clicks only if tab not mininised, if tab on background - force bring it to front
; ControlClick, x799 y449, ahk_id %WinID%
; same as above
; ControlClick, x799 y449, ahk_id %WinID%,,,,NA
; same as above
; Controlclick x799 y449,ahk_id %WinID%,,Left,1,NA
; same as above
PostClick(799,449,"Particle Clicker - Google Chrome")
sleep 1000
}
return
F12::ExitApp

Hotkey to toggle another hotkey into a different function or mode

I'm trying to get a the hotkey LCTRL to toggle XButton2 into different modes.
The issue is that it's saying that there are duplicate hotkeys in the same script. I clearly don't know what's going on. Help?
~LCtrl::
actioncameratoggle := !actioncameratoggle
if actioncameratoggle
{
~XButton2::
rTurn := !rTurn
if rTurn
{
send {lctrl}
mousemove, 800, 450
send {ctrl}
mousemove, 1600, 450
}
else
{
}
}
else
{
~XButton2::
{
}
}
return
Thanks!
With your Script You can not toggle with the same hotkeys, the reason is you want to use the if with the Else commands > if hotkey1:: else hotkey1::, you will need the #if commands. Look to this example code.
You can Write this example.ahk Code and try it out on your Windows System.
Change the code a little bit for your needs, and it is done.
I did make the script so that the other guys can simple test it out.
; [+ = Shift] [! = Alt] [^ = Ctrl] [# = Win]
a := 1
#If mode1 ; All hotkeys below this line will only work if mode1 is TRUE or 1
t::1
; Here you can put your first hotkey code ~XButton2::
#If
#If mode2 ; All hotkeys below this line will only work if mode2 is TRUE or 1
t::2
; And here you can put your second hotkey code ~XButton2::
#If
; toggle between [t::1] and [t::2]
;a = 1 => t::1
;a = 2 => t::2
;type LCtrl key to toggle between two the same hotkeys
;you can test it out in notepad - if you then type the key t
~LCtrl::
if (a=1)
{
mode1 = 1
mode2 = 0
a := 2
}else{
mode1 = 0
mode2 = 1
a := 1
}

AHK: remap numeric keypad with Numlock On so it behaves like numeric keypad with Numlock Off

I'd like to remap the numeric keypad with Numlock On to behave like the numeric keypad with Numlock Off, including being able to extend the selection with Shift/Ctrl held down.
The problem I have is the following
Numpad8::Up
Doesn't have correct behaviour when shift+numpad8 is pressed, the cursor moves up, but no text is selected. The following also don't work as I'd like (same behaviour as Numpad::8).
+Numpad8::Up
+Numpad8::+Up
If I remap a normal key, the selection behaviour is correct when shift is pressed:
w::Up
Any hints?
The reason I'm doing this is to make a CoolerMaster QuickFire TK's numeric keypad behave like it has a standard numeric keypad layout (I've some registry keyboard remapping happening as well, which is why I want the navigation behaviour with Numlock On).
It's possible, but it's a pain in the ass... compared to normal remapping.
Here is the normal behaviour of the Numpad8-key:
With numpad on:
num8: up
shift+num8: mark up
With numpad off:
num8: 8
shift+num8:up
So if you want to reverse that, what we want is this:
With numpad on:
num8: 8
shift+num8:up
With numpad off:
num8: up
shift+num8: mark up
This is how it could be achived:
*NumpadUp::
If GetKeyState("NumLock", "T")
SendInput, {Shift Down}{Up}
Else If GetKeyState("Shift")
SendInput, {Shift Up}{NumpadUp}
Else
SendInput, {Shift Up}{Numpad8}
Return
*Numpad8::
If GetKeyState("Shift")
SendInput, {Shift Down}{NumpadUp}
Else
SendInput, {NumpadUp}
Return
Now you just need to do the same thing for the other numpad keys that you want to reverse.
Here is a different approach that uses a little hack to make it look like the Numpad toggles when pressing the numlock key. But it ensures that the numlock actually is always off and only the numlock light is changed.
SetNumLockState, Off
fakeNumlockOn := False
Return
NumLock::
SetNumLockState, Off
fakeNumlockOn := !fakeNumlockOn
SetNumLockLEDs(fakeNumlockOn ? "on" : "off")
Sleep, 100
SetNumLockLEDs(fakeNumlockOn ? "on" : "off")
Return
SetNumLockLEDs(state) {
Loop, 11
KeyboardLED(2,state,A_Index-1)
}
/*
Keyboard LED control for AutoHotkey_L
http://www.autohotkey.com/forum/viewtopic.php?p=468000#468000
KeyboardLED(LEDvalue, "Cmd", Kbd)
LEDvalue - ScrollLock=1, NumLock=2, CapsLock=4
Cmd - on/off/switch
Kbd - index of keyboard (probably 0 or 2)
*/
KeyboardLED(LEDvalue, Cmd, Kbd=0) {
SetUnicodeStr(fn,"\Device\KeyBoardClass" Kbd)
h_device := NtCreateFile(fn,0+0x00000100+0x00000080+0x00100000,1,1,0x00000040+0x00000020,0)
If (Cmd = "switch") ;switches every LED according to LEDvalue
KeyLED:= LEDvalue
If (Cmd = "on") ;forces all choosen LED's to ON (LEDvalue= 0 ->LED's according to keystate)
KeyLED:= LEDvalue | (GetKeyState("ScrollLock", "T") + 2*GetKeyState("NumLock", "T") + 4*GetKeyState("CapsLock", "T"))
If (Cmd = "off") { ;forces all choosen LED's to OFF (LEDvalue= 0 ->LED's according to keystate)
LEDvalue := LEDvalue ^ 7
KeyLED := LEDvalue & (GetKeyState("ScrollLock", "T") + 2*GetKeyState("NumLock", "T") + 4*GetKeyState("CapsLock", "T"))
}
success := DllCall( "DeviceIoControl" , "ptr", h_device , "uint", CTL_CODE( 0x0000000b , 2 , 0 , 0 ) , "int*", KeyLED << 16 , "uint", 4 , "ptr", 0 , "uint", 0 , "ptr*", output_actual , "ptr", 0 )
NtCloseFile(h_device)
return success
}
CTL_CODE( p_device_type, p_function, p_method, p_access ) {
return, ( p_device_type << 16 ) | ( p_access << 14 ) | ( p_function << 2 ) | p_method
}
NtCreateFile(ByRef wfilename,desiredaccess,sharemode,createdist,flags,fattribs) {
VarSetCapacity(objattrib,6*A_PtrSize,0)
VarSetCapacity(io,2*A_PtrSize,0)
VarSetCapacity(pus,2*A_PtrSize)
DllCall("ntdll\RtlInitUnicodeString","ptr",&pus,"ptr",&wfilename)
NumPut(6*A_PtrSize,objattrib,0)
NumPut(&pus,objattrib,2*A_PtrSize)
status:=DllCall("ntdll\ZwCreateFile","ptr*",fh,"UInt",desiredaccess,"ptr",&objattrib ,"ptr",&io,"ptr",0,"UInt",fattribs,"UInt",sharemode,"UInt",createdist ,"UInt",flags,"ptr",0,"UInt",0, "UInt")
return % fh
}
NtCloseFile(handle) {
return DllCall("ntdll\ZwClose","ptr",handle)
}
SetUnicodeStr(ByRef out, str_) {
VarSetCapacity(out,2*StrPut(str_,"utf-16"))
StrPut(str_,&out,"utf-16")
}