Page 1 of 2

Scintilla find/replace

Posted: Sun Sep 23, 2012 10:21 pm
by Crusiatus Black
Hey all,

Me again, asking for help and info once again.

I'm trying to find info regarding implementing find/replace features in Scintilla, but I can't find
any stuff that explains to me how I can implement this in PureBasic. I would like to code a
dialog similar to the one in the PureBasic IDE, coding the window is easy, but the step of
actually finding / selecting and replacing content in scintilla is unclear to me.

Does anyone have some pointers where precisely I should look, and perhaps some example
code?

Thanks!

Re: Scintilla find/replace

Posted: Sun Sep 23, 2012 11:47 pm
by Tenaja
http://www.scintilla.org/
has everything you need. The Documentation page describes all of the commands, and the source code for the SciTe editor is also available there.
You might also consider checking out GoScintilla on this forum; SRod has done a great job of making a library that converts many lines into a short command.

Edit: there are two Search methods with Scintilla; one repositions the screen, and the other does not. Use the appropriate one for your task.

Re: Scintilla find/replace

Posted: Mon Sep 24, 2012 12:43 am
by falsam
Crusiatus Black wrote:Does anyone have some pointers where precisely I should look, and perhaps some examplecode? Thanks!
Minimalist code include 2 procedures.
ScintillaStringFind(StringFind.s) and ScintillaStringReplace(StringFind.s, StringReplace.s)

Short Key
Ctrl + F : Open Search Replace Dialog
F3 : When Open Search Replace Dialog is close, to continue the search.

Code: Select all

; Scintilla : Search Replace Exemple

EnableExplicit

Enumeration
  #Mainform
  #Editor
  
  #StringFindForm
	
	#StringFind
	#StringFindCheck
	#StringReplace
	#Btn_StringFindNext
	#Btn_StringFindReplace
	#Btn_StringFindEnd
        #Kbd_CtrlF  ;Shortcut Find / replace
	#Kbd_F3     ;Shortcut Next string
	#Kbd_Return 

EndEnumeration

Define.l Event, WEvent, MEvent, GEvent, TEvent

Global WindowStyle.i=#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_ScreenCentered

;Find replace
Global StringFind.s, StringReplace.s

;Indentation
Global SciPos.l, SciLine.l, SciCol.l, SciIndent.l

; Find String
Procedure ScintillaStringFind(StringFind.s)
  Protected Current.l, start.l, Anchor.l
    If StringFind <> ""
      Current = ScintillaSendMessage(#Editor, #SCI_GETCURRENTPOS)
      ScintillaSendMessage(#Editor, #SCI_SETANCHOR, Current)
      ScintillaSendMessage(#Editor, #SCI_SEARCHANCHOR)
      Start = ScintillaSendMessage(#Editor, #SCI_SEARCHNEXT, 0, @StringFind)
      If  Start<> -1
        anchor = ScintillaSendMessage(#Editor, #SCI_GETANCHOR)
        ScintillaSendMessage(#Editor, #SCI_SETSEL, start, anchor)
      Else
        MessageRequester("Find string", "No more matching")
      EndIf
    EndIf
    
EndProcedure



; Find Replace string
Procedure  ScintillaStringReplace(StringFind.s, StringReplace.s)
  Protected StringSel.s 
  
  ;Storing the selected word
  ScintillaSendMessage(#Editor, #SCI_COPY)
  StringSel=GetClipboardText()
  
  If LCase(StringSel)=LCase(StringFind)
    SetClipboardText(StringReplace)
    ScintillaSendMessage(#Editor, #SCI_PASTE)
    
    ;Find the next occurrence
    ScintillaStringFind(StringFind)
  EndIf
  

EndProcedure


; Show find/replace string dialog
Procedure Open_StringFindForm()
	If OpenWindow(#StringFindForm,0,0,455,195,"Find/Replace",#PB_Window_Tool|#PB_Window_WindowCentered,WindowID(#Mainform))
	  TextGadget(#PB_Any,30,21,85,20,"Find")
	  ComboBoxGadget(#StringFind,130,18,310,23,#PB_ComboBox_Editable)
	  
	  CheckBoxGadget(#StringFindCheck,15,60,105,20,"Replace by")
		ComboBoxGadget(#StringReplace,130,59,310,23,#PB_ComboBox_Editable)
		
		ButtonGadget(#Btn_StringFindNext,20,150,125,20,"Next String")
		ButtonGadget(#Btn_StringFindReplace,160,150,80,20,"Find (Replace)")
		ButtonGadget(#Btn_StringFindEnd,360,150,80,20,"Close")
		
		DisableGadget(#StringReplace, #True)
		DisableGadget(#Btn_StringFindReplace, #True)
		   
		SetActiveGadget(#StringFind)
		
		AddKeyboardShortcut(#StringFindForm, #PB_Shortcut_Return, #Kbd_Return) ;Find String
	EndIf
EndProcedure

; Scintilla Callback
;
; CallBack To receive the events of Scintilla gadget
; scinotify * pointer to a structure containing the information on the event:
; See the bottom of this page http://www.scintilla.org/ScintillaDoc.html
;
; -Knowing the current cursor position and correctly position
;  after having press the Enter key
Procedure ScintillaCallBack(Gadget, *scinotify.SCNotification)
  ; Enter key is used
  If *scinotify\ch=13
    
    ;New line: Cursor on the indentation of the previous line.
    ScintillaSendMessage(Gadget, #SCI_SETLINEINDENTATION, SciLIne+1, SciIndent)
    If SciIndent=0 
      SciPos=SciPos+2
    EndIf
   
    ScintillaSendMessage(Gadget, #SCI_GOTOPOS, SciPos+SciIndent)

  EndIf
  
  ;Position in the chain scintilla
  SciPos = ScintillaSendMessage(Gadget, #SCI_GETANCHOR)
  
  ;Line from position
  SciLine = ScintillaSendMessage(Gadget, #SCI_LINEFROMPOSITION, SciPos)
  
  ;Column from position
  SciCol = ScintillaSendMessage(Gadget, #SCI_GETCOLUMN, SciPos)
  
  ;Determination de l'indentation
  SciIndent = ScintillaSendMessage(Gadget, #SCI_GETLINEINDENTATION, SciLine)  
EndProcedure


;Init scintilla properties (minimalist)
Procedure ScintillaSetProperties(Gadget)
 
 ScintillaSendMessage(Gadget,  #SCI_SETLEXER, 0); No lexer
  
 ScintillaSendMessage(Gadget, #SCI_STYLESETFONT, #STYLE_DEFAULT,@"Arial") ;Police
 ScintillaSendMessage(Gadget, #SCI_STYLESETSIZE, #STYLE_DEFAULT, 12) ;Size
 ScintillaSendMessage(Gadget, #SCI_STYLECLEARALL)
  
 ScintillaSendMessage(Gadget, #SCI_SETCARETLINEBACK, RGB(245, 245, 220));Line Active BackColor
 ScintillaSendMessage(Gadget, #SCI_SETCARETLINEVISIBLE, #True) ;Cursor Yes
 ScintillaSendMessage(gadget, #SCI_SETCARETWIDTH, 3) ;Cursor Size
 
 ScintillaSendMessage(Gadget, #SCI_SETUSETABS, #False) ;User tabs replace by space
 ScintillaSendMessage(Gadget, #SCI_SETINDENT, 8) ; Number spaces
        
EndProcedure


;Mainform
Procedure Open_MainForm()
  OpenWindow(#Mainform, 0, 0, 800, 600, "New Form", WindowStyle)
  ScintillaGadget(#Editor, 10, 10, 780, 580, @ScintillaCallBack())
  ScintillaSetProperties(#Editor)
  
  ;Init Shortcut  
  RemoveKeyboardShortcut(#Mainform, #PB_Shortcut_Tab)
  AddKeyboardShortcut(#Mainform, #PB_Shortcut_F|#PB_Shortcut_Control, #Kbd_CtrlF)
  AddKeyboardShortcut(#Mainform, #PB_Shortcut_F3, #Kbd_F3)
  
  SetActiveGadget(#Editor)

EndProcedure

Procedure Start()
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    InitScintilla()
  CompilerEndIf

  Open_MainForm()
EndProcedure

start()

Repeat
  Event  = WaitWindowEvent(10)
  WEvent = EventWindow()
  MEvent = EventMenu()
  GEvent = EventGadget()
  TEvent = EventType()
  Select Event
    Case #PB_Event_Menu
      Select MEvent
        Case #Kbd_CtrlF
          Open_StringFindForm()
          
        Case #Kbd_F3 
          ScintillaStringFind(StringFind)
          
        Case #Kbd_Return
          Select WEvent 
            Case #StringFindForm
              ScintillaStringFind(StringFind)
          EndSelect
  
      EndSelect
                
    Case #PB_Event_Gadget
      Select GEvent  
        Case #StringFindCheck
          If GetGadgetState(#StringFindCheck)=#PB_Checkbox_Checked
            DisableGadget(#StringReplace, #False)
            DisableGadget(#Btn_StringFindReplace, #False)
          Else
            DisableGadget(#StringReplace, #True)
            DisableGadget(#Btn_StringFindReplace, #True)
          EndIf
          
        Case #StringFind 
          StringFind=GetGadgetText(#StringFind)
          
        Case #StringReplace
          StringReplace=GetGadgetText(#StringReplace)
          
        Case #Btn_StringFindEnd
          CloseWindow(#StringFindForm)
          
        Case #Btn_StringFindNext
          ScintillaStringFind(GetGadgetText(#StringFind))
          
        Case #Btn_StringFindReplace
          ScintillaStringReplace(StringFind, StringReplace)
          
      EndSelect
        
    Case #PB_Event_CloseWindow
      End
  EndSelect
ForEver

Re: Scintilla find/replace

Posted: Mon Sep 24, 2012 5:09 am
by jassing
I get an IMA error...

Code: Select all

;Scintilla Callback
Procedure ScintillaCallBack()
	
EndProcedure ; << [ERROR] Invalid memory access. (write error at address 0)
Probably something conflicting -- but it's frustrating... Thanks for the demo -- I keep meaning to implement scintilla in a project, but never seem to get around to it...

Re: Scintilla find/replace

Posted: Mon Sep 24, 2012 6:55 am
by Crusiatus Black
Works perfectly for me, it's a nice start that shows me how I can do this.
Thank you very much for the example!

Jassing, the code as provided causes the exception? At the EndProcedure keyword?
Have you tried restarting the compiler and re-executing the code?

Re: Scintilla find/replace

Posted: Mon Sep 24, 2012 6:57 am
by Demivec
jassing wrote:I get an IMA error...

Code: Select all

;Scintilla Callback
Procedure ScintillaCallBack()
	
EndProcedure ; << [ERROR] Invalid memory access. (write error at address 0)
I get the same error.

Re: Scintilla find/replace

Posted: Mon Sep 24, 2012 7:01 am
by Crusiatus Black
PureBasic version, OS version and x86/x64? :) I would like to reproduce that error.

Re: Scintilla find/replace

Posted: Mon Sep 24, 2012 7:04 am
by Danilo
Help says the callback has 2 params:

Code: Select all

  ProcedureDLL ScintillaCallBack(Gadget, *scinotify.SCNotification)
    ;
    ; The ProcedureDLL declaration is important for the callback to work correctly on MacOSX,
    ; on all other OS it has no effect.
    ;
  EndProcedure

Re: Scintilla find/replace

Posted: Mon Sep 24, 2012 7:05 am
by Demivec
Crusiatus Black wrote:PureBasic version, OS version and x86/x64? :) I would like to reproduce that error.
Win XP x86

Re: Scintilla find/replace

Posted: Mon Sep 24, 2012 7:29 am
by Demivec
@falsam: I got your example working after making the parameter change suggested by Danillo. It is a great example.

The replace function has one quirk though. It works fine unless there is no text selected before the first replacement is attempted.

To reproduce it:
  • type three lines
    * Click somewhere in the first line without creating a selection
    * Call up the find dialog (it can also be called up before this)
    * Enter a 'find' word for something in the second or third line of the document
    * Enter a 'replace by' word
    * Click 'Find (Replace)'.
The result is nothing will take place without first clicking 'Find Next'.

Re: Scintilla find/replace

Posted: Mon Sep 24, 2012 8:04 am
by falsam
Sorry for this huge mistake in the callback. I've updated the code.

Re: Scintilla find/replace

Posted: Mon Sep 24, 2012 8:30 am
by falsam
Update the code :
I added a bonus: Calculation of the indentation for the next line.

Re: Scintilla find/replace

Posted: Mon Sep 24, 2012 9:59 pm
by Crusiatus Black
Thanks! :)

Re: Scintilla find/replace

Posted: Fri Aug 23, 2013 12:48 am
by dobro

Code: Select all

Procedure ScintillaStringFind(StringFind.s)
  Protected Current.l, start.l, Anchor.l
    If StringFind <> ""
      Current = ScintillaSendMessage(#Editor, #SCI_GETCURRENTPOS)
      ScintillaSendMessage(#Editor, #SCI_SETANCHOR, Current)
      ScintillaSendMessage(#Editor, #SCI_SEARCHANCHOR)
      Start = ScintillaSendMessage(#Editor, #SCI_SEARCHNEXT, 0, @StringFind)
      If  Start<> -1
        anchor = ScintillaSendMessage(#Editor, #SCI_GETANCHOR)
        ScintillaSendMessage(#Editor, #SCI_SETSEL, start, anchor)
      Else
        MessageRequester("Find string", "No more matching")
      EndIf
    EndIf
   
EndProcedure
does not work, if the search term contains an emphasis

example: the word "repeated" in french "répété" :cry:


ps : excellent Falsam
dommage ça ne marche plus avec les Accents ... :)

Re: Scintilla find/replace

Posted: Fri Aug 23, 2013 12:54 am
by Tenaja
dobro wrote:does not work, if the search term contains an emphasis

example: the word "repeated" in french "répété" :cry:
That is due to the lack of a UTF8 conversion...see SRod's GoScintilla for a sample.