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 MonoTextEdit

Goto Contents

// *******************************************************************************************
// MonoTextEdit.c                                                           CARBON EVENT MODEL
// *******************************************************************************************
//
// This program demonstrates:
//
// o  A "bare-bones" monostyled text editor.
//
// o  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/scroller value, and scroll bar/scroller 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:
//
// o  A 'plst' resource.
//
// o  An 'MBAR' resource, and 'MENU' resources for Apple, File, Edit, and Help dialog pop-up
//    menus (preload, non-purgeable).  
//
// o  A 'CNTL' resources (purgeable) for the vertical scroll bar in the text editor window.
//
// o  'TEXT' and associated 'styl' resources (all purgeable) for the Help dialog.
//
// o  'PICT' resources (purgeable) for the Help dialog.
//
// o  A 'STR#' resource  (purgeable) containing error text strings.  
//
// 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  iHelp            2
#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 rVScrollbar       128
#define rErrorStrings     128
#define  eMenuBar         1
#define  eWindow          2
#define  eDocStructure    3
#define  eEditRecord      4
#define  eExceedChara     5
#define  eNoSpaceCut      6
#define  eNoSpacePaste    7
#define kMaxTELength      32767
#define kTab              0x09
#define kBackSpace        0x08
#define kForwardDelete    0x7F
#define kReturn           0x0D
#define kEscape           0x1B
#define topLeft(r)        (((Point *) &(r))[0])
#define botRight(r)       (((Point *) &(r))[1])

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

typedef struct
{
  TEHandle   textEditStrucHdl;
  ControlRef vScrollbarRef;
} docStructure, **docStructureHandle;

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

Boolean          gRunningOnX = false;
MenuID           gHelpMenu;
ControlActionUPP gScrollActionFunctionUPP;
TEClickLoopUPP   gCustomClickLoopUPP;
SInt16           gNumberOfWindows = 0;
SInt16           gOldControlValue;

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

void            main                 (void);
void            doPreliminaries      (void);
OSStatus        appEventHandler      (EventHandlerCallRef,EventRef,void *);
OSStatus        windowEventHandler   (EventHandlerCallRef,EventRef,void *);
void            doIdle               (void);
void            doKeyEvent           (SInt8);
void            scrollActionFunction (ControlRef,SInt16);
void            doInContent          (Point,Boolean);
void            doDrawContent        (WindowPtr);
void            doActivateDeactivate (WindowRef,Boolean);
WindowRef       doNewDocWindow       (void);
EventHandlerUPP doGetHandlerUPP      (void);
Boolean         customClickLoop      (void);
void            doSetScrollBarValue  (ControlRef,SInt16 *);
void            doAdjustMenus        (void);
void            doMenuChoice         (MenuID,MenuItemIndex);
void            doFileMenu           (MenuItemIndex);
void            doEditMenu           (MenuItemIndex);
SInt16          doGetSelectLength    (TEHandle);
void            doAdjustScrollbar    (WindowRef);
void            doAdjustCursor       (WindowRef);
void            doCloseWindow        (WindowRef);
void            doSaveAsFile         (TEHandle);
void            doOpenCommand        (void);
void            doOpenFile           (FSSpec);
void            doDrawDataPanel      (WindowRef);
void            doErrorAlert         (SInt16);
void            navEventFunction     (NavEventCallbackMessage,NavCBRecPtr,
                                      NavCallBackUserData);

extern void     doHelp               (void);

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

void  main(void)
{
  MenuBarHandle menubarHdl;
  SInt32        response;
  MenuRef       menuRef;
  EventTypeSpec applicationEvents[] = { { kEventClassApplication, kEventAppActivated    },
                                        { kEventClassCommand,     kEventProcessCommand  },
                                        { kEventClassMenu,        kEventMenuEnableItems },
                                        { kEventClassMouse,       kEventMouseMoved      } };

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

  doPreliminaries();

  // ............................................................... set up menu bar and menus

  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    doErrorAlert(eMenuBar);
  SetMenuBar(menubarHdl);
  DrawMenuBar();

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

    menuRef = GetMenuRef(mAppleApplication);
    DeleteMenuItem(menuRef,iHelp);

    HMGetHelpMenu(&menuRef,NULL);
    InsertMenuItem(menuRef,"\pMonoTextEdit Help",0);
    gHelpMenu = GetMenuID(menuRef);

    gRunningOnX = true;
  }
  else
  {
    menuRef = GetMenuRef(mFile);
    if(menuRef != NULL)
      SetMenuItemCommandID(menuRef,iQuit,kHICommandQuit);
  }

  // ..................................................... create universal procedure pointers

  gScrollActionFunctionUPP = NewControlActionUPP((ControlActionProcPtr) scrollActionFunction);
  gCustomClickLoopUPP      = NewTEClickLoopUPP((TEClickLoopProcPtr) customClickLoop);
 
  // ....................................................... install application event handler
  
  InstallApplicationEventHandler(NewEventHandlerUPP((EventHandlerProcPtr) appEventHandler),
                                 GetEventTypeCount(applicationEvents),applicationEvents,
                                 0,NULL);

  // ......................................................................... install a timer

  InstallEventLoopTimer(GetCurrentEventLoop(),0,TicksToEventTime(GetCaretTime()),
                        NewEventLoopTimerUPP((EventLoopTimerProcPtr) doIdle),NULL,
                        NULL);

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

  doNewDocWindow();

  // .............................................................. run application event loop

  RunApplicationEventLoop();
}

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

void  doPreliminaries(void)
{
  MoreMasterPointers(192);
  InitCursor();
}

// *************************************************************************** appEventHandler

OSStatus  appEventHandler(EventHandlerCallRef eventHandlerCallRef,EventRef eventRef,
                          void * userData)
{
  OSStatus      result = eventNotHandledErr;
  UInt32        eventClass;
  UInt32        eventKind;
  HICommand     hiCommand;
  MenuID        menuID;
  MenuItemIndex menuItem;

  eventClass = GetEventClass(eventRef);
  eventKind  = GetEventKind(eventRef);

  switch(eventClass)
  {
    case kEventClassApplication:
      if(eventKind == kEventAppActivated)
        SetThemeCursor(kThemeArrowCursor);
      break;

    case kEventClassCommand:
      if(eventKind == kEventProcessCommand)
      {
        GetEventParameter(eventRef,kEventParamDirectObject,typeHICommand,NULL,
                          sizeof(HICommand),NULL,&hiCommand);
        menuID = GetMenuID(hiCommand.menu.menuRef);
        menuItem = hiCommand.menu.menuItemIndex;
        if((hiCommand.commandID != kHICommandQuit) && 
           ((menuID >= mAppleApplication && menuID <= mEdit)) || menuID == gHelpMenu)
        {
          doMenuChoice(menuID,menuItem);
          result = noErr;
        }
      }
      break;

    case kEventClassMenu:
      if(eventKind == kEventMenuEnableItems)
      {
        GetWindowClass(FrontWindow(),&windowClass);
        if(windowClass == kDocumentWindowClass)
          doAdjustMenus();
        result = noErr;
      }
      break;

    case kEventClassMouse:
      if(eventKind == kEventMouseMoved)
      {
        GetWindowClass(FrontWindow(),&windowClass);
        if(windowClass == kDocumentWindowClass)
          doAdjustCursor(FrontWindow());
       result = noErr;
      }
      break;

  return result;
}

// ************************************************************************ windowEventHandler

OSStatus  windowEventHandler(EventHandlerCallRef eventHandlerCallRef,EventRef eventRef,
                             void* userData)
{
  OSStatus        result = eventNotHandledErr;
  UInt32          eventClass;
  UInt32          eventKind;
  WindowRef       windowRef;
  UInt32          modifiers; 
  Point           mouseLocation;
  Boolean         shiftKeyDown = false;
  ControlRef      controlRef;
  ControlPartCode controlPartCode;
  SInt8           charCode;

  eventClass = GetEventClass(eventRef);
  eventKind  = GetEventKind(eventRef);

  switch(eventClass)
  {
    case kEventClassWindow:                                              // event class window
      GetEventParameter(eventRef,kEventParamDirectObject,typeWindowRef,NULL,sizeof(windowRef),
                        NULL,&windowRef);
      switch(eventKind)
      {
        case kEventWindowDrawContent:
          doDrawContent(windowRef);
          result = noErr;
          break;

        case kEventWindowActivated:
          doActivateDeactivate(windowRef,true);
          result = noErr;
          break;

        case kEventWindowDeactivated:
          doActivateDeactivate(windowRef,false);
          result = noErr;
          break;

        case kEventWindowClickContentRgn:
          GetEventParameter(eventRef,kEventParamMouseLocation,typeQDPoint,NULL,
                            sizeof(mouseLocation),NULL,&mouseLocation);
          SetPortWindowPort(FrontWindow());
          GlobalToLocal(&mouseLocation);
          GetEventParameter(eventRef,kEventParamKeyModifiers,typeUInt32,NULL,
                            sizeof(modifiers),NULL,&modifiers);
          if(modifiers & shiftKey)
            shiftKeyDown = true;
          doInContent(mouseLocation,shiftKeyDown);
          result = noErr;
          break;

        case kEventWindowClose:
          doCloseWindow(windowRef);
          result = noErr;
          break;
      }
      break;

    case kEventClassMouse:                                                // event class mouse
      switch(eventKind)
      {
        case kEventMouseDown:
          GetEventParameter(eventRef,kEventParamMouseLocation,typeQDPoint,NULL,
                            sizeof(mouseLocation),NULL,&mouseLocation);
          SetPortWindowPort(FrontWindow());
          GlobalToLocal(&mouseLocation);
          controlRef = FindControlUnderMouse(mouseLocation,FrontWindow(),&controlPartCode);
          if(controlRef)
          {
            gOldControlValue = GetControlValue(controlRef);
            TrackControl(controlRef,mouseLocation,gScrollActionFunctionUPP);
            result = noErr;
          }
        break;
      }
      break;

    case kEventClassKeyboard:                                          // event class keyboard
      switch(eventKind)
      {
        case kEventRawKeyDown:
        case kEventRawKeyRepeat: 
          GetEventParameter(eventRef,kEventParamKeyMacCharCodes,typeChar,NULL,
                            sizeof(charCode),NULL,&charCode);
          GetEventParameter(eventRef,kEventParamKeyModifiers,typeUInt32,NULL,
                            sizeof(modifiers),NULL,&modifiers);
          if((modifiers & cmdKey) == 0)
            doKeyEvent(charCode);
          result = noErr;
          break;
      }
      break;
  }

  return result;
}

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

void  doIdle(void)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;

  windowRef = FrontWindow();
  if(GetWindowKind(windowRef) == kApplicationWindowKind)
  {
    docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
    if(docStrucHdl != NULL)
      TEIdle((*docStrucHdl)->textEditStrucHdl);
  }
}

// ******************************************************************************** doKeyEvent

void  doKeyEvent(SInt8 charCode)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             selectionLength;

  if(charCode <= kEscape && charCode != kBackSpace && charCode != kReturn)
    return;

  windowRef = FrontWindow();
  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

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

  doDrawDataPanel(windowRef);
}

// ********************************************************************** scrollActionFunction

void  scrollActionFunction(ControlRef controlRef,SInt16 partCode)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             linesToScroll;
  SInt16             controlValue, controlMax;

  windowRef = GetControlOwner(controlRef);
  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));;
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

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

  if(partCode)
  {
    if(partCode != kControlIndicatorPart)
    {
      switch(partCode)
      {
        case kControlUpButtonPart:
        case kControlDownButtonPart:
          linesToScroll = 1;
          break;
        
        case kControlPageUpPart:
        case kControlPageDownPart:
          linesToScroll = (((*textEditStrucHdl)->viewRect.bottom - 
                            (*textEditStrucHdl)->viewRect.top) /
                            (*textEditStrucHdl)->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(controlRef,linesToScroll);

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

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

// ******************************************************************************* doInContent

void  doInContent(Point mouseLocation,Boolean shiftKeyDown)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;

  windowRef = FrontWindow();
  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  if(PtInRect(mouseLocation,&(*textEditStrucHdl)->viewRect))
    TEClick(mouseLocation,shiftKeyDown,textEditStrucHdl);
}

// ***************************************************************************** doDrawContent

void  doDrawContent(WindowRef windowRef)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  GrafPtr            oldPort;
  RgnHandle          visibleRegionHdl = NewRgn();
  Rect               portRect;

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  GetPort(&oldPort);
  SetPortWindowPort(windowRef);

  GetPortVisibleRegion(GetWindowPort(windowRef),visibleRegionHdl);
  EraseRgn(visibleRegionHdl);

  UpdateControls(windowRef,visibleRegionHdl);

  GetWindowPortBounds(windowRef,&portRect);
  TEUpdate(&portRect,textEditStrucHdl);

  doDrawDataPanel(windowRef);

  DisposeRgn(visibleRegionHdl);
  SetPort(oldPort);
}

// *********************************************************************** doActivateDocWindow

void  doActivateDeactivate(WindowRef windowRef,Boolean becomingActive)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  if(becomingActive)
  {
    SetPortWindowPort(windowRef);

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

    TEActivate(textEditStrucHdl);
    ActivateControl((*docStrucHdl)->vScrollbarRef);
    doAdjustScrollbar(windowRef);
    doAdjustCursor(windowRef);
  }
  else
  {
    TEDeactivate(textEditStrucHdl);
    DeactivateControl((*docStrucHdl)->vScrollbarRef);
  }
}

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

WindowRef  doNewDocWindow(void)
{
  WindowRef          windowRef;
  OSStatus           osError;
  Rect               contentRect = { 100,100,400,595 };
  WindowAttributes   attributes  = kWindowStandardHandlerAttribute |
                                    kWindowStandardDocumentAttributes;
  docStructureHandle docStrucHdl;
  Rect               portRect, destAndViewRect;
  EventTypeSpec    windowEvents[] = { { kEventClassWindow,   kEventWindowDrawContent     },
                                      { kEventClassWindow,   kEventWindowActivated       },
                                      { kEventClassWindow,   kEventWindowDeactivated     },
                                      { kEventClassWindow,   kEventWindowClickContentRgn },
                                      { kEventClassWindow,   kEventWindowClose           },
                                      { kEventClassMouse,    kEventMouseDown             },
                                      { kEventClassKeyboard, kEventRawKeyDown            },
                                      { kEventClassKeyboard, kEventRawKeyRepeat          } };

  osError = CreateNewWindow(kDocumentWindowClass,attributes,&contentRect,&windowRef);
  if(osError != noErr)
  {
    doErrorAlert(eWindow);
    return NULL;
  }

  ChangeWindowAttributes(windowRef,0,kWindowResizableAttribute);
  RepositionWindow(windowRef,NULL,kWindowCascadeOnMainScreen);
  SetWTitle(windowRef,"\puntitled");
  SetPortWindowPort(windowRef);
  TextSize(10);

  InstallWindowEventHandler(windowRef,doGetHandlerUPP(),GetEventTypeCount(windowEvents),
                            windowEvents,0,NULL);

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

  gNumberOfWindows ++;

  (*docStrucHdl)->vScrollbarRef = GetNewControl(rVScrollbar,windowRef);

  GetWindowPortBounds(windowRef,&portRect);
  destAndViewRect = portRect;
  destAndViewRect.right -= 15;
  destAndViewRect.bottom -= 15;
  InsetRect(&destAndViewRect,2,2);

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

  if(!((*docStrucHdl)->textEditStrucHdl = TENew(&destAndViewRect,&destAndViewRect)))
  {
    DisposeWindow(windowRef);
    gNumberOfWindows --;
    DisposeHandle((Handle) docStrucHdl);
    doErrorAlert(eEditRecord);
    return NULL;
  }

  HUnlock((Handle) docStrucHdl);

  TESetClickLoop(gCustomClickLoopUPP,(*docStrucHdl)->textEditStrucHdl);
  TEAutoView(true,(*docStrucHdl)->textEditStrucHdl);
  TEFeatureFlag(teFOutlineHilite,1,(*docStrucHdl)->textEditStrucHdl);

  ShowWindow(windowRef);

  return windowRef;
}

// *************************************************************************** doGetHandlerUPP

EventHandlerUPP  doGetHandlerUPP(void)
{
  static EventHandlerUPP windowEventHandlerUPP;

  if(windowEventHandlerUPP == NULL)
    windowEventHandlerUPP = NewEventHandlerUPP((EventHandlerProcPtr) windowEventHandler);

  return windowEventHandlerUPP;
}

// *************************************************************************** customClickLoop

Boolean  customClickLoop(void)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  GrafPtr            oldPort;
  RgnHandle          oldClip;
  Rect               tempRect, portRect;
  Point              mouseXY;
  SInt16             linesToScroll = 0;

  windowRef = FrontWindow();
  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

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

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

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

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

  return true;
}

// *********************************************************************** doSetScrollBarValue

void  doSetScrollBarValue(ControlRef controlRef,SInt16 *linesToScroll)
{
  SInt16 controlValue, controlMax;

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

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

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

// ***************************************************************************** doAdjustMenus

void  doAdjustMenus(void)
{
  MenuRef            fileMenuHdl, editMenuHdl;
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  ScrapRef           scrapRef;
  OSStatus           osError;
  ScrapFlavorFlags   scrapFlavorFlags;

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

  if(gNumberOfWindows > 0)
  {
    windowRef = FrontWindow();
    docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
    textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

    EnableMenuItem(fileMenuHdl,iClose);

    if((*textEditStrucHdl)->selStart < (*textEditStrucHdl)->selEnd)
    {
      EnableMenuItem(editMenuHdl,iCut);
      EnableMenuItem(editMenuHdl,iCopy);
      EnableMenuItem(editMenuHdl,iClear);
    }
    else
    {
      DisableMenuItem(editMenuHdl,iCut);
      DisableMenuItem(editMenuHdl,iCopy);
      DisableMenuItem(editMenuHdl,iClear);
    }

    GetCurrentScrap(&scrapRef);

    osError = GetScrapFlavorFlags(scrapRef,kScrapFlavorTypeText,&scrapFlavorFlags);
    if(osError == noErr)
      EnableMenuItem(editMenuHdl,iPaste);
    else
      DisableMenuItem(editMenuHdl,iPaste);
      
    if((*textEditStrucHdl)->teLength > 0)
    {
      EnableMenuItem(fileMenuHdl,iSaveAs);
      EnableMenuItem(editMenuHdl,iSelectAll);
    }
    else
    {
      DisableMenuItem(fileMenuHdl,iSaveAs);
      DisableMenuItem(editMenuHdl,iSelectAll);
    }
  }
  else
  {
    DisableMenuItem(fileMenuHdl,iClose);
    DisableMenuItem(fileMenuHdl,iSaveAs);
    DisableMenuItem(editMenuHdl,iClear);
    DisableMenuItem(editMenuHdl,iSelectAll);
  }

  DrawMenuBar();
}

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

void  doMenuChoice(MenuID menuID, MenuItemIndex menuItem)
{
  if(menuID == 0)
    return;

  if(gRunningOnX)
    if(menuID == gHelpMenu)
      if(menuItem == 1)
        doHelp();

  switch(menuID)
  {
    case mAppleApplication:
      if(menuItem == iAbout)
        SysBeep(10);
      else if(menuItem == iHelp)
        doHelp();
      break;

    case mFile:
      doFileMenu(menuItem);
      break;

    case mEdit:
      doEditMenu(menuItem);
      break;
  }
}

// ******************************************************************************** doFileMenu

void  doFileMenu(MenuItemIndex menuItem)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;

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

    case iOpen:
      doOpenCommand();
      doAdjustScrollbar(FrontWindow());
      break;

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

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

// ******************************************************************************** doEditMenu

void  doEditMenu(MenuItemIndex menuItem)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt32             totalSize, contigSize, newSize;
  SInt16             selectionLength;
  ScrapRef           scrapRef;
  Size               sizeOfTextData;

  windowRef = FrontWindow();

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  switch(menuItem)
  {
    case iUndo:
      break;

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

    case iCopy:
      if(ClearCurrentScrap() == noErr)
        TECopy(textEditStrucHdl);
      TEToScrap();
      if(TEToScrap() != noErr)
        ClearCurrentScrap();
      break;

    case iPaste:
      GetCurrentScrap(&scrapRef);;
      GetScrapFlavorSize(scrapRef,kScrapFlavorTypeText,&sizeOfTextData);
      newSize = (*textEditStrucHdl)->teLength + sizeOfTextData;
      if(newSize > kMaxTELength)
        doErrorAlert(eNoSpacePaste);
      else
      {
        if(TEFromScrap() == noErr)
        {
          TEPaste(textEditStrucHdl);
          doAdjustScrollbar(windowRef);
        }
      }
      break;

    case iClear:
      TEDelete(textEditStrucHdl);
      doAdjustScrollbar(windowRef);
      break;

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

  doDrawDataPanel(windowRef);
}

// ************************************************************************* doGetSelectLength

SInt16  doGetSelectLength(TEHandle textEditStrucHdl)
{
  SInt16 selectionLength;

  selectionLength = (*textEditStrucHdl)->selEnd - (*textEditStrucHdl)->selStart;
  return selectionLength;
}

// ************************************************************************* doAdjustScrollbar

void  doAdjustScrollbar(WindowRef windowRef)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             numberOfLines, controlMax, controlValue;

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));;
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

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

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

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

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

  SetControlViewSize((*docStrucHdl)->vScrollbarRef,(*textEditStrucHdl)->viewRect.bottom - 
                     (*textEditStrucHdl)->viewRect.top);

  TEScroll(0,((*textEditStrucHdl)->viewRect.top - (*textEditStrucHdl)->destRect.top) - 
               (GetControlValue((*docStrucHdl)->vScrollbarRef) *
              (*textEditStrucHdl)->lineHeight),textEditStrucHdl);
}

// **************************************************************************** doAdjustCursor

void  doAdjustCursor(WindowRef windowRef)
{
  GrafPtr   oldPort;
  RgnHandle arrowRegion, iBeamRegion;
  Rect      portRect, cursorRect;
  Point     mouseXY;

  GetPort(&oldPort);
  SetPortWindowPort(windowRef);

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

  GetWindowPortBounds(windowRef,&portRect);
  cursorRect = portRect;
  cursorRect.bottom -= 15;
  cursorRect.right  -= 15;
  LocalToGlobal(&topLeft(cursorRect));
  LocalToGlobal(&botRight(cursorRect));  

  RectRgn(iBeamRegion,&cursorRect);
  DiffRgn(arrowRegion,iBeamRegion,arrowRegion);
  
  GetGlobalMouse(&mouseXY);

  if(PtInRgn(mouseXY,iBeamRegion))
    SetThemeCursor(kThemeIBeamCursor);
  else
    SetThemeCursor(kThemeArrowCursor);

  DisposeRgn(arrowRegion);
  DisposeRgn(iBeamRegion);

  SetPort(oldPort);
}

// ***************************************************************************** doCloseWindow

void  doCloseWindow(WindowRef windowRef)
{
  docStructureHandle docStrucHdl;

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));;

  DisposeControl((*docStrucHdl)->vScrollbarRef);
  TEDispose((*docStrucHdl)->textEditStrucHdl);
  DisposeHandle((Handle) docStrucHdl);
  DisposeWindow(windowRef);

  gNumberOfWindows --;
}

// ****************************************************************************** doSaveAsFile

void  doSaveAsFile(TEHandle textEditStrucHdl)
{
  OSErr            osError = noErr;
  NavDialogOptions dialogOptions;
  NavEventUPP      navEventFunctionUPP;
  WindowRef        windowRef;
  OSType           fileType;
  NavReplyRecord   navReplyStruc;
  AEKeyword        theKeyword;
  DescType         actualType;
  FSSpec           fileSpec;  
  SInt16           fileRefNum;
  Size             actualSize;
  SInt32           dataLength;
  Handle           editTextHdl;
    
  osError = NavGetDefaultDialogOptions(&dialogOptions);

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

    fileType = 'TEXT';

    navEventFunctionUPP = NewNavEventUPP((NavEventProcPtr) navEventFunction);
    osError = NavPutFile(NULL,&navReplyStruc,&dialogOptions,navEventFunctionUPP,fileType,
                         'kjBb',NULL);
    DisposeNavEventUPP(navEventFunctionUPP);

    if(navReplyStruc.validRecord && osError == noErr)
    {
      if((osError = AEGetNthPtr(&(navReplyStruc.selection),1,typeFSS,&theKeyword,
                                &actualType,&fileSpec,sizeof(fileSpec),&actualSize)) == noErr)

      {
        if(!navReplyStruc.replacing)
        {
          osError = FSpCreate(&fileSpec,'kjBb',fileType,navReplyStruc.keyScript);
          if(osError != noErr)
          {
            NavDisposeReply(&navReplyStruc);
          }
        }
        
        if(osError == noErr)
          osError = FSpOpenDF(&fileSpec,fsRdWrPerm,&fileRefNum);

        if(osError == noErr)
        {
          SetWTitle(windowRef,fileSpec.name);
          dataLength = (*textEditStrucHdl)->teLength;
          editTextHdl = (*textEditStrucHdl)->hText;
          FSWrite(fileRefNum,&dataLength,*editTextHdl);
        }

        NavCompleteSave(&navReplyStruc,kNavTranslateInPlace);
      }

      NavDisposeReply(&navReplyStruc);
    }
  }
}

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

void  doOpenCommand(void)
{  
  OSErr            osError  = noErr;
  NavDialogOptions dialogOptions;
  NavEventUPP      navEventFunctionUPP;
  NavReplyRecord   navReplyStruc;
  SInt32           index, count;
  AEKeyword        theKeyword;
  DescType         actualType;
  FSSpec           fileSpec;  
  Size             actualSize;
  FInfo            fileInfo;

  osError = NavGetDefaultDialogOptions(&dialogOptions);

  if(osError == noErr)
  {
    navEventFunctionUPP = NewNavEventUPP((NavEventProcPtr) navEventFunction);
    osError = NavGetFile(NULL,&navReplyStruc,&dialogOptions,navEventFunctionUPP,NULL,NULL,
                         NULL,NULL);
    DisposeNavEventUPP(navEventFunctionUPP);

    if(osError == noErr && navReplyStruc.validRecord)
    {
      osError = AECountItems(&(navReplyStruc.selection),&count);
      if(osError == noErr)
      {
        for(index=1;index<=count;index++)
        {
          osError = AEGetNthPtr(&(navReplyStruc.selection),index,typeFSS,&theKeyword,
                                &actualType,&fileSpec,sizeof(fileSpec),&actualSize);
          {
            if((osError = FSpGetFInfo(&fileSpec,&fileInfo)) == noErr)
              doOpenFile(fileSpec);
          }
        }
      }
    
      NavDisposeReply(&navReplyStruc);  
    }
  }
}

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

void  doOpenFile(FSSpec fileSpec)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             fileRefNum;
  SInt32             textLength;
  Handle             textBuffer;

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

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));;
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  SetWTitle(windowRef,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,textEditStrucHdl);

  HUnlock(textBuffer);
  DisposeHandle(textBuffer);

  FSClose(fileRefNum);

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

  doDrawContent(windowRef);
}

// *************************************************************************** doDrawDataPanel

void  doDrawDataPanel(WindowRef windowRef)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  RGBColor           whiteColour = { 0xFFFF, 0xFFFF, 0xFFFF };
  RGBColor           blackColour = { 0x0000, 0x0000, 0x0000 };
  RGBColor           blueColour = { 0x1818, 0x4B4B, 0x8181 };
  ControlRef         controlRef;
  Rect               panelRect;
  Str255             textString;

  SetPortWindowPort(windowRef);

  docStrucHdl = (docStructureHandle) (GetWRefCon(windowRef));;
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;
  controlRef = (*docStrucHdl)->vScrollbarRef;

  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) (*textEditStrucHdl)->teLength,textString);
  MoveTo(47,295);
  DrawString(textString);

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

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

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

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

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

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

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

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

  GetIndString(errorString,rErrorStrings,errorCode);

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

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

void  navEventFunction(NavEventCallbackMessage callBackSelector,NavCBRecPtr callBackParms,
                       NavCallBackUserData callBackUD)
{
}

// *******************************************************************************************
// HelpDialog.c
// *******************************************************************************************

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

#include <Carbon.h>

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

#define eHelpDialog           8
#define eHelpDocStructure     9
#define eHelpText             10
#define eHelpPicture          11
#define eHelpControls         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          textEditStrucHdl;
  ControlRef        scrollbarHdl;
  SInt16            pictCount;
  pictInfoStructure *pictInfoStructurePtr;
}  docStructure, ** docStructureHandle;

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

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

GrafPtr                gOldPort;
EventHandlerUPP        helpWindowEventHandlerUPP;
ControlUserPaneDrawUPP userPaneDrawFunctionUPP;
ControlActionUPP       actionFunctionUPP;
SInt16                 gTextResourceID;
SInt16                 gPictResourceBaseID;
RgnHandle              gSavedClipRgn  = NULL;

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

void      doHelp                 (void);
OSStatus  helpWindowEventHandler (EventHandlerCallRef,EventRef,void *);
void      userPaneDrawFunction   (ControlRef,SInt16);
Boolean   doGetText              (WindowRef,SInt16,Rect);
Boolean   doGetPictureInfo       (WindowRef,SInt16);
void      actionFunction         (ControlRef,SInt16);
void      doScrollTextAndPicts   (WindowRef);
void      doDrawPictures         (WindowRef,Rect *);
void      doCloseHelp            (WindowRef);
void      doDisposeDescriptors   (void);
void      doSetBackgroundWhite   (void);

extern void  doErrorAlert        (SInt16);

// ************************************************************************************ doHelp

void  doHelp(void)
{
  OSStatus           osError;
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  ControlRef         controlRef;
  ControlID          controlID;
  Rect               windowRect      = { 0,  0,  353,382 };
  Rect               pushButtonRect  = { 312,297,332,366 };
  Rect               userPaneRect    = { 16, 16, 296,351 };
  Rect               scrollBarRect   = { 16, 350,296,366 };
  Rect               popupButtonRect = { 312,12, 332,256 };
  Rect               destRect, viewRect;
  EventTypeSpec      dialogEvents[] = {{ kEventClassControl, kEventControlClick } };

  GetPort(&gOldPort);

  // ..................................................... create universal procedure pointers

  helpWindowEventHandlerUPP = NewEventHandlerUPP((EventHandlerProcPtr)
                                                 helpWindowEventHandler);
  userPaneDrawFunctionUPP = NewControlUserPaneDrawUPP((ControlUserPaneDrawProcPtr) 
                                                       userPaneDrawFunction);
  actionFunctionUPP = NewControlActionUPP((ControlActionProcPtr) actionFunction);

  // ............................................................... create modal class window

  osError = CreateNewWindow(kMovableModalWindowClass,kWindowStandardHandlerAttribute,
                            &windowRect,&windowRef);
  if(osError == noErr)
  {
    RepositionWindow(windowRef,FrontWindow(),kWindowAlertPositionOnMainScreen);
    SetThemeWindowBackground(windowRef,kThemeBrushDialogBackgroundActive,false);

    InstallWindowEventHandler(windowRef,helpWindowEventHandlerUPP,
                              GetEventTypeCount(dialogEvents),dialogEvents,windowRef,NULL);

    if(!(docStrucHdl = (docStructureHandle) NewHandle(sizeof(docStructure))))
    {
      doErrorAlert(eHelpDocStructure);
      DisposeWindow(windowRef);
      doDisposeDescriptors();
      return;
    }

    SetWRefCon(windowRef,(SInt32) docStrucHdl);
    SetPortWindowPort(windowRef);

    // .......................... create root, push button, user pane, and scroll bar controls

    CreateRootControl(windowRef,&controlRef);

    if((osError = CreatePushButtonControl(windowRef,&pushButtonRect,CFSTR("OK"),&controlRef))
        == noErr)
    {
      SetWindowDefaultButton(windowRef,controlRef);
      controlID.id = 'done';
      SetControlID(controlRef,&controlID);
    }

    if(osError == noErr)
    {
      if((osError = CreateUserPaneControl(windowRef,&userPaneRect,0,&controlRef)) == noErr)
      {
        SetControlData(controlRef,kControlEntireControl,kControlUserPaneDrawProcTag, 
                       sizeof(userPaneDrawFunctionUPP),(Ptr) &userPaneDrawFunctionUPP);
      }
    }

    if(osError == noErr)
    {
      if((osError = CreateScrollBarControl(windowRef,&scrollBarRect,0,0,1,0,true,
                                           actionFunctionUPP,&controlRef)) == noErr)
      (*docStrucHdl)->scrollbarHdl = controlRef;
      controlID.id = 'scro';
      SetControlID(controlRef,&controlID);
    }

    if(osError == noErr)
    {
      if((osError = CreatePopupButtonControl(windowRef,&popupButtonRect,CFSTR("Title:"),131,
                                             false,-1,0,0,&controlRef)) == noErr)
      controlID.id = 'popu';
      SetControlID(controlRef,&controlID);
    }

    if(osError != noErr)
    {
      doErrorAlert(eHelpControls);
      DisposeWindow(windowRef);
      doDisposeDescriptors();
      return;
    }
  }
  else
  {
    doErrorAlert(eHelpDialog);
    doDisposeDescriptors();
    return;
  }
  // .......................... set destination and view rectangles, create TextEdit structure

  InsetRect(&userPaneRect,kTextInset,kTextInset / 2);
  destRect = viewRect = userPaneRect;
    (*docStrucHdl)->textEditStrucHdl = TEStyleNew(&destRect,&viewRect);

  // .................... 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(windowRef,gTextResourceID,viewRect)))
  {
    doCloseHelp(windowRef);
    doDisposeDescriptors();
    return;
  }

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

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

  // ............................... create an empty region for saving the old clipping region

  gSavedClipRgn = NewRgn();

  // .......................................................... show window and run modal loop

  ShowWindow(windowRef);
  RunAppModalLoopForWindow(windowRef);
}

// ******************************************************************** helpWindowEventHandler

OSStatus  helpWindowEventHandler(EventHandlerCallRef eventHandlerCallRef,EventRef eventRef,
                                 void *userData)
{
  OSStatus           result = eventNotHandledErr;
  WindowRef          windowRef;
  UInt32             eventClass;
  UInt32             eventKind;
  Point              mouseLocation;
  ControlRef         controlRef;
  ControlPartCode    controlPartCode;
  ControlID          controlID;
  MenuItemIndex      menuItem;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  Rect               viewRect;

  windowRef  = userData;
  eventClass = GetEventClass(eventRef);
  eventKind  = GetEventKind(eventRef);

  if(eventClass == kEventClassControl)
  {
    if(eventKind == kEventControlClick)
    {
      GetEventParameter(eventRef,kEventParamMouseLocation,typeQDPoint,NULL,
                        sizeof(mouseLocation),NULL,&mouseLocation);
      GlobalToLocal(&mouseLocation);
      controlRef = FindControlUnderMouse(mouseLocation,windowRef,&controlPartCode);
      if(controlRef)
      {
        GetControlID(controlRef,&controlID);
        if(controlID.id == 'done')                                              // push button
        {
          if(TrackControl(controlRef,mouseLocation,NULL))
          {
            QuitAppModalLoopForWindow(windowRef);
            doCloseHelp(windowRef);
            doDisposeDescriptors();
            result = noErr;
          }
        }
        if(controlID.id == 'scro')                                               // scroll bar
        {
          TrackControl(controlRef,mouseLocation,actionFunctionUPP);
          result = noErr;  
        }
        else if(controlID.id == 'popu')                                  // pop-up menu button
        {
          TrackControl(controlRef,mouseLocation,(ControlActionUPP) -1);
          menuItem = GetControlValue(controlRef);
          switch(menuItem)
          {
            case 1:
              gTextResourceID     = rTextIntroduction;
              gPictResourceBaseID = rPictIntroductionBase;
              break;

            case 2:
              gTextResourceID     = rTextCreatingText;
              gPictResourceBaseID = rPictCreatingTextBase;
              break;

            case 3:
              gTextResourceID     = rTextModifyHelp;
              break;
          }

          docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
          textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;
          viewRect = (*textEditStrucHdl)->viewRect;

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

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

          doDrawPictures(windowRef,&viewRect);

          result = noErr;
        }
      }
    }
  }

  return result;
}

// ********************************************************************** userPaneDrawFunction

void  userPaneDrawFunction(ControlRef controlRef,SInt16 thePart)
{
  Rect               itemRect, viewRect;
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  Boolean           inState;

  windowRef = GetControlOwner(controlRef);

  GetControlBounds(controlRef,&itemRect);
  InsetRect(&itemRect,1,1);
  itemRect.right += 15;

  if(IsWindowVisible(windowRef))
    inState = IsWindowHilited(windowRef);
  DrawThemeListBoxFrame(&itemRect,inState);

  doSetBackgroundWhite();
  EraseRect(&itemRect);

  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;
  viewRect = (*textEditStrucHdl)->viewRect;

  TEUpdate(&viewRect,textEditStrucHdl);
  doDrawPictures(windowRef,&viewRect);
}

// ********************************************************************************* doGetText

Boolean  doGetText(WindowRef windowRef,SInt16 textResourceID,Rect viewRect)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  Handle             helpTextHdl;
  StScrpHandle       stylScrpStrucHdl;
  SInt16             numberOfLines, heightOfText, heightToScroll;

  doSetBackgroundWhite();

  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  TESetSelect(0,32767,textEditStrucHdl);
  TEDelete(textEditStrucHdl);

  (*textEditStrucHdl)->destRect = (*textEditStrucHdl)->viewRect;
  SetControlValue((*docStrucHdl)->scrollbarHdl,0);

  helpTextHdl = GetResource('TEXT',textResourceID);
  if(helpTextHdl == NULL)
  {
    doErrorAlert(eHelpText);
    return false;
  }

  stylScrpStrucHdl = (StScrpHandle) GetResource('styl',textResourceID);
  if(stylScrpStrucHdl == NULL)
  {
    doErrorAlert(eHelpText);
    return false;
  }

  TEStyleInsert(*helpTextHdl,GetHandleSize(helpTextHdl),stylScrpStrucHdl,textEditStrucHdl);

  ReleaseResource(helpTextHdl);
  ReleaseResource((Handle) stylScrpStrucHdl);

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

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

  return true;
}

// ************************************************************************** doGetPictureInfo

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

  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);

  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;

  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;
  textHdl = (*textEditStrucHdl)->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,textEditStrucHdl);

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

    OffsetRect(&pictInfoPtr[a].bounds,
               (((*textEditStrucHdl)->destRect.right + (*textEditStrucHdl)->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

void  actionFunction(ControlRef scrollbarHdl,SInt16 partCode)
{
  WindowRef          windowRef;
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             delta, oldValue, offset, lineHeight, fontAscent;
  Point              thePoint;
  Rect               viewRect, portRect;
  TextStyle          style;

  if(partCode)
  {
    windowRef = GetControlOwner(scrollbarHdl);
    docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
    textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;
    viewRect = (*textEditStrucHdl)->viewRect;
    thePoint.h = viewRect.left + kTextInset;

    if(partCode != kControlIndicatorPart)
    {
      switch(partCode)
      {
        case kControlUpButtonPart:
          thePoint.v = viewRect.top - 4;
          offset = TEGetOffset(thePoint,textEditStrucHdl);
          thePoint = TEGetPoint(offset,textEditStrucHdl);
          TEGetStyle(offset,&style,&lineHeight,&fontAscent,textEditStrucHdl);
          delta = thePoint.v - lineHeight - viewRect.top;
          break;

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

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

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

      oldValue = GetControlValue(scrollbarHdl);

      if(((delta < 0) && (oldValue > 0)) || ((delta > 0) && 
         (oldValue < GetControlMaximum(scrollbarHdl))))
      {
        GetClip(gSavedClipRgn);
        GetWindowPortBounds(windowRef,&portRect);
        ClipRect(&portRect);

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

    doScrollTextAndPicts(windowRef);
  }
}

// ********************************************************************** doScrollTextAndPicts

void  doScrollTextAndPicts(WindowRef windowRef)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             scrollDistance, oldScroll;
  Rect               updateRect;

  doSetBackgroundWhite();

  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  oldScroll = (*textEditStrucHdl)->viewRect.top -(*textEditStrucHdl)->destRect.top;
  scrollDistance = oldScroll - GetControlValue((*docStrucHdl)->scrollbarHdl);
  if(scrollDistance == 0)
    return;

  TEScroll(0,scrollDistance,textEditStrucHdl);

  if((*docStrucHdl)->pictCount == 0)
    return;

  updateRect = (*textEditStrucHdl)->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(windowRef,&updateRect);
}

// **************************************************************************** doDrawPictures

void  doDrawPictures(WindowRef windowRef,Rect *updateRect)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             pictCount, pictIndex, vOffset;
  PicHandle          thePictHdl;
  Rect               pictLocRect, dummyRect;

  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  vOffset = (*textEditStrucHdl)->destRect.top - 
            (*textEditStrucHdl)->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);
  }
}

// ******************************************************************************* doCloseHelp

void  doCloseHelp(WindowRef windowRef)
{
  docStructureHandle docStrucHdl;
  TEHandle           textEditStrucHdl;
  SInt16             a;

  docStrucHdl = (docStructureHandle) GetWRefCon(windowRef);
  textEditStrucHdl = (*docStrucHdl)->textEditStrucHdl;

  if(gSavedClipRgn)
    DisposeRgn(gSavedClipRgn);

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

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

  DisposeHandle((Handle) docStrucHdl);
  DisposeWindow(windowRef);
  SetPort(gOldPort);
}

// ********************************************************************** doDisposeDescriptors

void  doDisposeDescriptors(void)
{
  DisposeEventHandlerUPP(helpWindowEventHandlerUPP);
  DisposeControlUserPaneDrawUPP(userPaneDrawFunctionUPP);
  DisposeControlActionUPP(actionFunctionUPP);
}

// ********************************************************************** doSetBackgroundWhite

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

  RGBBackColor(&whiteColour);
  BackPat(GetQDGlobalsWhite(&whitePattern));
}

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

Demonstration Program MonoTextEdit 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:

o 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.  (A 'TEXT' file titled "MonoTextEdit Document"
  is included.)

o 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.)

o 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.

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

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 MonoTextEdit Help from the Mac OS 8/9 Apple menu or Mac OS X 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.

MonoTextEdit.c

defines

kMaxTELength represents the maximum allowable number of bytes in a TextEdit structure.  kTab,
kBackSpace, kForwardDelete, kReturn, and kEscape representing the character codes generated by
the tab, delete, forward delete, Return and escape keys.

typedefs

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

Global Variables

scrollActionFunctionUPP will be assigned a universal procedure pointer to an action (callback)
function for the scroll bar.  customClickLoopUPP will be assigned a universal procedure pointer
to a custom click loop (callback) function.  gOldControlValue will be assigned the scroll bar's
control value.

main

The main function creates universal procedure pointers for the application-defined scroll
action and custom click loop (callback) functions and installs a timer set to fire repeatedly
at the interval returned by a call to GetCaretTime.  When the timer fires, the function doIdle
is called.

windowEventHandler

When the kEventClassMouse event type is received, if the call to FindControlUnderMouse reveals
that a control (that is, the vertical scroll bar) is under the mouse, TrackControl is called
with a universal procedure pointer to an action function passed in the third parameter.

When the kEventRawKeyDown and kEventRawKeyRepeat event types are received, the function
doKeyEvent is called only if the Command key was not down.

doIdle

doIdle is called whenever the installed timer fires.

The first line gets a reference to the front window.  If the front window is a document window,
a handle to the window's document structure is retrieved.  A handle to the TextEdit structure
associated with the window is stored in the document structure's textEditStrucHdl field.  This
is passed in the call to TEIdle, which blinks the insertion point caret.

doKeyEvent

doKeyEvent is called when the kEventRawKeyDown and kEventRawKeyRepeat event types are received. 
It handles all key-down events that are not Command key equivalents.

If the character code is equal to that for the escape key or lower, and except if it is the
carriage return or backspace character, the function simply returns. 

The first three lines get a handle to the TextEdit structure whose handle is stored in 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 TextEdit
structure.  If this is zero (that is, there is no selection range and an insertion point is
being displayed), the selEnd field is incremented by one.  This, in effect, creates a selection
range comprising the character following the insertion point.  TEDelete deletes the current
selection range from the TextEdit structure.  Such deletions could change the number of text
lines in the TextEdit structure, requiring the vertical scroll bar to be adjusted; hence the
call to the 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 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 number of characters minus the selection length plus 1 is less than 32,767, the
character code is passed to TEKey for insertion into the TextEdit structure.  In addition, and
since all this could change the number of lines in the TextEdit structure, the scroll bar
adjustment function is called.

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

The last line calls a 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 callback function
which will be repeatedly called by TrackControl when the kEventMouseDown event type is
received.  It will be called repeatedly while the mouse button remains down in the scroll
box/scroller, scroll arrows or gray areas/track of the vertical scroll bar.

The first line gets a reference to the window object for the window which "owns" the control. 
The next two lines get a handle to the TextEdit structure associated with the window.

Within the outer if block, the first if block executes if the control part is not the scroll
box/scroller (that is, the 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/track, 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/track 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/track.

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 TextEdit 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/scroller (that is, the indicator), the variable
linesToScroll is assigned a value equal to the control's value as it was the 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 function which prints data
extracted from the edit and control structures at the bottom of the window.

doInContent

doInContent is called when the kEventWindowClickContentRgn event type is received.

The first three lines retrieve a handle to the TextEdit structure associated with the front
window.  The call to PtInRect checks whether the mouse-down 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, TEClick is called to advise TextEdit of the mouse-down event.  Note
that the position of the shift key is passed in the second parameter.  (TEClick's behaviour
depends on the position of the shift key.)

doDrawContent

doDrawContent is called when the kEventWindowDrawContent event type is received.

The first two lines get the handle to the TextEdit structure associated with the window.

UpdateControls is called to draw the scroll bar.  The call to TEUpdate draws the text currently
in the TextEdit structure.

doActivateDeactivate

doActivateDeactivatew performs window activation/deactivation.  It is called when the
kEventWindowActivated and kEventWindowDecativated event types are received.

The first two lines retrieve a handle to the TextEdit 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 TextEdit 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 TextEdit structure associated with the window,
ActivateControl activates the scroll bar, doAdjustScrollbar adjusts the scroll bar, and
doAdjustCursor adjusts the cursor shape.

If the window is becoming inactive, TEDeactivate deactivates the TextEdit 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, associates a document structure with that window, creates a
vertical scroll bar, creates a monostyled TextEdit structure, installs the custom click loop
(callback) function, enables automatic scrolling, and enables outline highlighting.

The call to CreateNewWindow and the following block creates a new window with the standard
document window attributes less the size box/resize control.  Note that the window's graphics
port is set as the current port before the later call to TENew.  (Since the TextEdit structure
assumes the drawing environment specified in the graphics port structure, setting the graphics
port must be done before TENew creates the TextEdit structure.)

The call to TextSize sets the text size.  This, together with the default application font,
will be copied from the graphics port to the TextEdit structure when TENew is called.)

After the window event handler is installed, a document structure is created and the handle
stored in the window's window object.  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 TextEdit
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 (callback)
function customClickLoop in the clickLoop field of the TextEdit structure.  TEAutoView enables
automatic scrolling for the TextEdit structure.  TEFeatureFlag enables outline highlighting for
the TextEdit structure.

The last line returns a reference to the newly opened window's window object.

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 TextEdit structure associated with the window. 
The next two lines save the current graphics port and set the window's 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 TextEdit 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
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, the first three lines in the if block get a handle to the
TextEdit structure associated with the front window and the first call to EnableMenuItem
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 data of flavour type
'TEXT' in the scrap (the call to GetScrapFlavourFlags), the Paste item is enabled, otherwise it
is disabled.  If there is any text in the TextEdit 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.

doMenuChoice

If the MonoTextEdit Help item in the Mac OS 8/9 Apple menu or Mac OS X Help menu is chosen, the
function doHelp is called.

doFileMenu

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

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 TextEdit
structures, TECut, TECopy, and TEPaste do not copy/paste text to/from the scrap.  This program,
however, supports copying/pasting to/from the scrap.

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

The iCut case handles the Cut command.  Firstly, the call to ClearCurrentScrap attempts to
clear the 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 TextEdit 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 scrap.  If the TEToScrap
call is not successful, ClearCurrentScrap cleans up as best it can by emptying the scrap.

The iCopy case handles the Copy command.  If the call to ClearCurrentScrap to empty the scrap
is successful, TECopy is called to copy the selected text