Typewriter Simulator for Scintilla Controls

Share your advanced PureBasic knowledge/code with the community.
Axolotl
Addict
Addict
Posts: 835
Joined: Wed Dec 31, 2008 3:36 pm

Typewriter Simulator for Scintilla Controls

Post by Axolotl »

I would say the subject/headline says it all.
Others would say another pointless and useless program from me......
No matter, as long as it's fun.

Seriously, because of a question in the german forum, I quickly put something together.
Edit: Small update (added Notepad++) to the supported editors

Code: Select all

; -----------------------------------------------------------------------------
; Proudly presented by Axolotl 
; -----------------------------------------------------------------------------
EnableExplicit 

Enumeration EWindow 1 ; start with 1 (ZERO can be used for error detecting) 
  #WINDOW_Main 
EndEnumeration 

Enumeration EGadget 1 ; start with 1 (ZERO can be used for error detecting) 
  #GADGET_btnStart 
  #GADGET_btnStop 
  #GADGET_cbbEditors 
  #GADGET_btnFind 
  #GADGET_edtCode 
  #GADGET_trbSpeed 
EndEnumeration 

Enumeration EEvent #PB_Event_FirstCustomValue 
  #EVENT_Begin 
  #EVENT_Finished 
EndEnumeration

Enumeration EStatusbar 1 
  #STATUSBAR 
EndEnumeration 

; some constants .... 
; 
#MainCaption$     = "Typewriter Simulator for Scintilla Controls " ; + #PB_Editor_BuildCount + "." + #PB_Editor_CompileCount 
#MainWindowFlags  = #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget  


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

Global hwndEditor, hwndScintilla          ; 
Global ClassTextEditor$, TitleEditor$     ; find the correct editor 
Global TextFormat                         ; get Codepage .. ASCII or UTF8 
Global TextToSend$                        ; 
Global TypeTextStopped, TypeTextDelay     ; 
Global QuitApp = #False 


; -----------------------------------------------------------------------------
; >> PureBasic, Notepad++, SciTE 
; 
Procedure EnumChildProcEditors(hWnd, lParam) 
  Protected text${#MAX_PATH}  

	If hWnd 
		GetClassName_(hwnd, @text$, #MAX_PATH) 
		If text$ = ClassTextEditor$ 
			GetWindowText_(hwnd, @text$, #MAX_PATH) 

			If TitleEditor$ Or FindString(text$, TitleEditor$) 
				hwndEditor = hWnd 
				ProcedureReturn 0   ; stop enumeration 
			EndIf
		EndIf
		ProcedureReturn 1   ; continue enumeration 
	EndIf
	ProcedureReturn 0   ; stop enumeration 
EndProcedure 

; -----------------------------------------------------------------------------
; >> Scintilla  
; 
Procedure EnumChildProcScintilla(hWnd, lParam) 
  Protected text${#MAX_PATH}  

	If hWnd
		GetClassName_(hWnd, @text$, #MAX_PATH)
		If text$ = "Scintilla" 
			hwndScintilla = hWnd 
			ProcedureReturn 0   ; stop enumeration 
		EndIf
		ProcedureReturn 1   ; continue enumeration 
	EndIf
	ProcedureReturn 0   ; stop enumeration 
EndProcedure

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

Procedure FindEditorWithScintillaControl(Which=0) 
  ; 

  Select Which  
    Case 0 ; 0 .. Notepad2 
    	TitleEditor$      = "Notepad2" 
    	ClassTextEditor$  = "Notepad2" 
    Case 1 ; 1 .. Purebasic 
    	TitleEditor$      = "PureBasic"  
    	ClassTextEditor$  = "WindowClass_2" 
    Case 2 ; 0 .. Notepad++ 
    	TitleEditor$      = "Notepad++" 
    	ClassTextEditor$  = "Notepad++" 
    Default 
      ProcedureReturn #False  ; failed 
  EndSelect 

  ; enum the child windows 
  ; 
	EnumChildWindows_(0, @EnumChildProcEditors(), 0)
	If hwndEditor 
  	EnumChildWindows_(hwndEditor, @EnumChildProcScintilla(), 0) 

		If hwndScintilla 
  		Select SendMessage_(hwndScintilla, #SCI_GETCODEPAGE, #Null, #Null) 
  			Case 0     : TextFormat = #PB_Ascii   :Debug "TextFormat == ASCII" 
  			Case 65001 : TextFormat = #PB_UTF8    :Debug "TextFormat == UTF8" 
  		EndSelect 
      ProcedureReturn #True ; succeeded  
    EndIf 
  EndIf 
  

  ProcedureReturn #False  ; failed 
EndProcedure 

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

Procedure GetProcessFromWindow(hWnd)  
	Protected hProcess   

	If GetWindowThreadProcessId_(hWnd, @hProcess) 
		ProcedureReturn OpenProcess_(#PROCESS_ALL_ACCESS, #False, hProcess) 
	EndIf 
	ProcedureReturn 0 
EndProcedure

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

Procedure SendText(Text$) 
	Protected hProcess, length
	Protected *MemoryID, *Buffer, *Text ; format

  hProcess = GetProcessFromWindow(hwndScintilla) 

	If hProcess 
		length = StringByteLength(Text$, TextFormat) 
		*Buffer = AllocateMemory(length + SizeOf(Character)) 
		If *Buffer 
			PokeS(*Buffer, Text$, #PB_Default, TextFormat) 
			*MemoryID = VirtualAllocEx_(hProcess, #Null, Length, #MEM_RESERVE|#MEM_COMMIT, #PAGE_EXECUTE_READWRITE)
			If *MemoryID
				WriteProcessMemory_(hProcess, *MemoryID, *Buffer, Length, #Null)
				SendMessage_(hwndScintilla, #SCI_ADDTEXT, Length, *MemoryID) 
				VirtualFreeEx_(hProcess, *MemoryID, length, #MEM_RELEASE) 
			EndIf 
			FreeMemory(*Buffer) 
		EndIf
		CloseHandle_(hProcess) 
	EndIf 
EndProcedure 

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

Procedure ThreadedTextSending(*Value) 
  Protected state, index, count, word, length, textline$, text$   

  PostEvent(#EVENT_Begin)

  index = 1                     ; start 
  length = Len(TextToSend$)     ; end 

  While index <= length 
    If QuitApp = #True : Break : EndIf ; user has clicked the close button 
    If TypeTextStopped = #False 
      text$ = Mid(TextToSend$, index, 1) 
      SendText(text$) 
      index + 1 
    EndIf 
;   Delay(TypeTextDelay) 
    Delay(Random(TypeTextDelay/10, 10)) 
  Wend 

  PostEvent(#EVENT_Finished) 
EndProcedure 


; --- OnEvent_ function -------------------------------------------------------

Procedure OnEvent_SizeWindow() 
  Protected ww, wh 

  ww = WindowWidth(#WINDOW_Main) : wh = WindowHeight(#WINDOW_Main) 

  ResizeGadget(#GADGET_trbSpeed, ww - 160, #PB_Ignore, #PB_Ignore, #PB_Ignore) 
  ResizeGadget(#GADGET_edtCode, #PB_Ignore, #PB_Ignore, ww - 8, wh - 40) 
EndProcedure 

; --- Main Window -------------------------------------------------------------

Procedure CreateMainWindow(WndW = 600, WndH = 600)  
  If OpenWindow(#WINDOW_Main, 0, 0, WndW, WndH, #MainCaption$, #MainWindowFlags) 
    StickyWindow(#WINDOW_Main, 1) 

    CreateStatusBar(#STATUSBAR, WindowID(#WINDOW_Main)) 
    AddStatusBarField(#PB_Ignore) 
    AddStatusBarField(120) 
    WndH - StatusBarHeight(#STATUSBAR) 

    ButtonGadget(#GADGET_btnStart, 4, 4, 72, 24, "Start")  
    ButtonGadget(#GADGET_btnStop, 80, 4, 72, 24, "Stop") 

    ComboBoxGadget(#GADGET_cbbEditors, 160, 4, 104, 24) 
      AddGadgetItem(#GADGET_cbbEditors, -1, "Notepad2") 
      AddGadgetItem(#GADGET_cbbEditors, -1, "Purebasic") 
      AddGadgetItem(#GADGET_cbbEditors, -1, "Notepad++") 
   
    ButtonGadget(#GADGET_btnFind, 268, 4, 72, 24, "Find Editor")  

    TrackBarGadget(#GADGET_trbSpeed, WndW-160,  4, 152, 24, 1, 20)  

    EditorGadget(#GADGET_edtCode, 4, 32, WndW-8, WndH-40) 

    BindEvent(#PB_Event_SizeWindow, @OnEvent_SizeWindow(), #WINDOW_Main) 

  EndIf 
  ProcedureReturn IsWindow(#WINDOW_Main)  ; non-zero if valid window 
EndProcedure 

Macro ButtonUpdateTyping(State)  
  DisableGadget(#GADGET_btnStart, 1-State) 
  DisableGadget(#GADGET_btnStop, State) 
EndMacro 

Macro ButtonUpdateLookForEditors(State) 
  DisableGadget(#GADGET_cbbEditors, State) 
  DisableGadget(#GADGET_btnFind, State) 
EndMacro 

; --- main() ------------------------------------------------------------------

Procedure main() 
  Protected state, text$ 
  Protected Thread  
 
  If CreateMainWindow() 

    ButtonUpdateTyping(0) 

    SetGadgetState(#GADGET_trbSpeed, 10)  ; init 
    PostEvent(#PB_Event_Gadget, #WINDOW_Main, #GADGET_trbSpeed) ; TypeTextDelay = 20 

    SetGadgetState(#GADGET_cbbEditors, 0) 

    SetGadgetText(#GADGET_edtCode, ";" + #LF$ + 
                                   "; Test Code " + #LF$ +  
                                   "; Paste some Code here ... " + #LF$ +  
                                   "; 1. look for your Editor " + #LF$ +  
                                   "; 2. start the typing " + #LF$ +  
                                   "; " + #LF$ +  
                                   "; You can start and stop the typing at any time. " + #LF$ +  
                                   "" + #LF$ +  
                                   "; BoF") ; end of code 
    ; 
   
    Repeat  ; .. main loop 
      Select WaitWindowEvent(250)  
        Case #PB_Event_None 

        Case #PB_Event_CloseWindow  ; close button on main window --> close application 
          If EventWindow() = #WINDOW_Main 
            QuitApp = #True 
            If IsThread(Thread) 
              If WaitThread(Thread, 2000) = 0   ; timeout was reached 
                KillThread(Thread)              ; stop the thread right now 
              EndIf 
            EndIf 
            Break   ; bye 
          EndIf 

        Case #EVENT_Begin   ; Update GUI 
          ButtonUpdateTyping(0) 
          ButtonUpdateLookForEditors(1) 

        Case #EVENT_Finished   ; Update GUI 
          Beep_(200, 200) 
          ButtonUpdateTyping(1) 
          ButtonUpdateLookForEditors(0) 
          TypeTextStopped = #False 

        Case #PB_Event_Gadget 
          Select EventGadget()
            Case #GADGET_btnStart 
              If TypeTextStopped = #False 
                TextToSend$ = GetGadgetText(#GADGET_edtCode)  ; get the entire text 
                Thread = CreateThread(@ThreadedTextSending(), 0) 
              Else 
                TypeTextStopped = #False 
                ButtonUpdateTyping(0) 
              EndIf 

            Case #GADGET_btnStop 
              TypeTextStopped = #True 
              ButtonUpdateTyping(1) 

            Case #GADGET_btnFind 
              state = GetGadgetState(#GADGET_cbbEditors) 
              text$ = GetGadgetText(#GADGET_cbbEditors) 
              If FindEditorWithScintillaControl(state) 
                StatusBarText(#STATUSBAR, 0, "Editor: " + text$ + " (" + hwndEditor + ", Scintilla = " + hwndScintilla + ") found. ") 
                ButtonUpdateTyping(1) 
              Else 
                StatusBarText(#STATUSBAR, 0, "Editor: " + text$ + " (?, Scintilla = ?) not found. ") 
                ButtonUpdateTyping(0) 
              EndIf 

            Case #GADGET_trbSpeed 
              TypeTextDelay = GetGadgetState(#GADGET_trbSpeed) * 100  ; 
              StatusBarText(#STATUSBAR, 1, "Speed: " + Str(TypeTextDelay/10) + " ") 

          EndSelect 
      EndSelect
    ForEver ; end of main loop 
  EndIf 
EndProcedure 

; --- start -----------------------------------------------------------------
End main() 
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
PeDe
Enthusiast
Enthusiast
Posts: 284
Joined: Sun Nov 26, 2017 3:13 pm

Re: Typewriter Simulator for Scintilla Controls

Post by PeDe »

Thanks for nice useless program.
I tested with Notepad2 5.0.26-beta4, and had to change the value for 'ClassTextEditor$' to 'Notepad2U'.

Peter
User avatar
spikey
Enthusiast
Enthusiast
Posts: 769
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: Typewriter Simulator for Scintilla Controls

Post by spikey »

Notepad++ can already do this for itself, see https://npp-user-manual.org/docs/ghost-typing/

My favourite is:
DEBUGGING /diːˈbʌɡɪŋ/ noun - The classic mystery game where you are the detective, the victim and the murderer.
User avatar
tft
User
User
Posts: 99
Joined: Mon Dec 29, 2008 9:34 am

Re: Typewriter Simulator for Scintilla Controls

Post by tft »

Hallo

"There are no useless projects. I have just written a tool that does exactly 2 things: data backup from X to Y, from 4 sources to 2 targets, as part of my current project. In line with my Twitch TV ambitions, I am looking for the possibility to have text written into the PureBasic editor. This is for representation purposes."

TFT
TFT seid 1989
Aktuelles Projekte : Driving School Evergarden
YouTube : Pure Basic to go
FaceBook : Temuçin SourceMagic Games
DISCORD : SourceMagic
W10 , i9 9900K ,32 GB Ram , GTX Titan , 3 Monitore FHD
ARDUINO Freak :-)
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Typewriter Simulator for Scintilla Controls

Post by Kwai chang caine »

Works nice for IDE PB here
Thanks for this "Useless" program, who can be UseFull for me :mrgreen:
ImageThe happiness is a road...
Not a destination
boddhi
Enthusiast
Enthusiast
Posts: 524
Joined: Mon Nov 15, 2010 9:53 pm

Re: Typewriter Simulator for Scintilla Controls

Post by boddhi »

It may not be useful specifically for this code, but just in case you want to know if the typing was successful.

Modification of Main() procedure only
Add to variable declarations section:

Code: Select all

Protected.i TextLengthBefore,TextLengthAfter
Then replace #EVENT_Begin and #EVENT_Finished cases

Code: Select all

      Case #EVENT_Begin                         ; Update GUI
        ; •••••••• ADD START ••••••••
        TextLengthBefore = SendMessage_(hwndScintilla, #SCI_GETTEXTLENGTH, 0, 0)
        ; •••••••• ADD END ••••••••
        Pc_ButtonUpdateTyping(#False)
        Pc_ButtonUpdateLookForEditors(#True)
      Case #EVENT_Finished                      ; Update GUI
        Beep_(200, 200)
        Pc_ButtonUpdateTyping(#True)
        Pc_ButtonUpdateLookForEditors(#False)
        TypeTextStopped = #False
        ; •••••••• ADD START ••••••••
        TextLengthAfter=SendMessage_(hwndScintilla, #SCI_GETTEXTLENGTH, 0, 0)
        Select Bool(TextLengthAfter>TextLengthBefore)
          Case #True
            MessageRequester("Result", "Complete typing !", #PB_MessageRequester_Info)
          Case #False
            MessageRequester("Result", "No typing !", #PB_MessageRequester_Error)
        EndSelect
        TextLengthbefore = 0:TextLengthAfter = 0
        ; •••••••• ADD END ••••••••
 
Test it in "Integrated IDE debugger" and "Compile without debugger" modes.
If my English syntax and lexicon are incorrect, please bear with Google translate and DeepL. They rarely agree with each other!
Except on this sentence...
Post Reply