Main.c

/*****************************************************************
 
    Program:    < ADSP Chat >
    File:       < Main.c >
    
    Written by  Pete Helm, Scott Kuechle
    of <Apple Macintosh Developer Technical Support>
    
    modified by Scott Kuechle
    10/92 SRK Converted from Pascal to C
    8/94 SRK Modified to use a queue of parameter
             blocks.
 
    Copyright © 1992, 1994 Apple Computer, Inc.
    All rights reserved.
    
*****************************************************************/
 
 
/*****************************************************************/
/*  I N C L U D E S
/*****************************************************************/
 
#include    "ADSP Chat.h"
 
/*****************************************************************/
/*  G L O B A L   V A R I A B L E   D E C L A R A T I O N S
/*****************************************************************/
 
DialogPtr myDialog;
 
Boolean gHasWaitNextEvent,gInBackground,gGrowRect,gStopped,gStopRect,gGoRect,gHasSystem7;
Boolean gMoofEntityFilter;
 
MenuHandle ZoneMenu,ObjectMenu,TypeMenu;
short LastZoneMenuChoice,LastTypeMenuChoice,LastObjectMenuChoice;
SysEnvRec gMac;
short gMenuItem;
 
/*****************************************************************/
/*
/* E X T E R N A L S
/*
/*****************************************************************/
 
 
extern  myFillRoundRectAnimationRtn (Rect r, PatPtr pat);
extern  void connectToPeer();
extern  void removeADSP();
extern  void initializeADSP();
extern  removeMyName();
extern  void ADSPLoop();
extern  OSErr InitAppleTalk();
extern  Boolean ATPZoneRequest (MenuHandle zoneMenu);
extern  LookupNames (MenuHandle lookupMenu,Boolean doObjects);
extern  GetZones();
extern  GetOurZone();
extern  void writeOutgoing(DSPPBPtr dspPBPtr,
                            short ccbRefNum,
                            Ptr dataPtr,
                            short reqCount);
extern  void removeConnectionEnd();
extern  void removeADSPBuffers();
 
extern  Str255 gZoneString,gTypeStr,gObjStr;
extern  Boolean gConnectionWasJustMade;
extern  TRCCB gMyCCB;               /* our ccb */
extern  QHdr    gAvailQueue,gReadQueue,gDoneQueue;
extern  short   gCCBRefNum;         /* ccb reference number returned by adsp on a dspInit */
extern  Ptr outgoingDataBuffer;
extern  DSPPBPtr GetQElement(QHdrPtr qHdrPtr);
 
 
#pragma segment Main
 
// *****************************************************************
// *    CopyPstr
// *
// *    copies a pascal string
// *****************************************************************
void CopyPstr(Ptr pSource, Ptr pDest)
{
    BlockMove(pSource, pDest, pSource[0]+1);
}
 
// *****************************************************************
// *    PStrCat
// *
// *    appends pascal string sourceStr, to pascal string destinationStr.
// *    Overflow is checked, and the copy halts if the destination
// *    string is filled.
// *****************************************************************
void PStrCat(Ptr sourceStr, Ptr destinationStr)
{
    unsigned int    srcIndex, dstIndex;
    unsigned int    bytesToCopy;
    
    srcIndex = 1;
    dstIndex = destinationStr[0] + 1;
    bytesToCopy = sourceStr[0];
    
    while(bytesToCopy > 0 && dstIndex < 255)
    {
        destinationStr[dstIndex] = sourceStr[srcIndex];
        dstIndex++;
        srcIndex++;
        bytesToCopy--;
    }
    destinationStr[0] = dstIndex - 1;
}
 
// *****************************************************************
// *    DoActivate
// *
// * This is called when a window is activated or deactivated.
// * In Sample, the Window Manager's handling of activate and
// * deactivate events is sufficient. Other applications may have
// * TextEdit records, controls, lists, etc., to activate/deactivate.
// *****************************************************************
void DoActivate (WindowPtr window, Boolean becomingActive)
{
#pragma unused (window,becomingActive)
 
} /*DoActivate*/
 
 
// *****************************************************************
// *    ShowError
// *
// * Our routine for displaying an error message in a dialog box.
// *****************************************************************
void ShowError(short index)
{
short itemHit;
 
 
    switch (index)
    {
        case atalkErr:
            ParamText("\perror loading AppleTalk drivers!","\p","\p","\p");
            break;
        case memErr:
            ParamText("\pmemory error.","\p","\p","\p");
            break;
        case menuErr:
            ParamText("\perror initializing menus.","\p","\p","\p");
            break;
        case nbpErr:
            ParamText("\pCouldn't register our name on the network (duplicate already exists).","\p","\p","\p");
            break;
        case noTargetErr:
            ParamText("\pTarget not found.","\p","\p","\p");
            break;
        case noConnErr:
            ParamText("\pNo connection has been made.","\p","\p","\p");
            break;
        case writeNotDoneErr:
            ParamText("\pPrevious write has not completed yet.","\p","\p","\p");
            break;
        case badROMsErr:
            ParamText("\pYour machine does not have at least 128K ROMs.","\p","\p","\p");
            break;
        case heapErr:
            ParamText("\pNot enough heap space.","\p","\p","\p");
            break;
        case noMemErr:
            ParamText("\pNot enough memory available for our application.","\p","\p","\p");
            break;
        case DrvrErr:
            ParamText("\pFatal Device Manager error.","\p","\p","\p");
            break;
        case ListenErr:
            ParamText("\pCould not setup connection listener.","\p","\p","\p");
            break;
        case dspInitErr:
            ParamText("\pADSP Initialization failed.","\p","\p","\p");
            break;
        case dspOpenErr:
            ParamText("\pAttempt to open connection failed.","\p","\p","\p");
            break;
        case dspRemoveErr:
            ParamText("\pError returned while eliminating the connection end.","\p","\p","\p");
            break;
        
        default:
            ParamText("\pdefault error displayed here.","\p","\p","\p");
            break;
 
    }
    
    itemHit = Alert(rErrorDialog, nil);
 
}
 
// *****************************************************************
// *    FatalError
// *
// * This is called when we detect that we cannot operate in the
// * current environment.
// *****************************************************************
void FatalError(error)
    short error;
{
 
    ShowError(error);
    ExitToShell();
}
 
// *****************************************************************
// *    IsAppWindow
// *
// *    Tells us whether or not a window is an application window
// *
// *****************************************************************
Boolean IsAppWindow(window)
    WindowPtr   window;
{
    short       windowKind;
 
    if ( window == nil )
        return false;
    else {  /* application windows have windowKinds >= userKind (8) or dialogKind (2) */
        windowKind = ((WindowPeek) window)->windowKind;
        return (windowKind >= userKind) || (windowKind == dialogKind);
    }
} /* IsAppWindow */
 
// *****************************************************************
// *    DialogEditing
// *
// *    handles the standard editing operations (cut, copy, paste)
// *    for our dialog.
// *****************************************************************
void DialogEditing (short menuItem)
{
    /* handles Edit menu */
 
    DialogPeek dp;
    short ignore;
    long evenBiggerIgnore;
 
        dp = (DialogPeek)myDialog;
 
        switch(menuItem)
        {
            case iCut: 
                    evenBiggerIgnore = ZeroScrap(); /* you really shouldn't ignore this error.  see TESample for details */
                    TECut(dp->textH);
                    ignore = TEToScrap; /* you really shouldn't ignore this error.  see TESample for details */
                break;
            case iCopy: 
                    evenBiggerIgnore = ZeroScrap(); /* you really shouldn't ignore this error.  see TESample for details */
                    TECopy(dp->textH);
                    ignore = TEToScrap; /* you really shouldn't ignore this error.  see TESample for details */
                break;
            case iPaste: 
                        /* you really shouldn't ignore this either.                                             */
                        /* one should be checking the length of the incoming scrap to see if it         */
                        /* will fit in our remaining text edit space.  but since we have a weenine 255  */
                        /* char dialog text edit item in any case we'll blow it off...see TESample for  */
                        /* details on "doing the right thing"                                                   */
                    ignore = TEFromScrap;
                    TEPaste(dp->textH);
                break;
            case iClear: 
                TEDelete(dp->textH);
        }
}
 
 
// *****************************************************************
// *    IsDAWindow
// *
// *    Check if a window belongs to a desk accessory.
// *****************************************************************
Boolean IsDAWindow(WindowPtr window)
{
 
    if (window == nil)
        return false;
    else    /* DA windows have negative windowKinds */
        return((((WindowPeek)window)->windowKind < 0) ? true : false);
} /*IsDAWindow*/
 
 
// *****************************************************************
// *    DoCloseWindow
// *
// * Close a window. This handles only desk accessory windows because we do not
// * allow our window to be closed. TESample provides an example of how to handle
// * the closing of application windows.
// *****************************************************************
void DoCloseWindow (WindowPtr window)
{
/* Close a window. This handles only desk accessory windows because we do not*/
/* allow our window to be closed. TESample provides an example of how to handle*/
/* the closing of application windows.*/
 
 
    if (IsDAWindow(window))
        CloseDeskAcc(((WindowPeek)window)->windowKind);
} /*DoCloseWindow*/
 
 
// *****************************************************************
// *    HiliteConnectButton
// *
// *    sets our connect button to the desired state (active or inactive)
// *****************************************************************
void HiliteConnectButton (short mode)
{
    Rect r;
    short kind;
    Handle h;
 
        GetDItem(myDialog, kConnectButtonID, &kind, &h, &r);
        HiliteControl((ControlHandle)h, mode);
}
 
// *****************************************************************
// *    outlinePopUpMenus
// *
// *    this is a group routine for drawing the popup menu outlines
// *    compleat with drop shadow. the menu title is draw to the left
// *    of the popUp item itself by this routine.
// *****************************************************************
void outlinePopUpMenus (WindowPtr whichWindow, Rect r, Str255 itemString)
{
 
 
    FrameRect(&r);
    MoveTo(r.left + 2, r.bottom);
    LineTo(r.right, r.bottom);
    MoveTo(r.right, r.bottom);
    LineTo(r.right, r.top + 2);
    InsetRect(&r, 1, 1);
    EraseRect(&r);
    InsetRect(&r, -1, -1);
    MoveTo(r.left + 5, r.top + 11);
    TextFont(geneva);
    TextSize(9);
    DrawString(itemString);
    drawPopUpTri(whichWindow, r);
 
}
 
 
 
 
// *****************************************************************
// *    UpdateUserItems
// *
// *    update procedure for the user items in our dialog.
// *****************************************************************
pascal void UpdateUserItems (WindowPtr whichWindow,
                             short theItem)
{
    GrafPtr savedPort;
    Rect r;
    short kind;
    Handle h;
 
        GetPort(&savedPort);
        SetPort(whichWindow);
        GetDItem(whichWindow, theItem, &kind, &h, &r);
        switch(theItem)
        {
            case kzoneItemID: 
                outlinePopUpMenus(whichWindow, r, gZoneString);
                break;
                
            case ktypeItemID: 
                outlinePopUpMenus(whichWindow, r, gTypeStr);
                break;
 
            case kobjectItemID: 
                outlinePopUpMenus(whichWindow, r, gObjStr);
                break;
 
            case kRemoteMacsTimeBorderID: 
                UpdateItemBorder(theItem, r);
                break;
 
            case kIncomingMessageBorderID: 
                UpdateItemBorder(theItem, r);
                break;
 
            case kConnectStatusBorder: 
                UpdateItemBorder(theItem, r);
                break;
 
            case kPopupBorderID: 
                UpdateItemBorder(theItem, r);
                break;
 
        }
        
        SetPort(savedPort);
}
 
 
// *****************************************************************
// *    DoModeless
// *
// * this handles mouse clicks inside our main dialog
// *****************************************************************
void DoModeless (DialogPtr whichDialog, short whichItem)
{
    Rect r;
    short kind;
    Handle h;
    MenuHandle allPurposeMenu;
    long menuResult;
    Point pt;
 
 
        if (whichDialog == myDialog)
            {
                GetDItem(whichDialog, whichItem, &kind, &h, &r);
                switch(whichItem)
                {
                    case kQuitButtonID: 
                        Terminate();
                            break;
                    case kzoneItemID:
                        pt.v = r.top;
                        pt.h = r.left + 1;
                        LocalToGlobal(&pt);
                        SetCursor(*(GetCursor(watchCursor)));
                        allPurposeMenu = NewMenu(ZoneMenuID, "");
                        InsertMenu(allPurposeMenu, -1);
                        GetZones(allPurposeMenu);
 
                        InitCursor();
                        menuResult = PopUpMenuSelect(allPurposeMenu, pt.v, pt.h, 1);
                        if (menuResult != 0)
                        {
                            GetItem(allPurposeMenu, LoWord(menuResult), gZoneString);
                            gMenuItem = LoWord(menuResult);
                            CopyPstr("\p", gObjStr);
                            InvalRect(&r);                                      
                        }
                        InitCursor();
                        DeleteMenu(ZoneMenuID);
                        DisposeMenu(allPurposeMenu);
                            break;
                    case ktypeItemID:
                        if (gMoofEntityFilter == false)
                        {
                            pt.v = r.top - 2;
                            pt.h = r.left + 1;
                            LocalToGlobal(&pt);
                            allPurposeMenu = NewMenu(TypeMenuID, "");
                            InsertMenu(allPurposeMenu, -1);
                            SetCursor(*(GetCursor(watchCursor)));
                            LookupNames(allPurposeMenu, false);
                            InitCursor();
                            menuResult = PopUpMenuSelect(allPurposeMenu, pt.v, pt.h, 1);
                            if (menuResult != 0)
                            {
                                GetItem(allPurposeMenu, LoWord(menuResult), gTypeStr);
                                if (strcmp(gTypeStr,"Moof") == 0)
                                    HiliteConnectButton(0);
                                else
                                    HiliteConnectButton(255);
                                gMenuItem = LoWord(menuResult);
                                InvalRect(&r);
                            }
                            
                            DeleteMenu(TypeMenuID);
                            DisposeMenu(allPurposeMenu);
                        }
                        else
                            SysBeep(1);
                            break;
                    case kobjectItemID:
                        pt.v = r.top - 2;
                        pt.h = r.left + 1;
                        LocalToGlobal(&pt);
                        allPurposeMenu = NewMenu(ObjectMenuID, "");
                        InsertMenu(allPurposeMenu, -1);
                        SetCursor(*(GetCursor(watchCursor)));
                        LookupNames(allPurposeMenu, true);
                        InitCursor();
                        menuResult = PopUpMenuSelect(allPurposeMenu, pt.v, pt.h, 1);
                        if (menuResult != 0)
                        {
                            GetItem(allPurposeMenu, LoWord(menuResult), gObjStr);
                            gMenuItem = LoWord(menuResult);
                            InvalRect(&r);
                        }
 
 
                        DeleteMenu(ObjectMenuID);
                        DisposeMenu(allPurposeMenu);
                            break;
                    case kConnectButtonID: /* connect button */
                            connectToPeer();
                            break;
                    case kMoofFilterCheckBox: 
                        if (gMoofEntityFilter == false)
                        {
                            SetCtlValue((ControlHandle)h,1);
                            gMoofEntityFilter = true;
                            CopyPstr("\pMoof", gTypeStr);
                            HiliteConnectButton(0);
                        }
                        else
                        {
                            SetCtlValue((ControlHandle)h,0);
                            gMoofEntityFilter = false;
                            CopyPstr("\p", gTypeStr);
                            if (gMyCCB.state == sClosed)
                                HiliteConnectButton(255);
                            
                        }
                        
                        gObjStr[0] = 0;
                        InvalRect(&myDialog->portRect);
 
                            break;  /* iMoofFilter */
 
                        
                } /* of case */
            }
    }
 
// *****************************************************************
// *    setEachUserItem
// *
// *    sets the update routine for our dialogs user items
// *****************************************************************
void setEachUserItem (short item)
{
    Rect r;
    short kind;
    Handle h;
    
        GetDItem(myDialog, item, &kind, &h, &r);
        SetDItem(myDialog, item, userItem, (Handle)UpdateUserItems, &r);
}
 
 
// *****************************************************************
// *    DoMenuCommand
// *
// *This is called when an item is chosen from the menu bar (after calling
// * MenuSelect or MenuKey). It performs the right operation for each command.
// * It is good to have both the result of MenuSelect and MenuKey go to
// * one routine like this to keep everything organized.
// *****************************************************************
void DoMenuCommand (long menuResult)
{
    short menuID;       /*the resource ID of the selected menu*/
    short menuItem;     /*the item number of the selected menu*/
    short itemHit;
    Str255 daName;
    short daRefNum;
    Boolean handledByDA;
 
 
        menuID = HiWord(menuResult);    /* use built-ins (for efficiency)...*/
        menuItem = LoWord(menuResult);  /*to get menu item number and menu number*/
        switch (menuID)
        {
        case mApple: 
                switch(menuItem)
                {
                    case iAbout:                /*bring up alert for About*/
                        itemHit = Alert(rAboutDialog, nil);
                        break;
                    default:
                            /*all non-About items in this menu are DAs*/
                        GetItem(GetMHandle(mApple), menuItem, daName);
                        daRefNum = OpenDeskAcc(daName);
                        break;
                }
            break;
        case mFile: 
                switch(menuItem)
                {
                    case iClose: 
                        DoCloseWindow(FrontWindow());
                        break;
                    case iQuit: 
                        Terminate();
                        break;
                }
            break;
        case mEdit:                     /*call SystemEdit for DA editing & MultiFinder*/
                {
                    if (IsDAWindow(FrontWindow()))
                        handledByDA = SystemEdit(menuItem - 1);
                    else
                        DialogEditing(menuItem);
                }
            break;
        }
        
        HiliteMenu(0);                  /*unhighlight what MenuSelect (or MenuKey) hilited*/
        
} /*DoMenuCommand*/
 
 
// *****************************************************************
// *    AdjustMenus
// *
// * 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 is just as valid.
// *****************************************************************
void AdjustMenus()
{
    WindowPtr window;
    MenuHandle menu;
 
 
        window = FrontWindow();
 
        menu = GetMHandle(mFile);
        if (IsDAWindow(window))             /*we can allow desk accessories to be closed from the menu*/
            EnableItem(menu, iClose);
        else
            DisableItem(menu, iClose);          /*but not our traffic light window*/
 
        menu = GetMHandle(mEdit);
        if (IsDAWindow(window))
        {                           /*a desk accessory might need the undo item */
            EnableItem(menu, iUndo);
            EnableItem(menu, iCut);
            EnableItem(menu, iCopy);
            EnableItem(menu, iPaste);
            EnableItem(menu, iClear);
        }
        else
        {                           /* but we do not support undo!!! */
            DisableItem(menu, iUndo);
            EnableItem(menu, iCut);
            EnableItem(menu, iCopy);
            EnableItem(menu, iPaste);
            EnableItem(menu, iClear);
        }
 
} /*AdjustMenus*/
 
// *****************************************************************
// *    DisplayCurrentStatus
// *
// *    updates our connection status string.
// *****************************************************************
void DisplayCurrentStatus(Ptr displayStr)
{
    Rect r;
    short kind;
    Handle h;
    
    Str255 current;
 
        GetDItem(myDialog, kConnectionStatusString, &kind, &h, &r);
        GetIText(h,&current);
        if (IUCompString(current,displayStr) != 0)
            SetIText(h, displayStr);
 
}
 
 
 
// *****************************************************************
// *    DisposeQueueMemory
// *
// *    deallocate the memory we used for our error queue
// *****************************************************************
void DisposeQueueMemory(QHdrPtr qHdrPtr)
{
    QElemPtr QElem;
    OSErr err;
 
            /* if free queue has any items, dispose of them */
        while (qHdrPtr->qHead != nil)
        {
            QElem = qHdrPtr->qHead;
            err = Dequeue(QElem,qHdrPtr);
            DisposePtr((Ptr)QElem);
        }
}
 
 
// *****************************************************************
// *    Terminate
// *
// *    if we need to quit the program, we remove the connection
// *    end (if any), deallocate any memory we used, remove our
// *    nbp name from the network and exit.
// *****************************************************************
void Terminate()
{
        /* did we establish a connection end? */
    if (gCCBRefNum != 0)
            /* do a dspRemove to close the connection end */
        removeConnectionEnd(gCCBRefNum);
 
        /* deallocate the memory we used for our adsp buffers */
    removeADSPBuffers();
        /* remove our nbp name "moof" */
    removeMyName();
 
        /* dispose of memory we allocated
            for our queues */
    DisposeQueueMemory(&gAvailQueue);
    DisposeQueueMemory(&gDoneQueue);
    DisposeQueueMemory(&gReadQueue);
    
    ExitToShell();
}
 
// *****************************************************************
// *    Exit
// *
// *    this routine is called for fatal control call errors. We exit
// *    the program in this situation.
// *****************************************************************
void Exit(short message)
{
    ShowError(message);
    Terminate();
}
 
// *****************************************************************
// *    DoIdleProc
// *
// *    this routine is called each time through our event loop. We
// *    process any adsp errors that were returned and check for connections
// *    coming and going.
// *****************************************************************
void DoIdleProc()
{
    ADSPLoop();
}
 
// *****************************************************************
// *    AdjustCursor
// *
// *Change the cursor's shape, depending on its position. This also calculates the region
// * where the current cursor resides (for WaitNextEvent). If the mouse is ever outside of
// * that region, an event would be generated, causing this routine to be called,
// * allowing us to change the region to the region the mouse is currently in. If
// * there is more to the event than just Òthe mouse movedÓ, we get called before the
// * event is processed to make sure the cursor is the right one. In any (ahem) event,
// * this is called again before we fall back into WNE.
// *****************************************************************
void AdjustCursor (Point mouse, RgnHandle region)
{
    WindowPtr window;
    RgnHandle arrowRgn;
    RgnHandle ourRgn;
    Rect globalPortRect;
 
    Rect r;
    short kind;
    Handle h;
 
        window = FrontWindow(); /*we only adjust the cursor when we are in front*/
        if ((!gInBackground) && (!IsDAWindow(window)))
        {
                /*calculate regions for different cursor shapes*/
            arrowRgn = NewRgn();
            ourRgn = NewRgn();
 
                /*start with a big, big rectangular region*/
            SetRectRgn(arrowRgn, extremeNeg, extremeNeg, extremePos, extremePos);
 
                /*calculate ourRgn*/
            if (IsAppWindow(window))
            {
                SetPort(window);            /*make a global version of the portRect*/
                SetOrigin(-window->portBits.bounds.left, -window->portBits.bounds.top);
                globalPortRect = window->portRect;
                RectRgn(ourRgn, &globalPortRect);
                SectRgn(ourRgn, window->visRgn, ourRgn);
                SetOrigin(0, 0);
            }
 
                /*subtract other regions from arrowRgn*/
            DiffRgn(arrowRgn, ourRgn, arrowRgn);
 
                /*change the cursor and the region parameter*/
            if (PtInRgn(mouse, ourRgn))
            {
                GetDItem(myDialog, kOutgoingTextID, &kind, &h, &r);
                GlobalToLocal(&mouse);
                if (PtInRect(mouse, &r))
                    SetCursor(*GetCursor(iBeamCursor));
                else
                    SetCursor(&qd.arrow);
                CopyRgn(ourRgn, region);
            }
            else
            {
 
                SetCursor(&qd.arrow);
                CopyRgn(arrowRgn, region);
 
            }
 
                /*get rid of our local regions*/
            DisposeRgn(arrowRgn);
            DisposeRgn(ourRgn);
        }
} /*AdjustCursor*/
 
// *****************************************************************
// *    UpdateItemBorder
// *
// *    re-draws the borders around our items
// *****************************************************************
void UpdateItemBorder (short item, Rect r)
{
    FontInfo fInfo;
    Rect tRect;
    Str255 theBorderString;
 
        switch(item)
        {
            case kRemoteMacsTimeBorderID:
                    strcpy(&theBorderString,"Time from remote Mac");
                break;
            case kIncomingMessageBorderID:
                    strcpy(&theBorderString,"Incoming message");
                break;
            case kPopupBorderID:
                    strcpy(&theBorderString,"Select A Target Machine");
                break;
            case kOutgoingMessageBorderID:
                    strcpy(&theBorderString,"Outgoing message");
                break;
            case kConnectStatusBorder:
                    strcpy(&theBorderString,"Connection Status");
                break;
 
            default:
                break;
        }
 
        c2pstr(theBorderString);
 
        GetFontInfo(&fInfo);
        FrameRect(&r);
        MoveTo(r.left + 5, r.top + fInfo.ascent / 2 - 1);
        tRect.left = r.left + 4;
        tRect.right = tRect.left + StringWidth(theBorderString) + 1;
        tRect.top = r.top;
        tRect.bottom = r.top + 1;
        EraseRect(&tRect);
        DrawString(&theBorderString);
 
}
 
// *****************************************************************
// *    PlotSICN
// *
// * this is the PlotSICN code stolen from Tech Note #
// *****************************************************************
void PlotSICN (Rect theRect,SICNHand theSICN, short theIndex)
{
 
    SignedByte state;   /* we want a chance to restore original state */
    BitMap srcBits;     /* built up around 'SICN' data so we can _CopyBits */
 
        /* check the index for a valid value */
        if ((GetHandleSize((Handle)theSICN) / sizeof(SICN)) > theIndex)
        {
 
            /* store the resource's current locked/unlocked condition */
            state = HGetState((Handle)theSICN);
 
            /* lock the resource so it won't move during the _CopyBits call */
            HLock((Handle)theSICN);
 
            /* set up the small icon's bitmap */
            /*$PUSH*/
            /*$R-*/
            /* turn off range checking */
            srcBits.baseAddr = (Ptr)(&(**theSICN[theIndex]));
            /*$POP*/
            srcBits.rowBytes = 2;
            SetRect(&srcBits.bounds, 0, 0, 16, 16);
 
            /* draw the small icon in the current grafport */
            CopyBits(&srcBits, &qd.thePort->portBits, &srcBits.bounds, &theRect, srcCopy, nil);
 
            /* restore the resource's locked/unlocked condition */
            HSetState((Handle)theSICN, state);
        }
 
}
 
// *****************************************************************
// *    drawPopUpTri
// *
// * this procedure draws the new "standard" SICN triangle now used
// * in popup menus I guess the drop shadow was not enough of an
// * indiciation to the users of the presence of the pop-up.
// * standard in 7.0 and up systems 
// *****************************************************************
void drawPopUpTri (WindowPtr whichWindow, Rect r)
{
 
#pragma unused (whichWindow)
 
    Handle popUpTri;
    Rect popUpTriRect;
 
        popUpTri = GetResource('SICN', kStandardTriSICN);
        if (popUpTri)
        {
            popUpTriRect = r;
            popUpTriRect.right = popUpTriRect.right - 1;
            popUpTriRect.left = popUpTriRect.right - 16;
            popUpTriRect.top = popUpTriRect.top + 1;
            popUpTriRect.bottom = popUpTriRect.top + 16;
            PlotSICN(popUpTriRect, (SICNHand)popUpTri, 0);
            ReleaseResource(popUpTri);
        }
 
 
}
 
// *****************************************************************
// *    DoEvent
// *
// * Do the right thing for an event. Determine what kind of event it is, and call
// * the appropriate routines.
// *****************************************************************
void DoEvent (EventRecord event)
{
    short part;
    char key;
    WindowPtr whichWindow;
    DialogPeek dp;
    short teLength;
    DSPPBPtr dspPBPtr;
 
 
    whichWindow = FrontWindow();
 
        switch(event.what)
        {
            case nullEvent: 
                break;
            case mouseDown: 
                part = FindWindow(event.where, &whichWindow);
                if (part != 0)
                {
                    switch(part)
                    {
                        case inMenuBar: 
                            /*process the menu command*/
                                AdjustMenus();
                                DoMenuCommand(MenuSelect(event.where));
                            break;
                        case inSysWindow:               /*let the system handle the mouseDown*/
                            SystemClick(&event, whichWindow);
                            break;
 
                        case inContent: 
                            if (whichWindow != FrontWindow())
                                SelectWindow(whichWindow);
                            break;
 
                        case inDrag:                        /*pass screenBits.bounds to get all gDevices*/
                            DragWindow(whichWindow, event.where, &qd.screenBits.bounds);
                            break;
                        
                        case inGrow: 
                            break;
                        case inZoomIn:
                        case inZoomOut:
                            break;
                    }
                }
                break;
            case keyDown:
            case autoKey:
                                /*check for menukey equivalents*/
                    key = (char)(event.message & charCodeMask);
                    if ((event.modifiers & cmdKey) != 0)    /*Command key down*/
                    {
                        if ((event.what == keyDown) || (event.what == autoKey))
                        {
                            AdjustMenus();          /*enable/disable/check menu items properly*/
                            DoMenuCommand(MenuKey(key));
                        }
                    }
                    else if (key == (char)kEnterKey) /* enter key */
                    {
                        if ( (gMyCCB.state == sClosed) ||
                                (gMyCCB.state == sPassive))
                            ShowError(noConnErr);
                        else
                        {
                            dp = (DialogPeek)myDialog;
                    
                                /* we'll take a maximum of 255 characters from the edit box */
                            teLength = ((**(*dp).textH).teLength < 255) ? (**(*dp).textH).teLength : 255;
                    
                                /* grab the text from our edit text box */
                            BlockMove( *((**(*dp).textH).hText), outgoingDataBuffer, teLength);
                            
                                /* we'll make sure we get a parameter block
                                    so we can send our data */
                            do
                            {
                                    /* get QElement for the write call */
                                dspPBPtr = GetQElement(&gAvailQueue);
                            }
                            while ( (dspPBPtr == NULL) );
                            
                            writeOutgoing(dspPBPtr,
                                            gCCBRefNum,
                                            outgoingDataBuffer,
                                            teLength);
 
                            /* Change selection to outgoing text TERecord */
                            SelIText(myDialog, kOutgoingTextID, 0, 32767);
                        
                        }
                    
                    }
                break;                                /*call DoActivate with the window and...*/
            case activateEvt:                        /*true for activate, false for deactivate*/
                DoActivate((WindowPtr)event.message, (event.modifiers & activeFlag) != 0);
                break;
            case updateEvt: 
                break;
            case osEvent: 
                switch(event.message >> 24) /*high byte of message*/
                {
                    case suspendResumeMessage:
                            gInBackground = (event.message & resumeMask) == 0;
                            DoActivate(FrontWindow(), !gInBackground);
                    break;
                }
 
        }
    } /*DoEvent*/
 
// *****************************************************************
// *    EventLoop
// *
// * Get events forever, and handle them by calling DoEvent.
// * Get the events by calling WaitNextEvent, if it's available, otherwise
// * by calling GetNextEvent. Also call AdjustCursor each time through the loop.
// *****************************************************************
void EventLoop()
{
    RgnHandle cursorRgn;
    Boolean gotEvent;
    EventRecord event;
    long sleepTime;
    WindowPtr whichDialog;
    short whichItem;
    
    
        cursorRgn = NewRgn();           /*weÕll pass WNE an empty region the 1st time thru*/
        sleepTime = 2;
        do
        {
            if (gHasWaitNextEvent)  /*put us 'asleep' forever under MultiFinder*/
            {
                gotEvent = WaitNextEvent(everyEvent, &event, sleepTime, cursorRgn);
            }
            else
            {
                SystemTask();               /*must be called if using GetNextEvent*/
                gotEvent = GetNextEvent(everyEvent, &event);
            }
 
            DoIdleProc();
            AdjustCursor(event.where, cursorRgn); /*make sure we have the right cursor*/
            whichDialog = FrontWindow();
            
                /* don't pass command keys or <enter> keys to our dialog */
            if (!((event.what==keyDown)&&(event.modifiers & cmdKey)) 
            && (IsDialogEvent(&event))
            && !((event.what==keyDown)&&((event.message & charCodeMask) == (char)kEnterKey)))       
            {
                if (DialogSelect(&event, &whichDialog, &whichItem))
                    DoModeless(whichDialog, whichItem);
            }
            else        
                DoEvent(event);
 
        }
        while (true);                   /*loop forever; we quit through an ExitToShell*/
}
 
 
 
// *****************************************************************
// *    TrapAvailable
// *
// * Check to see if a given trap is implemented. This is only used by the
// * Initialize routine in this program, so we put it in the Initialize segment.
// * The recommended approach to see if a trap is implemented is to see if
// * the address of the trap routine is the same as the address of the
// * Unimplemented trap.
// *****************************************************************
#pragma segment Initialize
Boolean TrapAvailable(tNumber,tType)
    short       tNumber;
    TrapType    tType;
{
    if ( ( tType == ToolTrap ) &&
        ( gMac.machineType > envMachUnknown ) &&
        ( gMac.machineType < envMacII ) )
    {       /* it's a 512KE, Plus, or SE */
        tNumber = tNumber & 0x03FF;
        if ( tNumber > 0x01FF )                 /* which means the tool traps */
            tNumber = _Unimplemented;           /* only go to 0x01FF */
    }
    return NGetTrapAddress(tNumber, tType) != GetTrapAddress(_Unimplemented);
} /*TrapAvailable*/
 
// *****************************************************************
// *    zeroOutStrings
// *
// *    clears out the name, time and incoming text strings
// *****************************************************************
void zeroOutStrings()
{
/* this procedure emptys all connection oriented strings in the main dialog */
 
    Rect r;
    short kind;
    Handle h;
 
        GetDItem(myDialog, kRemoteMacsNameID, &kind, &h, &r);
        SetIText(h, "\p");
        GetDItem(myDialog, kRemoteMacsTimeID, &kind, &h, &r);
        SetIText(h, "\p");
        GetDItem(myDialog, kIncomingTextID, &kind, &h, &r);
        SetIText(h, "\p");
}
 
// *****************************************************************
// *    SetupUserItems
// *
// *****************************************************************
void SetupUserItems()
{
    setEachUserItem(kzoneItemID);
    setEachUserItem(ktypeItemID);
    setEachUserItem(kobjectItemID);
    setEachUserItem(kRemoteMacsTimeBorderID);
    setEachUserItem(kIncomingMessageBorderID);
    setEachUserItem(kConnectStatusBorder);
    setEachUserItem(kPopupBorderID);
 
/* set connect button to unhilited */
    HiliteConnectButton(255);
}
 
 
// *****************************************************************
// *    Initialize
// *
// *    program initialization
// *****************************************************************
void Initialize()
{
 
    Rect r;
    short kind;
    Handle h;
    Handle menuBar;
    DialogPeek dp;
 
        gInBackground = false;
 
        InitGraf(&qd.thePort);
        InitFonts();
        InitWindows();
        InitMenus();
        TEInit();
        InitDialogs(nil);
        InitCursor();
 
 
        if (InitAppleTalk() != noErr)
            Exit(atalkErr);
            
        gZoneString[0] = 0;gTypeStr[0] = 0;gObjStr[0] = 0;
 
        GetOurZone();
 
            /*  we will allocate our own window storage instead of letting the Window */
            /*  Manager do it because GetNewWindow may load in temp. resources before */
            /*  making the NewPtr call, and this can lead to heap fragmentation. */
        myDialog = (DialogPtr)(NewPtr(sizeof(DialogRecord)));
        if (myDialog == nil)
            Exit(memErr);
 
 
        myDialog = GetNewDialog(rDialog, (Ptr)myDialog, (DialogPtr)-1);
 
        SetPort(myDialog);
 
        dp = (DialogPeek)myDialog;
 
        TextFont(geneva);
        TextSize(9);
 
        (**(dp->textH)).txFont = geneva;
        (**(dp->textH)).txSize = 9;
 
        SetWTitle(myDialog, "\pADSP");
 
        ShowWindow(myDialog);
 
        LastZoneMenuChoice = 0;
        LastTypeMenuChoice = 0;
        LastObjectMenuChoice = 0;
 
        SetupUserItems();
            /* show blinking cursor in outgoing text box */
        SelIText(myDialog, kOutgoingTextID, 0, 32767);
 
            /* turn on "Moof" entity filter (check box) initially */
        GetDItem(myDialog, kMoofFilterCheckBox, &kind, &h, &r);
        SetCtlValue((ControlHandle)h,1);
        gMoofEntityFilter = true;
        CopyPstr("\pMoof", gTypeStr);
        gObjStr[0] = 0;
        HiliteConnectButton(0);
 
        
        menuBar = GetNewMBar(rMenuBar);     /*read menus into menu bar*/
        if (menuBar == nil)
            Exit(menuErr);
        SetMenuBar(menuBar);                    /*install menus*/
        DisposHandle(menuBar);
        AddResMenu(GetMHandle(mApple), 'DRVR'); /*add DA names to Apple menu*/
        DrawMenuBar();
 
        gStopped = true;
        zeroOutStrings();
        
        initializeADSP();
        
} /*Initialize*/
 
// *****************************************************************
// *    CheckEnvirons
// *
// *    check the current operating environment
// *****************************************************************
void CheckEnvirons()
{
    long  total, contig, response;
    OSErr ignoreError;
 
    
        /* ignore the error returned from SysEnvirons; even if an error occurred,*/
        /* the SysEnvirons glue will fill in the SysEnvRec*/
            ignoreError = SysEnvirons(sysEnvironsVersion, &gMac);
 
        /* Make sure that the machine has at least 128K ROMs. If it doesn't, exit. */
        if (gMac.machineType < 0) FatalError(9);
        if ((long) GetApplLimit() - (long) ApplicZone() < kMinHeap) FatalError(10);
        PurgeSpace(&total, &contig);
        if (total < kMinSpace)
            if (UnloadScrap() != noErr)
                FatalError(11);
            else
            {
                PurgeSpace(&total, &contig);
                if (total < kMinSpace)
                    FatalError(11);
            }
            
        /* verify if WaitNextEvent, Gestalt and PPCToolbox are available */
        gHasWaitNextEvent = TrapAvailable(_WaitNextEvent, ToolTrap);
        if (TrapAvailable(_Gestalt, ToolTrap)) 
        {       /* verify if system 7.0 */
            Gestalt(gestaltSystemVersion,&response );
            if (response >= 0x0700)
                gHasSystem7 = true;
            else gHasSystem7 = false;
        }
        else
        {
            SysBeep(20);
            gHasSystem7 = false;
        }
 
}
 
 
// *****************************************************************
// *    Main
// *
// *****************************************************************
#pragma segment Main
main()
{
    MaxApplZone();          /*expand the heap so code segments load at the top*/
    CheckEnvirons();        /*check for some basic requirements; exits if not met*/
    Initialize();           /*initialize the program*/
    UnloadSeg(&Initialize); /*note that Initialize must not be in Main!*/
    EventLoop();            /*call the main event loop*/
 
}