Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Text /
Chapter 2 - TextEdit / Using TextEdit


Responding to Events Using TextEdit

This section discusses some of the TextEdit routines that your application can call in response to event notification. You can use TextEdit routines to

Handling a Null Event

Your program needs to call TEIdle whenever it receives a null event. If there is more than one edit record associated with an active window, make sure you pass TEIdle the handle to the currently active edit record. (See "Activating an Edit Record" in the following section for more information.)

If you have turned on text buffering through the TEFeatureFlag function, you should call TEIdle before any pause of more than a few ticks--for example, before WaitNextEvent. A possibility of a long delay before characters appear on the screen exists--especially in non-Roman systems. Blinking the caret alerts the user to this delay.

To blink the caret at a constant frequency, you should call TEIdle at least once through your main event loop--otherwise, the caret blinks irregularly. No matter how often you call TEIdle, the time between blinks is never to be less than the minimum interval.

Listing 2-4 shows a sample application-defined procedure, MyDoIdle, that calls TEIdle to handle a null event.

Listing 2-4 An idle-processing procedure

PROCEDURE MyDoIdle(myWindow: WindowPtr);
VAR
   myData:     MyDocRecHnd;   {handle to a document record}
   myTERec:    TEHandle;      {handle to TextEdit record}
BEGIN
   myData   := MyDocRecHnd(GetWRefCon(myWindow));
   IF myData <> NIL THEN
      BEGIN
         myTERec  := myData^^.editRec;
         IF myTERec <> NIL THEN
            TEIdle(myTERec);
      END;
END;
Note
The value stored in the low-memory global CaretTime determines the blinking time for the caret. (The user can also set the minimum interval through the General Controls control panel.) You can use the Event Manager's GetCaretTime function to retrieve this value. For more information, see the chapter "The Event Manager" in Inside Macintosh: Macintosh Toolbox Essentials.

Activating an Edit Record

When a window becomes active or inactive, the Window Manager updates the frames of the windows on the screen, and then informs the Event Manager that an activate event has occurred. The next time WaitNextEvent is called from your main event loop, the Event Manager notifies your application that an activate event has occurred. (An activate event can have a flag set indicating that a window is to be deactivated.) When your application receives this notification, it needs to call TEActivate for an activate event and TEDeactivate for a deactivate event. When you call TEActivate, you pass it the handle to the edit record to be activated; when you call TEDeactivate, you pass it the handle to the currently active edit record.

An application can have more than one edit record associated with it. The active edit record is the one where the next editing operation is to take place. The TEActivate procedure identifies an edit record as the active one by either highlighting the selection range or displaying a caret at the insertion point. The TEDeactivate procedure changes an edit record's status from active to inactive and removes the highlighting or the caret. If outline highlighting is on, TEDeactivate frames the selection range or displays a dimmed caret.

Note
The TEActivate procedure does not set the selection range; it uses the current values in the selStart and selEnd fields of the edit record to highlight the specified text or display a caret at the insertion point. The TEDeactivate procedure does not affect the current settings of these fields.
Before you can activate an edit record, you need to deactivate the currently active edit record, if there is one. If your application has a routine which it calls to activate and deactivate its own windows, you can include processing in that routine to make an edit record the active one or make the currently active record inactive. Because deactivate events happen before activate events, these events occur in the proper order when the user switches from one window to another.

If there is more than one edit record associated with a window, you'll probably want to call TEDeactivate whenever the mouse button is clicked in an edit record other than the active one. In this case, each TEDeactivate call not associated with a window deactivate event would be coupled with a call to TEActivate.

You can modify the text of an edit record associated with a background window; however, to do so, you need to call TEActivate for that edit record before you call any other TextEdit routines.

Note
When you use TEClick and TESetSelect to set the selection range or insertion point, the selection range is not highlighted nor is a blinking caret displayed at the insertion point until the edit record is activated through TEActivate. However, if you had already turned on outline highlighting (through the TEFeatureFlag function), the text of the selection range is framed or a gray, unblinking caret is displayed at the insertion point.

Handling Mouse-Down Events

When your application receives notification of a mouse-down event that it determines TextEdit should handle, it needs to pass the click on to the TEClick procedure. Before calling TEClick, your application needs to perform the following steps:

  1. Convert the mouse location that is passed in the event record from global to local coordinates, so that it can pass those local coordinates to TEClick. To perform the conversion, you can use the GlobalToLocal QuickDraw procedure. (For more information, see Inside Macintosh: Imaging.)
  2. Determine if the Shift key was held down at the time of the click to extend the selection. The behavior of TEClick depends on the user's actions.

    • If the Shift key was down, TEClick extends the current selection range.
    • If the Shift key was not held down, TEClick removes highlighting of the current selection range and positions the insertion point as close as possible to the location where the mouse click occurred.
    • When the mouse is moved or dragged, TEClick expands or shortens the selection range a character at a time. The TEClick procedure keeps control until the user releases the mouse button.
    • If the mouse button is clicked twice (a double-click), TEClick extends the selection to include the entire word where the cursor is positioned.

Note
As long as the mouse button is held down, TEClick repeatedly calls the click loop routine pointed to from the clikLoop field of the edit record.
Listing 2-5 shows an application-defined procedure, MyDoContentClick, that calls TEClick, passing it a mouse-down event.

Listing 2-5 Passing a mouse-down event to TextEdit

PROCEDURE MyDoContentClick (myWindow: WindowPtr; event: EventRecord);
VAR
   myData:  MyDocRecHnd;      {handle to a document record}
   myTERec: TEHandle;         {handle to TextEdit record}
   mouse:   Point;
BEGIN
   myData   := MyDocRecHnd(GetWrefCon(myWindow));  {get window's data record}
   IF myData = NIL THEN
      exit(MyDoContentClick);
   myTERec  := myData^^.editRec;                   {get TERec}
   IF myTERec = NIL THEN
      exit(MyDoContentClick);
   SetPort(myWindow);
   mouse := event.where;                     {get the click position}
   GlobalToLocal(mouse);                     {convert to local coordinates}
   IF PtInRect(mouse, myTERec^^.viewRect) THEN 
      BEGIN
         shiftDown := BAnd (event.modifiers, shiftKey) <> 0; 
                           {extend if Shift is down}
         TEClick(mouse, shiftDown, myTERec);
      END;
END; 
When TEClick is called, the clickTime field of the edit record contains the time when TEClick was last called. When TEClick returns, it sets the clickTime field, adjusting the current tick count. The default click loop procedure uses this value.

Responding to an Update Event

After changing any fields of the edit record that affect the appearance of the text or after any editing or scrolling operation that alters the onscreen appearance of the text, you need to call TEUpdate.

Your application needs to call TEUpdate every time the Event Manager function WaitNextEvent reports an update event for a text editing window--after you call the Window Manager procedure BeginUpdate, and before you call the EndUpdate procedure. You call the following routines when an update event occurs:

BeginUpdate(myWindow); 
EraseRect(myWindow^.portRect); 
TEUpdate(myWindow^.portRect, hTE); 
EndUpdate(myWindow); 
If you don't include the EraseRect procedure, the caret may sometimes remain visible when the window is deactivated. For more information about responding to events, see the chapter "Event Manager" in Inside Macintosh: Macintosh Toolbox Essentials. For more information about the Window Manager, see the chapter "Window Manager" in Inside Macintosh: Macintosh Toolbox Essentials.

Accepting Text Input Through Key-Down Events

When the user enters text through the keyboard, your application needs to call the TEKey procedure to accept the keyboard input a byte at a time or to delete a character when the user backspaces over it. Call TEKey every time the Event Manager function WaitNextEvent reports a key-down event that your application determines TextEdit should handle.

Because TEKey accepts every character it is passed, your application needs to first filter out Command-key equivalents, special keys, and nonprinting characters as appropriate, such as Enter or Tab, and only pass TEKey a text, a Return key character, an arrow key character, or a backspace key character.

Note
If you want to display the text as multiple paragraphs, don't filter out Return key characters.
Listing 2-6 shows the MyHandleKeyDown procedure which calls TEKey to accept text a character at a time. First MyHandleKeyDown filters out special characters. For example, it treats the Tab key as a special character, and calls an application-defined routine, MyDoTab, to handle this character appropriately for the document. Then it checks to make sure that inserting the character won't exceed the maximum text length allowed. It does not count the Delete or arrow keys because they are not text characters.

If the maximum text length is not exceeded, the code passes the character to TEKey. Otherwise, it calls an application-defined routine, MyAlertUser, to notify the user that the character is not inserted, and that inserting it would exceed the edit record text limitation. In this example listing, the maximum text length is set to the highest possible value; you can specify a lower limit.

Listing 2-6 Inserting text in a document

PROCEDURE MyHandleKeyDown(myWindow: WindowPtr; event: EventRecord);
CONST
   kMaxTELength = 32767;
   kTab = $09;
   kDel = $08;
   kRightArrow = $1D;
   kLeftArrow = $1C;
   kDownArrow = $1F;
   kUpArrow = $1E;
VAR
   myData:     MyDocRecHnd;      {handle to a document record}
   myTERec:    TEHandle;         {handle to TextEdit record}
   key:        CHAR;
BEGIN
   myData   := MyDocRecHnd(GetWRefCon(myWindow));  {get window's data record}
   IF myData = NIL THEN
      exit(MyDoContentClick);
   myTERec  := myData^^.editRec;                   {get TERec}
   IF myTERec = NIL THEN
      exit(MyDoContentClick);
   key := CHR(BAnd(event.message, charCodeMask));
   IF key = char(kTab) THEN {handle special characters}
      MyDoTab(event)
   ELSE            
      BEGIN
         IF (key = CHR(kDel)) | (key = CHR(kRightArrow)) | 
         (key = CHR(kLeftArrow)) | (key = CHR(kUpArrow)) | 
         (key = CHR(kDownArrow)) | {don't count deletes or arrow keys}
         (LongInt(myTERec^^.teLength - MyGetTESelLength(myTERec) + 1 <
          kMaxTELength)
         THEN
            BEGIN
               TEKey(key, myTERec); {insert character in document}
               MyAdjustScrollbars(window, FALSE);
            END
         ELSE
            MyAlertUser(eExceedChar); 
      END; 
   END;
Before testing to ensure that the input character does not exceed the edit record's text limitation, the code subtracts the length of the selection range, which the inserted character is to replace, from the current length of the text. To get the length of the selection range, the code calls an application-defined function, MyGetTESelLength. Listing 2-7 shows this function. Several other sample application-defined routines in this chapter also call this function.

Listing 2-7 Getting the selection range length

FUNCTION MyGetTESelLength (myTERec: TEHandle): Integer; 
   Begin
      MyGetTESelLength := myTERec^^.selEnd - myTERec^^.selStart;
   END;
If the selection range is an insertion point and the key is not an arrow key character or a Backspace key character, TEKey inserts the character before the insertion point. When the character direction is right-to-left, the character is inserted to the right of the insertion point. When the character direction is left-to-right, the character is inserted to the left of the insertion point.

When you call TEKey and the keyboard script is different from the font script, TextEdit changes the font script to correspond to the keyboard script. If the font at the insertion point is the same as the keyboard script, then this font is used. If a font was written to the TextEdit style scrap record (in the null scrap) and never used and that font script coincides with the keyboard script, then it is used. Otherwise, TextEdit searches through the fonts in the style table until it locates a font that corresponds to the keyboard. If one does not exist, then it uses the application font.

When the user backspaces over characters of a multistyled edit record, TEKey deletes the characters but it saves the character attributes associated with the last character deleted in order to apply it to any new characters that the user might enter; the character attributes are saved in the null scrap's style scrap record. As soon as the user clicks in another area of the text, TEKey clears the attributes from the null scrap.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
6 JUL 1996