Dictionary Trainer

Applications, Games, Tools, User libs and useful stuff coded in PureBasic
User avatar
A.D.
User
User
Posts: 98
Joined: Tue Oct 06, 2009 9:11 pm

Dictionary Trainer

Post by A.D. »

Hello Friends!

i wrote a program which helps adding words to the dictionary of the windows speech recognition engine. Read the readme file for using instructions. i hope it will be useful to you!

Code: Select all

;{
; Command-Splitting für Txt-Dateien u. erweitertes Command-Splitting for VA-Profile
; delete digits groups [1...100], shownext und saveskipped (standard ist 1)
; Bilder in Exe kompilieren
; Kommentare schreiben/evtl. Lizenztext anpassen
; Linkware (Backlink to http://www.iconsmind.com required), book3.ico

; [Hello;Greetings] Computer; Hi (multicommand)
; The dynamic sections don't have to just be at the beginning.  They can be anywhere in
;  the command.  Also, As a side-effect, If you put a semicolon on at the End of the 
;  selections, it makes the section optional:
; 
;  [Greetings;Hello]computer[how are you;]
; 
;   Greetings computer how are you
;   Hello computer how are you
;   Greetings computer
;   Hello computer
;         
; Note that there is a semicolon after 'how are you' To indicate that the entire section is optional.
; eject car [1..100]
;}

;EnableExplicit

Global ComboBox, TextNext, TextM, Train_Button, Skip_Button, Frame, Help_Button,Lock_Button, Continue_Button
Global Img_Window_0_0, Handle.i, Delay, Delay2, Digits, Words_Loaded, SplitCommands
Global Starting = #True,  FileName.s, sysroot.s, lock = #False
Global MainWin, WinListView, ListView

Global NewList WordsToTrain.s()
Global NewList TrainedWords.s()
Global NewList Dummy.s()

DebugLevel 0

UsePNGImageDecoder()
Global Img_Window_0_0 = LoadImage(#PB_Any,"question.png")
Global Img_Window_0_1 = LoadImage(#PB_Any,"cross.png")

Enumeration FormFont
  #Font_Window_0_0
  #Font_Window_0_1
  #Font_Window_0_2
EndEnumeration

LoadFont(#Font_Window_0_0,"Arial", 11, #PB_Font_Bold)
LoadFont(#Font_Window_0_1,"Arial", 14, #PB_Font_Bold)
LoadFont(#Font_Window_0_2,"System", 1);, #PB_Font_Bold)


Declare OpenWin(x = 0, y = 0, width = 495, height = 205)
Declare SendKeys(handle,window$,keys$)
Declare PrepareWord()
Declare Init()
Declare StartTraining()
Declare TrainButton()
Declare SkipButton()
Declare ChangeComboBox()
Declare FindWin(window$)
Declare SaveTrained()
Declare Unlock()
Declare LoadProfile(FileName.s, List Words.s())
Declare LoadText(FileName.s, List Words.s())
Declare ContinueButton()
Declare LoadTrained()
Declare TrainingComplete()
Declare ParseWords(*CurrentNode, CurrentSublevel, List Words.s())
Declare FilterWords(List Words.s(), List Trained.s())
Declare WinEvents(event)
Declare OpenListView(x = 0, y = 0, width = 300, height = 200)


Procedure OpenWin(x = 0, y = 0, width = 495, height = 205)
  
  MainWin = OpenWindow(#PB_Any, x, y, width, height, "Dictionary Trainer", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
  
  CreateStatusBar(0, WindowID(MainWin)) : AddStatusBarField(490) : StatusBarText(0, 0, "")
  ComboBox = ComboBoxGadget(#PB_Any, 20, 20, 220, 25)
  
  TextM = TextGadget(#PB_Any, 30, 80, 250, 20, "")
  SetGadgetFont(TextM, FontID(#Font_Window_0_0))
  Frame = FrameGadget(#PB_Any, 20, 72, 270, 35, "", #PB_Frame_Flat)
  
  TextNext = TextGadget(#PB_Any, 24, 114, 330, 20, "(0/0) next word: ")
  SetGadgetColor(TextNext, #PB_Gadget_FrontColor,RGB(0,115,170))
  SetGadgetFont(TextNext, FontID(#Font_Window_0_0))
  
  Train_Button = ButtonGadget(#PB_Any, 305, 74, 80, 32, "Train")
  Skip_Button = ButtonGadget(#PB_Any, 400, 74, 80, 32, "Skip")
  Continue_Button = ButtonGadget(#PB_Any, 400, 118, 80, 32, "Continue")
  
 Help_Button = ButtonImageGadget(#PB_Any, 438, 14, 40, 37, ImageID(Img_Window_0_0))
 Lock_Button = ButtonImageGadget(#PB_Any, 250, 19, 26, 26, ImageID(Img_Window_0_1))
  
EndProcedure

 Procedure PrepareWord()
  
  SendKeys(handle, "", "{ENTER}") : Delay(Delay) 
  SendKeys(handle,"", WordsToTrain()) : Delay(Delay) 
  SendKeys(handle,"","{ENTER}")     : Delay(Delay) 
  SendKeys(handle,"", Chr(32))        : Delay(Delay) 
  SendKeys(handle,"","{TAB}")         : Delay(Delay)
  SendKeys(handle,"","{TAB}")         : Delay(Delay)  
  SendKeys(handle, "", "{ENTER}")  : Delay(Delay)  
  SendKeys(handle, "", "{ENTER}")  : Delay(Delay)  
  
EndProcedure

Procedure Init()
  
  If ExamineEnvironmentVariables()
    While NextEnvironmentVariable()
      If  EnvironmentVariableName()  = "SystemRoot"
        sysroot = EnvironmentVariableValue() 
      EndIf
    Wend
  EndIf 
  
  AddGadgetItem(ComboBox, -1, "select a VA-Profile (*.vap) or a Textfile (*.txt)")   
  
  If Not OpenPreferences("DictionaryTrainer.ini") : MessageRequester("Error!", "Could not open configuration file!") : EndIf
  splitcommands = ReadPreferenceInteger("splitcommands", 1)
  delay2 = ReadPreferenceInteger("delaychar", 0)
  delay = ReadPreferenceInteger("delayctrlkeys", 0)
  digits = ReadPreferenceInteger("nodigits", 0)
  ClosePreferences()
  
  ;LoadProfile("test3.txt", WordsToTrain()) : Lock = #True : FileName = "test3.txt" : splitcommands = 1
  
  EndProcedure

Procedure Unlock()
 
 If Lock = #True
 RemoveGadgetItem(ComboBox, 1)
 SetGadgetFont(ComboBox, #PB_Default)
 SetGadgetState(ComboBox, -1)
 DisableGadget(ComboBox, 0)
 Lock = #False 
 ClearList(WordsToTrain())
 ClearList(TrainedWords())
 ClearList(Dummy())
 FileName.s = ""
 Starting = #True
 SetGadgetText(TextNext,  "(0/0) next word: ")
 SetGadgetText(TextM,  "")
 ;If IsWindow(WinListView) : CloseWindow(WinListView) : EndIf
 
 EndIf
                
EndProcedure

Procedure SaveTrained()
 
 ;ProcedureReturn
 Protected buffer.s
 file.s = "trainedwords.ini"
 
If OpenFile(0, file) : bom = ReadStringFormat(0) : CloseFile(0) : Else : MessageRequester("Error!", "trainedwords.ini could not be opened") : End : EndIf
If OpenFile(0, file, bom) : If bom <> #PB_Ascii : bom = ReadStringFormat(0) : EndIf : Else : MessageRequester("Error!", "trainedwords.ini could not be opened") : End : EndIf

; in Datei schreiben
;Debug "TrainedWords_Listsize" : Debug ListSize(TrainedWords())
FileSeek(0, Lof(0))
ResetList(TrainedWords()) 
While NextElement(TrainedWords())
  ;Debug TrainedWords()
  WriteStringN(0, TrainedWords(), bom)
 Wend
 
buffer = ""
CloseFile(0)           
     
EndProcedure

Procedure ParseWords(*CurrentNode, CurrentSublevel, List Words.s())

Protected Text$

If XMLNodeType(*CurrentNode) = #PB_XML_Normal
  
  Text$ = GetXMLNodeName(*CurrentNode)
  If Text$ = "CommandString"
    Text$ = GetXMLNodeText(*CurrentNode) 
    
    ReplaceString(text$, "*", " ", #PB_String_NoCase | #PB_String_InPlace)
    ReplaceString(text$, "[", " ", #PB_String_NoCase | #PB_String_InPlace)
    ReplaceString(text$, "]", " ", #PB_String_NoCase | #PB_String_InPlace)
    ReplaceString(text$, ";", " ", #PB_String_NoCase | #PB_String_InPlace)
    
    For i = 0 To 64
      ;If i <> 32 : ReplaceString(text$, Chr(i), Chr(32), #PB_String_NoCase | #PB_String_InPlace) : EndIf
      If i <> 39 : ReplaceString(text$, Chr(i), Chr(32), #PB_String_NoCase | #PB_String_InPlace) : EndIf
    Next i
    For i = 91 To 96
      ReplaceString(text$, Chr(i), " ", #PB_String_NoCase | #PB_String_InPlace)
    Next i
    For i = 123 To 191
      ReplaceString(text$, Chr(i), " ", #PB_String_NoCase | #PB_String_InPlace)
    Next i
    For i = 204 To 208
      ReplaceString(text$, Chr(i), " ", #PB_String_NoCase | #PB_String_InPlace)
    Next i
    For i = 215 To 216
      ReplaceString(text$, Chr(i), " ", #PB_String_NoCase | #PB_String_InPlace)
    Next i
    
    text$ = LTrim(text$, " ") : text$ = RTrim(text$, " ")

    Repeat
     text$ = ReplaceString(text$, "  ", " ")
     done = FindString(text$, "  ")
    Until done = 0
    
   If SplitCommands = 0
    AddElement(Words())
    Words() = text$
    Debug Words()
   Else
    For i = 1 To Len(Text$)
      w.s = StringField(Text$, i, " ")
      If w.s
       AddElement(Words())
       Words() = w.s
       ;Debug Words()
      EndIf
    Next 
   EndIf
    
  EndIf
                     
    *ChildNode = ChildXMLNode(*CurrentNode)
    
    While *ChildNode <> 0
      ParseWords(*ChildNode, CurrentSublevel + 1, Words.s())      
      *ChildNode = NextXMLNode(*ChildNode)
    Wend        
  
  EndIf

EndProcedure

Procedure LoadProfile(FileName.s, List Words.s())

  If GetExtensionPart(FileName.s) = "vap"
  XML = LoadXML(#PB_Any, FileName.s)
  
  If XML 
    If XMLStatus(XML) <> #PB_XML_Success
      Message$ = "Error in the XML file:" + Chr(13)
      Message$ + "Message: " + XMLError(XML) + Chr(13)
      Message$ + "Line: " + Str(XMLErrorLine(XML)) + "   Character: " + Str(XMLErrorPosition(XML))
      MessageRequester("Error", Message$)
      End
    EndIf      
    *MainNode = MainXMLNode(XML)     
    ParseWords(*MainNode, 0, Words())    
   Else
    MessageRequester("Error", "The file cannot be opened.")
    End 
 EndIf    
 
 Else
  LoadText(FileName.s, Words())
 EndIf
 
 LoadTrained() : FilterWords(Words(), Dummy())
 Words_loaded =  ListSize(Words()) ;: Debug "Words loaded: " : Debug Words_loaded
 If Words_loaded = 0 : MessageRequester("Error!", "This profile does not contain any words to be trained!") : Unlock() : ProcedureReturn : EndIf
 FirstElement(Words())
 SetGadgetText(TextM, Words())
 NextElement(Words())
 SetGadgetText(TextNext,  "(1/" + Words_Loaded+")" + " next word: " + Words())
 FirstElement(WordsToTrain()) 

EndProcedure

Procedure LoadText(FileName.s, List Words.s())

Protected buffer.s

If ReadFile(0, FileName.s) : bom = ReadStringFormat(0) : CloseFile(0) : Else : MessageRequester("Error", "The file cannot be opened.") : End : EndIf
If ReadFile(0, FileName.s, bom) : If bom <> #PB_Ascii : bom = ReadStringFormat(0) : EndIf : Else : MessageRequester("Error", "The file cannot be opened.") : End : EndIf
buffer = ReadString(0, #PB_File_IgnoreEOL, Lof(0))  : buffer = LTrim(buffer) : buffer = RTrim(buffer) 

For i = 0 To 64
If i => 46 And i <= 57 : EndIf
If i <> 39 And Not ( i => 46 And i <= 57) : ReplaceString(buffer, Chr(i), Chr(32), #PB_String_NoCase | #PB_String_InPlace) : EndIf
Next i
For i = 91 To 96
ReplaceString(buffer, Chr(i), " ", #PB_String_NoCase | #PB_String_InPlace)
Next i
For i = 123 To 191
ReplaceString(buffer, Chr(i), " ", #PB_String_NoCase | #PB_String_InPlace)
Next i
For i = 204 To 208
ReplaceString(buffer, Chr(i), " ", #PB_String_NoCase | #PB_String_InPlace)
Next i
For i = 215 To 216
ReplaceString(buffer, Chr(i), " ", #PB_String_NoCase | #PB_String_InPlace)
Next i

;Debug buffer 

For i = 1 To Len(buffer)
  w.s = StringField(buffer, i, " ") 
  If w.s
  AddElement(Words()) 
  Words() = w.s
  EndIf
Next
 
;  ResetList(Words())
;   While NextElement(Words())
;      debug Len(Words())
;      Debug Words()
;  Wend    

buffer = ""     
CloseFile(0)           

EndProcedure

Procedure LoadTrained()

Protected buffer.s
Protected file.s = "trainedwords.ini"

If ReadFile(0, file.s) : bom = ReadStringFormat(0) : CloseFile(0) : Else : MessageRequester("Error", "The file cannot be opened.") : End : EndIf
If ReadFile(0, file.s, bom) : If bom <> #PB_Ascii : bom = ReadStringFormat(0) : EndIf : Else : MessageRequester("Error", "The file cannot be opened.") : End : EndIf
;If ReadFile(0, file.s) : Debug "" : Else : MessageRequester("Error", "The file cannot be opened.") : End : EndIf
buffer = ReadString(0, #PB_File_IgnoreEOL, Lof(0))  

 buffer = LTrim(buffer, Chr(10)) : buffer = RTrim(buffer, Chr(10)) ;Debug buffer 

ResetList(Dummy())
For i = 1 To Len(buffer)
  w.s = StringField(buffer, i, Chr(10)) 
  If w.s
  w.s = Mid(w.s, 1, Len(w.s)-1)
  AddElement(Dummy())
  Dummy() = w.s 
  EndIf
Next 

; Debug "Dummy-Anzahl: " : Debug ListSize(Dummy())
; ResetList(Dummy())
;   While NextElement(Dummy())
;      Debug Len(Dummy())
;      Debug Dummy()
;  Wend    

buffer = ""     
CloseFile(0)           

EndProcedure

Procedure FilterWords(List Words.s(), List Trained.s())

; Doppelte Worte aus der Liste entfernen
ResetList(Words()) 
While NextElement(Words())
 word.s = Words() : Index = ListIndex(Words())
  ResetList(Words()) 
   While NextElement(Words())
     If Words() = word.s And ListIndex(Words()) <> Index : DeleteElement(Words(),1) : EndIf 
  Wend
  SelectElement(Words(), Index)
 Wend

;Bereits trainierte Worte aus der Liste entfernen
ForEach Words()
 ForEach Dummy()
   If Dummy() = Words() : DeleteElement(Words(),1) : If ListSize(Words()) = 0 : Break 2 : EndIf : EndIf
Next 
Next

; Debug "nach Filterung übrig"
; ForEach Words()
;   Debug Len(words())
;   Debug Words()
; Next

 ClearList(Trained())
 
EndProcedure 

Procedure StartTraining()
  
  If Lock = #True
 
  win$="Sprachwörterbuch" ;win$="Sprach-UX-Konfiguration"
  handle = FindWin(win$)   
  
  PrepareWord()
  Starting = #False
  AddElement(TrainedWords()) : TrainedWords() = WordsToTrain() 
   
  If  NextElement(WordsToTrain()) 
    SetGadgetText(TextM, WordsToTrain())
    If NextElement(WordsToTrain())
      SetGadgetText(TextNext,  "(" + ListIndex(WordsToTrain()) + "/" + Words_Loaded + ")" + " next word: " + WordsToTrain())
      PreviousElement(WordsToTrain())
    Else
      SetGadgetText(TextNext, "this is the last word!")
    EndIf
  EndIf
  
 EndIf
  
EndProcedure

Procedure TrainButton()
  
  If Lock = #True
 
 ; Finish Recording And Go To Next word
  SendKeys(handle,"","{TAB}") : Delay(Delay) 
  SendKeys(handle,"","{TAB}") : Delay(Delay)  
  SendKeys(handle, "", "{ENTER}") : Delay(Delay)  
  
  PrepareWord()
  AddElement(TrainedWords()) : TrainedWords() = WordsToTrain() 
  If ListIndex(WordsToTrain())+1 = ListSize(WordsToTrain()) : TrainingComplete() : EndIf
    
  If  NextElement(WordsToTrain())
    SetGadgetText(TextM, WordsToTrain())
    If  NextElement(WordsToTrain())
       SetGadgetText(TextNext,  "(" + ListIndex(WordsToTrain()) + "/" + Words_Loaded + ")" + " next word: " + WordsToTrain())
      PreviousElement(WordsToTrain())
    Else
      SetGadgetText(TextNext, "this is the last word!")
    EndIf
  EndIf
  
  EndIf
  
EndProcedure

Procedure SkipButton()
  
  Static last
  
  If Lock = #False : ProcedureReturn : EndIf
    
  If NextElement(WordsToTrain()) 
    SetGadgetText(TextM, WordsToTrain())
    If NextElement(WordsToTrain())
      SetGadgetText(TextNext,  "(" + ListIndex(WordsToTrain()) + "/" + Words_Loaded + ")" + " next word: " + WordsToTrain())
      PreviousElement(WordsToTrain())
    EndIf
   EndIf
 
  If ListIndex(WordsToTrain())+1 = ListSize(WordsToTrain()) : SetGadgetText(TextNext, "this is the last word!") : EndIf
   
  If ListIndex(WordsToTrain())+1 = ListSize(WordsToTrain()) And last = #False : last = #True : ProcedureReturn : EndIf
  If last = #True : TrainingComplete() : last = #False : EndIf
  
EndProcedure

Procedure ContinueButton()
  
  If Lock = #True
    file.s = FileName 
    SaveTrained()
    Unlock() 
    FileName = file
    LoadProfile(file, WordsToTrain()) 
    Lock = #True 
  EndIf
  
EndProcedure

Procedure TrainingComplete()
  
  SaveTrained()
  Unlock()
  SetGadgetText(TextNext, "Training complete!")
  
EndProcedure

Procedure FindWin(window$)
  
  h.i = FindWindow_(0,window$)
  SetForegroundWindow_(handle)
  Delay(250) 
  ProcedureReturn h
  
EndProcedure

Procedure ChangeComboBox()
  
  state = GetGadgetState(ComboBox)
  If state = 0 
    FileName.s = OpenFileRequester("Load Profile/File", GetCurrentDirectory(), "Voice Attack|*.vap|Text|*.txt", 1)
    If FileName.s <> "" And (GetExtensionPart(FileName.s) = "vap" Or GetExtensionPart(FileName.s) = "txt")
      SetGadgetFont(ComboBox, FontID(#Font_Window_0_2))
      AddGadgetItem(ComboBox, 1, GetFilePart(FileName.s))
      SetGadgetState(ComboBox, 1)
      DisableGadget(ComboBox,1) 
      Lock = #True
      LoadProfile(FileName.s, WordsToTrain()) 
    Else
      FileName.s = ""
      RemoveGadgetItem(ComboBox, 1)
      SetGadgetState(ComboBox, -1)
    EndIf   
  EndIf
              
EndProcedure

Procedure SendKeys(handle,window$,keys$)
  
; Author: Danilo
; Date: 07. April 2003
; Posted 07 April 2003 by Danilo on german forum

  If window$<>"" : handle=FindWindow_(0,window$) : EndIf ; Use window$ instead of handle.
  If IsWindow_(handle)=0                                 ; Does the target window actually exist?
    ProcedureReturn 0                                    ; Nope, so report 0 for failure to type.
  Else
    ; This block gives the target window the focus before typing.
    thread1=GetWindowThreadProcessId_(GetForegroundWindow_(),0)
    thread2=GetWindowThreadProcessId_(handle,0)
    If thread1<>thread2 : AttachThreadInput_(thread1,thread2,#True) : EndIf
    SetForegroundWindow_(handle) ; Target window now has the focus for typing.
    Delay(delay2)                ; 1/8 second pause before typing, to prevent fast CPU problems.
                                 ; Now the actual typing starts.
    For r=1 To Len(keys$)
      vk$=Mid(keys$,r,1)
      If vk$="{" ; Special key found.
        s=FindString(keys$,"}",r+1)-(r+1) ; Get length of special key.
        s$=Mid(keys$,r+1,s)               ; Get special key name.
        Select s$                         ; Get virtual key code of special key.
          Case "ALTDOWN" : keybd_event_(#VK_MENU,0,0,0) ; Hold ALT down.
          Case "ALTUP" : keybd_event_(#VK_MENU,0,#KEYEVENTF_KEYUP,0) ; Release ALT.
          Case "BACKSPACE" : vk=#VK_BACK
          Case "CONTROLDOWN" : keybd_event_(#VK_CONTROL,0,0,0) ; Hold CONTROL down.
          Case "CONTROLUP" : keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP,0) ; Release CONTROL.
          Case "DELETE" : vk=#VK_DELETE
          Case "DOWN" : vk=#VK_DOWN
          Case "END" : vk=#VK_END
          Case "ENTER" : vk=#VK_RETURN
          Case "T" : vk=#VK_T
          Case "A" : vk=#VK_A
          Case "D" : vk=#VK_D
          Case "X" : vk=#VK_X
          Case "V" : vk=#VK_V
          Case "F1" : vk=#VK_F1
          Case "F2" : vk=#VK_F2
          Case "F3" : vk=#VK_F3
          Case "F4" : vk=#VK_F4
          Case "F5" : vk=#VK_F5
          Case "F6" : vk=#VK_F6
          Case "F7" : vk=#VK_F7
          Case "F8" : vk=#VK_F8
          Case "F9" : vk=#VK_F9
          Case "F10" : vk=#VK_F10
          Case "F11" : vk=#VK_F11
          Case "F12" : vk=#VK_F12
          Case "ESCAPE" : vk=#VK_ESCAPE
          Case "HOME" : vk=#VK_HOME
          Case "INSERT" : vk=#VK_INSERT
          Case "LEFT" : vk=#VK_LEFT
          Case "PAGEDOWN" : vk=#VK_NEXT
          Case "PAGEUP" : vk=#VK_PRIOR
          Case "PRINTSCREEN" : vk=#VK_SNAPSHOT
          Case "RETURN" : vk=#VK_RETURN
          Case "RIGHT" : vk=#VK_RIGHT
          Case "SHIFTDOWN" : shifted=1 : keybd_event_(#VK_SHIFT,0,0,0) ; Hold SHIFT down.
          Case "SHIFTUP" : shifted=0 : keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0) ; Release SHIFT.
          Case "TAB" : vk=#VK_TAB
          Case "UP" : vk=#VK_UP
        EndSelect
        If Left(s$,3)<>"ALT" And Left(s$,7)<>"CONTROL" And Left(s$,5)<>"SHIFT"
          keybd_event_(vk,0,0,0) : keybd_event_(vk,0,#KEYEVENTF_KEYUP,0) ; Press the special key.
        EndIf
        r=r+s+1 ; Continue getting the keystrokes that follow the special key.
      Else
        vk=VkKeyScanEx_(Asc(vk$),GetKeyboardLayout_(0)) ; Normal key found.
        If vk>304 And shifted=0 : keybd_event_(#VK_SHIFT,0,0,0) : EndIf ; Due to shifted character.
        keybd_event_(vk,0,0,0) : keybd_event_(vk,0,#KEYEVENTF_KEYUP,0)  ; Press the normal key.
        If vk>304 And shifted=0 : keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0) : EndIf ; Due to shifted character.
      EndIf
    Next
    If thread1<>thread2 : AttachThreadInput_(thread1,thread2,#False) : EndIf ; Finished typing to target window!
    keybd_event_(#VK_MENU,0,#KEYEVENTF_KEYUP,0)                              ; Release ALT key if user forgot.
    keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP,0)                           ; Release CONTROL key if user forgot.
    keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0)                             ; Release SHIFT key if user forgot.
    ProcedureReturn 1                                                        ; Report successful typing! :)
  EndIf
EndProcedure

Procedure OpenListView(x = 0, y = 0, width = 300, height = 200)
  
  If Not WinListView
    If ListSize(WordsToTrain()) = 0 : ProcedureReturn : EndIf
    x = WindowX(MainWin) + 495 + 30 : y = WindowY(MainWin) - 30
    WinListView = OpenWindow(#PB_Any, x, y, width, height, "Words loaded", #PB_Window_SystemMenu)
    ListView = ListViewGadget(#PB_Any, 4, 4, 292, 192)
    
    index = ListIndex(WordsToTrain())
    ForEach WordsToTrain()
      AddGadgetItem(ListView, -1, WordsToTrain())
    Next
    SelectElement(WordsToTrain(), Index)
    SetGadgetState(ListView, Index)
  EndIf
  
EndProcedure

Procedure WinEvents(event)
  
  If EventWindow() = MainWin 
  Select event
    Case #PB_Event_CloseWindow
      If Starting = #False And Lock = #True : SaveTrained() : EndIf
      ProcedureReturn #False
      
    Case #PB_Event_Menu
      Select EventMenu()
      Case 1
       ;If Starting = #True : StartTraining() : Else : TrainButton() : EndIf
      Case 2
        SkipButton()  
      Case 3
        ContinueButton()
      EndSelect
      
    Case #PB_Event_Gadget
      Select EventGadget()
        Case Train_Button 
          If Starting = #True : StartTraining() : Else : TrainButton() : EndIf
        Case Lock_Button
           If Starting = #True : Unlock() 
           Else : TrainingComplete() : EndIf
        Case Skip_Button
          SkipButton()  
        Case Continue_Button
          ContinueButton()
        Case ComboBox
          Select EventType()
            Case #PB_EventType_Change
              ChangeComboBox()
          EndSelect
        Case Help_Button
          If ListSize(WordsToTrain()) = 0 : RunProgram("readme.pdf") : Else : OpenListView(0,0, 300, 200) : EndIf
      EndSelect
  EndSelect
  
  ProcedureReturn #True
  
  ElseIf EventWindow() = WinListView
   
   Select event
    Case #PB_Event_CloseWindow
      CloseWindow(WinListView) : WinListView = 0
     Case #PB_Event_Gadget
      Select EventGadget()
      EndSelect   
  EndSelect
  ProcedureReturn #True
  
  EndIf
  
EndProcedure


;OnErrorCall(@ErrorHandler())

OpenWin() : Init()
RunProgram("sapisvr.exe", "-SpeechUX", sysroot  + "\Speech\Common\") 

AddKeyboardShortcut(MainWin, #PB_Shortcut_Space, 1)
AddKeyboardShortcut(MainWin, #PB_Shortcut_Control|#PB_Shortcut_S, 2)
AddKeyboardShortcut(MainWin, #PB_Shortcut_Control|#PB_Shortcut_C, 3)

While WinEvents(WaitWindowEvent())
Wend
Download here:
https://1drv.ms/u/s!Al_YF_Qzak_XhHQaUIgAwoydny-S

I also would like to say thanks to the whole Purebasic Community, also to Fred, for their helpfullness and this great forum! Sincerely!

A.D.
Repeat
PureBasic
ForEver