If you took all of the energy that's been expended on writing text-editing software and concentrated that energy on other, less mundane programming problems, computer science would probably be a decade ahead of where it is now. Okay, that might be an exaggeration, but it is true that, when it comes to text editors, a huge amount of effort has been dedicated to reinventing the wheel. Wouldn't it be great to have one piece of text-editing code that all programmers could use as the starting point for their own custom text editors?
With Visual C++'s CRichEditCtrl control, which gives programmers access to Windows 95's rich edit control, you can get a huge jump on any text-editing functionality that you need to install in your applications. The rich edit control is capable of handling fonts, paragraph styles, and text color, as well as handling other types of tasks that are traditionally found in text editors. In fact, a rich edit control provides a solid starting point for any text-editing tasks that your application must handle.
For all intents and purposes, you can consider the rich edit control to be a sort of "black box" word processor. That is, to add text-editing capabilities to your MFC application, you need to do very little programming. Nor do you need to know much about the inner workings of a rich edit control. Just place the control in your application's main window and call the control object's member functions to control it. A rich edit control enables the user to perform the following text-editing tasks:
As the preceding list proves, a rich edit control is powerful. It is, in fact, almost a complete word-processor-in-a-box that you can plug into your program and use immediately. Of course, because a rich edit control offers so many features, there's a lot to learn. In this chapter, you'll get an introduction to creating and manipulating the rich edit control.
There are a couple of ways you can add rich edit text-editing capabilities to your application using Visual C++. The first way is to add a rich edit control to your application's frame or view window. When you use this method, you are completely responsible for dealing with the control. Another way to add a rich edit control to your application is to make your view window an object of MFC's CRichEditView class. When you use this method, MFC handles much of the work for you.
In this section, you'll see how to directly create and manipulate a rich edit control. Using this method, you get an introduction to the control itself and how your MFC program manipulates it directly. Just follow these steps to create the RichEdit application:
ON THE WEB
http://www.quecorp.com/semfc The complete source code and executable file for the RichEdit application can be found in the CHAP13\RichEdit directory at this book's Web site.
1. Start a new AppWizard project workspace called RichEdit, as shown in Figure 13.1.
Here's how you should start the RichEdit project workspace.
2. Give the new project the following settings in the AppWizard dialog boxes. The New Project Information dialog box should then look like Figure 13.2.
Step 1: Single document
Step 2: Default settings
Step 3: Default settings
Step 4: Turn off Printing and Print Preview
Step 5: Default settings
Step 6: Default settings
These are the AppWizard settings for the RichEdit project.
3. Using the resource editor, remove the application's Edit menu. Also, remove all items from the File menu except for Exit.
4. Using the resource editor, remove all of the buttons from the application's toolbar.
5. Add four buttons to the application's toolbar, as shown in Figure 13.3. Give the buttons the IDs ID_UNDERLINED, ID_LEFT, ID_CENTERED, and ID_RIGHT.
Use the resource editor's painting tools to add four buttons to the application's toolbar.
6. Use ClassWizard to associate the ID_UNDERLINED command with the OnUnderlined() message response function, as shown in Figure 13.4. Make sure that you have CMyRichEditView selected in the Class
Name box before you add the function.Add the OnUnderlined() message response function to the view class.
7. Click the
Edit Code button, and then add the lines shown in Listing 13.1 to the new OnUnderlined() function, right after the TODO: Add your command handler code here comment:Listing 13.1 LST13_01.CPP Code for the OnUnderlined() Function
CHARFORMAT charFormat; charFormat.cbSize = sizeof(CHARFORMAT); charFormat.dwMask = CFM_UNDERLINE; m_richEdit.GetSelectionCharFormat(charFormat); if (charFormat.dwEffects & CFM_UNDERLINE) charFormat.dwEffects = 0; else charFormat.dwEffects = CFE_UNDERLINE; m_richEdit.SetSelectionCharFormat(charFormat);8. Use ClassWizard to associate the ID_LEFT command with the OnLeft() message response function, as shown in Figure 13.5. Again, make sure that you have CMyRichEditView selected in the Class
Name box before you add the function.Add the OnLeft() message response function to the view class.
9. Click the
Edit Code button, and then add the lines shown in Listing 13.2 to the new OnLeft() function, right after the TODO: Add your command handler code here comment:Listing 13.2 LST13_02.CPP Code for the OnLeft() Function
PARAFORMAT paraFormat; paraFormat.cbSize = sizeof(PARAFORMAT); paraFormat.dwMask = PFM_ALIGNMENT; paraFormat.wAlignment = PFA_LEFT; m_richEdit.SetParaFormat(paraFormat);10. Use ClassWizard to associate the ID_CENTERED command with the OnCentered() message response function in the view class, as shown in Figure 13.6.
Add the OnCentered() message response function to the view class.
11. Click the
Edit Code button, and then add the lines shown in Listing 13.3 to the new OnCentered() function, right after the TODO: Add your command handler code here comment:Listing 13.3 LST13_03.CPP Code for the OnCentered() Function
PARAFORMAT paraFormat; paraFormat.cbSize = sizeof(PARAFORMAT); paraFormat.dwMask = PFM_ALIGNMENT; paraFormat.wAlignment = PFA_CENTER; m_richEdit.SetParaFormat(paraFormat);12. Use ClassWizard to associate the ID_RIGHT command with the OnRight() message response function in the view class, as shown in Figure 13.7.
Add the OnRight() message response function to the view class.
13. Click the
Edit Code button, and then add the lines shown in Listing 13.4 to the new OnRight() function, right after the TODO: Add your command handler code here comment:Listing 13.4 LST13_04.CPP Code for the OnRight() Function
PARAFORMAT paraFormat; paraFormat.cbSize = sizeof(PARAFORMAT); paraFormat.dwMask = PFM_ALIGNMENT; paraFormat.wAlignment = PFA_RIGHT; m_richEdit.SetParaFormat(paraFormat);14. Use ClassWizard to override the OnCreate() message response function in the view class, as shown in Figure 13.8.
Add the OnCreate() message response function to the view class.
15. Click the
Edit Code button, and then add the following line to the new OnRight() function, right after the TODO: Add your command handler code here comment:m_richEdit.Create(WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOVSCROLL | ES_MULTILINE, CRect(0,0,388,302), this, 111);16. Load the RichEditView.h header file, and add the following lines to the class's Attributes section, right after the line CMyRichEditDoc* GetDocument() that's already there:
protected: CRichEditCtrl m_richEdit;17. Load the MainFrm.cpp file, and add the following lines to the beginning of the PreCreateWindow() function:
cs.cx = 400; cs.cy = 400;
You've now completed the RichEdit application. To compile the application, choose Developer Studio's Build, Build command. You can then run
the program by choosing the Build, Execute command. When you do, the application's main window appears. First, click in the window to give the
rich edit control the focus. Then just start typing. Want to try out character attributes? Click the Underline button to add underlining to either selected text or the
next text that you type. To try out paragraph formatting, click either the Left, Center, or Right buttons to specify paragraph alignment. Figure 13.9 shows the rich
edit control with the different character and paragraph styles used.
A RichEdit control is a complete word processor.
If you carefully examine the RichEdit application's main window, you'll notice that you can see both the window's border and the rich edit control's border. The application shows the control's border so that you can see the control in the window. However, you can get rid of the edit control's border by removing the WS_BORDER flag from the edit control's window styles. When you do this, the main window will show no evidence of the control, except when you type text into it.
Now that you've had a chance to try out a rich edit control, you'll want to examine the source code that accomplishes all those text-editing tricks. In the sections that follow, you'll see how to create a rich edit control and place it in a window. You'll also learn how to change default text settings such as character and paragraph formatting.
In the RichEdit application, the rich edit control is created in the OnCreate() member function, which MFC calls when the Windows sends the WM_CREATE message. In OnCreate(), the program creates the control by calling its Create() member function:
m_richEdit.Create(WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOVSCROLL | ES_MULTILINE, CRect(0,0,388,302), this, 111);
This function's four arguments are the control's style flags, the control's size, a pointer to the control's parent window, and the control's ID. The style constants include the same constants that you would use for creating any type of window, with the addition of special styles used with rich edit controls. Table 13.1 lists these special styles.
Table 13.1 Rich Edit Styles
Style | Description |
ES_AUTOHSCROLL | Automatically scrolls horizontally |
ES_AUTOVSCROLL Automatically scrolls vertically | |
ES_CENTER | Centers text |
ES_LEFT | Left aligns text |
ES_LOWERCASE | Lowercases all text |
ES_MULTILINE | Enables multiple lines |
ES_NOHIDESEL | Disallows hiding selected text when losing the focus |
ES_OEMCONVERT | Converts from ANSI characters to OEM characters and back to ANSI |
ES_PASSWORD | Displays characters as asterisks |
ES_READONLY | Disables editing in the control |
ES_RIGHT | Right aligns text |
ES_UPPERCASE | Uppercases all text |
ES_WANTRETURN | Inserts return characters into text when Enter is pressed |
As soon as the rich edit control is created, you might want to initialize it in some way. The CRichEditCtrl class features a number of member functions that enable you to initialize and manipulate the control. Those member functions and their descriptions are listed in Table 13.2.
Table 13.2 Member Functions of the CRichEditCtrl Class
Function | Description |
CanPaste() | Determines whether the Clipboard's contents can be pasted into the control |
CanUndo() | Determines whether the last edit can be undone |
Clear() | Clears selected text |
Copy() | Copies selected text to the Clipboard |
Create() | Creates the control |
Cut() | Cuts selected text to the Clipboard |
DisplayBand() | Displays a portion of the control's text |
EmptyUndoBuffer() | Resets the control's undo flag |
FindText() | Finds the given text |
FormatRange() | Formats text for an output target device |
GetCharPos() | Gets the position of a given character |
GetDefaultCharFormat() | Gets the default character format |
GetEventMask() | Gets the control's event mask |
GetFirstVisibleLine() | Gets the index of the first visible line |
GetIRichEditOle() | Gets the IRichEditOle interface pointer for the control |
GetLimitText() | Gets the maximum number of characters that can be entered |
GetLine() | Gets the specified text line |
GetLineCount() | Gets the number of lines in the control |
GetModify() | Determines whether the control's contents have changed since the last save |
GetParaFormat() | Gets the paragraph format of selected text |
GetRect() | Gets the control's formatting rectangle |
GetSel() | Gets the position of the currently selected text |
GetSelectionCharFormat() | Gets the character format of selected text |
GetSelectionType() | Gets the selected text's contents type |
GetSelText() | Gets the currently selected text |
GetTextLength() | Gets the length of the control's text |
HideSelection() | Hides or shows selected text |
LimitText() | Sets the maximum number of characters that can be entered |
LineFromChar() | Gets the number of the line containing the given character |
LineIndex() | Gets the character index of a given line |
LineLength() | Gets the length of the given line |
LineScroll() | Scrolls the text the given number of lines and characters |
Paste() | Pastes the Clipboard's contents into the control |
PasteSpecial() | Pastes the Clipboard's contents using the given format |
ReplaceSel() | Replaces selected text with the given text |
RequestResize() | Forces the control to send EN_REQUESTRESIZE notification messages |
SetBackgroundColor() | Sets the control's background color |
SetDefaultCharFormat() | Sets the default character format |
SetEventMask() | Sets the control's event mask |
SetModify() | Toggles the control's modification flag |
SetOLECallback() | Sets the control's IRichEditOleCallback COM object |
SetOptions() | Sets the control's options |
SetParaFormat() | Sets the selection's paragraph format |
SetReadOnly() | Disables editing in the control |
SetRect() | Sets the control's formatting rectangle |
SetSel() | Sets the selected text |
SetSelectionCharFormat() | Sets the selected text's character format |
SetTargetDevice() | Sets the control's target output device |
SetWordCharFormat() | Sets the current word's character format |
StreamIn() | Brings text in from an input stream |
StreamOut() | Stores text in an output stream |
Undo() | Undoes the last edit |
As you can tell from Table 13.2, you can do a lot more with a rich edit control than can possibly be described within the scope of this book. However, the RichEdit program shows you the basics of using the rich edit control in an application, by setting character attributes and paragraph formats. When you include a rich edit control in an application, you'll probably want to give the user some control over its contents. For this reason, you'll usually create menu and toolbar commands for selecting the various options that you want to support in the application. RichEdit doesn't have an Options menu, but it does have a toolbar, whose buttons enable the user to manipulate the rich edit control.
Thanks to MFC's message mapping, these toolbar buttons are associated with the functions that respond to them. For example, when the user clicks the Underline button, MFC calls the OnUnderlined() method, where the program toggles the underline text attribute. In OnUnderline(), the program first initializes a CHARFORMAT structure, like this:
CHARFORMAT charFormat; charFormat.cbSize = sizeof(CHARFORMAT); charFormat.dwMask = CFM_UNDERLINE;
A CHARFORMAT structure holds information about character formatting and is declared by Windows as shown in Listing 13.5.
Listing 13.5 LST13_05.CP The CHARFORMAT Structure
typedef struct _charformat { UINT cbSize; _WPAD _wPad1; DWORD dwMask; DWORD dwEffects; LONG yHeight; LONG yOffset; COLORREF crTextColor; BYTE bCharSet; BYTE bPitchAndFamily; TCHAR szFaceName[LF_FACESIZE]; _WPAD _wPad2; } CHARFORMAT;
In Listing 13.5, cbSize is the size of the structure; dwMask indicates which members of the structure are valid (this mask can be a combination of
dwEffects is the character effects (this value can be a combination of
yHeight is the character height; yOffset is the character baseline offset (for superscript and subscript characters); crTextColor is the text color; bCharSet is the character set value (see the ifCharSet member of the LOGFONT structure); bPitchAndFamily is the font pitch and family, and szFaceName is the font name.
After initializing the CHARFORMAT structure as needed to toggle underlining, the program calls the control's GetSelectionCharFormat() member function, like this:
m_richEdit.GetSelectionCharFormat(charFormat);
This function call, whose single argument is a reference to the CHARFORMAT structure, will return the current character format in the structure's dwEffects member. The program checks the result of this function to determine whether to turn underlining on or off, as shown in Listing 13.6.
Listing 13.6 LST13_06.CPP Determining Whether to Turn Underlining On or Off
if (charFormat.dwEffects & CFM_UNDERLINE) charFormat.dwEffects = 0; else charFormat.dwEffects = CFE_UNDERLINE; m_richEdit.SetSelectionCharFormat(charFormat);
The call to the control's SetSelectionCharFormat() member function sets the character format. Its single argument is a reference to the CHARFORMAT structure containing information about the requested character format.
The RichEdit application also enables the user to switch between the three types of paragraph alignment. This is accomplished similarly to toggling character formats. Listing 13.7 shows the three functions OnLeft(), OnRight(), and OnCenter() that handle the alignment commands. As you can see, the main difference is the use of the PARAFORMAT structure instead of CHARFORMAT and the call to SetParaFormat() instead of SetSelectionCharFormat().
Listing 13.7 LST13_07.TXT Changing Paragraph Formats
void CRicheditView::OnLeft() { PARAFORMAT paraFormat; paraFormat.cbSize = sizeof(PARAFORMAT); paraFormat.dwMask = PFM_ALIGNMENT; paraFormat.wAlignment = PFA_LEFT; m_richEdit.SetParaFormat(paraFormat); } void CRicheditView::OnCenter() { PARAFORMAT paraFormat; paraFormat.cbSize = sizeof(PARAFORMAT); paraFormat.dwMask = PFM_ALIGNMENT; paraFormat.wAlignment = PFA_CENTER; m_richEdit.SetParaFormat(paraFormat); } void CRicheditView::OnRight() { PARAFORMAT paraFormat; paraFormat.cbSize = sizeof(PARAFORMAT); paraFormat.dwMask = PFM_ALIGNMENT; paraFormat.wAlignment = PFA_RIGHT; m_richEdit.SetParaFormat(paraFormat); }
As I mentioned previously, there's an easier way to add a rich edit control to your application: Derive your view class from MFC's CRichEditView class. When you do this with AppWizard, you get an almost complete application, one that not only enables the user to type and edit text but that also incorporates sophisticated cut-and-paste features, as well as file saving and loading and even printing and print preview. To see all of this work, first create the MFCRichEdit application by performing the following steps:
ON THE WEB
http://www.quecorp.com/semfc The complete source code and executable file for the MFC RichEdit application can be found in the CHAP13\MFCRichEdit directory at this book'sWeb site.
1. Start a new AppWizard project workspace called MFCRichEdit, as shown in Figure 13.10.
Here's how to use AppWizard to start a project workspace called MFCRichEdit.
2. Give the new project the following settings in the AppWizard dialog boxes:
Step 1: Single document
Step 2: Default settings
Step 3: Default settings
Step 4: Default settings
Step 5: Default settings
Step 6: Change the base class for CMFCRichEditView to CRichEditView (see Figure 13.11).
You want to derive your view class from MFC's CRichEditView.
3. Add a button to the application's toolbar, as shown in Figure 13.12. Give the button the ID ID_UNDERLINED.
Use the drawing tools to add a button to the application's toolbar.
4. Use ClassWizard to associate the ID_UNDERLINED command with the OnUnderlined() message response function, as shown in Figure 13.13. Make sure that you have CMFCRichEditView selected in the Class
Name box before you add the function.Add the OnUnderlined() message response function to the view class.
5. Click the
Edit Code button, and then add the lines shown in Listing 13.8 to the new OnUnderlined() function, right after the TODO: Add your command handler code here comment:Listing 13.8 LST13_08.CPP Code for the OnUnderlined() Function
CHARFORMAT charFormat = GetCharFormatSelection(); if (charFormat.dwEffects & CFM_UNDERLINE) charFormat.dwEffects = 0; else charFormat.dwEffects = CFE_UNDERLINE; SetCharFormat(charFormat);
You've now completed the MFCRichEdit application. To compile the application, choose Developer Studio's Build, Build command. You can
then run the program by choosing the Build, Execute command. When you do, the application's main window appears, as shown in Figure 13.14.
Take some time to experiment with the application. As you'll discover, the application automatically takes care of editing, file-saving, file-loading, print, and
print-preview features.
The MFCRichEdit application provides a host of features with little effort from the programmer.
Although MFC's CRichEditView class automatically takes care of most text-handling features, you still have to provide your own code for doing things like setting character and paragraph styles. These tasks, however, are accomplished similarly to the way in which they were handled in the original program, RichEdit. The difference is that, rather than calling a rich edit control's member functions directly, you call the CRichEditView object's member functions. The sample program demonstrates only text underlining, but that should be enough to show how you can easily add other features.
An important advantage of using the CRichEditDoc and CRichEditView classes is that CRichEditDoc and CRichEditView manage the document and view using MFC's document/view architecture, enabling you to save your documents in RTF format and to reload them with the formatting intact.