Important: The information in this document is obsolete and should not be used for new development.
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
- handle idle processing in response to null events (
TEIdle
)- identify the active edit record in response to an activate event (
TEActivate
andTEDeactivate
)- handle mouse-down events (
TEClick
)- update the destination rectangle in response to an update event (
TEUpdate
)- handle key-down events (
TEKey
)
Handling a Null Event
Your program needs to callTEIdle
whenever it receives a null event. If there is more than one edit record associated with an active window, make sure you passTEIdle
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 callTEIdle
before any pause of more than a few ticks--for example, beforeWaitNextEvent
. 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 callTEIdle
, the time between blinks is never to be less than the minimum interval.Listing 2-4 shows a sample application-defined procedure,
MyDoIdle
, that callsTEIdle
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'sGetCaretTime
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 timeWaitNextEvent
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 callTEActivate
for an activate event andTEDeactivate
for a deactivate event. When you callTEActivate
, you pass it the handle to the edit record to be activated; when you callTEDeactivate
, 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. TheTEDeactivate
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.
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.
- Note
- The
TEActivate
procedure does not set the selection range; it uses the current values in theselStart
andselEnd
fields of the edit record to highlight the specified text or display a caret at the insertion point. TheTEDeactivate
procedure does not affect the current settings of these fields.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, eachTEDeactivate
call not associated with a window deactivate event would be coupled with a call toTEActivate
.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
andTESetSelect
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 throughTEActivate
. However, if you had already turned on outline highlighting (through theTEFeatureFlag
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 theTEClick
procedure. Before callingTEClick
, your application needs to perform the following steps:
- 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 theGlobalToLocal
QuickDraw procedure. (For more information, see Inside Macintosh: Imaging.)- 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. TheTEClick
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.
Listing 2-5 shows an application-defined procedure,
- Note
- As long as the mouse button is held down,
TEClick
repeatedly calls the click loop routine pointed to from theclikLoop
field of the edit record.MyDoContentClick
, that callsTEClick
, 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;WhenTEClick
is called, theclickTime
field of the edit record contains the time whenTEClick
was last called. WhenTEClick
returns, it sets theclickTime
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 callTEUpdate
.Your application needs to call
TEUpdate
every time the Event Manager functionWaitNextEvent
reports an update event for a text editing window--after you call the Window Manager procedureBeginUpdate
, and before you call theEndUpdate
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 theEraseRect
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 theTEKey
procedure to accept the keyboard input a byte at a time or to delete a character when the user backspaces over it. CallTEKey
every time the Event Manager functionWaitNextEvent
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 passTEKey
a text, a Return key character, an arrow key character, or a backspace key character.
Listing 2-6 shows the
- Note
- If you want to display the text as multiple paragraphs, don't filter out Return key characters.
MyHandleKeyDown
procedure which callsTEKey
to accept text a character at a time. FirstMyHandleKeyDown
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.