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


Saving and Restoring a TextEdit Document, and
Implementing Undo

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.

Saving 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 and TEUseStyleScrap. For example, you can use the following steps to save a multistyled text document to disk:

  1. Create a text file, select all the text of the edit record, and save it in the text file's
    data fork.
  2. 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.
  3. Save the character attribute information in the resource fork of the file.

The application-defined procedure MyDoSaveAsTextEdit shown in Listing 2-14 uses this method. Notice that this procedure avoids using TESetSelect to select all of the edit record's text. The TESetSelect 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 call TESetSelect, call TEDeactivate. Also, if you have outline highlighting turned on through the TEFeatureFlag function's teFOutlineHilite 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 the hText field of the edit record; call TECalText after you do this. Before you write the new handle to the hText 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 the hText field of the edit record to point to it. The MyDoOpenTextEdit procedure shown in Listing 2-15 shows an example of this. Before copying the text to a buffer, the MyDoOpenTextEdit 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 calling TEUseStyleScrap.

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 the TEUseStyleScrap 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 the CharByte 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;

Previous Book Contents Book Index Next

© Apple Computer, Inc.
6 JUL 1996