Example17: Find & Replace

Category: Controls - Scintilla

Date: 02-16-2022

Return to Index


 
'Scintilla offers methods to search for and replace text. It also offers limited support for
'using regular expressions in a search.
 
'Scintilla does not provide a Find & Replace window, so the container must provide any user
'interface. This snippet demonstrates how to do just that.
 
'In a nutshell, the snippet below uses the Scintilla SCI_FindText message to find a specified
'string, which is then selected. That allows the use of SCI_ReplaceSel to replace the selection
'with the desired string.
 
'There are two other approaches to implementing Find & Replace within Scintilla.
'   1. Using SCI_SearchAnchor, SCI_SearchNext, and SCI_SearchPrev messages
'   2. Using "target" messages
 
'The 2nd option, using target messages, is useful in avoiding undesirable screen flashing
'or text scrolling that can occur which SCI_ReplaceSel is used in a Replace All action.
'In general, the target approach works by setting the target text, the range of text to
'be replaced, and using SCI_ReplaceTarget to replace all instances of the target text.
 
 
 
'Primary Code:
'Other than the PowerBASIC code that creates the Find & Replace dialog itself, there are
'only 3 procedures which do most of the work FindNext(), ReplaceOnce(), and ReplaceAll().
'Within those procedures here are the Scintilla messages that are used to implement
'Find & Replace.
 
'The selected text position and length is found with these two messages:
  %SCI_GetCurrentPos
  %SCI_GetLength
 
'The position/length information is used to find the next occurrence of a text string
'using this message:
  %SCI_FindText
 
'After text is found, it is selected with this message:
   %SCI_SetSel
 
'Then, if required, replacement of the selected text is done with this message:
   %SCI_ReplaceSel
 
 
 
'Compilable Example:  (Jose Includes)
#Compiler PBWin 9, PBWin 10
#Compile EXE
#Dim All
%Unicode=1
#Include "Win32API.inc"
#Include "scintilla_gb.inc"
#Resource "gbsnippets.pbr"
 
%IDF_FindThis     = 5001 : %IDF_FindText     = 5002 : %IDF_ReplaceThis  = 5003
%IDF_ReplaceText  = 5004 : %IDF_Find         = 5005 : %IDF_Replace      = 5006
%IDF_ReplaceAll   = 5007 : %IDF_Close        = 5008 : %IDF_MatchCase    = 5009
%IDF_MatchWhole   = 5010 : %IDF_Up           = 5011 : %IDF_Down         = 5012
 
%ID_Sci = 1000 : %ID_BtnA = 1001 : %ID_BtnB = 1002
Global hDlg, hSci, hLib, hFind As DWord
 
Global xFindText, xReplaceText, SelectedText As String
Global MatchWord, MatchCase, Direction, FindFlags, SelLength, Anchor, CurPos As Long
Global FindStructure As Sci_TextToFind
 
Function PBMain() As Long
   hLib = LoadLibrary("SCILEXER.DLL")
   Dialog New Pixels, 0, "Scintilla Example",300,300,320,160, %WS_OverlappedWindow To hDlg
   Control Add Button, hDlg, %ID_BtnA, "Find", 10,10,70,20, %WS_Child Or %WS_Visible
   Control Add "Scintilla", hDlg, %ID_Sci, "", 100,10,180,130, %WS_Child Or %WS_Visible
   Control Handle hDlg, %ID_Sci To hSci     'get handle to Scintilla window
   Dialog Show Modal hDlg Call DlgProc
End Function
 
CallBack Function DlgProc() As Long
   Local txt As String
   txt = "Select Case var$ 'first line" + $CrLf + "End Select 'last line" + Chr$(0)
   Select Case CB.Msg
      Case %WM_InitDialog
         BuildAcceleratorTable
         InitializeScintilla
         PostMessage hSci, %SCI_SetSel, 0,0 'unselect initially
      Case %WM_Command
         Select Case CB.Ctl
            Case %ID_BtnA : DisplayFindDialog
         End Select
      Case %WM_Size
         Control Set Size hDlg, %ID_Sci, Lo(WordCB.lParam)-110, Hi(WordCB.lParam)-20
      Case %WM_Destroy
         If hLib Then FreeLibrary hLib      'free the Scintilla library
   End Select
End Function
 
Sub InitializeScintilla
   Local txt As String
   txt = "If x = 2 Then" + $CrLf + "   'do nothing" + $Crlf
   txt = txt + "Else" + $crlf + "   'keep doing NOTHING"
   txt = txt + $crlf + "   'still nothinge" + $crlf + "End If" + Chr$(0)
   SendMessage hSci, %SCI_SetText, 0, StrPTR(txt)
   SendMessage hSci, %SCI_SetMarginWidthN, 0, 20
End Sub
 
Sub BuildAcceleratorTable
   Local c As Long, ac() As ACCELAPI, hAccelerator As DWord  ' for keyboard accelator table values
   Dim ac(1)
   ac(c).fvirt = %FVIRTKEY Or %FCONTROL : ac(c).key   = %VK_F : ac(c).cmd   = %ID_BtnA     : Incr c
   ac(c).fvirt = %FVIRTKEY              : ac(c).key   = %VK_F3 : ac(c).cmd  = %ID_BtnA     : Incr c
   Accel Attach hDlg, AC() To hAccelerator
End Sub
 
   %IDF_FindThis     = 5001 : %IDF_FindText     = 5002 : %IDF_ReplaceThis  = 5003
   %IDF_ReplaceText  = 5004 : %IDF_Find         = 5005 : %IDF_Replace      = 5006
   %IDF_ReplaceAll   = 5007 : %IDF_Close        = 5008 : %IDF_MatchCase    = 5009
   %IDF_MatchWord    = 5010 : %IDF_Up           = 5011 : %IDF_Down         = 5012
 
Sub DisplayFindDialog()
   Local h As Long, w As Long
   Dialog Get Client hDlg To h,w
   Dialog New Pixels, hDlg, "Find & Replace", 100, 100, 360, 130, %WS_SysMenu Or %WS_Caption Or %WS_ClipChildren To hFind
   Dialog Set Icon hFind, "aainfo"
 
   Control Add Label, hFind, %IDF_FindThis, "Find This:", 5, 10, 60, 20
   Control Add Label, hFind, %IDF_ReplaceThis, "Replace With:", 5, 40, 70, 20
   Control Add TextBox, hFind, %IDF_FindText, "", 80, 10, 185, 20
   Control Add TextBox, hFind, %IDF_ReplaceText, "", 80, 40, 185, 20
 
   Control Add Checkbox, hFind, %IDF_MatchCase, "Match Case", 15, 70, 90, 20
   Control Add Checkbox, hFind, %IDF_MatchWord, "Match Whole Word", 15, 90, 120, 20
   Control Add Option, hFind, %IDF_UP, "Up", 165, 70, 90, 20
   Control Add Option, hFind, %IDF_Down, "Down", 165, 90, 90, 20
   Control Set Option hFind, %IDF_Down, %IDF_Up, %IDF_Down
 
   Control Add Button, hFind, %IDF_Find, "Find Next", 275, 10, 75, 20
   Control Add Button, hFind, %IDF_Replace, "Replace", 275, 40, 75, 20
   Control Add Button, hFind, %IDF_ReplaceAll, "Replace All", 275, 65, 75, 20
   Control Add Button, hFind, %IdCancel, "Close", 275, 100, 75, 20
 
   Dialog Show Modal hFind Call SearchProc()
End Sub
 
   'Find dialog equates
   %IDF_FindThis     = 5001 : %IDF_FindText     = 5002 : %IDF_ReplaceThis  = 5003
   %IDF_ReplaceText  = 5004 : %IDF_Find         = 5005 : %IDF_Replace      = 5006
   %IDF_ReplaceAll   = 5007 : %IDF_Close        = 5008 : %IDF_MatchCase    = 5009
   %IDF_MatchWhole   = 5010 : %IDF_Up           = 5011 : %IDF_Down         = 5012
 
CallBack Function SearchProc() As Long
   Local iCheck&, x As Long, y As Long
   Select Case CB.Msg
      Case %WM_InitDialog
         'get selected text and put into the FindText textbox
         SelLength = SendMessage(hSci, %SCI_GetSelText, 0, 0)       'get length of selected text with Chr$(0)
         SelectedText = String$(SelLength," ")                      'set buffer to that length
         SendMessage hSci, %SCI_GetSelText, 0, StrPTR(SelectedText) 'selected text + chr$(0)
         Control Set Text hFind, %IDF_FindText, SelectedText
         GetFindDialogData
         EnableFindButtons
      Case %WM_SYSCOMMAND
         If (CB.wParam AND &HFFF0) = %SC_Close Then          'trap Alt-F4 and X Button
            Control Set Focus hDlg, %ID_Sci
         End If
      Case %WM_Command
         Select Case CB.Ctl
            Case %IdCancel
               Dialog End hFind
            Case %IDF_Find       : FindNext(1)      'find, with warning
            Case %IDF_Replace    : ReplaceOnce(0)   'replace current selection
            Case %IDF_ReplaceAll : ReplaceAll       'replace all without warning
            Case %IDF_CLose      : Dialog End hFind
            Case %IDF_FindText, %IDF_ReplaceText
               If CB.Ctlmsg = %EN_Change Then GetFindDialogData : EnableFindButtons
         End Select
      Case %WM_Destroy
         Control Set Focus hDlg, %ID_Sci               'focus
         Dialog End hFind
   End Select
End Function
 
Function FindNext(warning As LongAs Long
   Local iResult As Long
   GetFindDialogData
   FindStructure.Chrg.cpMin = SendMessage(hSci,IIF(Direction, %SCI_GetCurrentPos, %SCI_GetAnchor),0,0) 'if up-curpos, if down-anchor
   FindStructure.Chrg.cpMax = Direction * SendMessage(hSci,%SCI_GetLength,0,0) 'if up-length   if down-0
   FindStructure.lpstrText = StrPTR(xFindText)
   iResult = SendMessage (hSci, %SCI_FindText, FindFlags, VarPTR(FindStructure))
   If iResult = -1 Then
      Function = 0   'no match
      If Warning = 1 Then
         If Direction = 1 Then MsgBox "Match not found between current position and end of document!", %MB_Ok Or %MB_IconExclamation, "Find Text"
         If Direction = 0 Then MsgBox "Match not found between current position and beginning of document!", %MB_Ok Or %MB_IconExclamation, "Find Text"
      End If
   Else
      Function = 1   'match found
      SendMessage hSci, %SCI_SetSel, FindStructure.ChrgText.cpMin, FindStructure.ChrgText.cpMax
   End If
End Function
 
Function ReplaceOnce(Repeat As LongAs Long
   GetFindDialogData
   If SelLength = 1 Then
      Function = 0
      If Repeat = 0 Then MsgBox "No text selected for replacement!", %MB_Ok Or %MB_IconExclamation, "Find Text"  'notify user
      Exit Function                               'exit if no selection and a Replace is in work
   End If
   Function = 1
   SendMessage hSci, %SCI_ReplaceSel, 0, StrPTR(xReplaceText)
   FindNext(0)
End Function
 
Sub ReplaceAll
   Local iResult As Long
   iResult = ReplaceOnce(1)
   Do While iResult
      iResult = ReplaceOnce(1)
   Loop
End Sub
 
Sub GetFindDialogData
   Control Get Text hFind, %IDF_FindText To xFindText          'find text
   If Trim$(xFindText)="Then Exit Sub                        'no action is FindText is empty
   Control Get Text hFind, %IDF_ReplaceText To xReplaceText    'replace text
   xReplaceText = xReplaceText + Chr$(0)                        'replace text + chr$(0)
   xFindText = xFindText + Chr$(0)                              'find text + chr$(0)
   Control Get Check hFind, %IDF_MatchWord To MatchWord       'find whole words
   Control Get Check hFind, %IDF_MatchCase To MatchCase       'match case
   Control Get Check hFind, %IDF_Down To Direction            '1=down 0=up
   FindFlags = MatchCase * %SCFind_MatchCase + MatchWord * %SCFind_WholeWord  'search constraints
   SelLength = SendMessage(hSci, %SCI_GetSelText, 0, 0)       'get length of selected text with Chr$(0)
   SelectedText = String$(SelLength," ")                      'set buffer to that length
   SendMessage hSci, %SCI_GetSelText, 0, StrPTR(SelectedText) 'selected text + chr$(0)
   Anchor = SendMessage(hSci, %SCI_GetAnchor, 0, 0)           'anchor
   CurPos = SendMessage(hSci, %SCI_GetCurrentPos,0,0)         'current position
End Sub
 
Sub EnableFindButtons
   If Trim$(xFindText) = Chr$(0) Then
      Control Disable hFind, %IDF_Find : Control Disable hFind, %IDF_ReplaceAll
   Else
      Control Enable hFind, %IDF_Find  : Control Enable hFind, %IDF_Find
   End If
   If SelLength Then
      Control Enable hFind, %IDF_Replace
   Else
      Control Disable hFind, %IDF_Replace
   End If
End Sub
 
   '  If ((MatchCase=1) AND (FindText = SelectedText)) Or ((MatchCase=0) AND (LCase$(FindText)=LCase$(SelectedText))) Then
   '  End If
 
'gbs_00637
'Date: 03-10-2012


created by gbSnippets
http://www.garybeene.com/sw/gbsnippets.htm