Current known limitations is in "Space bar save." The time it takes to actually send the "Space" the script can be done with it before the action of pressing space. Sleep times are included but I'm not 100% sure it is 100% reliable (though on my 50 uses of it it worked quite well actually). If you notice after pressing space in the INI file "MasterControl" "Key1"it is still "Working" you may need to adjust sleep time (some people like using message boxes for data and work flow. I have found that INI write gets a lot of it without messing up the flow of the program). Other Limitations Include placing text after a word (i.e. "a possible solution|") where you attempt to continue after solution without a space. I also believe I have stressed the limitations of #persistent loops and will simply need to move to a main loop function. The difference in Persistent and a main loop will be a better use of variables... So IniWrite (though a good tool for workflow and data checking) is not as good as process data manipulation using variables that change in process (Like static variables). I'm not sure if a workaround can be made for the aforementioned problem (typing directly after text with no space) but I will try.
Code: Select all
#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.
#SingleInstance force ; this prevents the script from popping up the dialogue box warning if you start the program without shutting it down.
DetectHiddenWindows, On ; unnecessary but left just in case of future changes.
Global Cache:=% A_WorkingDir "\Cache"
if !FileExist(Cache)
FileCreateDir, % Cache ; a working Cache designed for the dictionary, descriptions, words.
CoordMode, Caret, Screen
CoordMode, Mouse, Screen
Gui, AI:New, +AlwaysOnTop +Resize +toolWindow ; naming the AI window and it's tools.. This may be later changed to suite setting and so on.
Gui, Add, Edit, r2 ym x100 vWords w200 hwndTracker, this is a long string of words to use. It is to help in getting a large amount of words to test in the possibility of edit and editing controls. ; added a window handle "tracker" for future work. I believe it might be possible to work inside the text instead of the end of line..
Gui, Margin, 50, 20 ; just a border provided to the edit box
Gui, Show, , AI work ; renaming the window and showing it.
Gui, Words:New, +AlwaysOnTop -SysMenu -Caption +toolWindow ; adding a new window I might add a window handle later.
Gui, Add, Listbox, r6 gWord vWord x0 y0, ; a sub-label and subroutine for the listbox.
Gui, margin, 0, 0; the only thing visible will be the listbox.
Gui, Show, , Words ; allowing the Words window to be seen.
WinSet, Transparent, 0, Words ; instead of winhide I decided to go with transparent. I found that winshow activates the window which affects the flow of keyboard input.
WinActivate, AI ; initiating AI window so it's active instead of having to activate it.
GuiControlGet, InFocus, AI:Focus ;<------- This text is primarily for start quality
GuiControlGet, text, AI:, % InFocus ;<------- This text is primarily for start quality
IniFixer(InFocus, "0", "Start", "Start") ;<------- This text is primarily for start quality
IniWrite, Start, Recorder.ini, MasterControl, Key1 ;<------- This text is primarily for start quality
FileMove, % Cache "\Temp.txt", % Cache "\Tem.txt", 1 ;<------- This text is primarily for start quality
FileAppend, % text, % Cache "\temp.txt", UTF-8
#Persistent
SetTimer, Tick, on ; Timer set for word checking in editor versus what's in the dictionary. It is a permanent loop recalling itself with no stop.
return
Tick()
{
IfWinNotActive, AI
return
IfWinActive, AI
{
GuiControlGet, InFocus, AI:Focus ; This collects the infocus location of window AI Box (i.e. edit1 edit2 etc...)
GuiControlGet, text, AI:, % InFocus
IniRead, n, Recorder.ini, MasterControl, Key1 ; Collecting text of AI window in Edit1
if (n == "ERROR") ; In the first time reading of an ini value if it is blank it comes back with "ERROR" as it's code
{
IniWrite, Start, Recorder.ini, MasterControl, Key1 ; Setting keys to have a value
FileAppend, % text, % Cache "\temp.txt", UTF-8
}
FileRead, contents, % Cache "\Temp.txt" ; A temp cache location was created to store and read for changes.
IniRead, n, Recorder.ini, MasterControl, Key1 ; Using a variable to initiate find differences.
if (contents == text)
{
if ((n == "Working")||(n == "Done")) ; reset any values not reset already.
{
IniFixer(InFocus, "0", "Start", "Start")
IniWrite, Start, Recorder.ini, MasterControl, Key1
IniWrite, 0, Recorder.ini, MasterControl, Key2 ; y is stored for later use.
IniWrite, "", Recorder.ini, MasterControl, Key3 ; no text to collect as its at the end of the line.
}
}
else if (contents != text) ; using the contents of the cache helps to eliminate the need to process to far in the loop.
{
if (n == "Start")
{
IniFixer(InFocus, "0", "Start", "Start")
IniWrite, Working, Recorder.ini, MasterControl, Key1 ; Ini changed value from start to working.
wordarray:=[] ; Creating an array to check values against.
Loop, Parse, % contents, % A_Space ; parsing contents of "Temp" file
{
x++
wordarray["ValuePos" x] := A_LoopField ; Creating an associative array for exact location and word.
}
x:=0 ; resetting x for later use.
Loop, Parse, % text, % A_Space ; parsing the "Edit" box to check against the array values.
{
y++
nstring:= wordarray["ValuePos" y] ; y is the delimiter for the line. By using the y value I count 1 at a time and get the value.
if (nstring == "") ; if nstring from the array is blank a new word at the end of the line is being created.
{
IniWrite, % y, Recorder.ini, MasterControl, Key2 ; y is stored for later use.
IniWrite, "", Recorder.ini, MasterControl, Key3 ; no text to collect as its at the end of the line.
Break ; ends the loop so no other values are retained.
}
if (nstring != A_LoopField) ; this is where inside work is being done by the user on words in the "Edit" box
{
IfInString, A_LoopField, %nstring% ; This is an additional word being added to the already going text.
{
IniWrite, % y, Recorder.ini, MasterControl, Key2 ; recording both the y and nstring value for later use.
IniWrite, % nstring, Recorder.ini, MasterControl, Key3
Break
}
IfNotInString, A_LoopField, %nstring% ; this would be a deleted or modified word the user has changed or removed.
{
IniWrite, % y, Recorder.ini, MasterControl, Key2 ; storing the y variable and the LoopField (word that's been modified or deleted)
IniWrite, % A_LoopField, Recorder.ini, MasterControl, Key3
Break
}
}
}
}
IniRead, v, Recorder.ini, MasterControl, Key2 ; Collecting the information from the above recieved information
IniRead, nstring, Recorder.ini, MasterControl, Key3
Loop, Parse, % text, % A_Space ; parsing the "Edit" box text
{
x++
if (x == v) ; if x == v (or the y value where it was previously found) the LoopField is saved in a var.
text:=% A_LoopField
}
text:= RegExReplace(text, nstring "$", "") ; The orginal text being worked on is necessary. So RegEx the nstring from the obtained original reslut (which might be "", or a word)
str:= StrLen(text) ; I might use this later to create a aesthetic value of "selecting (or highlighting) the text as it's being updated.
var2:= SubStr(text, 1, str) ; redundante from text. (basically text == var2). But var is quick to copy and paste.
if (InFocus == "Edit1") ; in the event of more "Edit" boxes added one day.
{
IniRead, val, Recorder.ini, Edit1, Key1 ; gaining the values to check against for only doing what's necessary (helps to speed up the process by not doing redundant processes).
IniRead, strc, Recorder.ini, Edit1, Key2
IniRead, window, Recorder.ini, Edit1, Key3
if (val == "ERROR") ; In the first time reading of an ini value if it is blank it comes back with "ERROR" as it's code
{
IniFixer(InFocus, str, "Done", "Start")
val:=% "Done"
strc:=% str
window:=% "Start"
}
}
if (val == "Done") ; The value of done is first and formost to set window "words" to transparent.
WinSet, Transparent, 0, Words
if (str < 2) ; this is second to look for a less then statement to set the IniFixer values for restart.
IniFixer(InFocus, str, "Start", "Start")
if (val != "Done") ; if The value isn't equal to done then it passes this information. (1 minor issue with this is "Done" may already be existent When ini wrote "Start" But, that really shouldn't be a problem since I delimit by 4 or greater)
{
if (str != strc) ; if a change in string length occurs
{
WinGet, visible, Transparent, Words ; a variable to "see" if the window is transparent or not.
if (visible != 0) ; if the window is visible (not equal to 0)
{
IniRead, n, Recorder.ini, MasterControl, Key1
if (n == "Working")
IniWrite, Start, Recorder.ini, MasterControl, Key1
}
IniFixer(InFocus, str, "Start", window) ; IniWrite values.
if (str > 3) ; if string length is greater than 3
{
if (window != "Done") ; if window isn't done yet
{
IniWrite, Done, Recorder.ini, % InFocus, Key3 ; IniWrite Done.
window:=% "Done" ; Set Window to done just in case.
Y:=% A_CaretY+11 ; Set caret value below insert.
WinMove, Words, , %A_CaretX%, %Y% ; move Window "words" to caret location.
Winset, AlwaysOnTop, Off, AI ; turn off always on top on Window "AI"
WinSet, Transparent, Off, Words ; Turn of transparency of Window "words" I have found winshow affects typing input.
WinActivate, AI ; activate Window "AI" just in case.
}
descriptions:=DesGet() ; get list of words, dictionary, or whatever (dictionary is not included so you will have to either created it or download)
words:=MyFixed(descriptions) ; removing "Special characters, and adding pipelines"
Sort, words, U D| ; sorting by "|" pipelines for duplicates.
Loop, Parse, % words, | ; parsing by pipelines
{
var1:= SubStr(A_LoopField, 1, str) ; creating a substring that is the same length as var2
if (var1 == var2) ; checking for absolute equality.
{
z++
IfNotInString, List, % A_LoopField ; Sometimes "it seems" duplicates still will exist maybe a loop parse error (IDK but I add this to ensure only 1 result {Might be residual spaces that are existent after MyFixed method})
{
if (z == 1) ; The first occurance gets a double pipeline.
List:=% List A_LoopField "||"
else if (z != 1) ; all other occurances get a single pipeline
List:=% List A_LoopField "|"
}
}
}
if (List != "") ; Sometimes the list can be empty.
{
GuiControl, Words:, Listbox1, | ; Clears the list So new list can be updated.
GuiControl, Words:, Listbox1, % List ; sets the new list
}
if (List == "") ; Sometimes the list can be empty. No word found or the word is just mispelled.
{
IniFixer(InFocus, str, "Done", window) ; Changing the settings of IniFixer
WinSet, Transparent, 0, Words ; Makes the Window "words" transparent.
}
}
else if (str < 4) ; if the string is less than 4 the window is transparent.
WinSet, Transparent, 0, Words
}
}
}
}
List:= "" ; Clear list so residual data is removed.
return
}
~Backspace:: ; Backspace is now a hotkey the "~" tilde is to allow Backspace to act normally while doing something.
IfWinActive, AI ; Checks if window "AI" is active and changes IniFixer as needed. (The user is modifying a word.)
{
GuiControlGet, InFocus, AI:Focus
GuiControlGet, text, AI:, % InFocus
IniFixer(InFocus, "0", "Start", "Start")
IniWrite, Start, Recorder.ini, MasterControl, Key1
}
return
~Space:: ; On Space the Change is updated to the "Temp" file to reflect user changes.
IfWinActive, AI ; Checks if window "AI" is active and changes IniFixer as needed. (The user is modifying a word.)
{
sleep, 150 ; <-- the sent Space is actually slower than the process below. Which places the "MasterControl" "Key1" in "Working" I placed it to 150 because 50 ms was still too slow sometimes.
GuiControlGet, InFocus, AI:Focus
GuiControlGet, text, AI:, % InFocus
IniFixer(InFocus, "0", "Start", "Start")
IniWrite, Start, Recorder.ini, MasterControl, Key1
FileMove, % Cache "\Temp.txt", % Cache "\Tem.txt", 1
FileAppend, % text, % Cache "\temp.txt", UTF-8
WinSet, Transparent, 0, Words ; puting words in transparent.
}
return
$Tab:: ; placing a $ operator helps to allow the `t character to be sent (via send) without activating the Hotkey.
Word: ; g-sub-label is added
WinGet, visible, Transparent, Words ; a variable to "see" if the window is transparent or not.
if (visible != 0) ; if the window is visible (not equal to 0)
{
IniRead, v, Recorder.ini, MasterControl, Key2 ; getting y variable from the Tick loop
IniRead, nstring, Recorder.ini, MasterControl, Key3 ; getting the nstring variable from the Tick loop.
WinActivate, AI ; activates AI but might not need to do that actually as it probably already is active.
GuiControlGet, InFocus, AI:Focus ; Gains the name of the AI window Edit box.
GuiControlGet, text, AI:, % InFocus ; gets the text of the AI edit box text.
MsgBox, % v "`n" nstring
GuiControlGet, Word, Words:, Listbox1 ; gets the selected "||" word of the listbox from words.
y:=0 ; reset y for next use
Loop, Parse, % text, % A_Space ; parses the text of AI window to get the last word being worked on.
{
y++
if (y == v) ; if y == v the loop location is equal to the place user is editing.
{
MsgBox, % A_LoopField "`n" v "`n" y
wordvar:=% A_LoopField ; setting wordvar to equal the LoopField
}
}
NS:= RegExReplace(wordvar, wordvar, Word) ; replace wordvar with Word
IniWrite, % NS ", " wordvar, Recorder.ini, checker, key1
save:=% Clipboard ; saving Clipboard information (incase user has the Clipboard with something in it) I also know I could use ClipSaved.
Clipboard:= ""
str:= StrLen(wordvar) ; Getting string length of wordvar
str2:= StrLen(nstring) ; Getting string length of nstring
str:=(str-str2) ; subracting str from str2
if (str == 0) ; from time to time it is understandble the user pre-places the space to correct. in the event of that str == 0
str:=% str2 ; from there we'll just set str to str2 to avoid a non-deleted edit that was supposed to be replaced.
IniWrite, % str ", " str2 ", " nstring, Recorder.ini, checker, key3
Winset, AlwaysOnTop, On, AI ; setting window "AI" on top.
Clipboard:=% NS ; Puting NS RegEx into Clipboard
IniWrite, % Clipboard, Recorder.ini, checker, key2
ControlSend, % InFocus, +{Left %str%}^v, AI ; selecting only the characters the user is working on and pasting over them.
WinSet, Transparent, 0, Words ; puting words in transparent.
GuiControlGet, text, AI:, % InFocus ; getting the updated information.
FileMove, % Cache "\Temp.txt", % Cache "\Tem.txt", 1 ; moving Temp file to new Tem file. (I prefer this over delete).
FileAppend, % text, % Cache "\Temp.txt", UTF-8 ; Appending text to new file Temp.
Clipboard:=% save ; putting back the Clipboard to it's orginal state.
list:= ; the variable list is empty.
save:= "" ; the variable save is empty.
IniWrite, Start, Recorder.ini, MasterControl, Key1 ; updating the changes to Ini.
IniWrite, 0, Recorder.ini, MasterControl, Key2 ; y is stored for later use.
IniWrite, "", Recorder.ini, MasterControl, Key3 ; no text to collect as its at the end of the line.
IniFixer(InFocus, "0", "Done", "Start") ; Fixing IniFixer
wordvar:= "" ; the variable wordvar is now empty
return
}
IfWinActive, AI ; In the event the window is invisible and you want to sent tab to the edit box this will send it to the last spot in the text box.
{
GuiControlGet, InFocus, AI:Focus
GuiControlGet, text, AI:, % InFocus
Clipboard:=% text "`t"
ControlSend, % InFocus, ^a^v, AI
}
else ; in the event you are working anywhere else it will send tab anywhere else with no issues. (the great use of $ without setting off the Hotkey).
Send, {Tab}
return
IniFixer(InFocus, str, val, window) ; there are many edit boxes only the first is used here I am currently processing 5 edit boxes in my personal work.
{
if (InFocus == "Edit1")
{
IniWrite, % val, Recorder.ini, % InFocus, Key1
IniWrite, % str, Recorder.ini, % InFocus, Key2
IniWrite, % window, Recorder.ini, % InFocus, Key3
}
if (InFocus == "Edit2")
{
IniWrite, % val, Recorder.ini, % InFocus, Key1
IniWrite, % str, Recorder.ini, % InFocus, Key2
IniWrite, % window, Recorder.ini, % InFocus, Key3
}
if (InFocus == "Edit3")
{
IniWrite, % val, Recorder.ini, % InFocus, Key1
IniWrite, % str, Recorder.ini, % InFocus, Key2
IniWrite, % window, Recorder.ini, % InFocus, Key3
}
if (InFocus == "Edit4")
{
IniWrite, % val, Recorder.ini, % InFocus, Key1
IniWrite, % str, Recorder.ini, % InFocus, Key2
IniWrite, % window, Recorder.ini, % InFocus, Key3
}
if (InFocus == "Edit5")
{
IniWrite, % val, Recorder.ini, % InFocus, Key1
IniWrite, % str, Recorder.ini, % InFocus, Key2
IniWrite, % window, Recorder.ini, % InFocus, Key3
}
return
}
MyFixed(string) ; a series of string replace commands to eliminate special characters and so on.
{
StringReplace, string, string, (*Inner thought[lmn]) (, , All
StringReplace, string, string, {*What I'm doing[lmn]} {, , All
StringReplace, string, string, Chris Says: , , All
StringReplace, string, string, (*Chris thinks[lmn]) ( , , All
StringReplace, string, string, {*Chris is doing[lmn]} { , , All
StringReplace, string, string, (*Luke thinks[lmn]) (, , All
StringReplace, string, string, {*Luke is doing[lmn]} {, , All
StringReplace, string, string, *[10], , All
StringReplace, string, string, "[10], , All ; " <--- this is being done because of my edited language encoder for AHK.
StringReplace, string, string, }[10], , All
StringReplace, string, string, )[10], , All
StringReplace, string, string, `n`r, , All
StringReplace, string, string, `n, , All
StringReplace, string, string, `r, , All
var:= SubStr(string, 1, 1) ; for beginning of string
if (var == "*")
{
str:=StrLen(string)
string:=SubStr(string, 2, str)
}
nv = " ; " <--- this is being done because of my edited language encoder for AHK.
if (var == nv)
{
str:=StrLen(string)
string:= SubStr(string, 2, str)
}
StringReplace, string, string, ., % A_Space, All
StringReplace, string, string, |, % A_Space, All
StringReplace, string, string, !, % A_Space, All
StringReplace, string, string, ?, % A_Space, All
StringReplace, string, string, ", % A_Space, All ; " <--- this is being done because of my edited language encoder for AHK.
StringReplace, string, string, *, % A_Space, All
StringReplace, string, string, `,, % A_Space, All
StringReplace, string, string, % A_Space A_Space, % A_Space, All
StringReplace, string, string, % A_Space, |, All
StringReplace, string, string, `n, |, All
return % string
}
DesGet() ; gets the original text from the descriptions text file.
{
FileRead, descriptions, % Cache "\descriptions.txt"
if Not ErrorLevel
{
Loop, Parse, % descriptions, `n
{
if (A_LoopField == "")
Continue
IfNotInstring, list, % A_LoopField
list:=% list A_LoopField "|"
}
descriptions:=% list
}
Return % descriptions
}
AIGuiClose:
esc::
IniWrite, Start, Recorder.ini, MasterControl, Key1
IniWrite, 0, Recorder.ini, MasterControl, Key2
IniWrite, "", Recorder.ini, MasterControl, Key3
exitapp