Date: 02-16-2022
Return to Index
created by gbSnippets
'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(Word, CB.lParam)-110, Hi(Word, CB.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 Long) As 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 Long) As 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
http://www.garybeene.com/sw/gbsnippets.htm