[Next] [Previous ] Chapter18
   [Contents ]  [Chapter 17: Entry fields ] [Chapter 19: Other Window Classes]

Multiline Edit Controls

When OS/2 was released in the middle of the MacintoshTM era, many people wondered why it didn't have s control similar to that used in any of the Mac's popular, easy-to-use word processors. IBM's answer in OS/2 1.2 was the multiline edit control (usually abbreviated as MLE); this control provided a similar yet simpler version of what people saw on the Macintosh.  It supported the multiline text entry and browsing that they were familiar with and the anchor point selection style discussed in Chapter 17.
But let's not stop there: The MLE was also one of the first controls to support a selectable font, and it can handle very large text buffers easily. Being a stream-based editing control means that word wrap also came cheaply. Finally, it included a primitive undo capability.
Unfortunately, IBM tried (and failed) to emulate the Macintosh; it has no multifont capability, which contributed heavily to ease-of-use that made Mac such a big seller. Also, it seems clumsily written. Even with all of these problems, the MLE still is quite usable and is nifty for grabbing a chunk of text from the user when needed. MLEs are used everywhere - in the WPS (setting pages), in containers (editing icon text), and so on.

Terminology, Etc.

Table 18.1 shows the styles available for the MLE control.
Table 18.1 MLE Styles
Style  Description
MLS_BORDER
Creates an MLE with a surrounding border
MLS_DISABLEUNDO
Specifies that the MLE should ignore undo actions.
MLS_HSCROLL Specifies that the MLE should have a horizontal scrollbar.
MLS_IGNORETAB
Specifies that the MLE should ignore the tab key and instead pass the WM_CHAR message to its owner.
MLS_READONLY
Creates an MLE that is read-only.
MLS_WORDWRAP
Specifies that the MLE should wrap words to the next line that do not fit on the current line.
MLS_VSCROLL Specifies that the MLE should have a vertical scrollbar.

The MLE has a concept of an import/export buffer that is used to set and query the text in the control (called importing and exporting text). Also, since the control is used frequently to read from and write to files, the MLE supports different and-of-line formats.
Table 18.2 MLE End-of-Line Formats
Format
 Description
CR-LF
A carriage return (CR) followed by a line feed (LF) denotes the end of a line.
LF
LF denotes the end of a line
Windows MLE
On import, CR CR LF is ignored, and CR LF is interpreted as end of line. On export, CR LF is used to specify end of line and CR CR LF is used to denote line breaks caused by word wrapping.

To set the import/export buffer, the MLE expects to receive an MLM_SETIMPORTEXPORT message before receiving any MLM_IMPORT (import text from buffer) or  MLM_EXPORT (export text to buffer). The format of the text to be imported or exported is specified in the MLM_FORMAT message. The MLM_SETIMPORTEXPORT message simply tells the MLE the address of the buffer to be used in later message; thus, if this message is sent followed immediately by an MLM_IMPORT message, whatever was in the buffer will get imported. Similarly, multiple MLM_IMPORT messages can be sent to import the same text multiple times.
Additional messages that correspond to well-known or easily understood capabilities are the MLM_SETSEL (set selection) and MLM_SETWRAP (set world wrap) messages.
Two items need to be noted. The first is the concept of an insertion point (datatype is IPT), which is simply an offset in the MLE from the beginning of the text. The second is that of line numbers; it may seem obvious since we are programming using a language whose arrays  begin at index 0, but it doesn't hurt to state explicitly that line numbers, when used in the various MLE messages, began at 0 also.

MLE1

The following sample shows an MLE and performs some rudimentary operations with it.

MLE1.C
MLE1.RC
MLE1.H
MLE1.MAK
MLE1.DEF


Entry fields


Figure 18.1 MLE control.

The code does most of the important work in addText and selectAllText.
   WinSendMsg(hwndMle,
              MLM_SETIMPORTEXPORT,
              MPFROMP(achImpExp),
              MPFROMLONG(sizeof(achImpExp)));
   WinSendMsg(hwndMle,
              MLM_FORMAT,
              MPFROMLONG(MLFIE_NOTRANS),
              0);
As was stated earlier, the import/export transfer buffer must be set before any text is imported. Also, since the internal representation of a new-line character is simply a line feed, we have to tell the MLE that the format of the imported text is just that (MLFIE_NOTRANS).
   iInsert=0;

   for (usIndex=1; usIndex<=20; usIndex++) {
      sprintf(achImpExp,"This is line %d.\n",usIndex);

      WinSendMsg(hwndMle,
                 MLM_IMPORT,
                 MPFROMP(&iInsert),
                 MPFROMLONG(strlen(achImpExp)));
   } /* endfor */
Finally, we loop to insert 20 lines of text. As can be seen in the message section at the end of this chapter, MLM_IMPORT updates mpParm1 to reflect the point just after the place where the last character was inserted; this is to prepare the application for the next import (or export, for MLM_EXPORT).
The processing of the input focus is interesting.
   case WM_SETFOCUS:
      if (SHORT1FROMMP(mpParm2)) {
         WinPostMsg(hwndWnd,MYM_SETFOCUS,0,0);
      } /* endif */
      break;
   case MYM_SETFOCUS:
      WinSetFocus(HWND_DESKTOP,pidData->hwndMle);
      break;
While a focus change is in progress, applications are not supposed to call WinSetFocus or WinFocusChange. Presentation Manager will not prevent this from being done, but since it has not completed the focus processing, any window to which the focus is assigned will lose it immediately. The only way to accomplish this - as in the code just given - is to post a message that will call WinSetFocus. Since posting is being done, not sending, the message gets executed whenever it gets dispatched, which is after the focus change has completed.

How to Upset a User Rather Quickly.

Upon running MLE1, it is noticeable how the control repainted itself whenever any changes took place. Whenever an application does a lot of textual manipulations, this can look rather nasty. Fortunately, two messages can be used to disable and enable updates - MLM_DISABLEREFRESH and MLM_ENABLEREFRESH. The first messages tells the MLE that the application is making many changes and that it should not update the display until an  MLM_ENABLEREFRESH message is sent.
Gotcha!
The MLM_DISABLEREFRESH message does not work as advertised; instead of disabling display updates and disabling the mouse pointer, it simply disables the mouse pointer. A better way to perform this action is to use the WinEnableWindowUpdate function specifying FALSE as the second parameter (and reenabling using the same function with TRUE as the second parameter). Also, the MLM_DISABLEREFRESH message disables the mouse systemwide, instead of just over itself, which can be quite annoying for operations that take up large amounts of time. An application that is guilty of this is System Editor, readers can start the editor and read a file that is greater than 500K to see an example of this.

No Refreshment

MLE2 is the next sample to be looked at. It calls WinEnableWindowUpdate to disable the window refresh before inserting the text and calls it again to enable the window refresh afterward. Its behavior should be compared with that of MLE1.

MLE2.C
MLE2
.RC
MLE2
.H
MLE2
.MAK
MLE2
.DEF

Clipboard Support

In Chapter 17, we discussed what the clipboard is and which entry-field messages can be used to interface with it. The MLE has a similar set of messages - MLM_COPY, MLM_CUT and MLM_PASTE - that perform analogous functions. As with the entry field, the first two messages require that some text is selected in the MLE, so these two usually are used in conjunction with MLM_SETSEL message. Because the concepts associated with the clipboard were explained thoroughly in the last chapter, we will move on the next topic.

Navigation without a Sextant

Suppose the insertion point corresponding to a known line number withing an MLE has to be found. Or, given an insertion point, the line number where the insertion point can be found to be determined. Because of the word-wrap capability of the MLE, these can be difficult - if not impossible - to calculate without some help from the control. Fortunately, the MLE has two such messages that perform these functions for you; they are  MLM_CHARFROMLINE and MLM_LINEFROMCHAR.

Line by Line

The following example uses the MLM_CHARFROMLINE message to read its contents line by line and to write each line to a file.

MLE3.C
MLE3
.RC
MLE3
.H
MLE3
.MAK
MLE3
.DEF

The main difference between this sample and the previous two is the addition of the function exportText. Its purpose is to read, line by line the contents of the MLE and to write each line to a file. To do this, we make use of the MLM_QUERYLINECOUNT, MLM_CHARFROMLINE, and MLM_QUERYLINELENGTH messages. First, we need to determine how many lines are in the MLE; the first message does this.
   lNumLines=LONGFROMMR(WinSendMsg(hwndMle,
                                   MLM_QUERYLINECOUNT,
                                   0,
                                   0));
Obviously, we use this as the terminating condition of a for loop. Each iteration of the loop performs the following: Determine the offset of the first character on the line using MLM_CHARFROMLINE; query the length of the line using MLM_QUERYLENGTH; finally, query the data on the line using MLM_EXPORT.
   for (lIndex=0; lIndex<lNumLines; lIndex++) {
      iBegin=LONGFROMMR(WinSendMsg(hwndMle,
                                   MLM_CHARFROMLINE,
                                   MPFROMLONG(lIndex),
                                   0));
      lSzLine=LONGFROMMR(WinSendMsg(hwndMle,
                                    MLM_QUERYLINELENGTH,
                                    MPFROMLONG(iBegin),
                                    0));

      memset(achImpExp,0,sizeof(achImpExp));

      WinSendMsg(hwndMle,
                 MLM_EXPORT,
                 MPFROMP(&iBegin),
                 MPFROMP(&lSzLine));

      fputs(achImpExp,pfExport);
   } /* endfor */
Gotcha!
The MLM_QUERYLINECOUNT takes as its parameter an insertion point instead of a line number, as would be imagined.

Searching for What Was That Again ?

An action that is commonly performed on large quantities of text is searching for a particular string.  Before digging out Knuth volumes, readers should take note of the MLM_SEARCH message. This message will do both search and search-and-replace actions on the text contained withing the MLE. The method of communication is via the MLE_SEARCHDATA structure, which specifies the string to search for and (optionally) a replacement string.
   typedef struct _SEARCH    /* search */
   {
      USHORT cb;            /* size of search spec structure       */
      PCHAR  pchFind;       /* string to search for                */
      PCHAR  pchReplace;    /* string to replace with              */
      SHORT  cchFind;       /* length of pchFindString             */
      SHORT  cchReplace;    /* length of replace string            */
      IPT    iptStart;      /* point at which to start search      */
      /* (negative indicates cursor pt)      */
      /* becomes pt where string found       */
      IPT    iptStop;       /* point at which to stop search       */
      /* (negative indicates EOT)            */
      USHORT cchFound;      /* Length of found string at iptStart  */
   } MLE_SEARCHDATA;
cb specifies the size of the structure,  pchFind points to the search text. pchReplace points to the text to replace with. cchFind specifies the length of the search text. cchReplace specifies the length of the replacement text. iptStart on entry specifies the search starting point. If this is -1, cursor position is used. On exit, iptStart specifies the insertion point of the first character of the occurrence found, if one is found. iptStop specifies the search ending point. If this is -1, the end of text is used. If this is less than iptStart, the search wraps to the beginning of the text after it reaches the end. cchFound specifies the length of the text found.
mpParm1 specifies one or more flags that are used to determine the action of the search.
Table 18.3 mpParm1- Style flags in MLM_SEARCH Message 
Format
 Description
MLFSEARCH_CASESENSITIVE If set, only exact matches are considered a successful match.  If not set, any case-combination of the correct characters in the correct sequence is considered a successful match.
MLFSEARCH_SELECTMATCH If set, the MLE selects the text and scrolls it into view when found, just as if the application had sent an MLM_SETSEL message.  This is not done if MLFSEARCH_CHANGEALL is also indicated.
MLFSEARCH_CHANGEALL Using the MLE_SEARCHDATA structure specified in mpParm1, all occurrences of pchFind are found, searching from iptStart to iptStop, and replacing them with pchReplace. If this style is selected, the cchFound field has no meaning, and the iptStart value points to the place where the search stopped, or is the same as iptStop because the search has not been stopped at any of the found strings. The current cursor location is not moved.  However, any existing selection is deselected.
  
Since the MLE can hold a large quantity of text, searches conceivably can take a long time to complete. Because of this, the MLE periodically sends the application a WM_CONTROL message with an MLN_SEARCHPAUSE notification code; this allows the application to halt the search(usually per the user's request0; it also can be used to implement a progress indicator.

As if That Weren't Enough

Finally, there is a number of messages that perform miscellaneous functions. To select a font, there is the MLM_SETFONT message, which is a bit tricky to use since it expects a font attributes structure(FATTRS). Fortunately, the Font Dialog (see Chapter 26) returns the FATTRS structure  for the font selected, so if we consent to using this (a good idea), we can avoid a lot of work. The current font is returned in a FATTRS structure by the MLM_QUERYFONT message.
Gotcha!
The MLM_SETFONT message is the only way to change the font of an MLE control. WinSetPresParam will not work as it does with the other window classes.

Undo support is provided through three messages: MLM_UNDO, which actually performs the und