November 1998

Transferring TRichEdit text

by Gerry Myers

Sample files are available as part of the file nov98.zip from our Web site. Visit www.zdjournals.com/cpb and click the Source Code hyperlink.

If you've ever used a Rich Edit component, you've no doubt been impressed with its visual appeal. You can display characters in different fonts and paragraphs with varied indentations. And, most of this functionality is hidden, so you needn't know anything about the rich text format (RTF) layout to make good use of it.

That's an advantage if you're concerned only with displaying your text. However, what if your application requires that you gain access to the underlying formatted text--for instance, to copy formatted text to another component, or to send an RTF string out a socket or port? You'll find that this hidden functionality becomes a disadvantage.

In this article, we'll show you how to directly access the formatted text of a TRichEdit component. We'll also discuss the marriage between the TRichEdit VCL component and the underlying Rich Edit common control provided by Windows. (Throughout this article, we'll use the terms component and common control to differentiate between the TRichEdit component and the Windows Rich Edit common control.)

The Windows Rich Edit common control

The Write and WordPad applications that come with Windows are good examples of the Rich Edit common control at work. Note that the Rich Edit control doesn't provide mechanisms for the user to directly format the text that appears in buttons, menus, font list boxes, and so on. The application that uses the control must offer any such capabilities. However, once the user specifies certain formatting, the application can then call the functions of the Rich Edit control to set the characteristics of the text.

The TRichEdit component

As great as the Windows Rich Edit common control is, C++Builder makes it easier to use. The TRichEdit component is a wrapper class for the Rich Edit common control. That doesn't mean that TRichEdit mimics the operation of the common control--it simply provides an object-oriented interface to it. TRichEdit takes care of all the structs and pointers required when using the common control directly. For our discussion, we'll cover the mechanisms TRichEdit provides that allow you to work directly on the RTF text (as opposed to the plain text). If you look through the list of TRichEdit methods, you'll come across several that you'd think (by their titles) return the contents of the rich edit as a string or character buffer. Let's look at one in particular: GetSelTextBuf(). According to the Help file, this method loads a user-provided character buffer with the selected (highlighted) text from the Rich Edit component. Sounds good. Unfortunately, it gives you only the text and none of the formatting (control) characters. So, if you have a nicely formatted string such as

This is some formatted text...

all you get back from a method like GetSelTextBuf() is:

This is some formatted text...

In fact, all the methods and properties of TRichEdit that have the word text in their titles operate the same way. You may then turn to TRichEdit's Lines property, which is a TStrings* type. Knowing that the text of the Rich Edit control is stored there, you may try code like


String s = RichEdit1->Lines->Text;

to pull out all the rich-edit text at one time. Again, you'll find that only the plain text is returned. So, what's the deal with hiding the RTF characters? Now, you've discovered the disadvantage of the Rich Edit common control provided by Windows.

An example

To explain the things that work and don't work when you're accessing the RTF contents of the Rich Edit control, we wrote the example program shown in Figure A.

Figure A: This example program demonstrates using the TRichEdit component.
[ Figure A ]

We've included buttons that let you copy text from one TRichEdit component to another. Notice the three groups of buttons: one group copies RTF text using the Clipboard's functionality, one copies RTF text using streams, and the third copies plain text using the TRichEdit component's properties and methods.

We've already discussed the two buttons for transferring plain text. The other buttons represent the means to access the underlying RTF text in a Rich Edit common control via the Clipboard and via streams.

Using the Clipboard

Since the Rich Edit common control is integrated with Windows, that control and the Windows Clipboard are fully aware of each other. The Rich Edit common control has functions that pass RTF text to and from the Clipboard, and the Clipboard recognizes the rich text format and knows how to handle it.

Since the TRichEdit component is a wrapper around the Rich Edit common control, it contains methods that make direct use of the common control's Clipboard functions. It should be no surprise that the easiest way to transfer RTF text from one TRichEdit component to another is through the Clipboard. Listing A shows the simple code in the Rich Via Clipboard Copy All button's event handler. In just a few lines, RTF text is copied and pasted through the clipboard.

Listing A: Copying RTF text via the Clipboard

// The rich edit control registers the following 
// clipboard formats: CF_RTF and 
// CF_RETEXTOBJ (rich edit text and objects).
RichEdit1->SelectAll();
RichEdit1->CopyToClipboard();
RichEdit2->Clear();
RichEdit2->PasteFromClipboard();
The first line in Listing A is required to select all the text in RichEdit1, because the CopyToClipboard() method sends only the selected (highlighted) text to the Clipboard. The third line clears the destination component's contents for aesthetic reasons only.

As you may have guessed, the code for the Rich Via Clipboard Copy Selected button is even simpler, because only the selected text is copied. This code looks like Listing A, minus the line


RichEdit1->SelectAll();

Using the Clipboard, you can paste RTF text from your TRichEdit component into other applications that recognize RTF. For example, you could have a Copy button or Edit | Copy menu item that simply executes the single command

RichEdit1->CopyToClipboard()

Then, other applications like Write or WordPad can use their Edit | Paste menu item to insert your RTF text. The process can also go the other way--you can paste RTF text from other applications into your TRichEdit component.

Using streams

The Rich Edit common control (and, therefore, the TRichEdit component) provides built-in functions for reading and writing RTF files on disk--a very nice feature. Your application needn't understand anything about the RTF layout to be able to store and recall RTF data. As we mentioned earlier, the TRichEdit component has a Lines property. This property is a TStrings* and contains all of the text-processing properties and methods. Remember, RTF text can't be accessed by using the familiar Lines properties Text or String[ i ]. However, the Lines property can make use of the underlying Rich Edit common control's disk-streaming capability. Therefore, to save or load your RTF text, simply call

RichEdit1->Lines->SaveToFile( filename ); 

or

RichEdit1->Lines->LoadFromFile( filename );

Such code is great for disk access--but what about transferring RTF text to other components? There's hope. Just as the SaveToFile() and LoadFromFile() methods use streams to pass formatted data to disk, you can use the SaveToStream() and LoadFromStream() methods of Lines to pass RTF text to a stream (for instance, TMemoryStream). Listing B shows the code we placed in the event handler of our example's Rich Via Stream Copy All button.

Listing B: Copying via streams

// Create a memory stream.
TMemoryStream* strm = new TMemoryStream;

// Save the rich edit text to the stream.
RichEdit1->Lines->SaveToStream( strm );

// Reset to front of stream.
strm->Position = 0;

// Copy the stream to the other rich edit control.
RichEdit2->Lines->LoadFromStream( strm );

// Clean up.
delete strm;
Note: Flaw in using Text
The Help file says that SaveToStream() uses the Text property of Lines to get the RTF text from the TRichEdit component. However, if you use Lines->Text directly, only plain text is returned. Something is going on that isn't explained, but that's beyond the scope of this article.

Let's look at one last capability. So far, everything we've discussed about transferring RTF text has treated the text as a mysterious RTF object whose insides we didn't have to understand. But, what if you want to get a character buffer of the actual RTF text, so you can look at the special control characters and do some tweaking? Or, what if you want to send the RTF text through a socket or port, but the text must first be in a character-string format?

To get an actual character string of the RTF text, you begin by using a stream, as in the previous example. The only difference is that once the RTF text is saved to the stream, you can call the stream's Read method to read the contents of the stream into a user-provided character buffer. Listing C shows the appropriate code snippet.

Listing C: RTF text-to-character buffer

// Create a memory stream.
TMemoryStream* strm = new TMemoryStream;

// Save the rich edit text to the stream.
RichEdit1->Lines->SaveToStream( strm );

// Reset to front of stream.
strm->Position = 0;

// Create a temporary character buffer.
int bufSize = strm->Size;
char* buf = new char [ bufSize + 1 ];

// Copy the stream to the other rich edit control.
strm->Read( buf, bufSize );

// Do stuff with the buffer now.
. . .

// Clean up.
delete strm;
delete buf;
Before we leave our discussion on streams, you'll notice that another button in the Rich Via Stream group--Copy Selected--is disabled. Its operation is left as an exercise for the reader to figure out how to copy only the selected text to (or from) the memory stream. You'll remember that the SaveToStream() method copies the entire TRichEdit contents regardless of what text is selected (highlighted). Sorry, I left you the harder one.

Conclusion

You can use the TRichEdit component as a simple means to display text in an attractive manner. But, when you want direct access, you can also make the component give up its RTF text. In this article, we've shown you how.