MacTech Network:   MacTech Forums  |  MacForge.net  |  Computer Memory  |  Register Domains  |  Cables  |  iPod Deals  |  Mac Deals  |  Mac Book Shelf


  MacTech Magazine

The journal of Macintosh technology

 
 

Magazine In Print
  About MacTech  
  Home Page  
  Subscribe  
  Archives DVD  
  Submit News  
  MacTech Forums  
  Get a copy of MacTech RISK FREE  
Google
Entire Web
mactech.com
Mac Community
More...
MacTech Central
  by Category  
  by Company  
  by Product  
MacTech News
  MacTech News  
  Previous News  
  MacTech RSS  
Article Archives
  Show Indices  
  by Volume  
  by Author  
  Source Code FTP  
Inside MacTech
  Writer's Kit  
  Editorial Staff  
  Editorial Calendar  
  Back Issues  
  Advertising  
Contact Us
  Customer Service  
  MacTech Store  
  Legal/Disclaimers  
  Webmaster Feedback  

Demonstration Program MTLETextEditor

Goto Contents

// *******************************************************************************************
// MTLETextEditor.h                                                        CLASSIC EVENT MODEL
// *******************************************************************************************
//
// This program demonstrates the use of the Multilingual Text Engine API to create a basic
// multi-styled text editor.  New documents created by the program are created and saved as
// Textension ('txtn') documents.  Existing 'TEXT' documents and Unicode ('utxt') documents
// are saved in the original format.  In the case of 'TEXT' documents, style information is
// saved in a 'styl' resource. 
//
// The program utilises the following resources:
//
// o  A 'plst' resource.
//
// o  An 'MBAR' resource, and 'MENU' resources for Apple, File, Edit, Size, Style, Colour,
//    and Justification (preload, non-purgeable).  
//
// o  A 'WIND' resource (purgeable) (initially not visible).  
//
// o  A 'STR ' resource (purgeable) containing the "missing application name" string, which is
//    copied to all document files created by the program.
//
// o  'STR#' resources (purgeable) containing error strings, the application's name (for
//    certain Navigation Services functions), and strings for the Edit menu Undo and Redo
//    items.
//
// o  A 'kind' resource (purgeable) describing file types, which is used by Navigation 
//    Services to  build the native file types section of the Show pop-up menu in the Open
//    dialog box.
//
// o  An 'open' resource (purgeable) containing the file type list for the Open dialog box.
//
// o  The 'BNDL' resource (non-purgeable), 'FREF' resources (non-purgeable), signature
//    resource (non-purgeable), and icon family resources (purgeable), required to support the
//    built application.
//
// o  A 'SIZE' resource with the acceptSuspendResumeEvents, canBackground, 
//    doesActivateOnFGSwitch, and isHighLevelEventAware flags set.
//
// *******************************************************************************************

// .................................................................................. includes

#include <Carbon.h>

// ................................................................................... defines

#define rMenubar                128
#define mAppleApplication       128
#define  iAbout                 1
#define mFile                   129
#define  iNew                   1
#define  iOpen                  2
#define  iClose                 4
#define  iSave                  5
#define  iSaveAs                6
#define  iRevert                7
#define  iPageSetup             9
#define  iPrint                 10
#define  iQuit                  12
#define mEdit                   130
#define  iUndo                  1
#define  iRedo                  2
#define  iCut                   4
#define  iCopy                  5
#define  iPaste                 6
#define  iClear                 7
#define  iSelectAll             8
#define mFont                   131
#define mSize                   132
#define  iTwelve                4
#define mStyle                  133
#define  iPlain                 1
#define  iBold                  3
#define  iUnderline             5
#define mColour                 134
#define  iBlack                 4
#define  iColourPicker          6
#define mJustification          135
#define  iDefault               1
#define  iLeft                  2
#define  iForceFull             6
#define mWindow                 136
#define mFirstHierarchical      160

#define rNewWindow              128
#define rAboutDialog            128
#define rErrorStrings           128
#define  eInstallHandler        1000
#define  eMaxWindows            1001
#define  eCantFindFinderProcess 1002
#define rMiscellaneousStrings   129
#define  sApplicationName       1
#define rOpenResource           128

#define kMaxWindows             8
#define kOpen                   0
#define kPrint                  1
#define kFileCreator            'bbJk'
#define MAX_UINT32              0xFFFFFFFF
#define MIN(a,b)                ((a) < (b) ? (a) : (b))
#define topLeft(r)              (((Point *) &(r))[0])

#define kATSUCGContextTag       32767L

// ....................................................................... function prototypes

void      main                         (void);
void      doPreliminaries              (void);
void      doInitialiseMTLE             (void);
void      doInstallAEHandlers          (void);
void      eventLoop                    (void);
UInt32    doGetSleepTime               (void);
void      doIdle                       (void);
void      doEvents                     (EventRecord *);
void      doMouseDown                  (EventRecord *);
void      doBringFinderToFront         (void);
OSStatus  doFindProcess                (OSType,OSType,ProcessSerialNumber *);
void      doActivate                   (EventRecord *);
void      doUpdate                     (EventRecord *);
Boolean   isApplicationWindow          (WindowRef,TXNObject *);
void      doAboutDialog                (void);
void      doSynchroniseFiles           (void);
OSStatus  openAppEventHandler          (AppleEvent *,AppleEvent *,SInt32);
OSStatus  reopenAppEventHandler        (AppleEvent *,AppleEvent *,SInt32);
OSStatus  openAndPrintDocsEventHandler (AppleEvent *,AppleEvent *,SInt32);
OSStatus  quitAppEventHandler          (AppleEvent *,AppleEvent *,SInt32);
OSStatus  doHasGotRequiredParams       (AppleEvent *);
void      doErrorAlert                 (SInt16);
void      doCopyPString                (Str255,Str255);
void      doConcatPStrings             (Str255,Str255);

void      doEnableDisableMenus         (Boolean);
void      doAdjustAndPrepareMenus      (void);
void      doAdjustFileMenu             (MenuRef,WindowRef);
void      doAdjustEditMenu             (MenuRef,WindowRef);
void      doPrepareFontMenu            (WindowRef);
void      doPrepareSizeMenu            (MenuRef,WindowRef);
void      doPrepareStyleMenu           (MenuRef,WindowRef);
void      doPrepareColourMenu          (MenuRef,WindowRef);
Boolean   isEqualRGB                   (RGBColor *,RGBColor *);
void      doPrepareJustificationMenu   (MenuRef,WindowRef);
void      doMenuChoice                 (SInt32);
void      doFileMenuChoice             (MenuItemIndex,WindowRef);
void      doEditMenuChoice             (MenuItemIndex,WindowRef);
void      doFontMenuChoice             (MenuID,MenuItemIndex,WindowRef);
void      doSizeMenuChoice             (MenuItemIndex,WindowRef);
void      doStyleMenuChoice            (MenuItemIndex,WindowRef);
void      doColourMenuChoice           (MenuItemIndex,WindowRef);
void      doJustificationMenuChoice    (MenuItemIndex,WindowRef);

OSStatus  doNewCommand                 (void);
OSStatus  doOpenCommand                (void);
OSStatus  doCloseCommand               (NavAskSaveChangesAction);
OSStatus  doSaveCommand                (void);
OSStatus  doSaveAsCommand              (void);
OSStatus  doRevertCommand              (void);
OSStatus  doQuitCommand                (NavAskSaveChangesAction);
OSStatus  doNewDocWindow               (WindowRef *,FSSpec *,TXNFileType);
OSStatus  doOpenFile                   (FSSpec,OSType);
void      doCloseWindow                (WindowRef,TXNObject);
OSStatus  doWriteFile                  (WindowRef,Boolean);
OSStatus  doCopyResources              (FSSpec,TXNFileType,Boolean);
OSStatus  doCopyAResource              (ResType,SInt16,SInt16,SInt16);
void      navEventFunction             (NavEventCallbackMessage,NavCBRecPtr,
                                        NavCallBackUserData);

// *******************************************************************************************
// MLTETextEditor.c
// *******************************************************************************************

// .................................................................................. includes

#include "MLTETextEditor.h"

// .......................................................................... global variables

SInt16            gAppResFileRefNum;
Boolean           gRunningOnX = false;
Boolean           gDone;
TXNFontMenuObject gTXNFontMenuObject;
RgnHandle         gCursorRgnHdl;
extern SInt16     gCurrentNumberOfWindows;

// ************************************************************************************** main

void  main(void)
{
  MenuBarHandle menubarHdl;
  SInt32        response;
  MenuRef       menuRef;
  OSStatus      osStatus = noErr;

  // ........................................................................ do preliminaries

  doPreliminaries();

  // .................................. save application's resource file file reference number

  gAppResFileRefNum = CurResFile();

  // ............................................................... set up menu bar and menus
  
  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    doErrorAlert(MemError());
  SetMenuBar(menubarHdl);

  CreateStandardWindowMenu(0,&menuRef);
  SetMenuID(menuRef,mWindow);
  InsertMenu(menuRef,0);
  DeleteMenuItem(menuRef,1);

  Gestalt(gestaltMenuMgrAttr,&response);
  if(response & gestaltMenuMgrAquaLayoutMask)
  {
    menuRef = GetMenuRef(mFile);
    if(menuRef != NULL)
    {
      DeleteMenuItem(menuRef,iQuit);
      DeleteMenuItem(menuRef,iQuit - 1);
    }

    gRunningOnX = true;
  }

  // .......................................... build hierarchical font menu and draw menu bar

  menuRef = GetMenuRef(mFont);
  osStatus = TXNNewFontMenuObject(menuRef,mFont,mFirstHierarchical,&gTXNFontMenuObject);
  if(osStatus != noErr)
    doErrorAlert(osStatus);

  DrawMenuBar();

  // ................................................... install required Apple event handlers

  doInstallAEHandlers();

  // ........................................................................ enter event loop

  eventLoop();
}

// *************************************************************************** doPreliminaries

void  doPreliminaries(void)
{
  MoreMasterPointers(960);
  InitCursor();
  FlushEvents(everyEvent,0);

  doInitialiseMTLE();
}

// ************************************************************************** doInitializeMTLE

void  doInitialiseMTLE(void)
{
  TXNMacOSPreferredFontDescription  defaultFont[1];
  OSStatus                          osStatus = noErr;
  SInt16                            fontID;

  GetFNum("\pNew York",&fontID);

  defaultFont[0].fontID    = fontID;  
  defaultFont[0].pointSize = 0x000C0000;
  defaultFont[0].fontStyle = kTXNDefaultFontStyle;
  defaultFont[0].encoding  = kTXNSystemDefaultEncoding;

  osStatus = TXNInitTextension(&defaultFont[0],1,kTXNWantMoviesMask);
  if(osStatus != noErr)
    doErrorAlert(osStatus);
}

// *********************************************************************** doInstallAEHandlers

void  doInstallAEHandlers(void)
{
  OSStatus osStatus = noErr;

  osStatus = AEInstallEventHandler(kCoreEventClass,kAEOpenApplication,
                          NewAEEventHandlerUPP((AEEventHandlerProcPtr) openAppEventHandler),
                          0L,false);
  if(osStatus != noErr)  doErrorAlert(eInstallHandler);

  osStatus = AEInstallEventHandler(kCoreEventClass,kAEReopenApplication,
                          NewAEEventHandlerUPP((AEEventHandlerProcPtr) reopenAppEventHandler),
                          0L,false);
  if(osStatus != noErr)  doErrorAlert(eInstallHandler);

  osStatus = AEInstallEventHandler(kCoreEventClass,kAEOpenDocuments,
                  NewAEEventHandlerUPP((AEEventHandlerProcPtr) openAndPrintDocsEventHandler),
                          kOpen,false);
  if(osStatus != noErr)  doErrorAlert(eInstallHandler);

  osStatus = AEInstallEventHandler(kCoreEventClass,kAEPrintDocuments,
                  NewAEEventHandlerUPP((AEEventHandlerProcPtr) openAndPrintDocsEventHandler),
                          kPrint,false);
  if(osStatus != noErr)  doErrorAlert(eInstallHandler);

  osStatus = AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,
                          NewAEEventHandlerUPP((AEEventHandlerProcPtr) quitAppEventHandler),
                          0L,false);
  if(osStatus != noErr)  doErrorAlert(eInstallHandler);
}

// ********************************************************************************* eventLoop

void  eventLoop(void)
{
  EventRecord eventStructure;

  gDone = false;
  gCursorRgnHdl = NewRgn();

  while(!gDone)
  {
    if(WaitNextEvent(everyEvent,&eventStructure,doGetSleepTime(),gCursorRgnHdl))
      doEvents(&eventStructure);
    else
    {
      if(eventStructure.what == nullEvent)
      {
        doIdle();
        doSynchroniseFiles();
      }
    }
  }
}

// **************************************************************************** doGetSleepTime

UInt32  doGetSleepTime(void)
{
  WindowRef windowRef;
  UInt32    sleepTime;
  TXNObject txnObject = NULL;

  windowRef = FrontWindow();

  if(isApplicationWindow(windowRef,&txnObject))
    sleepTime = TXNGetSleepTicks(txnObject);
  else
    sleepTime = GetCaretTime();

  return sleepTime;
}

// ************************************************************************************ doIdle

void  doIdle(void)
{
  WindowRef windowRef;
  TXNObject txnObject = NULL;

  windowRef = FrontWindow();
  if(isApplicationWindow(windowRef,&txnObject))
  {
    if(TXNGetChangeCount(txnObject))
      SetWindowModified(windowRef,true);
  }
}

// ********************************************************************************** doEvents

void  doEvents(EventRecord *eventStrucPtr)
{
  WindowRef windowRef;
  TXNObject txnObject = NULL;

  switch(eventStrucPtr->what)
  {
    case kHighLevelEvent:
      AEProcessAppleEvent(eventStrucPtr);
      break;

    case mouseDown:
      doMouseDown(eventStrucPtr);
      break;
  
    case keyDown:
      if(eventStrucPtr->modifiers & cmdKey)
      {
        doAdjustAndPrepareMenus();
        doMenuChoice(MenuEvent(eventStrucPtr));
      }
      break;

    case updateEvt:
      doUpdate(eventStrucPtr);
      break;

    case activateEvt:
      doActivate(eventStrucPtr);
      break;

    case osEvt:
      switch((eventStrucPtr->message >> 24) & 0x000000FF)
      {
        case suspendResumeMessage:
          if(eventStrucPtr->message & resumeFlag)
            SetThemeCursor(kThemeArrowCursor);
          break;
          
        case mouseMovedMessage:
          windowRef = FrontWindow();
          if(isApplicationWindow(windowRef,&txnObject))
            TXNAdjustCursor(txnObject,gCursorRgnHdl);
      }
      break;
  }
}

// ******************************************************************************* doMouseDown

void  doMouseDown(EventRecord *eventStrucPtr)
{
  WindowRef      windowRef;
  WindowPartCode partCode;
  OSStatus       osStatus  = noErr;
  TXNObject      txnObject = NULL;
  Boolean        handled    = false;
  SInt32         itemSelected;

  partCode = FindWindow(eventStrucPtr->where,&windowRef);

  switch(partCode)
  {
    case inMenuBar:
      doAdjustAndPrepareMenus();
      doMenuChoice(MenuSelect(eventStrucPtr->where));
      break;

    case inContent:
      if(windowRef != FrontWindow())
        SelectWindow(windowRef);
      else
      {
        if(isApplicationWindow(windowRef,&txnObject))
          TXNClick(txnObject,eventStrucPtr);
      }  
      break;

    case inGoAway:
      if(TrackGoAway(windowRef,eventStrucPtr->where))
        doCloseCommand(kNavSaveChangesClosingDocument);
      break;

    case inProxyIcon:
      osStatus = TrackWindowProxyDrag(windowRef,eventStrucPtr->where);
      if(osStatus == errUserWantsToDragWindow)
        handled = false;
      else if(osStatus == noErr)
        handled = true;

    case inDrag:
      if(!handled)
      {
        if(IsWindowPathSelectClick(windowRef,eventStrucPtr))
        {
          if(WindowPathSelect(windowRef,NULL,&itemSelected) == noErr)
          {
            if(LoWord(itemSelected) > 1)
              doBringFinderToFront();
          }

          handled = true;
        }
      }
      if(!handled)
        DragWindow(windowRef,eventStrucPtr->where,NULL);

      if(isApplicationWindow(windowRef,&txnObject))
        TXNAdjustCursor(txnObject,gCursorRgnHdl);

      break;

    case inGrow:
      if(isApplicationWindow(windowRef,&txnObject))
      {
        TXNGrowWindow(txnObject,eventStrucPtr);
        TXNAdjustCursor(txnObject,gCursorRgnHdl);
      }
      break;

    case inZoomIn:
    case inZoomOut:
      if(TrackBox(windowRef,eventStrucPtr->where,partCode))
      {
        if(isApplicationWindow(windowRef,&txnObject))
        {
          TXNZoomWindow(txnObject,partCode);
          TXNAdjustCursor(txnObject,gCursorRgnHdl);
        }
      }
      break;
  }
}

// ********************************************************************** doBringFinderToFront

void  doBringFinderToFront(void)
{
  ProcessSerialNumber finderProcess;

  if(doFindProcess('MACS','FNDR',&finderProcess) == noErr)
    SetFrontProcess(&finderProcess);
  else
    doErrorAlert(eCantFindFinderProcess);
}

// ***************************************************************************** doFindProcess

OSStatus  doFindProcess(OSType creator,OSType type,ProcessSerialNumber *outProcSerNo)
{  
  ProcessSerialNumber procSerialNo;
  ProcessInfoRec      procInfoStruc;
  OSStatus            osStatus = noErr;

  procSerialNo.highLongOfPSN = 0;
  procSerialNo.lowLongOfPSN  = kNoProcess;

  procInfoStruc.processInfoLength = sizeof(ProcessInfoRec);
  procInfoStruc.processName       = NULL;
  procInfoStruc.processAppSpec    = NULL;
  procInfoStruc.processLocation   = NULL;

  while(true)
  {
    osStatus = GetNextProcess(&procSerialNo);
    if(osStatus != noErr)
      break;

    osStatus = GetProcessInformation(&procSerialNo,&procInfoStruc);
    if(osStatus != noErr)
      break;
    if((procInfoStruc.processSignature == creator) && (procInfoStruc.processType == type))
      break;
  }

  *outProcSerNo = procSerialNo;

  return osStatus;
}

// ******************************************************************************** doActivate

void  doActivate(EventRecord *eventStrucPtr)
{
  WindowRef  windowRef;
  TXNObject  txnObject = NULL;
  Boolean    becomingActive;
  TXNFrameID txnFrameID = 0;

  windowRef = (WindowRef) eventStrucPtr->message;

  if(isApplicationWindow(windowRef,&txnObject))
  {
    becomingActive = ((eventStrucPtr->modifiers & activeFlag) == activeFlag);
    GetWindowProperty(windowRef,kFileCreator,'tFRM',sizeof(TXNFrameID),NULL,&txnFrameID);
    
    if(becomingActive)
      TXNActivate(txnObject,txnFrameID,becomingActive);
    else
      TXNActivate(txnObject,txnFrameID,becomingActive);

    TXNFocus(txnObject,becomingActive);
  }
}

// ********************************************************************************** doUpdate

void  doUpdate(EventRecord *eventStrucPtr)
{
  WindowRef windowRef;
  GrafPtr   oldPort;
  TXNObject txnObject = NULL;

  windowRef = (WindowRef) eventStrucPtr->message;

  GetPort(&oldPort);
  SetPortWindowPort(windowRef);

  if(isApplicationWindow(windowRef,&txnObject))
    TXNUpdate(txnObject);

  SetPort(oldPort);
}

// *********************************************************************** isApplicationWindow

Boolean  isApplicationWindow(WindowRef windowRef,TXNObject *txnObject)
{
  OSStatus osStatus = noErr;

  osStatus = GetWindowProperty(windowRef,kFileCreator,'tOBJ',sizeof(TXNObject),NULL,
                               txnObject);

  return (windowRef != NULL) && (GetWindowKind(windowRef) == kApplicationWindowKind);
}

// ***************************************************************************** doAboutDialog

void  doAboutDialog(void)
{
  DialogRef dialogRef;
  SInt16    itemHit;

  dialogRef = GetNewDialog(rAboutDialog,NULL,(WindowRef) -1);
  ModalDialog(NULL,&itemHit);
  DisposeDialog(dialogRef);
}

// ************************************************************************ doSynchroniseFiles

void  doSynchroniseFiles(void)
{
  UInt32        currentTicks;
  WindowRef     windowRef;
  static UInt32 nextSynchTicks = 0;
  OSStatus      hasNoAliasHdl = noErr;
  Boolean       aliasChanged;
  AliasHandle   aliasHdl = NULL;
  FSSpec        newFSSpec;
  OSStatus      osStatus = noErr;
  SInt16        trashVRefNum;
  SInt32        trashDirID;
  TXNObject     txnObject = NULL;

  currentTicks = TickCount();
  windowRef    = FrontWindow();

  if(currentTicks > nextSynchTicks)
  {
    while(windowRef != NULL)
    {
      hasNoAliasHdl = GetWindowProperty(windowRef,kFileCreator,'tALH',sizeof(AliasHandle),
                                        NULL,&aliasHdl);
      if(hasNoAliasHdl)
        break;

      aliasChanged = false;
      ResolveAlias(NULL,aliasHdl,&newFSSpec,&aliasChanged);
      if(aliasChanged)
      {
        SetWindowProperty(windowRef,kFileCreator,'FiSp',sizeof(FSSpec),&newFSSpec);
        SetWTitle(windowRef,newFSSpec.name);
      }

      osStatus = FindFolder(kUserDomain,kTrashFolderType,kDontCreateFolder,
                            &trashVRefNum,&trashDirID);

      if(osStatus == noErr)
      {
        do
        {
          if(newFSSpec.parID == fsRtParID)
            break;

          if((newFSSpec.vRefNum == trashVRefNum) && (newFSSpec.parID == trashDirID))
          {
            GetWindowProperty(windowRef,kFileCreator,'tOBJ',sizeof(TXNObject),NULL,
                              &txnObject);
            TXNDeleteObject(txnObject);
            DisposeWindow(windowRef);
            gCurrentNumberOfWindows --;
            break;
          }
        } while(FSMakeFSSpec(newFSSpec.vRefNum,newFSSpec.parID,"\p",&newFSSpec) == noErr);
      }

      windowRef = GetNextWindow(windowRef);
    }

    nextSynchTicks = currentTicks + 15;
  }
}

// *********************************************************************** openAppEventHandler

OSStatus  openAppEventHandler(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefCon)
{
  OSStatus osStatus = noErr;

  osStatus = doHasGotRequiredParams(appEvent);
  if(osStatus == noErr)
    osStatus = doNewCommand();

  return osStatus;
}

// ********************************************************************* reopenAppEventHandler

OSStatus  reopenAppEventHandler(AppleEvent *appEvent,AppleEvent *reply,
                                SInt32 handlerRefCon)
{
  OSStatus osStatus = noErr;

  osStatus = doHasGotRequiredParams(appEvent);
  if(osStatus == noErr)
    if(!FrontWindow())
      osStatus = doNewCommand();

  return osStatus;
}

// ************************************************************** openAndPrintDocsEventHandler

OSStatus  openAndPrintDocsEventHandler(AppleEvent *appEvent,AppleEvent *reply,
                                       SInt32 handlerRefcon)
{
  FSSpec     fileSpec;
  AEDescList docList;
  OSStatus   osStatus, ignoreErr;
  SInt32     index, numberOfItems;
  Size       actualSize;
  AEKeyword  keyWord;
  DescType   returnedType;
  FInfo      fileInfo;
  TXNObject  txnObject;

  osStatus = AEGetParamDesc(appEvent,keyDirectObject,typeAEList,&docList);

  if(osStatus == noErr)
  {
    osStatus = doHasGotRequiredParams(appEvent);
    if(osStatus == noErr)
    {
      osStatus = AECountItems(&docList,&numberOfItems);
      if(osStatus == noErr)
      {
        for(index=1;index<=numberOfItems;index++)
        {
          osStatus = AEGetNthPtr(&docList,index,typeFSS,&keyWord,&returnedType,
                                 &fileSpec,sizeof(fileSpec),&actualSize);
          if(osStatus == noErr)
          {
            osStatus = FSpGetFInfo(&fileSpec,&fileInfo);
            if(osStatus == noErr)
            {
              if(osStatus = doOpenFile(fileSpec,fileInfo.fdType))
                doErrorAlert(osStatus);
              
              if(osStatus == noErr && handlerRefcon == kPrint)
              {
                if(isApplicationWindow(FrontWindow(),&txnObject))
                {
                  if(osStatus = TXNPrint(txnObject))
                    doErrorAlert(osStatus);
                    
                  if(osStatus = doCloseCommand(kNavSaveChangesOther))
                    doErrorAlert(osStatus);
                }
              }
            }
          }
          else
            doErrorAlert(osStatus);
        }
      }
    }
    else
      doErrorAlert(osStatus);

    ignoreErr = AEDisposeDesc(&docList);
  }
  else
    doErrorAlert(osStatus);

  return osStatus;
}

// *********************************************************************** quitAppEventHandler

OSStatus  quitAppEventHandler(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon)
{
  OSStatus osStatus = noErr;

  osStatus = doHasGotRequiredParams(appEvent);
  if(osStatus == noErr)
  {
    while(FrontWindow())
    {
      osStatus = doCloseCommand(kNavSaveChangesQuittingApplication);

      if(osStatus != noErr && osStatus != kNavAskSaveChangesCancel)
        doErrorAlert(osStatus);
      if(osStatus == kNavAskSaveChangesCancel)
        return noErr;
    }
  }

  gDone = true;

  return osStatus;
}

// ******************************************************************** doHasGotRequiredParams

OSStatus  doHasGotRequiredParams(AppleEvent *appEvent)
{
  DescType returnedType;
  Size     actualSize;
  OSStatus osStatus = noErr;

  osStatus = AEGetAttributePtr(appEvent,keyMissedKeywordAttr,typeWildCard,&returnedType,
                               NULL,0,&actualSize);
  if(osStatus == errAEDescNotFound)
    osStatus = noErr;
  else if(osStatus == noErr)
    osStatus = errAEParamMissed;

  return osStatus;
}

// ****************************************************************************** doErrorAlert

void  doErrorAlert(SInt16 errorCode)
{
  Str255 errorString, theString;
  SInt16 itemHit;

  if(errorCode == kATSUFontsMatched)
    return;

  if(errorCode == eInstallHandler)
    GetIndString(errorString,rErrorStrings,1);
  else if(errorCode == eMaxWindows)
    GetIndString(errorString,rErrorStrings,2);
  else if(errorCode == eCantFindFinderProcess)
    GetIndString(errorString,rErrorStrings,3);
  else
  {
    GetIndString(errorString,rErrorStrings,4);
    NumToString((SInt32) errorCode,theString);
    doConcatPStrings(errorString,theString);
  }

  if(errorCode != memFullErr)
    StandardAlert(kAlertCautionAlert,errorString,NULL,NULL,&itemHit);
  else
  {
    StandardAlert(kAlertStopAlert,errorString,NULL,NULL,&itemHit);
    ExitToShell();
  }
}

// ***************************************************************************** doCopyPString

void  doCopyPString(Str255 sourceString,Str255 destinationString)
{
  SInt16 stringLength;

  stringLength = sourceString[0];
  BlockMove(sourceString + 1,destinationString + 1,stringLength);
  destinationString[0] = stringLength;
}

// ************************************************************************** doConcatPStrings

void  doConcatPStrings(Str255 targetString,Str255 appendString)
{
  SInt16 appendLength;

  appendLength = MIN(appendString[0],255 - targetString[0]);

  if(appendLength > 0)
  {
    BlockMoveData(appendString+1,targetString+targetString[0]+1,(SInt32) appendLength);
    targetString[0] += appendLength;
  }
}

// *******************************************************************************************
// MLTEMenus.c
// *******************************************************************************************

// .................................................................................. includes

#include "MLTETextEditor.h"

// .......................................................................... global variables

RGBColor                 gCurrentColourPickerColour = { 0x0000,0x0000,0x0000 };
extern SInt16            gCurrentNumberOfWindows;
extern TXNFontMenuObject gTXNFontMenuObject;
extern Boolean           gDone;

// ********************************************************************** doEnableDisableMenus

void  doEnableDisableMenus(Boolean enableMenus)
{
  if(enableMenus)
  {
    EnableMenuItem(GetMenuRef(mEdit),0);
    EnableMenuItem(GetMenuRef(mFont),0);
    EnableMenuItem(GetMenuRef(mSize),0);
    EnableMenuItem(GetMenuRef(mStyle),0);
    EnableMenuItem(GetMenuRef(mColour),0);
    EnableMenuItem(GetMenuRef(mJustification),0);
    EnableMenuItem(GetMenuRef(mWindow),0);
  }
  else
  {
    DisableMenuItem(GetMenuRef(mEdit),0);
    DisableMenuItem(GetMenuRef(mFont),0);
    DisableMenuItem(GetMenuRef(mSize),0);
    DisableMenuItem(GetMenuRef(mStyle),0);
    DisableMenuItem(GetMenuRef(mColour),0);
    DisableMenuItem(GetMenuRef(mJustification),0);
    DisableMenuItem(GetMenuRef(mWindow),0);
  }
}

// ******************************************************************* doAdjustAndPrepareMenus

void  doAdjustAndPrepareMenus(void)
{
  WindowRef windowRef;
  
  windowRef = FrontWindow();

  doAdjustFileMenu(GetMenuRef(mFile),windowRef);
  doAdjustEditMenu(GetMenuRef(mEdit),windowRef);
  doPrepareFontMenu(windowRef);
  doPrepareSizeMenu(GetMenuRef(mSize),windowRef);
  doPrepareStyleMenu(GetMenuRef(mStyle),windowRef);
  doPrepareColourMenu(GetMenuRef(mColour),windowRef);
  doPrepareJustificationMenu(GetMenuRef(mJustification),windowRef);

  DrawMenuBar();
}

// ************************************************************************** doAdjustFileMenu

void  doAdjustFileMenu(MenuRef menuRef,WindowRef windowRef)
{
  TXNObject txnObject = NULL;

  if(gCurrentNumberOfWindows <= kMaxWindows)
  {
    EnableMenuItem(menuRef,iNew);
    EnableMenuItem(menuRef,iOpen);
  }
  else
  {
    DisableMenuItem(menuRef,iNew);
    DisableMenuItem(menuRef,iOpen);
  }

  if(isApplicationWindow(windowRef,&txnObject)) 
  {
    EnableMenuItem(menuRef,iClose);

    if(TXNGetChangeCount(txnObject))
    {
      EnableMenuItem(menuRef,iSave);
      EnableMenuItem(menuRef,iRevert);
    }
    else
    {
      DisableMenuItem(menuRef,iSave);
      DisableMenuItem(menuRef,iRevert);
    }

    if(TXNDataSize(txnObject))
    {
      EnableMenuItem(menuRef,iSaveAs);
      EnableMenuItem(menuRef,iPageSetup);
      EnableMenuItem(menuRef,iPrint);  
    }
    else
    {
      DisableMenuItem(menuRef,iSaveAs);
      DisableMenuItem(menuRef,iPageSetup);
      DisableMenuItem(menuRef,iPrint);  
    }
  }
  else
  {
    DisableMenuItem(menuRef,iClose);
    DisableMenuItem(menuRef,iSave);
    DisableMenuItem(menuRef,iSaveAs);
    DisableMenuItem(menuRef,iRevert);
    DisableMenuItem(menuRef,iPageSetup);
    DisableMenuItem(menuRef,iPrint);
  }
}

// ************************************************************************** doAdjustEditMenu

void  doAdjustEditMenu(MenuRef menuRef,WindowRef windowRef)
{
  TXNObject    txnObject = NULL;
  SInt16       menuItem;
  Str255       itemText;
  TXNActionKey actionKey;

  if(isApplicationWindow(windowRef,&txnObject)) 
  {
    // ..................................................................... disable all items

    for(menuItem = iUndo;menuItem <= iSelectAll;menuItem ++)
      DisableMenuItem(menuRef,menuItem); 

    // ........................................ undo and redo default - can't undo, can't redo

    GetIndString(itemText,130,1);
    SetMenuItemText(menuRef,iUndo,itemText);  
    GetIndString(itemText,130,2);
    SetMenuItemText(menuRef,iRedo,itemText);

    // ....................................... if undoable, enable undo item and set item text

    if(TXNCanUndo(txnObject,&actionKey))
    {
      EnableMenuItem(menuRef,iUndo);
      
      if((actionKey < kTXNTypingAction) || (actionKey > kTXNMoveAction))
        actionKey = -1;
      
      GetIndString(itemText,130,2 * actionKey + 5);
        SetMenuItemText(menuRef,iUndo,itemText);
    }

    // ....................................... if redoable, enable redo item and set item text

    if(TXNCanRedo(txnObject,&actionKey))
    {
      EnableMenuItem(menuRef,iRedo);
      
      if((actionKey < kTXNTypingAction) || (actionKey > kTXNMoveAction))
        actionKey = -1;
      
      GetIndString(itemText,130,2 * actionKey + 6);
      SetMenuItemText(menuRef,iRedo,itemText);
    }

    // .................................. if there is a selection, enable cut, copy, and clear

    if(!TXNIsSelectionEmpty(txnObject))
    {
      EnableMenuItem(menuRef,iCut);
      EnableMenuItem(menuRef,iCopy);
      EnableMenuItem(menuRef,iClear);
    }

    // .................................................... if scrap is pastable, enable paste

    if(TXNIsScrapPastable())
      EnableMenuItem(menuRef,iPaste);

    // ..................................... if any characters in TXNObject, enable select all

    if(TXNDataSize(txnObject))
      EnableMenuItem(menuRef,iSelectAll);
  }
}

// ************************************************************************* doPrepareFontMenu

void  doPrepareFontMenu(WindowRef windowRef)
{
  TXNObject  txnObject = NULL;

  if(isApplicationWindow(windowRef,&txnObject)) 
    TXNPrepareFontMenu(txnObject,gTXNFontMenuObject);
}

// ************************************************************************* doPrepareSizeMenu

void  doPrepareSizeMenu(MenuRef menuRef,WindowRef windowRef)
{
  TXNObject          txnObject = NULL;
  static Fixed       itemSizes[8] = { 0x00090000,0x000A0000,0x000B0000,0x000C0000,
                                      0x000E0000,0x00120000,0x00180000,0x00240000 };
  TXNContinuousFlags txnContinuousFlags = 0;
  TXNTypeAttributes  txnTypeAttributes;
  OSStatus           osStatus = noErr;
  SInt16             menuItem;

  if(isApplicationWindow(windowRef,&txnObject)) 
  {
    txnTypeAttributes.tag            = kTXNQDFontSizeAttribute;
    txnTypeAttributes.size           = kTXNFontSizeAttributeSize;
    txnTypeAttributes.data.dataValue = 0;

    osStatus = TXNGetContinuousTypeAttributes(txnObject,&txnContinuousFlags,1,
                                              &txnTypeAttributes);
    if(osStatus == noErr)
    {
      for(menuItem = 1;menuItem < 8;menuItem ++)
      {
        CheckMenuItem(menuRef,menuItem,(txnContinuousFlags & kTXNSizeContinuousMask) &&
                      (txnTypeAttributes.data.dataValue == itemSizes[menuItem - 1]));
      }
    }
  }
}

// ************************************************************************ doPrepareStyleMenu

void  doPrepareStyleMenu(MenuRef menuRef,WindowRef windowRef)
{
  TXNObject          txnObject = NULL;
  TXNContinuousFlags txnContinuousFlags = 0;
  TXNTypeAttributes  txnTypeAttributes;
  OSStatus           osStatus = noErr;
  SInt16             menuItem;

  if(isApplicationWindow(windowRef,&txnObject)) 
  {
    txnTypeAttributes.tag            = kTXNQDFontStyleAttribute;
    txnTypeAttributes.size           = kTXNQDFontStyleAttributeSize;
    txnTypeAttributes.data.dataValue = 0;

    osStatus = TXNGetContinuousTypeAttributes(txnObject,&txnContinuousFlags,1,
                                              &txnTypeAttributes);
    if(osStatus == noErr)
    {
      CheckMenuItem(menuRef,iPlain,(txnContinuousFlags & kTXNStyleContinuousMask) &&
                    (txnTypeAttributes.data.dataValue == normal));

      for(menuItem = iBold;menuItem <= iUnderline;menuItem ++)
      {
        CheckMenuItem(menuRef,menuItem,(txnContinuousFlags & kTXNStyleContinuousMask) &&
                      (txnTypeAttributes.data.dataValue & (1 << (menuItem - iBold))));
      }
    }
  }
}

// *********************************************************************** doPrepareColourMenu

void  doPrepareColourMenu(MenuRef menuRef,WindowRef windowRef)
{
  TXNObject          txnObject = NULL;
  TXNContinuousFlags txnContinuousFlags = 0;
  TXNTypeAttributes  txnTypeAttributes;
  RGBColor           attributesColour;
  OSStatus           osStatus = noErr;
  SInt16             menuItem;
  RGBColor           itemColours[4] = { { 0xFFFF,0x0000,0x0000 },{ 0x0000,0x8888,0x0000 },
                                        { 0x0000,0x0000,0xFFFF },{ 0x0000,0x0000,0x0000 } };

  if(isApplicationWindow(windowRef,&txnObject)) 
  {
    txnTypeAttributes.tag          = kTXNQDFontColorAttribute;
    txnTypeAttributes.size         = kTXNQDFontColorAttributeSize;
    txnTypeAttributes.data.dataPtr = &attributesColour;

    osStatus = TXNGetContinuousTypeAttributes(txnObject,&txnContinuousFlags,1,
                                              &txnTypeAttributes);
    if(osStatus == noErr)
    {
      for(menuItem = 1;menuItem < 5;menuItem ++)
      {
        CheckMenuItem(menuRef,menuItem,(txnContinuousFlags & kTXNColorContinuousMask) &&
                      (isEqualRGB(&attributesColour,&itemColours[menuItem - 1])));
      }
    }
  }
}

// ******************************************************************************** isEqualRGB

Boolean  isEqualRGB(RGBColor *attributesColour,RGBColor *itemColour)
{
  return (attributesColour->red   == itemColour->red && 
          attributesColour->green == itemColour->green &&
          attributesColour->blue  == itemColour->blue);
}

// **************************************************************** doPrepareJustificationMenu

void  doPrepareJustificationMenu (MenuRef menuRef,WindowRef windowRef)
{
  TXNObject      txnObject = NULL;
  static UInt32  itemJustifications[6] = { kTXNFlushDefault,kTXNFlushLeft,kTXNFlushRight,
                                           kTXNCenter,kTXNFullJust,kTXNForceFullJust};
  TXNControlTag  txnControlTag[1];
  TXNControlData txnControlData[1];
  SInt16         menuItem;
  OSStatus       osStatus = noErr;

  if(isApplicationWindow(windowRef,&txnObject)) 
  {
    txnControlTag[0]         = kTXNJustificationTag ;
    txnControlData[0].uValue = 0;

    osStatus = TXNGetTXNObjectControls(txnObject,1,txnControlTag,txnControlData);

    if(osStatus == noErr)
    {
      for(menuItem = iDefault;menuItem <= iForceFull;menuItem ++ )
        CheckMenuItem(menuRef,menuItem,(txnControlData[0].uValue == 
                      itemJustifications[menuItem - 1]));
    }
  }
}

// ****************************************************************************** doMenuChoice

void  doMenuChoice(SInt32 menuChoice)
{
  MenuID        menuID;
  MenuItemIndex menuItem;
  OSStatus      osStatus = noErr;
  WindowRef     windowRef;
  TXNObject     txnObject = NULL;

  windowRef = FrontWindow();

  menuID = HiWord(menuChoice);
  menuItem = LoWord(menuChoice);

  if(menuID == 0)
    return;

  switch(menuID)
  {
    case mAppleApplication:
      if(menuItem == iAbout)
        doAboutDialog();
      break;
      
    case mFile:
      doFileMenuChoice(menuItem,windowRef);
      break;

    case mEdit:
      doEditMenuChoice(menuItem,windowRef);
      break;

    case mFont:
      doFontMenuChoice(menuID,menuItem,windowRef);      
      break;

    case mSize:
      doSizeMenuChoice(menuItem,windowRef);
      break;

    case mStyle:
      doStyleMenuChoice(menuItem,windowRef);
      break;

    case mColour:
      doColourMenuChoice(menuItem,windowRef);
      break;

    case mJustification:
      doJustificationMenuChoice(menuItem,windowRef);
      break;

    default:
      if(menuID >= mFirstHierarchical)
        doFontMenuChoice(menuID,menuItem,windowRef);
      break;
  }

  HiliteMenu(0);
}

// ************************************************************************** doFileMenuChoice

void  doFileMenuChoice(MenuItemIndex menuItem,WindowRef windowRef)
{
  TXNObject txnObject = NULL;
  OSStatus  osStatus = noErr;

  switch(menuItem)
  {
    case iNew:
      if(osStatus = doNewCommand())
        doErrorAlert(osStatus);
      break;

    case iOpen:
      if(osStatus = doOpenCommand())
        doErrorAlert(osStatus);
      break;

    case iClose:
      if((osStatus = doCloseCommand(kNavSaveChangesClosingDocument)) && 
          osStatus != kNavAskSaveChangesCancel)
        doErrorAlert(osStatus);
      break;

    case iSave:
      if(osStatus = doSaveCommand())
        doErrorAlert(osStatus);
      break;

    case iSaveAs:
      if(osStatus = doSaveAsCommand())
        doErrorAlert(osStatus);
      break;

    case iRevert:
      if(osStatus = doRevertCommand())
        doErrorAlert(osStatus);
      break;

    case iPageSetup:
      if(isApplicationWindow(windowRef,&txnObject))
      {
        osStatus = TXNPageSetup(txnObject);
        if(osStatus != userCanceledErr && osStatus != noErr)
          doErrorAlert(osStatus);
      }
      break;

    case iPrint:
      if(isApplicationWindow(windowRef,&txnObject))
      {
        osStatus = TXNPrint(txnObject);
        if(osStatus != userCanceledErr && osStatus != noErr)
          doErrorAlert(osStatus);
      }
      break;

    case iQuit:
      if((osStatus = doQuitCommand(kNavSaveChangesQuittingApplication)) && 
          osStatus != kNavAskSaveChangesCancel)
        doErrorAlert(osStatus);

      if(osStatus != kNavAskSaveChangesCancel)
      {
        if(gTXNFontMenuObject != NULL)
        {
          if(osStatus = TXNDisposeFontMenuObject(gTXNFontMenuObject))
            doErrorAlert(osStatus);
        }

        gTXNFontMenuObject = NULL;
      
        TXNTerminateTextension();
        gDone = true;
      }
      break;
  }
}

// ************************************************************************** doEditMenuChoice

void  doEditMenuChoice(MenuItemIndex menuItem,WindowRef windowRef)
{
  TXNObject txnObject = NULL;
  OSStatus  osStatus = noErr;

  if(isApplicationWindow(windowRef,&txnObject))
  {
    switch(menuItem)
    {
      case iUndo:
        TXNUndo(txnObject);
        break;

      case iRedo:
        TXNRedo(txnObject);
        break;

      case iCut:
        if((osStatus = TXNCut(txnObject)) == noErr)
          TXNConvertToPublicScrap();
        else
          doErrorAlert(osStatus);
        break;

      case iCopy:
        if((osStatus = TXNCopy(txnObject)) == noErr)
          TXNConvertToPublicScrap();
        else
          doErrorAlert(osStatus);
        break;

      case iPaste:
        if(osStatus = TXNPaste(txnObject))
          doErrorAlert(osStatus);
        break;

      case iClear:
        if(osStatus = TXNClear(txnObject))
          doErrorAlert(osStatus);
        break;

      case iSelectAll:
        TXNSelectAll(txnObject);
        break;
    }
  }
}

// ************************************************************************** doFontMenuChoice

void  doFontMenuChoice(MenuID menuID,MenuItemIndex menuItem,WindowRef windowRef)
{
  TXNObject txnObject = NULL;
  OSStatus  osStatus = noErr;

  if(isApplicationWindow(windowRef,&txnObject))
  {
    if(gTXNFontMenuObject != NULL)
    {
      if(osStatus = TXNDoFontMenuSelection(txnObject,gTXNFontMenuObject,menuID,menuItem))
        doErrorAlert(osStatus);
    }
  }
}

// ************************************************************************** doSizeMenuChoice

void  doSizeMenuChoice(MenuItemIndex menuItem,WindowRef windowRef)
{
  TXNObject         txnObject = NULL;
  static Fixed      itemSizes[8] = { 0x00090000,0x000A0000,0x000B0000,0x000C0000,
                                     0x000E0000,0x00120000,0x00180000,0x00240000 };
  Fixed             sizeToSet;
  TXNTypeAttributes txnTypeAttributes;
  OSStatus          osStatus = noErr;

  if(isApplicationWindow(windowRef,&txnObject))
  {
    sizeToSet = itemSizes[menuItem - 1];

    txnTypeAttributes.tag            = kTXNQDFontSizeAttribute;
    txnTypeAttributes.size           = kTXNFontSizeAttributeSize;
    txnTypeAttributes.data.dataValue = sizeToSet;

    if(TXNSetTypeAttributes(txnObject,1,&txnTypeAttributes,kTXNUseCurrentSelection,
                            kTXNUseCurrentSelection))
      doErrorAlert(osStatus);
  }
}

// ************************************************************************* doStyleMenuChoice

void  doStyleMenuChoice(MenuItemIndex menuItem,WindowRef windowRef)
{
  TXNObject         txnObject = NULL;
  static Style      itemStyles[5] = { normal,0,bold,italic,underline };
  Style             styleToSet;
  TXNTypeAttributes txnTypeAttributes;
  OSStatus          osStatus = noErr;
  
  if(isApplicationWindow(windowRef,&txnObject))
  {
    styleToSet = itemStyles[menuItem - 1];

    txnTypeAttributes.tag            = kTXNQDFontStyleAttribute;
    txnTypeAttributes.size           = kTXNQDFontStyleAttributeSize;
    txnTypeAttributes.data.dataValue = styleToSet;
        
    if(TXNSetTypeAttributes(txnObject,1,&txnTypeAttributes,kTXNUseCurrentSelection,
                            kTXNUseCurrentSelection))
      doErrorAlert(osStatus);
  }
}

// ************************************************************************ doColourMenuChoice

void  doColourMenuChoice(MenuItemIndex menuItem,WindowRef windowRef)
{
  TXNObject         txnObject = NULL;
  Point             where;
  Boolean           colorPickerButton;
  Str255            prompt = "\pPick a text colour";
  RGBColor          colourToSet;
  RGBColor          itemColours[4] = { { 0xFFFF,0x0000,0x0000 },{ 0x0000,0x8888,0x0000 },
                                       { 0x0000,0x0000,0xFFFF },{ 0x0000,0x0000,0x0000 } };
  TXNTypeAttributes txnTypeAttributes;
  OSStatus          osStatus = noErr;

  if(isApplicationWindow(windowRef,&txnObject))
  {
    if(menuItem == iColourPicker)
    {
      where.v = where.h = 0;
      colorPickerButton = GetColor(where,prompt,&gCurrentColourPickerColour,&colourToSet);
      if(colorPickerButton)
        gCurrentColourPickerColour = colourToSet;
      else
        return;
    }
    else
      colourToSet = itemColours[menuItem - 1];

    txnTypeAttributes.tag          = kTXNQDFontColorAttribute;
    txnTypeAttributes.size         = kTXNQDFontColorAttributeSize;
    txnTypeAttributes.data.dataPtr = &colourToSet;

    if(TXNSetTypeAttributes(txnObject,1,&txnTypeAttributes,kTXNUseCurrentSelection,
                            kTXNUseCurrentSelection))
      doErrorAlert(osStatus);
  }
}

// ***************************************************************** doJustificationMenuChoice

void  doJustificationMenuChoice(MenuItemIndex menuItem,WindowRef windowRef)
{
  TXNObject      txnObject = NULL;
  static UInt32  itemJustifications[6] = { kTXNFlushDefault,kTXNFlushLeft,kTXNFlushRight,
                                           kTXNCenter,kTXNFullJust,kTXNForceFullJust };
  OSStatus       osStatus = noErr;
  UInt32         justificationToSet;
  TXNControlTag  txnControlTag[1];
  TXNControlData txnControlData[1];
  
  if(isApplicationWindow(windowRef,&txnObject)) 
  {
    justificationToSet = itemJustifications[menuItem - 1];

    txnControlTag[0] = kTXNJustificationTag;

    osStatus = TXNGetTXNObjectControls(txnObject,1,txnControlTag,txnControlData);

    if(txnControlData[0].uValue != justificationToSet)
    {
      txnControlData[0].uValue = justificationToSet;
      osStatus = TXNSetTXNObjectControls(txnObject,false,1,txnControlTag,txnControlData);
      if(osStatus != noErr)
        doErrorAlert(osStatus);
    }
  }
}

// *******************************************************************************************
// MLTENewOpenCloseSave.c
// *******************************************************************************************

// .................................................................................. includes

#include "MLTETextEditor.h"

// .......................................................................... global variables

SInt16 gCurrentNumberOfWindows = 0;
SInt16 gUntitledWindowNumber = 0;

extern Boolean gRunningOnX = false;
extern SInt16  gAppResFileRefNum;

// ****************************************************************************** doNewCommand

OSStatus  doNewCommand(void)
{
  OSStatus  osStatus = noErr;
  WindowRef windowRef;

  if(gCurrentNumberOfWindows == kMaxWindows)
    return eMaxWindows;

  osStatus = doNewDocWindow(&windowRef,NULL,kTXNTextensionFile);

  if(osStatus == noErr)
    SetWindowProxyCreatorAndType(windowRef,kFileCreator,kTXNTextensionFile,kUserDomain);

  return osStatus;
}

// ***************************************************************************** doOpenCommand

OSStatus doOpenCommand(void)
{
  OSStatus          osStatus = noErr;
  NavDialogOptions  dialogOptions;
  NavTypeListHandle fileTypeListHdl = NULL;
  NavEventUPP       navEventFunctionUPP;
  NavReplyRecord    navReplyStruc;
  SInt32            count, index;
  AEKeyword         theKeyword;
  DescType          actualType;
  FSSpec            fileSpec;
  Size              actualSize;
  FInfo             fileInfo;
  OSType            fileType;

  osStatus = NavGetDefaultDialogOptions(&dialogOptions);

  if(osStatus == noErr)
  {
    GetIndString(dialogOptions.clientName,rMiscellaneousStrings,sApplicationName);
    fileTypeListHdl = (NavTypeListHandle) GetResource('open',rOpenResource);

    navEventFunctionUPP = NewNavEventUPP((NavEventProcPtr) navEventFunction);

    osStatus = NavGetFile(NULL,&navReplyStruc,&dialogOptions,navEventFunctionUPP,
                          NULL,NULL,fileTypeListHdl,NULL);

    DisposeNavEventUPP(navEventFunctionUPP);
      
    if(osStatus == noErr && navReplyStruc.validRecord)
    {
      osStatus = AECountItems(&(navReplyStruc.selection),&count);
      if(osStatus == noErr)
      {
        for(index=1;index<=count;index++)
        {
          osStatus = AEGetNthPtr(&(navReplyStruc.selection),index,typeFSS,&theKeyword,
                                 &actualType,&fileSpec,sizeof(fileSpec),&actualSize);

          if((osStatus = FSpGetFInfo(&fileSpec,&fileInfo)) == noErr)
          {
            fileType = fileInfo.fdType;
            osStatus = doOpenFile(fileSpec,fileType);
          }
        }
      }

      osStatus = NavDisposeReply(&navReplyStruc);
    }

    if(fileTypeListHdl != NULL)
      ReleaseResource((Handle) fileTypeListHdl);
  }

  if(osStatus == userCanceledErr)
    osStatus = noErr;
  
  return osStatus;
}

// **************************************************************************** doCloseCommand

OSStatus  doCloseCommand(NavAskSaveChangesAction action)
{
  WindowRef               windowRef;
  TXNObject               txnObject = NULL;
  OSStatus                osStatus = noErr;
  NavDialogOptions        dialogOptions;
  NavAskSaveChangesResult reply = 0;
  NavEventUPP             navEventFunctionUPP;
  Str255                  fileName;

  osStatus = NavGetDefaultDialogOptions(&dialogOptions);

  if(osStatus == noErr)
  {
    windowRef = FrontWindow();
    if(isApplicationWindow(windowRef,&txnObject))
    {
      if(TXNGetChangeCount(txnObject))
      {
        GetWTitle(windowRef,fileName);
        BlockMoveData(fileName,dialogOptions.savedFileName,fileName[0] + 1);
        GetIndString(dialogOptions.clientName,rMiscellaneousStrings,sApplicationName);

        navEventFunctionUPP = NewNavEventUPP((NavEventProcPtr) navEventFunction);

        osStatus = NavAskSaveChanges(&dialogOptions,action,&reply,navEventFunctionUPP,0);

        DisposeNavEventUPP(navEventFunctionUPP);

        if(osStatus == noErr)
        {
          switch(reply)
          {
            case kNavAskSaveChangesSave:
              if((osStatus = doSaveCommand()) == noErr)
                doCloseWindow(windowRef,txnObject);
              break;

            case kNavAskSaveChangesDontSave:
                doCloseWindow(windowRef,txnObject);
              break;

            case kNavAskSaveChangesCancel:
              osStatus = kNavAskSaveChangesCancel;
              break;
          }
        }
      }
      else
      {
        doCloseWindow(windowRef,txnObject);
      }
    }
  }

  return osStatus;
}

// ***************************************************************************** doSaveCommand

OSStatus  doSaveCommand(void)
{
  WindowRef windowRef;
  OSStatus  hasNoFileSpec;
  OSStatus  osStatus = noErr;
  FSSpec    fileSpec;

  windowRef = FrontWindow();

  hasNoFileSpec = GetWindowProperty(windowRef,kFileCreator,'FiSp',sizeof(FSSpec),NULL,
                                    &fileSpec);
  if(hasNoFileSpec)
    osStatus = doSaveAsCommand();
  else
    osStatus = doWriteFile(windowRef,false);

  if(osStatus == noErr)
    SetWindowModified(windowRef,false);

  return osStatus;
}

// *************************************************************************** doSaveAsCommand

OSStatus  doSaveAsCommand(void)
{
  OSStatus         osStatus = noErr;
  NavDialogOptions dialogOptions;
  WindowRef        windowRef;
  NavEventUPP      navEventFunctionUPP;
  TXNFileType      txnFileType;
  NavReplyRecord   navReplyStruc;
  AEKeyword        theKeyword;
  DescType         actualType;
  FSSpec           fileSpec;
  Size             actualSize;
  AliasHandle      aliasHdl;

  osStatus = NavGetDefaultDialogOptions(&dialogOptions);

  if(osStatus == noErr)
  {
    windowRef = FrontWindow();

    GetWTitle(windowRef,dialogOptions.savedFileName);
    GetIndString(dialogOptions.clientName,rMiscellaneousStrings,sApplicationName);

    navEventFunctionUPP = NewNavEventUPP((NavEventProcPtr) navEventFunction);

    GetWindowProperty(windowRef,kFileCreator,'FiTy',sizeof(TXNFileType),NULL,&txnFileType);

    osStatus = NavPutFile(NULL,&navReplyStruc,&dialogOptions,navEventFunctionUPP,
                          txnFileType,kFileCreator,NULL);

    DisposeNavEventUPP(navEventFunctionUPP);

    if(navReplyStruc.validRecord && osStatus == noErr)
    {
      if((osStatus = AEGetNthPtr(&(navReplyStruc.selection),1,typeFSS,&theKeyword,
                                 &actualType,&fileSpec,sizeof(fileSpec),&actualSize))
                                 == noErr)
      {
        if(!navReplyStruc.replacing)
        {
          osStatus = FSpCreate(&fileSpec,kFileCreator,txnFileType,navReplyStruc.keyScript);
          if(osStatus != noErr)
          {
            NavDisposeReply(&navReplyStruc);
            return osStatus;
          }
        }

        if(osStatus == noErr)
        {
          SetWTitle(windowRef,fileSpec.name);

          SetWindowProperty(windowRef,kFileCreator,'FiSp',sizeof(FSSpec),&fileSpec);
          SetPortWindowPort(windowRef);
          SetWindowProxyFSSpec(windowRef,&fileSpec);
          GetWindowProxyAlias(windowRef,&aliasHdl);
          SetWindowProperty(windowRef,kFileCreator,'tALH',sizeof(AliasHandle),&aliasHdl);
          SetWindowModified(windowRef,false);

          osStatus = doWriteFile(windowRef,!navReplyStruc.replacing);
        }

        NavCompleteSave(&navReplyStruc,kNavTranslateInPlace);
      }

      NavDisposeReply(&navReplyStruc);
    }
  }

  if(osStatus == userCanceledErr)
    osStatus = noErr;

  return osStatus;
}

// *************************************************************************** doRevertCommand

OSStatus  doRevertCommand(void)
{
  OSStatus                osStatus = noErr;
  NavDialogOptions        dialogOptions;
  NavEventUPP             navEventFunctionUPP;
  WindowRef               windowRef;
  Str255                  fileName;
  NavAskSaveChangesResult reply;
  TXNObject               txnObject = NULL;
    
  osStatus = NavGetDefaultDialogOptions(&dialogOptions);

  if(osStatus == noErr)
  {
    navEventFunctionUPP = NewNavEventUPP((NavEventProcPtr) navEventFunction);

    windowRef = FrontWindow();
    GetWTitle(windowRef,fileName);
    BlockMoveData(fileName,dialogOptions.savedFileName,fileName[0] + 1);

    osStatus = NavAskDiscardChanges(&dialogOptions,&reply,navEventFunctionUPP,0);

    DisposeNavEventUPP(navEventFunctionUPP);

    if(osStatus == noErr)
    {
      if(reply == kNavAskDiscardChanges)
      {
        if(isApplicationWindow(windowRef,&txnObject))
        {
          TXNRevert(txnObject);

          if(TXNDataSize(txnObject))
            SetWindowModified(windowRef,false);
        }
      }
    }
  }

  return osStatus;
}

// ***************************************************************************** doQuitCommand

OSStatus  doQuitCommand(NavAskSaveChangesAction action)
{
  OSStatus osStatus = noErr;

  while(FrontWindow())
  {
    osStatus = doCloseCommand(action);
    if(osStatus != noErr)
      return osStatus;
  }

  return osStatus;
}

// **************************************************************************** doNewDocWindow

OSStatus  doNewDocWindow(WindowRef *outWindowRef,FSSpec *fileSpec,TXNFileType txnFileType)
{
  WindowRef       windowRef;
  Str255          numberAsString, titleString = "\puntitled "; 
  Rect            availableBoundsRect, portRect;
  SInt16          windowHeight;
  TXNFrameOptions txnFrameOptions;
  OSStatus        osStatus  = noErr;
  TXNObject       txnObject  = NULL;
  TXNFrameID      txnFrameID;
  RGBColor        frameColour = { 0xEEEE, 0xEEEE, 0xEEEE };
  TXNControlTag   txnControlTag[1];
  TXNControlData  txnControlData[1];
  TXNMargins      txnMargins;
  CGContextRef    cgContextRef;

  // .............................................................................. get window

  if(!(windowRef = GetNewCWindow(rNewWindow,NULL,(WindowRef) -1)))
    return MemError();
  SetPortWindowPort(windowRef);

  ChangeWindowAttributes(windowRef,kWindowInWindowMenuAttribute,0);

  gUntitledWindowNumber++;
  if(gUntitledWindowNumber != 1)
  {
    NumToString(gUntitledWindowNumber,numberAsString);
    doConcatPStrings(titleString,numberAsString);
  }
  SetWTitle(windowRef,titleString);

  // .................................. extend window bottom to bottom of screen less the dock

  GetAvailableWindowPositioningBounds(GetMainDevice(),&availableBoundsRect);
  GetWindowPortBounds(windowRef,&portRect);
  LocalToGlobal(&topLeft(portRect));
  windowHeight = availableBoundsRect.bottom - portRect.top;
  SizeWindow(windowRef,630,windowHeight,false);

  // ............................................... get new TXNObject and attach window to it

  txnFrameOptions = kTXNWantHScrollBarMask | kTXNWantVScrollBarMask | kTXNShowWindowMask;

  osStatus = TXNNewObject(fileSpec,windowRef,NULL,txnFrameOptions,kTXNTextEditStyleFrameType,
                          txnFileType,kTXNSystemDefaultEncoding,&txnObject,&txnFrameID,
                          NULL);

  if(osStatus == noErr)
  {
    // .......................................... associate frame ID and TXNObject with window

    SetWindowProperty(windowRef,kFileCreator,'tOBJ',sizeof(TXNObject),&txnObject);
    SetWindowProperty(windowRef,kFileCreator,'tFRM',sizeof(TXNFrameID),&txnFrameID);
    if(fileSpec != NULL)
      SetWindowProperty(windowRef,kFileCreator,'FiSp',sizeof(FSSpec),fileSpec);
    SetWindowProperty(windowRef,kFileCreator,'FiTy',sizeof(TXNFileType),&txnFileType);

    // ........................................................................... set margins

    txnControlTag[0] = kTXNMarginsTag;
    txnControlData[0].marginsPtr = &txnMargins;
  
    txnMargins.leftMargin  = txnMargins.topMargin = 10;
    txnMargins.rightMargin = txnMargins.bottomMargin = 10;
    TXNSetTXNObjectControls(txnObject,false,1,txnControlTag,txnControlData);

    // ......................................... create core graphics context and pass to MLTE

    if(gRunningOnX)
    {
      CreateCGContextForPort(GetWindowPort(windowRef),&cgContextRef);
      txnControlTag[0] = kATSUCGContextTag;
      txnControlData[0].uValue = (UInt32) cgContextRef;
      TXNSetTXNObjectControls(txnObject,false,1,txnControlTag,txnControlData);
    }
  }
  else
    doErrorAlert(osStatus); 

  gCurrentNumberOfWindows ++;
  if(gCurrentNumberOfWindows == 1)
    doEnableDisableMenus(true);

  *outWindowRef = windowRef;

  return noErr;
}

// ******************************************************************************** doOpenFile

OSStatus  doOpenFile(FSSpec fileSpec,OSType fileType)
{
  OSStatus    osStatus = noErr;
  WindowRef   windowRef;
  AliasHandle aliasHdl;

  if(osStatus = doNewDocWindow(&windowRef,&fileSpec,fileType))
    return osStatus;

  SetWTitle(windowRef,fileSpec.name);

  SetWindowProxyFSSpec(windowRef,&fileSpec);
  GetWindowProxyAlias(windowRef,&aliasHdl);
  SetWindowProperty(windowRef,kFileCreator,'tALH',sizeof(AliasHandle),&aliasHdl);

  SetWindowModified(windowRef,false);

  return noErr;
}

// ******************************************************************************* doCloseFile

void  doCloseWindow(WindowRef windowRef,TXNObject txnObject)
{
  TXNDeleteObject(txnObject);
  DisposeWindow(windowRef);
  gCurrentNumberOfWindows --;
  if(gCurrentNumberOfWindows == 0)
    doEnableDisableMenus(false);
}

// ******************************************************************************* doWriteFile

OSStatus  doWriteFile(WindowRef windowRef,Boolean newFile)
{
  TXNPermanentTextEncodingType encodingType;
  TXNObject   txnObject = NULL;
  FSSpec      fileSpec, fileSpecTemp;
  TXNFileType txnFileType;
  UInt32      currentTime;
  Str255      tempFileName;
  OSStatus    osStatus  = noErr;
  SInt16      tempFileVolNum, tempFileRefNum, tempResForkRefNum = -1;
  SInt32      tempFileDirID;
  Boolean     hasResFile = false;

  GetWindowProperty(windowRef,kFileCreator,'tOBJ',sizeof(TXNObject),NULL,&txnObject);
  GetWindowProperty(windowRef,kFileCreator,'FiSp',sizeof(FSSpec),NULL,&fileSpec);
  GetWindowProperty(windowRef,kFileCreator,'FiTy',sizeof(TXNFileType),NULL,&txnFileType);

  encodingType = (txnFileType == kTXNTextFile) ? kTXNMacOSEncoding : kTXNUnicodeEncoding;

  GetDateTime(¤tTime);
  NumToString((SInt32) currentTime,tempFileName);
  
  osStatus = FindFolder(fileSpec.vRefNum,kTemporaryFolderType,kCreateFolder,&tempFileVolNum,
                        &tempFileDirID);
  if(osStatus == noErr)
    osStatus = FSMakeFSSpec(tempFileVolNum,tempFileDirID,tempFileName,&fileSpecTemp);
  if(osStatus == noErr || osStatus == fnfErr)
    osStatus = FSpCreate(&fileSpecTemp,'trsh','trsh',smSystemScript);
  if(osStatus == noErr)
    osStatus = FSpOpenDF(&fileSpecTemp,fsRdWrPerm,&tempFileRefNum);

  if(osStatus == noErr)
  {
    if(txnFileType == kTXNTextFile)
    {
      FSpCreateResFile(&fileSpecTemp,'trsh','trsh',smSystemScript);
      osStatus = ResError();
      if(osStatus == noErr)
        tempResForkRefNum = FSpOpenResFile(&fileSpecTemp,fsRdWrPerm);
      hasResFile = true;
    }
  }

  if(osStatus == noErr)
    osStatus = TXNSave(txnObject,txnFileType,kTXNMultipleStylesPerTextDocumentResType,
                       encodingType,&fileSpec,tempFileRefNum,tempResForkRefNum);
  
  if(osStatus == noErr)
    osStatus = FSpExchangeFiles(&fileSpecTemp,&fileSpec);
  if(osStatus == noErr)
    osStatus = FSpDelete(&fileSpecTemp);

  if(osStatus == noErr)
    osStatus = FSClose(tempFileRefNum);
  if(osStatus == noErr)
    if(tempResForkRefNum != -1)
      CloseResFile(tempResForkRefNum);

  osStatus = ResError();

  if(osStatus == noErr)
    if(newFile)
      osStatus = doCopyResources(fileSpec,txnFileType,hasResFile);

  return osStatus;
}

// *************************************************************************** doCopyResources

OSStatus  doCopyResources(FSSpec fileSpec,TXNFileType fileType,Boolean hasResFile)
{
  OSStatus osStatus = noErr;
  SInt16   fileRefNum;

  if(!hasResFile)
    FSpCreateResFile(&fileSpec,kFileCreator,fileType,smSystemScript);

  osStatus = ResError();
  if(osStatus == noErr)
    fileRefNum = FSpOpenResFile(&fileSpec,fsRdWrPerm);

  if(fileRefNum > 0)
    osStatus = doCopyAResource('STR ',-16396,gAppResFileRefNum,fileRefNum);
  else
    osStatus = ResError();

  if(osStatus == noErr)
    CloseResFile(fileRefNum); 

  osStatus = ResError();

  return osStatus;
}

// *************************************************************************** doCopyAResource

OSStatus  doCopyAResource(ResType resourceType,SInt16 resourceID,SInt16 sourceFileRefNum,
                      SInt16 destFileRefNum)
{
  Handle  sourceResourceHdl;
  Str255  sourceResourceName;
  ResType ignoredType;
  SInt16  ignoredID;

  UseResFile(sourceFileRefNum);

  sourceResourceHdl = GetResource(resourceType,resourceID);

  if(sourceResourceHdl != NULL)
  {
    GetResInfo(sourceResourceHdl,&ignoredID,&ignoredType,sourceResourceName);
    DetachResource(sourceResourceHdl);
    UseResFile(destFileRefNum);
    AddResource(sourceResourceHdl,resourceType,resourceID,sourceResourceName);
    if(ResError() == noErr)
      UpdateResFile(destFileRefNum);
  }

  ReleaseResource(sourceResourceHdl);

  return ResError();
}

// ************************************************************************** navEventFunction

void  navEventFunction(NavEventCallbackMessage callBackSelector,NavCBRecPtr callBackParms,
                       NavCallBackUserData callBackUD)
{
  WindowRef windowRef;

  switch(callBackSelector)
  {
    case kNavCBEvent:
      switch(callBackParms->eventData.eventDataParms.event->what)
      {
        case updateEvt:
          windowRef = (WindowRef) callBackParms->eventData.eventDataParms.event->message;
          if(GetWindowKind(windowRef) != kDialogWindowKind)
            doUpdate((EventRecord *) callBackParms->eventData.eventDataParms.event);
          break;
      }
      break;
  } 
}

// *******************************************************************************************

Demonstration Program MTLETextEditor Comments

This program, like all previous programs, demonstrates the programming of one particular aspect
of the Mac OS.  However, unlike all previous demonstration programs, it can also be used as a
useful application, that is, as a fully functional basic text editor.

New documents created by the text editor are created and saved as Textension ('txtn') file
types.  Existing files of type 'TEXT' and Unicode ('utxt') can be opened are saved as 'TEXT'
and Unicode files.  For 'TEXT' files, style information is saved in 'styl' resources.  Movies
may be embedded within documents.

Those areas of the program relating to file operations and window proxy icons follow the same
general approach as does the demonstration program Files (Chapter 18).  This includes the file
synchronisation function and the functions for copying the missing application name string
resource to the resource fork of saved files.  The Apple event handlers are identical to those
in the demonstration program Files, except for the added capability to handle the Print
Documents event.

MLTETextEditor.c

main

The application's resource fork file reference number is saved for use in the function
doCopyResources.

CreateStandardWindowMenu is used to create a Window menu, which is then given an ID and added
to the menu list.  If the program is running on Mac OS 8/9, the first item in the Window menu
(Zoom Window) is deleted.  (As will be seen, in this program, TXNZoomWindow is called when the
user clicks the zoom box/button.  TXNZoomWindow adjusts the scroll bars automatically; however,
this does not occur when Zoom Window is chosen from a Window menu.  It is thus necessary to
delete the item in this particular program.)

After a reference to the Font menu is obtained, TXNNewFontMenuObject is called to create a
hierarchical Font menu.  Note that the value passed in the third parameter, which specifies the
ID of the first sub-menu, must be 160 or higher.

doInitializeMLTE

doInitializeMLTE is called from doPreliminaries.  The call to TXNInitTextension initialises the
Textension library.  Font information specifying that the default font, font size, and font
style for the system default encoding (Unicode on systems with ATSUI) be New York, 12 point,
normal is passed in the first parameter.  The encoding field specifies how the application
wants to see text.  The third parameter specifies that embedded movies are to be supported.

doInstallAEHandlers

Note that openAndPrintDocsEventHandler will be called when both an Open Documents and a Print
Documents event is received, the difference being that the reference constant is set to kOpen
(0) for an Open Application event and to kPrint (1) for a Print Documents event.

eventLoop

Note that the value passed in WaitNextEvent's sleep parameter is the value returned by a call
to the function doGetSleepTime.  Note also that, when a NULL event is returned by
WaitNextEvent, the functions doIdle and doSynchroniseFiles are called.

doGetSleepTime

doGetSleepTime determines the value passed in WaitNextEvent's sleep parameter.

This is the first of many functions which call the function isApplicationWindow.  As will be
seen, isApplicationWindow returns true if there is an open window and if it is of the
application kind.  It also returns, in the txnObject parameter, the TXNObject to which the
window was attached when the window was created.

If there is an open window, and if it is of the application kind, TXNGetSleepTicks is called to
get the sleep time to be passed to WaitNextEvent.  This ensures that the function doIdle will
be called at the appropriate interval.  If the front window is of the dialog kind, the sleep
time is set to the value returned by a call to GetCaretTime.  (Actually, in this application,
which presents no dialogs with edit text items, it might be considered more appropriate to set
the sleep time to the maximum unsigned long value at the else statement.)

doIdle

doIdle is called to perform idle processing.

The call to TXNGetChangeCount gets the number of times the document has been changed since the
last time the TXNObject was saved.  If any changes have been made since the last save,
SetWindowModified is called to disable the window proxy icon.

doEvents

At the keyDown case, note that TXNKeyDown does not need to be called in a Carbon application. 
Carbon special-cases Command-key events to avoid them being sent to MLTE.  However, all other
key-downs get sent directly to the Type Services Manager and your application never gets to
"see" them.  This means that, in Carbon applications, you cannot filter out characters for
special handling before they are passed to MLTE (TXNKeyDown) as you could in a Classic
application.

The only exception is command-key events; for command keys, because MLTE 
has the habit of eating all keystrokes that go to it, even command keys that it 
can't process, we detect if the command key exists in the menus and special-case 
it to avoid sending it to MLTE.

At the mouseMovedMessage case within the osEvt case, TXNAdjustCursor is called to handle cursor
shape changing.  If the mouse is over a text area, TXNAdjustCursor sets the cursor to the
I-beam cursor.  If the cursor is over a movie, over a scroll bar, or outside a text area,
TXNAdjustCursor sets the cursor to the arrow cursor.

doMouseDown

At the inGrow case, TXNGrowWindow is called to handle the resizing operation.  At the
inZoomIn/inZoomOut case, TXNZoomWindow is called to zoom the window.  At the inDrag, inGrow,
and inZoomIn/inZoomOut cases, TXNAdjustCursor is called after the window has been dragged,
re-sized, or zoomed so that the mouse-moved region is re-calculated.

doActivate

As will be seen, when TXNObject is created and a window attached to it, SetWindowProperty is
called to associate the TXNObject frame ID with the window.  The call to GetWindowProperty
retrieves this frame ID.

If the window is becoming active, TXNActivate is called, with true passed in the third
parameter, to activate the scroll bars.  Also, TXNFocus is called, with true passed in the
second parameter to activate text input (selection and typing).  If the window is becoming
inactive, false is passed in TXNActivate's third parameter and TXNFocus' second parameter to
deactivate the scroll bars and text input.

doUpdate

TXNUpdate is called to redraw everything in the content area.  Note that this function calls
BeginUpdate and EndUpdate, so there is no necessity for the application to do so.

isApplicationWindow

isApplicationWindow is called from many functions.  It returns true if there is a front window
and if that window is of the application kind.  It also returns to the caller the TXNObject to
which that window is attached.  As will be seen, the TXNObject is associated with the window
when both are created, and is retrieved here by the call to GetWindowProperty.

doSynchroniseFiles

doSynchroniseFiles is the file synchronisation function (see Chapter 18).  It is adapted from
the function of the same name in the demonstration program Files.  In this version:

o The method used to determine whether the window has a file associated with it is to call
  GetWindowProperty in an attempt to retrieve the handle to the alias structure which, as will
  be seen, is associated with a window by a call to SetWindowProperty when a file is saved or
  loaded.

o If the aliasChanged parameter is set to true in the call to ResolveAlias, meaning that the
  location of the file has changed, the file system specification structure returned by
  ResolveAlias is associated with the window by the call to SetWindowProperty, replacing the
  previous file system specification structure stored in the window.

o At the inner if statement, if the file is found to be in the trash, GetWindowProperty is
  called to return the TXNObject associated with the window when it was created,
  TXNDeleteObject is called to delete the TXNObject and its associated data structures, and
  DisposeWindow is called to dispose of the window. 

openAndPrintDocsEventHandler

openAndPrintDocsEventHandler is called when an Open Documents or Print Documents Apple event is
received.  In both cases, doOpenFile is called to open and display the file.  In the case of a
Print Documents event, TXNPrint is also called to print the document, following which
doCloseCommand is called to dispose of the window and its TXNObject.

doErrorAlert

If the error code is kATSUFontsMatched (Ð8793), doErrorAlert simply returns. kATSUFontsMatched
is not an error as such.  It but is returned by ATSUMatchFontsToText when changes need to be
made to the fonts associated with the text.

MLTEMenus.c

doEnableDisableMenus

doEnableDisableMenus is called from doCloseWindow and doNewDocWindow to ensure that all menus
except the File menu are disabled if no windows are open and that those menus are enabled if at
least one window is open.

doAdjustFileMenu

If the call to TXNGetChangeCount reveals that the document has been changed since it was opened
or last saved, the File menu Save and Revert items are enabled, otherwise they are disabled.

If the call to TXNDataSize reveals that there are characters in the TXNObject, the Save As,
Page Setup, and Print items are enabled, otherwise they are disabled.

The else block executes only if no windows are open, ensuring that all File menu items except
New, Open, and Quit are disabled.

doEditMenu

At the first block, all Edit menu items are disabled.  At the second block, the default item
text for both the Undo and Redo items (Can't Undo, Can't Redo) is set.  This may be changed by
the next two blocks.

The next block addresses the Undo item.  The call to TXNCanUndo determines whether the last
action is undoable.  If the last action is undoable, TXNCanUndo returns, in the second
parameter, an action key code which will be used to index a STR# resource for a string
describing the undoable action.  If this action key code represents a typing, cut, paste,
clear, change font, change font colour, change font size, change font style, change alignment,
drag action, or move action, the appropriate string is retrieved by the call to GetIndString
and the item text is set to this string (for example, "Undo Cut").  If the action is any other
action, the item text is set to "Undo".

At the block beginning with the call to TXNCanRedo, the same process is repeated in respect of
the Redo item.

If the call to TXNIsSelectionEmpty reveals that the current selection is not empty, the Cut,
Copy, and Clear items are enabled.

If the call to TXNIsScrapPastable reveals that the current scrap contains data that is
supported by MLTE, the Paste item is enabled.

If the call to TXNDataSize reveals that there are characters in the TXNObject, the Select All
item is enabled.

doPrepareFontMenu

doAdjustFileMenu and doAdjustEditMenu are concerned with enabling and disabling menu items as
appropriate.  doPrepareFontMenu and the other menu preparation functions are concerned with
adding and removing checkmarks from items.

For the Font menu, all that is required is a call to TXNPrepareFontMenu.  If the insertion
point caret is in text in a particular font, or if a selection contains text in a single font,
that menu item will be checkmarked.  (If the font is in a Font menu sub-menu, the item in the
sub-menu is checkmarked and a "dash" marking character is placed in the Font menu item to which
the sub-menu is attached.)  On the other hand, if a selection contains text in more than one
font, all marking characters are removed from the Font menu and its sub-menus.

doPrepareSizeMenu

doPrepareSizeMenu does for the Size menu what doPrepareFontMenu does for the Font menu.  If the
insertion point caret is in text in a particular size, or if a selection contains text in a
single size, the associated Size menu item is checkmarked.  On the other hand, if a selection
contains text in more than one size, all Size menu items are un-checkmarked.

Font size is represented by a value of type Fixed (four bytes comprising 16-bit signed integer
plus 16-bit fraction).  Accordingly, the itemSizes array is initialised with the sizes
represented in the Size menu (9, 10, 11, 12, 14, 18, 24, 36) expressed as Fixed values.  Each
element in the array corresponds to an individual menu item.

The tag and size fields of a structure of type TXNTypeAttributes are assigned, respectively, a
value ('size') specifying the size attribute and the size of the Fixed data type.  The call to
TXNGetContinuousTypeAttributes tests the current selection to see if the font size is
continuous.  On output, bit 1 in the second parameter (txContinuousFlags) will be set if all
the text in the selection is all of one size, and the dataValue field of the data field of
txnTypeAttributes will contain that size.

All items in the menu are then walked.  If bit 1 of txContinuousFlags is not set, all items
will be un-checkmarked.  If bit 1 is set, and if the font size returned in the dataValue field
is equal to the value in that element of the itemSizes array corresponding to the current menu
item, that item is checkmarked, otherwise it is un-checkmarked.

doPrepareStyleMenu

The same general approach is used to prepare the Style menu.  In this case, the tag and size
fields of a structure of type TXNTypeAttributes are assigned, respectively, a value ('face')
specifying the style attribute and the size of the Style data type.  Also, the block which
checkmarks or un-checkmarks the menu items is a little different, reflecting the fact that the
bold, italic, and underline styles can be cumulative.

The first call to CheckMenuItem checkmarks the Plain menu item if all the text in the selection
is of the same style and if that style is the plain (normal) style.  The for loop addresses the
bold, italic, and underline menu items only.  If all the text in the selection is of the same
style, or combination of styles, the menu item/s corresponding to the bits set in the dataValue
field of the data field of txnTypeAttributes is/are checkmarked, otherwise, it/they is/are
uncheckmarked.

doPrepareColourMenu

doPrepareColourMenu is similar to doAdjustSizeMenu except that the tag and size fields of a
structure of type TXNTypeAttributes are assigned, respectively, a value ('klor') specifying the
colour attribute and the size of the RGBColor data type.  Note also that the address of the
attributesColour variable is assigned to the dataPtr field of the data field of
txnTypeAttributes, meaning that attributesColour receives the colour returned by the call to
TXNGetContinuousTypeAttributes.  It is the colour stored in attributesColour that is compared
with the colours stored in the itemColours array in order to determine whether a menuItem
should be checked or unchecked (assuming that the