TESample.cp

/*------------------------------------------------------------------------------------------
 
    Program:    CPlusTESample 2.0
    File:       TESample.cp
    Uses:       TEDocument.h
                TESample.h
 
    by Andrew Shebanow
    of Apple Macintosh Developer Technical Support
 
    Copyright © 1989-1990 Apple Computer, Inc.
    All rights reserved.
 
------------------------------------------------------------------------------------------*/
 
#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
 
// our class definitions
#include "TEDocument.h"
#include "TESample.h"
 
// ExtremeNeg and ExtremePos are used to set up wide open rectangles and regions.
const short kExtremeNeg = -32768;
const short kExtremePos = 32767 - 1; // required to address an old region bug
 
// kMaxOpenDocuments is used to determine whether a new document can be opened
// or created. We keep track of the number of open documents, and disable the
// menu items that create a new document when the maximum is reached. If the
// number of documents falls below the maximum, the items are enabled again.
const short kMaxOpenDocuments = 4;
 
const OSType kCreatorType = 'MOOT';
 
// Define max and min macros for efficiency.
#define max(a,b)        ((a) > (b) ? (a) : (b))
#define min(a,b)        ((a) < (b) ? (a) : (b))
 
// Our application object, initialized in main(). We make it
// global so our functions which don't belong to any class
// can find the active document.
TESample* gTheApplication;
 
// main is the entrypoint to the program
int main()
{
    // Create our application object. This MUST be the FIRST thing
    // done in main(), since it initializes the Toolbox for us.
    gTheApplication = new TESample;
    if (gTheApplication == nil)     // if we couldn't allocate object (impossible!?)
      ExitToShell();                // go back to Finder
 
    gTheApplication->ProcessArgs();
 
    // Start our main event loop running. This won't return until user quits
    gTheApplication->EventLoop();
 
    // We always return a value, like good little ANSI worshippers,
    // so that the compiler won't complain
    return 0;
}
 
// the constructor for our class, called automatically when we create
// an instance of this class. In this particular case, we only want
// one instance since the constructor does all the menu setups and
// creates our (untitled) document.
// Note that we call our base TApplication constructor, passing it
// our creator signature constant.
TESample::TESample() : TApplication(kCreatorType)
{
    Handle  menuBar;
 
    // read menus into menu bar
    menuBar = GetNewMBar(rMenuBar);
    // install menus
    SetMenuBar(menuBar);
    DisposeHandle(menuBar);
    // add DA names to Apple menu
    AppendResMenu(GetMenuHandle(mApple), 'DRVR');
    DrawMenuBar();
 
    // create empty mouse region
    fMouseRgn = NewRgn();
    // make sure we have a valid cursor region
    AdjustCursor();
}
 
// Tell TApplication class how much heap we need
long TESample::HeapNeeded()
{
    return (kMinSize * 1024);
}
 
// Calculate a sleep value for WaitNextEvent. This takes into account the things
// that DoIdle does with idle time.
 
unsigned long TESample::SleepVal()
{
    unsigned long sleep;
 
    sleep = kMaxSleepTime;              // default value for sleep
    // if we aren't in background, let document tell us how long to sleep
    if ((!fInBackground) && (fCurDoc != nil))
      sleep = min(sleep,fCurDoc->CalcIdle());
    return sleep;
}
 
// 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 TESample::DoIdle()
{
    TEDocument* fTECurDoc = (TEDocument*) fCurDoc;
 
    if (fTECurDoc != nil)
      fTECurDoc->DoIdle();
} // DoIdle
 
// Change the cursor's shape, depending on its position. This also calculates a
// region that includes the cursor for WaitNextEvent.
 
void TESample::AdjustCursor()
{
    TEDocument* fTECurDoc = (TEDocument*) fCurDoc;
 
    // notice that we don't change cursor if front window isn't ours
    if ( (!fInBackground) && (fTECurDoc != nil) )
      {
        RgnHandle   arrowRgn;
        RgnHandle   iBeamRgn;
        Point       mouse;
 
        // get mouse location and convert to global coordinates
        GetMouse(&mouse);
        LocalToGlobal(&mouse);
 
        // calculate regions for different cursor shapes
        arrowRgn = NewRgn();
        iBeamRgn = NewRgn();
        // start arrowRgn wide open
        SetRectRgn(arrowRgn, kExtremeNeg, kExtremeNeg, kExtremePos, kExtremePos);
        // calculate iBeamRgn
        fTECurDoc->GetVisTERgn(iBeamRgn);
        // subtract iBeamRgn from arrowRgn
        DiffRgn(arrowRgn, iBeamRgn, arrowRgn);
 
        // change the cursor and the region parameter
        if (PtInRgn(mouse, iBeamRgn))
          {
            SetCursor(*GetCursor(iBeamCursor));
            CopyRgn(iBeamRgn, fMouseRgn);
          }
        else
          {
            SetCursor(&qd.arrow);
            CopyRgn(arrowRgn, fMouseRgn);
          }
        // get rid of regions we don't need anymore
        DisposeRgn(arrowRgn);
        DisposeRgn(iBeamRgn);
      }
} // AdjustCursor
 
// Enable and disable menus based on the current state. The
// user can only select enabled menu items. We set up all the
// menu items before calling MenuSelect or MenuKey, since
// these are the only times that a menu item can be selected.
// Note that MenuSelect is also the only time the user will
// see menu items. This approach to deciding what enable/
// disable state a menu item has the advantage of
// concentrating all the decision-making in one routine, as
// opposed to being spread throughout the application. Other
// application designs may take a different approach that may
// or may not be as valid.
 
void TESample::AdjustMenus()
{
    WindowPtr   frontmost;
    MenuHandle  fileMenu, editMenu;
    long        offset;
    Boolean     undo;
    Boolean     cutCopyClear;
    Boolean     paste;
    Boolean     save, saveAs;
    Boolean     selectAll;
    TEDocument* fTECurDoc = (TEDocument*) fCurDoc;
 
    frontmost = FrontWindow();
 
    fileMenu = GetMenuHandle(mFile);
    if (fDocList->NumDocs() < kMaxOpenDocuments)
      {
        // New & Open are enabled when we can open more documents
        EnableItem(fileMenu, iNew);
        EnableItem(fileMenu, iOpen);
      }
    else
      {
        DisableItem(fileMenu, iNew);
        DisableItem(fileMenu, iOpen);
      }
    if (frontmost != (WindowPtr) nil)   // Close is enabled when there is a window to close
      EnableItem(fileMenu, iClose);
    else DisableItem(fileMenu, iClose);
 
    editMenu = GetMenuHandle(mEdit);
    undo = false;
    cutCopyClear = false;
    paste = false;
    save = saveAs = false;
    selectAll = false;
    if (frontmost != nil)
      {
        selectAll = true;
        if (fCurDoc == nil)
          {
            undo = true;                // all editing is enabled for DA windows
            cutCopyClear = true;
            paste = true;
          }
        else
          {
            // Cut, Copy, and Clear is enabled for app. windows with selections
            if ( fTECurDoc->HaveSelection() )
              cutCopyClear = true;
            // If we have any TEXT in the scrap, enable paste
            if ( GetScrap(nil, 'TEXT', &offset) )
                paste = true;
            // enable save if we are dirty
            save = fCurDoc->CanSave();
            saveAs = fCurDoc->CanSaveAs();
          }
      }
    if (save)
      EnableItem(fileMenu, iSave);
    else DisableItem(fileMenu, iSave);
    if (saveAs)
      EnableItem(fileMenu, iSaveAs);
    else DisableItem(fileMenu, iSaveAs);
 
    if (undo)
      EnableItem(editMenu, iUndo);
    else DisableItem(editMenu, iUndo);
    if ( cutCopyClear )
      {
        EnableItem(editMenu, iCut);
        EnableItem(editMenu, iCopy);
        EnableItem(editMenu, iClear);
      }
    else
      {
        DisableItem(editMenu, iCut);
        DisableItem(editMenu, iCopy);
        DisableItem(editMenu, iClear);
      }
    if (paste)
      EnableItem(editMenu, iPaste);
    else DisableItem(editMenu, iPaste);
    if (selectAll)
      EnableItem(editMenu, iSelectAll);
    else DisableItem(editMenu, iSelectAll);
} // AdjustMenus
 
 
// This is called when an item is chosen from the menu bar (after calling
// MenuSelect or MenuKey). It does the right thing for each command.
 
void TESample::DoMenuCommand(short menuID, short menuItem)
{
    short       itemHit;
    Str255      daName;
    short       daRefNum;
    WindowPtr   window;
    TEDocument* fTECurDoc = (TEDocument*) fCurDoc;
 
    window = FrontWindow();
    switch ( menuID )
      {
        case mApple:
            switch ( menuItem )
              {
                case iAbout:        // bring up alert for About
                    itemHit = Alert(rAboutAlert, nil);
                    break;
                default:            // all non-About items in this menu are DAs et al
                    GetMenuItemText(GetMenuHandle(mApple), menuItem, daName);
                    daRefNum = OpenDeskAcc(daName);
                    break;
              }
            break;
        case mFile:
            switch ( menuItem )
              {
                case iNew:
                    DoNew();
                    break;
                case iOpen:
                    DoOpen();
                    break;
                case iClose:
                    if (fTECurDoc != nil)
                      {
                        // only close the document if the user doesn't cancel
                        if (fTECurDoc->DoClose(true, yesResult, false) != cancelResult)
                          {
                            fDocList->RemoveDoc(fTECurDoc);
                            delete fTECurDoc;
                          }
                      }
                    else CloseDeskAcc(((WindowPeek) fWhichWindow)->windowKind);
                    // make sure our current document/window references are valid
                    fWhichWindow = FrontWindow();
                    if (fWhichWindow != nil)
                      {
                        fCurDoc = fDocList->FindDoc(fWhichWindow);
                        SetPort(fWhichWindow);
                      }
                    else fCurDoc = nil;
                    break;
                case iSave:
                    if (fTECurDoc != nil)
                      fTECurDoc->DoSave();
                    break;
                case iSaveAs:
                    if (fTECurDoc != nil)
                      fTECurDoc->DoSaveAs();
                    break;
                case iQuit:
                    DoQuit(true, yesResult);
                    break;
              }
            break;
        case mEdit:                 // call SystemEdit for DA editing & MultiFinder
            if (!SystemEdit(menuItem-1))
              {
                switch (menuItem)
                  {
                    case iCut:
                        fTECurDoc->DoCut();
                        break;
                    case iCopy:
                        fTECurDoc->DoCopy();
                        break;
                    case iPaste:
                        fTECurDoc->DoPaste();
                        break;
                    case iClear:
                        fTECurDoc->DoClear();
                        break;
                    case iSelectAll:
                        fTECurDoc->DoSelectAll();
                        break;
                   }
              }
            break;
      }
    HiliteMenu(0);                  // unhighlight what MenuSelect (or MenuKey) hilited
} // DoMenuCommand
 
// Create a new document and window.
 
void TESample::DoNew()
{
    TEDocument* tDoc;
 
    tDoc = new TEDocument(rDocWindow);
    FailNIL(tDoc);
 
    TRY
      {
        tDoc->OpenNewDoc();
        fDocList->AddDoc(tDoc);
      }
    RECOVER
      {
        delete tDoc;
      }
    ENDTRY
} // DoNew
 
void TESample::OpenADoc(short vRefNum, long dirID, StringPtr fName, OSType fType)
{
    if (fType != kTEFileType)
      Failure(eBadFileType,kTEDocErrStrings);
 
    TEDocument* tDoc = new TEDocument(rDocWindow);
    FailNIL(tDoc);
 
    TRY
      {
        CanonicalFileSpec fileSpec;
 
        fileSpec.vRefNum = vRefNum;
        fileSpec.dirID = dirID;
        CopyPString(fileSpec.fileName, fName);
 
        tDoc->OpenOldDoc(fileSpec,false);
        fDocList->AddDoc(tDoc);
      }
    RECOVER
      {
        delete tDoc;
      }
    ENDTRY
}
 
void TESample::DoOpen()
{
    Point where;
    Str255 prompt;
    SFTypeList typeList;
    SFReply reply;
    short vRefNum;
    long dirID;
 
    SetPt(&where,100,100);
    prompt[0] = '\0';
    typeList[0] = kTEFileType;
    SFGetFile(where,prompt,(FileFilterProcPtr) nil,
              1,typeList,(DlgHookProcPtr) nil,&reply);
    if (reply.good == false)
      return;
 
    WDToDirID(reply.vRefNum,vRefNum,dirID);
    OpenADoc(vRefNum,dirID,reply.fName, kTEFileType);
}