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


  MacTech Magazine

The journal of Macintosh technology

 
 
Secutech

Magazine In Print
  About MacTech  
  Home Page  
  Subscribe  
  Archives DVD  
  Submit News  
  Submit a Tip!  
  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 1

Go to Contents Go to Part 2
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// Text1.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
//
// This program demonstrates:
//
// ¥  A "bare-bones" monostyled text editor.
//
// ¥  A Help dialog which features the integrated scrolling of multistyled text and 
//    pictures.
//
// In the monostyled text editor demonstration, a panel is displayed at the bottom of all
// opened windows.  This panel displays the edit record length, number of lines, line 
// height, destination rectangle (top), scroll bar value, and scroll bar maximum value. 
//
// The bulk of the source code for the Help dialog is contained in the file HelpDialog.c.
// The dialog itself displays information intended to assist the user in adapting the
// Help dialog source code and resources to the requirements of his/her own application.
//
// The program utilises the following resources:
//
// ¥  An 'MBAR' resource, and 'MENU' resources for Apple, File, Edit, and Help dialog
//    pop-up menus (preload, non-purgeable).  
//
// ¥  A 'WIND' resource (purgeable) (initially visible).  
//
// ¥  'CNTL' resources (purgeable) for the vertical scroll bars in the text editor window
//    and Help dialog, and for the pop-up menu in the Help Dialog.
//     
// ¥  A 'DLOG' resource (purgeable, initially invisible) and associated 'dctb' resource 
//    (purgeable) for the Help dialog.
//
// ¥  'DITL' resources (purgeable) for the 'ALRT' and 'DLOG' resources.
//
// ¥  'TEXT' and associated 'styl' resources (all purgeable) for the Help dialog.
//
// ¥  'PICT' resources (purgeable) for the Help dialog.
//
// ¥  A 'STR ' resource  (purgeable) containing error text strings.  
//
// ¥  A 'SIZE' resource with the acceptSuspendResumeEvents, doesActivateOnFGSwitch, and
//    is32BitCompatible  flags set.    
//
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

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

#include <Appearance.h>
#include <Balloons.h>  
#include <ControlDefinitions.h>
#include <Devices.h>
#include <Gestalt.h>
#include <LowMem.h>
#include <Scrap.h>
#include <Sound.h>
#include <StandardFile.h>
#include <ToolUtils.h>

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

#define rMenubar         128
#define mApple           128
#define  iAbout          1
#define mFile            129
#define  iNew            1
#define  iOpen           2
#define  iClose          4
#define  iSaveAs         6
#define  iQuit           12
#define mEdit            130
#define  iUndo           1
#define  iCut            3
#define  iCopy           4
#define  iPaste          5
#define  iClear          6
#define  iSelectAll      7
#define rWindow          128
#define rVScrollbar      128
#define rErrorStrings    128
#define  eMenuBar        1
#define  eMenu           2
#define  eWindow         3
#define  eDocStructure   4
#define  eEditRecord     5
#define  eExceedChara    6
#define  eNoSpaceCut     7
#define  eNoSpacePaste   8
#define kMaxTELength     32767
#define kTab             0x09
#define kDel             0x7F
#define kReturn          0x0D
#define topLeft(r)      (((Point *) &(r))[0])
#define botRight(r)      (((Point *) &(r))[1])

// ............................................................................. typedefs

typedef struct
{
  TEHandle      editRecHdl;
  ControlHandle vScrollbarHdl;
} docStructure, **docStructureHandle;

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

ControlActionUPP   scrollActionFunctionUPP;
TEClickLoopUPP     customClickLoopUPP;
Boolean            gMacOS85Present = false;
Boolean            gDone;
Boolean            gInBackground;
RgnHandle          gCursorRegion;
SInt16             gNumberOfWindows = 0;
SInt16             gOldControlValue;

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

void            main                   (void);
void            doInitManagers         (void);
void            eventLoop              (void);
void            doIdle                 (void);
void            doEvents               (EventRecord *);
void            doKeyEvent             (SInt8);
pascal void     scrollActionFunction   (ControlHandle,SInt16);
void            doInContent            (EventRecord *);
void            doUpdate               (EventRecord *);
void            doActivate             (EventRecord *);
void            doActivateDocWindow    (WindowPtr,Boolean);
void            doOSEvent              (EventRecord *);
WindowPtr       doNewDocWindow         (void);
pascal Boolean  customClickLoop        (void);
void            doSetScrollBarValue    (ControlHandle,SInt16 *);
void            doAdjustMenus          (void);
void            doMenuChoice           (SInt32);
void            doFileMenu             (SInt16);
void            doEditMenu             (SInt16);
SInt16          doGetSelectLength      (TEHandle);
void            doAdjustScrollbar      (WindowPtr);
void            doAdjustCursor         (WindowPtr);
void            doCloseWindow          (WindowPtr);
void            doSaveAsFile           (TEHandle);
void            doOpenCommand          (void);
void            doOpenFile             (FSSpec);
void            doDrawDataPanel        (WindowPtr);
void            doErrorAlert           (SInt16);
void            doHelpMenu             (SInt16);

extern void      doHelp                (void);

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× main

void  main(void)
{
  OSErr        osError;
  SInt32       response;
  Handle       menubarHdl;
  MenuHandle   menuHdl;
  OSErr        osErr;  
  
  // .................................................................initialise managers

  doInitManagers();

  // ....................................... check whether Mac OS 8.5 or later is present

  osError = Gestalt(gestaltSystemVersion,&response);
  
  if(osError == noErr && response >= 0x00000850)
    gMacOS85Present = true;

  // ......................................................... create routine descriptors

  scrollActionFunctionUPP = NewControlActionProc((ProcPtr) scrollActionFunction);
  customClickLoopUPP = NewTEClickLoopProc((ProcPtr) customClickLoop);

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

  menuHdl = GetMenuHandle(mApple);
  if(menuHdl == NULL)
    doErrorAlert(eMenu);
  else
    AppendResMenu(menuHdl,'DRVR');

  osErr = HMGetHelpMenuHandle(&menuHdl);
  if(osErr == noErr)
    AppendMenu(menuHdl,"\pText1 Help");
  else
    doErrorAlert(eMenu);

  // ............................................................ open an untitled window

  doNewDocWindow();

  // .................................................................... enter eventLoop

  eventLoop();
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doInitManagers

void  doInitManagers(void)
{
  MaxApplZone();
  MoreMasters();

  InitGraf(&qd.thePort);
  InitFonts();
  InitWindows();
  InitMenus();
  TEInit();
  InitDialogs(NULL);
  InitCursor();

  FlushEvents(everyEvent,0);
  
  RegisterAppearanceClient();
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× eventLoop

void  eventLoop(void)
{
  EventRecord  eventStructure;
  Boolean      gotEvent;
  SInt32       sleepTime;

  gDone = false;
  gCursorRegion = NewRgn();
  sleepTime = LMGetCaretTime();

  while(!gDone)
  {
    gotEvent = WaitNextEvent(everyEvent,&eventStructure,sleepTime,gCursorRegion);

    if(gotEvent)
      doEvents(&eventStructure);
    else
    {
      if(gNumberOfWindows > 0)
        doIdle();
    }
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doIdle

void  doIdle(void)
{
  docStructureHandle  docStrucHdl;
  WindowPtr           windowPtr;
  
  windowPtr = FrontWindow();

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
  if(docStrucHdl != NULL)
    TEIdle((*docStrucHdl)->editRecHdl);
}  

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doEvents

void  doEvents(EventRecord *eventStrucPtr)
{
  WindowPtr  windowPtr;
  SInt16     partCode;
  SInt8      charCode;

  switch(eventStrucPtr->what)
  {
    case mouseDown:
      partCode = FindWindow(eventStrucPtr->where,&windowPtr);
      switch(partCode)
      {
        case inMenuBar:
          doAdjustMenus();
          doMenuChoice(MenuSelect(eventStrucPtr->where));
          break;

        case inContent:
          if(windowPtr != FrontWindow())
            SelectWindow(windowPtr);
          else
            doInContent(eventStrucPtr);
          break;

        case inDrag:
          DragWindow(windowPtr,eventStrucPtr->where,&qd.screenBits.bounds);
          doAdjustCursor(windowPtr);
          break;

        case inGoAway:
          if(TrackGoAway(windowPtr,eventStrucPtr->where))
            doCloseWindow(FrontWindow());
          break;
      }
      break;

    case keyDown:
    case autoKey:
      charCode = eventStrucPtr->message & charCodeMask;
      if((eventStrucPtr->modifiers & cmdKey) != 0)
      {
        doAdjustMenus();
        doMenuChoice(MenuEvent(eventStrucPtr));
      }
      else
        doKeyEvent(charCode);
      break;

    case updateEvt:
      doUpdate(eventStrucPtr);
      break;

    case activateEvt:
      doActivate(eventStrucPtr);
      break;

    case osEvt:
      doOSEvent(eventStrucPtr);
      break;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doKeyEvent

void  doKeyEvent(SInt8 charCode)
{
  WindowPtr           windowPtr;
  docStructureHandle  docStrucHdl;
  TEHandle            editRecHdl;
  SInt16              selectionLength;

  windowPtr = FrontWindow();
  docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
  editRecHdl = (*docStrucHdl)->editRecHdl;

  if(charCode == kTab)
  {
    // Do tab key handling here if required.
  }
  else if(charCode == kDel)
  {
    selectionLength = doGetSelectLength(editRecHdl);
    if(selectionLength == 0)
      (*editRecHdl)->selEnd += 1;
    TEDelete(editRecHdl);
    doAdjustScrollbar(windowPtr);
  }
  else
  {
    selectionLength = doGetSelectLength(editRecHdl);
    if(((*editRecHdl)->teLength - selectionLength + 1) < kMaxTELength)
    {
      TEKey(charCode,editRecHdl);
      doAdjustScrollbar(windowPtr);
    }
    else
      doErrorAlert(eExceedChara);
  }

  doDrawDataPanel(windowPtr);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× scrollActionFunction

pascal void  scrollActionFunction(ControlHandle controlHdl,SInt16 partCode)
{
  WindowPtr            windowPtr;
  docStructureHandle   docStrucHdl;
  TEHandle             editRecHdl;
  SInt16               linesToScroll;
  SInt16               controlValue, controlMax;

  windowPtr = (*controlHdl)->contrlOwner;
  docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
  editRecHdl = (*docStrucHdl)->editRecHdl;

  controlValue = GetControlValue(controlHdl);
  controlMax = GetControlMaximum(controlHdl);

  if(partCode)
  {
    if(partCode != kControlIndicatorPart)
    {
      switch(partCode)
      {
        case kControlUpButtonPart:
        case kControlDownButtonPart:
          linesToScroll = 1;
          break;
        
        case kControlPageUpPart:
        case kControlPageDownPart:
          linesToScroll = (((*editRecHdl)->viewRect.bottom - (*editRecHdl)->viewRect.top) /
                           (*editRecHdl)->lineHeight) - 1;
          break;
      }

      if((partCode == kControlDownButtonPart) || (partCode == kControlPageDownPart))
        linesToScroll = -linesToScroll;

      linesToScroll = controlValue - linesToScroll;
      if(linesToScroll < 0)
        linesToScroll = 0;
      else if(linesToScroll > controlMax)
        linesToScroll = controlMax;

      SetControlValue(controlHdl,linesToScroll);

      linesToScroll = controlValue - linesToScroll;
    }
    else
    {
      linesToScroll = gOldControlValue - controlValue;
      gOldControlValue = controlValue;
    }

    if(linesToScroll != 0)
    {
      TEScroll(0,linesToScroll * (*editRecHdl)->lineHeight,editRecHdl);
      doDrawDataPanel(windowPtr);
    }
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doInContent

void  doInContent(EventRecord *eventStrucPtr)
{
  WindowPtr            windowPtr;
  docStructureHandle   docStrucHdl;
  TEHandle             editRecHdl;
  Point                mouseXY;
  ControlHandle        controlHdl;
  SInt16               partCode;
  Boolean              shiftKeyPosition = false;

  windowPtr = FrontWindow();
  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
  editRecHdl = (*docStrucHdl)->editRecHdl;

  mouseXY = eventStrucPtr->where;
  SetPort(windowPtr);
  GlobalToLocal(&mouseXY);

  if((partCode = FindControl(mouseXY,windowPtr,&controlHdl)) != 0)
  {
    gOldControlValue = GetControlValue(controlHdl);
    TrackControl(controlHdl,mouseXY,scrollActionFunctionUPP);
  }
  else if(PtInRect(mouseXY,&(*editRecHdl)->viewRect))
  {
    if((eventStrucPtr->modifiers & shiftKey) != 0)
      shiftKeyPosition = true;
    TEClick(mouseXY,shiftKeyPosition,editRecHdl);
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doUpdate

void  doUpdate(EventRecord *eventStrucPtr)
{
  WindowPtr            windowPtr;
  docStructureHandle   docStrucHdl;
  TEHandle             editRecHdl;
  GrafPtr              oldPort;

  windowPtr = (WindowPtr) eventStrucPtr->message;
  docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
  editRecHdl = (*docStrucHdl)->editRecHdl;
  
  GetPort(&oldPort);
  SetPort(windowPtr);

  BeginUpdate((WindowPtr)eventStrucPtr->message);

  TEUpdate(&windowPtr->portRect,editRecHdl);
  UpdateControls(windowPtr,windowPtr->visRgn);

  doDrawDataPanel(windowPtr);

  EndUpdate((WindowPtr)eventStrucPtr->message);

  SetPort(oldPort);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doActivate

void  doActivate(EventRecord *eventStrucPtr)
{
  WindowPtr  windowPtr;
  Boolean    becomingActive;

  windowPtr = (WindowPtr) eventStrucPtr->message;
  becomingActive = ((eventStrucPtr->modifiers & activeFlag) == activeFlag);
  doActivateDocWindow(windowPtr,becomingActive);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doActivateDocWindow

void  doActivateDocWindow(WindowPtr windowPtr,Boolean becomingActive)
{
  docStructureHandle  docStrucHdl;
  TEHandle            editRecHdl;

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
  editRecHdl = (*docStrucHdl)->editRecHdl;

  if(becomingActive)
  {
    SetPort(windowPtr);

    (*editRecHdl)->viewRect.bottom = ((((*editRecHdl)->viewRect.bottom -
                                        (*editRecHdl)->viewRect.top) / 
                                        (*editRecHdl)->lineHeight) * 
                                        (*editRecHdl)->lineHeight) + 
                                        (*editRecHdl)->viewRect.top;
    (*editRecHdl)->destRect.bottom = (*editRecHdl)->viewRect.bottom;

    TEActivate(editRecHdl);
    ActivateControl((*docStrucHdl)->vScrollbarHdl);
    doAdjustScrollbar(windowPtr);
  }
  else
  {
    TEDeactivate(editRecHdl);
    DeactivateControl((*docStrucHdl)->vScrollbarHdl);
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doOSEvent

void  doOSEvent(EventRecord *eventStrucPtr)
{
  switch((eventStrucPtr->message >> 24) & 0x000000FF)
  {
    case suspendResumeMessage:
      gInBackground = (eventStrucPtr->message & resumeFlag) == 0;
      if(!gInBackground)
        SetCursor(&qd.arrow);
      if(gNumberOfWindows > 0)
        doActivateDocWindow(FrontWindow(),!gInBackground);
      HiliteMenu(0);
      break;

    case mouseMovedMessage:
      doAdjustCursor(FrontWindow());
      break;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doNewDocWindow

WindowPtr  doNewDocWindow(void)
{
  WindowPtr           windowPtr;
  docStructureHandle  docStrucHdl;
  Rect                destAndViewRect;

  if(!(windowPtr = GetNewCWindow(rWindow,NULL,(WindowPtr) -1)))
  {
    doErrorAlert(eWindow);
    return(NULL);
  }

  SetPort(windowPtr);
  TextSize(10);

  if(!(docStrucHdl = (docStructureHandle) NewHandle(sizeof(docStructure))))
  {
    doErrorAlert(eDocStructure);
    return(NULL);
  }
  SetWRefCon(windowPtr,(SInt32) docStrucHdl);

  gNumberOfWindows ++;

  (*docStrucHdl)->vScrollbarHdl = GetNewControl(rVScrollbar,windowPtr);
  
  destAndViewRect = windowPtr->portRect;
  destAndViewRect.right -= 15;
  destAndViewRect.bottom -= 15;
  InsetRect(&destAndViewRect,2,2);

  MoveHHi((Handle) docStrucHdl);
  HLock((Handle) docStrucHdl);

  if(!((*docStrucHdl)->editRecHdl = TENew(&destAndViewRect,&destAndViewRect)))
  {
    DisposeWindow(windowPtr);
    gNumberOfWindows --;
    DisposeHandle((Handle) docStrucHdl);
    doErrorAlert(eEditRecord);
    return(NULL);
  }
  
  HUnlock((Handle) docStrucHdl);
  
  TESetClickLoop(customClickLoopUPP,(*docStrucHdl)->editRecHdl);
  TEAutoView(true,(*docStrucHdl)->editRecHdl);
  TEFeatureFlag(teFOutlineHilite,1,(*docStrucHdl)->editRecHdl);

  return(windowPtr);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× customClickLoop

pascal Boolean  customClickLoop(void)
{
  WindowPtr            windowPtr;
  docStructureHandle   docStrucHdl;
  TEHandle             editRecHdl;
  GrafPtr              oldPort;
  RgnHandle            oldClip;
  Rect                 tempRect;
  Point                mouseXY;
  SInt16               linesToScroll = 0;

  windowPtr = FrontWindow();
  docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
  editRecHdl = (*docStrucHdl)->editRecHdl;

  GetPort(&oldPort);
  SetPort(windowPtr);
  oldClip = NewRgn();
  GetClip(oldClip);
  SetRect(&tempRect,-32767,-32767,32767,32767);
  ClipRect(&tempRect);

  GetMouse(&mouseXY);
  if(mouseXY.v < windowPtr->portRect.top)
  {
    linesToScroll = 1;
    doSetScrollBarValue((*docStrucHdl)->vScrollbarHdl,&linesToScroll);
    if(linesToScroll != 0)
      TEScroll(0,linesToScroll * ((*editRecHdl)->lineHeight),editRecHdl);
  }
  else if(mouseXY.v > windowPtr->portRect.bottom)
  {
    linesToScroll = -1;
    doSetScrollBarValue((*docStrucHdl)->vScrollbarHdl,&linesToScroll);
    if(linesToScroll != 0)
      TEScroll(0,linesToScroll * ((*editRecHdl)->lineHeight),editRecHdl);
  }

  if(linesToScroll != 0)
    doDrawDataPanel(windowPtr);

  SetClip(oldClip);
  DisposeRgn(oldClip);
  SetPort(oldPort);

  return(true);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doSetScrollBarValue

void  doSetScrollBarValue(ControlHandle controlHdl,SInt16 *linesToScroll)
{
  SInt16  controlValue, controlMax;

  controlValue = GetControlValue(controlHdl);
  controlMax = GetControlMaximum(controlHdl);

  *linesToScroll = controlValue - *linesToScroll;
  if(*linesToScroll < 0)
    *linesToScroll = 0;
  else if(*linesToScroll > controlMax)
    *linesToScroll = controlMax;

  SetControlValue(controlHdl,*linesToScroll);
  *linesToScroll = controlValue - *linesToScroll;
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doAdjustMenus

void  doAdjustMenus(void)
{
  MenuHandle           fileMenuHdl, editMenuHdl;
  WindowPtr            windowPtr;
  docStructureHandle   docStrucHdl;
  TEHandle             editRecHdl;
  SInt32               scrapOffset;

  fileMenuHdl = GetMenuHandle(mFile);
  editMenuHdl = GetMenuHandle(mEdit);

  if(gNumberOfWindows > 0)
  {
    windowPtr = FrontWindow();
    docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
    editRecHdl = (*docStrucHdl)->editRecHdl;

    EnableItem(fileMenuHdl,iClose);

    if((*editRecHdl)->selStart < (*editRecHdl)->selEnd)
    {
      EnableItem(editMenuHdl,iCut);
      EnableItem(editMenuHdl,iCopy);
      EnableItem(editMenuHdl,iClear);
    }
    else
    {
      DisableItem(editMenuHdl,iCut);
      DisableItem(editMenuHdl,iCopy);
      DisableItem(editMenuHdl,iClear);
    }

    if(GetScrap(NULL,'TEXT',&scrapOffset) > 0)
      EnableItem(editMenuHdl,iPaste);
    else
      DisableItem(editMenuHdl,iPaste);
      
    if((*editRecHdl)->teLength > 0)
    {
      EnableItem(fileMenuHdl,iSaveAs);
      EnableItem(editMenuHdl,iSelectAll);
    }
    else
    {
      DisableItem(fileMenuHdl,iSaveAs);
      DisableItem(editMenuHdl,iSelectAll);
    }
  }
  else
  {
    DisableItem(fileMenuHdl,iClose);
    DisableItem(fileMenuHdl,iSaveAs);
    DisableItem(editMenuHdl,iClear);
    DisableItem(editMenuHdl,iSelectAll);
  }    

  DrawMenuBar();
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doMenuChoice

void  doMenuChoice(SInt32 menuChoice)
{
  SInt16  menuID, menuItem;
  Str255  itemName;
  SInt16  daDriverRefNum;

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

  if(menuID == 0)
    return;

  switch(menuID)
  {
    case mApple:
      if(menuItem == iAbout)
        SysBeep(10);
      else
      {
        GetMenuItemText(GetMenuHandle(mApple),menuItem,itemName);
        daDriverRefNum = OpenDeskAcc(itemName);
      }
      break;

    case mFile:
      doFileMenu(menuItem);
      break;

    case mEdit:
      doEditMenu(menuItem);
      break;

    case kHMHelpMenuID:
      if(FrontWindow())  
        doActivateDocWindow(FrontWindow(),false);
      doHelpMenu(menuItem);
      break;
  }

  HiliteMenu(0);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doFileMenu

void  doFileMenu(SInt16 menuItem)
{
  WindowPtr            windowPtr;
  docStructureHandle   docStrucHdl;
  TEHandle             editRecHdl;

  switch(menuItem)
  {
    case iNew:
      if(windowPtr = doNewDocWindow())
        ShowWindow(windowPtr);
      break;

    case iOpen:
      doOpenCommand();
      break;

    case iClose:
      doCloseWindow(FrontWindow());
      break;

    case iSaveAs:
      docStrucHdl = (docStructureHandle) (GetWRefCon(FrontWindow()));
      editRecHdl = (*docStrucHdl)->editRecHdl;
      doSaveAsFile(editRecHdl);
      break;

    case iQuit:
      gDone = true;
      break;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doEditMenu

void  doEditMenu(SInt16 menuItem)
{
  WindowPtr            windowPtr;
  docStructureHandle   docStrucHdl;
  TEHandle             editRecHdl;
  SInt32               totalSize, contigSize, newSize, scrapOffset;
  SInt16               selectionLength;

  windowPtr = FrontWindow();
  docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
  editRecHdl = (*docStrucHdl)->editRecHdl;

  switch(menuItem)
  {
    case iUndo:
      break;

    case iCut:
      if(ZeroScrap() == noErr)
      {
        PurgeSpace(&totalSize,&contigSize);
        selectionLength = doGetSelectLength(editRecHdl);
        if(selectionLength > contigSize)
          doErrorAlert(eNoSpaceCut);
        else
        {
          TECut(editRecHdl);
          doAdjustScrollbar(windowPtr);
          if(TEToScrap() != noErr)
            ZeroScrap();
        }
      }
      break;

    case iCopy:
      if(ZeroScrap() == noErr)
      {
        TECopy(editRecHdl);
        if(TEToScrap() != noErr)
          ZeroScrap();
      }
      break;

    case iPaste:
      newSize = (*editRecHdl)->teLength + GetScrap(NULL,'TEXT',&scrapOffset);
      if(newSize > kMaxTELength)
        doErrorAlert(eNoSpacePaste);
      else
      {
        if(TEFromScrap() == noErr)
        {
          TEPaste(editRecHdl);
          doAdjustScrollbar(windowPtr);
        }
      }
      break;

    case iClear:
      TEDelete(editRecHdl);
      doAdjustScrollbar(windowPtr);
      break;

    case iSelectAll:
      TESetSelect(0,(*editRecHdl)->teLength,editRecHdl);
      break;
  }

  doDrawDataPanel(windowPtr);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doGetSelectLength

SInt16  doGetSelectLength(TEHandle editRecHdl)
{
  SInt16  selectionLength;
    
  selectionLength = (*editRecHdl)->selEnd - (*editRecHdl)->selStart;
  return(selectionLength);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doAdjustScrollbar

void  doAdjustScrollbar(WindowPtr windowPtr)
{
  docStructureHandle  docStrucHdl;
  TEHandle            editRecHdl;
  SInt16              numberOfLines, controlMax, controlValue;

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
  editRecHdl = (*docStrucHdl)->editRecHdl;

  numberOfLines = (*editRecHdl)->nLines;
  if(*(*(*editRecHdl)->hText + (*editRecHdl)->teLength - 1) == kReturn)
    numberOfLines += 1;

  controlMax = numberOfLines - (((*editRecHdl)->viewRect.bottom - 
               (*editRecHdl)->viewRect.top) /
               (*editRecHdl)->lineHeight);
  if(controlMax < 0)
    controlMax = 0;
  SetControlMaximum((*docStrucHdl)->vScrollbarHdl,controlMax);

  controlValue = ((*editRecHdl)->viewRect.top - (*editRecHdl)->destRect.top) / 
                  (*editRecHdl)->lineHeight;
  if(controlValue < 0)
    controlValue = 0;
  else if(controlValue > controlMax)
    controlValue = controlMax;

  SetControlValue((*docStrucHdl)->vScrollbarHdl,controlValue);

#if TARGET_CPU_PPC
  if(gMacOS85Present)
  {
    SetControlViewSize((*docStrucHdl)->vScrollbarHdl,(*editRecHdl)->viewRect.bottom - 
                       (*editRecHdl)->viewRect.top);
  }
#endif
  
  TEScroll(0,((*editRecHdl)->viewRect.top - (*editRecHdl)->destRect.top) - 
               (GetControlValue((*docStrucHdl)->vScrollbarHdl) *
              (*editRecHdl)->lineHeight),editRecHdl);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doAdjustCursor

void  doAdjustCursor(WindowPtr windowPtr)
{
  GrafPtr    oldPort;
  RgnHandle  arrowRegion, iBeamRegion;
  Rect       cursorRect;
  Point      mouseXY;

  if(gInBackground)
  {
    SetCursor(&qd.arrow);
    return;
  }

  GetPort(&oldPort);
  SetPort(windowPtr);

  arrowRegion = NewRgn();
  iBeamRegion = NewRgn();  
  SetRectRgn(arrowRegion,-32768,-32768,32766,32766);

  cursorRect = windowPtr->portRect;
  cursorRect.bottom -= 15;
  cursorRect.right  -= 15;
  LocalToGlobal(&topLeft(cursorRect));
  LocalToGlobal(&botRight(cursorRect));  

  RectRgn(iBeamRegion,&cursorRect);
  DiffRgn(arrowRegion,iBeamRegion,arrowRegion);
  
  GetMouse(&mouseXY);
  LocalToGlobal(&mouseXY);

  if(PtInRgn(mouseXY,iBeamRegion))
  {
    SetCursor(*(GetCursor(iBeamCursor)));
    CopyRgn(iBeamRegion,gCursorRegion);
  }
  else
  {
    SetCursor(&qd.arrow);
    CopyRgn(arrowRegion,gCursorRegion);
  }

  DisposeRgn(arrowRegion);
  DisposeRgn(iBeamRegion);

  SetPort(oldPort);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doCloseWindow

void  doCloseWindow(WindowPtr windowPtr)
{
  docStructureHandle  docStrucHdl;

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;

  DisposeControl((*docStrucHdl)->vScrollbarHdl);
  TEDispose((*docStrucHdl)->editRecHdl);
  DisposeHandle((Handle) docStrucHdl);
  DisposeWindow(windowPtr);

  gNumberOfWindows --;
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doSaveAsFile

void  doSaveAsFile(TEHandle editRecHdl)
{
  StandardFileReply  fileReply;
  WindowPtr          windowPtr;
  SInt16             fileRefNum;
  SInt32             dataLength;
  Handle             editTextHdl;

  StandardPutFile("\pSave as:","\pUntitled",&fileReply);
  if(fileReply.sfGood)
  {
    windowPtr = FrontWindow();
    SetWTitle(windowPtr,fileReply.sfFile.name);

    if(!(fileReply.sfReplacing))
      FSpCreate(&fileReply.sfFile,' KJB','TEXT',fileReply.sfScript);

    FSpOpenDF(&fileReply.sfFile,fsCurPerm,&fileRefNum);

    dataLength = (*editRecHdl)->teLength;
    editTextHdl = (*editRecHdl)->hText;
    FSWrite(fileRefNum,&dataLength,*editTextHdl);

    FSClose(fileRefNum);
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doOpenCommand

void  doOpenCommand(void)
{
  StandardFileReply  fileReply;
  SFTypeList         fileTypes;  

  fileTypes[0] = 'TEXT';

  StandardGetFile(NULL,1,fileTypes,&fileReply);
  if(fileReply.sfGood)
    doOpenFile(fileReply.sfFile);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doOpenFile

void  doOpenFile(FSSpec fileSpec)
{
  WindowPtr           windowPtr;
  docStructureHandle  docStrucHdl;
  TEHandle            editRecHdl;
  SInt16              fileRefNum;
  SInt32              textLength;
  Handle              textBuffer;

  if((windowPtr = doNewDocWindow()) == NULL)
    return;

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
  editRecHdl = (*docStrucHdl)->editRecHdl;

  SetWTitle(windowPtr,fileSpec.name);

  FSpOpenDF(&fileSpec,fsCurPerm,&fileRefNum);

  SetFPos(fileRefNum,fsFromStart,0);
  GetEOF(fileRefNum,&textLength);

  if(textLength > 32767)
    textLength = 32767;

  textBuffer = NewHandle((Size) textLength);

  FSRead(fileRefNum,&textLength,*textBuffer);

  MoveHHi(textBuffer);
  HLock(textBuffer);

  TESetText(*textBuffer,textLength,editRecHdl);

  HUnlock(textBuffer);
  DisposeHandle(textBuffer);

  FSClose(fileRefNum);

  (*editRecHdl)->selStart = 0;
  (*editRecHdl)->selEnd = 0;

  ShowWindow(windowPtr);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doDrawDataPanel

void  doDrawDataPanel(WindowPtr windowPtr)
{
  docStructureHandle   docStrucHdl;
  TEHandle             editRecHdl;
  RGBColor             whiteColour = { 0xFFFF, 0xFFFF, 0xFFFF };          
  RGBColor             blackColour = { 0x0000, 0x0000, 0x0000 };          
  RGBColor             blueColour = { 0x1818, 0x4B4B, 0x8181 };
  ControlHandle        controlHdl;
  Rect                 panelRect;
  Str255               textString;
        
  docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
  editRecHdl = (*docStrucHdl)->editRecHdl;
  controlHdl = (*docStrucHdl)->vScrollbarHdl;

  MoveTo(0,282);
  LineTo(495,282);

  RGBForeColor(&whiteColour);
  RGBBackColor(&blueColour);
  SetRect(&panelRect,0,283,495,300);
  EraseRect(&panelRect);

  MoveTo(3,295);
  DrawString("\pteLength               nLines          lineHeight");

  MoveTo(225,295);
  DrawString("\pdestRect.top              controlValue           contrlMax");

  SetRect(&panelRect,47,284,88,299);
  EraseRect(&panelRect);  
  SetRect(&panelRect,124,284,149,299);
  EraseRect(&panelRect);  
  SetRect(&panelRect,204,284,222,299);
  EraseRect(&panelRect);  
  SetRect(&panelRect,286,284,323,299);
  EraseRect(&panelRect);  
  SetRect(&panelRect,389,284,416,299);
  EraseRect(&panelRect);  
  SetRect(&panelRect,472,284,495,299);
  EraseRect(&panelRect);  

  NumToString((SInt32) (*editRecHdl)->teLength,textString);
  MoveTo(47,295);
  DrawString(textString);

  NumToString((SInt32) (*editRecHdl)->nLines,textString);
  MoveTo(124,295);
  DrawString(textString);

  NumToString((SInt32) (*editRecHdl)->lineHeight,textString);
  MoveTo(204,295);
  DrawString(textString);

  NumToString((SInt32) (*editRecHdl)->destRect.top,textString);
  MoveTo(286,295);
  DrawString(textString);

  NumToString((SInt32) GetControlValue(controlHdl),textString);
  MoveTo(389,295);
  DrawString(textString);

  NumToString((SInt32) GetControlMaximum(controlHdl),textString);
  MoveTo(472,295);
  DrawString(textString);

  RGBForeColor(&blackColour);
  RGBBackColor(&whiteColour);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doErrorAlert

void  doErrorAlert(SInt16 errorCode)
{
  AlertStdAlertParamRec  paramRec;
  Str255                 errorString;
  SInt16                 itemHit;

  paramRec.movable         = true;
  paramRec.helpButton      = false;
  paramRec.filterProc      = NULL;
  paramRec.defaultText     = (StringPtr) kAlertDefaultOKText;
  paramRec.cancelText      = NULL;
  paramRec.otherText       = NULL;
  paramRec.defaultButton   = kAlertStdAlertOKButton;
  paramRec.cancelButton    = 0;
  paramRec.position        = kWindowDefaultPosition;

  GetIndString(errorString,rErrorStrings,errorCode);

  if(errorCode < eWindow)
  {
    StandardAlert(kAlertStopAlert,errorString,NULL,¶mRec,&itemHit);
    ExitToShell();
  }
  else
    StandardAlert(kAlertCautionAlert,errorString,NULL,¶mRec,&itemHit);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doHelpMenu

void  doHelpMenu(SInt16 menuItem)
{
  MenuHandle  helpMenuHdl;
  SInt16      origHelpItems, numItems;

  HMGetHelpMenuHandle(&helpMenuHdl);

  numItems = CountMenuItems(helpMenuHdl);
  origHelpItems = numItems - 1;

  if(menuItem > origHelpItems)
    doHelp();  
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// HelpDialog.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

#include <Appearance.h>
#include <ControlDefinitions.h>
#include <Dialogs.h>
#include <Resources.h>
#include <TextUtils.h>

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

#define rHelpModal             128
#define  iUserPane             2
#define  iScrollBar            3
#define  iPopupMenu            4
#define  eHelpDialog           9
#define  eHelpDocRecord        10
#define  eHelpText             11
#define  eHelpPicture          12
#define rTextIntroduction      128
#define rTextCreatingText      129
#define rTextModifyHelp        130
#define rPictIntroductionBase  128
#define rPictCreatingTextBase  129
#define kTextInset             4

// ............................................................................. typedefs

typedef struct
{
  Rect      bounds;
  PicHandle  pictureHdl;
} pictInfoStructure;

typedef struct
{
  TEHandle           editRecHdl;
  ControlHandle      scrollbarHdl;
  SInt16             pictCount;
  pictInfoStructure  *pictInfoStructurePtr;
}  docStructure, ** docStructureHandle;

typedef struct
{
  RGBColor       backColour;
  PixPatHandle   backPixelPattern;
  Pattern        backBitPattern;
} backColourPattern;

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

ModalFilterUPP           eventFilterUPP;
ControlUserPaneDrawUPP   userPaneDrawFunctionUPP;
ControlActionUPP         actionFunctionUPP;
backColourPattern        gBackColourPattern;
SInt16                   gTextResourceID;
SInt16                   gPictResourceBaseID;
RgnHandle                gSavedClipRgn  = NULL;

extern Boolean          gMacOS85Present;

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

void            doHelp                 (void);
void            doCloseHelp            (DialogPtr,GrafPtr);
void            doDisposeDescriptors   (void);
pascal void     userPaneDrawFunction   (ControlHandle,SInt16);
Boolean         doGetText              (DialogPtr,SInt16,Rect);
Boolean         doGetPictureInfo       (DialogPtr,SInt16);
pascal void     actionFunction         (ControlHandle,SInt16);
void            doScrollTextAndPicts   (DialogPtr);
void            doDrawPictures         (DialogPtr,Rect *);
pascal Boolean  eventFilter            (DialogPtr,EventRecord *,SInt16 *);
void            doSaveBackground       (backColourPattern *);
void            doRestoreBackground    (backColourPattern *);
void            doSetBackgroundWhite   (void);

extern void     doUpdate               (EventRecord *);
extern void     doErrorAlert           (SInt16);

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doHelp

void  doHelp(void)
{
  DialogPtr            dialogPtr;
  docStructureHandle   docStrucHdl;
  GrafPtr              oldPort;
  ControlHandle        controlHdl;
  SInt16               itemHit, menuItem;
  Rect                 userItemRect, destRect, viewRect;

  // ......................................................... create routine descriptors

  eventFilterUPP          = NewModalFilterProc((ProcPtr) eventFilter);
  userPaneDrawFunctionUPP  = NewControlUserPaneDrawProc((ProcPtr) userPaneDrawFunction);
  actionFunctionUPP        = NewControlActionProc((ProcPtr) actionFunction);

  // ........................................ create dialog and attach document structure

  if(!(dialogPtr = GetNewDialog(rHelpModal,NULL,(WindowPtr) -1)))
  {
    doErrorAlert(eHelpDialog);
    doDisposeDescriptors();
    return;
  }

  if(!(docStrucHdl = (docStructureHandle) NewHandle(sizeof(docStructure))))
  {
    doErrorAlert(eHelpDocRecord);
    DisposeDialog(dialogPtr);
    doDisposeDescriptors();
    return;
  }

  SetWRefCon(dialogPtr,(SInt32) docStrucHdl);

  // ............................................... set graphics port and default button
  
  GetPort(&oldPort);
  SetPort(dialogPtr);
  SetDialogDefaultItem(dialogPtr,kStdOkItemIndex);

  // ..................................................... set user pane drawing function
  
  GetDialogItemAsControl(dialogPtr,iUserPane,&controlHdl);
  SetControlData(controlHdl,kControlNoPart,kControlUserPaneDrawProcTag, 
                 sizeof(userPaneDrawFunctionUPP),(Ptr) &userPaneDrawFunctionUPP);

  // ......................... set destination and view rectangles, create edit structure

  userItemRect = (*controlHdl)->contrlRect;
  InsetRect(&userItemRect,kTextInset,kTextInset / 2);
  destRect = viewRect = userItemRect;
  (*docStrucHdl)->editRecHdl = TEStyleNew(&destRect,&viewRect);

  // ................... assign handle to scroll bar to relevant document structure field 

  GetDialogItemAsControl(dialogPtr,iScrollBar,&controlHdl);
  (*docStrucHdl)->scrollbarHdl = controlHdl;

  // ............... initialise picture information structure field of document structure 

  (*docStrucHdl)->pictInfoStructurePtr = NULL;

  // ....................... assign resource IDs of first topic's 'TEXT'/'styl' resources
  
  gTextResourceID      = rTextIntroduction;
  gPictResourceBaseID  = rPictIntroductionBase;

  // ................................. load text resources and insert into edit structure

  if(!(doGetText(dialogPtr,gTextResourceID,viewRect)))
  {
    doCloseHelp(dialogPtr,oldPort);
    doDisposeDescriptors();
    return;
  }

  // .... search for option-space charas in text and load same number of 'PICT' resources

  if(!(doGetPictureInfo(dialogPtr,gPictResourceBaseID)))
  {
    doCloseHelp(dialogPtr,oldPort);
    doDisposeDescriptors();
    return;
  }

  // .......................... create an empty region for saving the old clipping region
    
  gSavedClipRgn = NewRgn();

  // ................................. show window and save background colour and pattern

  ShowWindow(dialogPtr);

  doSaveBackground(&gBackColourPattern);

  // ............................................................. enter ModalDialog loop

  do 
  {
    ModalDialog(eventFilterUPP,&itemHit);
  
    if(itemHit == iPopupMenu)
    {
      SetControlValue((*docStrucHdl)->scrollbarHdl,0);

      GetDialogItemAsControl(dialogPtr,iPopupMenu,&controlHdl);
      menuItem = GetControlValue(controlHdl);
      
      switch(menuItem)
      {
        case 1:
          gTextResourceID     = rTextIntroduction;
          gPictResourceBaseID = rPictIntroductionBase;
          break;
        
        case 2:
          gTextResourceID     = rTextCreatingText;
          gPictResourceBaseID = rPictCreatingTextBase;
          break;

        case 3:
          gTextResourceID     = rTextModifyHelp;
          break;
      }
  
      doSetBackgroundWhite();
      
      if(!(doGetText(dialogPtr,gTextResourceID,viewRect)))
      {
        doCloseHelp(dialogPtr,oldPort);
        doDisposeDescriptors();
        return;
      }
      if(!(doGetPictureInfo(dialogPtr,gPictResourceBaseID)))
      {
        doCloseHelp(dialogPtr,oldPort);
        doDisposeDescriptors();
        return;
      }

      doDrawPictures(dialogPtr,&viewRect);
      
      doRestoreBackground(&gBackColourPattern);
    }
      
  } while(itemHit != kStdOkItemIndex);

  // ........................................................................... clean up
    
  doCloseHelp(dialogPtr,oldPort);
  doDisposeDescriptors();
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doCloseHelp

void  doCloseHelp(DialogPtr dialogPtr,GrafPtr oldPort)
{
  docStructureHandle  docStrucHdl;
  TEHandle            editRecHdl;
  SInt16              a;
  
  docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
  editRecHdl = (*docStrucHdl)->editRecHdl;

  if(gSavedClipRgn)
    DisposeRgn(gSavedClipRgn);

  if((*docStrucHdl)->editRecHdl)
    TEDispose((*docStrucHdl)->editRecHdl);

  if((*docStrucHdl)->pictInfoStructurePtr)
  {
    for(a=0;a<(*docStrucHdl)->pictCount;a++)
      ReleaseResource((Handle) (*docStrucHdl)->pictInfoStructurePtr[a].pictureHdl);
    DisposePtr((Ptr) (*docStrucHdl)->pictInfoStructurePtr);
  }

  DisposeHandle((Handle) docStrucHdl);
  DisposeDialog(dialogPtr);

  SetPort(oldPort);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doDisposeDescriptors

void  doDisposeDescriptors(void)
{
  DisposeRoutineDescriptor(eventFilterUPP);
  DisposeRoutineDescriptor(userPaneDrawFunctionUPP);
  DisposeRoutineDescriptor(actionFunctionUPP);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× userPaneDrawFunction

pascal void  userPaneDrawFunction(ControlHandle controlHdl,SInt16 thePart)
{
  Rect                 itemRect, viewRect;
  docStructureHandle   docStrucHdl;
  TEHandle             editRecHdl;
  DialogPtr            dialogPtr;
  Boolean              inState;

  dialogPtr = (*controlHdl)->contrlOwner;
  itemRect = (*controlHdl)->contrlRect;
  InsetRect(&itemRect,1,1);
  itemRect.right += 15;

  DrawThemeListBoxFrame(&itemRect,kThemeStateActive);
  if(((WindowPeek) dialogPtr)->visible)
    inState = ((WindowPeek) dialogPtr)->hilited;
  DrawThemeListBoxFrame(&itemRect,inState);

  doSetBackgroundWhite();
  EraseRect(&itemRect);
  
  docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
  editRecHdl = (*docStrucHdl)->editRecHdl;
  viewRect = (*editRecHdl)->viewRect;

  TEUpdate(&viewRect,editRecHdl);
  doDrawPictures(dialogPtr,&viewRect);

  doRestoreBackground(&gBackColourPattern);  
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doGetText

Boolean  doGetText(DialogPtr dialogPtr,SInt16 textResourceID,Rect viewRect)
{
  docStructureHandle  docStrucHdl;
  TEHandle            editRecHdl;
  Handle              helpTextHdl;
  StScrpHandle        stylScrpRecHdl;
  SInt16              numberOfLines, heightOfText, heightToScroll;

  docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
  editRecHdl = (*docStrucHdl)->editRecHdl;

  TESetSelect(0,32767,editRecHdl);
  TEDelete(editRecHdl);
  
  (*editRecHdl)->destRect = (*editRecHdl)->viewRect;
  SetControlValue((*docStrucHdl)->scrollbarHdl,0);
      
  helpTextHdl = GetResource('TEXT',textResourceID);
  if(helpTextHdl == NULL)
  {
    doErrorAlert(eHelpText);
    return false;
  }

  stylScrpRecHdl = (StScrpHandle) GetResource('styl',textResourceID);
  if(stylScrpRecHdl == NULL)
  {
    doErrorAlert(eHelpText);
    return false;
  }
    
  TEStyleInsert(*helpTextHdl,GetHandleSize(helpTextHdl),stylScrpRecHdl,editRecHdl);

  ReleaseResource(helpTextHdl);
  ReleaseResource((Handle) stylScrpRecHdl);

  numberOfLines = (*editRecHdl)->nLines;
  heightOfText = TEGetHeight((SInt32) numberOfLines,1,editRecHdl);

  if(heightOfText > (viewRect.bottom - viewRect.top))
  {
    heightToScroll = TEGetHeight((SInt32) numberOfLines,1,editRecHdl) -
                                 (viewRect.bottom - viewRect.top);
    SetControlMaximum((*docStrucHdl)->scrollbarHdl,heightToScroll);
    ActivateControl((*docStrucHdl)->scrollbarHdl);
#if TARGET_CPU_PPC
    if(gMacOS85Present)
    {
      SetControlViewSize((*docStrucHdl)->scrollbarHdl,(*editRecHdl)->viewRect.bottom - 
                         (*editRecHdl)->viewRect.top);
    }
#endif
  }
  else
  {
    DeactivateControl((*docStrucHdl)->scrollbarHdl);
  }

  return true;
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doGetPictureInfo

Boolean  doGetPictureInfo(DialogPtr dialogPtr,SInt16 firstPictID)
{
  docStructureHandle   docStrucHdl;
  TEHandle             editRecHdl;
  Handle               textHdl;
  SInt32               offset, textSize;
  SInt16               numberOfPicts, a, lineHeight, fontAscent;
  SInt8                optionSpace[1] = "\xCA";
  pictInfoStructure    *pictInfoPtr;
  Point                picturePoint;
  TextStyle            whatStyle;
  
  docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);

  if((*docStrucHdl)->pictInfoStructurePtr != NULL)
  {
    for(a=0;a<(*docStrucHdl)->pictCount;a++)
      ReleaseResource((Handle) (*docStrucHdl)->pictInfoStructurePtr[a].pictureHdl);

    DisposePtr((Ptr) (*docStrucHdl)->pictInfoStructurePtr);
    (*docStrucHdl)->pictInfoStructurePtr = NULL;
  }

  (*docStrucHdl)->pictCount = 0;

  editRecHdl = (*docStrucHdl)->editRecHdl;
  textHdl = (*editRecHdl)->hText;

  textSize = GetHandleSize(textHdl);
  offset = 0;
  numberOfPicts = 0;

  HLock(textHdl);

  offset = Munger(textHdl,offset,optionSpace,1,NULL,0);
  while((offset >= 0) && (offset <= textSize))
  {
    numberOfPicts++;
    offset++;
    offset = Munger(textHdl,offset,optionSpace,1,NULL,0);    
  }
  
  if(numberOfPicts == 0)
  {
    HUnlock(textHdl);
    return true;
  }
    
  pictInfoPtr = (pictInfoStructure *) NewPtr(sizeof(pictInfoStructure) * numberOfPicts);
  (*docStrucHdl)->pictInfoStructurePtr = pictInfoPtr;

  offset = 0L;

  for(a=0;a<numberOfPicts;a++)
  {
    pictInfoPtr[a].pictureHdl = GetPicture(firstPictID + a);
    if(pictInfoPtr[a].pictureHdl == NULL)
    {
      doErrorAlert(eHelpPicture);
      return false;
    }

    offset = Munger(textHdl,offset,optionSpace,1,NULL,0);
    picturePoint = TEGetPoint((SInt16)offset,editRecHdl);

    TEGetStyle(offset,&whatStyle,&lineHeight,&fontAscent,editRecHdl);
    picturePoint.v -= lineHeight;
    offset++;
    pictInfoPtr[a].bounds = (**pictInfoPtr[a].pictureHdl).picFrame;

    OffsetRect(&pictInfoPtr[a].bounds,
               (((*editRecHdl)->destRect.right + (*editRecHdl)->destRect.left) -
               (pictInfoPtr[a].bounds.right + pictInfoPtr[a].bounds.left) ) / 2,
               - pictInfoPtr[a].bounds.top + picturePoint.v);
  }
  
  (*docStrucHdl)->pictCount = a;
  
  HUnlock(textHdl);

  return true;
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× actionFunction

pascal void  actionFunction(ControlHandle scrollbarHdl,SInt16 partCode)
{
  docStructureHandle   docStrucHdl;
  DialogPtr            dialogPtr;
  TEHandle             editRecHdl;
  SInt16               delta, oldValue, offset, lineHeight, fontAscent;
  Point                thePoint;
  Rect                 viewRect;
  TextStyle            style;

  if(partCode)
  {
    dialogPtr = (*scrollbarHdl)->contrlOwner;
    docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
    editRecHdl = (*docStrucHdl)->editRecHdl;
    viewRect = (*editRecHdl)->viewRect;
    thePoint.h = viewRect.left + kTextInset;
    
    if(partCode != kControlIndicatorPart)
    {    
      switch(partCode)
      {
        case kControlUpButtonPart:
          thePoint.v = viewRect.top - 4;
          offset = TEGetOffset(thePoint,editRecHdl);
          thePoint = TEGetPoint(offset,editRecHdl);
          TEGetStyle(offset,&style,&lineHeight,&fontAscent,editRecHdl);
          delta = thePoint.v - lineHeight - viewRect.top;
          break;

        case kControlDownButtonPart:
          thePoint.v = viewRect.bottom + 2;
          offset = TEGetOffset(thePoint,editRecHdl);
          thePoint = TEGetPoint(offset,editRecHdl);
          delta = thePoint.v - viewRect.bottom;
          break;

        case kControlPageUpPart:
          thePoint.v = viewRect.top + 2;
          offset = TEGetOffset(thePoint,editRecHdl);
          thePoint = TEGetPoint(offset,editRecHdl);
          TEGetStyle(offset,&style,&lineHeight,&fontAscent,editRecHdl);
          thePoint.v += lineHeight - fontAscent;
          thePoint.v -= viewRect.bottom - viewRect.top;
          offset = TEGetOffset(thePoint,editRecHdl);
          thePoint = TEGetPoint(offset,editRecHdl);
          TEGetStyle(offset,&style,&lineHeight,&fontAscent,editRecHdl);
          delta = thePoint.v - viewRect.top;
          if(offset == 0)
            delta -= lineHeight;
          break;
        
        case kControlPageDownPart:
          thePoint.v = viewRect.bottom - 2;
          offset = TEGetOffset(thePoint,editRecHdl);
          thePoint = TEGetPoint(offset,editRecHdl);
          TEGetStyle(offset,&style,&lineHeight,&fontAscent,editRecHdl);
          thePoint.v -= fontAscent;
          thePoint.v += viewRect.bottom - viewRect.top;
          offset = TEGetOffset(thePoint,editRecHdl);
          thePoint = TEGetPoint(offset,editRecHdl);
          TEGetStyle(offset,&style,&lineHeight,&fontAscent,editRecHdl);
          delta =  thePoint.v - lineHeight - viewRect.bottom;
          if(offset == (**editRecHdl).teLength)
            delta += lineHeight;
          break;
      }

      oldValue = GetControlValue(scrollbarHdl);

      if(((delta < 0) && (oldValue > 0)) || ((delta > 0) && 
         (oldValue < GetControlMaximum(scrollbarHdl))))
      {
         GetClip(gSavedClipRgn);
        ClipRect(&dialogPtr->portRect);

        SetControlValue(scrollbarHdl,oldValue + delta);
        SetClip(gSavedClipRgn);
      }
    }
    
    doScrollTextAndPicts(dialogPtr);
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doScrollTextAndPicts

void  doScrollTextAndPicts(DialogPtr dialogPtr)
{
  docStructureHandle  docStrucHdl;
  TEHandle            editRecHdl;
  SInt16              scrollDistance, oldScroll;
  Rect                updateRect;

  doSetBackgroundWhite();

  docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
  editRecHdl = (*docStrucHdl)->editRecHdl;

  oldScroll = (*editRecHdl)->viewRect.top -(*editRecHdl)->destRect.top;
  scrollDistance = oldScroll - GetControlValue((*docStrucHdl)->scrollbarHdl);
  if(scrollDistance == 0)
  {
    doRestoreBackground(&gBackColourPattern);  
    return;
  
  }
  TEScroll(0,scrollDistance,editRecHdl);

  if((*docStrucHdl)->pictCount == 0)
  {
    doRestoreBackground(&gBackColourPattern);  
    return;
  }
    
  updateRect = (*editRecHdl)->viewRect;

  if(scrollDistance > 0)
  {
    if(scrollDistance < (updateRect.bottom - updateRect.top))
      updateRect.bottom = updateRect.top + scrollDistance;
  }
  else
  {
    if( - scrollDistance < (updateRect.bottom - updateRect.top))
      updateRect.top = updateRect.bottom + scrollDistance;
  }

  doDrawPictures(dialogPtr,&updateRect);

  doRestoreBackground(&gBackColourPattern);  
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doDrawPictures

void  doDrawPictures(DialogPtr dialogPtr,Rect *updateRect)
{
  docStructureHandle   docStrucHdl;
  TEHandle             editRecHdl;
  SInt16               pictCount, pictIndex, vOffset;
  PicHandle            thePictHdl;
  Rect                 pictLocRect, dummyRect;

  docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
  editRecHdl = (*docStrucHdl)->editRecHdl;

  vOffset = (*editRecHdl)->destRect.top - (*editRecHdl)->viewRect.top - kTextInset;
  pictCount = (*docStrucHdl)->pictCount;

  for(pictIndex = 0;pictIndex < pictCount;pictIndex++)
  {
    pictLocRect = (*docStrucHdl)->pictInfoStructurePtr[pictIndex].bounds;
    OffsetRect(&pictLocRect,0,vOffset);

    if(!SectRect(&pictLocRect,updateRect,&dummyRect))
      continue;

    thePictHdl = (*docStrucHdl)->pictInfoStructurePtr[pictIndex].pictureHdl;

    LoadResource((Handle) thePictHdl);
    HLock((Handle) thePictHdl);

    GetClip(gSavedClipRgn);
    ClipRect(updateRect);
    DrawPicture(thePictHdl,&pictLocRect);

    SetClip(gSavedClipRgn);
    HUnlock((Handle) thePictHdl);
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× eventFilter

pascal Boolean  eventFilter(DialogPtr dialogPtr,EventRecord *eventStrucPtr,
                            SInt16 *itemHit)
{
  Boolean              handledEvent;
  GrafPtr              oldPort;
  Point                mouseXY;
  ControlHandle        controlHdl;
  docStructureHandle   docStrucHdl;

  handledEvent = false;

  if((eventStrucPtr->what == updateEvt) && 
     ((WindowPtr) eventStrucPtr->message != dialogPtr))
  {
    doUpdate(eventStrucPtr);
  }
  else
  {
    GetPort(&oldPort);
    SetPort(dialogPtr);

    if(eventStrucPtr->what == mouseDown)
    {
      mouseXY = eventStrucPtr->where;
      GlobalToLocal(&mouseXY);
      if(FindControl(mouseXY,dialogPtr,&controlHdl))
      {
        docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
        if(controlHdl == (*docStrucHdl)->scrollbarHdl)
        {
          TrackControl((*docStrucHdl)->scrollbarHdl,mouseXY,actionFunctionUPP);  
          *itemHit = iScrollBar;
          handledEvent = true;
        }
      }
    }
    else
    {
      handledEvent = StdFilterProc(dialogPtr,eventStrucPtr,itemHit);
    }

    SetPort(oldPort);
  }

  return(handledEvent);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doSaveBackground

void  doSaveBackground(backColourPattern *gBackColourPattern)
{
  GrafPtr  currentPort;
  
  GetPort(¤tPort);

  GetBackColor(&gBackColourPattern->backColour);
  gBackColourPattern->backPixelPattern  = NULL;

  if((**((CGrafPtr) currentPort)->bkPixPat).patType != 0)
    gBackColourPattern->backPixelPattern = ((CGrafPtr) currentPort)->bkPixPat;
  else
    gBackColourPattern->backBitPattern = 
                             *(PatPtr) (*(**((CGrafPtr) currentPort)->bkPixPat).patData);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doRestoreBackground

void  doRestoreBackground(backColourPattern *gBackColourPattern)
{
  RGBBackColor(&gBackColourPattern->backColour);

  if(gBackColourPattern->backPixelPattern)
    BackPixPat(gBackColourPattern->backPixelPattern);
  else
    BackPat(&gBackColourPattern->backBitPattern);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doSetBackgroundWhite

void  doSetBackgroundWhite(void)
{
  RGBColor  whiteColour = { 0xFFFF, 0xFFFF, 0xFFFF };

  RGBBackColor(&whiteColour);
  BackPat(&qd.white);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

Demonstration Program 1 Comments

When this program is run, the user should explore both the text editor and the Help
dialog.

Text Editor

In the text editor, the user should perform all the actions usually associated with a
simple text editor, that is:

*   Open a new document window, open an existing 'TEXT' file for display in a new 
    document window, and save a document to a 'TEXT' file.

*   Enter new text and use the Edit menu Cut, Copy, Paste, and Clear commands to edit
    the text.  (Pasting between documents and other applications is supported.)

*   Select text by clicking and dragging, double-clicking a word, shift-clicking, and 
    choosing the Select All command from the Edit menu.  Also select large amounts of
    text by clicking in the text and dragging the cursor above or below the window so as
    to invoke auto-scrolling.

*   Scroll a large document by dragging the scroll box (live scrolling is used), 
    clicking once in a scroll arrow or gray area, and holding the mouse down in a scroll
    arrow or gray area.

Whenever any action is taken, the user should observe the changes to the values displayed
in the data panel at the bottom of each window.  In particular, the relationship between
the destination rectangle and scroll bar control value should be noted.

The user should also note that outline highlighting is activated for all windows and that
the forward-delete key is supported by the application.  (The forward-delete key is not
supported by TextEdit.)

Help Dialog

The user should choose Text1 Help from the Help menu to open the Help dialog and then
scroll through the three help topics, which may be chosen in the pop-up menu at the
bottom of the dialog.  The help topics contain documentation on the Help dialog which
supplements the source code comments below.

Text1.c

#define

kMaxTELength represents the maximum allowable number of bytes in a TextEdit edit
structure.  kTab, kDel, and kReturn representing the character codes generated by the
tab, delete and return keys.

#typedef

The docStructure data type will be used for a small document structure comprising a
handle to an edit structure and a handle to a (vertical) scroll bar.

Global Variables

scrollActionFunctionUPP will be assigned a universal procedure pointer to an action 
function for the scroll bar.  customClickLoopUPP will be assigned a universal procedure 
pointer to a custom click loop function.  gMacOS85Present will be assigned true if Mac OS 
8.5 or later is present.  gCursorRegion is related to the WaitNextEvent's mouseRgn 
parameter.  gNumberOfWindows will keep track of the number of windows open at any one time.
gOldControlValue will be assigned the scroll bar's control value.

main

The main function initialises the system software managers, sets up the menus, opens 
a new document window and enters the event loop.  gMacOS85Present is assigned true of Mac 
OS 8.5 or later is present.  This global variable will govern whether certain Control 
Manager functions relating to proportional scroll boxes, and which are available only in 
Mac OS 8.5 or later, are called.

eventLoop

Note that WaitNextEvent's sleep parameter is assigned the value returned by the call to
LMGetCaretTime.

If WaitNextEvent returns a null event, and provided at least one window is open, the
application-defined function doIdle is called.

doIdle

doIdle is called whenever a NULL event is received.

The first line gets a pointer to the front window, allowing the next line to attempt to
retrieve a handle to that window's document structure.  If the attempt is successful,
TEIdle is called to blink the insertion point.

doEvents

Note that, in the case of a mouse-down event in the content area, the application-defined
function doInContent is called and that, in the case of key events, the
application-defined function doKeyEvent is called provided the key press is not a Command
key equivalent.

doKeyEvent

doKeyEvent handles all key-down events that are not Command key equivalents.  

The first three lines get a handle to the edit structure which forms part of the front
window's document structure.

The next line filters out the tab key character code.  (TextEdit does not support the tab
key and some applications may need to provide a tab key handler.)

The next character code to be filtered out is the forward-delete key character code. 
TextEdit does not recognise this key, so this else if block provides forward-delete key
support for the program.  The first line in this block gets the current selection length
from the edit structure.  If this is zero (that is, there is no selection range and an
insertion point is being displayed), the selEnd field is increased by one.  This, in
effect, creates a selection range comprising the character following the insertion point. 
TEDelete deletes the current selection range from the edit structure.  Such deletions
could change the number of text lines in the edit structure, requiring the vertical
scroll bar to be adjusted; hence the call to the application-defined function
doAdjustScrollbar.

Processing of those character codes which have not been filtered out is performed in the
else block.  A new character must not be allowed to be inserted in the edit structure if
the TextEdit limit of 32,767 characters will be exceeded.  Accordingly, and given that
TEKey replaces the selection range with the character passed to it, the first step is to
get the current selection length.  If the current length of the edit structure minus the
selection length plus 1 is less than 32,767, the character code is passed to TEKey for
insertion into the edit structure.  In addition, and since all this could change the
number of lines in the edit structure, the scroll bar adjustment function is called.

If the TextEdit limit will be exceeded by accepting the character, an alert box is
invoked advising the user of the situation.

The last line calls the application-defined function which prints data extracted from the
edit text and control structures at the bottom of the window.

scrollActionFunction

scrollActionFunction is associated with the vertical scroll bar.  It is the hook function
which will be repeatedly called by TrackControl while the mouse button remains down in
the scroll box, scroll arrows or gray areas of the vertical scroll bar.

The first line gets a pointer to the window which "owns" the control.  The next two lines
get a handle to the edit structure associated with the window.

Within the outer if block, the first if block executes if the control part is not the 
scroll box (indicator).  The purpose of the switch is to get a value into the variable 
linesToScroll.  If the mouse-down was in a scroll arrow, that value will be 1.  If the 
mouse-down was in a gray area, that value will be equivalent to one less than the number 
of text lines that will fit in the view rectangle.  (Subtracting 1 from the total number 
of lines that will fit in the view rectangle ensures that the line of text at the 
bottom/top of the view rectangle prior to a gray area scroll will be visible at the 
top/bottom of the window after the scroll.)

Immediately after the switch, the value in linesToScroll is changed to a negative value
if the mouse-down occurred in either the down scroll arrow or down gray area.

The next block ensures that no scrolling action will occur if the document is currently
scrolled fully up (control value equals control maximum) or fully down (control value
equals 0).  In either case, linesToScroll will be set to 0, meaning that the call to
TEScroll near the end of the function will not occur.

SetControlValue sets the control value to the value just previously calculated, that is,
to the current control value minus the value in linesToScroll.

The next line sets the value in linesToScroll back to what it was before the line
linesToScroll = controlvalue - linesToScroll executed. This value, multiplied by the
value in the lineHeight field of the edit structure, is later passed to TEScroll as the
parameter which specifies the number of pixels to scroll.

If the control part is the scroll box (indicator), the variable linesToScroll is assigned
a value equal to the control's value as it was last time this function was called minus
the control's current value.  The global variable which holds the control's "old" value
is then assigned the control's current value preparatory to the next call to this
function.

With the number of lines to scroll determined, TEScroll is called to scroll the text
within the view rectangle by the number of pixels specified in the second parameter. 
(Positive values scroll the text towards the bottom of the screen.  Negative values
scroll the text towards the top.)

The last line is for demonstration purposes only.  It calls the application-defined
function which prints data extracted from the edit and control structures at the bottom
of the window.

doInContent

doInContent continues mouse-down processing.

The first three lines retrieve a handle to the edit structure associated with that
window.

The next two lines convert the mouse-down coordinates from global to local coordinates. 
(Local coordinates will be required by upcoming calls to FindControl, TrackControl, and
PtInRect.)

If the mouse-down was in a control (that is, the scroll bar), the global variable
gOldControlValue is assigned the value of the control to cater for the case of the
mouse-down being within the scroll box (indicator), and TrackControl is called with the
universal procedure pointer to the application defined action function passed in the
third parameter.  TrackControl retains control until the mouse button is released, during
which time the previously described action function scrollActionFunction is repeatedly
called.

If the mouse-down was not in a control, PtInRect checks whether it occurred within the
view rectangle.  (Note that the view rectangle is in local coordinates, so the mouse-down
coordinates passed as the first parameter to the PtInRect call must also be in local
coordinates.)  If the mouse-down was in the view rectangle, a check is made of the shift
key position at the time of the mouse-down.  The result is passed as the second parameter
in the call to TEClick.  (TEClick's behaviour depends on the position of the shift key.)

doUpdate

doUpdate handles update events.  The first three lines get the handle to the edit
structure associated with the window.

Between the usual BeginUpdate and EndUpdate calls, TEUpdate is called to draw the text in
the edit structure, UpdateControls is called to draw the scroll bar, and the data panel
is redrawn.

doActivateDocWindow

doActivateDocWindow handles window activation/deactivation.

The first two lines retrieve a handle to the edit structure for the window.

If the window is becoming active, its graphics port is set as the current graphics port. 
The bottom of the view rectangle is then adjusted so that the height of the view
rectangle is an exact multiple of the value in the lineHeight field of the edit
structure.  (This avoids the possibility of only part of the full height of a line of
text appearing at the bottom of the view rectangle.)  TEActivate activates the edit
structure associated with the window, ActivateControl activates the scroll bar, and
doAdjustScrollbar adjusts the scroll bar.

If the window is becoming inactive, TEDeactivate deactivates the edit structure
associated with the window and DeactivateControl deactivates the scroll bar.
doNewDocWindow

doNewDocWindow is called at program launch and when the user chooses New or Open from the
File menu.  It opens a new window, attaches a document structure to that window, creates
a vertical scroll bar, creates a monostyled edit structure, installs the custom click
loop function, enables automatic scrolling, and enables outline highlighting.

GetNewCWindow opens a new window and sets its colour graphics port as the current colour
graphics port.  (Since the edit structure assumes the drawing environment specified in
the colour graphics port structure, setting the colour graphics port must be done before
the call to TENew to create the edit structure.)

The next two lines set the text size and font.  (These will be copied from the colour
graphics port to the edit structure when TENew is called.)

The next block creates a document structure and assigns a handle to that structure to the
window structure's refCon field.  The following line increments the global variable which
keeps track of the number of open windows.  GetNewControl creates a vertical scroll bar
and assigns a handle to it to the appropriate field of the document structure.  The next
block establishes the view and destination rectangles two pixels inside the window's port
rectangle less the scroll bar.

MoveHHi and HLock move the document structure high and lock it.  A monostyled edit
structure is then created by TENew and its handle is assigned to the appropriate field of
the document structure.  (If this call is not successful, the window and scroll bar are
disposed of, an error alert is displayed, and the function returns.)  The handle to the
document structure is then unlocked.

TESetClickLoop installs the universal procedure pointer to the custom click loop function
customClickLoop in the clickLoop field of the edit structure.  TEAutoView enables
automatic scrolling for the edit structure.  TEFeatureFlag enables outline highlighting
for the edit structure.

The last line returns a pointer to the newly opened window.

customClickLoop

customClickLoop replaces the default click loop function so as to provide for scroll bar
adjustment in concert with automatic scrolling.  Following a mouse-down within the view
rectangle, customClickLoop is called repeatedly by TEClick as long as the mouse button
remains down.

The first three lines retrieve a handle to the edit structure associated with the window. 
The next two lines save the current colour graphics port and set the window's colour
graphics port as the current port.

The window's current clip region will have been set by TextEdit to be equivalent to the
view rectangle.  Since the scroll bar has to be redrawn, the clipping region must be
temporarily reset to include the scroll bar.  Accordingly, GetClip saves the current
clipping region and the following two lines set the clipping region to the bounds of the
coordinate plane.  

GetMouse gets the current position of the cursor.  If the cursor is above the top of the
port rectangle, the text must be scrolled downwards.  Accordingly, the variable
linesToScroll is set to 1.  The subsidiary function doSetScrollBarValue (see below) is then
called to, amongst other things, reset the scroll bar's value.  Note that the value in
linesToScroll may be modified by doSetScrollBarValue.  If linesToScroll is not set to 0 by
doSetScrollBarValue, TEScroll is called to scroll the text by a number of pixels equivalent
to the value in the lineHeight field of the edit structure, and in a downwards direction.

If the cursor is below the bottom of the port rectangle, the same process occurs except
that the variable linesToScroll is set to -1, thus causing an upwards scroll of the text
(assuming that the value in linesToScroll is not changed to 0 by doSetScrollBarValue).

If scrolling has occurred, doDrawDataPanel redraws the data panel.  SetClip restores the
clipping region to that established by the view rectangle and SetPort restores the saved
colour graphics port.  Finally, the last line returns true.  (A return of false would
cause TextEdit to stop calling customClickLoop, as if the user had released the mouse
button.)

doSetScrollBarValue

doSetScrollBarValue is called from customClickLoop.  Apart from setting the scroll 
bar's value so as to cause the scroll box to follow up automatic scrolling, the function 
checks whether the limits of scrolling have been reached.

The first two lines get the current control value and the current control maximum value. 
At the next block, the value in the variable linesToScroll will be set to either 0 (if
the current control value is 0) or equivalent to the control maximum value (if the
current control value is equivalent to the control maximum value.  If these modifications
do not occur, the value in linesToScroll will remain as established at the first line in
this block, that is, the current control value minus the value in linesToScroll as passed
to the function.

SetControlValue sets the control's value to the value in linesToScroll.  The last line
sets the value in linesToScroll to 0 if the limits of scrolling have already been
reached, or to the value as it was when the doSetScrollBarValue function was entered.

doAdjustMenus

doAdjustMenus adjusts the menus.  Much depends on whether any windows are currently open.

If at least one window is open, Lines The first three lines in the if block get a handle
to the edit structure associated with the front window and the first call to EnableItem
enables the Close item in the File menu.  If there is a current selection range, the Cut,
Copy, and Clear items are enabled, otherwise they are disabled.  If there is any text in
the desk scrap (the call to GetScrap), the Paste item is enabled, otherwise it is
disabled.  If there is any text in the edit structure, the SaveAs and Select All items
are enabled, otherwise they are disabled.

If no windows are open, the Close, SaveAs, Clear, and Select All items are disabled.
doFileMenu

doFileMenu handles File menu choices, calling the appropriate application-defined
functions according to the menu item chosen.  In the SaveAs case, a handle to the edit
structure associated with the front window is retrieved and passed as a parameter to the
appropriate application-defined function.

(Note that, because TextEdit, rather than file operations, is the real focus of this
program, the file-related code has been kept to a minimum, even to the extent of having
no Save-related, as opposed to SaveAs-related, code.)

doEditMenu

doEditMenu handles choices from the Edit menu.  Recall that, in the case of monostyled
edit structures, TECut, TECopy, and TEPaste do not copy/paste text to/from the desk
scrap.  This program, however, supports copying/pasting to/from the desk scrap.

Before the usual switch is entered, a handle to the edit structure associated with the
front window is retrieved.

The iCut case handles the Cut command.  Firstly, the call to ZeroScrap attempts to empty
the desk scrap.  If the call succeeds, PurgeSpace establishes the size of the largest
block in the heap that would be available if a general purge were to occur.  The next
line gets the current selection length.  If the selection length is greater than the
available memory, the user is advised via an error message. Otherwise, TECut is called to
remove the selected text from the edit structure and copy it to the TextEdit private
scrap.  The scroll bar is adjusted, and TEToScrap is called to copy the private scrap to
the desk scrap.  If the latter call is not successful, ZeroScrap cleans up as best it can
by emptying the desk scrap.

The iCopy case handles the Copy command.  If the call to empty the desk scrap is
successful, TECopy is called to copy the selected text from the edit structure to the
TextEdit private scrap.  TEToScrap then copies the private scrap to the desk scrap.

The iPaste case handles the Paste command, which must not proceed if the paste would
cause the TextEdit limit of 32,767 bytes to be exceeded.  The first line establishes a
value equal to the number of bytes in the edit structure plus the number of bytes of the
specified data type ('TEXT') in the desk scrap.  If this value exceeds the TextEdit
limit, the user is advised via an error message.  Otherwise, TEFromScrap copies the desk
scrap to TextEdit's private scrap, TEPaste inserts the private scrap into the edit
structure, and the following line adjusts the scroll bar.

The iClear case handles the Clear command.  TEDelete deletes the current selection range
from the edit structure and the following line adjusts the scroll bar.

The iSelectAll case handle the Select All command.  TESetSelect sets the selection range
according to the first two parameters (selStart and selEnd).

doGetSelectLength

doGetSelectLength returns a value equal to the length of the current selection. 

doAdjustScrollbar

doAdjustScrollbar adjusts the vertical scroll bar.

The first two lines retrieve handles to the document structure and edit structure
associated with the window in question.

At the next block, the value in the nLines field of the edit structure is assigned to the
numberOfLines variable.  The next action is somewhat of a refinement and is therefore not
essential.  If the last character in the edit structure is the return character,
numberOfLines is incremented by one.  This will ensure that, when the document is
scrolled to its end, a single blank line will appear below the last line of text.

At the next block, the variable controlMax is assigned a value equal to the number of
lines in the edit structure less the number of lines which will fit in the view
rectangle.  If this value is less than 0 (indicating that the number of lines in the edit
structure is less than the number of lines that will fit in the view rectangle),
controlMax is set to 0.  SetControlMaximum then sets the control maximum value.  If
controlMax is 0, the scroll bar is automatically unhighlighted by the SetControlMaximum
call.

The first line of the next block assigns to the variable controlValue a value equal to
the number of text lines that the top of the destination rectangle is currently "above"
the top of the view rectangle.  If the calculation returns a value less than 0 (that is,
the document has been scrolled fully down), controlValue is set to 0.  If the calculation
returns a value greater than the current control maximum value (that is, the document has
been scrolled fully up), controlValue is set to equal that value.  SetControlValue sets
the control value to the value in controlValue.  For example, if the top of the view
rectangle is 2, the top of the destination rectangle is -34 and the lineHeight field of
the edit structure contains the value 13, the control value will be set to 3.

If the target is the PowerPC target, and if Mac OS 8.5 or later is present, 
SetControlViewSize is called to advise the Control Manager of the height of the view 
rectangle.  If Smart Scrolling is selected on in the Options tab of the Mac OS 8.5 and 
later Appearance control panel, this will cause the scroll boxes of the scroll bar to be
proportional.

With the control maximum value and the control value set, TEScroll is called to make sure
the text is scrolled to the position indicated by the scroll box.  Extending the example
in the previous paragraph, the second parameter in the TEScroll call is 2 - (34 - (3 *
13)), that is, 0.  In that case, no corrective scrolling actually occurs.

doAdjustCursor

doAdjustCursor adjusts the cursor to the I-Beam shape when the cursor is over the content
region less the scroll bar area, and to the arrow shape when the cursor is outside that
region.  It is similar to the cursor adjustment function in the demonstration program at
Chapter 13 - Offscreen Graphics Worlds, Pictures, Cursors, and Icons. 

doCloseWindow

doCloseWindow disposes of the specified window.  The associated scroll bar, the
associated edit structure and the associated document structure are disposed of before
the call to DisposeWindow.

doSaveAsFile, doOpenCommand, doOpenFile

The functions doSaveAsFile, doOpenCommand, and doOpenFile are document saving and opening
functions, enabling the user to open and save 'TEXT' documents.  Since the real focus of
this program is TextEdit, not file operations, the code is "bare bones" and as brief as
possible.

For a complete example of opening and saving monostyled 'TEXT' documents, see the
demonstration program at Chapter 16 - Files.

doDrawDataPanel

doDrawDataPanel draws the data panel at the bottom of each window.  Displayed in this
panel are the values in the teLength, nLines, lineHeight and destRect.top fields of the
edit structure and the contrlValue and contrlMax fields of the scroll bar's control
structure.

doHelpMenu

doHelpMenu handles a choice of the Text1 Help item added by this program to the
system-managed Help Menu.  The code reflects the fact that Apple reserves the right to
add items to the Help menu in future versions of the system software.

HMGetHelpMenuHandle gets a handle to the Help menu structure.  CountMenuItems returns the
number of items in the Help menu.  Since we know that