Important: The information in this document is obsolete and should not be used for new development.
Saving and Restoring a TextEdit Document, and
This section describes how to save to disk the contents of a document created using TextEdit, and restore it when the user opens the document. For both monostyled and multistyled text, you need to save and restore the text and its character attribute information. This section also discusses how to implement an Undo feature.
Implementing UndoSaving a TextEdit Document
To save the contents of a document created using TextEdit and a monostyled edit record, you store the text. You can also save the text characteristics, such as the font and its size and style, and the text margins; you can store this information in a resource. (Save the font name, not the font number.)To save the contents of a document created using TextEdit and a multistyled edit record, you need to save all of the associated character attribute information in addition to the text. Because the text format of the character attribute information in the style scrap is easier to export than the style record itself--it uses the Desk Manager's
'styl'
format--you should use the TextEdit routines that use the style scrap for moving character attribute information:TEGetStyleScrapHandle
andTEUseStyleScrap
. For example, you can use the following steps to save a multistyled text document to disk:
The application-defined procedure
- Create a text file, select all the text of the edit record, and save it in the text file's
data fork.- Call
TEGetStyleScrapHandle
to get a handle to the style scrap record. This creates the style scrap record and uses it to store the character attribute information.- Save the character attribute information in the resource fork of the file.
MyDoSaveAsTextEdit
shown in Listing 2-14 uses this method. Notice that this procedure avoids usingTESetSelect
to select all of the edit record's text. TheTESetSelect
procedure sets and highlights the selection range that you specify. Because you are selecting the text to save it, you don't want it to be highlighted. (Highlighting the text before saving it can mislead a user to presume that some other action is required.)However, if you want to use
TESetSelect
, you can circumvent highlighting of the selection range if you first render the edit record inactive; before you callTESetSelect
, callTEDeactivate
. Also, if you have outline highlighting turned on through theTEFeatureFlag
function'steFOutlineHilite
feature, turn it off. When the edit record is not the active one,TESetSelect
can set the selection range without causing it to be highlighted.Listing 2-14 Saving a multistyled text edit record to disk
PROCEDURE MyDoSaveAsTextEdit(textToSave: TEHandle); CONST kFileType = 'TEXT'; {file type of text file} kFileCreator = 'NIIM'; {creator code of text file} VAR reply: StandardFileReply; {location, name of file to save text to} styles: StScrpHandle; {contains all character } { attributes in text} dataLength: LongInt; {number of bytes of text to write} dataRefNum: Integer; {ref number of text file's data fork} rsrcRefNum: Integer; {ref number of text file's rsrc fork} savedStart: Integer; {saves offset of start of selection} savedEnd: Integer; {saves offset of end of selection} error: OSErr; {error code from toolbox} BEGIN StandardPutFile( '', '', reply); IF reply.sfGood THEN BEGIN {save the current starting and ending offsets of selection} savedStart := textToSave^^.selStart; savedEnd := textToSave^^.selEnd; {select all text; don't use TESetSelect because it } { draws selection} textToSave^^.selStart := 0; textToSave^^.selEnd := textToSave^^.teLength; {get a list of all the attributes in the text} styles := TEGetStyleScrapHandle(textToSave); {reset the selection back to what it was} textToSave^^.selStart := savedStart; textToSave^^.selEnd := savedEnd; {create the text file if it didn't exist before} IF NOT reply.sfReplacing THEN BEGIN error := FSpCreate(reply.sfFile, kFileCreator, kFileType, reply.sfScript); FSpCreateResFile(reply.sfFile, kFileCreator, kFileType, reply.sfScript); error := ResError; END; {open the text file} error := FSpOpenDF(reply.sfFile, fsCurPerm, dataRefNum); rsrcRefNum := FSpOpenResFile(reply.sfFile, fsCurPerm); error := ResError; {write the text to the file} dataLength := textToSave^^.teLength; error := FSWrite(dataRefNum, dataLength, textToSave^^.hText^ ); {Write the attributes to the file} AddResource(Handle(styles), 'styl', 0, ''); WriteResource(Handle(styles)); ReleaseResource(Handle(styles)); {close the text file} error := FSClose(dataRefNum); CloseResFile(rsrcRefNum); error := ResError; END; END;Restoring an Existing TextEdit Document
You can restore the text of an edit record when a user opens a document that was created using TextEdit. One way to do this is to read the text from the data fork into a handle, then write the handle to thehText
field of the edit record; callTECalText
after you do this. Before you write the new handle to thehText
field, dispose of the existing handle, if there is one. For a multistyled edit record, you need to reinstate both the text and the character attribute information for it. (For information about how to open a file, see Inside Macintosh: Files.)You can use a method similar to the one shown in Listing 2-14 on page 2-49 to save a multistyled text document. However, to restore the text, you retrieve the data from the file's data fork and write it to a buffer, then call
TESetText
to make a copy of the text and set thehText
field of the edit record to point to it. TheMyDoOpenTextEdit
procedure shown in Listing 2-15 shows an example of this. Before copying the text to a buffer, theMyDoOpenTextEdit
procedure checks to ensure that the text length does not exceed the 32 KB limit; if it does, TextEdit truncates the text before it copies it.The
MyDoOpenTextEdit
procedure retrieves the character attribute information from the resource fork of the disk file and reinstates it in the edit record's style record by callingTEUseStyleScrap
.Listing 2-15 Restoring a document that uses multistyled TextEdit
PROCEDURE MyDoOpenTextEdit(textToOpen: TEHandle); CONST kFileType = 'TEXT'; {file type of text file} VAR reply: StandardFileReply; {location, name of file to get text from} typeList: SFTypeList; {specifies 'TEXT' files in SF dialog} dataRefNum: Integer; {ref number of text file's data fork} rsrcRefNum: Integer; {ref number of text file's rsrc fork} textBuffer: Handle; {holds text from file} textLength: LongInt; {number of bytes of text to read} styles: StScrpHandle; {contains all character attributes in text} error: OSErr; {error code from toolbox} savedState: SignedByte; {saves state of 'styl' resource} BEGIN typeList[0] := kFileType; StandardGetFile(NIL, 1, typeList, reply); IF reply.sfGood THEN BEGIN {open the data fork of the text file} error := FSpOpenDF(reply.sfFile, fsCurPerm, dataRefNum); error := SetFPos(dataRefNum, fsFromStart, 0); {get the number of bytes of text in the file; limit to 32KB} error := GetEOF( dataRefNum, textLength ); IF textLength > 32767 THEN textLength := 32767; {allocate a buffer for the text} textBuffer := NewHandle(textLength); {read the text into the buffer} error := FSRead( dataRefNum, textLength, textBuffer^ ); {put the text into the TextEdit record} LockHHi(TextBuffer); TESetText(textBuffer^, textLength, textToOpen); HUnlock(textBuffer); {get rid of the text buffer} DisposeHandle(textBuffer); {close the data fork of the text file} error := FSClose(dataRefNum); {open the resource fork of the text file} rsrcRefNum := FSpOpenResFile(reply.sfFile, fsCurPerm); error := ResError; {get the style scrap} styles := StScrpHandle(GetResource('styl', 0)); error := ResError; IF styles <> NIL THEN BEGIN savedState := HGetState(Handle(styles)); {apply the character attributes to the TextEdit record} TEUseStyleScrap(0, textLength, styles, true, textToOpen); {restore state of 'styl' resource} HSetState(Handle(styles), savedState); END; {close the forks of the text file} error := FSClose(dataRefNum); CloseResFile(rsrcRefNum); error := ResError; END; END;Handling Undo
Application users find Undo an especially useful feature. Users might accidently choose Clear from the Edit menu instead of Cut, or they might backspace over more words than intended. In these and cases like them, Undo is invaluable.If you are implementing Undo for multistyled text, you need to save the character attribute information along with the text. Although this section discusses one method, there are a number of ways that you can do this. For example, when you want to save the current attributes of the selected text to allow the user to revert to them, your application calls the
TEGetStyleScrapHandle
function, which returns a handle to the style scrap's style record containing the attributes used for the selected text. To restore the style later, you call theTEUseStyleScrap
procedure. You also need to save the offsets into the edit record's text buffer of the first and last characters to which the character attribute information is to be applied.If your application supports any 2-byte script systems, your Undo operations needs to check for 2-byte characters. Normal cut or paste operations do not present a problem, but be careful when undoing a backspace. When TextEdit backspaces over single characters, it checks
CharByte
to determine if the character to be removed is a 2-byte character. If it is, it removes 2 bytes. (For more information about theCharByte
function, see the chapter the "Script Manager," in this book.) When an application program maintains a buffer of characters that have been backspaced over in order to support Undo, it needs to make a test similar to that in Listing 2-16.Listing 2-16 Checking for 2-byte characters when backspacing
IF myChar = BS then aTeHandle^^ do begin {support backspace undo} IF selStart <> selEnd then begin {not an insertion point save the selection} END ELSE begin i := selStart; IF i > 0 then begin repeat i := i - 1 until CharByte(hText^, i) <= 0; {Note: Guarantees that CharByte(x,0) <= 0} {Also, CharByte does not touch the heap} {Put bytes from i to selStart into buffer} END; END; END;