Autohotkey clipboard variable holding values forever? - autohotkey

I have the below simple code, which sends keystrokes for text in the clipboard with a 15ms delay in between characters (I use this to traverse huge lists of treeview elements).
Issue: If I have copied 'text1' to clipboard, followed by 'text2', this script outputs 'text1text2' instead of 'text2' alone.
If I reload the script, then it prints 'text2'.
Is there a mistake in the below code, or is it a bug in implementing %clipboard% in Autohotkey 1.1.14.03 ?
#v::
textToType=" "
textToType=%clipboard%
LoopCount:=StrLen(textToType)
;StringLen, LoopCount, textToType
Array%LoopCount%:=textToType
loop %LoopCount%
{
theChar:=Array%A_Index%
Send %theChar%
sleep 15
}
return
Update: Thanks for pointing out smarter ways of doing this, but I would still like to figure out what is wrong in the above piece of code.
Update 2:
The mistake was in my understanding of the AHK syntax. Array%LoopCount%:=textToType assigns the whole string value in textToType to the (LoopCount)th STRING element of the STRING array named 'Array'.
Update 3:
(Thanks #John Y for clarifying)
Actually, there's no "declared" array at all, in a traditional sense. You just have a bunch of individual variables, dynamically created as needed, that happen to have names with numbers at the end. Array1 and Array2 are not elements in some Array object. They are just two completely independent variables. AutoHotkey provides a way to glue numbers onto the ends of names, so you can use them like an array.

The reason your script doesn't work properly is because you're using a pseudo-array to store different words from your clipboard.
I've commented up your code to explain what it does:
#v::
textToType := "" ; Empty variable
textToType := Clipboard ; Move clipboard into variable
; Get lenght of the word
; Used as array index / loop count
LoopCount := StrLen(textToType)
; Put the clipboard in an array at index 'LoopCount'
Array%LoopCount% := textToType
; Loop through the array as many times
; as the string is long
Loop % LoopCount
{
; Retrieve the word at this index in the array
theChar := Array%A_Index%
; Send the whole word
Send, % theChar
sleep 15
}
return
Instead of sending each character at a time, you're sending whole words from specific indexes in your Array array.
Say you copy the word Dragon, that word is 6 letters long. So you'd put that in Array6, then you'd loop through your array 6 times using the same variable. At which point the loop would take each index at a time and move it into theChar. On your 6th lap in the loop you'd put Array6 into theChar and print the whole word at once.
Then you copy the word Stackoverflow. That's going to go into Array13, and we're going to loop 13 times. On the 6th lap we're going to print out Dragon which is in Array6, and then keep going until we reach 13 where we'll print Stackoverflow since that is in Array13.
So that's why your script isn't doing what you want it to. Hopefully this helps a little.
See the code sample alpha bravo posted, that's the correct way of achieving what you want to do.

keep it simple
#v::
loop Parse, Clipboard
{
Send %A_LoopField%
sleep 15
}
return

There must be a bug in implementation of clipboard assignment in AHK. With the below code, the behaviour of AHK is that everytime the value of dir is accessed, AHK fetches the latest value from clipboard, instead of fetching the value of dir at the time the script was activated.
; Remove all CR+LF's from the clipboard contents:
dir = %clipboard%
sleep 100
dir := StrReplace(dir, "`r`n")
EDIT:
To fix this, I added 1 second sleep before clipboard assignment code:
sleep 1000
; Remove all CR+LF's from the clipboard contents:
dir = %clipboard%
dir := StrReplace(dir, "`r`n")
100 millisecond sleep didn't seem to work.
Accessing value of dir now only gives value of last clipboard assignment at activation.

Related

AHK Load hotstrings without using #Include Subroutines.ahk

I have a txt file named subroutines.ahk in it is the below hotstring.
::btw::Thank you for the help
I know if I want to run my hotstring from another script all I have to do is include it in that script using:
#include subroutines.ahk
But I dont want to do that Instead from another script I want to Loop read the contents of subroutines.ahk and load the hotstring variables that way .
Loop, read,subroutines.ahk
{
Loop, parse, A_LoopReadLine, %A_Tab%
{
MsgBox, Field number %A_Index% is %A_LoopField%.
}
}
I would really appreciate your help on how I can load my hotstring variables into another script that way.
I plan to encode the hotsrings in subroutines.ahk and than decode the contents of the subroutines.ahk in the second program and load it into memory.
In the above example though i am first trying to figure out how I can loop read the txt file and run the hot strings from there.
Demonstrates the use of hotkeys that map to dynamic values
; Replace hash values with whatever method you use to decrypt your hotstrings
decrypted := {abc: "alpha", def: "beta"}
::abc::
Send % decrypted["abc"]
return
::def::
Send % decrypted["def"]
return
When you type "abc", "alpha" appears instead
# directives are processed only once when the script is launched. To work around this limitation, you can use reload to execute dynamically generated script that includes # directives.
Code sample:
When F1 is pressed, use the contents of "subroutines.ahk" to generate and run code that utilizes # directives:
F1:: reload_hotstrings()
#Include temp.ahk
reload_hotstrings()
{
FileDelete temp.ahk
loop read, subroutines.ahk
{
MsgBox Hotstring number %A_Index% is %A_LoopReadLine%.
FileAppend %A_LoopReadLine%`n, temp.ahk
}
reload
}
Alternatively, if you want to automatically generate hotkeys code whenever autohotkey starts:
I'm using source.dat for your subroutines.ahk file
Code sample:
FileGetTime source_time, source.dat
FileGetTime compiled_time, compiled.ahk
; calculate time difference between source and compiled files
EnvSub source_time, %compiled_time%, Seconds
; compile and run if source newer
if source_time > 0
{
FileDelete compiled.ahk
loop read, source.dat
{
FileAppend %A_LoopReadLine%`n, compiled.ahk
}
reload
}
#Include compiled.ahk

How to exit a loop depending on how long I keep a key pressed?

How can I end this loop depending on how long I keep X pressed? If I hold X for five seconds it should send B, S, C, Y, and U at one second intervals.
When I hold X for one second it should only send B, if I hold X for two seconds it should send B and S, etc. Currently it simply sends B, S, C, Y, and U.
*$x::
While(GetKeyState("x", "P"))
{
send b
Sleep 1000
send s
Sleep 1000
send c
Sleep 1000
send y
Sleep 1000
send u
Sleep 1000
}
Return
Solution does not have to be a loop.
x::
index:="i"
letters:="l"
output:="o"
l:="bscyu"
i:=(i<o0?++i:1)
StringSplit,o,l
send % o%i%
sleep,1000
return
x Up::i:=0
I can see a few possible approaches.
1) The first and perhaps worst is to have an if... to test the keystate, and an exit, after every single send. This is extremely repetitive, but not too terrible if you stick it in a function, and just call that, something like:
*$x::
while (GetKeyState("x", "P")) {
sendStuff('b')
sendStuff('s')
sendStuff('c')
...
}
sendStuff(str) {
Send str
if (!GetKeyState("x", "P")) {
exit
}
sleep 1000
}
2) The next is to continue operating as you are, with a chained series of commands, and have an external, separate script kill that series of commands while running, then re-Run() your script when you wish to restart. Killing external scripts is covered in the FAQ, here: http://www.autohotkey.com/docs/FAQ.htm#close
There are three relevant sections there which are a little long to paste here, but in summary, you may be interested in the following commands:
; Allow script's hidden main window to be detected.
DetectHiddenWindows On
; Avoid need to specify full path of file.
SetTitleMatchMode 2
; Kill another script: Update params to the script's case sensitive name.
WinClose Script Filemame.ahk - AutoHotkey
; Suspend other script.
PostMessage, 0x111, 65305,,, Script Filename.ahk - AutoHotkey
; Pause other script.
PostMessage, 0x111, 65306,,, Script Filename.ahk - AutoHotkey
; Pause/resume current script on Ctrl+Alt+P
^!p::Pause
3) There's also a longer code sample just below that, which describes canceling a loop, and it's my belief that this is the correct approach to this problem, as while it might be fiddlier initially, it is considerably more scalable, reduces code duplication, and permits you to handle multiple loop entry/exit conditions in a single place.
It is, in short, the best in terms of Programming Style.
Place the entries (b, s, c, y, u) that you wish to sent into a string, array or other iterable datastructure, and iterate over it, so that your while loop sends only a single item and a pause each iteration. Then the next time you hit your hotkey, you can either resume the loop from where you left off, or start over, depending on whether you maintain a pointer to your current position in the structure.
For example, using the above sendStuff function, a loop will give you the very powerful and trivially extensible:
*$x::
{
letters = b,s,c,y,u
Loop, parse, letters, `,
sendStuff(%A_LoopField%)
return
}
This post is so old, but I just can't help it.
sendChars := "bscyu"
$x::
i := 1 ; (!i ? 1 : i) ; permits starting where left off.
While (getKeyState("x", "P")) {
Send % subStr(sendChars, i, 1)
i := ((i==strLen(sendChars)) ? 1 : (i+1))
if (getKeyState("x", "P")) { ; why wait if you don't have to.
Sleep 1000
}
}
The docs point out that a While loop is only evaluated at the beginning of each iteration, so listing all the keys in a loop will just send all the keys, then break out of loop once at that top again and sees that "x" is no longer being pressed.

Looping in filemaker using a local variable

Been programming in C# for a little bit - trying to use file maker and I cant believe it but I cant even get a simple for loop to work.
What I want to do is simple: loop through for the amount of entries in my "amountOfRooms" field and create an entry in a table for each room.
Sounds so simple, but I cant get it to work. Right now I have this:
Set Variable[$cnt[Customers::AmountOfRooms]; value:1]
Go to Layout["rooms"(Rooms)]
Loop
Exit Loop If[$cnt = Customers::AmountOfRooms]
New Record / Request
Set Variable[$cnt; Value: $cnt + 1]
End Loop
Exit Script
No new records are created. I know the script is running because it does go to my layout, but doesnt create any new records. There is a "Repetition" field for my local variable - not sure how to use that or what it means? Any thoughts on how to do this simple loop? Thanks.
Loop
Depending on your relationship, the line Exit Loop If[$cnt = Customers::AmountOfRooms] might be equal at the first iteration because you set the variable to the value three lines above it: Set Variable[$cnt[Customers::AmountOfRooms]; value:1]
There are other ways to do it, but one common technique is to have a $i variable compare against $cnt like this:
Set Variable [$cnt; Value:Customers::AmountOfRooms]
Go to Layout ["Rooms" (Rooms)]
#
Set Variable [$i; Value:1]
Loop
Exit Loop If [$i >= $cnt]
New Record/Request
Set Variable [$i; Value:$i + 1]
End Loop
Exit Script []
Repetition Number
At a high level, you can think of a FileMaker variable as a 1-indexed array. So the statements:
# Set Repetition 1 of $i to 1
Set Variable [$i; Value:1]
#
# Set Repetition 2 of $j to "Second Array Position"
Set Variable [$j[2]; Value:"Second Array Position"]
Would be equivalent to:
# Set the first value of array $i to 1
$i[0] = 1;
#
# Set the second value of array $j to "Second Array Position"
$j[1] = "Second Array Position";
in some other languages.
It's worth nothing that the array comparison is not strictly apt, but it's a good way to begin thinking of them. Regardless, you don't need to worry about the repetition number in this instance. If you want to learn more, you can start here: http://www.filemaker.com/11help/html/scripts_ref1.36.15.html

Determine if string exists in file

I have a list of strings such as:
John
John Doe
Peter Pan
in a .txt file.
I want to make a loop that checks if a certain name exists. However, I do not want it to be true if I search for "Peter" and only "Peter Pan" exists. Each line has to be a full match.
Ha ha, ep0's answer is very sophisticated!
However, you want to use a parsing loop something like this (this example expects that your names are separated by carriage returns). Consider that you have a text file with contents arranged like this:
John
Harry
Bob
Joe
Here is your script:
fileread, thistext, %whatfile% ;get the text from the file into a variable
;Now, loop through each line and see if it matches your results:
loop, parse, thistext, `r`n, `r`n
{
if(a_loopfield = "John")
msgbox, Hey! It's John!
else
msgbox, No, it's %a_loopfield%
}
If your names are arranged in a different order, you might have to either change the delimiter for the parsing loop, or use regex instead of just a simple comparison.
If you want to check for multiple names use a trie. If you have just one name, you can use KMP.
I'll explain this for multiple names you want to check that exist, since for only one, the example provided on Wikipedia is more than sufficient and you can apply the same idea.
Construct the said trie from your names you want to find, and for each line in file, traverse the trie character by character until you hit a final node.
BONUS: trie is used by Aho-Corasick algorithm, which is an extension of KMP to multiple patters. Read about it. It's very worthwhile.
UPDATE:
For checking if a single name exists, hash the name you want to find, then read the text file line by line. For each line, hash it with the same function and compare it to the one you want to find. If they are equal, compare the strings character by character. You need to do this to avoid false positives (see hash collisions)

Displaying List of AutoHotkey Hotkeys

I’ve written script that contains numerous hotkeys (general structure is as below). I would like to create another one that when pressed displays a list of all of the hotkeys and their corresponding descriptions that the script contains in a nice, formatted table.
The formatting and display are tenuous since AutoHotkey’s output is limited to message-boxes, but possible. More problematic is getting the hotkeys and corresponding descriptions.
The hotkeys all call the same function with different arguments. I considered adding a variable to the function so that depending on the value, the function either performs the normal function when triggered by the normal hotkeys, or builds a string or something when triggered from the special display hotkey.
I cannot figure out a way to programmatically access the script’s hotkeys at all. I checked the docs and there don’t seem to be any A_ variables that can be used for this purpose, nor does the Hotkey command lend itself well (it can be used to test if a hotkey exists, but looping through the innumerable combinations is, at best, tedious).
Failed attempts:
I tried using Elliot’s suggestion of parsing the script itself (replacing the path with %A_ScriptFullPath%, and while it does work for a raw script, it does not when the script is compiled
I tried assigning the entire hotkey section of the script to a variable as a continuation section and then parsing the variable and creating hotkeys using the Hotkey command. This worked well right up until the last part because the Hotkey command cannot take arbitrary commands as the destination and requires existing labels.
The ListHotkeys command is not applicable because it only displays the hotkeys as plain text in the control window.
Does anyone know how I can display a list of the hotkeys and either their corresponding arguments or comments?
Example script:
SomeFunc(foobar)
{
MsgBox %foobar%
}
!^#A::SomeFunc("a") ; blah
^+NumpadMult::SomeFunc("c") ; blivet
^+!#`::SomeFunc("b") ; baz
^#Space::SomeFunc("d") ; ermahgerd
…
Example desired “outputs”:
C+A+ W+ A a | C+ S+ NumpadMult b
------------------+----------------------
C+A+S+W+ ` c | C+ W+ Space d
    or
Ctrl Alt Shift Win Key Action
-----------------------------------------
× × × A blah
× × NumpadMult baz
× × × × ` blivet
× × Space ermahgerd
etc.
The only thing I can think of is to read each line of your script individually and parse it. This code reads your script (script.ahk) one line at a time and parses it. This should get you started. Additionally, you could parse the line to check for the modifiers as well.
Loop
{
FileReadLine, line, C:\script.ahk, %A_Index%
if ErrorLevel
break
If Instr(line, "::")
{
StringSplit, linearray, line, ::,
key := linearray1
StringSplit, commandarray, linearray3, `;
action := commandarray2
hotkeyline := "key: " . key . "`tAction: " . action
final .= hotkeyline . "`r"
}
}
msgbox % final
return
I found a solution. It is not perfect (or ideal), and hopefully a proper, built-in method will become available in the future, but it works well (enough) and for raw and compiled scripts.
What I did was to use the FileInstall command which tells the compiler to add a file to the executable (and extract it when run).
Sadly, the FileInstall command will not allow the use of variables for the source file, so I cannot simply include the script itself (FileInstall, %A_ScriptFullPath%, %A_Temp%\%A_ScriptName%, 1).
As a work-around, I ended up extracting all of the desired hotkeys to a second file which I then parse as Elliot suggested, then delete, and #Include at the end of my script (it must be at the end since hotkeys will terminate the autoexecute section).
;;;;; Test.ahk ;;;;;
; Add hotkey file to executable and extract to Temp directory at runtime
FileInstall, Hotkeys.ahk, %A_Temp%\Hotkeys.ahk, 1
Loop
{
;Read a line from the extracted hotkey script and quit if error
FileReadLine, line, %A_Temp%\Hotkeys.ahk, %A_Index%
if ErrorLevel
break
;Trim whitespace
line=%line%
; Parse the line as in Elliot’s answer, but with tweaks as necessary
ParseHotkey(line)
…
}
FileDelete, %A_Temp%\Hotkeys.ahk ; Delete the extracted script
DisplayHotkeys() ; I ended up bulding and using a GUI instead
#Include, Hotkeys.ahk ; It is included at compile-time, so no A_Temp
;;;;; Hotkeys.ahk ;;;;;
z::MsgBox foo
y::MsgBox bar