diff --git a/Source/Includes/Conversions.ahk b/Source/Includes/Conversions.ahk index e7f7b33..3cb08fb 100644 --- a/Source/Includes/Conversions.ahk +++ b/Source/Includes/Conversions.ahk @@ -97,10 +97,16 @@ RebuildDatabase() CreateWordIndex() + CreateWordReplacementIndex() + CreateLastStateTable() CreateWordlistsTable() + CreateWordRelationTable() + + CreateWordRelationView() + SetDbVersion() g_WordListDB.EndTransaction() @@ -249,7 +255,7 @@ 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) );") + IF not g_WordListDB.Query("CREATE TABLE " . WordsTableName . " (ID INTEGER PRIMARY KEY, wordindexed TEXT NOT NULL, word TEXT NOT NULL, count INTEGER, worddescription TEXT, wordreplacement TEXT NOT NULL, lastused INTEGER DEFAULT (0), UNIQUE (word, wordreplacement));") { ErrMsg := g_WordListDB.ErrMsg() ErrCode := g_WordListDB.ErrCode() @@ -271,6 +277,19 @@ CreateWordIndex() } } +CreateWordReplacementIndex() +{ + global g_WordListDB + + IF not g_WordListDB.Query("CREATE INDEX WordReplacementIndexed ON Words (word, wordreplacement);") + { + ErrMsg := g_WordListDB.ErrMsg() + ErrCode := g_WordListDB.ErrCode() + msgbox Cannot Create WordReplacementIndexed Index - fatal error: %ErrCode% - %ErrMsg% + ExitApp + } +} + CreateWordlistsTable() { global g_WordListDB @@ -282,4 +301,30 @@ CreateWordlistsTable() msgbox Cannot Create Wordlists Table - fatal error: %ErrCode% - %ErrMsg% ExitApp } +} + +CreateWordRelationTable() +{ + global g_WordListDB + + IF not g_WordListDB.Query("CREATE TABLE WordRelations ( word_minus1 INTEGER NOT NULL REFERENCES Words (ID) ON DELETE CASCADE, word INTEGER NOT NULL REFERENCES Words (ID) ON DELETE CASCADE, count INTEGER, lastused INTEGER DEFAULT (0), PRIMARY KEY ( word_minus1, word ) ); ") + { + ErrMsg := g_WordListDB.ErrMsg() + ErrCode := g_WordListDB.ErrCode() + msgbox Cannot Create WordRelations Table - fatal error: %ErrCode% - %ErrMsg% + ExitApp + } +} + +CreateWordRelationView() +{ + global g_WordListDB + + IF not g_WordListDB.Query("CREATE VIEW VW_WordRelations AS SELECT WordRelations.word_minus1 AS word_minus1_ID, Words_0.word AS word_minus1, WordRelations.word AS word_ID, Words.word AS word, WordRelations.count, WordRelations.lastused FROM words AS Words_0 INNER JOIN ( WordRelations INNER JOIN Words ON WordRelations.word = Words.ID ) ON Words_0.ID = WordRelations.word_minus1; ") + { + ErrMsg := g_WordListDB.ErrMsg() + ErrCode := g_WordListDB.ErrCode() + msgbox Cannot Create WordRelations View - fatal error: %ErrCode% - %ErrMsg% + ExitApp + } } \ No newline at end of file diff --git a/Source/Includes/Sending.ahk b/Source/Includes/Sending.ahk index c12234e..7e568ae 100644 --- a/Source/Includes/Sending.ahk +++ b/Source/Includes/Sending.ahk @@ -33,6 +33,7 @@ SendWord(WordIndex) } ; Update Typed Count UpdateWordCount(sending,0) + UpdateWordHierarchy(sending) SendFull(sending, ForceBackspace) ClearAllVars(true) Return @@ -64,7 +65,7 @@ SendFull(SendValue,ForceBackspace=false) { Capitalize := true } - } else if ( RegExMatch(Substr(g_Word, 1, 1), "S)[A-ZÀ-ÖØ-ß]") > 0 ) + } else if ( RegExMatch(Substr(g_Word, 1, 1), "S)[A-ZÀ-ÖØ-ß]") > 0 ) { Capitalize := true } @@ -234,4 +235,4 @@ SendCompatible(SendValue,ForceSendForInput) Return } -;------------------------------------------------------------------------ \ No newline at end of file +;------------------------------------------------------------------------ diff --git a/Source/Includes/Wordlist.ahk b/Source/Includes/Wordlist.ahk index b5bc519..a56738e 100644 --- a/Source/Includes/Wordlist.ahk +++ b/Source/Includes/Wordlist.ahk @@ -177,6 +177,7 @@ AddWordToList(AddWord,ForceCountNewOnly,ForceLearn=false, ByRef LearnedWordsCoun global g_WordListDB if !(LearnedWordsCount) { + ;This section handles the creation of new wordlist database from text files. StringSplit, SplitAddWord, AddWord, | IfEqual, SplitAddWord2, D @@ -205,6 +206,7 @@ AddWordToList(AddWord,ForceCountNewOnly,ForceLearn=false, ByRef LearnedWordsCoun IfEqual, g_WordListDone, 0 ;if this is read from the wordlist { + ;If wordlist is not yet processed... IfNotEqual,LearnedWordsCount, ;if this is a stored learned word, this will only have a value when LearnedWords are read in from the wordlist { ; must update wordreplacement since SQLLite3 considers nulls unique @@ -237,13 +239,25 @@ AddWordToList(AddWord,ForceCountNewOnly,ForceLearn=false, ByRef LearnedWordsCoun IfNotEqual, ForceCountNewOnly, 1 { IF (StrLen(AddWord) < prefs_LearnLength) ; don't add the word if it's not longer than the minimum length for learning if we aren't force learning it + { + ;Word not learned: Length less than LearnLength + ClearWordHierarchy() Return + } if AddWord contains %prefs_ForceNewWordCharacters% + { + ;Word not learned: word contains character from prefs_ForceNewWordCharacters + ClearWordHierarchy() Return + } if AddWord contains %prefs_DoNotLearnStrings% + { + ;Word not learned: word contains string from prefs_DoNotLearnStrings + ClearWordHierarchy() Return + } CountValue = 1 @@ -265,12 +279,19 @@ AddWordToList(AddWord,ForceCountNewOnly,ForceLearn=false, ByRef LearnedWordsCoun IF ( CountValue < prefs_LearnCount ) { - g_WordListDB.QUERY("UPDATE words SET count = ('" . prefs_LearnCount . "') WHERE word = '" . AddWordTransformed . "');") + ;Update the 'lastused' field as well + g_WordListDB.QUERY("UPDATE words SET count = ('" . prefs_LearnCount . "'), lastused = (select datetime(strftime('%s','now'), 'unixepoch')) WHERE word = '" . AddWordTransformed . "';") } } else { UpdateWordCount(AddWord,0) ;Increment the word count if it's already in the list and we aren't forcing it on } } + + ;Since this section can only be reached by the above two conditions, this means that the word was either new and added; or it was + ;previous known and updated in the list. + ;Time to record the word hierarchy (parent-child relationship) information: + + UpdateWordHierarchy(AddWordTransformed) } Return @@ -306,7 +327,7 @@ CheckValid(Word,ForceLearn=false) { return } - } else if ( RegExMatch(Word, "S)[a-zA-Zà-öø-ÿÀ-ÖØ-ß]") = 0 ) + } else if ( RegExMatch(Word, "S)[a-zA-Zà-öø-ÿÀ-ÖØ-ß]") = 0 ) { Return } @@ -370,13 +391,34 @@ UpdateWordCount(word,SortOnly) Return StringReplace, wordEscaped, word, ', '', All - g_WordListDB.Query("UPDATE words SET count = count + 1 WHERE word = '" . wordEscaped . "';") + ;Update word count AND the lastused time for this word + g_WordListDB.Query("UPDATE words SET count = count + 1, lastused = (select datetime(strftime('%s','now'), 'unixepoch')) WHERE word = '" . wordEscaped . "';") + Return } ;------------------------------------------------------------------------ +UpdateWordHierarchyCount(word) +{ + global prefs_LearnMode + global g_WordListDB + global g_Word_Minus1 + ;Word = Word to increment count for + + ;Should only be called when LearnMode is on + IfEqual, prefs_LearnMode, Off + Return + + StringReplace, wordEscaped, word, ', '', All + StringReplace, wordMinus1Escaped, g_Word_Minus1, ', '', All + g_WordListDB.Query("UPDATE WordRelations SET count = count + 1, lastused = (select datetime(strftime('%s','now'), 'unixepoch')) WHERE word = (select ID from words where word = '" . wordEscaped . "') and word_minus1 = (select ID from words where word = '" . wordMinus1Escaped . "');") + Return +} + +;------------------------------------------------------------------------ + CleanupWordList(LearnedWordsOnly := false) { ;Function cleans up all words that are less than the LearnCount threshold or have a NULL for count @@ -386,10 +428,20 @@ CleanupWordList(LearnedWordsOnly := false) global prefs_LearnCount Progress, M, Please wait..., Cleaning wordlist, %g_ScriptTitle% if (LearnedWordsOnly) { - g_WordListDB.Query("DELETE FROM Words WHERE count < " . prefs_LearnCount . " AND count IS NOT NULL;") + g_WordListDB.Query("DELETE FROM Words WHERE count < " . prefs_LearnCount . " AND count IS NOT NULL and lastused < DATE('now','-7 day');") } else { g_WordListDB.Query("DELETE FROM Words WHERE count < " . prefs_LearnCount . " OR count IS NULL;") } + + ;Zap all lastused values greater than a threshold (anything last used earlier than today) to reduce dataset size for lastused functionality + g_WordListDB.Query("UPDATE words SET lastused = 0 where lastused < DATE('now','-7 day') and lastused <> 0;") + + ;Remove all word relationship that are under the threshold if older than 7 days. + g_WordListDB.Query("DELETE FROM WordRelations WHERE count < " . prefs_LearnCount . " AND lastused < DATE ('now','-7 day');") + + ;Remove all words relationships that are not in the word list [not really required due to constraint on table] + g_WordListDB.Query("DELETE FROM WordRelations WHERE word NOT IN (SELECT ID FROM words) OR word_minus1 NOT IN (SELECT ID FROM words);") + Progress, Off } @@ -467,12 +519,42 @@ StrUnmark(string) { ; Remove combining marks and return result. string := RegExReplace(StrGet(&buf, len, "UTF-16"), "\pM") - StringReplace, string, string, æ, ae, All - StringReplace, string, string, Æ, AE, All - StringReplace, string, string, œ, oe, All - StringReplace, string, string, Œ, OE, All - StringReplace, string, string, ß, ss, All + StringReplace, string, string, æ, ae, All + StringReplace, string, string, Æ, AE, All + StringReplace, string, string, Å“, oe, All + StringReplace, string, string, Å’, OE, All + StringReplace, string, string, ß, ss, All return, string -} \ No newline at end of file +} +;------------------------------------------------------------------------ + +BulkLearnFromClipboard(textblock) +{ + global g_TerminatingCharactersParsed + + ;Display progress bar window... + Progress, M, Please wait..., Bulk learning..., %g_ScriptTitle% + + ;Count how many individual items there are, we need this number to display + ;an accurate progress bar. + Loop, Parse, textblock, %g_TerminatingCharactersParsed%`r`n%A_Tab%%A_Space% + { + ;Count the individual items + Counter++ + } + + Loop, Parse, textblock, %g_TerminatingCharactersParsed%`r`n%A_Tab%%A_Space% + { + ;Display words to show progress... + ProgressPercent := Round(A_Index/Counter * 100) + Progress, %ProgressPercent%, Please wait..., %A_LoopField%, %g_ScriptTitle% + AddWordToList(A_LoopField, 0,"ForceLearn") + } + + ;Turn off progress bar window... + Progress, Off + + return +} diff --git a/Source/TypingAid.ahk b/Source/TypingAid.ahk index e11d755..2330ed7 100644 --- a/Source/TypingAid.ahk +++ b/Source/TypingAid.ahk @@ -1,1295 +1,1458 @@ -; 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 - -#NoTrayIcon -;disable hotkeys until setup is complete -Suspend, On -#NoEnv -ListLines Off - -g_OSVersion := GetOSVersion() - -;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 - -;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_NormalizationKD := 0x6 -g_NULL := 0 -g_Process_DPI_Unaware := 0 -g_Process_System_DPI_Aware := 1 -g_Process_Per_Monitor_DPI_Aware := 2 -g_PROCESS_QUERY_INFORMATION := 0x0400 -g_PROCESS_QUERY_LIMITED_INFORMATION := 0x1000 -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_DpiScalingFactor := A_ScreenDPI/96 -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 - -;Read in the WordList -ReadWordList() - -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() - -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 - AddWordToList(g_Word,0) - 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% - { - AddWordToList(g_Word,0) - ClearAllVars(true) - g_Word := InputChar - g_LastInput_Id := g_Active_Id - Return - } - - if InputChar in %prefs_ForceNewWordCharacters% - { - AddWordToList(g_Word,0) - ClearAllVars(true) - g_Word := InputChar - } else if InputChar in %prefs_EndWordCharacters% - { - g_Word .= InputChar - AddWordToList(g_Word, 1) - 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 { - AddWordToList(g_Word,0) - ClearAllVars(true) - Return - } - - ;Wait till minimum letters - IF ( StrLen(g_Word) < prefs_Length ) - { - CloseListBox() - Return - } - SetTimer, RecomputeMatchesTimer, -1 -} - -RecomputeMatchesTimer: - Thread, NoTimers - RecomputeMatches() - 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 - - 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, WordMatchOriginal, g_Word - - WordMatch := StrUnmark(WordMatchOriginal) - - StringUpper, WordMatch, WordMatch - - ; if a user typed an accented character, we should exact match on that accented character - if (WordMatch != WordMatchOriginal) { - WordAccentQuery = - LoopCount := StrLen(g_Word) - Loop, %LoopCount% - { - Position := A_Index - SubChar := SubStr(g_Word, Position, 1) - SubCharNormalized := StrUnmark(SubChar) - if !(SubCharNormalized == SubChar) { - StringUpper, SubCharUpper, SubChar - StringLower, SubCharLower, SubChar - StringReplace, SubCharUpperEscaped, SubCharUpper, ', '', All - StringReplace, SubCharLowerEscaped, SubCharLower, ', '', All - PrefixChars = - Loop, % Position - 1 - { - PrefixChars .= "?" - } - ; because SQLite cannot do case-insensitivity on accented characters using LIKE, we need - ; to handle it manually, so we need 2 searches for each accented character the user typed. - ;GLOB is used for consistency with the wordindexed search. - WordAccentQuery .= " AND (word GLOB '" . PrefixChars . SubCharUpperEscaped . "*' OR word GLOB '" . PrefixChars . SubCharLowerEscaped . "*')" - } - } - } else { - WordAccentQuery = - } - - 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 . WordAccentQuery - - 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 := g_WordListDB.Query("SELECT word, worddescription, wordreplacement FROM Words" . WhereQuery . OrderByQuery . " LIMIT " . LimitTotalMatches . ";") - - g_SingleMatch := Object() - g_SingleMatchDescription := Object() - g_SingleMatchReplacement := Object() - - for each, row in Matches.Rows - { - g_SingleMatch[++g_MatchTotal] := row[1] - g_SingleMatchDescription[g_MatchTotal] := row[2] - g_SingleMatchReplacement[g_MatchTotal] := row[3] - - continue - } - - ;If no match then clear Tip - IfEqual, g_MatchTotal, 0 - { - ClearAllVars(false) - Return - } - - SetupMatchPosition() - RebuildMatchList() - ShowListBox() -} - -;------------------------------------------------------------------------ - -~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 - - IfNotEqual, g_LastInput_Id, %g_MouseWin_Id% - { - Return - } - - SysGet, SM_CYCAPTION, 4 - SysGet, SM_CYSIZEFRAME, 33 - - TempY += SM_CYSIZEFRAME - IF ( ( MouseY >= TempY ) && (MouseY < (TempY + SM_CYCAPTION) ) ) - { - Return - } - - ; If we have a g_Word and an g_OldCaretX, check to see if the Caret moved - IfNotEqual, g_OldCaretX, - { - IfNotEqual, g_Word, - { - if (( g_OldCaretY != HCaretY() ) || (g_OldCaretX != HCaretX() )) - { - ; add the word if switching lines - AddWordToList(g_Word,0) - 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 - - IfNotEqual, prefs_LearnMode, On - { - Hotkey, $^+Delete, Off - } else { - Hotkey, $^+Delete, Off - ; We only want Ctrl-Shift-Delete enabled when the listbox is showing. - g_EnabledKeyboardHotKeys .= "$^+Delete" . g_DelimiterChar - } - - HotKey, $^+c, On - - 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 .= "$PgDn" . 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, %A_LoopField%, On - } - Return -} - -DisableKeyboardHotKeys() -{ - global g_DelimiterChar - global g_EnabledKeyboardHotKeys - Loop, Parse, g_EnabledKeyboardHotKeys, %g_DelimiterChar% - { - HotKey, %A_LoopField%, Off - } - Return -} - -;------------------------------------------------------------------------ - -#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 - -$^+h:: -MaybeOpenOrCloseHelperWindowManual() -Return - -$^+c:: -AddSelectedWordToList() -Return - -$^+Delete:: -DeleteSelectedWordFromList() -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 - { - SendCompatible(Key,0) - ProcessKey(Key,"") - IfEqual, prefs_NumPresses, 2 - SuspendOff() - Return - } - - if ( ( (WordIndex + 1 - MatchStart) > prefs_ListBoxRows) || ( g_Match = "" ) || (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()) - { - SendKey(Key) - ClearAllVars(false) - Return - } - - if ReturnLineWrong() - { - SendKey(Key) - ClearAllVars(true) - Return - } - - IfEqual, g_Word, ; only continue if word is not empty - { - SendKey(Key) - ClearAllVars(false) - 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() ) -} - -;------------------------------------------------------------------------ - -AddSelectedWordToList() -{ - ClipboardSave := ClipboardAll - Clipboard = - Sleep, 100 - SendCompatible("^c",0) - ClipWait, 0 - IfNotEqual, Clipboard, - { - AddWordToList(Clipboard,1,"ForceLearn") - } - Clipboard = %ClipboardSave% -} - -DeleteSelectedWordFromList() -{ - global g_MatchPos - global g_SingleMatch - - if !(g_SingleMatch[g_MatchPos] = "") ;only continue if g_SingleMatch is not empty - { - - DeleteWordFromList(g_SingleMatch[g_MatchPos]) - RecomputeMatches() - Return - } - -} - -;------------------------------------------------------------------------ - -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 - CloseListBox() - Ifequal,ClearWord,1 - { - g_Word = - g_OldCaretY= - g_OldCaretX= - g_LastInput_id= - g_ListBoxFlipped= - g_ListBoxMaxWordHeight= - } - - 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 = - } - } - } - } -} - -;------------------------------------------------------------------------ - -GetOSVersion() -{ - return ((r := DllCall("GetVersion") & 0xFFFF) & 0xFF) "." (r >> 8) -} - -;------------------------------------------------------------------------ - -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() - -; Update the Learned Words -MaybeUpdateWordlist() - -ExitApp - -#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 %A_ScriptDir%\Includes\Wordlist.ahk -#Include -#Include <_Struct> +; Based on version: TypingAid-master (2.22 + commits including Oct 27, 2015 from Github) +; 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 + +#NoTrayIcon +;disable hotkeys until setup is complete +Suspend, On +#NoEnv +ListLines Off + +g_OSVersion := GetOSVersion() + +;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 + +;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_NormalizationKD := 0x6 +g_NULL := 0 +g_Process_DPI_Unaware := 0 +g_Process_System_DPI_Aware := 1 +g_Process_Per_Monitor_DPI_Aware := 2 +g_PROCESS_QUERY_INFORMATION := 0x0400 +g_PROCESS_QUERY_LIMITED_INFORMATION := 0x1000 +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_DpiScalingFactor := A_ScreenDPI/96 +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 + +;Read in the WordList +ReadWordList() + +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() + +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 + AddWordToList(g_Word,0) + 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% + { + AddWordToList(g_Word,0) + ClearAllVars(true) + g_Word := InputChar + g_LastInput_Id := g_Active_Id + Return + } + + if InputChar in %prefs_ForceNewWordCharacters% + { + AddWordToList(g_Word,0) + ClearAllVars(true) + g_Word := InputChar + } else if InputChar in %prefs_EndWordCharacters% + { + g_Word .= InputChar + AddWordToList(g_Word, 1) + 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 { + AddWordToList(g_Word,0) + ClearAllVars(true) + Return + } + + ;Wait till minimum letters + IF ( StrLen(g_Word) < prefs_Length ) + { + CloseListBox() + Return + } + SetTimer, RecomputeMatchesTimer, -1 +} + +RecomputeMatchesTimer: + Thread, NoTimers + RecomputeMatches() + 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 g_Word_Minus1 + global g_Word_Minus2 + + 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, WordMatchOriginal, g_Word + + WordMatch := StrUnmark(WordMatchOriginal) + + StringUpper, WordMatch, WordMatch + + ; if a user typed an accented character, we should exact match on that accented character + if (WordMatch != WordMatchOriginal) { + WordAccentQuery = + LoopCount := StrLen(g_Word) + Loop, %LoopCount% + { + Position := A_Index + SubChar := SubStr(g_Word, Position, 1) + SubCharNormalized := StrUnmark(SubChar) + if !(SubCharNormalized == SubChar) { + StringUpper, SubCharUpper, SubChar + StringLower, SubCharLower, SubChar + StringReplace, SubCharUpperEscaped, SubCharUpper, ', '', All + StringReplace, SubCharLowerEscaped, SubCharLower, ', '', All + PrefixChars = + Loop, % Position - 1 + { + PrefixChars .= "?" + } + ; because SQLite cannot do case-insensitivity on accented characters using LIKE, we need + ; to handle it manually, so we need 2 searches for each accented character the user typed. + ;GLOB is used for consistency with the wordindexed search. + WordAccentQuery .= " AND (word GLOB '" . PrefixChars . SubCharUpperEscaped . "*' OR word GLOB '" . PrefixChars . SubCharLowerEscaped . "*')" + } + } + } else { + WordAccentQuery = + } + + 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 . WordAccentQuery + + 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) + WordCaseOrderStatement := "(CASE WHEN count IS NULL then " + IfEqual, prefs_ShowLearnedFirst, On + { + WordCaseOrderStatement .= "ROWID + 1 else 0" + } else { + WordCaseOrderStatement .= "ROWID else 'z'" + } + WordCaseOrderStatement .= "END) as ""Order1""" + + ;Add logic to factor 'lastused' date into the order of the matching words + ;86400 = number of seconds in a day. + ; - the logic here is to re-rank the lastused words from TODAY higher and subtract number of seconds since the day started. This way + ; the most recently used words will stay higher on the list. + WordCaseOrderStatement .= ", (CASE WHEN count IS NOT NULL AND DATE(lastused) = DATE('now') THEN (select max(count) from Words)+86400 -(strftime('%s',time('now'))-strftime('%s',time(lastused))) WHEN count IS NOT NULL then ( (count - " . Normalize . ") * ( 1 - ( '0.75' / (LENGTH(word) - " . WordLen . ")))) END ) as ""Order2""" + + WordRelationOrderStatement := "'z' as ""Order1"", (CASE WHEN DATE(lastused) = DATE('now') THEN (count*200) + (strftime('%s', TIME('now')) - strftime('%s', TIME(lastused))) ELSE (count*200) END) as ""Order2""" + + + MainWordQuery := "SELECT word, worddescription, wordreplacement," . WordCaseOrderStatement . " FROM Words" . WhereQuery . " LIMIT 180" + + if g_Word_Minus1 + { + ;OutputDebug, Two State Query + WordRelationQuery := "SELECT word, NULL as ""worddescription"", '' as ""wordreplacement"", " . WordRelationOrderStatement . " FROM VW_WordRelations WHERE word_minus1='" . g_Word_Minus1 . "' " . WordAccentQuery . " LIMIT 50" + MergedQuery := "select word, WORDDESCRIPTION, WORDREPLACEMENT, SUM(ORDER2) AS ORDER2 from (select * from (" . WordRelationQuery . ") UNION select * from (" . MainWordQuery . ")) GROUP BY word, wordreplacement, Order1 ORDER BY Order1, Order2 DESC" + } else { + ;OutputDebug, Single State Query + MergedQuery := "select word, WORDDESCRIPTION, WORDREPLACEMENT, SUM(ORDER2) AS ORDER2 from (" . MainWordQuery . ") GROUP BY word, wordreplacement, Order1 ORDER BY Order1, Order2 DESC" + } + + ;OutputDebug, % WordRelationQuery + ;OutputDebug, % MergedQuery + + Matches := g_WordListDB.Query(MergedQuery . ";") + + g_SingleMatch := Object() + g_SingleMatchDescription := Object() + g_SingleMatchReplacement := Object() + + for each, row in Matches.Rows + { + g_SingleMatch[++g_MatchTotal] := row[1] + g_SingleMatchDescription[g_MatchTotal] := row[2] + g_SingleMatchReplacement[g_MatchTotal] := row[3] + + continue + } + + ;If no match then clear Tip + IfEqual, g_MatchTotal, 0 + { + ClearAllVars(false) + Return + } + + SetupMatchPosition() + RebuildMatchList() + ShowListBox() +} + +;------------------------------------------------------------------------ + +~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 + + IfNotEqual, g_LastInput_Id, %g_MouseWin_Id% + { + Return + } + + SysGet, SM_CYCAPTION, 4 + SysGet, SM_CYSIZEFRAME, 33 + + TempY += SM_CYSIZEFRAME + IF ( ( MouseY >= TempY ) && (MouseY < (TempY + SM_CYCAPTION) ) ) + { + Return + } + + ; If we have a g_Word and an g_OldCaretX, check to see if the Caret moved + IfNotEqual, g_OldCaretX, + { + IfNotEqual, g_Word, + { + if (( g_OldCaretY != HCaretY() ) || (g_OldCaretX != HCaretX() )) + { + ; add the word if switching lines + AddWordToList(g_Word,0) + 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 + + IfNotEqual, prefs_LearnMode, On + { + Hotkey, $^+Delete, Off + } else { + Hotkey, $^+Delete, Off + ; We only want Ctrl-Shift-Delete enabled when the listbox is showing. + g_EnabledKeyboardHotKeys .= "$^+Delete" . g_DelimiterChar + } + + HotKey, $^+c, On + + 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 .= "$PgDn" . 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, %A_LoopField%, On + } + Return +} + +DisableKeyboardHotKeys() +{ + global g_DelimiterChar + global g_EnabledKeyboardHotKeys + Loop, Parse, g_EnabledKeyboardHotKeys, %g_DelimiterChar% + { + HotKey, %A_LoopField%, Off + } + Return +} + +;------------------------------------------------------------------------ + +#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 + +$^+h:: +MaybeOpenOrCloseHelperWindowManual() +Return + +$^+c:: +AddSelectedWordToList() +Return + +$^+Delete:: +DeleteSelectedWordFromList() +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 + { + SendCompatible(Key,0) + ProcessKey(Key,"") + IfEqual, prefs_NumPresses, 2 + SuspendOff() + Return + } + + if ( ( (WordIndex + 1 - MatchStart) > prefs_ListBoxRows) || ( g_Match = "" ) || (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()) + { + SendKey(Key) + ClearAllVars(false) + Return + } + + if ReturnLineWrong() + { + SendKey(Key) + ClearAllVars(true) + Return + } + + IfEqual, g_Word, ; only continue if word is not empty + { + SendKey(Key) + ClearAllVars(false) + 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() ) +} + +;------------------------------------------------------------------------ + +AddSelectedWordToList() +{ + ClipboardSave := ClipboardAll + Clipboard = + Sleep, 100 + SendCompatible("^c",0) + ClipWait, 0 + IfNotEqual, Clipboard, + { + showTrayTip("Forcing adding word '" . Clipboard . "'.", 1) + AddWordToList(Clipboard,1,"ForceLearn") + } + Clipboard = %ClipboardSave% +} + +DeleteSelectedWordFromList() +{ + global g_MatchPos + global g_SingleMatch + + if !(g_SingleMatch[g_MatchPos] = "") ;only continue if g_SingleMatch is not empty + { + + DeleteWordFromList(g_SingleMatch[g_MatchPos]) + RecomputeMatches() + Return + } + +} + +;------------------------------------------------------------------------ + +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 + Menu, Tray, add, Bulk Learn, BulkLearnFromClipboardMenu + 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 + CloseListBox() + Ifequal,ClearWord,1 + { + g_Word = + g_OldCaretY= + g_OldCaretX= + g_LastInput_id= + g_ListBoxFlipped= + g_ListBoxMaxWordHeight= + } + + g_SingleMatch = + g_SingleMatchDescription = + g_SingleMatchReplacement = + g_Match= + g_MatchPos= + g_MatchStart= + g_OriginalMatchStart= + Return +} + +;------------------------------------------------------------------------ + +; This is to blank all vars related to hierarchy matches +ClearWordHierarchy() +{ + global + g_Word_Minus1 = + g_Word_Minus2 = + ;OutputDebug, Clearing word hierarchy + Return +} + +;------------------------------------------------------------------------ + +; Update word hierachy (parent-child relationship) +UpdateWordHierarchy(AddWord) +{ + global prefs_DoNotLearnStrings + global prefs_ForceNewWordCharacters + global prefs_LearnCount + global prefs_LearnLength + global prefs_LearnMode + ;global g_WordListDone + global g_WordListDB + + global g_Word_Minus1 + global g_Word_Minus2 + + TransformWord(AddWord, AddWordReplacement, AddWordDescription, AddWordTransformed, AddWordIndexTransformed, AddWordReplacementTransformed, AddWordDescriptionTransformed) + + OutputDebug, --------[UpdateWordHierarchy]------------------------ + OutputDebug, % " g_Word_Minus2: [" . g_Word_Minus2 . "]" + OutputDebug, % " g_Word_Minus1: [" . g_Word_Minus1 . "]" + OutputDebug, % "AddWordTransformed: [" . AddWordTransformed . "]" + + if g_Word_Minus1 + { + OutputDebug, Hierarchy evaluation... + + ;Check if the word exists in our word hierachy table. + AddWordInList := g_WordListDB.Query("SELECT * FROM VW_WordRelations WHERE word = '" . AddWordTransformed . "' and word_minus1 = '" . g_Word_Minus1 . "';") + + IF !( AddWordInList.Count() > 0 ) + { + OutputDebug, [Hierarchy evaluation] word is not in the hierachy list... + ;if the word is not in the hierachy list... + IfNotEqual, ForceCountNewOnly, 1 + { + if (StrLen(AddWord) < prefs_LearnLength) ; don't add the word if it's not longer than the minimum length for learning if we aren't force learning it + Return + + if AddWord contains %prefs_ForceNewWordCharacters% + Return + + if AddWord contains %prefs_DoNotLearnStrings% + Return + + CountValue = 1 + + } else + { + CountValue := prefs_LearnCount ;set the count to LearnCount so it gets written to the file + } + + ; must update wordreplacement since SQLLite3 considers nulls unique + ;OutputDebug, % "[Hierarchy evaluation] INSERT INTO WordRelations (word_minus1, word, count, lastused) VALUES ((select ID from words where word = '" . g_Word_Minus1 . "'),(select ID from words where word = '" . AddWordTransformed . "'),'" . CountValue . "', (select datetime(strftime('%s','now'), 'unixepoch')));" + g_WordListDB.Query("INSERT INTO WordRelations (word_minus1, word, count, lastused) VALUES ((select ID from words where word = '" . g_Word_Minus1 . "'),(select ID from words where word = '" . AddWordTransformed . "'),'" . CountValue . "', (select datetime(strftime('%s','now'), 'unixepoch')));") + ;OutputDebug, [Hierarchy evaluation] Added to add new word relation: + + } else IfEqual, prefs_LearnMode, On + { + ;OutputDebug, [Hierarchy evaluation] word exists in the word hierarchy and LearnMode is turned 'On'... + ;If the word exists in the word hierarchy and we're LearnMode is turned 'On'... + + For each, row in AddWordInList.Rows + { + CountValue := row[3] + break + } + + IfEqual, ForceCountNewOnly, 1 + { + + IF ( CountValue < prefs_LearnCount ) + { + ;OutputDebug, % "[Hierarchy evaluation] UPDATE WordRelations SET count = ('" . prefs_LearnCount . "'), lastused = (select datetime(strftime('%s','now'), 'unixepoch')) WHERE word = (select ID from words where word = '" . AddWordTransformed . "') and word_minus1 = (select ID from words where word = '" . g_Word_Minus1 . "'));" + g_WordListDB.QUERY("UPDATE WordRelations SET count = ('" . prefs_LearnCount . "'), lastused = (select datetime(strftime('%s','now'), 'unixepoch')) WHERE word = (select ID from words where word = '" . AddWordTransformed . "') and word_minus1 = (select ID from words where word = '" . g_Word_Minus1 . "'));") + } + } + else + { + ;OutputDebug, Attempting to update count of existing word pair + UpdateWordHierarchyCount(AddWord) ;Increment the word count if it's already in the list and we aren't forcing it on + ;OutputDebug, Updated count of existing word pair + } + } + } + + +;-- END + g_Word_Minus2 := g_Word_Minus1 + g_Word_Minus1 := AddWordTransformed + + + ;OutputDebug, --------[END UpdateWordHierarchy]------------------------ + + 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 = + } + } + } + } +} + +;------------------------------------------------------------------------ + +GetOSVersion() +{ + return ((r := DllCall("GetVersion") & 0xFFFF) & 0xFF) "." (r >> 8) +} + +;------------------------------------------------------------------------ + +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 + +BulkLearnFromClipboardMenu: +MsgBox, 4, Bulk-learn words from clipboard contents, Would you like to continue? (press Yes or No) +IfMsgBox No +{ + Return +} +SuspendOn() +BulkLearnFromClipboard(clipboard) +SuspendOff() +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() + +; Update the Learned Words +MaybeUpdateWordlist() + +ExitApp + +#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 %A_ScriptDir%\Includes\Wordlist.ahk +#Include +#Include <_Struct> + +RemoveTrayTip: + SetTimer, RemoveTrayTip, Off + TrayTip +return + +showTrayTip(byref x, time) { + time *= 1000 + TrayTip, , %x% + SetTimer, RemoveTrayTip, %time% + return +} +return