VC++ 6.0 ebook chapter index
homepage aranna.altervista.org
Free counters!

The Edit Class

The edit class is in some ways the simplest predefined window class and in other ways the most complex. When you create a child window using the class name "edit," you define a rectangle based on the x position, y position, width, and height parameters of the CreateWindow call. This rectangle contains editable text. When the child window control has the input focus, you can type text, move the cursor, select portions of text using either the mouse or the Shift key and a cursor key, delete selected text to the clipboard by pressing Ctrl-X, copy text by pressing Ctrl-C, and insert text from the clipboard by pressing Ctrl-V.

One of the simplest uses of edit controls is for single-line entry fields. But edit controls are not limited to single lines, as I'll demonstrate in the POPPAD1 program shown in Figure 9-7. As we encounter various other topics in this book, the POPPAD program will be enhanced to use menus, dialog boxes (to load and save files), and printing. The final version will be a simple but complete text editor with surprisingly little overhead required in our code.

Figure 9-7. The POPPAD1 program.

POPPAD1.C

/*----------------------------------------
   POPPAD1.C -- Popup Editor using child window edit box
                (c) Charles Petzold, 1998
  ----------------------------------------*/

#include <windows.h>

#define ID_EDIT     1

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

TCHAR szAppName[] = TEXT ("PopPad1") ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;
     
     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;
     
     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"),
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }

     hwnd = CreateWindow (szAppName, szAppName,
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ; 
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static HWND hwndEdit ;
     
     switch (message)
     {
     case WM_CREATE :
          hwndEdit = CreateWindow (TEXT ("edit"), NULL,
                         WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
                                   WS_BORDER | ES_LEFT | ES_MULTILINE |
                                   ES_AUTOHSCROLL | ES_AUTOVSCROLL,
                         0, 0, 0, 0, hwnd, (HMENU) ID_EDIT,
                         ((LPCREATESTRUCT) lParam) -> hInstance, NULL) ;
          return 0 ;
          
     case WM_SETFOCUS :
          SetFocus (hwndEdit) ;
          return 0 ;
          
     case WM_SIZE : 
          MoveWindow (hwndEdit, 0, 0, LOWORD (lParam), HIWORD (lParam), TRUE) ;
          return 0 ;
          
     case WM_COMMAND :
          if (LOWORD (wParam) == ID_EDIT)
               if (HIWORD (wParam) == EN_ERRSPACE || 
                         HIWORD (wParam) == EN_MAXTEXT)
                    MessageBox (hwnd, TEXT ("Edit control out of space."),
                                szAppName, MB_OK | MB_ICONSTOP) ;
          return 0 ;
               
     case WM_DESTROY :
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}

POPPAD1 is a multiline editor (without any file I/O just yet) in less than 100 lines of C. (One drawback, however, is that the predefined multiline edit control is limited to 30,000 characters of text.) As you can see, POPPAD1 itself doesn't do very much. The predefined edit control is doing quite a lot. In this form, the program lets you explore what edit controls can do without any help from a program.

The Edit Class Styles

As noted earlier, you create an edit control using "edit" as the window class in the CreateWindow call. The window style is WS_CHILD, plus several options. As in static child window controls, the text in edit controls can be left-justified, right-justified, or centered. You specify this formatting with the window styles ES_LEFT, ES_RIGHT, and ES_CENTER.

By default, an edit control has a single line. You can create a multiline edit control with the window style ES_MULTILINE. For a single-line edit control, you can normally enter text only to the end of the edit control rectangle. To create an edit control that automatically scrolls horizontally, you use the style ES_AUTOHSCROLL. For a multiline edit control, text wordwraps unless you use the ES_AUTOHSCROLL style, in which case you must press the Enter key to start a new line. You can also include vertical scrolling in a multiline edit control by using the style ES_AUTOVSCROLL.

When you include these scrolling styles in multiline edit controls, you might also want to add scroll bars to the edit control. You do so by using the same window style identifiers as for nonchild windows: WS_HSCROLL and WS_VSCROLL. By default, an edit control does not have a border. You can add one by using the style WS_BORDER.

When you select text in an edit control, Windows displays it in reverse video. When the edit control loses the input focus, however, the selected text is no longer highlighted. If you want the selection to be highlighted even when the edit control does not have the input focus, you can use the style ES_NOHIDESEL.

When POPPAD1 creates its edit control, the style is given in the CreateWindow call:

WS_CHILD ¦ WS_VISIBLE ¦ WS_HSCROLL ¦ WS_VSCROLL ¦
     WS_BORDER ¦ ES_LEFT ¦ ES_MULTILINE ¦
     ES_AUTOHSCROLL ¦ ES_AUTOVSCROLL

In POPPAD1, the dimensions of the edit control are later defined by a call to MoveWindow when WndProc receives a WM_SIZE message. The size of the edit control is simply set to the size of the main window:

MoveWindow (hwndEdit, 0, 0, LOWORD (lParam),
                            HIWORD (lParam), TRUE) ;

For a single-line edit control, the height of the control must accommodate the height of a character. If the edit control has a border (as most do), use 1.5 times the height of a character (including external leading).

Edit Control Notification

Edit controls send WM_COMMAND messages to the parent window procedure. The meanings of the wParam and lParam variables are the same as for button controls:

LOWORD (wParam) Child window ID
HIWORD (wParam) Notification code
lParam Child window handle

The notification codes are shown below:

EN_SETFOCUS Edit control has gained the input focus.
EN_KILLFOCUS Edit control has lost the input focus.
EN_CHANGE Edit control's contents will change.
EN_UPDATE Edit control's contents have changed.
EN_ERRSPACE Edit control has run out of space.
EN_MAXTEXT Edit control has run out of space on insertion.
EN_HSCROLL Edit control's horizontal scroll bar has been clicked.
EN_VSCROLL Edit control's vertical scroll bar has been clicked.

POPPAD1 traps only EN_ERRSPACE and EN_MAXTEXT notification codes and displays a message box in response.

Using the Edit Controls

If you use several single-line edit controls on the surface of your main window, you'll need to use window subclassing to move the input focus from one control to another. You can accomplish this much as COLORS1 does, by intercepting Tab and Shift-Tab keystrokes. (Another example of window subclassing is shown later in this chapter in the HEAD program.) How you handle the Enter key is up to you. You can use it the same way as the Tab key or as a signal to your program that all the edit fields are ready.

If you want to insert text into an edit field, you can do so by using SetWindowText. Getting text out of an edit control involves GetWindowTextLength and GetWindowText. We'll see examples of these facilities in our later revisions to the POPPAD program.

Messages to an Edit Control

I won't cover all the messages you can send to an edit control using SendMessage because there are quite a few of them, and several will be used in the later POPPAD revisions. Here's a broad overview.

These messages let you cut, copy, or clear the current selection. A user selects the text to be acted upon by using the mouse or the Shift key and a cursor key, thereby highlighting the selected text in the edit control:

SendMessage (hwndEdit, WM_CUT, 0, 0) ;
SendMessage (hwndEdit, WM_COPY, 0, 0) ;
SendMessage (hwndEdit, WM_CLEAR, 0, 0) ;

WM_CUT removes the current selection from the edit control and sends it to the clipboard. WM_COPY copies the selection to the clipboard but leaves it intact in the edit control. WM_CLEAR deletes the selection from the edit control without passing it to the clipboard.

You can also insert clipboard text into the edit control at the cursor position:

SendMessage (hwndEdit, WM_PASTE, 0, 0) ;

You can obtain the starting and ending positions of the current selection:

SendMessage (hwndEdit, EM_GETSEL, (WPARAM) &iStart, 
                                  (LPARAM) &iEnd) ;

The ending position is actually the position of the last selected character plus 1.

You can select text:

SendMessage (hwndEdit, EM_SETSEL, iStart, iEnd) ;

You can also replace a current selection with other text:

SendMessage (hwndEdit, EM_REPLACESEL, 0, (LPARAM) szString) ;

For multiline edit controls, you can obtain the number of lines:

iCount = SendMessage (hwndEdit, EM_GETLINECOUNT, 0, 0) ;

For any particular line, you can obtain an offset from the beginning of the edit buffer text:

iOffset = SendMessage (hwndEdit, EM_LINEINDEX, iLine, 0) ;

Lines are numbered starting at 0. An iLine value of -1 returns the offset of the line containing the cursor. You obtain the length of the line from

iLength = SendMessage (hwndEdit, EM_LINELENGTH, iLine, 0) ;

and copy the line itself into a buffer using

iLength = SendMessage (hwndEdit, EM_GETLINE, iLine, (LPARAM) szBuffer) ;