View Full Version : Help with RichEdit Control
catventure
05-12-2020, 19:39
Hi,
I have a RichEdit control that contains hundreds of lines of text.
I have long wanted to be able to search the text from the start for a particular string and then go to or have the line that contains it scrolled/positioned to appear at the top of the RichEdit control and move the text caret there for possible editing of the text.
Struggling to do it. And I would like to finally get it to work.
I made a [Find] button control and a Textbox control to enter the text to be searched for below the Richedit control itself.
(Ideally, repeatedly pressing the [Find] button would cycle through all occurrence of the searchtext in the RichEdit and move the caret there for possible amending until the end of the text/lines and then start over again from the first line until the user stops pressing or a new search text is entered.]
Obviously, I can easily copy the text into Notepad or text editor and do it that way - but thought it would be good to do it from inside my program if I could find a simple way.
Code snippets below...
Local sLines() as string
Local myText as string
Local searchstr as string
Local numlines, i, respscroll as long
'get text to search for - works OK
Control Get Text hDlg 622 To searchstr
'get all text of richedit - works OK
Control Get Text hDlg, 620 To myText
'get number of lines in richedit - works OK, I think
numlines = Parse(myText, sLines, $CRLF)
'find a Line Number that Contains the 'string' to be searched for - seems OK?
For i=1 To numlines
If InStr( sLines(i), searchstr )<>0 Then
respscroll=i
Exit For
End If
Next i
' if found - scroll to found line and show at top of richedit - doesn't work
If respscroll>0 Then
MsgBox hdlg, "Search Result...", %MB_ICONASTERISK,"Info"
Control Send hdlg,620,%EM_LINESCROLL,0,respscroll
exit function
end if
'If text not found show inform user with messagebox - works OK
MsgBox hdlg, "Not Found", %MB_ICONWARNING, "Information"
This doesn't work properly although it does scroll a bit - but not to the top line of the richedit like I wanted and not sure how to move the caret there either.
Anyone got any ideas or can help solve this please?
catventure/Phil.
10239
Petr Schreiber
05-12-2020, 21:56
Hi Phil,
I would say the key to move the cursor is to perform a selection (even if from the same to the same char).
This code snippet will set cursor on position 1000 in the rich edit:
dword hRich
control handle cbhndl, %ID_EDITOR to hRich
SendMessage(hRich, %EM_SETSEL, 1000, 1000) ' 1000 for start, 1000 for end of selection - moves cursor there, selects nothing
Maybe it can help you :)
Petr
catventure
06-12-2020, 20:49
Hi Petr,
Thanks for your suggestion - can't just see how it can help immediately.. although I 'm sure it could do. :)
I show another code snippet here.
This I think would probably work but I'm having trouble getting the lines of text using %em_get line.
If I could get the line loaded into a string I can check if the search string is within the line and then if it was scroll that line to the top of the richedit. Doesn't matter about the caret being positioned anywhere...
Function findResp() As Long
Local numlines As Long ' for number of lines in richedit control
Local i As Long ' to store number in for/next loop
Local lineindex As Long ' index # of the first visible line
Local charindex As Long ' index of the first character on that line
Local linetextlen As Integer ' length of that line (note the data type!)
Local tempstr, tempstr2 as string 'to hold search string and linetext
Local linetext As String ' receives the line's text (HOW to do???)
'first get search string from Find editbox
Control Get Text hdlg, 627 To tempstr
'If empty findstring then exit
If tempstr="" Then Exit Function
'get total # of lines
Control Send hdlg, 620, %EM_GETLINECOUNT,0,0 To numlines
'get # of first visible line of control
Control Send hdlg, 620, %EM_GETFIRSTVISIBLELINE, 0, 0 To lineindex
'Check all lines from first visible to end of richedit text
For i=lineindex To numlines
' get line number and length of line text
Control Send hdlg, 620, %EM_LINEINDEX,lineindex, 0 To charindex
Control Send hdlg, 620, %EM_LINELENGTH, charindex,0 To linetextlen
linetext = (IIf(linetextlen >= 2, linetextlen, 2))
'*************
'How to copy the actual TEXT of the line into tempstr2?
'Following line does not work. How do I make it work?
Control Send hdlg, 620, %EM_GETLINE,i, linetext To tempstr2
'*************
'When line text is in tempstr2 Check to see if search string is in it.
If InStr(tempstr2,tempstr)>0 Then
respscroll=i
Exit For
End If
Next i
'If line contains it - scroll down To found Line And Show At top of richedit
If respscroll>0 Then
MsgBox hdlg, "Search Result...", %MB_ICONASTERISK,"Information"
Control Send hdlg,620,%EM_LINESCROLL,0,(respscroll-lineindex)
Control Send hdlg,620, %EM_LINEINDEX,respscroll , 0
'Control Send hdlg,620 ,%EM_SCROLLCARET, 0, 0
Exit Function
End If
MsgBox hdlg, "Not Found", %MB_ICONWARNING, "Information"
End Function
This may be a way; may not be the best way however.
Regards,
catventure.
catventure
07-12-2020, 22:07
Hi Petr,
I tried using %em_setsel as you suggested - but I don't fully understand how to scroll the richedit to the location of the found text.
(Thought it would do so automatically)
Please see my example. It is more simpler and elegant than the previous one and would be ideal if the scrolling worked...
It does find all the occurrences of the searchstring without problem but does not highlight or scroll to the found text how I wished it.
Function findResp() As Long
Local sText, selText As String
Global startFrom, foundPos As Integer
Global previousfoundPos As Long
Global lastselText As String
'get search text from find box
Control Get Text hdlg, 627 To selText
' If empty string EXIT
If selText="" Then
lastselText=""
Exit Function
End If
'get all of richedit text
Control Get Text hdlg, 620 To sText
' if search text not same as last time...
If lastselText <> selText Then
startFrom=1
previousfoundPos=0
End If
lastselText=selText
' if more than first time search...
If previousfoundPos<>0 Then
startFrom = previousfoundPos+Len(selText)
Else
startFrom=1
End If
' locate next occurrence of search string (if any)
x=InStr(startFrom,sText,selText)
If x<>0 Then
foundPos=x
Else
foundPos=0
End If
If foundPos>0 Then
'MsgBox 0, foundPos - Yes! it is finding the start positions of the searched for text OK
Control Send hdlg,620, %EM_SETSEL,foundPos,Len(selText)
' OK SO FAR -- BUT HOW TO SCROLL RICHEDIT OR MOVE CURSOR/CARET TO SELECTED TEXT??
previousfoundPos = foundPos+(startFrom-1)
Exit Function
Else
MsgBox hdlg, "Could Not Find"+$CRLF+""+selText
startFrom=1
previousfoundPos=0
End If
End Function
The example finds all occurrences in the text from the beginning of the richedit to the end then will start from the top again should the user keep pressing the button again with the same search string... That is good, I like that.
But how to take the user to the found text in the control?? This is the only bit missing from the puzzle. :) LOL!
I tried a few things like
control redraw hdlg,620
control send hdlg, 620, %em_scrollcaret ,0,0
to make the scrolling happen but no luck!
I will think some more - but tired now.
catventure/Phil.
catventure
11-12-2020, 01:15
Cannot yet solve this!
If there is not a way then I did think that a nice alternative idea would be to have a find/replace dialog for a richedit control...
User could easily find text with that!
I found this old script on Powerbasic forum.
https://forum.powerbasic.com/forum/user-to-user-discussions/source-code/25225-find-and-replace-common-dialog-boxes-in-rich-edit-control?t=24564
I do not understand it all - but apparently works - could be great if converted so it worked in my thinbasic adventure program for searching the code for strings in a simple richedit control.
Bye,
catventure.
Petr Schreiber
13-12-2020, 23:57
Phil,
my apologies, I cannot dive to the topic due to high workload.
However - could you please try this minimal example and see, if the text scrolls to selection?
USES "UI"
Begin ControlID
%bGotoPosition0
%bGotoPosition1000
%richEdit
End ControlID
function TBMain()
dword hDlg
DIALOG NEW pixels, 0, "RichEdit control example", 100, 50, 640, 480, %WS_DLGFRAME | %ds_center | %WS_CAPTION | %WS_SYSMENU to hDlg
DIM wx, hy AS LONG
DIALOG GET CLIENT hDlg TO wx, hy
string sBuffer
for i as long = 1 to 1000
sBuffer += format$(i, "0000") + $CRLF
next
CONTROL ADD rtf_GetClass, hDlg, %richEdit, sBuffer, 0, 0, wx/2, hy, %WS_CHILD | %WS_CLIPCHILDREN | %WS_VISIBLE | %ES_MULTILINE | %WS_VSCROLL | %WS_HSCROLL | %ES_AUTOVSCROLL | %ES_AUTOHSCROLL | %ES_WANTRETURN, %WS_EX_CLIENTEDGE
CONTROL ADD button, hDlg, %bGotoPosition0, "Go to position 0", wx/2+5, 10, 100, 30
CONTROL ADD button, hDlg, %bGotoPosition1000, "Go to position 1000", wx/2+5, 100, 100, 30
'---Show dialog in MODAL state
DIALOG SHOW MODAL hDlg, call dlgCallback
end function
callback function dlgCallback() as long
static hRich as dword
SELECT CASE cbMsg
case %WM_INITDIALOG
CONTROL HANDLE cbhndl, %richEdit TO hRich
CASE %WM_COMMAND
SELECT CASE cbCtl
case %bGotoPosition0
SendMessage(hRich, %EM_SETSEL, 0, 0)
control set focus cbhndl, %richEdit
case %bGotoPosition1000
SendMessage(hRich, %EM_SETSEL, 1000,1000)
control set focus cbhndl, %richEdit
END SELECT
END SELECT
end function
P
catventure
15-12-2020, 16:20
Phil,
my apologies, I cannot dive to the topic due to high workload.
However - could you please try this minimal example and see, if the text scrolls to selection?
P
Hi Petr.
Thanks - will check this out and report back.
A quick look and I see the text does not scroll beyond 215 or 216; so does not go to #1000
catventure.
Petr Schreiber
15-12-2020, 22:26
Hi Phil,
the position 1000 is in characters, not in lines. So the result is correct - each line is 5 chars - 4 for digits, one for line break. 5 * 200 = 1000.
To go to beginning of line 1000, you would need to do SendMessage(hRich, %EM_SETSEL, 1000*5-5,1000*5-5).
Petr
catventure
16-12-2020, 12:55
Hi Phil,
the position 1000 is in characters, not in lines. So the result is correct - each line is 5 chars - 4 for digits, one for line break. 5 * 200 = 1000.
To go to beginning of line 1000, you would need to do SendMessage(hRich, %EM_SETSEL, 1000*5-5,1000*5-5).
Petr
Yes - of course! I think you've solved it Petr. I can highlight the the selections from the curs pos by adding the length of the digits in the numbered text entries.
Now to see if I can adapt it to my program.
I will need to parse the whole richedit text string from the starting point of each search to the beginning of the 'found' string to count the number of $crlf's and add those to the position so that I can get the position of the SetSel correct. The number of $crlf's will differ with each subsequent search of the same string or different string - so need a bit of tinkering about. I guess there may be both $lf and $crlf's! Something to do before Xmas :)
Good work and thanks for simple and effective code example. :clapping:
Petr Schreiber
16-12-2020, 22:07
Hi Phil,
I am glad I could be of help :) I am sure there is a better solution, but I did not do Win32 coding for some time.
I am really happy it works for you!,
Petr
catventure
17-12-2020, 01:06
Hi Phil,
I am really happy it works for you!,
Petr
Hooray - it works!!! I was so close before but Petr's example has helped me a great deal to get it to work.
It was even easier than I had thought
Here is the video of it WORKING GREAT!!!
https://www.youtube.com/watch?v=NnVo36TLMu4
YES :dance1: :yahoo:
'for editor script coding control find buttons
Global laststr As String
Global occurrence As Long Value=1
'findResp() is called when user presses [FindText] button
'627 - editbox control for search text
'620 - Richedit control
'hDlg - window containing richedit control
'==========================
Function findResp() As Long
Local x, linebreaks As Long
'get text from find textbox
Control Get Text hDlg, 627 To searchstr
'if no text typed by user
If searchstr="" Then
laststr=""
occurrence=1
MsgBox hdlg, "Please type in a string to search for", %MB_ICONWARNING, "Information"
Exit Function
End If
'check if text same as last time
If laststr<>searchstr Then
occurrence=1
End If
'copy searchstr into laststr
laststr=searchstr
'try and find position and occurrence of text
x=InStr(response, searchstr, occurrence)
' if string not found...
If x=0 Then
MsgBox hdlg, "Could Not Find"+$CRLF+$DQ+searchstr+$DQ, %MB_ICONASTERISK,"Information"
laststr=""
occurrence=1
Exit Function
End If
'count number of linebreaks from start to position
linebreaks=ParseCount(Mid$(response,1,x), Any $LF)
'subtract number of lnebreaks from position
x =(x-linebreaks)
'highlight occurence of found string
If x=1 Then
Control Send hdlg,620, %EM_SETSEL, x-1, x+Len(searchstr)-1
Else
Control Send hdlg,620, %EM_SETSEL, x, x+Len(searchstr)
End If
Control Set Focus hdlg, 620
'In case user tries to find next occurrence of same string
Incr occurrence
End Function
'==========================
' When exiting Window dialog following vars are reset:
' laststr=""
' occurrence=1
catventure/Phil.
Petr Schreiber
19-12-2020, 19:06
Superb, I am really happy :drink:
P