From 7b7554016cd895e7269662bca8d2003616d7bd0f Mon Sep 17 00:00:00 2001 From: Theclaws Date: Sat, 19 Aug 2017 20:02:03 -0400 Subject: [PATCH 1/6] Update TypingAid.ahk --- Source/TypingAid.ahk | 147 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 141 insertions(+), 6 deletions(-) diff --git a/Source/TypingAid.ahk b/Source/TypingAid.ahk index e11d755..6179f4e 100644 --- a/Source/TypingAid.ahk +++ b/Source/TypingAid.ahk @@ -311,6 +311,8 @@ RecomputeMatches() global prefs_ShowLearnedFirst global prefs_SuppressMatchingWord + global g_Word_Minus1 + SavePriorMatchPosition() ;Match part-word with command @@ -388,17 +390,37 @@ RecomputeMatches() } WordLen := StrLen(g_Word) - OrderByQuery := " ORDER BY CASE WHEN count IS NULL then " + WordCaseOrderStatement := "(CASE WHEN count IS NULL then " IfEqual, prefs_ShowLearnedFirst, On { - OrderByQuery .= "ROWID + 1 else 0" + WordCaseOrderStatement .= "ROWID + 1 else 0" } else { - OrderByQuery .= "ROWID else 'z'" + WordCaseOrderStatement .= "ROWID else 'z'" } + WordCaseOrderStatement .= "END) as ""Order1""" - 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 . ";") + ;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 + { + ;Two State Query + WordRelationQuery := "SELECT word, NULL as ""worddescription"", '' as ""wordreplacement"", " . WordRelationOrderStatement . " FROM 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 { + ;Single State Query + MergedQuery := "select word, WORDDESCRIPTION, WORDREPLACEMENT, SUM(ORDER2) AS ORDER2 from (" . MainWordQuery . ") GROUP BY word, wordreplacement, Order1 ORDER BY Order1, Order2 DESC" + } + + Matches := g_WordListDB.Query(MergedQuery . ";") g_SingleMatch := Object() g_SingleMatchDescription := Object() @@ -988,6 +1010,7 @@ AddSelectedWordToList() ClipWait, 0 IfNotEqual, Clipboard, { + showTrayTip("Forcing adding word '" . Clipboard . "'.", 1) AddWordToList(Clipboard,1,"ForceLearn") } Clipboard = %ClipboardSave% @@ -1099,6 +1122,7 @@ BuildTrayMenu() 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 @@ -1139,6 +1163,94 @@ ClearAllVars(ClearWord) ;------------------------------------------------------------------------ +; This is to blank all vars related to hierarchy matches +ClearWordHierarchy() +{ + global + g_Word_Minus1 = + 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 + + TransformWord(AddWord, AddWordReplacement, AddWordDescription, AddWordTransformed, AddWordIndexTransformed, AddWordReplacementTransformed, AddWordDescriptionTransformed) + + if g_Word_Minus1 + { + ;Hierarchy evaluation... + + ;Check if the word exists in our word hierachy table. + AddWordInList := g_WordListDB.Query("SELECT * FROM WordRelations WHERE word = '" . AddWordTransformed . "' and word_minus1 = '" . g_Word_Minus1 . "';") + + IF !( AddWordInList.Count() > 0 ) + { + ;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 + g_WordListDB.Query("INSERT INTO WordRelations (word_minus1, word, count, lastused) VALUES ('" . g_Word_Minus1 . "','" . AddWordTransformed . "','" . CountValue . "', (select datetime(strftime('%s','now'), 'unixepoch')));") + + } else IfEqual, prefs_LearnMode, 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 ) + { + g_WordListDB.QUERY("UPDATE WordRelations SET count = ('" . prefs_LearnCount . "'), lastused = (select datetime(strftime('%s','now'), 'unixepoch')) WHERE word = '" . AddWordTransformed . "' and word_minus1 = '" . g_Word_Minus1 . "');") + } + } + else + { + UpdateWordHierarchyCount(AddWord) ;Increment the word count if it's already in the list and we aren't forcing it on + } + } + } + + g_Word_Minus1 := AddWordTransformed + + Return +} + +;------------------------------------------------------------------------ + FileAppendDispatch(Text,FileName,ForceEncoding=0) { IfEqual, A_IsUnicode, 1 @@ -1258,6 +1370,16 @@ if (g_PauseState == "Paused") } Return +BulkLearnFromClipboardMenu: +MsgBox, 4, Bulk-learn words from clipboard contents, Would you like to continue? (press Yes or No) +IfMsgBox No +{ + Return +} +BulkLearnFromClipboard(clipboard) +Return + + ExitScript: ExitApp Return @@ -1293,3 +1415,16 @@ ExitApp #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 From 16c25a7aec813de201c7e99addaa1a143e065994 Mon Sep 17 00:00:00 2001 From: Theclaws Date: Sat, 19 Aug 2017 20:03:45 -0400 Subject: [PATCH 2/6] Update Conversions.ahk --- Source/Includes/Conversions.ahk | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Source/Includes/Conversions.ahk b/Source/Includes/Conversions.ahk index e7f7b33..cec91be 100644 --- a/Source/Includes/Conversions.ahk +++ b/Source/Includes/Conversions.ahk @@ -101,6 +101,8 @@ RebuildDatabase() CreateWordlistsTable() + CreateWordRelationTable() + SetDbVersion() g_WordListDB.EndTransaction() @@ -249,7 +251,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 . " (wordindexed TEXT NOT NULL, word TEXT NOT NULL, count INTEGER, worddescription TEXT, wordreplacement TEXT NOT NULL, lastused INTEGER DEFAULT (0), PRIMARY KEY (word, wordreplacement) );") { ErrMsg := g_WordListDB.ErrMsg() ErrCode := g_WordListDB.ErrCode() @@ -282,4 +284,17 @@ CreateWordlistsTable() msgbox Cannot Create Wordlists Table - fatal error: %ErrCode% - %ErrMsg% ExitApp } -} \ No newline at end of file +} + +CreateWordRelationTable() +{ + global g_WordListDB + + IF not g_WordListDB.Query("CREATE TABLE WordRelations (word_minus1 TEXT NOT NULL, word TEXT NOT NULL, 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 + } +} From 829658119e58ef96b7d4f1433db7809beac36464 Mon Sep 17 00:00:00 2001 From: Theclaws Date: Sat, 19 Aug 2017 20:04:06 -0400 Subject: [PATCH 3/6] Update Sending.ahk --- Source/Includes/Sending.ahk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 +;------------------------------------------------------------------------ From 4f95a9a2b6ae1ba360167d73e9b43a1e5ac28e08 Mon Sep 17 00:00:00 2001 From: Theclaws Date: Sat, 19 Aug 2017 20:04:31 -0400 Subject: [PATCH 4/6] Update Wordlist.ahk --- Source/Includes/Wordlist.ahk | 97 ++++++++++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 9 deletions(-) diff --git a/Source/Includes/Wordlist.ahk b/Source/Includes/Wordlist.ahk index b5bc519..ac49fbe 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,8 +391,29 @@ 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 = '" . wordEscaped . "' and word_minus1 = '" . wordMinus1Escaped . "';") Return } @@ -390,6 +432,13 @@ CleanupWordList(LearnedWordsOnly := false) } 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') and lastused <> 0;") + + ;Remove all word relationship that are under the threshold if older than 14 days. + g_WordListDB.Query("DELETE FROM WordRelations WHERE count < " . prefs_LearnCount . " AND lastused < DATE ('now','-14 day');") + Progress, Off } @@ -467,12 +516,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 +} From f6207cbe843232980b749763f242b659bbb47e7f Mon Sep 17 00:00:00 2001 From: Theclaws Date: Wed, 6 Sep 2017 20:49:01 -0400 Subject: [PATCH 5/6] Upgrade to WordRelations Table and Words table Word table now has new primary key that uniquely identifies each value (old primary key converted to index and constraints) WordRelations table now uses Word table primary key to refer to words instead. New view added (VW_WordRelations) to view WordRelations table with word instead of index. --- Source/Includes/Conversions.ahk | 34 +++++++++++++++++++++++++++++++-- Source/Includes/Wordlist.ahk | 13 ++++++++----- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/Source/Includes/Conversions.ahk b/Source/Includes/Conversions.ahk index cec91be..3cb08fb 100644 --- a/Source/Includes/Conversions.ahk +++ b/Source/Includes/Conversions.ahk @@ -97,12 +97,16 @@ RebuildDatabase() CreateWordIndex() + CreateWordReplacementIndex() + CreateLastStateTable() CreateWordlistsTable() CreateWordRelationTable() + CreateWordRelationView() + SetDbVersion() g_WordListDB.EndTransaction() @@ -251,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, lastused INTEGER DEFAULT (0), 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() @@ -273,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 @@ -290,7 +307,7 @@ CreateWordRelationTable() { global g_WordListDB - IF not g_WordListDB.Query("CREATE TABLE WordRelations (word_minus1 TEXT NOT NULL, word TEXT NOT NULL, count INTEGER, lastused INTEGER DEFAULT (0), PRIMARY KEY ( word_minus1, word ));") + 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() @@ -298,3 +315,16 @@ CreateWordRelationTable() 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/Wordlist.ahk b/Source/Includes/Wordlist.ahk index ac49fbe..a56738e 100644 --- a/Source/Includes/Wordlist.ahk +++ b/Source/Includes/Wordlist.ahk @@ -413,7 +413,7 @@ UpdateWordHierarchyCount(word) 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 = '" . wordEscaped . "' and word_minus1 = '" . wordMinus1Escaped . "';") + 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 } @@ -428,16 +428,19 @@ 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') and lastused <> 0;") + 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 14 days. - g_WordListDB.Query("DELETE FROM WordRelations WHERE count < " . prefs_LearnCount . " AND lastused < DATE ('now','-14 day');") + ;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 } From 21e65806669374a360c968258a26483c724f7e04 Mon Sep 17 00:00:00 2001 From: Theclaws Date: Wed, 6 Sep 2017 20:49:51 -0400 Subject: [PATCH 6/6] Upgrade to WordRelations Table and Words table Word table now has new primary key that uniquely identifies each value (old primary key converted to index and constraints) WordRelations table now uses Word table primary key to refer to words instead. New view added (VW_WordRelations) to view WordRelations table with word instead of index. --- Source/TypingAid.ahk | 2888 +++++++++++++++++++++--------------------- 1 file changed, 1458 insertions(+), 1430 deletions(-) diff --git a/Source/TypingAid.ahk b/Source/TypingAid.ahk index 6179f4e..2330ed7 100644 --- a/Source/TypingAid.ahk +++ b/Source/TypingAid.ahk @@ -1,1430 +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 - - global g_Word_Minus1 - - 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 - { - ;Two State Query - WordRelationQuery := "SELECT word, NULL as ""worddescription"", '' as ""wordreplacement"", " . WordRelationOrderStatement . " FROM 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 { - ;Single State Query - MergedQuery := "select word, WORDDESCRIPTION, WORDREPLACEMENT, SUM(ORDER2) AS ORDER2 from (" . MainWordQuery . ") GROUP BY word, wordreplacement, Order1 ORDER BY Order1, Order2 DESC" - } - - 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 = - 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 - - TransformWord(AddWord, AddWordReplacement, AddWordDescription, AddWordTransformed, AddWordIndexTransformed, AddWordReplacementTransformed, AddWordDescriptionTransformed) - - if g_Word_Minus1 - { - ;Hierarchy evaluation... - - ;Check if the word exists in our word hierachy table. - AddWordInList := g_WordListDB.Query("SELECT * FROM WordRelations WHERE word = '" . AddWordTransformed . "' and word_minus1 = '" . g_Word_Minus1 . "';") - - IF !( AddWordInList.Count() > 0 ) - { - ;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 - g_WordListDB.Query("INSERT INTO WordRelations (word_minus1, word, count, lastused) VALUES ('" . g_Word_Minus1 . "','" . AddWordTransformed . "','" . CountValue . "', (select datetime(strftime('%s','now'), 'unixepoch')));") - - } else IfEqual, prefs_LearnMode, 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 ) - { - g_WordListDB.QUERY("UPDATE WordRelations SET count = ('" . prefs_LearnCount . "'), lastused = (select datetime(strftime('%s','now'), 'unixepoch')) WHERE word = '" . AddWordTransformed . "' and word_minus1 = '" . g_Word_Minus1 . "');") - } - } - else - { - UpdateWordHierarchyCount(AddWord) ;Increment the word count if it's already in the list and we aren't forcing it on - } - } - } - - g_Word_Minus1 := AddWordTransformed - - 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 -} -BulkLearnFromClipboard(clipboard) -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 +; 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