I am writing a spell-checker with a simple window that has a Rich Edit control inside it. What I need to do is to retrieve the text that is displayed inside the rich-edit and NOT the whole text that is loaded inside it. For example, you have a 1MB text file loaded into the rich-edit but you just want to retrieve the text that is currently being displayed in the rich-edit's rectangular window. How can I do that?

The second question is about locating specific words inside the current list-view's frame. For example, after I retrieve the contents of the current view of the rich-edit as a string, I am going to have to highlight it within the rich-edit to state that for example, it is a misspelled word. Does anybody have any idea how I can do that? What message(s) should I send to the rich edit control?

Thanks in advance.
Posted on 2007-12-07 06:30:29 by XCHG
I used this successfully for such a task.

For highlighting, I used to overload the Edit/Richedit class and handle WM_PAINT messages, use EM_POSFROMCHAR and draw red lines with MoveToEx/LineTo . This is the best way imho, to avoid trashing the undo/redo buffer and other data-conflicts, introduced by programmatically selecting+Effecting segments of the rich-text.
Posted on 2007-12-07 17:30:59 by Ultrano
Thanks that was really useful. The problem is how I should find out what texts are displayed in the current rectangular frame of the rich-edit? Suppose you have loaded a whole bunch of text into the rich-edit and you have scrolled down the text using the scroll-bar while your cursor is still at the first line. How can you find what page you are at and retrieve the texts in that page into a buffer?
Posted on 2007-12-08 04:49:22 by XCHG

No idea how well that'd work and the speed of it, but it'd be my initial try.
Posted on 2007-12-08 05:09:36 by f0dder
EM_GETSCROLLPOS is only supported with Rich Edit 3.0 :shock:
Posted on 2007-12-08 05:16:39 by XCHG
I think I answered the first question. Use that charfrompos msg to get the char-index at top-left corner and bottom-right corner.

To get a segment of the text,
retrieve PrevSelection (start and end) via EM_EXGETSEL
set the selection to the text to be copied
use EM_GETSELTEXT  to copy the text
Restore the PrevSelection via EM_EXSETSEL

I'm simply reading http://msdn2.microsoft.com/en-us/library/bb762654.aspx to give these hints :). Experiment with the messages, that's the way to learn using the common-controls.
Posted on 2007-12-08 05:18:53 by Ultrano

To get a segment of the text,
retrieve PrevSelection (start and end) via EM_EXGETSEL
set the selection to the text to be copied
use EM_GETSELTEXT  to copy the text
Restore the PrevSelection via EM_EXSETSEL

I believe that's how you retrieve the selected text not the text in view  8) The text I have here is not selected and why should it be? The user scrolls down to the bottom of the rich edit and then I will have to find the text that is seen. I tried the EM_CHARFROMPOS message but it is not that accurate. Sometimes when a line is in the view, it does not give you the starting position. You have to scroll all the way down to the next line somehow to get the line before  :O

Oh by the way, I also found this message which is useful EM_GETFIRSTVISIBLELINE
Posted on 2007-12-08 05:31:58 by XCHG
Alright! I sent the EM_GETFIRSTVISIBLELINE message to get the index of the first visible line. Then I sent the EM_GETLINE message to the contents of the first visible line. This works fine. Now what I have to do is to somehow, magically, find out how many characters are fit into the width and height of the richedit. Perhaps I could find its rectangular area with EM_GETRECT. Then I could perhaps invoke the GetTextMetrics() Win32 API to find the size of the font in the rich edit. Then again, somehow, I should find how many characters fit into a single view! I'm talking to myself eh?
Posted on 2007-12-08 06:08:12 by XCHG
You're overcomplicating things and not following what I meant.
I've given you the list of messages and parts of the sequence of sending them, not the whole solution. And hinted where to look, to also find EM_POSFROMCHAR.
It wouldn't be fun for any of us to just give the whole sequence, would it :). But at least you're exploring correctly so far, except for the wrong (too inconvenient) GetTextMetrics detour :P.
Another hint: it's all extremely easy, with just several messages in a sequence and point-subtracting/adding. And then overloading the richedit-class to paint in the appropriate places.
Posted on 2007-12-08 09:47:09 by Ultrano
GetTextMetrics wouldn't help anyway, unless you're dealing exclusively with monospace fonts... might as well Do Things Properlytm instead :)
Posted on 2007-12-08 17:15:13 by f0dder
Okay I did it! Finally. Sorry but the code is written in Delphi. Since it is all Win32 API, I think it wouldn't hurt if I put it here so that others, maybe having the same problem, could use it:

procedure TForm1.Button1Click(Sender: TObject);
  Buffer              : PChar;
  RichRect            : TRect;
  TextRange          : TTextRangeA;
  BottomRightPoint    : TPointL;
  LastCharacterIndex  : Cardinal;
  HEAP_ZERO_MEMORY    = $00000008;
(* Get the Rich Edit's rectangular area *)
SendMessage(RichEdit1.Handle, EM_GETRECT, 0, lParam(@RichRect));
(* Now try to set the upper leftmost point and get the character index of that point *)
UpperLeftPoint.x := RichRect.Left;
UpperLeftPoint.y := RichRect.Top;
(* Get the upper leftmost character index in view *)
FirstCharacterIndex := SendMessage(RichEdit1.Handle, EM_CHARFROMPOS, 0,
(* Now try to get the character index at the bottom rightmost point *)
BottomRightPoint.x := RichRect.Right - RichRect.Left;
BottomRightPoint.y := RichRect.Bottom - RichRect.Top;
LastCharacterIndex := SendMessage(RichEdit1.Handle, EM_CHARFROMPOS, 0,
(* Set the character index at the top leftmost position *)
TextRange.chrg.cpMin := FirstCharacterIndex;
(* The character index of the bottom rightmost character *)
TextRange.chrg.cpMax := LastCharacterIndex;
(* Allocate a buffer *)
Buffer := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY,
                    LastCharacterIndex - FirstCharacterIndex  + 1);
(* Set the buffer in the structure *)
TextRange.lpstrText := Buffer;
(* Get the range of text *)
SendMessage(RichEdit1.Handle, EM_GETTEXTRANGE, 0, lParam(@TextRange));
(* Celebrate my glory! *)
MessageBox(Form1.Handle, Buffer, nil, MB_ICONINFORMATION);
(* Release the buffer *)
HeapFree(GetProcessHeap, 0, Buffer);
Posted on 2007-12-09 06:35:48 by XCHG
A small note: you should only use VirtualAlloc for substantially large allocations, for something like this you're better off with HeapAlloc.
Posted on 2007-12-09 06:41:23 by f0dder
That's right. Thanks for pointing that out. I will modify the code. By the way, thanks to both of you for helping me with this.
Posted on 2007-12-09 06:50:18 by XCHG
No problem, that's one of the reasons I hang out around on the forums anyway, and you do show that you're doing work yourself, not expecting to be spoon-fed with working code :)
Posted on 2007-12-09 07:07:53 by f0dder
Thanks :oops: What I have to do now though is to highlight words in the rich edit. I think what I have to do is to retrieve the text from the current view with the code I posted above, then search for words in the buffer. Find which one of the words are not in the dictionary. Then get their index and somehow highlight them. Here we go again. Another puzzle  :mad:
Posted on 2007-12-09 07:12:48 by XCHG
This one was easier than I thought it could be. I can simply use the EM_SETSEL message and provide the starting and ending index of the string/char to be selected  :shock:
Posted on 2007-12-09 07:29:52 by XCHG
To be able to retrieve the number of lines that are visible in the current view of a rich-edit, I am doing this:

1) Find the number of characters in the current view.
2) I go through each line in the current view, and retrieve that line's number of characters. I add 2 to the length for CR and LF.
3) I will subtract that from the total number of characters in the view.
4) I will do that until there are no more characters left.

Does anybody know of another way of doing this which could perhaps be faster?
Posted on 2007-12-09 07:53:05 by XCHG
Sorry for piggybacking on this post but I have another important question to ask about rich-edit controls. Suppose you have found the upper-left corner of a word in the rich-edit. Now you want to underline that word. There are two things that I don't understand:

      [*]How am I supposed to know the height of the text so that I can navigate to the bottom left and start the drawing from there?
      [*]How should I find the length of the line?

I guess that for the first question, the answer is: the height of the text is the height that is specified in the CreateFont() Win32 API. But then again, it's just a guess. I'd appreciate it if somebody could help me with tihs.
Posted on 2007-12-14 04:56:36 by XCHG