Previous Book Contents Book Index Next

Inside Macintosh: Programmer's Guide to MacApp / Part 2 - Working With MacApp
Chapter 29 - Working With PowerTalk Mailers


Recipes--PowerTalk Mailers

The recipes and sample code in this section describe how to add MacApp's PowerTalk support to your application and how to define a document class that supports PowerTalk mailers.

Recipe--Adding PowerTalk Support to Your Application

To add MacApp's PowerTalk support to your application, you perform these steps:

  1. Build your application to include MacApp's PowerTalk support.
  2. Include the Mail menu in your application.
  3. Initialize MacApp's mailer support by calling InitUMailer from your application's main routine.
  4. Define an application class that is a subclass of TMailingApplication.
  5. Decide whether to define a new file type for your letter files.

The sample code in this recipe is from the DemoText application.

IMPORTANT
This recipe describes how to include MacApp's PowerTalk support in your application. To use that support, you also must follow the steps in "Recipe--Adding Mailer Support to a Document Class," beginning on page 628.

Build Your Application to Include MacApp's PowerTalk Support

Not all applications need electronic mail, so MacApp's PowerTalk support is conditionally compiled--that is, the mailer code isn't included unless you specifically ask for it. For example, if you build your application with MPW and the MABuild tool (described in Appendix A), you include MacApp's PowerTalk support with a build line similar to the following:

MABuild YourAppName [-options] -PowerTalk
Building with the -PowerTalk option sets the qPowerTalk compiler flag to TRUE. You can use the following line to specifically build your application without PowerTalk support:

MABuild YourAppName [-options] -noPowerTalk

Include the Mail Menu in Your Application

MacApp's Defaults.r file defines a 'CMNU' resource for the Mail menu used to perform mail operations. The Mail menu contains the commands Add Mailer/Remove Mailer, Send, Reply, Forward, and Open Next Letter.

You can add the Mail menu from MacApp's Defaults.r file to your application's resource definition file by cutting and pasting the text or by adding the following lines to your resource file:

#if qPowerTalk
include "Defaults.rsrc" 'CMNU' (mMail);
#endif
Code between the #if and #endif compiler directives will not be part of your application unless qPowerTalk is defined as TRUE when the code is compiled (which is the case when you build with the -PowerTalk option). The constant mMail is defined in the file MacAppTypes.r. You can include that file into your resource definition file with the following lines:

#ifndef __MacAppTypes__
#include "MacAppTypes.r"
#endif
You also add the Mail menu to your application's menu-bar resource, as shown in this code fragment from the resource definition file of the DemoText application:

resource 'MBAR' (kMBarDisplayed,
#if qNames
"kHierDisplayedMBar",
#endif
nonpurgeable) {
   {
      mApple; 
      mFile; 
      mEdit; 
      mText; 
      mFormat
#if qPowerTalk
      ;mMail
#endif
   }
};
If you don't have room in your application's menu bar for the Mail menu, you can add it as a hierarchical menu in your File menu.

Initialize MacApp's Mailer Support

Your application initializes MacApp's mailer support by calling InitUMailer from its main routine. You should call InitUMailer after calling InitUMacApp, with code similar to the following:

#if qPowerTalk
   if (HasAOCEToolBox())
      InitUMailer();
#endif
The line

if (HasAOCEToolBox())
determines whether the PowerTalk system software is available on the current machine. Use this test to isolate code that should not be performed unless PowerTalk support is available.

IMPORTANT
You should be careful to isolate code that deals with mailers, using either conditional compilation (the qPowerTalk flag) or conditional testing (HasAOCEToolBox). Otherwise, your application may try to call MacApp code or reference a MacApp compile time variable that was not included in the current build. This can cause the program to crash.

Define an Application Subclass of TMailingApplication

The TMailingApplication class contains application-level support for working with PowerTalk mailers, both in its own methods and in methods and fields inherited from the mixin class MMailing. To take advantage of this support, your application class must be a subclass of TMailingApplication. The DemoText application begins its application class definition as follows:

class TDemoTextApplication : public TMailingApplication

Decide Whether to Define a New Type for Letter Files

MacApp defines a default file type for the letters your application creates:

const DescType typeLetterDesc = 'lttr';
const DescType kStandardLetterFileType = typeLetterDesc;
The MMailing class field fMainLetterFileType stores the principal file type your application uses for letters. The field is set to kStandardLetterType in the constructor for TMailingApplication. By default, a letter file has the same Finder icon as the document it is added to, because MacApp does not supply special icons or bundle information for the letter file type.

You can specify a different file type for your application's letters by passing it to the IMailingApplication method. You can specify new icons for your letters by adding icon and bundle information to your application's resource definition file. See the "Finder Interface" chapter of Inside Macintosh: Macintosh Toolbox Essentials for more information about application icons and bundle information.

Recipe--Adding Mailer Support to a Document Class

MacApp's PowerTalk mailer support helps you add mailers to your documents, turning them into electronic mail. To add mailer support to your document class, you perform the following steps:

  1. Decide which mail formats your document will support.
  2. Define a subclass of TMailableDocument for your mailer document.
  3. In the constructor method for your mailable document class, set the fSendFormats field to match the formats you support.
  4. Override document methods to get and set the mailer's data for the formats you support. You may need to override the following methods:

    • AddNativeMailContent
    • AddSnapshotMailContent
    • AddStandardMailContent
    • ImageDocumentForMailer
    • ReadNativeMailContent
    • ReadSnapshotMailContent
    • ReadStandardMailContent

  5. If necessary, override the MMailable::SetReplyContents method to set up a reply mailer.
  6. If necessary, override the MMailable::MakeRoomForMailer method.
  7. If necessary, override the TLetter::OpenLetter method.

The sample code in this recipe is from the DemoText application.

IMPORTANT
This recipe describes how to add PowerTalk support to a document class. To implement this recipe, you must also perform the steps in "Recipe--Adding PowerTalk Support to Your Application," beginning on page 624.

Decide Which Mail Formats to Support

When you use a mailer to send a document as a letter, you can send the document in a native format (that is, any of the internal document formats supported by your application), in snapshot format (an image or "snapshot" of the document), in a special format called standard interchange format, or in any combination of these formats simultaneously. Standard interchange format is a set of data formats that consists of plain text, styled text, sound (AIFF), images ('PICT'), and QuickTime movies ('MooV').

The DemoText application sends letters containing both native and standard interchange format.

Define a Subclass of TMailableDocument for Your Mailer Document

Your application adds to MacApp's PowerTalk support primarily by overriding methods of the MMailable class. Since MMailable is mixed in with the TMailableDocument class, you normally override methods of MMailable by defining a document class that descends from TMailableDocument. The DemoText application begins its document class definition as follows:

class TTEDocument : public TMailableDocument

Set the Document's fSendFormats Field

In the constructor method for your mailable document class, you set the fSendFormats field to match the formats you support. The value for this field is computed by adding the constants for each of the three standard mail formats supported by your application. For example, the following line from the constructor method of the MMailable class indicates that the default setting is to support all three standard formats:

fSendFormats = kSMPNativeMask + kSMPImageMask +
            kSMPStandardInterchangeMask;
In its constructor method, the TTEDocument class changes the setting for fSendFormats with the following lines:

#if qPowerTalk
fSendFormats = kSMPNativeMask + kSMPStandardInterchangeMask;
#endif
In the DemoText application, the TTEDocument class supports native and standard interchange formats only.

Override Methods to Get and Set Mailer Data

Your document class may need to override certain methods, as described in the following sections, to get and set mailer data for the formats it supports.

AddNativeMailContent, ReadNativeMailContent

If your document class supplies DoRead and DoWrite methods to read and write its data, MacApp handles native format automatically. If your document class doesn't supply DoRead and DoWrite methods, or if you need to modify MacApp's default behavior, your document class can override the AddNativeMailContent and ReadNativeMailContent methods.

In the DemoText application, the TTEDocument class overrides the ReadNativeMailContent method:

Boolean TTEDocument::ReadNativeMailContent()
{  
   Boolean readContent = TMailableDocument::ReadNativeMailContent();
   
   // fStyles and fElements are native to DemoText (see
   // TTEDocument::DoRead) so they can be used as a valid check
   // for native content.
   if (fDocText && fStyles && fElements)
   {
      this->ShowReverted();
      readContent = TRUE;
   }
   return readContent;
}
This method calls TMailableDocument::ReadNativeMailContent to read the native format content, then calls ShowReverted so that the TTEDocument class can set up its text display.

AddSnapshotMailContent, ImageDocumentForMailer, ReadSnapshotMailContent

To support the snapshot format, your document class overrides the ImageDocumentForLetter and ReadSnapshotMailContent methods. The AddSnapshotMailContent method calls ImageDocumentForLetter, so you don't normally need to override AddSnapshotMailContent itself.

Your version of the ImageDocumentForLetter method provides a suitable image of your document. Your version of ReadSnapshotMailContent extracts the document image. The DemoText application does not support snapshot format, but you can find an example of an image-drawing routine in the "Standard Mail Package" chapter of Inside Macintosh: AOCE Application Interfaces.

AddStandardMailContent, ReadStandardMailContent

To support the standard mail format, your document class overrides the AddStandardMailContent and ReadStandardMailContent methods.

In the DemoText application, the TTEDocument class overrides the AddStandardMailContent method to add styled text content:

void TTEDocument::AddStandardMailContent(Boolean& okToSend)
{
   WindowRef theWindow = fTEView->GetGrafPort();
   Boolean appendFlag = FALSE;
   Handle theText = fTEView->ExtractText();
   SignedByte wasTextState = LockHandleHigh(theText);
   Ptr buffer = *theText;
   unsigned long bufferSize = GetHandleSize(theText);
   StScrpHandle theStyles = (StScrpHandle)NewHandle(10);
   fTEView->GivePasteData((Handle)theStyles, 'styl');
   SignedByte wasStyleState = LockHandleHigh((Handle)theStyles);
   Boolean startNewScript = TRUE;
   short scriptID = smRoman;
   FailOSErr(SMPAddContent(theWindow, kMailStyledTextSegmentType,
                     appendFlag,buffer, bufferSize, *theStyles,
                     startNewScript, scriptID));
   HSetState(theText, wasTextState);
   HSetState((Handle)theStyles, wasStyleState);
   DisposeIfHandle((Handle)theStyles);
   okToSend = TRUE;
}
This method extracts text from the document's fTEView field, calls a method of the view (GivePasteData) to extract the style information, then calls the Standard Mail Package routine SMPAddContent to add the styled text content.

The TTEDocument class overrides the ReadStandardMailContent method to perform the opposite operation--to extract styled text content from the mailer and install it in the document's own view. The ReadStandardMailContent method is implemented as follows:

Boolean TTEDocument::ReadStandardMailContent()
{
   Boolean        readContent = FALSE;
   const unsigned long kBufferSize = 10000;
   MailSegmentMasksegmentTypeMask = kMailStyledTextSegmentMask;
   Handle         buffer = NewHandle(kBufferSize);
   FailNIL(buffer);
   
   const short kNumStyles  = 200;
   const long  kStyleBufferSize = (sizeof(ScrpSTElement) * kNumStyles) + 4;
   StScrpHandle theStyles  = (StScrpHandle)NewHandleClear(kStyleBufferSize);
   FailNIL(theStyles);
   
   Boolean endOfContent = FALSE;
   OSErr err = noErr;
   Boolean allContentExtracted = TRUE;
   
   while ((err == noErr) && !endOfContent)
   {
      // Skip past empty text segments.
      ScriptCode  script   = 0;
      unsigned    longdataSize = 0;
      long        segmentLength = 0;
      long        segmentID = 0;
      Boolean     endOfScript = FALSE;
      Boolean     endOfSegment = FALSE;
      MailSegmentTypesegmentType;
      
      endOfContent = FALSE;
   
      unsigned long bufferSize = kBufferSize;
      SetHandleSize(buffer, bufferSize);
      SetHandleSize((Handle)theStyles, kStyleBufferSize);
      (**theStyles).scrpNStyles = kNumStyles;
      SignedByte wasStylesState = LockHandle((Handle)theStyles);
      SignedByte wasBufferState = LockHandle(buffer);
      err = SMPReadContent(fLetter->GetMailerWindowRef(), segmentTypeMask,
                      *buffer, bufferSize, &dataSize, *theStyles, &script,
                      &segmentType, &endOfScript, &endOfSegment, &endOfContent,
                      &segmentLength, &segmentID);
      
      if(allContentExtracted && (dataSize > 0) && !endOfContent)
         allContentExtracted = FALSE;
      
      if ((err == noErr) && (dataSize > 0))
      {
         HSetState(buffer, wasBufferState);
         HSetState((Handle)theStyles, wasStylesState);
         SetHandleSize(buffer, dataSize);
         unsigned long textSize = dataSize;
         dataSize = (sizeof(ScrpSTElement) * (**theStyles).scrpNStyles) + 4;
         SetHandleSize((Handle)theStyles, dataSize);
         TEStyleInsert(*buffer, textSize, (StScrpHandle)theStyles, fTEView->fHTE);
         fTEView->SynchView(FALSE);
         readContent = TRUE;
      }
   }
   
   DisposeIfHandle(buffer);
   DisposeIfHandle((Handle)theStyles);
   
   if(!allContentExtracted)
   {
      MacAppAlert(kContentNotExtracted, NULL);
   }

   return readContent;
}  // TTEDocument::ReadStandardMailContent
This method repeatedly calls SMPReadContent (a Standard Mail Package routine) to extract text and style data, and TEStyleInsert (a Toolbox routine) to insert the data into the document's fTEView field. If ReadStandardMailContent cannot extract all the content (text style, PICT, or any other format), it displays an alert box. The constant kContentNotExtracted identifies 'ALRT' and 'DITL' resources defined in the file TTEDocument.r. The MacApp routine MacAppAlert displays a dialog box with the text "This letter includes Standard Mail Format DemoText cannot display."

If Necessary, Override SetReplyContents

When a user chooses the Reply command from the Mail menu, an application typically creates a reply mailer document addressed to the sender of the original letter, with the same or a similar subject and recipient list. The reply letter may also contain the text from the original letter, perhaps separated by a dashed line or other border.

MacApp uses its standard (if somewhat complex) Apple event mechanism to respond to the Reply command. The DoMailMenuCommand method of the TLetter class handles the Reply menu command by calling the letter's DoReply method. DoReply creates and sends a Create Element (cAECreateElement) event. The event is dispatched to the application object and handled by the MScriptableObject::DoScriptCommand method, which calls DoAECreateElement. In the TMailingApplication class, DoAECreateElement creates a document object and calls the document's DoAECreateReply method. That method in turn calls the letter's DoAECreateReply method.

The TLetter::DoAECreateReply method takes care of certain overhead, including setting up the mailer view, then calls the document's SetReplyContents method, at last reaching the subject of this section.

In the MMailable class, which is mixed into TMailableDocument, the SetReplyContents method does nothing. To set up the reply letter in your desired format, you override SetReplyContents in your mailable document class. The TTEDocument class overrides SetReplyContents to set up a reply containing a dashed line followed by the text from the original letter, using the following code:

void TTEDocument::SetReplyContents(TDocument* replyToDoc)
{  
   // Extract text from original document and copy into this document.
   DisposeIfHandle(fDocText);
   fDocText = ((TTEDocument*)replyToDoc)->fDocText;
   FailOSErr(HandToHand(&fDocText));
   
   // Do the same for all style information.
   DisposeIfHandle((Handle)fElements);
   fElements = ((TTEDocument*)replyToDoc)->fElements;
   if (fElements != NULL)
      FailOSErr(HandToHand((Handle*)&fElements));
   
   DisposeIfHandle((Handle)fStyles);
   fStyles = ((TTEDocument*)replyToDoc)->fStyles;
   if (fStyles != NULL)
      FailOSErr(HandToHand((Handle*)&fStyles));
   
   // Call ShowReverted to adjust display.
   ShowReverted();
   
   // Insert a dashed-line spacer above the styled text.
   if (fTEView->Focus())
   {
      (*fTEView->fHTE)->selStart = 0;
      (*fTEView->fHTE)->selEnd = 0;
      
      CStr255 replySpacer;
      GetIndString(replySpacer, kPromptsRsrcID, kReplySpacer);
      TextStyle newStyle;
      newStyle.tsFace = 0;
      TESetStyle(doFace,&newStyle,FALSE,fTEView->fHTE);
      TEInsert((Ptr)&replySpacer.fStr[1], replySpacer.Length(),
               fTEView->fHTE);
      
      (*fTEView->fHTE)->selStart = 0;
      (*fTEView->fHTE)->selEnd = 0;
      fTEView->SynchView(FALSE);
   }
}
This code copies the text and style information from the original letter to the reply letter, then inserts a dashed line above the text as a spacer. The insertion point is placed above the space to allow the user to enter a reply to the original letter.

If Necessary, Override MMailable::MakeRoomForMailer

When you add a mailer to a document, MacApp attempts to create and position a mailer view automatically. It calls the document's MakeRoomForMailer method to make room for the new mailer view.

The MakeRoomForMailer method is defined in the TLetter and MMailable classes (MMailable is mixed in with the TMailableDocument class). In TLetter, MakeRoomForMailer calls the MakeRoomForMailer method of its document. In MMailable, MakeRoomForMailer adjusts the document's window to make room for a mailer view. MakeRoomForMailer operates on the assumption that the main content view, containing all other views, is a scroller with ID 'SCLR'.

If you have a simple document view in which one scroller view serves as the superview, set the view ID of the scroller to 'SCLR'. The default version of MakeRoomForMailer will then resize the mailer view appropriately as the document view changes size. The TTEDocument class creates a view that matches this format, but if your document view hierarchy contains views that are outside the scroller view, you must override MakeRoomForMailer to adjust for your hierarchy.

If Necessary, Override TLetter::OpenLetter

When a user opens an existing document with a mailer, the TMailingApplication::OpenOld method calls the application object's DoMakeDocument method to create a document. OpenOld calls various methods of the document to read data and create views, then calls the document's OpenLetter method.

The document's OpenLetter method is defined in the MMailable mixin class. It calls the OpenLetter method of its fLetter field. The TLetter::OpenLetter method contains this code:

Boolean readContent;
readContent = fMailDoc->ReadNativeMailContent();
if (!readContent)
   readContent = fMailDoc->ReadSnapshotMailContent();
if (!readContent)
   fMailDoc->ReadStandardMailContent();
This code first tries to read content in native mail format. If it is unsuccessful, it tries to read snapshot format. If it is still unsuccessful, it finally tries to read standard mail format. If your application needs to change this ordering, you can override the OpenLetter method in either your mailable document class or in a subclass of TLetter (or of TFileBasedLetter).


Previous Book Contents Book Index Next

© Apple Computer, Inc.
25 JUL 1996