Google Autocorrect "Did you mean:"

Post your working scripts, libraries and tools for AHK v1.1 and older
MrDoge
Posts: 160
Joined: 27 Apr 2020, 21:29

Google Autocorrect "Did you mean:"

12 Nov 2020, 20:15

"helllo wprld" is autocorrected to "hello world"

Code: Select all

Msgbox % GoogleAutoCorrect("helllo wprld")

GoogleAutoCorrect(word)
{
    OutputVar:=URLDownloadToVarWithHeader("https://www.google.com/search?q=" word)

    pos:=InStr(OutputVar, "spell=1")
    if(pos)
    {g6
    
        cut:=SubStr(OutputVar, 1 , pos-1)

        equalSign := InStr(cut, "=" ,, 0)

        equalSignString:=SubStr(cut, equalSign+1)

        lengthOfOptional:=0

        if (SubStr(equalSignString, -4)="&")  ;last 4 chars
        {
            lengthOfOptional:=5
        }

        autocorrected:=SubStr(equalSignString, 1, StrLen(equalSignString) - lengthOfOptional)

        autocorrected := StrReplace(autocorrected, "+" , " ")

        return autocorrected
    }
}

URLDownloadToVarWithHeader(url){
    hObject:=ComObjCreate("WinHttp.WinHttpRequest.5.1")
    hObject.Open("GET",url)
    hObject.SetRequestHeader("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)")

    hObject.Send()
    return hObject.ResponseText
}
the complete script that I use has 2 other features :
#g:: GoogleAutoCorrect() above
if it fails to autocorrect (if google doesn't provide a "Did You Mean" for this word), it will try to get suggestions.

#f:: Get a list of word suggestions from Google, sometimes it provides the correct spelling.
Image
if it fails to get suggestions, it will try to autocorrect.

it uses DownloadToVar("http://suggestqueries.google.com/complete/search?client=firefox&q=" word)
I ripped this IntelliSense from TypingAid https://www.autohotkey.com/boards/viewtopic.php?f=6&t=5644


#h:: Add the misspelled and the corrected word as a hotstring to another ahk script.
It will autocorrect automatically using #g
Image
rows:
options : I like to have *?C by Default, read here to understand them : https://www.autohotkey.com/docs/Hotstrings.htm#Options
misspelled
corrected

In the main script, you need to set the variable autocorrectPath to the path of the other ahk script
example:

Code: Select all

autocorrectPath=C:\Users\Public\AHK\autocorrect.ahk
to reload the other script everytime you add another hotstring, the main script will simply run the the other script
so be sure to have #SingleInstance force in your other script, I recommend having all these at the top of the other script

Code: Select all

#NoEnv ; For security
#SingleInstance force
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetKeyDelay, -1
setbatchlines, -1
#NoTrayIcon
#Persistent


#g #f #h will all copy the selected text using

Code: Select all

   Send, ^c
   Send, ^c
   sleep, 50
you can change that to

Code: Select all

   send, ^c
   ClipWait, 2
if it helps

EDIT : forgot to paste script

Code: Select all

#SingleInstance, force
#if
   autocorrectPath=C:\Users\Public\AHK\autocorrect.ahk

;  TypingAid
;  http://www.autohotkey.com/board/topic/49517-ahk-11typingaid-v2200-word-autocompletion-utility/
;
;  Press 1 to 0 keys to autocomplete the word upon suggestion 
;  Or use the Up/Down keys to select an item
;  (0 will match suggestion 10) 
;                              Credits:
;                                -Maniac
;                                -Jordi S
;                                -hugov
;                                -kakarukeys
;                                -Asaptrad
;                                -j4hangir
;                                -Theclaw
;___________________________________________ 

; Press 1 to 0 keys to autocomplete the word upon suggestion 
;___________________________________________

;    CONFIGURATIONS 
/* 
FileRead, response, C:\Users\User\Downloads\f (2).txt
array:=Jxon_Load(response)
wordSuggestions:=array[2]
*/
#MaxThreadsPerHotkey 4
#NoTrayIcon
;disable hotkeys until setup is complete
Suspend, On 
#NoEnv
ListLines Off

;Set the Coordinate Modes before any threads can be executed
CoordMode, Caret, Screen
CoordMode, Mouse, Screen

EvaluateScriptPathAndTitle()

SuspendOn()
BuildTrayMenu() 

OnExit, SaveScript

;Change the setup performance speed
SetBatchLines, 20ms
;read in the preferences file
ReadPreferences()

SetTitleMatchMode, 2
SetKeyDelay, -1

;bruh my vars
; fileLocation=%A_ScriptDir%\htmlDownloadPlace\didYouMean.html
waitingForKey:=false
notKeys:=[]
youtube_dlGuiFirstTime:=true
HotStringWinTitle=New Hotstring

;set windows constants
g_EVENT_SYSTEM_FOREGROUND := 0x0003
g_EVENT_SYSTEM_SCROLLINGSTART := 0x0012
g_EVENT_SYSTEM_SCROLLINGEND := 0x0013
g_GCLP_HCURSOR := -12
g_IDC_HAND := 32649
g_IDC_HELP := 32651
g_IMAGE_CURSOR := 2
g_LR_SHARED := 0x8000
g_NULL := 0
g_SB_VERT := 0x1
g_SIF_POS := 0x4
g_SM_CMONITORS := 80
g_SM_CXVSCROLL := 2
g_SM_CXFOCUSBORDER := 83
g_WINEVENT_SKIPOWNPROCESS := 0x0002
g_WM_LBUTTONUP := 0x202
g_WM_LBUTTONDBLCLK := 0x203
g_WM_MOUSEMOVE := 0x200
g_WM_SETCURSOR := 0x20

;setup code
g_Helper_Id = 
g_HelperManual = 
g_DelimiterChar := Chr(2)
g_cursor_hand := DllCall( "LoadImage", "Ptr", g_NULL, "Uint", g_IDC_HAND , "Uint", g_IMAGE_CURSOR, "int", g_NULL, "int", g_NULL, "Uint", g_LR_SHARED ) 
if (A_PtrSize == 8) {
   g_SetClassLongFunction := "SetClassLongPtr"
} else {
   g_SetClassLongFunction := "SetClassLong"
}
g_PID := DllCall("GetCurrentProcessId")
AutoTrim, Off 

InitializeListBox()

BlockInput, Send

InitializeHotKeys()
DisableKeyboardHotKeys()

;Change the Running performance speed (Priority changed to High in GetIncludedActiveWindow)
SetBatchLines, -1

g_WinChangedCallback := RegisterCallback("WinChanged")
g_ListBoxScrollCallback := RegisterCallback("ListBoxScroll")

if !(g_WinChangedCallback)
{
   MsgBox, Failed to register callback function
   ExitApp
}

if !(g_ListBoxScrollCallback)
{
   MsgBox, Failed to register ListBox Scroll callback function
   ExitApp
}

;Find the ID of the window we are using
GetIncludedActiveWindow()
g_Word=REEEEEEEEEE

/*
MainLoop()

; END

MainLoop()
{
   global g_TerminatingEndKeys
   Loop 
   { 

      ;If the active window has changed, wait for a new one
      IF !( ReturnWinActive() ) 
      {
         Critical, Off
         GetIncludedActiveWindow()
      } else {    
         Critical, Off
      }
   
      ;Get one key at a time 
      Input, InputChar, L1 V I, {BS}%g_TerminatingEndKeys%
   
      Critical
      EndKey := ErrorLevel
   
      ProcessKey(InputChar,EndKey)
   }
}
*/
ProcessKey(InputChar,EndKey)
{
   global g_Active_Id
   global g_Helper_Id
   global g_IgnoreSend
   global g_LastInput_Id
   global g_OldCaretX
   global g_OldCaretY
   global g_TerminatingCharactersParsed
   global g_Word
   global prefs_DetectMouseClickMove
   global prefs_EndWordCharacters
   global prefs_ForceNewWordCharacters
   global prefs_Length

   IfEqual, g_IgnoreSend, 1
   {
      g_IgnoreSend = 
      Return
   }

   IfEqual, EndKey,
   {
      EndKey = Max
   }

   IfEqual, EndKey, NewInput
   Return

   IfEqual, EndKey, Endkey:Tab
   If ( GetKeyState("Alt") =1 || GetKeyState("LWin") =1 || GetKeyState("RWin") =1 )
      Return

   ;If we have no window activated for typing, we don't want to do anything with the typed character
   IfEqual, g_Active_Id,
   {
      if (!GetIncludedActiveWindow())
      {
         Return
      }
   }

   IF !( ReturnWinActive() )
   {
      if (!GetIncludedActiveWindow())
      {
         Return
      }
   }

   IfEqual, g_Active_Id, %g_Helper_Id%
   {
      Return
   }

   ;If we haven't typed anywhere, set this as the last window typed in
   IfEqual, g_LastInput_Id,
   g_LastInput_Id = %g_Active_Id%

   IfNotEqual, prefs_DetectMouseClickMove, On
   {
      ifequal, g_OldCaretY,
      g_OldCaretY := HCaretY()

      if ( g_OldCaretY != HCaretY() )
      {
         ;Don't do anything if we aren't in the original window and aren't starting a new word
         IfNotEqual, g_LastInput_Id, %g_Active_Id%
         Return

         ; add the word if switching lines
         ClearAllVars(true)
         g_Word := InputChar
         Return 
      } 
   }

   g_OldCaretY := HCaretY()
   g_OldCaretX := HCaretX()

   ;Backspace clears last letter 
   ifequal, EndKey, Endkey:BackSpace
   {
      ;Don't do anything if we aren't in the original window and aren't starting a new word
      IfNotEqual, g_LastInput_Id, %g_Active_Id%
      Return

      StringLen, len, g_Word
      IfEqual, len, 1 
      {
         ClearAllVars(true)
      } else IfNotEqual, len, 0
      {
         StringTrimRight, g_Word, g_Word, 1
      }
   } else if ( ( EndKey == "Max" ) && !(InStr(g_TerminatingCharactersParsed, InputChar)) )
   {
      ; If active window has different window ID from the last input,
      ;learn and blank word, then assign number pressed to the word
      IfNotEqual, g_LastInput_Id, %g_Active_Id%
      {
         ClearAllVars(true)
         g_Word := InputChar
         g_LastInput_Id := g_Active_Id
         Return
      }

      if InputChar in %prefs_ForceNewWordCharacters%
      {
         ClearAllVars(true)
         g_Word := InputChar
      } else if InputChar in %prefs_EndWordCharacters%
      {
         g_Word .= InputChar
         ClearAllVars(true)
      } else { 
         g_Word .= InputChar
      }

   } else IfNotEqual, g_LastInput_Id, %g_Active_Id%
   {
      ;Don't do anything if we aren't in the original window and aren't starting a new word
      Return
   } else {
      ClearAllVars(true)
      Return
   }

   ;SetTimer, RecomputeMatchesTimer, -1
}
*/
;RecomputeMatchesTimer:
$#f::
   Clipboard=
   ; send, ^c
   ; ClipWait, 2
   Send, ^c
   Send, ^c
   sleep, 50
   word:=clipboard
   if (word)
   {
      IF !( ReturnWinActive() ) 
      {
         Critical, Off
         GetIncludedActiveWindow()
      } else { 
         Critical, Off
      }
      response:=DownloadToVar("http://suggestqueries.google.com/complete/search?client=firefox&q=" word)
      array:=Jxon_Load(response)
      wordSuggestions:=array[2]
      if (IsObject(wordSuggestions) and wordSuggestions[1])
      {
         Thread, NoTimers
         g_Word=REEEEEEEEEE
         EnableKeyboardHotKeys()

         RecomputeMatches()
      }
      else
      {
         ; url=http://www.google.com/search?q=%word%
         ; FileDelete, %fileLocation%
         ; UrlDownloadToFile, %url%, %fileLocation%
         OutputVar:=URLDownloadToVarWithHeader("http://www.google.com/search?q=" word)
         ; FileRead, OutputVar, %fileLocation%
         pos:=InStr(OutputVar, "spell=1")
         if(pos)
         {
            cut:=SubStr(OutputVar, 1 , pos-1)

            equalSign := InStr(cut, "=" ,, 0)

            equalSignString:=SubStr(cut, equalSign+1)

            lengthOfOptional:=0

            if (SubStr(equalSignString, -4)="&")  ;last 4 chars
            {
               lengthOfOptional:=5
            }

            autocorrected:=SubStr(equalSignString, 1, StrLen(equalSignString) - lengthOfOptional)

            autocorrected := StrReplace(autocorrected, "+" , " ")

            send, %autocorrected%
         }
      }

   }
return

; didYouMeanFirst:
$#g::
   Clipboard=
   ; send, ^c
   ; ClipWait, 2
   Send, ^c
   Send, ^c
   sleep, 50
   word:=clipboard
   ; didYouMeanFirst(word) 

   ; didYouMeanFirst(word) 
   ; {
   ; global
   if (word)
   {
      ; url=https://www.google.com/search?q=%word%
      ; url:="https://www.google.com/search?q=" word
      ; FileDelete, %fileLocation%
      ; UrlDownloadToFile, %url%, %fileLocation%
      OutputVar:=URLDownloadToVarWithHeader("https://www.google.com/search?q=" word)
      ; clipboard:=OutputVar
      ; p(OutputVar)
      ; FileRead, OutputVar, %fileLocation%
      ; clipboard:=OutputVar
      ; p(OutputVar)
      pos:=InStr(OutputVar, "spell=1")
      if(pos)
      {
         cut:=SubStr(OutputVar, 1 , pos-1)

         equalSign := InStr(cut, "=" ,, 0)

         equalSignString:=SubStr(cut, equalSign+1)

         lengthOfOptional:=0

         if (SubStr(equalSignString, -4)="&")  ;last 4 chars
         {
            lengthOfOptional:=5
         }

         autocorrected:=SubStr(equalSignString, 1, StrLen(equalSignString) - lengthOfOptional)

         autocorrected := StrReplace(autocorrected, "+" , " ")

         send, %autocorrected%
      }
      else
      {
         IF !( ReturnWinActive() ) 
         {
            Critical, Off
            GetIncludedActiveWindow()
         } else { 
            Critical, Off
         }
         response:=DownloadToVar("http://suggestqueries.google.com/complete/search?client=firefox&q=" word)
         array:=Jxon_Load(response)
         wordSuggestions:=array[2]

         Thread, NoTimers
         g_Word=REEEEEEEEEE
         EnableKeyboardHotKeys()

         RecomputeMatches()
      }
   }

   ; }

Return

waitForKey:
   Input, InputChar, L1 V I, {BS}{Down}{Up}{Lwin}
   if (waitingForKey=true)
   {
      for k, v in notKeys
      {
         if (v=InputChar)
            return
      }

      ClearAllVars(false)

   }
return

RecomputeMatches()
{
   ; This function will take the given word, and will recompile the list of matches and redisplay the wordlist.
   global g_MatchTotal
   global g_SingleMatch
   global g_SingleMatchDescription
   global g_SingleMatchReplacement
   global g_Word
   global g_WordListDB
   global prefs_ArrowKeyMethod
   global prefs_LearnMode
   global prefs_ListBoxRows
   global prefs_NoBackSpace
   global prefs_ShowLearnedFirst
   global prefs_SuppressMatchingWord
   global wordSuggestions

   global waitingForKey:=true
   SetTimer, waitForKey, -1

   SavePriorMatchPosition()

   ;Match part-word with command 
   g_MatchTotal = 0 

   IfEqual, prefs_ArrowKeyMethod, Off
   {
      IfLess, prefs_ListBoxRows, 10
      LimitTotalMatches := prefs_ListBoxRows
      else LimitTotalMatches = 10
   } else {
      LimitTotalMatches = 200
   }

   StringUpper, WordMatch, g_Word 

   StringReplace, WordExactEscaped, g_Word, ', '', All
   StringReplace, WordMatchEscaped, WordMatch, ', '', All

   IfEqual, prefs_SuppressMatchingWord, On
   {
      IfEqual, prefs_NoBackSpace, Off
      {
         SuppressMatchingWordQuery := " AND word <> '" . WordExactEscaped . "'"
      } else {
         SuppressMatchingWordQuery := " AND wordindexed <> '" . WordMatchEscaped . "'"
      }
   }

   ;WhereQuery := " WHERE wordindexed GLOB '" . WordMatchEscaped . "*' " . SuppressMatchingWordQuery
   ;Normalize := "wth"
/*    
   NormalizeTable := g_WordListDB.Query("SELECT MIN(count) AS normalize FROM Words" . WhereQuery . "AND count IS NOT NULL LIMIT " . LimitTotalMatches . ";")
   
   for each, row in NormalizeTable.Rows
   {
      Normalize := row[1]
   }
   */ 
   IfEqual, Normalize,
   {
      Normalize := 0
   }

   WordLen := StrLen(g_Word)
   OrderByQuery := " ORDER BY CASE WHEN count IS NULL then "
   IfEqual, prefs_ShowLearnedFirst, On
   {
      OrderByQuery .= "ROWID + 1 else 0"
   } else {
      OrderByQuery .= "ROWID else 'z'"
   }

   OrderByQuery .= " end, CASE WHEN count IS NOT NULL then ( (count - " . Normalize . ") * ( 1 - ( '0.75' / (LENGTH(word) - " . WordLen . ")))) end DESC, Word"

   Matches := wordSuggestions

   g_SingleMatch := Object()
   g_SingleMatchDescription := Object()
   g_SingleMatchReplacement := Object()

   for each, row in Matches
   { 
      g_SingleMatch[++g_MatchTotal] := row

      continue
   }

   ;If no match then clear Tip 
   IfEqual, g_MatchTotal, 0
   {
      ;p(1) no result :(
      ClearAllVars(false) ;marked
      Return 
   } 

   SetupMatchPosition()
   RebuildMatchList()
   ShowListBox()
   send,{lwin up}

}

;------------------------------------------------------------------------

~LButton:: 
   CheckForCaretMove("LButton","UpdatePosition")
return

;------------------------------------------------------------------------

~RButton:: 
   CheckForCaretMove("RButton","UpdatePosition")
Return

;------------------------------------------------------------------------

CheckForCaretMove(MouseButtonClick, UpdatePosition = false)
{
   global g_LastInput_Id
   global g_MouseWin_Id
   global g_OldCaretX
   global g_OldCaretY
   global g_Word
   global prefs_DetectMouseClickMove

   ;If we aren't using the DetectMouseClickMoveScheme, skip out
   IfNotEqual, prefs_DetectMouseClickMove, On
Return

if (UpdatePosition)
{
   ; Update last click position in case Caret is not detectable
   ;  and update the Last Window Clicked in
   MouseGetPos, MouseX, MouseY, g_MouseWin_Id
   WinGetPos, ,TempY, , , ahk_id %g_MouseWin_Id%
}

IfEqual, MouseButtonClick, LButton
{
   KeyWait, LButton, U 
} else KeyWait, RButton, U
;p(g_LastInput_Id " " g_MouseWin_Id)
;p(listBoxID "   " g_MouseWin_Id)

if (listBoxID=g_MouseWin_Id)
return

;p(23432)

;p(2355)
SysGet, SM_CYCAPTION, 4
SysGet, SM_CYSIZEFRAME, 33

TempY += SM_CYSIZEFRAME
IF ( ( MouseY >= TempY ) && (MouseY < (TempY + SM_CYCAPTION) ) )
{
Return
}

ClearAllVars(true)
; If we have a g_Word and an g_OldCaretX, check to see if the Caret moved
/*
IfNotEqual, g_OldCaretX, 
{
   if (( g_OldCaretY != HCaretY() ) || (g_OldCaretX != HCaretX() ))
   {
      ; add the word if switching lines
      ClearAllVars(true)
   }
}
*/
Return
}

;------------------------------------------------------------------------

InitializeHotKeys()
{
   global g_DelimiterChar
   global g_EnabledKeyboardHotKeys
   global prefs_ArrowKeyMethod
   global prefs_DisabledAutoCompleteKeys
   global prefs_LearnMode 

   g_EnabledKeyboardHotKeys =

   ;Setup toggle-able hotkeys

   ;Can't disable mouse buttons as we need to check to see if we have clicked the ListBox window

   ; If we disable the number keys they never get to the input for some reason,
   ; so we need to keep them enabled as hotkeys

   IfEqual, prefs_ArrowKeyMethod, Off
   {
      Hotkey, $^Enter, Off
      Hotkey, $^Space, Off
      Hotkey, $Tab, Off
      Hotkey, $Right, Off
      Hotkey, $Up, Off
      Hotkey, $Down, Off
      Hotkey, $PgUp, Off
      Hotkey, $PgDn, Off
      HotKey, $Enter, Off
      Hotkey, $NumpadEnter, Off
   } else {
      g_EnabledKeyboardHotKeys .= "$Up" . g_DelimiterChar
      g_EnabledKeyboardHotKeys .= "$Down" . g_DelimiterChar
      g_EnabledKeyboardHotKeys .= "$PgUp" . g_DelimiterChar
      g_EnabledKeyboardHotKeys .= "$1" . g_DelimiterChar
      g_EnabledKeyboardHotKeys .= "$2" . g_DelimiterChar
      g_EnabledKeyboardHotKeys .= "$3" . g_DelimiterChar
      g_EnabledKeyboardHotKeys .= "$4" . g_DelimiterChar
      g_EnabledKeyboardHotKeys .= "$5" . g_DelimiterChar
      g_EnabledKeyboardHotKeys .= "$6" . g_DelimiterChar
      g_EnabledKeyboardHotKeys .= "$7" . g_DelimiterChar
      g_EnabledKeyboardHotKeys .= "$8" . g_DelimiterChar
      g_EnabledKeyboardHotKeys .= "$9" . g_DelimiterChar
      g_EnabledKeyboardHotKeys .= "$0" . g_DelimiterChar
      If prefs_DisabledAutoCompleteKeys contains E
         Hotkey, $^Enter, Off
      else g_EnabledKeyboardHotKeys .= "$^Enter" . g_DelimiterChar
         If prefs_DisabledAutoCompleteKeys contains S
         HotKey, $^Space, Off
      else g_EnabledKeyboardHotKeys .= "$^Space" . g_DelimiterChar
         If prefs_DisabledAutoCompleteKeys contains T
         HotKey, $Tab, Off
      else g_EnabledKeyboardHotKeys .= "$Tab" . g_DelimiterChar
         If prefs_DisabledAutoCompleteKeys contains R
         HotKey, $Right, Off
      else g_EnabledKeyboardHotKeys .= "$Right" . g_DelimiterChar
         If prefs_DisabledAutoCompleteKeys contains U
         HotKey, $Enter, Off
      else g_EnabledKeyboardHotKeys .= "$Enter" . g_DelimiterChar
         If prefs_DisabledAutoCompleteKeys contains M
         HotKey, $NumpadEnter, Off
      else g_EnabledKeyboardHotKeys .= "$NumpadEnter" . g_DelimiterChar
      }

   ; remove last ascii 2
   StringTrimRight, g_EnabledKeyboardHotKeys, g_EnabledKeyboardHotKeys, 1

}

EnableKeyboardHotKeys()
{
   global g_DelimiterChar
   global g_EnabledKeyboardHotKeys
   Loop, Parse, g_EnabledKeyboardHotKeys, %g_DelimiterChar%
   {
      Hotkey, If
         HotKey, %A_LoopField%, On
   }
Return
}

DisableKeyboardHotKeys()
{
   global g_DelimiterChar
   global g_EnabledKeyboardHotKeys
   Loop, Parse, g_EnabledKeyboardHotKeys, %g_DelimiterChar%
   {
      Hotkey, If
         HotKey, %A_LoopField%, Off
   }
Return
}

;------------------------------------------------------------------------
; #if winactive("Word List Appears Here.")
#MaxThreadsPerHotkey 1 
$1:: 
$2:: 
$3::
$4:: 
$5:: 
$6:: 
$7:: 
$8:: 
$9:: 
$0::
   CheckWord(A_ThisHotkey)
Return

$^Enter::
$^Space::
$Tab::
$Up::
$Down::
$PgUp::
$PgDn::
$Right::
$Enter::
$NumpadEnter::
   EvaluateUpDown(A_ThisHotKey)
Return

~backspace::
   ClearAllVars(false) ;marked
return

~left::
   ClearAllVars(false) ;marked
return

; $^+h::
; MaybeOpenOrCloseHelperWindowManual()
Return

;------------------------------------------------------------------------

; If hotkey was pressed, check wether there's a match going on and send it, otherwise send the number(s) typed 
CheckWord(Key)
{
   global g_ListBox_Id
   global g_Match
   global g_MatchStart
   global g_NumKeyMethod
   global g_SingleMatch
   global g_Word
   global prefs_ListBoxRows
   global prefs_NumPresses

   StringRight, Key, Key, 1 ;Grab just the number pushed, trim off the "$"

   IfEqual, Key, 0
   {
      WordIndex := g_MatchStart + 9
   } else {
      WordIndex := g_MatchStart - 1 + Key
   } 

   IfEqual, g_NumKeyMethod, Off
   {
      SendCompatible(Key,0)
      ProcessKey(Key,"")
      Return
   }
   IfEqual, prefs_NumPresses, 2
   SuspendOn()

   ; If active window has different window ID from before the input, blank word 
   ; (well, assign the number pressed to the word) 
   if !(ReturnWinActive())
   { 
      SendCompatible(Key,0)
      ProcessKey(Key,"")
      IfEqual, prefs_NumPresses, 2
      SuspendOff()
      Return 
   } 

   if ReturnLineWrong() ;Make sure we are still on the same line
   { 
      SendCompatible(Key,0)
      ProcessKey(Key,"") 
      IfEqual, prefs_NumPresses, 2
      SuspendOff()
      Return 
   } 

   IfNotEqual, g_Match, 
   {
      ifequal, g_ListBox_Id, ; only continue if match is not empty and list is showing
      { 
         SendCompatible(Key,0)
         ProcessKey(Key,"")
         IfEqual, prefs_NumPresses, 2
         SuspendOff()
         Return 
      }
   }
/*    
   ifequal, g_Word, ; only continue if g_word is not empty 
   { 
   p(234)
      SendCompatible(Key,0)
      ProcessKey(Key,"")
      IfEqual, prefs_NumPresses, 2
      SuspendOff()
      Return 
   }
   */

   if ( ( (WordIndex + 1 - MatchStart) > prefs_ListBoxRows) || (g_SingleMatch[WordIndex] = "") ) ; only continue g_SingleMatch is not empty 
   { 
      SendCompatible(Key,0)
      ;ProcessKey(Key,"")
      IfEqual, prefs_NumPresses, 2
      SuspendOff()
      Return 
   }
   IfEqual, prefs_NumPresses, 2
   {
      Input, KeyAgain, L1 I T0.5, 1234567890

      ; If there is a timeout, abort replacement, send key and return
      IfEqual, ErrorLevel, Timeout
      {
         SendCompatible(Key,0)
         ProcessKey(Key,"")
         SuspendOff()
         Return
      }

      ; Make sure it's an EndKey, otherwise abort replacement, send key and return
      IfNotInString, ErrorLevel, EndKey:
         {
            SendCompatible(Key . KeyAgain,0)
            ProcessKey(Key,"")
            ProcessKey(KeyAgain,"")
            SuspendOff()
            Return
         }

         ; If the 2nd key is NOT the same 1st trigger key, abort replacement and send keys   
         IfNotInString, ErrorLevel, %Key%
         {
            StringTrimLeft, KeyAgain, ErrorLevel, 7
            SendCompatible(Key . KeyAgain,0)
            ProcessKey(Key,"")
            ProcessKey(KeyAgain,"")
            SuspendOff()
            Return
         }

         ; If active window has different window ID from before the input, blank word 
         ; (well, assign the number pressed to the word) 
         if !(ReturnWinActive())
         { 
            SendCompatible(Key . KeyAgain,0)
            ProcessKey(Key,"")
            ProcessKey(KeyAgain,"")
            SuspendOff()
            Return 
         } 

         if ReturnLineWrong() ;Make sure we are still on the same line
         { 
            SendCompatible(Key . KeyAgain,0)
            ProcessKey(Key,"")
            ProcessKey(KeyAgain,"")
            SuspendOff()
            Return 
         } 
      }

      SendWord(WordIndex)
      IfEqual, prefs_NumPresses, 2
      SuspendOff()
      Return 
   }

   ;------------------------------------------------------------------------
   ;If a hotkey related to the up/down arrows was pressed
   EvaluateUpDown(Key)
   {
      global g_ListBox_Id
      global g_Match
      global g_MatchPos
      global g_MatchStart
      global g_MatchTotal
      global g_OriginalMatchStart
      global g_SingleMatch
      global g_Word
      global prefs_ArrowKeyMethod
      global prefs_DisabledAutoCompleteKeys
      global prefs_ListBoxRows

      IfEqual, prefs_ArrowKeyMethod, Off
      {
         if (Key != "$LButton")
         {
            SendKey(Key)
            Return
         }
      }

      IfEqual, g_Match,
      {
         SendKey(Key)
      Return
   }

   IfEqual, g_ListBox_Id,
   {
      SendKey(Key)
      Return
   }

   if !(ReturnWinActive())
   {
      ;p(2)

      SendKey(Key)
      ClearAllVars(false) ;marked
      Return
   }

   if ReturnLineWrong()
   {
      SendKey(Key)
      ClearAllVars(true)
      Return
   } 

   IfEqual, g_Word, ; IF EMPTY :  only continue if word is not empty 
   {
      p(3)

      SendKey(Key)
      ClearAllVars(false) ;marked
      Return
   }

   if ( ( Key = "$^Enter" ) || ( Key = "$Tab" ) || ( Key = "$^Space" ) || ( Key = "$Right") || ( Key = "$Enter") || ( Key = "$LButton") || ( Key = "$NumpadEnter") )
   {
      IfEqual, Key, $^Enter
      {
         KeyTest = E
      } else IfEqual, Key, $Tab
      {
         KeyTest = T
      } else IfEqual, Key, $^Space
      { 
         KeyTest = S 
      } else IfEqual, Key, $Right
      {
         KeyTest = R
      } else IfEqual, Key, $Enter
      {
         KeyTest = U
      } else IfEqual, Key, $LButton
      {
         KeyTest = L
      } else IfEqual, Key, $NumpadEnter
      {
         KeyTest = M
      }
      if (KeyTest == "L") {
         ;when hitting LButton, we've already handled this condition         
      } else if prefs_DisabledAutoCompleteKeys contains %KeyTest%
      {
         SendKey(Key)
      Return 
   }
   if (g_SingleMatch[g_MatchPos] = "") ;only continue if g_SingleMatch is not empty
   {
      SendKey(Key)
      g_MatchPos := g_MatchTotal
      RebuildMatchList()
      ShowListBox()
      Return
   }
   SendWord(g_MatchPos)
   Return

}

PreviousMatchStart := g_OriginalMatchStart

IfEqual, Key, $Up
{ 
   g_MatchPos--

   IfLess, g_MatchPos, 1
   {
      g_MatchStart := g_MatchTotal - (prefs_ListBoxRows - 1)
      IfLess, g_MatchStart, 1
      g_MatchStart = 1
      g_MatchPos := g_MatchTotal
   } else IfLess, g_MatchPos, %g_MatchStart%
   {
      g_MatchStart --
   } 
} else IfEqual, Key, $Down
{
   g_MatchPos++
   IfGreater, g_MatchPos, %g_MatchTotal%
   {
      g_MatchStart =1
      g_MatchPos =1
   } Else If ( g_MatchPos > ( g_MatchStart + (prefs_ListBoxRows - 1) ) )
   {
      g_MatchStart ++
   } 
} else IfEqual, Key, $PgUp
{
   IfEqual, g_MatchPos, 1
   {
      g_MatchPos := g_MatchTotal - (prefs_ListBoxRows - 1)
      g_MatchStart := g_MatchTotal - (prefs_ListBoxRows - 1)
   } Else {
      g_MatchPos-=prefs_ListBoxRows 
      g_MatchStart-=prefs_ListBoxRows
   }

   IfLess, g_MatchPos, 1
   g_MatchPos = 1
   IfLess, g_MatchStart, 1
   g_MatchStart = 1

} else IfEqual, Key, $PgDn
{
   IfEqual, g_MatchPos, %g_MatchTotal%
   {
      g_MatchPos := prefs_ListBoxRows
      g_MatchStart := 1
   } else {
      g_MatchPos+=prefs_ListBoxRows
      g_MatchStart+=prefs_ListBoxRows
   }

   IfGreater, g_MatchPos, %g_MatchTotal%
   g_MatchPos := g_MatchTotal

   If ( g_MatchStart > ( g_MatchTotal - (prefs_ListBoxRows - 1) ) )
   {
      g_MatchStart := g_MatchTotal - (prefs_ListBoxRows - 1) 
      IfLess, g_MatchStart, 1
      g_MatchStart = 1
   }
}

IfEqual, g_MatchStart, %PreviousMatchStart%
{
   Rows := GetRows()
   IfNotEqual, g_MatchPos,
   {
      ListBoxChooseItem(Rows)
   }
} else {
   RebuildMatchList()
   ShowListBox()
}
Return
}

;------------------------------------------------------------------------

ReturnLineWrong()
{
   global g_OldCaretY
   global prefs_DetectMouseClickMove
   ; Return false if we are using DetectMouseClickMove
   IfEqual, prefs_DetectMouseClickMove, On
   Return

   Return, ( g_OldCaretY != HCaretY() )
}

;------------------------------------------------------------------------

EvaluateScriptPathAndTitle()
{
   ;relaunches to 64 bit or sets script title
   global g_ScriptTitle

   SplitPath, A_ScriptName,,,ScriptExtension,ScriptNoExtension,

   If A_Is64bitOS
   {
      IF (A_PtrSize = 4)
      {
         IF A_IsCompiled
         {

            ScriptPath64 := A_ScriptDir . "\" . ScriptNoExtension . "64." . ScriptExtension

            IfExist, %ScriptPath64%
            {
               Run, %ScriptPath64%, %A_WorkingDir%
               ExitApp
            }
         }
      }
   }

   if (SubStr(ScriptNoExtension, StrLen(ScriptNoExtension)-1, 2) == "64" )
   {
      StringTrimRight, g_ScriptTitle, ScriptNoExtension, 2
   } else {
      g_ScriptTitle := ScriptNoExtension
   }

   if (InStr(g_ScriptTitle, "TypingAid"))
   {
      g_ScriptTitle = TypingAid
   }

   return
}

;------------------------------------------------------------------------

InactivateAll()
{
   ;Force unload of Keyboard Hook and WinEventHook
   Input
   SuspendOn()
   CloseListBox()
   MaybeSaveHelperWindowPos()
   DisableWinHook()
}

SuspendOn()
{
   global g_ScriptTitle
   Suspend, On
   Menu, Tray, Tip, %g_ScriptTitle% - Inactive
/*    
   If A_IsCompiled
   {
      Menu, tray, Icon, %A_ScriptFullPath%,3,1
   } else
   {
      Menu, tray, Icon, %A_ScriptDir%\%g_ScriptTitle%-Inactive.ico, ,1
   }
   */
}

SuspendOff()
{
   global g_ScriptTitle
   Suspend, Off
   Menu, Tray, Tip, %g_ScriptTitle% - Active
/*    
   If A_IsCompiled
   {
      Menu, tray, Icon, %A_ScriptFullPath%,1,1
   } else
   {
      Menu, tray, Icon, %A_ScriptDir%\%g_ScriptTitle%-Active.ico, ,1
   }
   */
} 

;------------------------------------------------------------------------

BuildTrayMenu()
{

   Menu, Tray, DeleteAll
   Menu, Tray, NoStandard
   Menu, Tray, add, Settings, Configuration
   Menu, Tray, add, Pause, PauseResumeScript
   IF (A_IsCompiled)
   {
      Menu, Tray, add, Exit, ExitScript
   } else {
      Menu, Tray, Standard
   }
   Menu, Tray, Default, Settings
   ;Initialize Tray Icon
   Menu, Tray, Icon
}

;------------------------------------------------------------------------

; This is to blank all vars related to matches, ListBox and (optionally) word 
ClearAllVars(ClearWord)
{
   global
   ;p(ClearWord "  " "no")
   waitingForKey:=false
   CloseListBox()
   Ifequal,ClearWord,1
   {
      ;p(9999)
      g_Word =
      g_OldCaretY=
      g_OldCaretX=
      g_LastInput_id=
   }

   g_SingleMatch =
   g_SingleMatchDescription =
   g_SingleMatchReplacement =
   g_Match= 
   g_MatchPos=
   g_MatchStart= 
   g_OriginalMatchStart=
   Return
}

;------------------------------------------------------------------------

FileAppendDispatch(Text,FileName,ForceEncoding=0)
{
   IfEqual, A_IsUnicode, 1
   {
      IfNotEqual, ForceEncoding, 0
      {
         FileAppend, %Text%, %FileName%, %ForceEncoding%
      } else
      {
         FileAppend, %Text%, %FileName%, UTF-8
      }
   } else {
      FileAppend, %Text%, %FileName%
   }
   Return
}

MaybeFixFileEncoding(File,Encoding)
{
   IfGreaterOrEqual, A_AhkVersion, 1.0.90.0
   {

      IfExist, %File%
      { 
         IfNotEqual, A_IsUnicode, 1
         {
            Encoding =
         }

         EncodingCheck := FileOpen(File,"r")

         If EncodingCheck
         {
            If Encoding
            {
               IF !(EncodingCheck.Encoding = Encoding)
                  WriteFile = 1
            } else
            {
               IF (SubStr(EncodingCheck.Encoding, 1, 3) = "UTF")
                  WriteFile = 1
            }

            IF WriteFile
            {
               Contents := EncodingCheck.Read()
               EncodingCheck.Close()
               EncodingCheck =
               FileCopy, %File%, %File%.preconv.bak
               FileDelete, %File%
               FileAppend, %Contents%, %File%, %Encoding%

               Contents =
            } else
            {
               EncodingCheck.Close()
               EncodingCheck = 
            }
         }
      }
   }
}

;------------------------------------------------------------------------

MaybeCoInitializeEx()
{
   global g_NULL
   global g_ScrollEventHook
   global g_WinChangedEventHook

   if (!g_WinChangedEventHook && !g_ScrollEventHook)
   {
      DllCall("CoInitializeEx", "Ptr", g_NULL, "Uint", g_NULL)
   }

}

MaybeCoUninitialize()
{
   global g_WinChangedEventHook
   global g_ScrollEventHook
   if (!g_WinChangedEventHook && !g_ScrollEventHook)
   {
      DllCall("CoUninitialize")
   }
}

;------------------------------------------------------------------------

Configuration:
   GoSub, LaunchSettings
Return

PauseResumeScript:
   if (g_PauseState == "Paused")
   {
      g_PauseState =
      Pause, Off
      EnableWinHook()
      Menu, tray, Uncheck, Pause
   } else {
      g_PauseState = Paused
      DisableWinHook()
      SuspendOn()
      Menu, tray, Check, Pause
      Pause, On, 1
   }
Return

ExitScript:
   ExitApp
Return 

SaveScript:
   ; Close the ListBox if it's open
   CloseListBox()

   SuspendOn()

   ;Change the cleanup performance speed
   SetBatchLines, 20ms
   Process, Priority,,Normal

   ;Grab the Helper Window Position if open
   MaybeSaveHelperWindowPos()

   ;Write the Helper Window Position to the Preferences File
   MaybeWriteHelperWindowPos()

   ExitApp
; start of includes
; start of Conversions.ahk

; end of Conversions.ahk
; these functions handle database conversion
; always set the SetDbVersion default argument to the current highest version

SetDbVersion(dBVersion = 3)
{
	global g_WordListDB
	g_WordListDB.Query("INSERT OR REPLACE INTO LastState VALUES ('databaseVersion', '" . dBVersion . "', NULL);")
}


; returns true if we need to rebuild the whole database
MaybeConvertDatabase()
{
	global g_WordListDB
	
	databaseVersionRows := g_WordListDB.Query("SELECT lastStateNumber FROM LastState WHERE lastStateItem = 'databaseVersion';")
	
	if (databaseVersionRows)
	{
		for each, row in databaseVersionRows.Rows
		{
			databaseVersion := row[1]
		}
	}
	
	if (!databaseVersion)
	{
		   tableConverted := g_WordListDB.Query("SELECT tableconverted FROM LastState;")
	} else {
		tableConverted := g_WordListDB.Query("SELECT lastStateNumber FROM LastState WHERE lastStateItem = 'tableConverted';")
	}
   
	if (tableConverted)
	{
		for each, row in tableConverted.Rows
		{
			WordlistConverted := row[1]
		}
	}
	
	IfNotEqual, WordlistConverted, 1
	{
		RebuildDatabase()		
		return, true
	}
	
	if (!databaseVersion)
	{
		RunConversionOne(WordlistConverted)
	}
	
	if (databaseVersion < 2)
	{
		RunConversionTwo()
	}
	
	if (databaseVersion < 3)
	{
		RunConversionThree()
	}
	
	return, false
}


; Rebuilds the Database from scratch as we have to redo the wordlist anyway.
RebuildDatabase()
{
	global g_WordListDB
	g_WordListDB.BeginTransaction()
	g_WordListDB.Query("DROP TABLE Words;")
	g_WordListDB.Query("DROP INDEX WordIndex;")
	g_WordListDB.Query("DROP TABLE LastState;")
	
	CreateWordsTable()
	
	CreateWordIndex()
	
	CreateLastStateTable()
	
	SetDbVersion()
	g_WordListDB.EndTransaction()
		
}

;Runs the first conversion
RunConversionOne(WordlistConverted)
{
	global g_WordListDB
	g_WordListDB.BeginTransaction()
	
	g_WordListDB.Query("ALTER TABLE LastState RENAME TO OldLastState;")
	
	CreateLastStateTable()
	
	g_WordListDB.Query("DROP TABLE OldLastState;")
	g_WordListDB.Query("INSERT OR REPLACE INTO LastState VALUES ('tableConverted', '" . WordlistConverted . "', NULL);")
	
	;superseded by conversion 3
	;g_WordListDB.Query("ALTER TABLE Words ADD COLUMN worddescription TEXT;")
	
	SetDbVersion(1)
	g_WordListDB.EndTransaction()
	
}

RunConversionTwo()
{
	global g_WordListDB
	
	;superseded by conversion 3
	;g_WordListDB.Query("ALTER TABLE Words ADD COLUMN wordreplacement TEXT;")
	
	;SetDbVersion(2)
}

RunConversionThree()
{
	global g_WordListDB
	g_WordListDB.BeginTransaction()
	
	CreateWordsTable("Words2")
	
	g_WordListDB.Query("UPDATE Words SET wordreplacement = '' WHERE wordreplacement IS NULL;")
	
	g_WordListDB.Query("INSERT INTO Words2 SELECT * FROM Words;")
	
	g_WordListDB.Query("DROP TABLE Words;")
	
	g_WordListDB.Query("ALTER TABLE Words2 RENAME TO Words;")
	
	CreateWordIndex()
	
	SetDbVersion(3)
	g_WordListDB.EndTransaction()
}

CreateLastStateTable()
{
	global g_WordListDB

	IF not g_WordListDB.Query("CREATE TABLE LastState (lastStateItem TEXT PRIMARY KEY, lastStateNumber INTEGER, otherInfo TEXT) WITHOUT ROWID;")
	{
		ErrMsg := g_WordListDB.ErrMsg()
		ErrCode := g_WordListDB.ErrCode()
		MsgBox Cannot Create LastState Table - fatal error: %ErrCode% - %ErrMsg%
		ExitApp
	}
}

CreateWordsTable(WordsTableName:="Words")
{
	global g_WordListDB
	
	IF not g_WordListDB.Query("CREATE TABLE " . WordsTableName . " (wordindexed TEXT NOT NULL, word TEXT NOT NULL, count INTEGER, worddescription TEXT, wordreplacement TEXT NOT NULL, PRIMARY KEY (word, wordreplacement) );")
	{
		ErrMsg := g_WordListDB.ErrMsg()
		ErrCode := g_WordListDB.ErrCode()
		msgbox Cannot Create %WordsTableName% Table - fatal error: %ErrCode% - %ErrMsg%
		ExitApp
	}
}

CreateWordIndex()
{
	global g_WordListDB

	IF not g_WordListDB.Query("CREATE INDEX WordIndex ON Words (wordindexed);")
	{
		ErrMsg := g_WordListDB.ErrMsg()
		ErrCode := g_WordListDB.ErrCode()
		msgbox Cannot Create WordIndex Index - fatal error: %ErrCode% - %ErrMsg%
		ExitApp
	}
}
; start of Helper.ahk
; These functions and labels are related to interacting with the Helper Window

MaybeOpenOrCloseHelperWindow(ActiveProcess,ActiveTitle,ActiveId)
{
   ; This is called when switching the active window
   global g_HelperManual
   
   IfNotEqual, g_HelperManual,
   {
      MaybeCreateHelperWindow()
      Return
   }

   IF ( CheckHelperWindowAuto(ActiveProcess,ActiveTitle) )
   {
      global g_HelperClosedWindowIds
      ; Remove windows which were closed
      Loop, Parse, g_HelperClosedWindowIDs, |
      {
         IfEqual, A_LoopField,
            Continue
            
         IfWinExist, ahk_id %A_LoopField%
         {
            TempHelperClosedWindowIDs .= "|" . A_LoopField . "|"
         }
      }
      
      g_HelperClosedWindowIDs = %TempHelperClosedWindowIDs%
      TempHelperClosedWindowIDs =
      
      SearchText := "|" . ActiveId . "|"
      
      IfInString, g_HelperClosedWindowIDs, %SearchText%
      {
         MaybeSaveHelperWindowPos()
      } else MaybeCreateHelperWindow()
   
   } else MaybeSaveHelperWindowPos()

   Return
   
}

CheckHelperWindowAuto(ActiveProcess,ActiveTitle)
{
   global prefs_HelperWindowProgramExecutables
   global prefs_HelperWindowProgramTitles
   
   quotechar := """"
   
   Loop, Parse, prefs_HelperWindowProgramExecutables, |
   {
      IfEqual, ActiveProcess, %A_LoopField%
         Return, true
   }

   Loop, Parse, prefs_HelperWindowProgramTitles, |
   {
      if (SubStr(A_LoopField, 1, 1) == quotechar && SubStr(A_LoopField, StrLen(A_LoopField), 1) == quotechar)
      {
         StringTrimLeft, TrimmedString, A_LoopField, 1
         StringTrimRight, TrimmedString, TrimmedString, 1
         IfEqual, ActiveTitle, %TrimmedString%
         {
            Return, true
         }
      } else IfInString, ActiveTitle, %A_LoopField%
      {
         Return, true
      }
   }

   Return
}

MaybeOpenOrCloseHelperWindowManual()
{
   ;Called when we hit Ctrl-Shift-H
      
   global g_Helper_Id
   global g_HelperManual
   
   ;If a helper window already exists 
   IfNotEqual, g_Helper_Id,
   {
      ; If we've forced a manual helper open, close it. Else mark it as forced open manually
      IfNotEqual, g_HelperManual,
      {
         HelperWindowClosed()
      } else g_HelperManual=1
   } else {
      global g_Active_Id
      WinGetTitle, ActiveTitle, ahk_id %g_Active_Id%
      WinGet, ActiveProcess, ProcessName, ahk_id %g_Active_Id%
      ;Check for Auto Helper, and if Auto clear closed flag and open
      IF ( CheckHelperWindowAuto(ActiveProcess,ActiveTitle) )
      {
         global g_HelperClosedWindowIDs
         SearchText := "|" . g_Active_Id . "|"
         StringReplace, g_HelperClosedWindowIDs, g_HelperClosedWindowIDs, %SearchText%
               
      } else {
         ; else Open a manually opened helper window
         g_HelperManual=1
      }
      MaybeCreateHelperWindow()
   }
      
   Return
}

;------------------------------------------------------------------------

;Create helper window for showing ListBox
MaybeCreateHelperWindow()
{
   Global g_Helper_Id
   Global g_XY
   ;Don't open a new Helper Window if One is already open
   IfNotEqual, g_Helper_Id,
      Return
      
   Gui, HelperGui:+Owner -MinimizeBox -MaximizeBox +AlwaysOnTop
   Gui, HelperGui:+LabelHelper_
   Gui, HelperGui:Add, Text,,List appears here 
   IfNotEqual, g_XY, 
   {
      StringSplit, Pos, g_XY, `, 
      Gui, HelperGui:Show, X%Pos1% Y%Pos2% NoActivate
   } else {
      Gui, HelperGui:Show, NoActivate
   }
   WinGet, g_Helper_Id, ID,,List appears here 
   WinSet, Transparent, 125, ahk_id %g_Helper_Id%
   return 
}

;------------------------------------------------------------------------

Helper_Close:
HelperWindowClosed()
Return

HelperWindowClosed()
{
   global g_Helper_Id
   global g_HelperManual
   IfNotEqual, g_Helper_Id,
   {
      ;Check g_LastActiveIdBeforeHelper and not g_Active_Id in case we are on the Helper Window
      global g_LastActiveIdBeforeHelper
      WinGetTitle, ActiveTitle, ahk_id %g_LastActiveIdBeforeHelper%
      WinGet, ActiveProcess, ProcessName, ahk_id %g_LastActiveIdBeforeHelper%
      
      If ( CheckHelperWindowAuto(ActiveProcess,ActiveTitle) )
      {
         global g_HelperClosedWindowIDs
         
         SearchText := "|" . g_LastActiveIdBeforeHelper . "|"         
         IfNotInString g_HelperClosedWindowIDs, %SearchText%
            g_HelperClosedWindowIDs .= SearchText
      }
   
      g_HelperManual=   
   
      MaybeSaveHelperWindowPos()
   }
   Return
}

;------------------------------------------------------------------------

MaybeSaveHelperWindowPos()
{
   global g_Helper_Id
   IfNotEqual, g_Helper_Id, 
   {
      global g_XY
      global g_XYSaved
      WinGetPos, hX, hY, , , ahk_id %g_Helper_Id%
      g_XY = %hX%`,%hY%
      g_XYSaved = 1
      g_Helper_Id = 
      Gui, HelperGui:Hide
   }
   Return
}

;------------------------------------------------------------------------
; end of Helper.ahk

; start of ListBox.ahk
;These functions and labels are related to the shown list of words

InitializeListBox()
{
   global
   
   Gui, ListBoxGui: -Caption +AlwaysOnTop +ToolWindow +Delimiter%g_DelimiterChar%
   
   Local ListBoxFont
   if (prefs_ListBoxFontOverride && prefs_ListBoxFontOverride != "<Default>")
   {
      ListBoxFont := prefs_ListBoxFontOverride
   } else IfEqual, prefs_ListBoxFontFixed, On
   {
      ListBoxFont = Courier New
   } else {
      ListBoxFont = Tahoma
   }
      
   Gui, ListBoxGui:Font, s%prefs_ListBoxFontSize%, %ListBoxFont%

   Loop, %prefs_ListBoxRows%
   {
      GuiControl, ListBoxGui:-Redraw, g_ListBox%A_Index%
      ;can't use a g-label here as windows sometimes passes the click message when spamming the scrollbar arrows
      Gui, ListBoxGui: Add, ListBox, vg_ListBox%A_Index% R%A_Index% X0 Y0 hwndg_ListBoxHwnd%A_Index%
   }

   Return
}
   
ListBoxClickItem(wParam, lParam, msg, ClickedHwnd)
{
   global
   Local NewClickedItem
   Local TempRows
   static LastClickedItem
   
   TempRows := GetRows()
   
   if (ClickedHwnd != g_ListBoxHwnd%TempRows%)
   {
      return
   }
   
   ; if we clicked in the scrollbar, jump out
   if (A_GuiX > (g_ListBoxPosX + g_ListBoxContentWidth))
   {
      SetSwitchOffListBoxTimer()
      Return
   }
   
   GuiControlGet, g_MatchPos, ListBoxGui:, g_ListBox%TempRows%
   
   if (msg == g_WM_LBUTTONUP)
   {
      if prefs_DisabledAutoCompleteKeys not contains L
      {
         SwitchOffListBoxIfActive()
         EvaluateUpDown("$LButton")   
      } else {
         ; Track this to make sure we're double clicking on the same item
         NewClickedItem := g_MatchPos
         SetSwitchOffListBoxTimer()
      }
         
   } else if (msg == g_WM_LBUTTONDBLCLK)
   {
      SwitchOffListBoxIfActive()
      
      if prefs_DisabledAutoCompleteKeys contains L
      {
         if (LastClickedItem == g_MatchPos)
         {
            EvaluateUpDown("$LButton")   
         }
      }
   } else {
      SwitchOffListBoxIfActive()
   }
      
   ; clear or set LastClickedItem
   LastClickedItem := NewClickedItem
   
   Return
}

SetSwitchOffListBoxTimer()
{
   static DoubleClickTime
   
   if !(DoubleClickTime)
   {
      DoubleClickTime := DllCall("GetDoubleClickTime")
   }
   ;When single click is off, we have to wait for the double click time to pass
   ; before re-activating the edit window to allow double click to work
   SetTimer, SwitchOffListBoxIfActiveSub, -%DoubleClickTime%
}
   

SwitchOffListBoxIfActiveSub:
SwitchOffListBoxIfActive()
Return

ListBoxScroll(Hook, Event, EventHwnd)
{
   global
   
   Local MatchEnd
   Local SI
   Local TempRows
   Local Position
   
   if (g_ListBox_Id)
   {
   
      TempRows := GetRows()
      if (g_ListBoxHwnd%TempRows% != EventHwnd)
      {
         return
      }
      
      if (Event == g_EVENT_SYSTEM_SCROLLINGSTART)
      {
         ; make sure the timer is clear so we don't switch while scrolling
         SetTimer, SwitchOffListBoxIfActiveSub, Off
         return
      }
      
      SI:=GetScrollInfo(g_ListBoxHwnd%TempRows%)
   
      if (!SI.npos)
      {
         return
      }
   
      if (SI.npos == g_MatchStart)
      {
         return
      }
   
      g_MatchStart := SI.npos
   
      SetSwitchOffListBoxTimer()   
   }
}

; based on code by HotKeyIt
;  http://www.autohotkey.com/board/topic/78829-ahk-l-scrollinfo/
;  http://www.autohotkey.com/board/topic/55150-class-structfunc-sizeof-updated-010412-ahkv2/
GetScrollInfo(ctrlhwnd) {
  global g_SB_VERT
  global g_SIF_POS
  SI:=new _Struct("cbSize,fMask,nMin,nMax,nPage,nPos,nTrackPos")
  SI.cbSize:=sizeof(SI)
  SI.fMask := g_SIF_POS
  If !DllCall("GetScrollInfo","PTR",ctrlhwnd,"Int",g_SB_VERT,"PTR",SI[""])
    Return false
  else Return SI
}

ListBoxChooseItem(Row)
{
   global
   GuiControl, ListBoxGui: Choose, g_ListBox%Row%, %g_MatchPos%
}

;------------------------------------------------------------------------

CloseListBox()
{
   global g_ListBox_Id
   IfNotEqual, g_ListBox_Id,  ;if exist, close
   {
      Gui, ListBoxGui: Hide
      ListBoxEnd()
   }
   Return
}

DestroyListBox()
{
   Gui, ListBoxGui:Destroy
   ListBoxEnd()
   Return
}

ListBoxEnd()
{
   global g_ScrollEventHook
   global g_ScrollEventHookThread
   global g_ListBox_Id
   global g_WM_LBUTTONUP
   global g_WM_LBUTTONDBLCLK
   
   g_ListBox_Id =
   
   OnMessage(g_WM_LBUTTONUP, "")
   OnMessage(g_WM_LBUTTONDBLCLK, "")

   if (g_ScrollEventHook) {
      DllCall("UnhookWinEvent", "Uint", g_ScrollEventHook)
      g_ScrollEventHook =
      g_ScrollEventHookThread =
      MaybeCoUninitialize()
   }
   DisableKeyboardHotKeys()
   return
}

;------------------------------------------------------------------------

SavePriorMatchPosition()
{
   global g_MatchPos
   global g_MatchStart
   global g_OldMatch
   global g_OldMatchStart
   global g_SingleMatch
   global prefs_ArrowKeyMethod
   
   if !(g_MatchPos)
   {
      g_OldMatch =
      g_OldMatchStart = 
   } else IfEqual, prefs_ArrowKeyMethod, LastWord
   {
      g_OldMatch := g_SingleMatch[g_MatchPos]
      g_OldMatchStart = 
   } else IfEqual, prefs_ArrowKeyMethod, LastPosition
   {
      g_OldMatch := g_MatchPos
      g_OldMatchStart := g_MatchStart
   } else {
      g_OldMatch =
      g_OldMatchStart =
   }
      
   Return
}

SetupMatchPosition()
{
   global g_MatchPos
   global g_MatchStart
   global g_MatchTotal
   global g_OldMatch
   global g_OldMatchStart
   global g_SingleMatch
   global prefs_ArrowKeyMethod
   global prefs_ListBoxRows
   
   IfEqual, g_OldMatch, 
   {
      IfEqual, prefs_ArrowKeyMethod, Off
      {
         g_MatchPos = 
         g_MatchStart = 1
      } else {
         g_MatchPos = 1
         g_MatchStart = 1
      }
   } else IfEqual, prefs_ArrowKeyMethod, Off
   {
      g_MatchPos = 
      g_MatchStart = 1
   } else IfEqual, prefs_ArrowKeyMethod, LastPosition
   {
      IfGreater, g_OldMatch, %g_MatchTotal%
      {
         g_MatchStart := g_MatchTotal - (prefs_ListBoxRows - 1)
         IfLess, g_MatchStart, 1
            g_MatchStart = 1
         g_MatchPos := g_MatchTotal
      } else {
         g_MatchStart := g_OldMatchStart
         If ( g_MatchStart > (g_MatchTotal - (prefs_ListBoxRows - 1) ))
         {
            g_MatchStart := g_MatchTotal - (prefs_ListBoxRows - 1)
            IfLess, g_MatchStart, 1
               g_MatchStart = 1
         }
         g_MatchPos := g_OldMatch
      }
   
   } else IfEqual, prefs_ArrowKeyMethod, LastWord
   {
      ListPosition =
      Loop, %g_MatchTotal%
      {
         if ( g_OldMatch == g_SingleMatch[A_Index] )
         {
            ListPosition := A_Index
            Break
         }
      }
      IfEqual, ListPosition, 
      {
         g_MatchPos = 1
         g_MatchStart = 1
      } Else {
         g_MatchStart := ListPosition - (prefs_ListBoxRows - 1)
         IfLess, g_MatchStart, 1
            g_MatchStart = 1
         g_MatchPos := ListPosition
      }
   } else {
      g_MatchPos = 1
      g_MatchStart = 1
   }
             
   g_OldMatch = 
   g_OldMatchStart = 
   Return
}

RebuildMatchList()
{
   global g_Match
   global g_MatchLongestLength
   global g_MatchPos
   global g_MatchStart
   global g_MatchTotal
   global g_OriginalMatchStart
   global prefs_ListBoxRows
   
   g_Match = 
   g_MatchLongestLength =
   
   if (!g_MatchPos)
   {
      ; do nothing
   } else if (g_MatchPos < g_MatchStart)
   {
      g_MatchStart := g_MatchPos
   } else if (g_MatchPos > (g_MatchStart + (prefs_ListBoxRows - 1)))
   {
      g_MatchStart := g_MatchPos - (prefs_ListBoxRows -1)
   }
   
   g_OriginalMatchStart := g_MatchStart
   
   MaxLength := ComputeListBoxMaxLength()
   HalfLength := Round(MaxLength/2)
   
   Loop, %g_MatchTotal%
   {
      CurrentLength := AddToMatchList(A_Index, MaxLength, HalfLength, 0, true)
      IfGreater, CurrentLength, %LongestBaseLength%
         LongestBaseLength := CurrentLength      
   }
   
   Loop, %g_MatchTotal%
   {
      CurrentLength := AddToMatchList(A_Index, MaxLength, HalfLength, LongestBaseLength, false)
      IfGreater, CurrentLength, %g_MatchLongestLength%
         g_MatchLongestLength := CurrentLength      
   }
   StringTrimRight, g_Match, g_Match, 1        ; Get rid of the last linefeed 
   Return
}

AddToMatchList(position, MaxLength, HalfLength, LongestBaseLength, ComputeBaseLengthOnly)
{
   global g_DelimiterChar
   global g_Match
   global g_MatchStart
   global g_NumKeyMethod
   global g_SingleMatch
   global g_SingleMatchDescription
   global g_SingleMatchReplacement
   global prefs_ListBoxFontFixed
   
   IfEqual, g_NumKeyMethod, Off
   {
      prefix =
   } else IfLess, position, %g_MatchStart%
   {
      prefix =
   } else if ( position > ( g_MatchStart + 9 ) )
   {
      prefix = 
   } else {
      prefix := Mod(position - g_MatchStart +1,10) . " "
   }
   
   CurrentMatch := g_SingleMatch[position]
   if (g_SingleMatchReplacement[position] || g_SingleMatchDescription[position])
   {
      AdditionalDataExists := true
      BaseLength := HalfLength
   } else if (ComputeBaseLengthOnly) {
      ; we don't need to compute the base length if there
      ; is no Replacement or Description
      Return, 0
   } else {
      BaseLength := MaxLength
   }
   
   CurrentMatchLength := StrLen(CurrentMatch) + StrLen(prefix)
   
   if (CurrentMatchLength > BaseLength)
   {
      CompensatedBaseLength := BaseLength - strlen(prefix)
      ; remove 3 characters so we can add the ellipsis
      StringLeft, CurrentMatch, CurrentMatch, CompensatedBaseLength - 3
      CurrentMatch .= "..."
   
      CurrentMatchLength := StrLen(CurrentMatch) + StrLen(prefix)
   }
   
   if (ComputeBaseLengthOnly)
   {
      Return, CurrentMatchLength
   }
   
   Iterations := 0
   Tabs = 
   Remainder := 0
   
   if (AdditionalDataExists) 
   {
      if (g_SingleMatchReplacement[position])
      {
         CurrentMatch .= "->" . g_SingleMatchReplacement[position]
      }
      if (g_SingleMatchDescription[position])
      {
         ;;CurrentMatch .= "|" . g_SingleMatchDescription[position]
         IfEqual, prefs_ListBoxFontFixed, On
         {
            Iterations := Ceil(LongestBaseLength/8) - Floor((strlen(CurrentMatch) + strlen(prefix))/8)
         
            Remainder := Mod(strlen(CurrentMatch) + strlen(prefix), 8)
         
            Loop, %Iterations%
            {
               Tabs .= Chr(9)
            }
         } else {
            Iterations := 1
            Remainder := 0
            Tabs := Chr(9)
         }
         
         CurrentMatch .= Tabs . "|" . g_SingleMatchDescription[position]
      }
         
      CurrentMatchLength := strlen(CurrentMatch) + strlen(prefix) - strlen(Tabs) + (Iterations * 8) - Remainder
      
      ;MaxLength - prefix length to make room for prefix
      if (CurrentMatchLength > MaxLength)
      {
         CompensatedMaxLength := MaxLength - strlen(prefix) + strlen(Tabs) - (Iterations * 8) + Remainder
         ; remove 3 characters so we can add the ellipsis
         StringLeft, CurrentMatch, CurrentMatch, CompensatedMaxLength - 3
         CurrentMatch .= "..."
         CurrentMatchLength := strlen(CurrentMatch) + strlen(prefix) - strlen(Tabs) + (Iterations * 8) - Remainder
      }
   }
   
   g_Match .= prefix . CurrentMatch
   
   g_Match .= g_DelimiterChar
   Return, CurrentMatchLength
}

;------------------------------------------------------------------------

; find out the longest length we can use in the listbox
; Any changes to this function probably need to be reflected in ShowListBox() or ForceWithinMonitorBounds
ComputeListBoxMaxLength()
{
   global g_ListBoxCharacterWidthComputed
   global g_MatchTotal
   global g_SM_CMONITORS
   global g_SM_CXFOCUSBORDER
   global g_SM_CXVSCROLL
   global prefs_ListBoxMaxWidth
   
   ; grab the width of a vertical scrollbar

   Rows := GetRows()
   
   IfGreater, g_MatchTotal, %Rows%
   {
      SysGet, ScrollBarWidth, %g_SM_CXVSCROLL%
      if ScrollBarWidth is not integer
         ScrollBarWidth = 17
   } else ScrollBarWidth = 0

   ; Grab the internal border width of the ListBox box
   SysGet, BorderWidthX, %g_SM_CXFOCUSBORDER%
   If BorderWidthX is not integer
      BorderWidthX = 1
   
   ;Use 8 pixels for each character in width
   ListBoxBaseSizeX := g_ListBoxCharacterWidthComputed + ScrollBarWidth + (BorderWidthX * 2)
   
   ListBoxPosX := HCaretX()
   ListBoxPosY := HCaretY()
   
   SysGet, NumMonitors, %g_SM_CMONITORS%

   IfLess, NumMonitors, 1
      NumMonitors =1
         
   Loop, %NumMonitors%
   {
      SysGet, Mon, Monitor, %A_Index%
      IF ( ( ListBoxPosX < MonLeft ) || (ListBoxPosX > MonRight ) || ( ListBoxPosY < MonTop ) || (ListBoxPosY > MonBottom ) )
         Continue
      
      MonWidth := MonRight - MonLeft
      break
   }
   
   if !prefs_ListBoxMaxWidth
   {
      Width := MonWidth
   } else if (prefs_ListBoxMaxWidth < MonWidth)
   {
      Width := prefs_ListBoxMaxWidth
   } else 
   {
      Width := MonWidth
   }
   
   return Floor((Width-ListBoxBaseSizeX)/ g_ListBoxCharacterWidthComputed)
}
   

;Show matched values
; Any changes to this function may need to be reflected in ComputeListBoxMaxLength()
ShowListBox()
{
   global
   ;g_LastInput_id

   IfNotEqual, g_Match,
   {
      Local BorderWidthX
      Local ListBoxActualSize
      Local ListBoxActualSizeH
      Local ListBoxActualSizeW
      Local ListBoxPosY
      Local ListBoxSizeX
      Local ListBoxThread
      Local MatchEnd
      Local Rows
      Local ScrollBarWidth
      static ListBox_Old_Cursor

      Rows := GetRows()
      
      IfGreater, g_MatchTotal, %Rows%
      {
         SysGet, ScrollBarWidth, %g_SM_CXVSCROLL%
         if ScrollBarWidth is not integer
            ScrollBarWidth = 17
      } else ScrollBarWidth = 0
   
      ; Grab the internal border width of the ListBox box
      SysGet, BorderWidthX, %g_SM_CXFOCUSBORDER%
      If BorderWidthX is not integer
         BorderWidthX = 1
      
      ;Use 8 pixels for each character in width
      ListBoxSizeX := g_ListBoxCharacterWidthComputed * g_MatchLongestLength + g_ListBoxCharacterWidthComputed + ScrollBarWidth + (BorderWidthX * 2)
      
      g_ListBoxPosX := HCaretX()
      ListBoxPosY := HCaretY()
      
      ; In rare scenarios, the Cursor may not have been detected. In these cases, we just won't show the ListBox.
      IF (!(g_ListBoxPosX) || !(ListBoxPosY))
      {
         return
      }
      
      MatchEnd := g_MatchStart + (prefs_ListBoxRows - 1)
      
      Loop, %prefs_ListBoxRows%
      { 
         IfEqual, A_Index, %Rows%
         {
            GuiControl, ListBoxGui: -Redraw, g_ListBox%A_Index%
            GuiControl, ListBoxGui: Move, g_ListBox%A_Index%, w%ListBoxSizeX%
            GuiControl, ListBoxGui: ,g_ListBox%A_Index%, %g_DelimiterChar%%g_Match%
            IfNotEqual, g_MatchPos,
            {
               GuiControl, ListBoxGui: Choose, g_ListBox%A_Index%, %MatchEnd%
               GuiControl, ListBoxGui: Choose, g_ListBox%A_Index%, %g_MatchPos%
            }
            GuiControl, ListBoxGui: +AltSubmit +Redraw, g_ListBox%A_Index%
            GuiControl, ListBoxGui: Show, g_ListBox%A_Index%
            GuiControlGet, ListBoxActualSize, ListBoxGui: Pos, g_ListBox%A_Index%
            Continue
         }
      
         GuiControl, ListBoxGui: Hide, g_ListBox%A_Index%
         GuiControl, ListBoxGui: -Redraw, g_ListBox%A_Index%
         GuiControl, ListBoxGui: , g_ListBox%A_Index%, %g_DelimiterChar%
      }
      
      ForceWithinMonitorBounds(g_ListBoxPosX,ListBoxPosY,ListBoxActualSizeW,ListBoxActualSizeH,Rows)
      
      g_ListBoxContentWidth := ListBoxActualSizeW - ScrollBarWidth - BorderWidthX
      
      IfEqual, g_ListBox_Id,
      {
         
         if prefs_DisabledAutoCompleteKeys not contains L
         {
            if (!ListBox_Old_Cursor)
            {
               ListBox_Old_Cursor := DllCall(g_SetClassLongFunction, "Uint", g_ListBoxHwnd%Rows%, "int", g_GCLP_HCURSOR, "int", g_cursor_hand)
            }
            
            DllCall(g_SetClassLongFunction, "Uint", g_ListBoxHwnd%Rows%, "int", g_GCLP_HCURSOR, "int", g_cursor_hand)
            
         ; we only need to set it back to the default cursor if we've ever unset the default cursor
         } else if (ListBox_Old_Cursor)
         {
            DllCall(g_SetClassLongFunction, "Uint", g_ListBoxHwnd%Rows%, "int", g_GCLP_HCURSOR, "int", ListBox_Old_Cursor)
         }
            
      }
      
      Gui, ListBoxGui: Show, NoActivate X%g_ListBoxPosX% Y%ListBoxPosY% H%ListBoxActualSizeH% W%ListBoxActualSizeW%, Word List Appears Here.
      Gui, ListBoxGui: +LastFound +AlwaysOnTop
      
         EnableKeyboardHotKeys()
/*             
      IfEqual, g_ListBox_Id,
      {
         
         EnableKeyboardHotKeys()   
      }
       */
      WinGet, g_ListBox_Id, ID, Word List Appears Here.
      listBoxID:=g_ListBox_Id
      ListBoxThread := DllCall("GetWindowThreadProcessId", "Ptr", g_ListBox_Id, "Ptr", g_NULL)
      if (g_ScrollEventHook && (ListBoxThread != g_ScrollEventHookThread))
      {
         DllCall("UnhookWinEvent", "Uint", g_ScrollEventHook)
         g_ScrollEventHook =
         g_ScrollEventHookThread =
         MaybeCoUninitialize()
      }
         
      if (!g_ScrollEventHook) {
         MaybeCoInitializeEx()
         g_ScrollEventHook := DllCall("SetWinEventHook", "Uint", g_EVENT_SYSTEM_SCROLLINGSTART, "Uint", g_EVENT_SYSTEM_SCROLLINGEND, "Ptr", g_NULL, "Uint", g_ListBoxScrollCallback, "Uint", g_PID, "Uint", ListBoxThread, "Uint", g_NULL)
         g_ScrollEventHookThread := ListBoxThread
      }
      
      OnMessage(g_WM_LBUTTONUP, "ListBoxClickItem")
      OnMessage(g_WM_LBUTTONDBLCLK, "ListBoxClickItem")
      
      IfNotEqual, prefs_ListBoxOpacity, 255
         WinSet, Transparent, %prefs_ListBoxOpacity%, ahk_id %g_ListBox_Id%
   }
}

; Any changes to this function may need to be reflected in ComputeListBoxMaxLength()
ForceWithinMonitorBounds(ByRef ListBoxPosX,ByRef ListBoxPosY,ListBoxActualSizeW,ListBoxActualSizeH,Rows)
{
   global g_SM_CMONITORS
   global prefs_ListBoxOffset
   ;Grab the number of non-dummy monitors
   SysGet, NumMonitors, %g_SM_CMONITORS%
   
   IfLess, NumMonitors, 1
      NumMonitors =1
         
   Loop, %NumMonitors%
   {
      SysGet, Mon, Monitor, %A_Index%
      IF ( ( ListBoxPosX < MonLeft ) || (ListBoxPosX > MonRight ) || ( ListBoxPosY < MonTop ) || (ListBoxPosY > MonBottom ) )
         Continue
      
      If ( (ListBoxPosX + ListBoxActualSizeW ) > MonRight )
      {
         ListBoxPosX := MonRight - ListBoxActualSizeW
         If ( ListBoxPosX < MonLeft )
            ListBoxPosX := MonLeft
      }
         
      
      ; + prefs_ListBoxOffset Move ListBox down a little so as not to hide the caret. 
      ListBoxPosY := ListBoxPosY + prefs_ListBoxOffset
      If ( (ListBoxPosY + ListBoxActualSizeH ) > MonBottom )
      {
         ListBoxPosY := HCaretY() - Ceil(prefs_ListBoxOffset - (ListBoxActualSizeH / Rows )) - ListBoxActualSizeH
      }
         
      Break
   }

   Return      
}

;------------------------------------------------------------------------

GetRows()
{
   global g_MatchTotal
   global prefs_ListBoxRows
   IfGreater, g_MatchTotal, %prefs_ListBoxRows%
      Rows := prefs_ListBoxRows
   else Rows := g_MatchTotal
   
   Return, Rows
}
;------------------------------------------------------------------------

; function to grab the X position of the caret for the ListBox
HCaretX() 
{ 
   global g_Helper_Id
    
   WinGetPos, HelperX,,,, ahk_id %g_Helper_Id% 
   if HelperX !=
   { 
      return HelperX
   } 
   if ( CheckIfCaretNotDetectable() )
   { 
      MouseGetPos, MouseX
      return MouseX
   } 
   return A_CaretX 
} 

;------------------------------------------------------------------------

; function to grab the Y position of the caret for the ListBox
HCaretY() 
{ 
   global g_Helper_Id

   WinGetPos,,HelperY,,, ahk_id %g_Helper_Id% 
   if HelperY != 
   { 
      return HelperY
   } 
   if ( CheckIfCaretNotDetectable() )
   { 
      MouseGetPos, , MouseY
      return MouseY + 20
   } 
   return A_CaretY 
}

;------------------------------------------------------------------------

CheckIfCaretNotDetectable()
{
   ;Grab the number of non-dummy monitors
   SysGet, NumMonitors, 80
   
   IfLess, NumMonitors, 1
      NumMonitors = 1
   
   if !(A_CaretX)
   {
      Return, 1
   }
   
   ;if the X caret position is equal to the leftmost border of the monitor +1, we can't detect the caret position.
   Loop, %NumMonitors%
   {
      SysGet, Mon, Monitor, %A_Index%
      if ( A_CaretX = ( MonLeft ) )
      {
         Return, 1
      }
      
   }
   
   Return, 0
}
; end of ListBox.ahk

;start of lib
ZTrim( N := "" ) { ; SKAN /  CD:01-Jul-2017 | LM:03-Jul-2017 | Topic: goo.gl/TgWDb5
Local    V  := StrSplit( N, ".", A_Space ) 
Local    V0 := SubStr( V.1,1,1 ),   V1 := Abs( V.1 ),      V2 :=  RTrim( V.2, "0" )
Return ( V0 = "-" ? "-" : ""   )  ( V1 = "" ? 0 : V1 )   ( V2 <> "" ? "." V2 : "" )
}

;: Title: sizeof function by HotKeyIt
;

; Function: sizeof
; Description:
;      sizeof() is based on AHK_L Objects and supports both, ANSI and UNICODE version, so to use it you will require <a href=http://www.autohotkey.com/forum/viewtopic.php?t=43049>Lexikos AutoHotkey_L.exe</a> or other versions based on it that supports objects.<br><br>nsizeof is used to calculate the size of structures or data types. <br>Visit <a href=http://www.autohotkey.com/forum/viewtopic.php?t=43049>sizeof at AutoHotkey</a> forum, any feedback is welcome.
; Syntax: size:= sizeof(Structure_Definition or Structure_Object)
; Parameters:
;	   Field types - All AutoHotkey and Windows Data Types are supported<br>AutoHotkey Data Types<br> Int, Uint, Int64, UInt64, Char, UChar, Short, UShort, Fload and Double.<br>Windows Data Types<br> - note, TCHAR UCHAR and CHAR return actual character rather than the value, use Asc() function to find out the value/code<br>Windows Data types: Asc(char)<br>ATOM,BOOL,BOOLEAN,BYTE,CHAR,COLORREF,DWORD,DWORDLONG,DWORD_PTR,<br>DWORD32,DWORD64,FLOAT,HACCEL,HALF_PTR,HANDLE,HBITMAP,HBRUSH,HCOLORSPACE,HCONV,HCONVLIST,HCURSOR,HDC,<br>HDDEDATA,HDESK,HDROP,HDWP,HENHMETAFILE,HFILE,HFONT,HGDIOBJ,HGLOBAL,HHOOK,HICON,HINSTANCE,HKEY,HKL,<br>HLOCAL,HMENU,HMETAFILE,HMODULE,HMONITOR,HPALETTE,HPEN,HRESULT,HRGN,HRSRC,HSZ,HWINSTA,HWND,INT,<br>INT_PTR,INT32,INT64,LANGID,LCID,LCTYPE,LGRPID,LONG,LONGLONG,LONG_PTR,LONG32,LONG64,LPARAM,LPBOOL,<br>LPBYTE,LPCOLORREF,LPCSTR,LPCTSTR,LPCVOID,LPCWSTR,LPDWORD,LPHANDLE,LPINT,LPLONG,LPSTR,LPTSTR,LPVOID,<br>LPWORD,LPWSTR,LRESULT,PBOOL,PBOOLEAN,PBYTE,PCHAR,PCSTR,PCTSTR,PCWSTR,PDWORD,PDWORDLONG,PDWORD_PTR,<br>PDWORD32,PDWORD64,PFLOAT,PHALF_PTR,PHANDLE,PHKEY,PINT,PINT_PTR,PINT32,PINT64,PLCID,PLONG,PLONGLONG,<br>PLONG_PTR,PLONG32,PLONG64,POINTER_32,POINTER_64,POINTER_SIGNED,POINTER_UNSIGNED,PSHORT,PSIZE_T,<br>PSSIZE_T,PSTR,PTBYTE,PTCHAR,PTSTR,PUCHAR,PUHALF_PTR,PUINT,PUINT_PTR,PUINT32,PUINT64,PULONG,PULONGLONG,<br>PULONG_PTR,PULONG32,PULONG64,PUSHORT,PVOID,PWCHAR,PWORD,PWSTR,SC_HANDLE,SC_LOCK,SERVICE_STATUS_HANDLE,<br>SHORT,SIZE_T,SSIZE_T,TBYTE,TCHAR,UCHAR,UHALF_PTR,UINT,UINT_PTR,UINT32,UINT64,ULONG,ULONGLONG,<br>ULONG_PTR,ULONG32,ULONG64,USHORT,USN,WCHAR,WORD,WPARAM
;	   <b>Parameters</b> - <b>Description</b>
;	   size - The size of structure or data type
;	   Structure_Definition - C/C++ syntax or usual definition (must not be multiline) e.g. "Int x,Int y", C/C++ definitions must be multiline.
; Return Value:
;     sizeof returns size of structures or data types
; Remarks:
;		None.
; Related:
; Example:
;		file:

sizeof(_TYPE_,parent_offset=0,ByRef _align_total_=0){
  ;Windows and AHK Data Types, used to find out the corresponding size
  static _types__:="
  (LTrim Join
    ,ATOM:2,LANGID:2,WCHAR:2,WORD:2,PTR:" A_PtrSize ",UPTR:" A_PtrSize ",SHORT:2,USHORT:2,INT:4,UINT:4,INT64:8,UINT64:8,DOUBLE:8,FLOAT:4,CHAR:1,UCHAR:1,__int64:8
    ,TBYTE:" (A_IsUnicode?2:1) ",TCHAR:" (A_IsUnicode?2:1) ",HALF_PTR:" (A_PtrSize=8?4:2) ",UHALF_PTR:" (A_PtrSize=8?4:2) ",INT32:4,LONG:4,LONG32:4,LONGLONG:8
    ,LONG64:8,USN:8,HFILE:4,HRESULT:4,INT_PTR:" A_PtrSize ",LONG_PTR:" A_PtrSize ",POINTER_64:" A_PtrSize ",POINTER_SIGNED:" A_PtrSize "
    ,BOOL:4,SSIZE_T:" A_PtrSize ",WPARAM:" A_PtrSize ",BOOLEAN:1,BYTE:1,COLORREF:4,DWORD:4,DWORD32:4,LCID:4,LCTYPE:4,LGRPID:4,LRESULT:4,PBOOL:" A_PtrSize "
    ,PBOOLEAN:" A_PtrSize ",PBYTE:" A_PtrSize ",PCHAR:" A_PtrSize ",PCSTR:" A_PtrSize ",PCTSTR:" A_PtrSize ",PCWSTR:" A_PtrSize ",PDWORD:" A_PtrSize "
    ,PDWORDLONG:" A_PtrSize ",PDWORD_PTR:" A_PtrSize ",PDWORD32:" A_PtrSize ",PDWORD64:" A_PtrSize ",PFLOAT:" A_PtrSize ",PHALF_PTR:" A_PtrSize "
    ,UINT32:4,ULONG:4,ULONG32:4,DWORDLONG:8,DWORD64:8,ULONGLONG:8,ULONG64:8,DWORD_PTR:" A_PtrSize ",HACCEL:" A_PtrSize ",HANDLE:" A_PtrSize "
     ,HBITMAP:" A_PtrSize ",HBRUSH:" A_PtrSize ",HCOLORSPACE:" A_PtrSize ",HCONV:" A_PtrSize ",HCONVLIST:" A_PtrSize ",HCURSOR:" A_PtrSize ",HDC:" A_PtrSize "
     ,HDDEDATA:" A_PtrSize ",HDESK:" A_PtrSize ",HDROP:" A_PtrSize ",HDWP:" A_PtrSize ",HENHMETAFILE:" A_PtrSize ",HFONT:" A_PtrSize ",USAGE:" 2 "
   )"
  static _types_:=_types__ "
  (LTrim Join
     ,HGDIOBJ:" A_PtrSize ",HGLOBAL:" A_PtrSize ",HHOOK:" A_PtrSize ",HICON:" A_PtrSize ",HINSTANCE:" A_PtrSize ",HKEY:" A_PtrSize ",HKL:" A_PtrSize "
     ,HLOCAL:" A_PtrSize ",HMENU:" A_PtrSize ",HMETAFILE:" A_PtrSize ",HMODULE:" A_PtrSize ",HMONITOR:" A_PtrSize ",HPALETTE:" A_PtrSize ",HPEN:" A_PtrSize "
     ,HRGN:" A_PtrSize ",HRSRC:" A_PtrSize ",HSZ:" A_PtrSize ",HWINSTA:" A_PtrSize ",HWND:" A_PtrSize ",LPARAM:" A_PtrSize ",LPBOOL:" A_PtrSize ",LPBYTE:" A_PtrSize "
     ,LPCOLORREF:" A_PtrSize ",LPCSTR:" A_PtrSize ",LPCTSTR:" A_PtrSize ",LPCVOID:" A_PtrSize ",LPCWSTR:" A_PtrSize ",LPDWORD:" A_PtrSize ",LPHANDLE:" A_PtrSize "
     ,LPINT:" A_PtrSize ",LPLONG:" A_PtrSize ",LPSTR:" A_PtrSize ",LPTSTR:" A_PtrSize ",LPVOID:" A_PtrSize ",LPWORD:" A_PtrSize ",LPWSTR:" A_PtrSize "
     ,PHANDLE:" A_PtrSize ",PHKEY:" A_PtrSize ",PINT:" A_PtrSize ",PINT_PTR:" A_PtrSize ",PINT32:" A_PtrSize ",PINT64:" A_PtrSize ",PLCID:" A_PtrSize "
     ,PLONG:" A_PtrSize ",PLONGLONG:" A_PtrSize ",PLONG_PTR:" A_PtrSize ",PLONG32:" A_PtrSize ",PLONG64:" A_PtrSize ",POINTER_32:" A_PtrSize "
     ,POINTER_UNSIGNED:" A_PtrSize ",PSHORT:" A_PtrSize ",PSIZE_T:" A_PtrSize ",PSSIZE_T:" A_PtrSize ",PSTR:" A_PtrSize ",PTBYTE:" A_PtrSize "
     ,PTCHAR:" A_PtrSize ",PTSTR:" A_PtrSize ",PUCHAR:" A_PtrSize ",PUHALF_PTR:" A_PtrSize ",PUINT:" A_PtrSize ",PUINT_PTR:" A_PtrSize "
     ,PUINT32:" A_PtrSize ",PUINT64:" A_PtrSize ",PULONG:" A_PtrSize ",PULONGLONG:" A_PtrSize ",PULONG_PTR:" A_PtrSize ",PULONG32:" A_PtrSize "
     ,PULONG64:" A_PtrSize ",PUSHORT:" A_PtrSize ",PVOID:" A_PtrSize ",PWCHAR:" A_PtrSize ",PWORD:" A_PtrSize ",PWSTR:" A_PtrSize ",SC_HANDLE:" A_PtrSize "
     ,SC_LOCK:" A_PtrSize ",SERVICE_STATUS_HANDLE:" A_PtrSize ",SIZE_T:" A_PtrSize ",UINT_PTR:" A_PtrSize ",ULONG_PTR:" A_PtrSize ",VOID:" A_PtrSize "
     )"
  local _,_ArrName_:="",_ArrType_,_ArrSize_,_defobj_,_idx_,_LF_,_LF_BKP_,_match_,_offset_,_padding_,_struct_
				,_total_union_size_,_uix_,_union_,_union_size_,_in_struct_,_mod_,_max_size_,_struct_align_
	_offset_:=parent_offset           ; Init size/offset to 0 or parent_offset

  If IsObject(_TYPE_){    ; If structure object - check for offset in structure and return pointer + last offset + its data size
    return _TYPE_["`a`a"]
  }
  
  If RegExMatch(_TYPE_,"^[\w\d\._]+$"){ ; structures name was supplied, resolve to global var and run again
      If InStr(_types_,"," _TYPE_ ":")
        Return SubStr(_types_,InStr(_types_,"," _TYPE_ ":") + 2 + StrLen(_TYPE_),1)
      else If InStr(_TYPE_,"."){ ;check for object that holds structure definition
        Loop,Parse,_TYPE_,.
          If A_Index=1
            _defobj_:=%A_LoopField%
          else _defobj_:=_defobj_[A_LoopField]
        Return sizeof(_defobj_,parent_offset)
      } else Return sizeof(%_TYPE_%,parent_offset)
  } else _defobj_:=""    
  If InStr(_TYPE_,"`n") {   ; C/C++ style definition, convert
    _offset_:=""            ; This will hold new structure
    ,_struct_:=[]            ; This will keep track if union is structure
    ,_union_:=0              ; This will keep track of union depth
    Loop,Parse,_TYPE_,`n,`r`t%A_Space%%A_Tab%
    {
      _LF_:=""
      Loop,Parse,A_LoopField,`,`;,`t%A_Space%%A_Tab%
      {
        If RegExMatch(A_LoopField,"^\s*//") ;break on comments and continue main loop
            break
        If (A_LoopField){ ; skip empty lines
            If (!_LF_ && _ArrType_:=RegExMatch(A_LoopField,"[\w\d_#@]\s+[\w\d_#@]")) ; new line, find out data type and save key in _LF_ Data type will be added later
              _LF_:=RegExReplace(A_LoopField,"[\w\d_#@]\K\s+.*$")
            If Instr(A_LoopField,"{"){ ; Union, also check if it is a structure
              _union_++,_struct_.Insert(_union_,RegExMatch(A_LoopField,"i)^\s*struct\s*\{"))
            } else If InStr(A_LoopField,"}") ; end of union/struct
              _offset_.="}"
            else { ; not starting or ending struct or union so add definitions and apply Data Type.
              If _union_ ; add { or struct{
                  Loop % _union_
                    _ArrName_.=(_struct_[A_Index]?"struct":"") "{"
              _offset_.=(_offset_ ? "," : "") _ArrName_ ((_ArrType_ && A_Index!=1)?(_LF_ " "):"") RegExReplace(A_LoopField,"\s+"," ")
              ,_ArrName_:="",_union_:=0
            }
        }
      }
    }
    _TYPE_:=_offset_
    ,_offset_:=parent_offset           ; Init size/offset to 0 or parent_offset
  }
  
  ; Following keep track of union size/offset
  _union_:=[]               ; keep track of union level, required to reset offset after union is parsed
  ,_struct_:=[]              ; for each union level keep track if it is a structure (because here offset needs to increase
  ,_union_size_:=[]          ; keep track of highest member within the union or structure, used to calculate new offset after union
  ,_struct_align_:=[]        ; keep track of alignment before structure
  ,_total_union_size_:=0     ; used in combination with above, each loop the total offset is updated if current data size is higher
  ;,_align_total_:=0          ; used to calculate alignment for total size of structure
  ,_in_struct_:=1
  ; Parse given structure definition and calculate size
  ; Structures will be resolved by recrusive calls (a structure must be global)
  Loop,Parse,_TYPE_,`,`; ;,%A_Space%%A_Tab%`n`r
  {
    _in_struct_+=StrLen(A_LoopField)+1
    If ("" = _LF_ := trim(A_LoopField,A_Space A_Tab "`n`r"))
      continue
    _LF_BKP_:=_LF_ ;to check for ending brackets = union,struct
    ; Check for STARTING union and set union helpers
    While (_match_:=RegExMatch(_LF_,"i)^(struct|union)?\s*\{\K"))
      ; correct offset for union/structure, sizeof_maxsize returns max size of union or structure
        _max_size_:=sizeof_maxsize(SubStr(_TYPE_,_in_struct_-StrLen(A_LoopField)-1+(StrLen(_LF_BKP_)-StrLen(_LF_))))
        ,_union_.Insert(_offset_+=(_mod_:=Mod(_offset_,_max_size_))?Mod(_max_size_-_mod_,_max_size_):0)
        ,_union_size_.Insert(0)
        ,_struct_align_.Insert(_align_total_>_max_size_?_align_total_:_max_size_)
        ,_struct_.Insert(RegExMatch(_LF_,"i)^struct\s*\{")?(1,_align_total_:=0):0)
        ,_LF_:=SubStr(_LF_,_match_)
    StringReplace,_LF_,_LF_,},,A
    
    If InStr(_LF_,"*"){ ; It's a pointer, size will be always A_PtrSize
      _offset_ += (_mod_:=Mod(_offset_ + A_PtrSize,A_PtrSize)?A_PtrSize-_mod_:0) + A_PtrSize
      ,_align_total_:=_align_total_<A_PtrSize?A_PtrSize:_align_total_
    } else {
      ; Split array type and optionally the size of array, e.g. "TCHAR chr[5]"
      RegExMatch(_LF_,"^(?<ArrType_>[\w\d\._#@]+)?\s*(?<ArrName_>[\w\d\._#@]+)?\s*\[?(?<ArrSize_>\d+)?\]?\s*$",_)
      If (!_ArrName_ && !_ArrSize_ && !InStr( _types_  ,"," _ArrType_ ":"))
        _ArrName_:=_ArrType_,_ArrType_:="UInt"
      If InStr(_ArrType_,"."){ ;check for object that holds structure definition
        Loop,Parse,_ArrType_,.
          If A_Index=1
            _defobj_:=%A_LoopField%
          else _defobj_:=_defobj_[A_LoopField]
        ; _ArrType_:=_defobj_                                                                     ;                   ??????????????????????????????????????
      }
      If (_idx_:=InStr( _types_  ,"," _ArrType_ ":")) ; AHK or Windows data type
        _padding_:=SubStr( _types_  , _idx_+StrLen(_ArrType_)+2 , 1 ),_align_total_:=_align_total_<_padding_?_padding_:_align_total_
      else _padding_:= sizeof(_defobj_?_defobj_:%_ArrType_%,0,_align_total_),_max_size_:=sizeof_maxsize(_defobj_?_defobj_:%_ArrType_%)
      if (_max_size_){
        if (_mod_:=Mod(_offset_,_max_size_))
          _offset_ += Mod(_max_size_-_mod_,_max_size_)
      } else if _mod_:=Mod(_offset_,_padding_)
        _offset_ += Mod(_padding_-_mod_,_padding_)
      _offset_ += (_padding_ * (_ArrSize_?_ArrSize_:1))
      _max_size_:=0
    }
    ; It's a union or struct, check if new member is higher then previous members
    If (_uix_:=_union_.MaxIndex()) && (_max_size_:=_offset_ - _union_[_uix_])>_union_size_[_uix_]
      _union_size_[_uix_]:=_max_size_
    _max_size_:=0
    ; It's a union and not struct
    If (_uix_ && !_struct_[_uix_])
      _offset_:=_union_[_uix_]

    ; Check for ENDING union and reset offset and union helpers
    While (SubStr(_LF_BKP_,0)="}"){
      If !(_uix_:=_union_.MaxIndex()){
        MsgBox,0, Incorrect structure, missing opening braket {`nProgram will exit now `n%_TYPE_%
        ExitApp
      }
      ; reset offset and align because we left a union or structure
      if (_uix_>1 && _struct_[_uix_-1]){
        If (_mod_:=Mod(_offset_,_struct_align_[_uix_]))
          _offset_+=Mod(_struct_align_[_uix_]-_mod_,_struct_align_[_uix_])
      } else _offset_:=_union_[_uix_]
      ; a member of union/struct is smaller than previous align, restore
      if (_struct_[_uix_] &&_struct_align_[_uix_]>_align_total_)
        _align_total_ := _struct_align_[_uix_]
      ; Increase total size of union/structure if necessary
      _total_union_size_ := _union_size_[_uix_]>_total_union_size_?_union_size_[_uix_]:_total_union_size_
      ,_union_.Remove() ,_struct_.Remove() ,_union_size_.Remove(),_struct_align_.Remove() ; remove latest items
      ,_LF_BKP_:=SubStr(_LF_BKP_,1,StrLen(_LF_BKP_)-1)
      If (_uix_=1){ ; leaving top union, add offset
        if (_mod_:=Mod(_total_union_size_,_align_total_))
          _total_union_size_ += Mod(_align_total_-_mod_,_align_total_)
        _offset_+=_total_union_size_,_total_union_size_:=0
      }
    }
  }
  _offset_+= Mod(_align_total_ - Mod(_offset_,_align_total_),_align_total_)
  Return _offset_
}
sizeof_maxsize(s){
  static _types__:="
  (LTrim Join
    ,ATOM:2,LANGID:2,WCHAR:2,WORD:2,PTR:" A_PtrSize ",UPTR:" A_PtrSize ",SHORT:2,USHORT:2,INT:4,UINT:4,INT64:8,UINT64:8,DOUBLE:8,FLOAT:4,CHAR:1,UCHAR:1,__int64:8
    ,TBYTE:" (A_IsUnicode?2:1) ",TCHAR:" (A_IsUnicode?2:1) ",HALF_PTR:" (A_PtrSize=8?4:2) ",UHALF_PTR:" (A_PtrSize=8?4:2) ",INT32:4,LONG:4,LONG32:4,LONGLONG:8
    ,LONG64:8,USN:8,HFILE:4,HRESULT:4,INT_PTR:" A_PtrSize ",LONG_PTR:" A_PtrSize ",POINTER_64:" A_PtrSize ",POINTER_SIGNED:" A_PtrSize "
    ,BOOL:4,SSIZE_T:" A_PtrSize ",WPARAM:" A_PtrSize ",BOOLEAN:1,BYTE:1,COLORREF:4,DWORD:4,DWORD32:4,LCID:4,LCTYPE:4,LGRPID:4,LRESULT:4,PBOOL:" A_PtrSize "
    ,PBOOLEAN:" A_PtrSize ",PBYTE:" A_PtrSize ",PCHAR:" A_PtrSize ",PCSTR:" A_PtrSize ",PCTSTR:" A_PtrSize ",PCWSTR:" A_PtrSize ",PDWORD:" A_PtrSize "
    ,PDWORDLONG:" A_PtrSize ",PDWORD_PTR:" A_PtrSize ",PDWORD32:" A_PtrSize ",PDWORD64:" A_PtrSize ",PFLOAT:" A_PtrSize ",PHALF_PTR:" A_PtrSize "
    ,UINT32:4,ULONG:4,ULONG32:4,DWORDLONG:8,DWORD64:8,ULONGLONG:8,ULONG64:8,DWORD_PTR:" A_PtrSize ",HACCEL:" A_PtrSize ",HANDLE:" A_PtrSize "
     ,HBITMAP:" A_PtrSize ",HBRUSH:" A_PtrSize ",HCOLORSPACE:" A_PtrSize ",HCONV:" A_PtrSize ",HCONVLIST:" A_PtrSize ",HCURSOR:" A_PtrSize ",HDC:" A_PtrSize "
     ,HDDEDATA:" A_PtrSize ",HDESK:" A_PtrSize ",HDROP:" A_PtrSize ",HDWP:" A_PtrSize ",HENHMETAFILE:" A_PtrSize ",HFONT:" A_PtrSize ",USAGE:" 2 "
   )"
  static _types_:=_types__ "
  (LTrim Join
     ,HGDIOBJ:" A_PtrSize ",HGLOBAL:" A_PtrSize ",HHOOK:" A_PtrSize ",HICON:" A_PtrSize ",HINSTANCE:" A_PtrSize ",HKEY:" A_PtrSize ",HKL:" A_PtrSize "
     ,HLOCAL:" A_PtrSize ",HMENU:" A_PtrSize ",HMETAFILE:" A_PtrSize ",HMODULE:" A_PtrSize ",HMONITOR:" A_PtrSize ",HPALETTE:" A_PtrSize ",HPEN:" A_PtrSize "
     ,HRGN:" A_PtrSize ",HRSRC:" A_PtrSize ",HSZ:" A_PtrSize ",HWINSTA:" A_PtrSize ",HWND:" A_PtrSize ",LPARAM:" A_PtrSize ",LPBOOL:" A_PtrSize ",LPBYTE:" A_PtrSize "
     ,LPCOLORREF:" A_PtrSize ",LPCSTR:" A_PtrSize ",LPCTSTR:" A_PtrSize ",LPCVOID:" A_PtrSize ",LPCWSTR:" A_PtrSize ",LPDWORD:" A_PtrSize ",LPHANDLE:" A_PtrSize "
     ,LPINT:" A_PtrSize ",LPLONG:" A_PtrSize ",LPSTR:" A_PtrSize ",LPTSTR:" A_PtrSize ",LPVOID:" A_PtrSize ",LPWORD:" A_PtrSize ",LPWSTR:" A_PtrSize "
     ,PHANDLE:" A_PtrSize ",PHKEY:" A_PtrSize ",PINT:" A_PtrSize ",PINT_PTR:" A_PtrSize ",PINT32:" A_PtrSize ",PINT64:" A_PtrSize ",PLCID:" A_PtrSize "
     ,PLONG:" A_PtrSize ",PLONGLONG:" A_PtrSize ",PLONG_PTR:" A_PtrSize ",PLONG32:" A_PtrSize ",PLONG64:" A_PtrSize ",POINTER_32:" A_PtrSize "
     ,POINTER_UNSIGNED:" A_PtrSize ",PSHORT:" A_PtrSize ",PSIZE_T:" A_PtrSize ",PSSIZE_T:" A_PtrSize ",PSTR:" A_PtrSize ",PTBYTE:" A_PtrSize "
     ,PTCHAR:" A_PtrSize ",PTSTR:" A_PtrSize ",PUCHAR:" A_PtrSize ",PUHALF_PTR:" A_PtrSize ",PUINT:" A_PtrSize ",PUINT_PTR:" A_PtrSize "
     ,PUINT32:" A_PtrSize ",PUINT64:" A_PtrSize ",PULONG:" A_PtrSize ",PULONGLONG:" A_PtrSize ",PULONG_PTR:" A_PtrSize ",PULONG32:" A_PtrSize "
     ,PULONG64:" A_PtrSize ",PUSHORT:" A_PtrSize ",PVOID:" A_PtrSize ",PWCHAR:" A_PtrSize ",PWORD:" A_PtrSize ",PWSTR:" A_PtrSize ",SC_HANDLE:" A_PtrSize "
     ,SC_LOCK:" A_PtrSize ",SERVICE_STATUS_HANDLE:" A_PtrSize ",SIZE_T:" A_PtrSize ",UINT_PTR:" A_PtrSize ",ULONG_PTR:" A_PtrSize ",VOID:" A_PtrSize "
     )"
  max:=0,i:=0
  s:=trim(s,"`n`r`t ")
  If InStr(s,"}"){
    Loop,Parse,s
      if (A_LoopField="{")
        i++
      else if (A_LoopField="}"){
        if --i<1{
          end:=A_Index
          break
        }
      }
    if end
      s:=SubStr(s,1,end)
  }
  Loop,Parse,s,`n,`r
  {
    _struct_:=(i:=InStr(A_LoopField," //"))?SubStr(A_LoopField,1,i):A_LoopField
    Loop,Parse,_struct_,`;`,{},%A_Space%%A_Tab%
      if A_LoopField&&!InStr(".union.struct.","." A_LoopField ".")
        if (!InStr(A_LoopField,A_Tab)&&!InStr(A_LoopField," "))
          max:=max<4?4:max
        else if (sizeof(A_LoopField,0,size:=0) && max<size)
          max:=size
  }
  return max
}

;end of lib

; start of Preferences File.ahk
;These functions and labels are related to the preferences file

MaybeWriteHelperWindowPos()
{
   global g_XY
   global g_XYSaved
   ;Update the Helper Window Position
   IfEqual, g_XYSaved, 1
   {
      IfNotEqual, g_XY, 
         IniWrite, %g_XY%, %A_ScriptDir%\LastState.ini, HelperWindow, XY
   }
   Return
}

;------------------------------------------------------------------------

ReadPreferences(RestoreDefaults = false,RestorePreferences = false)
{
   global dft_IncludeProgramExecutables
   global dft_IncludeProgramTitles
   global dft_ExcludeProgramExecutables
   global dft_ExcludeProgramTitles
   global dft_Length
   global dft_NumPresses
   global dft_LearnMode
   global dft_LearnCount
   global dft_LearnLength
   global dft_DoNotLearnStrings
   global dft_ArrowKeyMethod
   global dft_DisabledAutoCompleteKeys
   global dft_DetectMouseClickMove
   global dft_NoBackSpace
   global dft_AutoSpace
   global dft_ShowLearnedFirst
   global dft_SuppressMatchingWord
   global dft_SendMethod
   global dft_TerminatingCharacters
   global dft_ForceNewWordCharacters
   global dft_EndWordCharacters
   global dft_ListBoxOffSet
   global dft_ListBoxFontFixed
   global dft_ListBoxFontOverride
   global dft_ListBoxFontSize
   global dft_ListBoxCharacterWidth
   global dft_ListBoxMaxWidth
   global dft_ListBoxOpacity
   global dft_ListBoxRows
   global dft_HelperWindowProgramExecutables
   global dft_HelperWindowProgramTitles
   
   global prefs_IncludeProgramExecutables
   global prefs_IncludeProgramTitles
   global prefs_ExcludeProgramExecutables
   global prefs_ExcludeProgramTitles
   global prefs_Length
   global prefs_NumPresses
   global prefs_LearnMode
   global prefs_LearnCount
   global prefs_LearnLength
   global prefs_DoNotLearnStrings
   global prefs_ArrowKeyMethod
   global prefs_DisabledAutoCompleteKeys
   global prefs_DetectMouseClickMove
   global prefs_NoBackSpace
   global prefs_AutoSpace
   global prefs_ShowLearnedFirst
   global prefs_SuppressMatchingWord
   global prefs_SendMethod
   global prefs_TerminatingCharacters
   global prefs_ForceNewWordCharacters
   global prefs_EndWordCharacters
   global prefs_ListBoxOffset
   global prefs_ListBoxFontFixed
   global prefs_ListBoxFontOverride
   global prefs_ListBoxFontSize
   global prefs_ListBoxCharacterWidth
   global prefs_ListBoxMaxWidth
   global prefs_ListBoxOpacity
   global prefs_ListBoxRows
   global prefs_HelperWindowProgramExecutables
   global prefs_HelperWindowProgramTitles
   
   ;g_PrefsFile is global so it works in Settings.ahk
   global g_PrefsFile
   global g_PrefsSections
   global g_XY
   
   g_PrefsFile = %A_ScriptDir%\Preferences.ini
   Defaults = %A_ScriptDir%\Defaults.ini
   LastState = %A_ScriptDir%\LastState.ini
   
   MaybeFixFileEncoding(g_PrefsFile,"UTF-16")
   MaybeFixFileEncoding(Defaults,"UTF-16")
   MaybeFixFileEncoding(LastState,"UTF-16")
   
   dft_TerminatingCharacters = {enter}{space}{esc}{tab}{Home}{End}{PgUp}{PgDn}{Up}{Down}{Left}{Right}.;`,¿?¡!'"()[]{}{}}{{}``~`%$&*-+=\/><^|@#:
   
   
   ; There was a bug in TypingAid 2.19.7 that broke terminating characters for new preference files, this code repairs it
   BrokenTerminatingCharacters = {enter}{space}{esc}{tab}{Home}{End}{PgUp}{PgDn}{Up}{Down}{Left}{Right}.;
   IfExist, %g_PrefsFile%
   {
      IniRead, MaybeFixTerminatingCharacters, %g_PrefsFile%, Settings, TerminatingCharacters, %A_Space%
      IF (MaybeFixTerminatingCharacters == BrokenTerminatingCharacters)
      {
         IniWrite, %dft_TerminatingCharacters%, %g_PrefsFile%, Settings, TerminatingCharacters
      }
   }      
   
   SpaceVar := "%A_Space%"
   
   IniValues =
   (
      dft_IncludeProgramExecutables,prefs_IncludeProgramExecutables,IncludePrograms,%SpaceVar%
      dft_IncludeProgramTitles,prefs_IncludeProgramTitles,IncludePrograms,%SpaceVar%
      dft_ExcludeProgramExecutables,prefs_ExcludeProgramExecutables,ExcludePrograms,%SpaceVar%
      dft_ExcludeProgramTitles,prefs_ExcludeProgramTitles,ExcludePrograms,%SpaceVar%
      ,Title,Settings,%SpaceVar%
      dft_Length,prefs_Length,Settings,3
      dft_NumPresses,prefs_NumPresses,Settings,1
      dft_LearnMode,prefs_LearnMode,Settings,On
      dft_LearnCount,prefs_LearnCount,Settings,5
      dft_LearnLength,prefs_LearnLength,Settings,%SpaceVar%
      dft_DoNotLearnStrings,prefs_DoNotLearnStrings,Settings,%SpaceVar%
      dft_ArrowKeyMethod,prefs_ArrowKeyMethod,Settings,First
      dft_DisabledAutoCompleteKeys,prefs_DisabledAutoCompleteKeys,Settings,%SpaceVar%
      dft_DetectMouseClickMove,prefs_DetectMouseClickMove,Settings,On
      dft_NoBackSpace,prefs_NoBackSpace,Settings,On
      dft_AutoSpace,prefs_AutoSpace,Settings,Off
      dft_ShowLearnedFirst,prefs_ShowLearnedFirst,Settings,Off
      dft_SuppressMatchingWord,prefs_SuppressMatchingWord,Settings,Off
      dft_SendMethod,prefs_SendMethod,Settings,1
      dft_TerminatingCharacters,prefs_TerminatingCharacters,Settings,`%dft_TerminatingCharacters`%
      dft_ForceNewWordCharacters,prefs_ForceNewWordCharacters,Settings,%SpaceVar%
      dft_EndWordCharacters,prefs_EndWordCharacters,Settings,%SpaceVar%
      dft_ListBoxOffSet,prefs_ListBoxOffset,ListBoxSettings,14
      dft_ListBoxFontFixed,prefs_ListBoxFontFixed,ListBoxSettings,Off
      dft_ListBoxFontOverride,prefs_ListBoxFontOverride,ListBoxSettings,<Default>
      dft_ListBoxFontSize,prefs_ListBoxFontSize,ListBoxSettings,10 
      dft_ListBoxCharacterWidth,prefs_ListBoxCharacterWidth,ListBoxSettings,%SpaceVar%
      dft_ListBoxMaxWidth,prefs_ListBoxMaxWidth,ListBoxSettings,%SpaceVar%
      dft_ListBoxOpacity,prefs_ListBoxOpacity,ListBoxSettings,215
      dft_ListBoxRows,prefs_ListBoxRows,ListBoxSettings,10
      dft_HelperWindowProgramExecutables,prefs_HelperWindowProgramExecutables,HelperWindow,%SpaceVar%
      dft_HelperWindowProgramTitles,prefs_HelperWindowProgramTitles,HelperWindow,%SpaceVar%
      ,XY,HelperWindow,%SpaceVar%
   )
   
   g_PrefsSections := Object()
    
   Loop, Parse, IniValues, `n, `r%A_Space%
   {
      StringSplit, CurrentIniValues, A_LoopField, `,
      DftVariable := CurrentIniValues1
      NormalVariable := CurrentIniValues2
      IniSection := CurrentIniValues3
      DftValue := CurrentIniValues4
      ; maybe strip "prefs_" prefix
      if (substr(NormalVariable, 1, 6) == "prefs_")
      {
         StringTrimLeft, KeyName, NormalVariable, 6
      } else {
         KeyName := NormalVariable
      }
      
      g_PrefsSections[KeyName] := IniSection
      
      ; this is done because certain characters can break the parsing (comma, for example)
      IF (DftValue == "%dft_TerminatingCharacters%")
      {
         DftValue := dft_TerminatingCharacters
      }

      IF ( DftValue = "%A_Space%" )
         DftValue := A_Space
      
      IF !(RestoreDefaults)
         IniRead, %NormalVariable%, %g_PrefsFile%, %IniSection%, %KeyName%, %A_Space%
      
      IF DftVariable
      { 
         IniRead, %DftVariable%, %Defaults%, %IniSection%, %KeyName%, %DftValue%
         IF (RestoreDefaults || %NormalVariable% == "")
         {
            %NormalVariable% := %DftVariable%
         }
      }
   }
   
   ValidatePreferences()
   ParseTerminatingCharacters()
   
   ; Legacy support for old Preferences File
   IfNotEqual, Etitle,
   {
      IfEqual, prefs_IncludeProgramTitles,
      {
         prefs_IncludeProgramTitles = %Etitle%
      } else {
         prefs_IncludeProgramTitles .= "|" . Etitle
      }
      
      Etitle=      
   }
   
   g_XY := XY
   
   IF ( RestoreDefaults || RestorePreferences )
      Return
   
   IfExist, %LastState%
   {    
      IniRead, g_XY, %LastState%, HelperWindow, XY, %A_Space%
   }
   
   ConstructHelpStrings()
         
   Return
}

ValidatePreferences()
{
   global g_ListBoxCharacterWidthComputed, g_NumKeyMethod
   global prefs_ArrowKeyMethod, prefs_DisabledAutoCompleteKeys
   global dft_ArrowKeyMethod
   global prefs_AutoSpace, prefs_DetectMouseClickMove, prefs_LearnCount, prefs_LearnLength, prefs_LearnMode, prefs_Length
   global dft_AutoSpace, dft_DetectMouseClickMove, dft_LearnCount, dft_LearnLength, dft_LearnMode, dft_Length
   global prefs_ListBoxCharacterWidth, prefs_ListBoxFontFixed, prefs_ListBoxFontSize, prefs_ListBoxMaxWidth, prefs_ListBoxOffset, prefs_ListBoxOpacity, prefs_ListBoxRows
   global dft_ListBoxCharacterWidth, dft_ListBoxFontFixed, dft_ListBoxFontSize, dft_ListBoxMaxWidth, dft_ListBoxOffset, dft_ListBoxOpacity, dft_ListBoxRows
   global prefs_NoBackSpace, prefs_NumPresses, prefs_SendMethod, prefs_ShowLearnedFirst, prefs_SuppressMatchingWord, prefs_TerminatingCharacters
   global dft_NoBackSpace, dft_NumPresses, dft_SendMethod, dft_ShowLearnedFirst, dft_SuppressMatchingWord, dft_TerminatingCharacters
   
   if prefs_Length is not integer
   {
      prefs_Length := dft_Length
   }
   
   if (prefs_Length < 1) {
      prefs_Length = 1
   }
   
   if prefs_NumPresses not in 1,2
      prefs_NumPresses := dft_NumPresses
   
   If prefs_LearnMode not in On,Off
      prefs_LearnMode := dft_LearnMode
   
   If prefs_LearnCount is not Integer
   {
      prefs_LearnCount := dft_LearnCount
   }
   
   if (prefs_LearnCount < 1)
   {
      prefs_LearnCount = 1
   }
   
   if dft_LearnLength is not Integer
   {
      dft_LearnLength := prefs_Length + 2
   }
   
   if prefs_LearnLength is not Integer
   {
      prefs_LearnLength := dft_LearnLength
   } else If ( prefs_LearnLength < ( prefs_Length + 1 ) )
   {
      prefs_LearnLength := prefs_Length + 1
   }
   
   if prefs_DisabledAutoCompleteKeys contains N
   {
      g_NumKeyMethod = Off
   } else {
      g_NumKeyMethod = On
   }
   
   IfNotEqual, prefs_ArrowKeyMethod, Off
      If prefs_DisabledAutoCompleteKeys contains E
         If prefs_DisabledAutoCompleteKeys contains S
            If prefs_DisabledAutoCompleteKeys contains T
               If prefs_DisabledAutoCompleteKeys contains R
                  If prefs_DisabledAutoCompleteKeys contains U
                     If prefs_DisabledAutoCompleteKeys contains M
                        prefs_ArrowKeyMethod = Off
   
   If prefs_ArrowKeyMethod not in First,Off,LastWord,LastPosition
   {
      prefs_ArrowKeyMethod := dft_ArrowKeyMethod
   }
   
   If prefs_DetectMouseClickMove not in On,Off
      prefs_DetectMouseClickMove := dft_DetectMouseClickMove
   
   If prefs_NoBackSpace not in On,Off
      prefs_NoBackSpace := dft_NoBackSpace
      
   If prefs_AutoSpace not in On,Off
      prefs_AutoSpace := dft_AutoSpace
   
   if prefs_ShowLearnedFirst not in On,Off
      prefs_ShowLearnedFirst := dft_ShowLearnedFirst
   
   if prefs_SuppressMatchingWord not in On,Off
      prefs_SuppressMatchingWord := dft_SuppressMatchingWord
   
   if prefs_SendMethod not in 1,2,3,1C,2C,3C,4C
      prefs_SendMethod := dft_SendMethod
   
   ;SendPlay does not work when not running as Administrator, switch to SendInput
   If not A_IsAdmin
   {
      IfEqual, prefs_SendMethod, 1
      {
         prefs_SendMethod = 2
      } else IfEqual, prefs_SendMethod, 1C
      {
         prefs_SendMethod = 2C   
      }
   }
   
   IfEqual, prefs_TerminatingCharacters,
      prefs_TerminatingCharacters := dft_TerminatingCharacters
   
   if prefs_ListBoxOffset is not Integer
      prefs_ListBoxOffset := dft_ListBoxOffSet
      
   if prefs_ListBoxFontFixed not in On,Off
      prefs_ListBoxFontFixed := dft_ListBoxFontFixed
   
   If prefs_ListBoxFontSize is not Integer
   {
      prefs_ListBoxFontSize := dft_ListBoxFontSize
   }
   else IfLess, prefs_ListBoxFontSize, 2
   {
      prefs_ListBoxFontSize = 2
   }
   
   if dft_ListBoxCharacterWidth is not Integer
   {
      dft_ListBoxCharacterWidth =
   }
   
   if prefs_ListBoxCharacterWidth is not Integer
   {
      prefs_ListBoxCharacterWidth := dft_ListBoxCharacterWidth
   }
   
   if prefs_ListBoxCharacterWidth is Integer
   {
      g_ListBoxCharacterWidthComputed := prefs_ListBoxCharacterWidth
   } else {
      g_ListBoxCharacterWidthComputed := Ceil(prefs_ListBoxFontSize * 0.8)
   }
   
   if dft_ListBoxMaxWidth is not Integer
   {
      dft_ListBoxMaxWidth =
   }
   
   if prefs_ListBoxMaxWidth is not Integer
   {
      prefs_ListBoxMaxWidth := dft_ListBoxMaxWidth
   }
   
   if !prefs_ListBoxMaxWidth
   {
      ; skip out
   } else if prefs_ListBoxMaxWidth is Integer
   {
      IfLess, prefs_ListBoxMaxWidth, 100
         prefs_ListBoxMaxWidth = 100
   }
      
   If prefs_ListBoxOpacity is not Integer
      prefs_ListBoxOpacity := dft_ListBoxOpacity
   
   IfLess, prefs_ListBoxOpacity, 0
      prefs_ListBoxOpacity = 0
   else IfGreater, prefs_ListBoxOpacity, 255
      prefs_ListBoxOpacity = 255
                  
   If prefs_ListBoxRows is not Integer
      prefs_ListBoxRows := dft_ListBoxRows
   
   IfLess, prefs_ListBoxRows, 3
      prefs_ListBoxRows = 3
   else IfGreater, prefs_ListBoxRows, 30
      prefs_ListBoxRows = 30
            
   Return
}

ParseTerminatingCharacters()
{
   global prefs_TerminatingCharacters
   global g_TerminatingCharactersParsed
   global g_TerminatingEndKeys
   
   Loop, Parse, prefs_TerminatingCharacters
   {
      IfEqual, OpenWord, 1
      {
         If ( A_LoopField == "}" )
         {
            OpenWord =
            IF !(Word)
               TempCharacters .= "{}"
            else If ( Word = "{" || Word = "}")
               TempCharacters .= Word
            else
               TempEndKeys .= "{" . Word . "}"
            
            Word =
         } else 
         {
            Word .= A_LoopField
         }
      } else if ( A_LoopField  == "{" )
      {
         OpenWord = 1
      } else
      {
         TempCharacters .= A_LoopField
      }
   }
      
   IfNotEqual, Word,
      TempCharacters .= Word
   
   g_TerminatingCharactersParsed := TempCharacters
   g_TerminatingEndKeys := TempEndKeys
}

SavePreferences(PrefsToSave)
{
   global
   local index
   local element
   local KeyName
   local PrefsExist
   
   ValidatePreferences()
   
   IfExist, %g_PrefsFile%
   {
      PrefsExist := true
   } else {
      PrefsExist := false
   }
      
   for index, element in PrefsToSave
   {
      if (substr(element, 1, 6) == "prefs_")
      {
         StringTrimLeft, KeyName, element, 6
      } else {
         KeyName := element
      }
   
      If (%element% == dft_%KeyName%)
      {
         ; Make sure preferences already exist so we don't create 0 byte file
         if (PrefsExist == true)
         {
            IniDelete, %g_PrefsFile%,% g_PrefsSections[KeyName], %KeyName%
         }
      } else {
         IniWrite,% %element%, %g_PrefsFile%,% g_PrefsSections[KeyName], %KeyName%
      }
   }
   
   Return
}

ConstructHelpStrings()
{
   global

helpinfo_LearnMode=
(
;"Learn new words as you type" defines whether or not the script should learn new words as you type them, either On or Off.
)

helpinfo_LearnLength=
(
;"Minimum length of words to learn" is the minimum number of characters in a word for it to be learned. This must be at least Length+1.
)

helpinfo_LearnCount=
(
;"Add to wordlist after X times" defines the number of times you have to type a word within a single session for it to be learned permanently.
)

helpinfo_ListBoxRows=
(
;"Maximum number of results to show" is the maximum number of rows to show in the ListBox. This value can range from 3 to 30.
)

helpinfo_Length=
(
;"Show wordlist after X characters" is the minimum number of characters that need to be typed before the program shows a List of words.
;For example, if you need to autocomplete "assemble" in the word list, set this to 2, type 'as' and a list will appear.
)

helpinfo_SendMethod=
(
;"Send Method" is used to change the way the program sends the keys to the screen, this is included for compatibility reasons.
;Try changing this only when you encounter a problem with key sending during autocompletion.
;  1 = Fast method that reliably buffers key hits while sending. HAS BEEN KNOWN TO NOT FUNCTION ON SOME MACHINES.
;      If the script detects that this method will not work on the machine, it will switch to method 2.
;      (Might not work with characters that cannot be typed using the current keyboard layout.)
;  2 = Fastest method with unreliable keyboard buffering while sending. Has been known to not function on some machines.
;  3 = Slowest method, will not buffer or accept keyboard input while sending. Most compatible method.
;The options below use the clipboard to copy and paste the data to improve speed, but will leave an entry in any clipboard 
;history tracking routines you may be running. Data on the clipboard *will* be preserved prior to autocompletion.
;  4 = Same as 1 above.
;  5 = Same as 2 above, doesn't work on some machines.
;  6 = Same as 3 above.
;  7 = Alternate method.
)

helpinfo_DisabledAutoCompleteKeys=
(
;"Auto Complete Keys" is used to enable or disable hotkeys for autocompleting the selected item in the list.
)

helpinfo_ArrowKeyMethod=
(
;"Wordlist row highlighting" is the way the arrow keys are handled when a list is shown.
;Options are:
;  Off - only use the number keys
;  First - resets the highlighted row to the beginning whenever you type a new character
;  LastWord - keeps the highlighted row on the prior selected word if it's still in the list, else resets to the beginning
;  LastPosition - maintains the highlighted row's position
)

helpinfo_NoBackSpace=
(
;"Case correction" is used to correct the case of any previously typed characters.
;  On - characters you have already typed will be backspaced and replaced with the case of the word you have chosen.
;  Off - characters you have already typed will not be changed
)

helpinfo_DetectMouseClickMove=
(
;"Monitor mouse clicks" is used to detect when the cursor is moved with the mouse.
; On - %g_ScriptTitle% will not work when used with an On-Screen keyboard.
; Off - %g_ScriptTitle% will not detect when the cursor is moved within the same line using the mouse, and scrolling the text will clear the list.
)

helpinfo_AutoSpace=
(
;"Type space after autocomplete" is used to automatically add a space to the end of an autocompleted word.
; On - Add a space to the end of the autocompleted word.
; Off - Do not add a space to the end of the autocompleted word.
)

helpinfo_DoNotLearnStrings=
(
;"Sub-strings to not learn" is a comma separated list of strings. Any words which contain any of these strings will not be learned.
;This can be used to prevent the program from learning passwords or other critical information.
;For example, if you have ord98 in "Sub-strings to not learn", password987 will not be learned.
)

helpinfo_SuppressMatchingWord=
(
;"Suppress matching word" is used to suppress a word from the Word list if it matches the typed word.
;  If "Case correction" is On, then the match is case-sensitive.
;  If "Case correction" is Off, then the match is case in-sensitive.
; On - Suppress matching word from the word list.
; Off - Do not suppress matching word from the word list.
)

helpinfo_NumPresses=
(
;"Number of presses" is the number of times the number hotkey must be tapped for the word to be selected, either 1 or 2.
)

helpinfo_ShowLearnedFirst=
(
;"Show learned words first" controls whether the learned words appear before or after the words from Wordlist.txt.
)

helpinfo_ListBoxOffset=
(
;"List appears X pixels below cursor" is the number of pixels below the top of the caret (vertical blinking line) to display the list.
)

helpinfo_ListBoxFontFixed=
(
;"Fixed width font in list" controls whether a fixed or variable character font width is used.
;(e.g., in fixed width, "i" and "w" take the same number of pixels)
)

helpinfo_ListBoxFontSize=
(
;"Font size in list" controls the size of the font in the list.
)

helpinfo_ListBoxOpacity=
(
;"list opacity" is how transparent (see-through) the Wordlist Box should be. Use a value of 255 to make it so the
;Wordlist Box is fully ypaque, or use a value of 0 to make it so the Wordlist Box cannot be seen at all.
)

helpinfo_ListBoxCharacterWidth=
(
;"List character width override" is the width (in pixels) of one character in the Wordlist Box.
;This number should only need to be changed if the box containing the list is not the correct width.
;Some things which may cause this to need to be changed would include:
; 1. Changing the Font DPI in Windows
; 2. Changing the "Fixed width font in list" setting
; 3. Changing the "Font size in list" setting
;Leave this blank to let %g_ScriptTitle% try to compute the width.
)

helpinfo_ListBoxFontOverride=
(
;"List font" is used to specify a font for the Wordlist Box to use. The default for Fixed is Courier,
;and the default for Variable is Tahoma.
)

helpinfo_ListBoxMaxWidth=
(
;"List max width in pixels" is used to specify the maximum width for the Wordlist Box in pixels. By default, this will not expand beyond the width of the current monitor.
)

helpinfo_IncludeProgramTitles=
(
;"Window titles you want %g_ScriptTitle% enabled for" is a list of strings (separated by | ) to find in the title of the window you want %g_ScriptTitle% enabled for.
;If one of the strings is found in the title, %g_ScriptTitle% is enabled for that window.
)

helpinfo_ExcludeProgramTitles=
(
;"Window titles you want %g_ScriptTitle% disabled for" is a list of strings (separated by | ) to find in the title of the window you want %g_ScriptTitle% disabled for.
;If one of the strings is found in the title, %g_ScriptTitle% is disabled for that window.
)
   
helpinfo_IncludeProgramExecutables=
(
;"Processes you want %g_ScriptTitle% enabled for" is a list of executable (.exe) files that %g_ScriptTitle% should be enabled for.
;If one of the executables matches the current program, %g_ScriptTitle% is enabled for that program.
)

helpinfo_ExcludeProgramExecutables=
(
;"Processes you want %g_ScriptTitle% disabled for" is a list of executable (.exe) files that %g_ScriptTitle% should be disabled for.
;If one of the executables matches the current program, %g_ScriptTitle% is disabled for that program.
)

helpinfo_HelperWindowProgramTitles=
(
;"Window titles you want the helper window enabled for" is a list of strings (separated by | ) to find in the title of the window that the helper window should be automatically enabled for.
;If one of the strings is found in the title, the helper window will pop up automatically for that program.
)

helpinfo_HelperWindowProgramExecutables=
(
;"Processes you want the helper window enabled for" is a list of executable (.exe) files that the helper window should be automatically enabled for.
;If one of the executables matches the current program, the helper window will pop up automatically for that program.
)

helpinfo_TerminatingCharacters=
(
;"Terminating Characters" is a list of characters (EndKey) which will signal the program that you are done typing a word.
;You probably need to change this only when using this with certain programming languages.
;
;Default setting:
;%dft_TerminatingCharacters%
;
; More information on how to configure "Terminating Characters":
;A list of keys may be found here:
; http://www.autohotkey.com/docs/KeyList.htm
;For more details on how to format the list of characters please see the EndKeys section (paragraphs 2,3,4) of:
; http://www.autohotkey.com/docs/commands/Input.htm
)

helpinfo_ForceNewWordCharacters=
(
;"Force New Word Characters" is a comma separated list of characters which forces the program to start a new word whenever
;one of those characters is typed. Any words which begin with one of these characters will never be learned (even
;if learning is enabled). If you were typing a word when you hit one of these characters that word will be learned
;if learning is enabled. Characters in "Force New Word Characters" should not be in "Terminating Characters".
;Change this only if you know what you are doing, it is probably only useful for certain programming languages.
; ex: ForceNewWordCharacters=@,:,#
))

helpinfo_EndWordCharacters=
(
;"End Word Characters" is a comma separated list of characters which forces the program to start end the current
;word whenever one of those characters is typed. Unlike "Terminating Characters", the character becomes the last
;character in the typed word. If you were typing a word when you hit one of these characters that word will be 
;permanently learned. Characters in "Force New Word Characters" should not be in "Terminating Characters".
;Change this only if you know what you are doing, it is probably only useful for certain programming languages.
; ex: EndWordCharacters=@,:,#
)

helpinfo_FullHelpString =
(
%helpinfo_LearnMode%`r`n`r`n%helpinfo_LearnLength%`r`n`r`n%helpinfo_LearnCount%

%helpinfo_DoNotLearnStrings%`r`n`r`n%helpinfo_NumPresses%

%helpinfo_DisabledAutoCompleteKeys%`r`n`r`n%helpinfo_SendMethod%

%helpinfo_NoBackSpace%`r`n`r`n%helpinfo_DetectMouseClickMove%`r`n`r`n%helpinfo_AutoSpace%

%helpinfo_ListBoxRows%`r`n`r`n%helpinfo_Length%`r`n`r`n%helpinfo_ShowLearnedFirst%

%helpinfo_ArrowKeyMethod%`r`n`r`n%helpinfo_SuppressMatchingWord%

%helpinfo_ListBoxOffset%`r`n`r`n%helpinfo_ListBoxFontFixed%`r`n`r`n%helpinfo_ListBoxFontSize%

%helpinfo_ListBoxOpacity%`r`n`r`n%helpinfo_ListBoxCharacterWidth%`r`n`r`n%helpinfo_ListBoxFontOverride%

%helpinfo_ListBoxMaxWidth%

%helpinfo_IncludeProgramTitles%`r`n`r`n%helpinfo_ExcludeProgramTitles%`r`n`r`n%helpinfo_IncludeProgramExecutables%`r`n`r`n%helpinfo_ExcludeProgramExecutables%

%helpinfo_HelperWindowProgramTitles%`r`n`r`n%helpinfo_HelperWindowProgramExecutables%

%helpinfo_TerminatingCharacters%`r`n`r`n%helpinfo_ForceNewWordCharacters%`r`n`r`n%helpinfo_EndWordCharacters%
)

}

; end of Preferences File.ahk

; start of Sending.ahk
; These functions and labels are related to sending the word to the program

SendKey(Key)
{
   IfEqual, Key, $^Enter
   {
      Key = ^{Enter}
   } else IfEqual, Key, $^Space
   { 
      Key = ^{Space}
   } else {
      Key := "{" . SubStr(Key, 2) . "}"
   }
   
   SendCompatible(Key,1)
   Return
}

;------------------------------------------------------------------------
   
SendWord(WordIndex)
{
   global g_SingleMatch
   global g_SingleMatchReplacement
   ;Send the word
   if (g_SingleMatchReplacement[WordIndex])
   {
      sending := g_SingleMatchReplacement[WordIndex]
      ForceBackspace := true
   } else {
      sending := g_SingleMatch[WordIndex]
      ForceBackspace := false
   }

   SendFull(sending, ForceBackspace)   
   ClearAllVars(true)
   Return
}  

;------------------------------------------------------------------------
            
SendFull(SendValue,ForceBackspace=false)
{
   global g_Active_Id
   global g_Word
   global prefs_AutoSpace
   global prefs_NoBackSpace
   global prefs_SendMethod
   
   SwitchOffListBoxIfActive()
   
   BackSpaceLen := StrLen(g_Word)
   
   if (ForceBackspace || prefs_NoBackspace = "Off") {
      BackSpaceWord := true
   }
   
   ; capitalize first letter if we are forcing a backspace AND CaseCorrection is off
/*    
   if (ForceBackspace && !(prefs_NoBackspace = "Off")) {
      IfEqual, A_IsUnicode, 1
      {     
         if ( RegExMatch(Substr(g_Word, 1, 1), "S)\p{Lu}") > 0 )  
         {
            Capitalize := true
         }
      } else if ( RegExMatch(Substr(g_Word, 1, 1), "S)[A-ZР-жи-п]") > 0 )
      {
         Capitalize := true
      }
      
      StringLeft, FirstLetter, SendValue, 1
         StringTrimLeft, SendValue, SendValue, 1
      if (Capitalize) {
         StringUpper, FirstLetter, FirstLetter
      } else {
         StringLower, FirstLetter, FirstLetter
      }
      SendValue := FirstLetter . SendValue
   }
    *
   ; If we are not backspacing, remove the typed characters from the string to send
   if !(BackSpaceWord)
   {
      StringTrimLeft, SendValue, SendValue, %BackSpaceLen%
   }
   
   ; if autospace is on, add a space to the string to send
   IfEqual, prefs_AutoSpace, On
      SendValue .= A_Space
   ;p(SendValue)
   */
   IfEqual, prefs_SendMethod, 1
   {
      ; Shift key hits are here to account for an occassional bug which misses the first keys in SendPlay
      sending = {Shift Down}{Shift Up}{Shift Down}{Shift Up}      
      if (BackSpaceWord)
      {
         sending .= "{BS " . BackSpaceLen . "}"
      }
      sending .= "{Raw}" . SendValue
         p(sending)
         
      SendPlay, %sending% ; First do the backspaces, Then send word (Raw because we want the string exactly as in wordlist.txt) 
      Return
   }

   if (BackSpaceWord)
   {

      sending = {BS %BackSpaceLen%}{Raw}%SendValue%
   } Else {
      sending = {Raw}%SendValue%
   }
   
   IfEqual, prefs_SendMethod, 2
   {
      SendInput, %sending% ; First do the backspaces, Then send word (Raw because we want the string exactly as in wordlist.txt)      
      Return
   }

   IfEqual, prefs_SendMethod, 3
   {
      SendEvent, %sending% ; First do the backspaces, Then send word (Raw because we want the string exactly as in wordlist.txt) 
      Return
   }
   
   ClipboardSave := ClipboardAll
   Clipboard = 
   Clipboard := SendValue
   ClipWait, 0
   
   if (BackSpaceWord)
   {
      sending = {BS %BackSpaceLen%}{Ctrl Down}v{Ctrl Up}
   } else {
   sending = {Ctrl Down}v{Ctrl Up}
   }
   
   IfEqual, prefs_SendMethod, 1C
   {
      sending := "{Shift Down}{Shift Up}{Shift Down}{Shift Up}" . sending
      SendPlay, %sending% ; First do the backspaces, Then send word via clipboard
   } else IfEqual, prefs_SendMethod, 2C
   {
      SendInput, %sending% ; First do the backspaces, Then send word via clipboard
   } else IfEqual, prefs_SendMethod, 3C
   {
      SendEvent, %sending% ; First do the backspaces, Then send word via clipboard
   } else {
      ControlGetFocus, ActiveControl, ahk_id %g_Active_Id%
      IfNotEqual, ActiveControl,
         ControlSend, %ActiveControl%, %sending%, ahk_id %g_Active_Id%
   }
         
   Clipboard := ClipboardSave
   Return
}

;------------------------------------------------------------------------

SendCompatible(SendValue,ForceSendForInput)
{
   global g_IgnoreSend
   global prefs_SendMethod
   IfEqual, ForceSendForInput, 1
   {
      g_IgnoreSend = 
      SendEvent, %SendValue%
      Return
   }
   
   SendMethodLocal := SubStr(prefs_SendMethod, 1, 1)
   IF ( ( SendMethodLocal = 1 ) || ( SendMethodLocal = 2 ) )
   {
      SendInput, %SendValue%
      Return
   }

   IF ( ( SendMethodLocal = 3 ) || ( SendMethodLocal = 4 ) )
   {
      g_IgnoreSend = 1
      SendEvent, %SendValue%
      Return
   }
   
   SendInput, %SendValue%   
   Return
}

;------------------------------------------------------------------------
; end of Sending.ahk

; start of Settings.ahk
; GUI for TypingAid configuration
; by HugoV / Maniac

LaunchSettings:
if (g_InSettings == true)
{
   return
}
InactivateAll()
Menu, Tray, Disable, Settings
g_InSettings := true
ClearAllVars(True)
Menu_OldLearnCount := prefs_LearnCount
; initialize this to make sure the object exists
Menu_ChangedPrefs := Object()
ConstructGui()
; Call "HandleMessage" when script receives WM_SETCURSOR message
OnMessage(g_WM_SETCURSOR, "HandleSettingsMessage")
; Call "HandleMessage" when script receives WM_MOUSEMOVE message
OnMessage(g_WM_MOUSEMOVE, "HandleSettingsMessage")
; clear and re-initialize variables after constructing the GUI as some controls call the edit flag immediately
Menu_ChangedPrefs =
Menu_ChangedPrefs := Object()
Menu_ValueChanged := false
Return

ConstructGui()
{
   global prefs_ArrowKeyMethod, prefs_AutoSpace, prefs_DetectMouseClickMove, prefs_DisabledAutoCompleteKeys, prefs_DoNotLearnStrings
   global helpinfo_ArrowKeyMethod, helpinfo_AutoSpace, helpinfo_DetectMouseClickMove, helpinfo_DisabledAutoCompleteKeys, helpinfo_DoNotLearnStrings
   global prefs_EndWordCharacters, prefs_ForceNewWordCharacters, prefs_LearnCount, prefs_LearnLength, prefs_LearnMode, prefs_Length
   global helpinfo_EndWordCharacters, helpinfo_ForceNewWordCharacters, helpinfo_LearnCount, helpinfo_LearnLength, helpinfo_LearnMode, helpinfo_Length
   global prefs_NoBackSpace, prefs_NumPresses, prefs_SendMethod, prefs_ShowLearnedFirst, prefs_SuppressMatchingWord, prefs_TerminatingCharacters
   global helpinfo_NoBackSpace, helpinfo_NumPresses, helpinfo_SendMethod, helpinfo_ShowLearnedFirst, helpinfo_SuppressMatchingWord, helpinfo_TerminatingCharacters
   global prefs_ExcludeProgramExecutables, prefs_ExcludeProgramTitles, prefs_IncludeProgramExecutables, prefs_IncludeProgramTitles, prefs_HelperWindowProgramExecutables, prefs_HelperWindowProgramTitles
   global helpinfo_ExcludeProgramExecutables, helpinfo_ExcludeProgramTitles, helpinfo_IncludeProgramExecutables, helpinfo_IncludeProgramTitles, helpinfo_HelperWindowProgramExecutables, helpinfo_HelperWindowProgramTitles
   global prefs_ListBoxCharacterWidth, prefs_ListBoxFontFixed, prefs_ListBoxFontOverride, prefs_ListBoxFontSize, prefs_ListBoxMaxWidth, prefs_ListBoxOffset, prefs_ListBoxOpacity, prefs_ListBoxRows
   global helpinfo_ListBoxCharacterWidth, helpinfo_ListBoxFontFixed, helpinfo_ListBoxFontOverride, helpinfo_ListBoxFontSize, helpinfo_ListBoxMaxWidth, helpinfo_ListBoxOffset, helpinfo_ListBoxOpacity, helpinfo_ListBoxRows
   global helpinfo_FullHelpString
   global Menu_ArrowKeyMethodOptionsText, Menu_CaseCorrection, Menu_ListBoxOpacityUpDown, Menu_SendMethodOptionsCode, Menu_SendMethodC
   global Menu_CtrlEnter, Menu_CtrlSpace, Menu_Enter, Menu_SingleClick, Menu_NumberKeys, Menu_NumpadEnter, Menu_RightArrow, Menu_Tab
   global g_ScriptTitle
   ; Must be global for colors to function, colors will not function if static
   global Menu_VisitForum
   
   Menu_CaseCorrection=
   Menu_ArrowKeyMethodOptionsText=
   
   MenuFontList:=Writer_enumFonts() ; see note at function for credit

   MenuGuiWidth=700
   MenuGuiHeight=480
   MenuGuiRows = 8
   MenuGuiHelpIcon = %A_Space%(?)%A_Space%

   MenuSeparatorX = 10
   MenuSeparatorY = 8
   MenuEditIndentX = 10
   MenuEditIndentY = 20
   MenuHelpIndentX = 30
   MenuHelpIndentY = 0
	
   MenuRowHeight := (MenuGuiHeight - ((MenuGuiRows +1 ) * MenuSeparatorY ))/MenuGuiRows

   MenuTextMenuRowY := (MenuRowHeight - 6 ) / 3

   MenuTabWidth:=MenuGuiWidth-4
   MenuTabHeight:=MenuGuiHeight-75
   MenuTabHeightEdit:=MenuTabHeight-40

   MenuOneColGroupWidth := MenuGuiWidth - (2 * MenuSeparatorX)
   MenuTwoColGroupWidth := (MenuGuiWidth - (3 * MenuSeparatorX))/2
   MenuThreeColGroupWidth := (MenuGuiWidth - (4 * MenuSeparatorX))/3
   MenuDualThreeColGroupWidth := (MenuThreeColGroupWidth * 2) + MenuSeparatorX

   MenuOneColEditWidth := MenuOneColGroupWidth - (MenuEditIndentX * 2)
   MenuTwoColEditWidth := MenuTwoColGroupWidth - (MenuEditIndentX * 2)
   MenuThreeColEditWidth := MenuThreeColGroupWidth - (MenuEditIndentX * 2)
   MenuOneColEditWidthEdit := MenuOneColEditWidth - 140
   MenuOneColEditButton := MenuOneColEditWidthEdit + 30

   MenuGroup1BoxX := MenuSeparatorX
   MenuGroup1EditX := MenuGroup1BoxX + MenuEditIndentX
   MenuGroup1of1HelpX := MenuGroup1BoxX + MenuOneColGroupWidth - MenuHelpIndentX
   MenuGroup1of2HelpX := MenuGroup1BoxX + MenuTwoColGroupWidth - MenuHelpIndentX
   MenuGroup1of3HelpX := MenuGroup1BoxX + MenuThreeColGroupWidth - MenuHelpIndentX

   MenuGroup2of2BoxX := MenuGroup1BoxX + MenuTwoColGroupWidth + MenuSeparatorX
   MenuGroup2of2EditX := MenuGroup2of2BoxX + MenuEditIndentX
   MenuGroup2of2HelpX := MenuGroup2of2BoxX + MenuTwoColGroupWidth - MenuHelpIndentX
   
   MenuGroup2of3BoxX := MenuGroup1BoxX + MenuThreeColGroupWidth + MenuSeparatorX
   MenuGroup2of3EditX := MenuGroup2of3BoxX + MenuEditIndentX
   MenuGroup2of3HelpX := MenuGroup2of3BoxX + MenuThreeColGroupWidth - MenuHelpIndentX
	
   MenuGroup3of3BoxX := MenuGroup2of3BoxX + MenuThreeColGroupWidth + MenuSeparatorX
   MenuGroup3of3EditX := MenuGroup3of3BoxX + MenuEditIndentX
   MenuGroup3of3HelpX := MenuGroup3of3BoxX + MenuThreeColGroupWidth - MenuHelpIndentX
	
   MenuRowY := MenuSeparatorY + 30
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   Gui, MenuGui:Font, s8, Arial

   Gui, MenuGui:Add, Tab2, x2 w%MenuTabWidth% h%MenuTabHeight%, General Settings|Wordlist Box|Programs|Advanced (Experts Only)|About && Help

   Gui, MenuGui:Tab, 1 ; General Settings

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , Learn new words as you type
   Menu_LearnModeOptions=|On|Off|
   StringReplace, Menu_LearnModeOptions, Menu_LearnModeOptions, |%prefs_LearnMode%|,|%prefs_LearnMode%||
   StringTrimLeft, Menu_LearnModeOptions, Menu_LearnModeOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r5 vprefs_LearnMode gEditValue, %Menu_LearnModeOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of3HelpX% y%MenuRowHelpY% vhelpinfo_LearnMode gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack


   Gui, MenuGui:Add, GroupBox, x%MenuGroup2of3BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , Minimum length of word to learn
   Menu_LearnLengthOptions=|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|
   StringReplace,  Menu_LearnLengthOptions, Menu_LearnLengthOptions, |%prefs_LearnLength%|,|%prefs_LearnLength%||
   StringTrimLeft, Menu_LearnLengthOptions, Menu_LearnLengthOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup2of3EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r5 vprefs_LearnLength gEditValue, %Menu_LearnLengthOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup2of3HelpX% y%MenuRowHelpY% vhelpinfo_LearnLength gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack
   

   Gui, MenuGui:Add, GroupBox, x%MenuGroup3of3BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight%, Add to wordlist after X times
   Menu_LearnCountOptions=|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|
   StringReplace,  Menu_LearnCountOptions, Menu_LearnCountOptions, |%prefs_LearnCount%|,|%prefs_LearnCount%||
   StringTrimLeft, Menu_LearnCountOptions, Menu_LearnCountOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup3of3EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r5 vprefs_LearnCount gEditValue, %Menu_LearnCountOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup3of3HelpX% y%MenuRowHelpY% vhelpinfo_LearnCount gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack
   

   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuTwoColGroupWidth% h%MenuRowHeight% , Sub-strings to not learn
   Gui, MenuGui:Add, Edit, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuTwoColEditWidth% r1 vprefs_DoNotLearnStrings Password gEditValue, %prefs_DoNotLearnStrings%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of2HelpX% y%MenuRowHelpY% vhelpinfo_DoNotLearnStrings gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   Gui, MenuGui:Add, GroupBox, x%MenuGroup2of2BoxX% y%MenuRowY% w%MenuTwoColGroupWidth% h%MenuRowHeight% , Number of presses
   Menu_NumPressesOptions=|1|2|
   StringReplace,  Menu_NumPressesOptions, Menu_NumPressesOptions, |%prefs_NumPresses%|,|%prefs_NumPresses%||
   StringTrimLeft, Menu_NumPressesOptions, Menu_NumPressesOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup2of2EditX% y%MenuRowEditY% w%MenuTwoColEditWidth% r5 vprefs_NumPresses gEditValue, %Menu_NumPressesOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup2of2HelpX% y%MenuRowHelpY% vhelpinfo_NumPresses gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack


   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuDualThreeColGroupWidth% h%MenuRowHeight% , Auto Complete Keys
   ;  E = Ctrl + Enter
   ;  S = Ctrl + Space
   ;  T = Tab
   ;  R = Right Arrow
   ;  N = Number Keys
   ;  U = Enter
   ;  L = Single Click
   ;  M = Numpad Enter
   Menu_CheckedE=Checked
   Menu_CheckedS=Checked
   Menu_CheckedT=Checked
   Menu_CheckedR=Checked
   Menu_CheckedN=Checked
   Menu_CheckedU=Checked
   Menu_CheckedL=Checked
   Menu_CheckedM=Checked
   Loop, parse, prefs_DisabledAutoCompleteKeys
   {
	  If (A_LoopField = "E")
		 Menu_CheckedE =
	  If (A_LoopField = "S")
		 Menu_CheckedS =
	  If (A_LoopField = "T")
		 Menu_CheckedT =
	  If (A_LoopField = "R")
		 Menu_CheckedR =
	  If (A_LoopField = "N")
		 Menu_CheckedN =
	  If (A_LoopField = "U")
		 Menu_CheckedU =
	  If (A_LoopField = "L")
		 Menu_CheckedL =
	  If (A_LoopField = "M")
		 Menu_CheckedM =
   }

   MenuCheckmarkIndent := MenuTwoColEditWidth/3 + MenuEditIndentX
   Gui, MenuGui:Add, Checkbox, x%MenuGroup1EditX% yp+%MenuTextMenuRowY% vMenu_CtrlEnter gEditValue %Menu_CheckedE%, Ctrl + Enter
   Gui, MenuGui:Add, Checkbox, xp%MenuCheckmarkIndent% yp vMenu_Tab gEditValue %Menu_CheckedT%, Tab
   Gui, MenuGui:Add, Checkbox, xp%MenuCheckmarkIndent% yp vMenu_RightArrow gEditValue %Menu_CheckedR%, Right Arrow
   Gui, MenuGui:Add, Checkbox, xp%MenuCheckmarkIndent% yp vMenu_SingleClick gEditValue %Menu_CheckedL%, Single Click
   Gui, MenuGui:Add, Checkbox, x%MenuGroup1EditX% yp+%MenuTextMenuRowY% vMenu_CtrlSpace gEditValue %Menu_CheckedS%, Ctrl + Space
   Gui, MenuGui:Add, Checkbox, xp%MenuCheckmarkIndent% yp vMenu_NumberKeys gEditValue %Menu_CheckedN%, Number Keys
   Gui, MenuGui:Add, Checkbox, xp%MenuCheckmarkIndent% yp vMenu_Enter gEditValue %Menu_CheckedU%, Enter
   Gui, MenuGui:Add, Checkbox, xp%MenuCheckmarkIndent% yp vMenu_NumpadEnter gEditValue %Menu_CheckedM%, Numpad Enter

   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup2of3HelpX% y%MenuRowHelpY% vhelpinfo_DisabledAutoCompleteKeys gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack


   Gui, MenuGui:Add, GroupBox, x%MenuGroup3of3BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , Send Method
   Menu_SendMethodOptionsText=1 - Default (Type)|2 - Fast (Type)|3 - Slow (Type)|4 - Default (Paste)|5 - Fast (Paste)|6 - Slow (Paste)|7 - Alternate method
   Menu_SendMethodOptionsCode=1|2|3|1C|2C|3C|4C
   Loop, parse, Menu_SendMethodOptionsCode, |
   {
	  If (prefs_SendMethod = A_LoopField)
		 Menu_SendCount:=A_Index
   }

   Loop, parse, Menu_SendMethodOptionsText, |
   {
	  Menu_SendMethodOptions .= A_LoopField "|"
	  If (A_Index = Menu_SendCount)
		 Menu_SendMethodOptions .= "|"
   }   
   Gui, MenuGui:Add, DDL, x%MenuGroup3of3EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r5 vMenu_SendMethodC gEditValue altsubmit, %Menu_SendMethodOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup3of3HelpX% y%MenuRowHelpY% vhelpinfo_SendMethod gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack
   

   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , Case correction
   Menu_CaseCorrectionOptions=|On|Off|
   If (prefs_NoBackSpace = "on")
	  Menu_CaseCorrection=Off
   Else If (prefs_NoBackSpace = "off")
	  Menu_CaseCorrection=On
   StringReplace,  Menu_CaseCorrectionOptions, Menu_CaseCorrectionOptions, |%Menu_CaseCorrection%|,|%Menu_CaseCorrection%||
   StringTrimLeft, Menu_CaseCorrectionOptions, Menu_CaseCorrectionOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r5 vMenu_CaseCorrection gEditValue, %Menu_CaseCorrectionOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of3HelpX% y%MenuRowHelpY% vhelpinfo_NoBackSpace gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack


   Gui, MenuGui:Add, GroupBox, x%MenuGroup2of3BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , Monitor mouse clicks 
   Menu_DetectMouseClickMoveOptions=|On|Off|
   StringReplace,  Menu_DetectMouseClickMoveOptions, Menu_DetectMouseClickMoveOptions, |%prefs_DetectMouseClickMove%|,|%prefs_DetectMouseClickMove%||
   StringTrimLeft, Menu_DetectMouseClickMoveOptions, Menu_DetectMouseClickMoveOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup2of3EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r5 vprefs_DetectMouseClickMove gEditValue, %Menu_DetectMouseClickMoveOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup2of3HelpX% y%MenuRowHelpY% vhelpinfo_DetectMouseClickMove gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   Gui, MenuGui:Add, GroupBox, x%MenuGroup3of3BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , Type space after autocomplete
   Menu_AutoSpaceOptions=|On|Off|
   StringReplace,  Menu_AutoSpaceOptions, Menu_AutoSpaceOptions, |%prefs_AutoSpace%|,|%prefs_AutoSpace%||
   StringTrimLeft, Menu_AutoSpaceOptions, Menu_AutoSpaceOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup3of3EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r5 vprefs_AutoSpace gEditValue, %Menu_AutoSpaceOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup3of3HelpX% y%MenuRowHelpY% vhelpinfo_AutoSpace gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   Gui, MenuGui:Tab, 2 ; listbox ---------------------------------------------------------


   MenuRowY := MenuSeparatorY + 30
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   Gui, MenuGui:Add, GroupBox, x%MenuGroup2of3BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , Show wordlist after X characters
   Menu_LengthOptions=|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|
   StringReplace,  Menu_LengthOptions, Menu_LengthOptions, |%prefs_Length%|,|%prefs_Length%||
   StringTrimLeft, Menu_LengthOptions, Menu_LengthOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup2of3EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r5 vprefs_Length gEditValue, %Menu_LengthOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup2of3HelpX% y%MenuRowHelpY% vhelpinfo_Length gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , Maximum number of results to show
   Menu_ListBoxRowsOptions=|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|
   StringReplace,  Menu_ListBoxRowsOptions, Menu_ListBoxRowsOptions, |%prefs_ListBoxRows%|,|%prefs_ListBoxRows%||
   StringTrimLeft, Menu_ListBoxRowsOptions, Menu_ListBoxRowsOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r5 vprefs_ListBoxRows gEditValue, %Menu_ListBoxRowsOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of3HelpX% y%MenuRowHelpY% vhelpinfo_ListBoxRows gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   Gui, MenuGui:Add, GroupBox, x%MenuGroup3of3BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , Show learned words first
   Menu_ShowLearnedFirstOptions=|On|Off|
   StringReplace,  Menu_ShowLearnedFirstOptions, Menu_ShowLearnedFirstOptions, |%prefs_ShowLearnedFirst%|,|%prefs_ShowLearnedFirst%||
   StringTrimLeft, Menu_ShowLearnedFirstOptions, Menu_ShowLearnedFirstOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup3of3EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% vprefs_ShowLearnedFirst gEditValue, %Menu_ShowLearnedFirstOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup3of3HelpX% y%MenuRowHelpY% vhelpinfo_ShowLearnedFirst gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY


   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuTwoColGroupWidth% h%MenuRowHeight% , Wordlist row highlighting
   Menu_ArrowKeyMethodOptionsText=Off - only use the number keys|First - reset selected word to the beginning|LastWord - keep last word selected|LastPosition - keep the last cursor position
   Loop, parse, Menu_ArrowKeyMethodOptionsText, |
   {
	  Menu_ArrowKeyMethodOptions .= A_LoopField "|"
	  StringSplit, Split, A_LoopField, -
      Split1 := Trim(Split1)
	  If (Split1 = prefs_ArrowKeyMethod)
	  {
		 Menu_ArrowKeyMethodOptions .= "|"
	  }   
   }

   Gui, MenuGui:Add, DDL, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuTwoColEditWidth% r5 vprefs_ArrowKeyMethod gEditValue altsubmit, %Menu_ArrowKeyMethodOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of2HelpX% y%MenuRowHelpY% vhelpinfo_ArrowKeyMethod gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   Gui, MenuGui:Add, GroupBox, x%MenuGroup2of2BoxX% y%MenuRowY% w%MenuTwoColGroupWidth% h%MenuRowHeight% , Suppress matching word
   Menu_SuppressMatchingWordOptions=|On|Off|
   StringReplace,  Menu_SuppressMatchingWordOptions, Menu_SuppressMatchingWordOptions, |%prefs_SuppressMatchingWord%|,|%prefs_SuppressMatchingWord%||
   StringTrimLeft, Menu_SuppressMatchingWordOptions, Menu_SuppressMatchingWordOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup2of2EditX% y%MenuRowEditY% w%MenuTwoColEditWidth% vprefs_SuppressMatchingWord gEditValue, %Menu_SuppressMatchingWordOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup2of2HelpX% y%MenuRowHelpY% vhelpinfo_SuppressMatchingWord gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY


   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , List appears X pixels below cursor
   Menu_ListBoxOffsetOptions=|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32
   StringReplace,  Menu_ListBoxOffsetOptions, Menu_ListBoxOffsetOptions, |%prefs_ListBoxOffset%|,|%prefs_ListBoxOffset%||
   StringTrimLeft, Menu_ListBoxOffsetOptions, Menu_ListBoxOffsetOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r5 vprefs_ListBoxOffset gEditValue, %Menu_ListBoxOffsetOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of3HelpX% y%MenuRowHelpY% vhelpinfo_ListBoxOffset gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   Gui, MenuGui:Add, GroupBox, x%MenuGroup2of3BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , Fixed width font in list
   Menu_ListBoxFontFixedOptions=|On|Off|
   StringReplace,  Menu_ListBoxFontFixedOptions, Menu_ListBoxFontFixedOptions, |%prefs_ListBoxFontFixed%|,|%prefs_ListBoxFontFixed%||
   StringTrimLeft, Menu_ListBoxFontFixedOptions, Menu_ListBoxFontFixedOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup2of3EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r5 vprefs_ListBoxFontFixed gEditValue, %Menu_ListBoxFontFixedOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup2of3HelpX% y%MenuRowHelpY% vhelpinfo_ListBoxFontFixed gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   Gui, MenuGui:Add, GroupBox, x%MenuGroup3of3BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , Font size in list
   Menu_ListBoxFontSizeOptions=|8|9|10|11|12|13|14|15|16|17|18|19|20|
   StringReplace,  Menu_ListBoxFontSizeOptions, Menu_ListBoxFontSizeOptions, |%prefs_ListBoxFontSize%|,|%prefs_ListBoxFontSize%||
   StringTrimLeft, Menu_ListBoxFontSizeOptions, Menu_ListBoxFontSizeOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup3of3EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r5 vprefs_ListBoxFontSize gEditValue, %Menu_ListBoxFontSizeOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup3of3HelpX% y%MenuRowHelpY% vhelpinfo_ListBoxFontSize gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY


   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , List opacity
   Gui, MenuGui:Add, Edit, xp+10 yp+20 w%MenuThreeColEditWidth% vprefs_ListBoxOpacity gEditValue, %prefs_ListBoxOpacity%
   Gui, MenuGui:Add, UpDown, xp+10 yp+20 w%MenuThreeColEditWidth% vMenu_ListBoxOpacityUpDown Range0-255, %prefs_ListBoxOpacity%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of3HelpX% y%MenuRowHelpY% vhelpinfo_ListBoxOpacity gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   Gui, MenuGui:Add, GroupBox, x%MenuGroup2of3BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , List character width override
   Menu_ListBoxCharacterWidthOptions=||5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|
   StringReplace,  Menu_ListBoxCharacterWidthOptions, Menu_ListBoxCharacterWidthOptions, |%prefs_ListBoxCharacterWidth%|,|%prefs_ListBoxCharacterWidth%||
   StringTrimLeft, Menu_ListBoxCharacterWidthOptions, Menu_ListBoxCharacterWidthOptions, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup2of3EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r5 vprefs_ListBoxCharacterWidth gEditValue, %Menu_ListBoxCharacterWidthOptions%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup2of3HelpX% y%MenuRowHelpY% vhelpinfo_ListBoxCharacterWidth gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   Gui, MenuGui:Add, GroupBox, x%MenuGroup3of3BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , List font
   sort, MenuFontList, D|
   MenuFontList := "|<Default>|" . MenuFontList
   If (MenuListBoxFont = "") or (MenuListBoxFont = " ")
   {
      StringReplace, MenuFontList, MenuFontList, |%prefs_ListBoxFontOverride%|, |%prefs_ListBoxFontOverride%||
   }
   ; remove the extra leading "|" we added for searching
   StringTrimLeft, MenuFontList, MenuFontList, 1
   Gui, MenuGui:Add, DDL, x%MenuGroup3of3EditX% y%MenuRowEditY% w%MenuThreeColEditWidth% r10 w200 vprefs_ListBoxFontOverride gEditValue, %MenuFontList%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup3of3HelpX% y%MenuRowHelpY% vhelpinfo_ListBoxFontOverride gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuThreeColGroupWidth% h%MenuRowHeight% , List max width in pixels
   Gui, MenuGui:Add, Edit, xp+10 yp+20 w%MenuThreeColEditWidth% vprefs_ListBoxMaxWidth gEditValue, %prefs_ListBoxMaxWidth%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of3HelpX% y%MenuRowHelpY% vhelpinfo_ListBoxMaxWidth gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY


   Gui, MenuGui:Tab, 3 ; Programs ---------------------------------------------------------


   MenuRowY := MenuSeparatorY + 30
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuOneColGroupWidth% h%MenuRowHeight% , Window titles you want %g_ScriptTitle% enabled for
   Gui, MenuGui:Add, Edit, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuOneColEditWidthEdit% r1 vprefs_IncludeProgramTitles gEditValue, %prefs_IncludeProgramTitles%
   Gui, MenuGui:Add, Button, x%MenuOneColEditButton% yp w130 gSetEnableTitles, Edit
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of1HelpX% y%MenuRowHelpY% vhelpinfo_IncludeProgramTitles gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack
   
   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuOneColGroupWidth% h%MenuRowHeight% , Window titles you want %g_ScriptTitle% disabled for
   Gui, MenuGui:Add, Edit, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuOneColEditWidthEdit% r1 vprefs_ExcludeProgramTitles gEditValue, %prefs_ExcludeProgramTitles%
   Gui, MenuGui:Add, Button, x%MenuOneColEditButton% yp w130 gSetDisableTitles, Edit
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of1HelpX% y%MenuRowHelpY% vhelpinfo_ExcludeProgramTitles gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuOneColGroupWidth% h%MenuRowHeight% , Processes you want %g_ScriptTitle% enabled for
   Gui, MenuGui:Add, Edit, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuOneColEditWidthEdit% r1 vprefs_IncludeProgramExecutables gEditValue, %prefs_IncludeProgramExecutables%
   Gui, MenuGui:Add, Button, x%MenuOneColEditButton% yp w130 gSetEnableProcess, Edit
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of1HelpX% y%MenuRowHelpY% vhelpinfo_IncludeProgramExecutables gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuOneColGroupWidth% h%MenuRowHeight% , Processes you want %g_ScriptTitle% disabled for
   Gui, MenuGui:Add, Edit, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuOneColEditWidthEdit% r1 vprefs_ExcludeProgramExecutables gEditValue, %prefs_ExcludeProgramExecutables%
   Gui, MenuGui:Add, Button, x%MenuOneColEditButton% yp w130 gSetDisableProcess, Edit
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of1HelpX% y%MenuRowHelpY% vhelpinfo_ExcludeProgramExecutables gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   ;HelperWindowProgramTitles

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuOneColGroupWidth% h%MenuRowHeight% , Window titles you want the helper window enabled for
   Gui, MenuGui:Add, Edit, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuOneColEditWidthEdit% r1 vprefs_HelperWindowProgramTitles gEditValue, %prefs_HelperWindowProgramTitles%
   Gui, MenuGui:Add, Button, x%MenuOneColEditButton% yp w130 gSetHelpTitles, Edit
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of1HelpX% y%MenuRowHelpY% vhelpinfo_HelperWindowProgramTitles gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   ;HelperWindowProgramExecutables

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuOneColGroupWidth% h%MenuRowHeight% , Processes you want the helper window enabled for
   Gui, MenuGui:Add, Edit, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuOneColEditWidthEdit% r1 vprefs_HelperWindowProgramExecutables gEditValue, %prefs_HelperWindowProgramExecutables%
   Gui, MenuGui:Add, Button, x%MenuOneColEditButton% yp w130 gSetHelpProcess, Edit
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of1HelpX% y%MenuRowHelpY% vhelpinfo_HelperWindowProgramExecutables gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack



   Gui, MenuGui:Tab, 4 ; advanced  -------------------------------------------------------------------------

   MenuRowY := MenuSeparatorY + 30
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuOneColGroupWidth% h%MenuRowHeight% , Terminating Characters (see http://www.autohotkey.com/docs/KeyList.htm)
   Gui, MenuGui:Add, Edit, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuOneColEditWidth% r1 vprefs_TerminatingCharacters gEditValue, %prefs_TerminatingCharacters%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of1HelpX% y%MenuRowHelpY% vhelpinfo_TerminatingCharacters gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowEditY := MenuRowY + MenuEditIndentY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuOneColGroupWidth% h%MenuRowHeight% , Force New Word Characters (comma separated)
   Gui, MenuGui:Add, Edit, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuOneColEditWidth% r1 vprefs_ForceNewWordCharacters gEditValue, %prefs_ForceNewWordCharacters%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of1HelpX% y%MenuRowHelpY% vhelpinfo_ForceNewWordCharacters gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack

   MenuRowY := MenuRowY + MenuRowHeight + MenuSeparatorY
   MenuRowEditY := MenuRowY + MenuEditIndentY
   MenuRowHelpY := MenuRowY - MenuHelpIndentY

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX% y%MenuRowY% w%MenuOneColGroupWidth% h%MenuRowHeight% , End Word Characters (comma separated)
   Gui, MenuGui:Add, Edit, x%MenuGroup1EditX% y%MenuRowEditY% w%MenuOneColEditWidth% r1 vprefs_EndWordCharacters gEditValue, %prefs_EndWordCharacters%
   Gui, MenuGui:Font, cGreen
   Gui, MenuGui:Add, Text, x%MenuGroup1of1HelpX% y%MenuRowHelpY% vhelpinfo_EndWordCharacters gHelpMe, %MenuGuiHelpIcon%
   Gui, MenuGui:Font, cBlack



   Gui, MenuGui:Tab, 5 ; about & help --------------------------------------------

   MenuRowY := MenuSeparatorY + 30
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY

   helpinfo_Intro=
   (
%g_ScriptTitle% is a simple, compact, and handy auto-completion utility.

It is customizable enough to be useful for regular typing and for programming.

Features:
As you type your word, up to 10 (or as defined in Settings) matches will appear in a drop-down dialog, numbered 1 - 0 (10th). To choose the match you want just hit the associated number on your keyboard (numpad does not work). Alternatively you can select an item from the drop-down using the Up/Down arrows. You can define a fixed position for the drop-down dialog to appear by hitting Ctrl-Shift-H to open a small helper window, or by specifying a list of programs in the preferences file. Please note that in Firefox, Thunderbird, and certain other programs you will probably need to open the helper window due to issues detecting the caret position.

Words should be stored in a file named 'Wordlist.txt' which should be located in the script directory. These words may be commented out by prefixing with a semicolon or simply removed or added. Words may include terminating characters (such as space), but you must select the word before typing the terminating character.

In addition to being able to use the number keys to select a word, you can select words from the drop-down via the Up/Down arrows. Hitting Up on the first item will bring you to the last and hitting Down on the last item will bring you to the first. Hitting Page Up will bring you up 10 items, or to the first item. Hitting Page Down will bring you down 10 items, or to the last item. You can hit Tab, Right Arrow, Ctrl-Space, or Ctrl-Enter to autocomplete the selected word. This feature can be disabled or have some of its behavior modified via Settings.

The script will learn words as you type them if "Learn new words as you type" is set to On in Settings. If you type a word more than 5 times (or as defined in "Minimum length of word to learn") in a single session the word will be permanently added to the list of learned words. Learned words will always appear below predefined words, but will be ranked and ordered among other learned words based on the frequency you type them. You can permanently learn a word by highlighting a word and hitting Ctrl-Shift-C (this works even if "Learn new words as you type" is set to Off). You may use Ctrl-Shift-Del to remove the currently selected Learned Word.
Learned words are stored in the WordlistLearned.db sqlite3 database. Learned words are backed up in WordlistLearned.txt. To modify the list of Learned words manually, delete the WordlistLearned.db database, then manually edit the WordlistLearned.txt file. On the next launch of the script, the WordlistLearned.db database will be rebuilt.

Word descriptions can be added to 'Wordlist.txt' that will appear in the wordlist next to the word. These descriptions should be in the form of <word>|d|<description>, e.g., Tylenol|d|Pain Reliever. This could be used for spelling replacements, text expansion, or translation aids. Multiple replacements can be defined for a word (put each on a separate line). Descriptions can be added to each word as well.

Word replacements can be added to 'Wordlist.txt' that will appear in the wordlist next to the word. When the word is chosen, it will be backspaced out and replaced with the new word. These replacements should be in the form of <word>|r|<description>, e.g., fire|r|fuego. This could be used for things like definitions, translation aids, or function arguments. When Fixed Width fonts are used in the wordlist, the description columns will be tabbed evenly so they line up.

When Settings are changed, the script will automatically create a file named Preferences.ini in the script directory. This file allows for sharing settings between users. Users are encouraged to only edit settings by using the Settings window.
To allow for distribution of standardized preferences, a Defaults.ini may be distributed with the same format as Preferences.ini. If the Defaults.ini is present, this will override the hardcoded defaults in the script. A user may override the Defaults.ini by changing settings in the Settings window.

Customizable features include (see also detailed description below)

   * Enable or disable learning mode.
   * Number of characters a word needs to have in order to be learned.
   * Number of times you must type a word before it is permanently learned.
   * Number of items to show in the list at once.
   * Number of characters before the list of words appears.
   * Change the method used to send the word to the screen.
   * Enable, disable, or customize the arrow key's functionality.
   * Disable certain keys for autocompleting a word selected via the arrow keys.
   * Change whether the script simply completes or actually replaces the word (capitalization change based on the wordlist file).
   * Enable or disable the resetting of the Wordlist Box on a mouseclick.
   * Change whether a space should be automatically added after the autocompleted word or not.
   * List of strings which will prevent any word which contains one of these strings from being learned.
   * Change whether the typed word should appear in the word list or not.
   * Number of pixels below the caret to display the Wordlist Box.
   * Wordlist Box Default Font of fixed (Courier New) or variable (Tahoma) width.
   * Wordlist Box Font Size.
   * Wordlist Box Opacity setting to set the transparency of the List Box.
   * Wordlist Box Character Width to override the computed character width.
   * Wordlist Box Default Font override.
   * List of programs for which you want %g_ScriptTitle% enabled.
   * List of programs for which you do not want %g_ScriptTitle% enabled.
   * List of programs for which you want the Helper Window to automatically open.
   * List of characters which terminate a word.
   * List of characters which terminate a word and start a new word.
   * Number of times you must press a number hotkey to select the associated word (options are 1 and 2, 2 is buggy).
   
Unicode Support:
Full support for UTF-8 character set.
   )
   
   helpinfo_HelpText = %helpinfo_Intro%`r`n`r`n%helpinfo_FullHelpString%

   Loop, Parse, helpinfo_HelpText,`n, `r
   {
	  IF ( SubStr(A_LoopField, 1,1) = ";")
	  {
		 helpinfo_ModHelpText .= SubStr(A_LoopField,2) . "`r`n"
	  } else
	  {
		 helpinfo_ModHelpText .= A_LoopField . "`r`n"
	  }
   }

   Gui, MenuGui:Add, Edit, ReadOnly x%MenuGroup1BoxX% y%MenuRowY% w%MenuOneColGroupWidth% h%MenuTabHeightEdit%, %helpinfo_ModHelpText%

   helpinfo_ModHelpText =
   helpinfo_HelpText =
   helpinfo_Intro =

   Gui, MenuGui:tab, 

   MenuRowY := MenuTabHeight+15
   MenuRowHelpY := MenuRowY - MenuHelpIndentY
   MenuRowEditY := MenuRowY + MenuEditIndentY
   MenuRowThreeButtonWidth := (MenuTwoColGroupWidth - (4 * MenuEditIndentX))/3
   MenuRowThreeButtonNext := MenuEditIndentX + MenuRowThreeButtonWidth

   Gui, MenuGui:Add, GroupBox, x%MenuGroup1BoxX%           y%MenuRowY%     w%MenuTwoColGroupWidth% h50 , Configuration
   Gui, MenuGui:Add, Button,   x%MenuGroup1EditX%          y%MenuRowEditY% w%MenuRowThreeButtonWidth%    gSave   , Save && Close
   Gui, MenuGui:Add, Button,   xp+%MenuRowThreeButtonNext% yp          w%MenuRowThreeButtonWidth%    gRestore, Restore default
   Gui, MenuGui:Add, Button,   xp+%MenuRowThreeButtonNext% yp          w%MenuRowThreeButtonWidth%    gCancelButton , Cancel

   if (g_ScriptTitle == "TypingAid")
   {
      Gui, MenuGui:Font, cBlack bold
      Gui, MenuGui:Add, Text, x%MenuGroup2of2EditX% Yp-10, %g_ScriptTitle%
      Gui, MenuGui:Font, cBlack normal

      Gui, MenuGui:Add, Text, xp+60 Yp, is free software, support forum at
      Gui, MenuGui:Font, cGreen 
      ;the vMenu_VisitForum variable is necessary for the link highlighting
      Gui, MenuGui:Add, Text, x%MenuGroup2of2EditX% Yp+%MenuTextMenuRowY% vMenu_VisitForum gVisitForum, www.autohotkey.com (click here)
      Gui, MenuGui:Font, cBlack 
   }
   
   Gui, Menugui:+OwnDialogs
   Gui, MenuGui:Show, h%MenuGuiHeight% w%MenuGuiWidth%, %g_ScriptTitle% Settings
   Return
}

SetEnableTitles:
GetList("prefs_IncludeProgramTitles",0)
Return

SetDisableTitles:
GetList("prefs_ExcludeProgramTitles",0)
Return

SetEnableProcess:
GetList("prefs_IncludeProgramExecutables",1)
Return

SetDisableProcess:
GetList("prefs_ExcludeProgramExecutables",1)
Return

SetHelpTitles:
GetList("prefs_HelperWindowProgramTitles",0)
Return

SetHelpProcess:
GetList("prefs_HelperWindowProgramExecutables",1)
Return

GetList(TitleType,GetExe)
{
   global Menu_TitleType
   global Menu_InProcessList
   global g_ScriptTitle
   global prefs_IncludeProgramTitles
   global prefs_ExcludeProgramTitles
   global prefs_IncludeProgramExecutables
   global prefs_ExcludeProgramExecutables
   global prefs_HelperWindowProgramTitles
   global prefs_HelperWindowProgramExecutables
   
   Menu_InProcessList := true
   Menu_TitleType := TitleType
   If (GetExe =1)
   {
      WinGet, id, list,,, Program Manager
      Loop, %id%
      {
         tmptitle=
         tmpid := id%A_Index%
         WinGet, tmptitle, ProcessName, ahk_id %tmpid%
         If (tmptitle <> "")
            RunningList .= tmptitle "|"
      }
   } Else If (GetExe = 0) ; get list of active window titles
   {
      WinGet, id, list,,, Program Manager
      Loop, %id%
      {
         tmptitle=
         tmpid := id%A_Index%
         WinGetTitle, tmptitle, ahk_id %tmpid%
         If (tmptitle <> "")
            RunningList .= tmptitle "|"
      }
   }	
   GetExe=0
   
   GuiControlGet, MenuTitleList, MenuGui: , %Menu_TitleType%
   
   MenuProcessHeight := 380
   
   StringRight, ListType, Menu_TitleType, 6
	
   Sort,RunningList, D| U	
   Gui, ProcessList:+OwnerMenuGui
   Gui, MenuGui:+Disabled  ; disable main window
   Gui, ProcessList:Add, Text,x10 y10, Select program:
   Gui, ProcessList:Add, DDL, xp+100 yp w250 R10 gToEdit,%RunningList%
   Gui, ProcessList:Add, Text,x10 yp+30, Edit:
   Gui, ProcessList:Add, Edit, xp+100 yp w250
   Gui, ProcessList:Add, Button, xp+260 yp gAddNew1 w40 Default, Add
   if (ListType == "Titles")
   {
      Gui, ProcessList:Add, Text,x10 yp+30, Exact Match:
      Gui, ProcessList:Add, Checkbox, xp+100 yp
      MenuProcessHeight += 30
   }
   Gui, ProcessList:Add, Text, x10 yp+30, Current list:
   Gui, ProcessList:Add, ListBox, xp+100 yp w250 r10, %MenuTitleList%
   Gui, ProcessList:Add, Button, xp+260 yp gRemoveNew1 w40 , Del
   Gui, ProcessList:Add, Text, x10 yp+170, a) Select a program or window from the list or type a name in the`n%A_Space%%A_Space%%A_Space%%A_Space%%A_Space%'Edit' control (you may need to edit it further)`nb) Click ADD to add it to the list`nc) To remove a program/title, select an item from the 'current list' and`n%A_Space%%A_Space%%A_Space%%A_Space%click DEL.
   Gui, ProcessList:Add, Button, x10 yp+90 w190 gSaveTitleList, Save 
   Gui, ProcessList:Add, Button, xp+210 yp w190 gCancelTitle, Cancel
   Gui, ProcessList:Show, w420 h%MenuProcessHeight%, %g_ScriptTitle% Settings
   Return
}

VisitForum:
MsgBox , 36 , Visit %g_ScriptTitle% forum (www.autohotkey.com), Do you want to visit the %g_ScriptTitle% forum on www.autohotkey.com?
IfMsgBox, Yes
	Run, http://www.autohotkey.com/board/topic/49517-ahk-11typingaid-v2198-word-autocompletion-utility/
Return

Restore:
MsgBox, 1, Restore Defaults, This will restore all settings to default. Continue?
IfMsgBox, Cancel
   return
RestoreDefaults()
gosub, Cancel
return

RestoreDefaults()
{
   global g_PrefsFile
   global g_ScriptTitle
   global Menu_OldLearnCount
   global prefs_LearnCount

   ReadPreferences("RestoreDefaults")

   IF ( Menu_OldLearnCount < prefs_LearnCount )
   {
      MsgBox, 1, Restore Defaults, Restoring Defaults will increase the Learn Count value.`r`nWhen exiting %g_ScriptTitle%, this will permanently delete any words`r`nfrom the Learned Words which have been typed less times`r`nthan the new Learn Count. Continue?
      IfMsgBox, Cancel
      {
         ReturnValue := "Cancel"
      }
   }
   
   if (ReturnValue == "Cancel")
   {
      ReadPreferences(,"RestorePreferences")
      return
   } else {
      
      IfExist, %g_PrefsFile%
      {
         try {
            FileCopy, %g_PrefsFile%, %PrefsFile%-%A_Now%.bak, 1
            FileDelete, %g_PrefsFile%
         } catch {
            MsgBox,,Restore Defaults,Unable to back up preferences! Canceling...
            ReadPreferences(,"RestorePreferences")
            return
         }
      }
      
      ApplyChanges()
      MsgBox,,Restore Defaults, Defaults have been restored.
   }
   
   return
}

MenuGuiGuiEscape:
MenuGuiGuiClose:
CancelButton:
if (Menu_ValueChanged == true)
{
   MsgBox, 4, Cancel, Changes will not be saved. Cancel anyway?
   IfMsgBox, Yes
   {
      gosub, Cancel
   }
} else {
   gosub, Cancel
}
return

Cancel:
Gui, MenuGui:Destroy
; Clear WM_SETCURSOR action
OnMessage(g_WM_SETCURSOR, "")
; Clear WM_MOUSEMOVE action
OnMessage(g_WM_MOUSEMOVE, "")
;Clear mouse flags
HandleSettingsMessage("", "", "", "")
g_InSettings := false
Menu, Tray, Enable, Settings
GetIncludedActiveWindow()
Return

Save:
Save()
return

Save()
{
   global prefs_ArrowKeyMethod, prefs_DisabledAutoCompleteKeys, prefs_LearnCount, prefs_ListBoxOpacity, prefs_NoBackSpace, prefs_SendMethod
   global Menu_ChangedPrefs, Menu_ListBoxOpacityUpDown, Menu_OldLearnCount
   global g_ScriptTitle
   ; should only save preferences.ini if different from defaults
   Menu_ChangedPrefs["prefs_ArrowKeyMethod"] := prefs_ArrowKeyMethod
   Menu_ChangedPrefs["prefs_DisabledAutoCompleteKeys"] := prefs_DisabledAutoCompleteKeys
   Menu_ChangedPrefs["prefs_NoBackSpace"] := prefs_NoBackSpace
   Menu_ChangedPrefs["prefs_SendMethod"] := prefs_SendMethod
   Gui, MenuGui:Submit
   prefs_ListBoxOpacity := Menu_ListBoxOpacityUpDown
   
   IF (Menu_OldLearnCount < prefs_LearnCount )
   {   
      MsgBox, 1, Save, Saving will increase the Learn Count value.`r`nWhen exiting %g_ScriptTitle%, this will permanently delete any words`r`nfrom the Learned Words which have been typed less times`r`nthan the new Learn Count. Continue?
      IfMsgBox, Cancel
      {
         ReturnValue := "Cancel"
      }
   }
   
   If ( ReturnValue == "Cancel" )
   {
      ReadPreferences(,"RestorePreferences")
   } else {
      SaveSettings()
      ApplyChanges()
   }
   gosub, Cancel
   Return
}

SaveSettings()
{
   Global
   
   Local Menu_PrefsToSave
   Local Split
   Local Split0
   Local Split1

   Local key
   Local value
   
   Menu_PrefsToSave := Object()
  
   Loop, parse, Menu_SendMethodOptionsCode, | ; get sendmethod
   {
      If (Menu_SendMethodC = A_Index)
         prefs_SendMethod:=A_LoopField
   }
   
   prefs_DisabledAutoCompleteKeys=
   If (Menu_CtrlEnter = 0)
      prefs_DisabledAutoCompleteKeys .= "E"
   If (Menu_Tab = 0)
      prefs_DisabledAutoCompleteKeys .= "T"
   If (Menu_CtrlSpace = 0)
      prefs_DisabledAutoCompleteKeys .= "S"
   If (Menu_RightArrow = 0)
      prefs_DisabledAutoCompleteKeys .= "R"
   If (Menu_NumberKeys = 0)
      prefs_DisabledAutoCompleteKeys .= "N"
   If (Menu_Enter = 0)
      prefs_DisabledAutoCompleteKeys .= "U"
   If (Menu_SingleClick = 0)
      prefs_DisabledAutoCompleteKeys .= "L"
   If (Menu_NumpadEnter = 0)
      prefs_DisabledAutoCompleteKeys .= "M"

   Loop, parse, Menu_ArrowKeyMethodOptionsText, |
   {
      StringSplit, Split, A_LoopField, -
      Split1 := Trim(Split1)
      If (prefs_ArrowKeyMethod = A_Index)
      {
         prefs_ArrowKeyMethod := Split1
      }   
   }

   If (Menu_CaseCorrection = "on")
      prefs_NoBackSpace=Off
   Else If (Menu_CaseCorrection = "off")
      prefs_NoBackSpace=On
   
   ; Determine list of preferences to save
   For key, value in Menu_ChangedPrefs
   {
      IF (%key% <> value)
      {
         Menu_PrefsToSave.Insert(key)
      }
   }

   SavePreferences(Menu_PrefsToSave)
}

ApplyChanges()
{
   ValidatePreferences()
   ParseTerminatingCharacters()
   InitializeHotKeys()
   DestroyListBox()
   InitializeListBox()
   
   Return

}   

EditValue:
Menu_ValueChanged := true
IF (A_GuiControl && !(SubStr(A_GuiControl ,1 ,5) == "Menu_") )
{
   Menu_ChangedPrefs[A_GuiControl] := %A_GuiControl%
}
Return

HelpMe:
HelpMe()
return

HelpMe()
{
   global g_ScriptTitle
   Loop, Parse, %A_GuiControl%,`r`n
   {
      IF ( SubStr(A_LoopField, 1,1) = ";")
      {
         Menu_Help .= SubStr(A_LoopField,2) . "`r`n"
      } else {
         Menu_Help .= A_LoopField . "`r`n"
      }
   }
   MsgBox , 32 , %g_ScriptTitle% Help, %Menu_Help%
   return
}
   
; derived from work by shimanov, 2005
; http://www.autohotkey.com/forum/viewtopic.php?p=37696#37696
HandleSettingsMessage( p_w, p_l, p_m, p_hw )
{
   Global g_IDC_HELP, g_IMAGE_CURSOR, g_LR_SHARED, g_NULL, g_WM_SETCURSOR, g_WM_MOUSEMOVE, g_cursor_hand
   Static Help_Hover, h_cursor_help, URL_Hover, h_old_cursor, Old_GuiControl
   
   ; pass in all blanks to clear flags
   if ((!p_w) && (!p_l) && (!p_m) && (!p_hw)) {
      Help_Hover =
      URL_Hover =
      h_old_cursor =
      Old_GuiControl =
   }
   
   if ( p_m = g_WM_SETCURSOR )
   {
      if ( Help_Hover || URL_Hover)
         return, true
   } else if (A_GuiControl == Old_GuiControl)
   {
      return
   } else if ( p_m = g_WM_MOUSEMOVE )
	{
      if (Help_Hover || URL_Hover)
      {
         
			Gui, MenuGui:Font, cGreen     ;;; xyz
			GuiControl, MenuGui:Font, %Old_GuiControl% ;;; xyz
      }
      
      if ( SubStr(A_GuiControl, 1, 9) == "helpinfo_" )
		{
			if !(Help_Hover)
			{
				IF !(h_cursor_help)
				{
					h_cursor_help := DllCall( "LoadImage", "Ptr", g_NULL, "Uint", g_IDC_HELP , "Uint", g_IMAGE_CURSOR, "Int", g_NULL, "Int", g_NULL, "Uint", g_LR_SHARED ) 
				}
				old_cursor := DllCall( "SetCursor", "Uint", h_cursor_help )
				Help_Hover = true
				URL_Hover = 
				Gui, MenuGui:Font, cBlue        ;;; xyz
				GuiControl, MenuGui:Font, %A_GuiControl% ;;; xyz
			}
		} else if (A_GuiControl == "Menu_VisitForum")
		{	
			if !(URL_Hover)
			{
				old_cursor := DllCall( "SetCursor", "uint", g_cursor_hand )
				URL_Hover = true
				Help_Hover =
				Gui, MenuGui:Font, cBlue        ;;; xyz
				GuiControl, MenuGui:Font, %A_GuiControl% ;;; xyz
			}
				
		} else if (Help_Hover || URL_Hover)
      {
			DllCall( "SetCursor", "Uint", h_old_cursor )
			Help_Hover=
			URL_Hover=
			h_old_cursor=
		}
		IF !(h_old_cursor)
		{
			h_old_cursor := old_cursor
      }
      
      Old_GuiControl := A_GuiControl
   }
}

SaveTitleList:
SaveTitleList()
return

SaveTitleList()
{
   global Menu_InProcessList
   global Menu_TitleType
   ControlGet, MenuTitleList, List, , ListBox1
   Menu_InProcessList := false
   Gui, ProcessList:Destroy
   Gui, MenuGui:-Disabled  ; enable main window
   Gui, MenuGui:Show
   StringReplace, MenuTitleList, MenuTitleList, `n, |, All

   GuiControl, MenuGui:Text, %Menu_TitleType%, %MenuTitleList%
   Menu_ChangedPrefs[Menu_TitleType] := %Menu_TitleType%
	
   return
}

ProcessListGuiEscape:
ProcessListGuiClose:
CancelTitle:
Menu_InProcessList := false
Gui, ProcessList:Destroy
Gui, MenuGui:-Disabled ; enable main window
Gui, MenuGui:Show
Return

ToEdit:
ToEdit()
return

ToEdit()
{
   GuiControlGet, MenuOutputVar, ProcessList:,ComboBox1
   GuiControl, ProcessList:, Edit1, 
   GuiControl, ProcessList:, Edit1, %MenuOutputVar%
   ControlFocus, Edit1
   return
}

AddNew1:
AddNew1()
return

AddNew1()
{
   GuiControlGet, MenuExactMatch, ProcessList:, Button2
   GuiControlGet, MenuOutputVar, ProcessList:,Edit1
   ControlGet, MenuTitleList, List, , ListBox1
   
   if (MenuExactMatch == 1)
   {
      MenuOutputVar := """" . MenuOutputVar . """"
   }
   
   StringReplace, MenuTitleList, MenuTitleList, `n, |, All
   MenuTitleList := "|" . MenuTitleList . "|"
   
   SearchString := "|" . MenuOutputVar . "|"
   
   IfInString, MenuTitleList, |%MenuOutputVar%|
   {
      MsgBox, 16, , Duplicate entry.
      return
   }
   
   GuiControl, ProcessList:, ListBox1, %MenuOutputVar%|
   GuiControl, ProcessList:, Edit1, 
   GuiControl, ProcessList:, Button2, 0
   ControlFocus, Edit1
   return
}

RemoveNew1:
RemoveNew1()
return

RemoveNew1()
{
   GuiControlGet, MenuOutputVar, ProcessList:, Listbox1
   ControlGet, MenuTitleList, List, , ListBox1
   StringReplace, MenuTitleList, MenuTitleList, `n, |, All
   MenuTitleList := "|" . MenuTitleList . "|"
   StringReplace, MenuTitleList, MenuTitleList, |%MenuOutputVar%|, |, all
   StringTrimRight, MenuTitleList, MenuTitleList, 1
   GuiControl, ProcessList:, ListBox1, |
   GuiControl, ProcessList:, ListBox1, %MenuTitleList%
   
   return
}

; copied from font explorer http://www.autohotkey.com/forum/viewtopic.php?t=57501&highlight=font
Writer_enumFonts()
{
   global g_NULL
   Writer_enumFontsProc(0, 0, 0, 0,"Clear")
   hDC := DllCall("GetDC", "Uint", g_NULL) 
   DllCall("EnumFonts", "Uint", hDC, "Uint", g_NULL, "Uint", RegisterCallback("Writer_enumFontsProc", "F"), "Uint", g_NULL) 
   DllCall("ReleaseDC", "Uint", g_NULL, "Uint", hDC)
	
   return Writer_enumFontsProc(0, 0, 0, 0, "ReturnS")
}

Writer_enumFontsProc(lplf, lptm, dwType, lpData, Action = 0)
{
   static s
   
   ifEqual, Action, Clear
   {
      s=
      return
   }
	
   ifEqual, Action, ReturnS, return s

   s .= DllCall("MulDiv", "Int", lplf+28, "Int",1, "Int", 1, "str") "|"
   return 1
}
; end of Settings.ahk

; start of Window.ahk
;These functions and labels are related to the active window

EnableWinHook()
{
   global g_EVENT_SYSTEM_FOREGROUND
   global g_NULL
   global g_WINEVENT_SKIPOWNPROCESS
   global g_WinChangedEventHook
   global g_WinChangedCallback
   ; Set a hook to check for a changed window
   If !(g_WinChangedEventHook)
   {
      MaybeCoInitializeEx()
      g_WinChangedEventHook := DllCall("SetWinEventHook", "Uint", g_EVENT_SYSTEM_FOREGROUND, "Uint", g_EVENT_SYSTEM_FOREGROUND, "Ptr", g_NULL, "Uint", g_WinChangedCallback, "Uint", g_NULL, "Uint", g_NULL, "Uint", g_WINEVENT_SKIPOWNPROCESS)
      
      if !(g_WinChangedEventHook)
      {
         MsgBox, Failed to register Event Hook!
         ExitApp
      }
   }
   
   Return
}

DisableWinHook()
{
   global g_WinChangedEventHook
   
   if (g_WinChangedEventHook)
   {
      if (DllCall("UnhookWinEvent", "Uint", g_WinChangedEventHook))
      {
         g_WinChangedEventHook =
         MaybeCoUninitialize()
      } else {
         MsgBox, Failed to Unhook WinEvent!
         ExitApp
      }
   }
   return
}

; Hook function to detect change of focus (and remove ListBox when changing active window) 
WinChanged(hWinEventHook, event, wchwnd, idObject, idChild, dwEventThread, dwmsEventTime)
{
   global g_inSettings
   global g_ManualActivate
   global g_OldCaretY
   global prefs_DetectMouseClickMove
   
   If (event <> 3)
   {
      return
   }
   
   if (g_ManualActivate = true)
   {
      ; ignore activations we've set up manually and clear the flag
      g_ManualActivate = 
      return
   }      
   
   if (g_inSettings = true )
   {
      return
   }
   
   if (SwitchOffListBoxIfActive())
   {
      return
   }
   
   IF ( ReturnWinActive() )
   {
      IfNotEqual, prefs_DetectMouseClickMove, On 
      {
         IfNotEqual, g_OldCaretY,
         {
            if ( g_OldCaretY != HCaretY() )
            {
               CloseListBox()
            }
         }
      }
      
   } else {
      GetIncludedActiveWindow()
   }
   Return
}

SwitchOffListBoxIfActive()
{   
   global g_Active_Id
   global g_ListBox_Id
   global g_ManualActivate
   
   if (g_Active_Id && g_ListBox_Id) {
      WinGet, Temp_id, ID, A   
      IfEqual, Temp_id, %g_ListBox_Id%
      {
         ;set so we don't process this activation
         g_ManualActivate := true
         WinActivate, ahk_id %g_Active_Id%
         return, true
      }
   }
   return, false
}
   
   
;------------------------------------------------------------------------

; Wrapper function to ensure we always enable the WinEventHook after waiting for an active window
; Returns true if the current window is included
GetIncludedActiveWindow()
{
   CurrentWindowIsActive := GetIncludedActiveWindowGuts()
   EnableWinHook()
   Return, CurrentWindowIsActive
}

GetIncludedActiveWindowGuts()
{
   global g_Active_Id
   global g_Active_Title
   global g_Helper_Id
   global g_LastActiveIdBeforeHelper
   global g_ListBox_Id
   global g_MouseWin_Id
   Process, Priority,,Normal
   ;Wait for Included Active Window
   
   CurrentWindowIsActive := true
   
   Loop
   {
      WinGet, ActiveId, ID, A
      WinGet, ActiveProcess, ProcessName, ahk_id %ActiveId%
      WinGetTitle, ActiveTitle, ahk_id %ActiveId%
      IfEqual, ActiveId, 
      {
         IfNotEqual, g_MouseWin_Id,
         {
            IfEqual, g_MouseWin_Id, %g_ListBox_Id% 
            {
               WinActivate, ahk_id %g_Active_Id%
               Return, CurrentWindowIsActive
            }
         }
         
         CurrentWindowIsActive := false
         InactivateAll()
         ;Wait for any window to be active
         WinWaitActive, , , , ZZZYouWillNeverFindThisStringInAWindowTitleZZZ
         Continue
      }
      IfEqual, ActiveId, %g_Helper_Id%
         Break
      IfEqual, ActiveId, %g_ListBox_Id%
         Break
      If CheckForActive(ActiveProcess,ActiveTitle)
         Break
      
      CurrentWindowIsActive := false
      InactivateAll()
      SetTitleMatchMode, 3 ; set the title match mode to exact so we can detect a window title change
      ; Wait for the current window to no longer be active
      WinWaitNotActive, %ActiveTitle% ahk_id %ActiveId%
      SetTitleMatchMode, 2
      ActiveId = 
      ActiveTitle =
      ActiveProcess =
   }

   IfEqual, ActiveId, %g_ListBox_Id%
   {
      g_Active_Id :=  ActiveId
      g_Active_Title := ActiveTitle
      Return, CurrentWindowIsActive
   }
   
   ;if we are in the Helper Window, we don't want to re-enable script functions
   IfNotEqual, ActiveId, %g_Helper_Id%
   {
      ; Check to see if we need to reopen the helper window
      MaybeOpenOrCloseHelperWindow(ActiveProcess,ActiveTitle,ActiveId)
      SuspendOff()
      ;Set the process priority back to High
      Process, Priority,,High
      g_LastActiveIdBeforeHelper = %ActiveId%
      
   } else {
      IfNotEqual, g_Active_Id, %g_Helper_Id%
         g_LastActiveIdBeforeHelper = %g_Active_Id%               
   }
   
   global g_LastInput_Id
   ;Show the ListBox if the old window is the same as the new one
   IfEqual, ActiveId, %g_LastInput_Id%
   {
      WinWaitActive, ahk_id %g_LastInput_Id%,,0
      ;Check Caret Position again
      CheckForCaretMove("LButton")
      ShowListBox()      
   } else {
      CloseListBox()
   }
   g_Active_Id :=  ActiveId
   g_Active_Title := ActiveTitle
   Return, CurrentWindowIsActive
}

CheckForActive(ActiveProcess,ActiveTitle)
{
   ;Check to see if the Window passes include/exclude tests
   global g_InSettings
   global prefs_ExcludeProgramExecutables
   global prefs_ExcludeProgramTitles
   global prefs_IncludeProgramExecutables
   global prefs_IncludeProgramTitles
   
   quotechar := """"
   
   If g_InSettings
      Return,
   
   Loop, Parse, prefs_ExcludeProgramExecutables, |
   {
      IfEqual, ActiveProcess, %A_LoopField%
         Return,
   }
   
   Loop, Parse, prefs_ExcludeProgramTitles, |
   {
      
      if (SubStr(A_LoopField, 1, 1) == quotechar && SubStr(A_LoopField, StrLen(A_LoopField), 1) == quotechar)
      {
         StringTrimLeft, TrimmedString, A_LoopField, 1
         StringTrimRight, TrimmedString, TrimmedString, 1
         IfEqual, ActiveTitle, %TrimmedString%
         {
            return,
         }
      }  else IfInString, ActiveTitle, %A_LoopField%
      {
         return,
      }
   }

   IfEqual, prefs_IncludeProgramExecutables,
   {
      IfEqual, prefs_IncludeProgramTitles,
         Return, 1
   }

   Loop, Parse, prefs_IncludeProgramExecutables, |
   {
      IfEqual, ActiveProcess, %A_LoopField%
         Return, 1
   }

   Loop, Parse, prefs_IncludeProgramTitles, |
   {
      if (SubStr(A_LoopField, 1, 1) == quotechar && SubStr(A_LoopField, StrLen(A_LoopField), 1) == quotechar)
      {
         StringTrimLeft, TrimmedString, A_LoopField, 1
         StringTrimRight, TrimmedString, TrimmedString, 1
         IfEqual, ActiveTitle, %TrimmedString%
         {
            Return, 1
         }
      } else IfInString, ActiveTitle, %A_LoopField%
      {
         Return, 1
      }
   }

   Return, 
}

;------------------------------------------------------------------------
      
ReturnWinActive()
{
   global g_Active_Id
   global g_Active_Title
   global g_InSettings
   
   IF g_InSettings
      Return
   
   if (SwitchOffListBoxIfActive())
   {
      return, true
   }
   
   WinGet, Temp_id, ID, A
   WinGetTitle, Temp_Title, ahk_id %Temp_id%
   Last_Title := g_Active_Title
   ; remove all asterisks, dashes, and spaces from title in case saved value changes
   StringReplace, Last_Title, Last_Title,*,,All
   StringReplace, Temp_Title, Temp_Title,*,,All
   StringReplace, Last_Title, Last_Title,%A_Space%,,All
   StringReplace, Temp_Title, Temp_Title,%A_Space%,,All
   StringReplace, Last_Title, Last_Title,-,,All
   StringReplace, Temp_Title, Temp_Title,-,,All
   Return, (( g_Active_Id == Temp_id ) && ( Last_Title == Temp_Title ))
}

; end of Window.ahk
   ; #Include %A_ScriptDir%\Includes\Conversions.ahk
   ; #Include %A_ScriptDir%\Includes\Helper.ahk
   ; #Include %A_ScriptDir%\Includes\ListBox.ahk
   ; #Include %A_ScriptDir%\Includes\Preferences File.ahk
   ; #Include %A_ScriptDir%\Includes\Sending.ahk
   ; #Include %A_ScriptDir%\Includes\Settings.ahk
   ; #Include %A_ScriptDir%\Includes\Window.ahk

   ; #Include <DBA>
   ; #Include <_Struct>

;end of includes

;start of p()
p(oArray="")
{
    
    if IsObject(oArray)
    {
        if IsArray(oArray)
            msgbox % Array_Print(oArray)
        else
            msgbox % ObjectPrint(oArray)
    }
    Else
        msgbox % oArray
}

;https://www.autohotkey.com/boards/viewtopic.php?t=64332#p275856 by jeeswg
IsArray(oArray)
{
    local
    if !ObjCount(oArray)
        return 1
    if !(ObjCount(oArray) = ObjLength(oArray))
        || !(ObjMinIndex(oArray) = 1)
    return 0
    for vKey in oArray
        if !(vKey = A_Index)
        return 0
    return 1
}

;https://autohotkey.com/board/topic/85201-array-deep-copy-treeview-viewer-and-more/ by GeekDude
Array_Print(Array) 
{
    
    if Array_IsCircle(Array)
        return "Error: Circular refrence"
    Output=
    For Key23, Value24 in Array
    {
        
        
        If (IsObject(Value24))
        {
            if IsArray(Value24)
                Output .= "[" . Array_Print(Value24) . "]"
            else
            Output .= "{" . ObjectPrint(Value24) . "}"
    }
    
    Else If Value24 is not number
        Output .= """" . Value24 . """"
    Else
        Output .= Value24
    
    Output .= ", "
}
StringTrimRight, OutPut, OutPut, 2
Return OutPut
}

ObjectPrint(Array) {
    if Array_IsCircle(Array)
        return "Error: Circular refrence"
    Output=
    For Key23, Value24 in Array
    {
        If Key23 is not Number
            Output .= """" . Key23 . """:"
        Else
            Output .= Key23 . ":"
        
        If (IsObject(Value24))
        {
            if IsArray(Value24)
                Output .= "[" . Array_Print(Value24) . "]"
            else
            Output .= "{" . ObjectPrint(Value24) . "}"
    }
    Else If Value24 is not number
        Output .= """" . Value24 . """"
    Else
        Output .= Value24
    
    Output .= ", "
}
StringTrimRight, OutPut, OutPut, 2
Return OutPut
}

Array_IsCircle(Obj, Objs=0)
{
    if !Objs
        Objs := {}
    For Key23, Val in Obj
        if (IsObject(Val)&&(Objs[&Val]||Array_IsCircle(Val,(Objs,Objs[&Val]:=1))))
        return 1
    return 0
}
;end of p()

   Jxon_Load(ByRef src, args*)
   {
      static q := Chr(34)

      key := "", is_key := false
      stack := [ tree := [] ]
      is_arr := { (tree): 1 }
      next := q . "{[01234567890-tfn"
      pos := 0
      while ( (ch := SubStr(src, ++pos, 1)) != "" )
      {
         if InStr(" `t`n`r", ch)
            continue
         if !InStr(next, ch, true)
         {
            ln := ObjLength(StrSplit(SubStr(src, 1, pos), "`n"))
            col := pos - InStr(src, "`n",, -(StrLen(src)-pos+1))

            msg := Format("{}: line {} col {} (char {})"
            , (next == "") ? ["Extra data", ch := SubStr(src, pos)][1]
            : (next == "'") ? "Unterminated string starting at"
            : (next == "\") ? "Invalid \escape"
            : (next == ":") ? "Expecting ':' delimiter"
            : (next == q) ? "Expecting object key enclosed in double quotes"
            : (next == q . "}") ? "Expecting object key enclosed in double quotes or object closing '}'"
            : (next == ",}") ? "Expecting ',' delimiter or object closing '}'"
            : (next == ",]") ? "Expecting ',' delimiter or array closing ']'"
            : [ "Expecting JSON value(string, number, [true, false, null], object or array)"
            , ch := SubStr(src, pos, (SubStr(src, pos)~="[\]\},\s]|$")-1) ][1]
            , ln, col, pos)
            throw Exception(msg, -1, ch)
         }

         is_array := is_arr[obj := stack[1]]

         if i := InStr("{[", ch)
         {
            val := (proto := args[i]) ? new proto : {}
               is_array? ObjPush(obj, val) : obj[key] := val
               ObjInsertAt(stack, 1, val)

               is_arr[val] := !(is_key := ch == "{")
               next := q . (is_key ? "}" : "{[]0123456789-tfn")
            }

            else if InStr("}]", ch)
            {
               ObjRemoveAt(stack, 1)
            next := stack[1]==tree ? "" : is_arr[stack[1]] ? ",]" : ",}"
            }

            else if InStr(",:", ch)
            {
               is_key := (!is_array && ch == ",")
               next := is_key ? q : q . "{[0123456789-tfn"
            }

            else ; string | number | true | false | null
            {
               if (ch == q) ; string
               {
                  i := pos
                  while i := InStr(src, q,, i+1)
                  {
                     val := StrReplace(SubStr(src, pos+1, i-pos-1), "\\", "\u005C")
                     static end := A_AhkVersion<"2" ? 0 : -1
                     if (SubStr(val, end) != "\")
                        break
                  }
                  if !i ? (pos--, next := "'") : 0
                     continue

                  pos := i ; update pos

                  val := StrReplace(val, "\/", "/")
                  , val := StrReplace(val, "\" . q, q)
                  , val := StrReplace(val, "\b", "`b")
                  , val := StrReplace(val, "\f", "`f")
                  , val := StrReplace(val, "\n", "`n")
                  , val := StrReplace(val, "\r", "`r")
                  , val := StrReplace(val, "\t", "`t")

                  i := 0
                  while i := InStr(val, "\",, i+1)
                  {
                     if (SubStr(val, i+1, 1) != "u") ? (pos -= StrLen(SubStr(val, i)), next := "\") : 0
                        continue 2

                     ; \uXXXX - JSON unicode escape sequence
                     xxxx := Abs("0x" . SubStr(val, i+2, 4))
                     if (A_IsUnicode || xxxx < 0x100)
                        val := SubStr(val, 1, i-1) . Chr(xxxx) . SubStr(val, i+6)
                  }

                  if is_key
                  {
                     key := val, next := ":"
                     continue
                  }
               }

               else ; number | true | false | null
               {
                  val := SubStr(src, pos, i := RegExMatch(src, "[\]\},\s]|$",, pos)-pos)

                  ; For numerical values, numerify integers and keep floats as is.
                  ; I'm not yet sure if I should numerify floats in v2.0-a ...
                  static number := "number", integer := "integer"
                  if val is %number%
                  {
                     if val is %integer%
                        val += 0
                  }
                  ; in v1.1, true,false,A_PtrSize,A_IsUnicode,A_Index,A_EventInfo,
                  ; SOMETIMES return strings due to certain optimizations. Since it
                  ; is just 'SOMETIMES', numerify to be consistent w/ v2.0-a
                  else if (val == "true" || val == "false")
                     val := %value% + 0
                  ; AHK_H has built-in null, can't do 'val := %value%' where value == "null"
                  ; as it would raise an exception in AHK_H(overriding built-in var)
                  else if (val == "null")
                     val := ""
                  ; any other values are invalid, continue to trigger error
                  else if (pos--, next := "#")
                     continue

                  pos += i-1
               }

               is_array? ObjPush(obj, val) : obj[key] := val
            next := obj==tree ? "" : is_array ? ",]" : ",}"
            }
         }

         return tree[1]
      }

      DownloadToVar(URL)
      {
         HTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")	; https://msdn.microsoft.com/en-us/library/windows/desktop/aa384106(v=vs.85).aspx
         HTTP.Open("GET", URL)
         HTTP.Send()
         HTTP.WaitForResponse()
         Return HTTP.ResponseText
      }
      ; DownloadToVar(url){
      ;  if(!regExMatch(url,"i)https?://"))
      ;   url:="https://" url
      ;  hObject:=ComObjCreate("WinHttp.WinHttpRequest.5.1")
      ;  hObject.Open("GET",url)
      ;  hObject.Send()
      ;  return hObject.ResponseText
      ; }
      $^+h::
      $!h::
      $#h::
         send,{lwin up}
         ; Get the selected text. The clipboard is used instead of "ControlGet Selected"
         ; as it works in more editors and word processors, java apps, etc. Save the
         ; current clipboard contents to be restored later.
         AutoTrim On ; Delete any leading and trailing whitespace on the clipboard.  Why would you want this?
         ; ClipboardOld = %ClipboardAll%
         Clipboard = ; Must start off blank for detection to work.
         Send, ^c
         Send, ^c
         sleep, 50
         word:=Clipboard
         ; ClipWait 1
         if ErrorLevel ; ClipWait timed out.
            return
         ; Replace CRLF and/or LF with `n for use in a "send-raw" hotstring:
         ; The same is done for any other characters that might otherwise
         ; be a problem in raw mode:
         ; StringReplace, Hotstring, Clipboard, ``, ````, All ; Do this replacement first to avoid interfering with the others below.
         ; StringReplace, Hotstring, Hotstring, `r`n, ``r, All ; Using `r works better than `n in MS Word, etc.
         ; StringReplace, Hotstring, Hotstring, `n, ``r, All
         ; StringReplace, Hotstring, Hotstring, %A_Tab%, ``t, All
         ; StringReplace, Hotstring, Hotstring, `;, ```;, All
         ; Clipboard = %ClipboardOld% ; Restore previous contents of clipboard.
         ; This will move the InputBox's caret to a more friendly position:
         ;SetTimer, MoveCaret, 10
         ; Show the InputBox, providing the default hotstring:

         if (youtube_dlGuiFirstTime)
         {
            margin:=[10,10]
            youtube_dlGuiFirstTime:=false
            editSize:=[200, 25]
            textSize:=[0, 0]
            optionsPos:=[textSize[1]+margin[1], margin[2]]
            edit1Pos:=[textSize[1]+margin[1], editSize[2] + margin[2]]
            edit2Pos:=[textSize[1]+margin[1], 2*editSize[2] + margin[2]]
            textPos:=[10, ZTrim(editPos[2]+1.5) ]
            guiSize:=[editSize[1]+textSize[1]+ margin[1]*2, 3*editSize[2]+ margin[2]*2]
            guiPos:=[A_ScreenWidth/2-guiSize[1]/2,A_ScreenHeight/2-guiSize[2]/2]
            Name2=youtube-dl options
            Gui,Font,s12 w500 q5, Consolas
            Gui,add,Edit, % "x" optionsPos[1] " y" optionsPos[2] " w" editSize[1] " h" editSize[2] " voptions"
            Gui,add,Edit, % "x" edit1Pos[1] " y" edit1Pos[2] " w" editSize[1] " h" editSize[2] " vE1"
            Gui,add,Edit, % "x" edit2Pos[1] " y" edit2Pos[2] " w" editSize[1] " h" editSize[2] " vE2 hwndhwndE2"
         }
         GuiControl, Text,options,*?C
         GuiControl, Text,E1 ,%word%
         GuiControl, Text,E2 ,%word%

         ; GuiControl, Focus, E2

         ; SendMessage, 0xB1,0,% StrLen(Hotstring),,ahk_id %hwndE2%
         ; SendMessage, 0xB1,0,% StrLen(Hotstring),Edit3

         ; SendMessage, 0xB1,0,% StrLen(Hotstring),E2
         ControlFocus,, ahk_id %hwndE2%
         SendMessage, 177, 0, -1,, ahk_id %hwndE2% ;select all

         Gui,show, % "x" guiPos[1] " y" guiPos[2] " w" guiSize[1] " h" guiSize[2] ,%HotStringWinTitle%

         send,{lwin up}

         ;sleep, 10

         ; send, ^a

         ; Gosub, didYouMeanFirst
         ; Otherwise, add the hotstring and reload the script:

         if (word)
         {
            ; url=https://www.google.com/search?q=%word%
            ; url:="https://www.google.com/search?q=" word
            ; FileDelete, %fileLocation%
            ; UrlDownloadToFile, %url%, %fileLocation%
            OutputVar:=URLDownloadToVarWithHeader("https://www.google.com/search?q=" word)
            ; clipboard:=OutputVar
            ; p(OutputVar)
            ; FileRead, OutputVar, %fileLocation%
            ; clipboard:=OutputVar
            ; p(OutputVar)
            pos:=InStr(OutputVar, "spell=1")
            if(pos)
            {
               cut:=SubStr(OutputVar, 1 , pos-1)

               equalSign := InStr(cut, "=" ,, 0)

               equalSignString:=SubStr(cut, equalSign+1)

               lengthOfOptional:=0

               if (SubStr(equalSignString, -4)="&amp;")  ;last 4 chars
               {
                  lengthOfOptional:=5
               }

               autocorrected:=SubStr(equalSignString, 1, StrLen(equalSignString) - lengthOfOptional)

               autocorrected := StrReplace(autocorrected, "+" , " ")

               ; send, %autocorrected%
               GuiControl, Text,E2 ,%autocorrected%
            }
            else
            {
               IF !( ReturnWinActive() ) 
               {
                  Critical, Off
                  GetIncludedActiveWindow()
               } else { 
                  Critical, Off
               }
               response:=DownloadToVar("http://suggestqueries.google.com/complete/search?client=firefox&q=" word)
               array:=Jxon_Load(response)
               wordSuggestions:=array[2]

               Thread, NoTimers
               g_Word=REEEEEEEEEE
               EnableKeyboardHotKeys()

               RecomputeMatches()
            }
         }

      return
/* 
MoveCaret:
   IfWinNotActive, New Hotstring
      return
   ; Otherwise, move the InputBox's insertion point to where the user will type the abbreviation.
   send, {Lwin up}
   Send {HOME}
   Loop % StrLen(Hotstring) + 4
      SendInput {Right}
   sendInput, +{end}
   Gosub, didYouMeanFirst
   SetTimer, MoveCaret, Off
return
      */

      ;start of functions
      URLDownloadToVarWithHeader(url){
         hObject:=ComObjCreate("WinHttp.WinHttpRequest.5.1")
         hObject.Open("GET",url)
         hObject.SetRequestHeader("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)")

         hObject.Send()
      return hObject.ResponseText
   }

   #if WinActive("New Hotstring") and !winexist("Word List Appears Here.")
      $enter::
      ; p("enter in wrong place")
      Gui, Submit, hide
      ;Clipboard:=e2

      Hotstring:=E1
      Corrected_Hotstring:=E2

      FileAppend, `n:%options%:%Hotstring%::%Corrected_Hotstring%, %autocorrectPath% ; Put a `n at the beginning in case file lacks a blank line at its end.
      ; it would be best if it overwrote the string you had highlighted with the replacement you just typed in
      send,{lwin up}
      send, %Corrected_Hotstring%

      Run, "%autocorrectPath%"
      DisableKeyboardHotKeys()

   return
my code is really messy, but it works, so I don't bother.
Last edited by MrDoge on 13 Nov 2020, 21:54, edited 2 times in total.
trust_me
Posts: 98
Joined: 29 Jul 2017, 10:46

Re: Google Autocorrect "Did you mean:"

13 Nov 2020, 04:56

Code: Select all

test.ahk (9) : ==> This line does not contain a recognized action.
     Specifically: g6
Some paste errors ?

Nice ,some interesting ideas :thumbup:

Thanks for sharing !
MrDoge
Posts: 160
Joined: 27 Apr 2020, 21:29

Re: Google Autocorrect "Did you mean:"

13 Nov 2020, 22:03

ty, I forgot about dependencies

I've edited the script above (putting all dependencies in the same script oof).

can you try again ?
AHKStudent
Posts: 1472
Joined: 05 May 2018, 12:23

Re: Google Autocorrect "Did you mean:"

13 Nov 2020, 23:56

very impressive work, works fast, thank you
User avatar
TheDewd
Posts: 1513
Joined: 19 Dec 2013, 11:16
Location: USA

Re: Google Autocorrect "Did you mean:"

14 Nov 2020, 17:16

Here's my attempt at making the "Did you mean?" script simpler.

Code: Select all

MsgBox, % GoogleAutoCorrect("Helllo Worlpd")

GoogleAutoCorrect(Word) {
	ReqURL := "https://www.google.com/search?q=" Word
	objReq := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	objReq.Open("GET", ReqURL)
	objReq.SetRequestHeader("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)")
	objReq.Send()

	HTML := objReq.ResponseText

	RegExMatch(HTML, "value=""(.*?)""", A)
	RegExMatch(HTML, ";spell=1.*?>(.*?)<\/a>", B)

	return (B1 ? B1 : A1)
}

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 219 guests