TEDocument.cp

/*------------------------------------------------------------------------------------------
 
    Program:    CPlusTESample 2.0
    File:       TEDocument.cp
    Uses:       TEDocument.h
                TESample.h
 
    by Andrew Shebanow
    of Apple Macintosh Developer Technical Support
 
    Copyright © 1989-1990 Apple Computer, Inc.
    All rights reserved.
 
------------------------------------------------------------------------------------------*/
 
// Mac Includes
#ifndef __TYPES__
#include <Types.h>
#endif
#ifndef __QUICKDRAW__
#include <QuickDraw.h>
#endif
#ifndef __FONTS__
#include <Fonts.h>
#endif
#ifndef __EVENTS__
#include <Events.h>
#endif
#ifndef __CONTROLS__
#include <Controls.h>
#endif
#ifndef __WINDOWS__
#include <Windows.h>
#endif
#ifndef __MENUS__
#include <Menus.h>
#endif
#ifndef __TEXTEDIT__
#include <TextEdit.h>
#endif
#ifndef __DIALOGS__
#include <Dialogs.h>
#endif
#ifndef __MENUS__
#include <Menus.h>
#endif
#ifndef __DEVICES__
#include <Devices.h>
#endif
#ifndef __SCRAP__
#include <Scrap.h>
#endif
#ifndef __TOOLUTILS__
#include <ToolUtils.h>
#endif
#ifndef __MEMORY__
#include <Memory.h>
#endif
#ifndef __SEGLOAD__
#include <SegLoad.h>
#endif
#ifndef __FILES__
#include <Files.h>
#endif
#ifndef __OSUTILS__
#include <OSUtils.h>
#endif
#ifndef __TRAPS__
#include <Traps.h>
#endif
#ifndef __PACKAGES__
#include <Packages.h>
#endif
#ifndef __ERRORS__
#include <Errors.h>
#endif
 
#ifndef __TEDOCUMENT__
#include "TEDocument.h"
#endif
#ifndef __TESAMPLE__
#include "TESample.h"
#endif
 
// prototypes for functions that can't belong to TEDocument, but
// which are closely tied into our documents
pascal TEClickLoopUPP GetOldClickLoop();
pascal void PascalClickLoop();
pascal void CommonAction(ControlHandle control,short* amount);
pascal void VActionProc(ControlHandle control,short part);
pascal void HActionProc(ControlHandle control,short part);
// this routine is written in Assembler, since it needs to tweak registers
pascal void ASMCLICKLOOP();
 
// kTextMargin is the number of pixels we leave blank at the edge of the window.
const short kTextMargin = 2;
 
// kMaxDocWidth is an arbitrary number used to specify the width of the TERec's
// destination rectangle so that word wrap and horizontal scrolling can be
// demonstrated.
const short kMaxDocWidth = 576;
 
// kMinDocDim is used to limit the minimum dimension of a window when GrowWindow
// is called.
const short kMinDocDim = 64;
 
// kMaxTELength is an arbitrary number used to limit the length of text in the TERec
// so that various errors won't occur from too many characters being in the text.
const short kMaxTELength = 32000;
 
// kControlInvisible is used the same way to 'turn on' the control.
const short kControlVisible = 0xFF;
 
// ScrollBarAdjust, GrowBoxAdjust, and ScrollBar width are used in calculating
// values for control positioning and sizing.
const short kScrollbarAdjust = 15;
const short kGrowboxAdjust = 15;
const short kScrollbarWidth = 16;
 
// kTESlop provides some extra security when pre-flighting edit commands.
const short kTESlop = 1024;
 
// kScrollTweek compensates for off-by-one requirements of the scrollbars
// to have borders coincide with the growbox.
const short kScrollTweek = 2;
 
// kCrChar is used to match with a carriage return when calculating the
// number of lines in the TextEdit record. kDelChar is used to check for
// delete in keyDowns.
const short kCrChar = 13;
const short kDelChar = 8;
 
// notice that we pass the resID parameter up to our base class,
// which actually creates the window for us
TEDocument::TEDocument(short resID) : TDocument(resID, kTEFileType)
{
    Boolean good;
    Rect destRect, viewRect;
 
    good = false;
    SetPort(fDocWindow);
    GetTERect(&viewRect);
    destRect = viewRect;
    destRect.right = destRect.left + kMaxDocWidth;
    fDocTE = TENew(&destRect, &viewRect);
    FailNIL(fDocTE);
 
    // set up TE record
    AdjustViewRect();
    TEAutoView(true, fDocTE);
    
    // save and replace clickLoop
    fDocClick = (*fDocTE)->clickLoop;
    TEClickLoopUPP upp = NewTEClickLoopProc(&ASMCLICKLOOP);
    (*fDocTE)->clickLoop = upp;
 
    fDocVScroll = fDocHScroll = nil;
 
    // get vertical scrollbar
    TRY
      {
        fDocVScroll = GetNewControl(rVScroll, fDocWindow);
        FailNILResource(fDocVScroll);
        fDocHScroll = GetNewControl(rHScroll, fDocWindow);
        FailNILResource(fDocHScroll);
      }
    RECOVER
      {
        TEDispose(fDocTE);
        if (fDocVScroll)
          delete fDocVScroll;
        if (fDocHScroll)
          delete fDocHScroll;
        FailNewMessage(eNoWindow,gFailMessage,kTEDocErrStrings);
      }
    ENDTRY
 
    AdjustScrollValues(true);
    ShowWindow(fDocWindow);
    SelectWindow(fDocWindow);
}
 
TEDocument::~TEDocument()
{
    HideWindow(fDocWindow);
    if ( fDocTE != nil )
      {
        DisposeRoutineDescriptor((*fDocTE)->clickLoop);
        (*fDocTE)->clickLoop = NULL;
        TEDispose(fDocTE);          // dispose the TEHandle if we got far enough to make one
      }
    if ( fDocVScroll != nil )
      {
        DisposeControl(fDocVScroll);
      }
    if ( fDocHScroll != nil )
      {
        DisposeControl(fDocHScroll);
      }
    // base class destructor will dispose of window
}
 
void TEDocument::DoZoom(short partCode)
{
    Rect tRect;
 
    tRect = fDocWindow->portRect;
    EraseRect(&tRect);
    ZoomWindow(fDocWindow, partCode, fDocWindow == FrontWindow());
    AdjustScrollbars(true);     // adjust, redraw anyway
    AdjustTE();
    InvalRect(&tRect);          // invalidate the whole content
    // the scrollbars were taken care of by AdjustScrollbars, so validate Õem
    tRect = (*fDocVScroll)->contrlRect;
    ValidRect(&tRect);
    tRect = (*fDocHScroll)->contrlRect;
    ValidRect(&tRect);
}
 
// Called when a mouseDown occurs in the grow box of an active window.
 
void TEDocument::DoGrow(EventRecord* theEvent)
{
    long growResult;
    Rect tRect, tRect2;
 
    tRect = qd.screenBits.bounds;
    tRect.left = kMinDocDim;
    tRect.top = kMinDocDim;
    growResult = GrowWindow(fDocWindow, theEvent->where, &tRect);
    // see if it really changed size
    if ( growResult != 0 )
      {
        tRect = (*fDocTE)->viewRect;
        SizeWindow(fDocWindow, LoWord(growResult), HiWord(growResult), true);
        AdjustScrollbars(true);
        AdjustTE();
        // calculate & validate the region that hasnÕt changed so it wonÕt get redrawn
        // Note: we copy rectangles so that we don't take address of object fields.
        tRect2 = (*fDocTE)->viewRect;
        (void) SectRect(&tRect, &tRect2, &tRect);
        tRect2 = fDocWindow->portRect; InvalRect(&tRect2);
        ValidRect(&tRect);
        tRect2 = (*fDocVScroll)->contrlRect; ValidRect(&tRect2);
        tRect2 = (*fDocHScroll)->contrlRect; ValidRect(&tRect2);
      }
}
 
void TEDocument::DoContent(EventRecord* theEvent)
{
    Point mouse;
    ControlHandle control;
    short part, value;
    Boolean shiftDown;
    Rect teRect;
 
    SetPort(fDocWindow);
    mouse = theEvent->where;                            // get the click position
    GlobalToLocal(&mouse);
    GetTERect(&teRect);
    if ( PtInRect(mouse, &teRect) )
      {
        // see if we need to extend the selection
        shiftDown = (theEvent->modifiers & shiftKey) != 0;  // extend if Shift is down
        TEClick(mouse, shiftDown, fDocTE);
      }
    else
      {
        part = FindControl(mouse, fDocWindow, &control);
        switch ( part )
          {
            case 0:
                // do nothing if not in a control
                break;
            case inThumb:
                value = GetControlValue(control);
                part = TrackControl(control, mouse, nil);
                if ( part != 0 )
                  {
                    value -= GetControlValue(control);
                    // value now has CHANGE in value; if value changed, scroll
                    if ( value != 0 )
                        if ( control == fDocVScroll )
                            TEScroll(0, value * (*fDocTE)->lineHeight, fDocTE);
                        else TEScroll(value, 0, fDocTE);
                  }
                break;
            default:                        // they clicked in an arrow, so track & scroll
                {
                    ControlActionUPP upp;
                    if ( control == fDocVScroll )
                        upp = NewControlActionProc(VActionProc);
                    else
                        upp = NewControlActionProc(HActionProc);
                    value = TrackControl(control, mouse, upp);
                    DisposeRoutineDescriptor(upp);
                }
                break;
          }
      }
}
 
void TEDocument::DoKeyDown(EventRecord* theEvent)
{
    char key;
 
    if (theEvent->modifiers & cmdKey)   // don't process command characters
      return;
    key = (char) (theEvent->message & charCodeMask);
    // we have a char. for our window; see if we are still below TextEditÕs
    // limit for the number of characters
    if ((key != kDelChar) &&
        ((*fDocTE)->teLength - ((*fDocTE)->selEnd - (*fDocTE)->selStart) + 1 >= kMaxTELength) )
      Failure(eExceedChar,kTEDocErrStrings);
 
    TEKey(key, fDocTE);
    AdjustScrollbars(false);
    AdjustTE();
    fDirty = true;
}
 
void TEDocument::DoActivate(Boolean becomingActive)
{
    if ( becomingActive )
      {
        RgnHandle   tempRgn;
        RgnHandle   clipRgn;
        Rect        growRect;
        Rect        tRect;
 
        // since we donÕt want TEActivate to draw a selection in an area where
        // weÕre going to erase and redraw, weÕll clip out the update region
        // before calling it.
        tempRgn = NewRgn();
        clipRgn = NewRgn();
        // save old update region
        CopyRgn(((WindowPeek) fDocWindow)->updateRgn, tempRgn);
        // put it in local coords
        OffsetRgn(tempRgn, fDocWindow->portBits.bounds.left, fDocWindow->portBits.bounds.top);
        GetClip(clipRgn);
        // subtract updateRgn from clipRgn
        DiffRgn(clipRgn, tempRgn, tempRgn);
        // make it the new clipRgn
        SetClip(tempRgn);
        TEActivate(fDocTE);
        // restore the full-blown clipRgn
        SetClip(clipRgn);
        // get rid of temp regions
        DisposeRgn(tempRgn);
        DisposeRgn(clipRgn);
 
        // the controls must be redrawn on activation:
        (*fDocVScroll)->contrlVis = kControlVisible;
        (*fDocHScroll)->contrlVis = kControlVisible;
        // copy rectangles to avoid unsafe object field references!
        tRect = (*fDocVScroll)->contrlRect; InvalRect(&tRect);
        tRect = (*fDocHScroll)->contrlRect; InvalRect(&tRect);
        // the growbox needs to be redrawn on activation:
        growRect = fDocWindow->portRect;
        // adjust for the scrollbars
        growRect.top = growRect.bottom - kScrollbarAdjust;
        growRect.left = growRect.right - kScrollbarAdjust;
        InvalRect(&growRect);
      }
    else
      {
        TEDeactivate(fDocTE);
        // the controls must be hidden on deactivation:
        HideControl(fDocVScroll);
        HideControl(fDocHScroll);
        // we draw grow icon immediately, since we deactivate controls
        // immediately, and the update delay looks funny
        DrawGrowIcon(fDocWindow);
      }
}
 
void TEDocument::DoUpdate()
{
    BeginUpdate(fDocWindow);                // this sets up the visRgn
    if ( ! EmptyRgn(fDocWindow->visRgn) )   // draw if updating needs to be done
      {
        DrawWindow();
      }
    EndUpdate(fDocWindow);
}
 
// calculate how much idle time we need
 
unsigned long TEDocument::CalcIdle()
{
    if (HaveSelection())
      return GetCaretTime();
    else return kMaxSleepTime;  // if we don't have a selection, we don't need to idle
}
 
// This is called whenever we get a null event et al.
// It takes care of necessary periodic actions. For this program,
// it calls TEIdle.
 
void TEDocument::DoIdle()
{
    TEIdle(fDocTE);
} // DoIdle
 
// Draw the contents of an application window.
 
void TEDocument::DrawWindow()
{
    Rect tRect;
 
    SetPort(fDocWindow);
    tRect = fDocWindow->portRect;
    EraseRect(&tRect);
    TEUpdate(&tRect, fDocTE);
    DrawControls(fDocWindow);
    DrawGrowIcon(fDocWindow);
} // DrawWindow
 
// Return a rectangle that is inset from the portRect by the size of
// the scrollbars and a little extra margin.
 
void TEDocument::GetTERect(Rect* teRect)
{
    *teRect = fDocWindow->portRect;
    InsetRect(teRect, kTextMargin, kTextMargin);            // adjust for margin
    teRect->bottom = teRect->bottom - kScrollbarAdjust; // and for the scrollbars
    teRect->right = teRect->right - kScrollbarAdjust;
} // GetTERect
 
// setup a region which contains the visible text
 
void TEDocument::GetVisTERgn(RgnHandle rgn)
{
    Rect teRect;
 
    teRect = (*fDocTE)->viewRect;   // get a local copy of viewRect
    SetPort(fDocWindow);            // make sure we have right port
    LocalToGlobal((Point*) &teRect.top);
    LocalToGlobal((Point*) &teRect.bottom);
    RectRgn(rgn, &teRect);
    // we temporarily change the portÕs origin to ÒglobalfyÓ the visRgn
    SetOrigin(-(fDocWindow->portBits.bounds.left),
              -(fDocWindow->portBits.bounds.top));
    SectRgn(rgn, fDocWindow->visRgn, rgn);
    SetOrigin(0, 0);
} // GetTERgn
 
void TEDocument::ReadFromFile(short refNum)
{
    long curPos, size;
    Handle h;
 
    // determine how much data is available to read
    FailOSErr(GetFPos(refNum,&curPos));
    FailOSErr(GetEOF(refNum,&size));
    size -= curPos;
 
    // check for size > 32K
    if (size > kMaxTELength)
      Failure(eExceedChar,kTEDocErrStrings);
 
    // allocate a handle to store it in
    h = NewHandle(size);
    FailNIL(h);
 
    TRY
      {
        FailOSErr(FSRead(refNum,&size,*h));
      }
    RECOVER
      {
        DisposeHandle(h);
      }
    ENDTRY
 
    // now make the text the current text
    SetPort(fDocWindow);
    HLock(h);
    TESetText(*h,size,fDocTE);
    DisposeHandle(h);
 
    // make sure everything is up to date
    Rect tRect = (*fDocTE)->viewRect;
    InvalRect(&tRect);
    AdjustTE();
    AdjustScrollbars(false);
}
 
void TEDocument::WriteToFile(short refNum)
{
    Handle h;
    long size;
 
    // get COPY of TEHandle - doesn't alloc memory
    h = (Handle) TEGetText(fDocTE);
    size = GetHandleSize(h);
    FailOSErr(FSWrite(refNum,&size,*h));
}
 
// Return boolean value indicating that there is or is not a
// selection in the document
 
Boolean TEDocument::HaveSelection()
{
    if ( (*fDocTE)->selStart < (*fDocTE)->selEnd )
      return true;
    else return false;
}
 
// Update the TERec's view rect so that it is the greatest multiple of
// the lineHeight that still fits in the old viewRect.
 
void TEDocument::AdjustViewRect()
{
    TEPtr te;
 
    te = *fDocTE;
    te->viewRect.bottom = (((te->viewRect.bottom - te->viewRect.top) / te->lineHeight)
                            * te->lineHeight) + te->viewRect.top;
} // AdjustViewRect
 
// Scroll the TERec around to match up to the potentially updated scrollbar
// values. This is really useful when the window has been resized such that the
// scrollbars became inactive but the TERec was already scrolled.
 
void TEDocument::AdjustTE()
{
    TEPtr te;
 
    te = *fDocTE;
    TEScroll((te->viewRect.left - te->destRect.left) - GetControlValue(fDocHScroll),
             (te->viewRect.top - te->destRect.top) -
                (GetControlValue(fDocVScroll) * te->lineHeight),
             fDocTE);
} // AdjustTE
 
// Re-calculate the position and size of the viewRect and the scrollbars.
// kScrollTweek compensates for off-by-one requirements of the scrollbars
// to have borders coincide with the growbox.
 
void TEDocument::AdjustScrollSizes()
{
    Rect teRect;
 
    GetTERect(&teRect);
    (*fDocTE)->viewRect = teRect;
    AdjustViewRect();
    MoveControl(fDocVScroll, fDocWindow->portRect.right - kScrollbarAdjust, -1);
    SizeControl(fDocVScroll, kScrollbarWidth,
                fDocWindow->portRect.bottom - fDocWindow->portRect.top -
                    kGrowboxAdjust + kScrollTweek);
    MoveControl(fDocHScroll, -1, fDocWindow->portRect.bottom - kScrollbarAdjust);
    SizeControl(fDocHScroll,
                fDocWindow->portRect.right - fDocWindow->portRect.left -
                    kGrowboxAdjust + kScrollTweek,
                kScrollbarWidth);
} // AdjustScrollSizes
 
// Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them
// and we don't want that). If the controls are to be resized as well, call the procedure to do that,
// then call the procedure to adjust the maximum and current values. Finally re-enable the controls
// by jamming a $FF in their contrlVis fields (ShowControl re-draws the control, which may not be
// necessary).
 
void TEDocument::AdjustScrollbars(Boolean needsResize)
{
    // First, turn visibility of scrollbars off so we wonÕt get unwanted redrawing
    (*fDocVScroll)->contrlVis = 0;
    (*fDocHScroll)->contrlVis = 0;
    if ( needsResize )
      AdjustScrollSizes();
    AdjustScrollValues(needsResize);
    // Now, restore visibility in case we never had to draw during adjustment
    (*fDocVScroll)->contrlVis = 0xff;
    (*fDocHScroll)->contrlVis = 0xff;
} // AdjustScrollbars
 
// Calculate the new control maximum value and current value, whether it is the horizontal or
// vertical scrollbar. The vertical max is calculated by comparing the number of lines to the
// vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document
// width to the width of the viewRect. The current values are set by comparing the offset between
// the view and destination rects. If necessary, redraw the control by calling ShowControl.
 
void TEDocument::AdjustHV(Boolean isVert,Boolean mustRedraw)
{
    short value, lines, max;
    short oldValue, oldMax;
    TEPtr te;
    ControlHandle control;
 
    if (isVert)
      control = fDocVScroll;
    else control = fDocHScroll;
    oldValue = GetControlValue(control);
    oldMax = GetControlMaximum(control);
    te = *fDocTE;                           // point to TERec for convenience
    if ( isVert )
      {
        lines = te->nLines;
        // since nLines isnÕt right if the last character is a return, check for that case
        if ( *(*te->hText + te->teLength - 1) == kCrChar )
          lines += 1;
        max = lines - ((te->viewRect.bottom - te->viewRect.top) /
                       te->lineHeight);
      }
    else max = kMaxDocWidth - (te->viewRect.right - te->viewRect.left);
 
    if ( max < 0 )
      max = 0;
    SetControlMaximum(control, max);
 
    // Must deref. after SetControlMaximum since, technically, it could draw and therefore move
    // memory. This is why we donÕt just do it once at the beginning.
    te = *fDocTE;
    if ( isVert )
      value = (te->viewRect.top - te->destRect.top) / te->lineHeight;
    else value = te->viewRect.left - te->destRect.left;
 
    if ( value < 0 )
      value = 0;
    else if ( value >  max )
      value = max;
 
    SetControlValue(control, value);
    // now redraw the control if asked to or if a setting changed
    if ( mustRedraw || (max != oldMax) || (value != oldValue) )
        ShowControl(control);
} // AdjustHV
 
// Simply call the common adjust routine for the vertical and horizontal scrollbars.
 
void TEDocument::AdjustScrollValues(Boolean mustRedraw)
{
    AdjustHV(true, mustRedraw);
    AdjustHV(false, mustRedraw);
} // AdjustScrollValues
 
TEClickLoopUPP TEDocument::GetClickLoop()
{
    return fDocClick;
}
 
TEHandle TEDocument::GetTEHandle()
{
    return fDocTE;
}
 
void TEDocument::DoCut()
{
    long total, contig;
 
    if (ZeroScrap() == noErr)
      {
        PurgeSpace(&total, &contig);
        if ((*fDocTE)->selEnd - (*fDocTE)->selStart + kTESlop > contig)
          Failure(eNoSpaceCut,kTEDocErrStrings);
 
        TECut(fDocTE);
        fDirty = true;
        if (TEToScrap() != noErr)
          {
            (void) ZeroScrap();
            Failure(eNoCut,kTEDocErrStrings);
          }
      }
    AdjustScrollbars(false);
    AdjustTE();
}
 
void TEDocument::DoCopy()
{
    if (ZeroScrap() == noErr)
      {
        TECopy(fDocTE);             // after copying, export the TE scrap
        if (TEToScrap() != noErr)
          {
            ZeroScrap();
            Failure(eNoCopy,kTEDocErrStrings);
          }
      }
    AdjustScrollbars(false);
    AdjustTE();
}
 
void TEDocument::DoPaste()
{
    Handle aHandle;
    long oldSize, newSize;
    OSErr saveErr;
 
    if (TEFromScrap() != noErr)
      Failure(eNoPaste,kTEDocErrStrings);
 
    if ( TEGetScrapLength() + ((*fDocTE)->teLength -
         ((*fDocTE)->selEnd - (*fDocTE)->selStart)) > kMaxTELength )
      Failure(eExceedPaste,kTEDocErrStrings);
 
    aHandle = (Handle) TEGetText(fDocTE);
    oldSize = GetHandleSize(aHandle);
    newSize = oldSize + TEGetScrapLength() + kTESlop;
 
    // preflight the growth of the text handle for textedit,
    // since it will crash if it doesn't have enough memory
    SetHandleSize(aHandle, newSize);
    saveErr = MemError();
    SetHandleSize(aHandle, oldSize);
    if (saveErr != noErr)
      Failure(eNoSpacePaste,kTEDocErrStrings);
 
    TEPaste(fDocTE);
    fDirty = true;
 
    AdjustScrollbars(false);
    AdjustTE();
}
 
void TEDocument::DoClear()
{
    TEDelete(fDocTE);
    fDirty = true;
    AdjustScrollbars(false);
    AdjustTE();
}
 
void TEDocument::DoSelectAll()
{
    long selSize = (*fDocTE)->teLength;
    TESetSelect(0,selSize,fDocTE);
}
 
/*
    Routines used by this class, which don't belong to the class since we use
    them as toolbox filter routines, and you cannot pass class methods as ProcPtrs.
*/
 
// we refer back to the owning application so that we can get access to
// the document list, to find the current document object.
extern TESample* gTheApplication;
 
// Common algorithm for pinning the value of a control. It returns the actual amount
// the value of the control changed.
 
pascal void CommonAction(ControlHandle control,short* amount)
{
    short       value, max;
 
    value = GetControlValue(control);
    max = GetControlMaximum(control);
    *amount = value - *amount;
    if ( *amount <= 0 )
        *amount = 0;
    else if ( *amount >= max )
        *amount = max;
    SetControlValue(control, *amount);
    *amount = value - *amount;
} // CommonAction
 
 
// Determines how much to change the value of the vertical scrollbar by and how
// much to scroll the TE record.
 
pascal void VActionProc(ControlHandle control,short part)
{
    short       amount;
    WindowPtr   window;
    TEPtr       te;
    TEDocument* doc;
 
    if ( part != 0 )
      {
        window = (*control)->contrlOwner;
        doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(window);
        te = *(doc->GetTEHandle());
        switch ( part )
          {
            case inUpButton:
            case inDownButton:      // one line
                amount = 1;
                break;
            case inPageUp:          // one page
            case inPageDown:
                amount = (te->viewRect.bottom - te->viewRect.top) / te->lineHeight;
                break;
          }
        if ( (part == inDownButton) || (part == inPageDown) )
            amount = -amount;       // reverse direction for a downer
        CommonAction(control, &amount);
        if ( amount != 0 )
            TEScroll(0, amount * te->lineHeight, doc->GetTEHandle());
      }
} // VActionProc
 
// Determines how much to change the value of the horizontal scrollbar by and how
// much to scroll the TE record.
 
pascal void HActionProc(ControlHandle control,short part)
{
    short       amount;
    WindowPtr   window;
    TEPtr       te;
    TEDocument* doc;
 
    if ( part != 0 )
      {
        window = (*control)->contrlOwner;
        doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(window);
        te = *(doc->GetTEHandle());
        switch ( part )
          {
            case inUpButton:
            case inDownButton:      // a few pixels
                amount = 4;
                break;
            case inPageUp:          // a page
            case inPageDown:
                amount = te->viewRect.right - te->viewRect.left;
                break;
          }
        if ( (part == inDownButton) || (part == inPageDown) )
            amount = -amount;       // reverse direction
        CommonAction(control, &amount);
        if ( amount != 0 )
            TEScroll(amount, 0, doc->GetTEHandle());
      }
} // VActionProc
 
// Gets called from our assembly language routine, AsmClickLoop, which is in
// turn called by the TEClick toolbox routine. Saves the windows clip region,
// sets it to the portRect, adjusts the scrollbar values to match the TE scroll
// amount, then restores the clip region.
 
pascal void PascalClickLoop()
{
    RgnHandle   region;
    WindowPtr wind;
    TEDocument* doc;
 
    wind = FrontWindow();
    doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(wind);
    region = NewRgn();
    GetClip(region);    // save clip
    ClipRect(&wind->portRect);
    doc->AdjustScrollValues(false);
    SetClip(region);    // restore clip
    DisposeRgn(region);
} // PascalClickLoop
 
// Gets called from our assembly language routine, AsmClickLoop, which is in
// turn called by the TEClick toolbox routine. It returns the address of the
// default clickLoop routine that was put into the TERec by TEAutoView to
// AsmClickLoop so that it can call it.
 
pascal TEClickLoopUPP GetOldClickLoop()
{
    TEDocument* doc;
 
    doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(FrontWindow());
    if (doc == nil)
      return nil;
    return doc->GetClickLoop();
} // GetOldClickLoop