AEObject.c

/*------------------------------------------------------------------------------
*
*  Apple Developer Technical Support
*
*  AppleEventª Object Model and Object Support Library routines
*
*  Program:AEObject-Edition Sample
*  File: AEObject.c -C Source
*
*  by:   C.K. Haun <TR>
*
*  Copyright © 1991,1992 Apple Computer, Inc.
*  All rights reserved.
*
*------------------------------------------------------------------------------
* This file contains most of the routines that deal with AppleEvent Objects, and
* the routines that use them.
* Object resolution, and Object accessors play a large part here.  Once you under-
* stand and have written a few accessors, you'll see that this stuff becomes a great
* deal easier.
* vers 1.0.1- All AE constants changed to reflect Winter '92 registry 
*----------------------------------------------------------------------------*/
/* And another Note:
* I am resolving simple objects in this file and sample.  I intend to
* go on to complex objects in a future sample, please stay tuned */
 
/* Some words about the objects and handlers */
/* Windows */
/* When a window is asked for or used, the token I create is an AEDesc */
/* of type cWindow, and the data associated with this is  */
/* (suprisingly enough) a window pointer.  In this sample I can do anything */
/* with my data based on a window pointer. */
/* The rest of the tokens are AEDescs containing structures in the dataHandle, */
/* please see the AESampStructs.h file for the definition of these structures */
/* ¥¥¥ Remember ---  A token can be anything you like. It does NOT get passed outside */
/* your application, it only stays inside it.   */
#define __AEOBJ__
 
#include "Sampdefines.h"
 
#pragma segment MyInit
 
/* InstallObjectHandlers normally would be in the Initialize.c file, but I wanted to put it here, */
/* along with the rest of the Object Model code so you could see everything in one place */
 
/* This routine starts the ObjectSupportLibrary, and installs all the callbacks and  */
/* object accessors for this program. */
OSErr InstallObjectHandlers(void)
{
    /* Initialize the library.  This makes some internal calls to the AppleEvent manager */
    OSErr myErr = AEObjectInit();
    if (!myErr) {
        /* The meat of the initialization.  Here I'm installing all the object accessors */
        /* I've written.  These are the routines that the OSL will call when I call */
        /* AEResolve on an object in an AppleEvent.  These routines do all the  */
        /* parsing of the object specifier, segment by segment.  Each of them returns a */
        /* token that describes the in-memory structure of the object specified in a way */
        /* my application can understand.  No one else will ever see these tokens */
        /* they are ONLY for use inside my program.  This means that they can be anything that */
        /* makes sense to me and my appliction. */
        /* The actual token structures I use are defined in the AESampStructs.h file. */
        /* This accessor returns a window token from a null descriptor.  This  */
        /* accessor will be called a LOT, since specifing anything in my application */
        /* will usually reference an owning window. */
        
        myErr = AEInstallObjectAccessor(cWindow, typeNull, NewOSLAccessorProc(WindFromNull), 0, false);
        
        /* This accessor gets ANY property from a window token */
        /* Since a window token describes all the important junk about the window */
        /* I only need one property from window accessor */
        
        myErr = AEInstallObjectAccessor(typeProperty, cWindow, NewOSLAccessorProc(PropertyFromWindow), 0, false);
        
        /* This installs my Text from Window accessor.  If there is a text object in this window */
        /* this routine will return a token for it */
        
        myErr = AEInstallObjectAccessor(cText, cWindow, NewOSLAccessorProc(TextFromWindow), 0, false);
        
        /* Now be able to pass back properties of my Text objects */
        
        myErr = AEInstallObjectAccessor(typeProperty, cText, NewOSLAccessorProc(PropertyFromText), 0, false);
        
        /* This installs my Word from Text accessor. */
        
        myErr = AEInstallObjectAccessor(cWord, cText, NewOSLAccessorProc(WordFromText), 0, false);
        
        myErr = AEInstallObjectAccessor(cDrawingArea, cWindow, NewOSLAccessorProc(DrawFromWindow), 0, false);
        
        /* This guy graps a graphic shape from my window, no matter what it actually is */
        
        myErr = AEInstallObjectAccessor(cGraphicShape, cDrawingArea, NewOSLAccessorProc(ShapeFromDraw), 0, false);
        myErr = AEInstallObjectAccessor(cGraphicShape, cWindow, NewOSLAccessorProc(ShapeFromDraw), 0, false);
        /* these grab specific shapes from the drawing area */
        /* The canny observer will notice that these all call the ShapeFromDraw routine */
        /* Since my shapes are all defined in the same type of structure, this makes  */
        /* a lot of sense for me */
                
        myErr = AEInstallObjectAccessor(cGraphicLine, cDrawingArea, NewOSLAccessorProc(ShapeFromDraw), 0, false);
        myErr = AEInstallObjectAccessor(cOval, cDrawingArea, NewOSLAccessorProc(ShapeFromDraw), 0, false);
        myErr = AEInstallObjectAccessor(cRectangle, cDrawingArea, NewOSLAccessorProc(ShapeFromDraw), 0, false);
        myErr = AEInstallObjectAccessor(cGraphicLine, cWindow, NewOSLAccessorProc(ShapeFromDraw), 0, false);
        myErr = AEInstallObjectAccessor(cOval, cWindow, NewOSLAccessorProc(ShapeFromDraw), 0, false);
        myErr = AEInstallObjectAccessor(cRectangle, cWindow, NewOSLAccessorProc(ShapeFromDraw), 0, false);
 
        /* and the same type o' thing for shape properties */
        myErr = AEInstallObjectAccessor(typeProperty, cGraphicLine, NewOSLAccessorProc(PropertyFromShape), 0, false);
        myErr = AEInstallObjectAccessor(typeProperty, cOval, NewOSLAccessorProc(PropertyFromShape), 0, false);
        myErr = AEInstallObjectAccessor(typeProperty, cRectangle, NewOSLAccessorProc(PropertyFromShape), 0, false);
 
 
        
        /* And the list will go on and on.  Once you've written a few accessors, you realize  */
        /* how handy they are, and you start installing them for everything that can  */
        /* possibly be gotten from anything else.  The only limit will be the  */
        /* amount of code space you want to dedicate to accessors. */
        /* Remember, the more accessors you put in, the more ways users will have to */
        /* get at your data, and the less they will have to learn or remember. */
        /* Our goal, as always, is to make it as easy for the user as possible. */
        /* I will also use the same routine to go from a null */
        
        /* Here I am installing all the call back routines that the OSL needs.  */
        /* These are not all implemented as doing something, but they will eventually */
        /* These tokens will be called by the OSL as it does it's parsing  */
        /* thrugh a passed object specifier. */
        /* You may not need all of these, any you don't need just pass */
        /* a nil */
        
        myErr = AESetObjectCallbacks(NULL, 
            NewOSLCountProc(MyCountProc), 
            NewOSLDisposeTokenProc(MyDisposeTokenProc),
            NULL, NULL, NULL, NULL);
        
    }
    return(myErr);
}
 
 
#pragma segment AEOSL
/* My GetData event handler.  This routine resolves the object passed (calling AEResolve) */
/* and then returns the appropriate data to the sender of the event */
pascal OSErr AEGetDataHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
{
 #pragma unused (refIn)
 
    OSErr myErr = noErr;
    AEDesc tokenBack;
    AEDesc theObject;
    AEDesc returnDesc;
    /* variables for the various tokens I'll be getting back */
    CWordObjHandle wordToken;
    CTextObjHandle textToken;
    PropertyTHdl propertyToken;
    Handle myDataHandle, tempHandle;
    Size dataSize;
    /* window is just a window ptr */
    gCurrentReply = reply;
    mVerboseOutput("\p\nEntering GetData handler")
    myErr = AEGetParamDesc(messagein, keyDirectObject, typeObjectSpecifier, &theObject);
        mAEErrorDisplay("\p getting object spec", myErr)
    if (!myErr) {
        returnDesc.descriptorType = typeNull;               /* set this up for a later check */
        myErr = AEResolve(&theObject, kAEIDoMinimum, &tokenBack);
        mAEErrorDisplay("\p AEResolve", myErr)
        
        if (!myErr) {
            /* Hey, marvy, we got an object token back.  That means that we have  */
            /*  enough information to derive the data. */
            /* Again, and forever, we don't care how you actually access data and create tokens */
            /* inside your application, you know how to do that much bettern than we ever will. */
            /* As long as it makes sense, and is efficient, for you, it's fine with us. */
            /* I am going to have to do some switching and stuff here to put the data I got into the reply */
            
            /* By the Way:  You could make all your tokens with the same field for the  */
            /* data and descriptor type.  If you did that, you wouldn't need the case */
            /* here for a GetData event, you would just pass back what you got. */
            /* I thought about it for this sample, but it seems more descriptive to */
            /* go through the specific objects.  I may change my mind.... */
            
            /* Anyway, I switch off the type of token I got back... */
            switch (tokenBack.descriptorType) {
                case cWindow:
                    /* for this, I'm going to pass back the Window ID I use in this program. */
                    /* Why not the window pointer?  Because we don't have shared memory in the current */
                    /* Mac OS, so I don't want people mucking around in my windows directly. */
                    /* And really, if you're getting a request for a bare window, all the other side */
                    /* is probably trying to see is if a window exisits.  I hope. */
                    /* they can pass back this as a selector for NewElement, or something */
                    /* You won't see this for a GetData much, in fact you can't from this  */
                    /* sample get it.  I mean it ain't in the GetData menu */
                    
                    break;
                case cText:
                    /* Pass back the chunk o' text.  Since I'm using TextEdit, I know that I'll */
                    /* never be over 32k here.  Keep in mind that the AppleEvent manager can only */
                    /* handle data up to 64k, so if you are passing back more text (or whatever) */
                    /* than that you'll have to do it in a series of returns, or pass back an */
                    /* alias to a file, or some such. */
                    textToken = (CTextObjHandle)tokenBack.dataHandle;
                    myDataHandle = (Handle)TEGetText((*textToken)->theText);
                    HLock(myDataHandle);
                    dataSize = GetHandleSize(myDataHandle);
                    /* again, this check is not really necessary, but I want to put it here */
                    /* as a warning, and maybe someday I'll stop using TextEdit. */
                    if (dataSize > k64k) {
                        /* if this were true, you would have to do something. */
                        dataSize = k64k;
                    }
                    myErr = AECreateDesc(typeChar, (Ptr)*myDataHandle, dataSize, &returnDesc);
                    HUnlock(myDataHandle);
                    /* and kill the remaining token */
                    myErr = MyDisposeTokenProc(&tokenBack);
                    /* the descriptor will be added to the reply later in this function */
                    break;
                case cWord:
                    /* same basic thing for a word as for a text */
                    wordToken = (CWordObjHandle)tokenBack.dataHandle;
                    myDataHandle = NewHandle((*wordToken)->endPos - (*wordToken)->startPos);
                    HLock(myDataHandle);
                    dataSize = GetHandleSize(myDataHandle);
                    /* move the text from the te chars handle to my data handle */
                    tempHandle = (Handle)TEGetText((*wordToken)->theText);
                    HLock(tempHandle);
                    BlockMove((Ptr)(*tempHandle + (*wordToken)->startPos), (Ptr)*myDataHandle, dataSize);
                    HUnlock(tempHandle);
                    /* again, this check is not really necessary, but I want to put it here */
                    /* as a warning. */
                    if (dataSize > k64k) {
                        /* if this were true, you would have to do something. */
                        dataSize = k64k;
                    }
                    myErr = AECreateDesc(typeChar, (Ptr)*myDataHandle, dataSize, &returnDesc);
                    HUnlock(myDataHandle);
                    /* return value will be added to reply later in this function */
                    /* and kill the remaining token */
                    myErr = MyDisposeTokenProc(&tokenBack);
                    
                    break;
                case typeProperty:
                    /* just return the property as the type included in the token */
                    /* The SetData handler (as you will see) needs to do a lot more */
                    propertyToken = (PropertyTHdl)tokenBack.dataHandle;
                    HLock((Handle)propertyToken);
                    myDataHandle = (*propertyToken)->theData;
                    dataSize = GetHandleSize(myDataHandle);
                    /* again, this check is not really necessary, but I want to put it here */
                    /* as a warning. */
                    if (dataSize > k64k) {
                        /* if this were true, you would have to do something. */
                        dataSize = k64k;
                    }
                    HLock(myDataHandle);
                    myErr = AECreateDesc((*propertyToken)->theDataType, (Ptr)*myDataHandle, dataSize, &returnDesc);
                    myErr = MyDisposeTokenProc(&tokenBack);
                    break;
                    /* and others as I add them */
                case cGraphicShape:
                case cGraphicLine:
                    MakeGraphicLine((CShapeObjHandle)tokenBack.dataHandle, &returnDesc);
                    myErr = MyDisposeTokenProc(&tokenBack);
                    break;
                case cRectangle:
                case cOval:
                    /* both of these have a default descriptor type of typeRectange, so I'll go make that */
                    MakeTypeRect((CShapeObjHandle)tokenBack.dataHandle, &returnDesc);
                    myErr = MyDisposeTokenProc(&tokenBack);
                    break;
                    
            }
            
            /* Here I either have a good descriptor for a return value, or I have  */
            /* 'null' in the type field if something went wrong. */
            if (returnDesc.descriptorType == typeNull) {
                /* something bad happend, there will be no reply.   */
                /* the error is already in myErr */
            } else {
                AEDesc newDesc;
                AEDesc wantType;
                OSErr tempErr;
                /* we have a good descriptor.  THere's one more thing to do, and it's */
                /* technically optional, but you should support it if you can.  That's  */
                /* seeing if the sender included a type that the wanted the data back as. */
                /* if they did, I will try and coerce my return descriptor to that type */
                /* First, is there a wantType? */
                /* I equate this to tempErr instead of myErr because it is not fatal if */
                /* this get fails, I don't want to pass this error back to the sender */
 
 
                tempErr = AEGetParamDesc(messagein, keyAERequestedType, typeType, &wantType);
                if (!tempErr) {
                    /* there was a wantType.  Try and coerce my data to that type */
                    /* If the coercion fails, I will just return my type of data.  The  */
                    /* sender of this event may be able to do the coercion on their */
                    /* end, perhaps they have more coercion handlers on their machine */
                    /* ¥ NOTE: DON'T assume that you can't coerce the descriptor! */
                    /* i.e. don't  evaluate the wantType yourself and say */
                    /* "my application doesn't know how to do that" and fail. */
                    /* True, your app may not know.  But someone else on the */
                    /* machine MIGHT know, and they may have installed a system */
                    /* level coercion routine that will make this work even if */
                    /* your application knew nothing about it.  So try the coercion */
                    tempErr = AECoerceDesc(&returnDesc, (DescType)**(wantType.dataHandle), &newDesc);
                    if (!tempErr) {
                        AEDesc tempDesc;
                        /* it coerced.  So, because of the way I'm doing this, swap */
                        /* things around (kinda) */
                        tempDesc = returnDesc;
                        returnDesc = newDesc;
                        AEDisposeDesc(&tempDesc);
                    }
                }
 
                myErr = AEPutParamDesc(reply, keyDirectObject, &returnDesc);
                myErr = AEDisposeDesc(&returnDesc);
                
            }
        }                                                   /* aeresolve error */
    }                                                       /* get object */
    gCurrentReply = nil;
    return(myErr);
    
}
 
pascal OSErr AESetDataHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
{
#pragma unused (refIn)
    OSErr myErr = noErr;
    PropertyTHdl propertyBack;
    CTextObjHandle cTextBack;
    CWordObjHandle cWordBack;
    AEDesc tokenBack;
    AEDesc theObject;
    AEDesc theData;
    gCurrentReply = reply;
    mVerboseOutput("\p\nEntering SetData handler")
    myErr = AEGetParamDesc(messagein, keyDirectObject, typeObjectSpecifier, &theObject);
    
    mAEErrorDisplay("\p getting object spec", myErr)
    /* get the data to set now also.  If we don't have any data, we can't */
    /* do this event. */
    /* I'm asking for the data as typeWildCard, so no coercions will take place. */
    /* I may coerce the data later, but now I want it as sent. */
    myErr = AEGetParamDesc(messagein, keyAEData, typeWildCard, &theData);
        mAEErrorDisplay("\p getting the data", myErr)
    myErr = AEResolve(&theObject, kAEIDoMinimum, &tokenBack);
        mAEErrorDisplay("\p AEResolve", myErr)
    if (!myErr) {
        /* we have a token telling us what the object to set is.  */
        /* from that, we can do our setting */
        /* How you actually implement this is again very much up to  */
        /* you. This is only a simple sample, do what seems best in your application */
        switch (tokenBack.descriptorType) {
            case cWindow:
                /* this shouldn't ever come back, since you can't set a  */
                /* window per sŽ.  Look at the property switch for that stuff */
                break;
            case cText:
                /* setting the text of this text token */
                /* our text token contains a TEHandle, so here's what we'll do.... */
                cTextBack = (CTextObjHandle)tokenBack.dataHandle;
                /* select all the text */
                TESetSelect(0, 32000, (*cTextBack)->theText);
                /* kill the old text */
                TEDelete((*cTextBack)->theText);
                HLock(theData.dataHandle);
                /* and set the new text */
                TESetText((Ptr)*(theData.dataHandle), GetHandleSize((theData.dataHandle)), (*cTextBack)->theText);
                HUnlock(theData.dataHandle);
                /* f“n */
                MyDisposeTokenProc(&tokenBack);
                break;
            case cWord:
                /* changing this one word to something else */
                /* basically the same as setting text, our range is just delimited */
                /* setting the text of this text token */
                /* our text token contains a TEHandle, so here's what we'll do.... */
                cWordBack = (CWordObjHandle)tokenBack.dataHandle;
                /* select all the text */
                TESetSelect((*cWordBack)->startPos, (*cWordBack)->endPos, (*cWordBack)->theText);
                TEDelete((*cWordBack)->theText);
                HLock(theData.dataHandle);
                TEInsert((Ptr)*(theData.dataHandle), GetHandleSize((theData.dataHandle)), (*cWordBack)->theText);
                /* f“n */
                MyDisposeTokenProc(&tokenBack);
                break;
            case typeProperty:
                propertyBack = (PropertyTHdl)tokenBack.dataHandle;
                /* since there are so many properties to set, I'll switch off  */
                /* the owner and set to that type */
                switch ((*propertyBack)->owningTokenType) {
                    case cWindow:
                        myErr = SetWindowProperty(propertyBack, &theData);      /* in Windows.c */
                        MyDisposeTokenProc(&tokenBack);
                        break;
                    case cText:
                    case cWord:
                    /* I'm not setting text or word properties in this sample, since I'm */
                    /* using plain old TextEdit (not even style) */
                        myErr = errAENotModifiable;
                        AddToReply("\p Text property setting not implemented",0);
                        break;
                    case cGraphicLine:
                    case cRectangle:
                    case cOval:
                        myErr = SetShapeProperty(propertyBack, &theData);      /* in Windows.c */
                        MyDisposeTokenProc(&tokenBack);
                    break;
                    
                    case typeProperty:
                        /* uhhh, if you ever have to set the property of a proprty, call me */
                        /* and we'll find help */
                        break;
                        
                }
                break;
        }
    }
    gCurrentReply = nil;
    return(myErr);
}
 
/* TextFromWindow returns a token identifying a text object attached to a window. */
/* If there is no text in this window, it returns an error */
pascal OSErr TextFromWindow(DescType desiredClass, const AEDesc *container, DescType containerClass, DescType form,
                            const AEDesc *selectionData, AEDesc *value, long LongInt)
{
#pragma unused (desiredClass,form,LongInt)
    OSErr myErr = noErr;
    WindowPtr theWindow;
    windowCHandle tempWC;
    short textNumber;
    CTextObjHandle newText;
    
    mVerboseOutput("\p\nGetting a Text from a Window")
    if (containerClass != cWindow) {
        myErr = errAENoSuchObject;                          /* no such luck speedy */
        AddToReply("\pYou did not ask for a text from a window, it's container", 0);
    } else {
        /* the container contains a window token in it's hot little datahandle */
        theWindow = *((WindowPtr *)(*(container->dataHandle)));
        
        tempWC = (windowCHandle)GetWRefCon(theWindow);
        /* Does this window contain a text edit record???? */
        if ((*tempWC)->boxHandle) {
            /* which number Text did they ask for?  Our sample only allows one */
            /* Text per window, so if it isn't one, bail */
            ShortFromDesc(&textNumber, selectionData);
            if (textNumber == 1) {
                /* create an cText object and pass it back */
                newText = (CTextObjHandle)NewHandle(sizeof(CTextObject));
                HLock((Handle)newText);
                /* fill it in */
                (*newText)->theOwningWindow = theWindow;
                (*newText)->theText = (*tempWC)->boxHandle;
                myErr = AECreateDesc(cText, (Ptr)*newText, sizeof(CTextObject), value);
                DisposeHandle((Handle)newText);              /* no longer needed */
            } else {
                myErr = errAENoSuchObject;
                AddToReply("\pNo Text Box with that index", 0);
            }
        } else {
            /* no text in this window */
            myErr = errAENoSuchObject;
            AddToReply("\pNo Text Box in the window you specified", 0);
        }
    }
    return(myErr);
}
 
/* Grab a word from the text */
/* This is a 3rd level call from AEResolve.  Before we got to here, AEResolve has  */
/* already called our WindFromNull and TextFromWindow accessors, the container */
/* passed in is our text container token (probably) */
pascal OSErr WordFromText(DescType desiredClass, const AEDesc *container, DescType containerClass, DescType form,
                          const AEDesc *selectionData, AEDesc *value, long LongInt)
{
 #pragma unused (desiredClass,containerClass,form,LongInt)
    OSErr myErr = noErr;
    long wordCount;
    long wordStart;
    long wordEnd;
    short countToGet;
    SignedByte textHState;
    CWordObjHandle wordToken;
    Handle theText;
    /* Since we'e getting word from text, we know the container is our Text token */
    CTextObjHandle ourTextObject = (CTextObjHandle)(container->dataHandle);
    /* grab the text for later */
    theText = (Handle)TEGetText((*ourTextObject)->theText);
    textHState = HGetState(theText);
    HLock(theText);
    if (GetHandleSize(theText)) {
        mVerboseOutput("\p\nGetting a word from a Text")
        ShortFromDesc(&countToGet, selectionData);
        
        /* call my word counter with a stopAt of the selection data */
        CountWords(container, &wordCount, countToGet, &wordStart);
        /* CountWords returns negative if there weren't any, or if we went too far */
        if (wordCount > 0) {
            /* now count to the next word, so we know the range */
            /* I know, I know, this is horribly inefficient, but it's an example, OK? */
            CountWords(container, &wordCount, countToGet + 1, &wordEnd);
            if (wordCount < 0) {
                /* ooops, no more words.  Just go to text len then */
                wordEnd = GetHandleSize(theText);
            }
            /* return a word token */
            wordToken = (CWordObjHandle)NewHandleClear(sizeof(CWordObject));
            /* fill in the token */
            (*wordToken)->theOwningWindow = (*ourTextObject)->theOwningWindow;
            (*wordToken)->theText = (*ourTextObject)->theText;
            (*wordToken)->startPos = wordStart;
            (*wordToken)->endPos = wordEnd;
            /* create the token */
            myErr = AECreateDesc(cWord, (Ptr)*wordToken, sizeof(CWordObject), value);
        } else {
            /* error, no words */
            myErr = errAENoSuchObject;
            AddToReply("\pCould not find this number word in this text", 0);
        }
    } else {
        myErr = errAENoSuchObject;
        AddToReply("\pNo words in this Text", 0);
    }
    HSetState(theText, textHState);
    return(myErr);
}
 
pascal OSErr DrawFromWindow(DescType desiredClass, const AEDesc *container, DescType containerClass, DescType form,
                            const AEDesc *selectionData, AEDesc *value, long LongInt)
{
#pragma unused (desiredClass,containerClass,form,selectionData,LongInt)
    OSErr myErr = noErr;
    mVerboseOutput("\p\nGetting a Drawing Area from a window")
    /* the drawing area token is the same as a window token (i.e. a window pointer) since it gives */
    /* us everything we need to know */
    /* so basically we duplicate the descriptor passed in to us, then change the descType */
    myErr = AEDuplicateDesc(container, value);
    value->descriptorType = cDrawingArea;
    return(myErr);
}
 
/* ShapeFromDraw pulls _any_ kind of shape from my drawing area.  It's called for */
/* a generic cGraphicShape, a cGraphicLine,cRectangle, and cOval.  This is where */
/* your code gets optimized and shrunken, so it is not the nightmare you  */
/* at first thought.  Really */
/* Note that this also gets called for shapes from cWindow (if someone asks that way) */
/* so golly, it's just so useful */
pascal OSErr ShapeFromDraw(DescType desiredClass, const AEDesc *container, DescType containerClass, DescType form,
                           const AEDesc *selectionData, AEDesc *value, long LongInt)
{
 #pragma unused (LongInt)
    OSErr myErr = noErr;
    windowCHandle tempWC;
    long selNumber;
    long tempCounter;
    CShapeObjHandle theToken = (CShapeObjHandle)NewHandleClear(sizeof(CShapeObject));
    ShapesHandle theShapes;
    short numShapes;
    mVerboseOutput("\p\nGetting a Shape from a window, specifically a ")
    AddToAEWindow((Ptr)&desiredClass, kFour);
    /* the container may be either a cWindow or a cDrawingArea.  In our infinite wisdom, */
    /* we have made these tokens the same, so we can use the same routine for both. */
    if (containerClass == cWindow || containerClass == cDrawingArea) {
        /* get the right window struct and the shapes attached to it */
        tempWC = (windowCHandle)GetWRefCon(*((WindowPtr *)(*(container->dataHandle))));
        theShapes = (*tempWC)->theShapes;
        numShapes = (*tempWC)->numShapes;
        /* do some default filling of the shape token, since there's some things */
        /* we already know */
        (*theToken)->theOwningWindow = *((WindowPtr *)(*(container->dataHandle)));
        (*theToken)->type = typeNull;                          /* flag for later folks */
        if (numShapes) {
            /* case off the type of shape we're asking for for the specific one */
            switch (desiredClass) {
                case cGraphicShape:
                    /* just any old thing */
                    /* case off the form wanted.  Since I am only sending myself graphics */
                    /* by index, that's the case I'll do first */
                case cGraphicLine:
                case cRectangle:
                case cOval:
                    /* and all these shapes are basically the same, so I'll use the same routine */
                    /* in fact, I'm going to go back to my window routine and make the type filed in my Shapes */
                    /* handle a desc type, hold on a minute....  OK, I'm back, this will make it even easier */
                    switch (form) {
                        case formAbsolutePosition:
                            if (selectionData->descriptorType == typeLongInteger) {
                                
                                selNumber = *((long *)*(selectionData->dataHandle));
                            } else {
                                /* see if you can coerce it to a long */
                            }
                            /* find the selNumber-th line in this drawing area */
                            if (selNumber == 0)
                                selNumber = 1;              /* no 0th */
                            tempCounter = 0;
                            while (theShapes) {
                                /* 'if' here depending on generic shape or specific type */
                                if (desiredClass == cGraphicShape) {
                                    tempCounter++;          /* doesn't matter what kind, we care about it */
                                } else {
                                    if ((*theShapes)->aeType == desiredClass)
                                        tempCounter++;
                                }
                                if (tempCounter == selNumber) {
                                    break;
                                } else {
                                    theShapes = (*theShapes)->nextShape;
                                }
                            }
                            /* if theShapes is not nil, then we found a shape that suites */
                            
                            if (theShapes) {
                                /* fill in my token */
                                (*theToken)->theShape = theShapes;
                                (*theToken)->shapeNumber = tempCounter;     /* why do I want this?  I dunno */
                                (*theToken)->type = desiredClass;
                            } else {
                                myErr = errAENoSuchObject;
                            }
                            break;
                        case formRelativePosition:          /* next, previous */
                            break;
                        case formTest:                      /* A logical or a comparison */
                            break;
                        case formRange:                     /* Two arbitrary objects and everything in between */
                            break;
                        case formPropertyID:                /* Key data is a 4-char property name */
                            break;
                        case formName:                      /* Key data may be of type 'TEXT' */
                            
                            break;
                        default:
                            myErr = errAENoSuchObject;
                            break;
                    }
                    break;
            }
        } else {
            /* no shapes with this window */
            myErr = errAENoSuchObject;
        }
    } else {
        myErr = errAENoSuchObject;
    }
    if (myErr == noErr && (*theToken)->type != typeNull) {
        HLock((Handle)theToken);
        myErr = AECreateDesc((*theToken)->type, (Ptr)*theToken, sizeof(CShapeObject), value);
        DisposeHandle((Handle)theToken);
    }
    return(myErr);
}
 
/* In this example, this will be the first accessor called by any call to AEResolve. */
/* Since we have no other objects sprouting from null but windows, anyone who */
/* wants to know about our documents has to have a cWindow object spec hanging off the */
/* topmost null. */
pascal OSErr WindFromNull(DescType desiredClass, const AEDesc *container, DescType containerClass, DescType form,
                          const AEDesc *selectionData, AEDesc *value, long LongInt)
{
 #pragma unused (desiredClass,container,LongInt)
    WindowPtr returnedWindow = nil;
    WindowPtr tempWindow = nil;
    short index = 0;
    short counter = 0;
    OSErr myErr = noErr;
    Str63 tempMatch;
    Str63 windowName;
    
    mVerboseOutput("\p\nGetting a Window from Nul")
    /* first check to see if we can deal with this, are they asking for a window from Nul?  */
    /* If they're not then we can't handle it */
    if (containerClass != typeNull)
        return(errAENoSuchObject);                          /* not something we recognize */
    /* case off form to see how we'll get the window thing */
    /* all of these may not be supported, but as time goes by they will be */
    
    switch (form) {
        case formAbsolutePosition:                          /* e.g., 1st, -2nd ( 2nd from end) */
            /* ignore the AEStatusWindow in searching this list */
            tempWindow = FrontWindow();
            /* a little utilty routine, see which number window they want */
            ShortFromDesc(&index, (AppleEvent *)selectionData);
            if (index == 0)
                index = 1;
            if (index == 1 && (((WindowPeek)FrontWindow())->windowKind != kAEStatusWindow)) {
                /* just grab the front window and return */
                returnedWindow = FrontWindow();
            } else {
                /* walk the list  */
                tempWindow = (WindowPtr)LMGetWindowList();
                while (tempWindow) {
                    windowCHandle tempWC = (windowCHandle)GetWRefCon(tempWindow);
                    if (index == (*tempWC)->windowIndex) {
                        returnedWindow = tempWindow;
                        break;
                    }
                    tempWindow = (WindowPtr)((WindowPeek)tempWindow)->nextWindow;
                }
            }
            if (returnedWindow) {
                
                myErr = AECreateDesc(cWindow, (Ptr)&returnedWindow, sizeof(returnedWindow), value);
                mAEErrorDisplay("\p creating object token", myErr);
            } else {
                /* error, couldn't find the thing */
                myErr = errAENoSuchObject;
                AddToReply("\pNo window of that index number", 0);
            }
            break;
        case formRelativePosition:                          /* next, previous */
            break;
        case formTest:                                      /* A logical or a comparison */
            break;
        case formRange:                                     /* Two arbitrary objects and everything in between */
            break;
        case formPropertyID:                                /* Key data is a 4-char property name */
            
            break;
        case formName:                                      /* Key data may be of type 'TEXT' */
            /* gettting one of these here things by name */
            /* so the AEDesc has a text thing in it.  Not a PString, just a charater thing, */
            /* so I'll make it a PString */
            PStringFromTextDesc(&tempMatch, selectionData);
            /* and as usual, walk the window list */
            tempWindow = FrontWindow();
            while (tempWindow) {
                GetWTitle(tempWindow, windowName);
                if (EqualString(windowName, tempMatch, false, false))
                    break;
                tempWindow = (WindowPtr)((WindowPeek)tempWindow)->nextWindow;
            }
            if (tempWindow) {
                /* we have a match */
                myErr = AECreateDesc(cWindow, (Ptr)&tempWindow, sizeof(tempWindow), value);
                mAEErrorDisplay("\p creating object token", myErr);
            } else {
                /* no match, return an error  */
                myErr = errAENoSuchObject;
                AddToReply("\pNo window with that name", 0);
            }
            break;
            
    }
    return(myErr);
}
 
/* PropertyFromWindow gets all the properties of my window object. */
pascal OSErr PropertyFromWindow(DescType desiredClass, const AEDesc *container, DescType containerClass, DescType form,
                                const AEDesc *selectionData, AEDesc *value, long LongInt)
{
 #pragma unused (desiredClass,containerClass,form,LongInt)
 
    OSErr myErr = noErr;
    Str63 windowName;
    WindowPtr tempPort;
    PropertyTHdl propertyTokenH = (PropertyTHdl)NewHandleClear(sizeof(PropertyToken));
    /* pull a property from the window */
    DescType propertyType = *(DescType *)(*(selectionData->dataHandle));
    ProcessSerialNumber tempPSN;
    Boolean tempBool;
    Handle theData;
    WindowPtr owningWindow;
    windowCHandle owningControl;
    mVerboseOutput("\p\n Getting a window property ")
    AddToAEWindow((Ptr)&propertyType, kFour);
    /* I can fill in some of the token information right now, since I have the window */
    owningWindow = *((WindowPtr *)(*(container->dataHandle)));
    owningControl = (windowCHandle)GetWRefCon(owningWindow);
    (*propertyTokenH)->owningTokenType = cWindow;           /* what this is a property of */
    (*propertyTokenH)->token.window = owningWindow;
    (*propertyTokenH)->inWindow = owningWindow; /* for consistency, though it's redundant */
    switch (propertyType) {
        /* can't get all of them yet, as this sample grows the amount of properties will also */
        /* grow */
        
        /* What's the name of this window? */
        case pName:
            (*propertyTokenH)->theProperty = pName;
            (*propertyTokenH)->theDataType = typeChar;
            GetWTitle(owningWindow, windowName);
            theData = NewHandle(windowName[0]);
            HLock(theData);
            BlockMove((Ptr)&windowName[1], (Ptr)*theData, windowName[0]);
            HUnlock(theData);
            (*propertyTokenH)->theData = theData;
            break;
            /* Boundry rectangle, in global coordinates */
        case pBounds:
            (*propertyTokenH)->theProperty = pBounds;
            (*propertyTokenH)->theDataType = typeQDRectangle;
            theData = NewHandle(sizeof(Rect));
            HLock(theData);
            BlockMove((Ptr)&owningWindow->portRect, (Ptr)*theData, sizeof(Rect));
            /* now we have to globalize the coordiantes */
            GetPort(&tempPort);
            SetPort(owningWindow);
            SetPort(tempPort);
            /* Now, can we force the MPW C compiler to beleive us that what we're passing is  */
            /* a Point pointer???? */
            LocalToGlobal((Point *)*theData);
            LocalToGlobal((Point *)(*theData + sizeof(Point)));
            /* yes we can, amazing */
            
            HUnlock(theData);
            (*propertyTokenH)->theData = theData;
            
            break;
            /* Do we have a title bar? */
        case pHasTitleBar:
            (*propertyTokenH)->theProperty = pHasTitleBar;
            (*propertyTokenH)->theDataType = typeBoolean;
            theData = NewHandle(sizeof(short));
            **theData = kMyTrue;                            /* they all do */
            (*propertyTokenH)->theData = theData;
            break;
            /* are we a modal window? */
        case pIsModal:
            (*propertyTokenH)->theProperty = pIsModal;
            (*propertyTokenH)->theDataType = typeBoolean;
            theData = NewHandle(sizeof(short));
            **theData = ((WindowPeek)owningWindow)->windowKind & dialogKind;
            (*propertyTokenH)->theData = theData;
            break;
            /* is this window 'dirty'? */
        case pIsModified:
            (*propertyTokenH)->theProperty = pIsModified;
            (*propertyTokenH)->theDataType = typeBoolean;
            theData = NewHandle(sizeof(short));
            **theData = (*owningControl)->windowDirty;
            (*propertyTokenH)->theData = theData;
            break;
            /* can the size be changed? */
        case pIsResizable:
            (*propertyTokenH)->theProperty = pIsResizable;
            (*propertyTokenH)->theDataType = typeBoolean;
            theData = NewHandle(sizeof(short));
            **theData = kMyTrue;
            (*propertyTokenH)->theData = theData;
            break;
            /* stationary? */
        case pIsStationeryPad:
            (*propertyTokenH)->theProperty = pIsResizable;
            (*propertyTokenH)->theDataType = typeBoolean;
            theData = NewHandle(sizeof(short));
            **theData = false;
            (*propertyTokenH)->theData = theData;
            break;
            /* Are we in the users face right now? */
        case pIsFrontProcess:
            (*propertyTokenH)->theProperty = pIsResizable;
            (*propertyTokenH)->theDataType = typeBoolean;
            theData = NewHandle(sizeof(short));
            GetFrontProcess(&tempPSN);
            SameProcess(&tempPSN, &gOurSN, &tempBool);
            **theData = tempBool;
            (*propertyTokenH)->theData = theData;
            break;
            /* are we in the Zoomed state? */
        case pIsZoomed:
            break;
            /* are we visible to the user now? */
        case pVisible:
            (*propertyTokenH)->theProperty = pVisible;
            (*propertyTokenH)->theDataType = typeBoolean;
            theData = NewHandle(sizeof(short));
            **theData = kMyTrue;                            /* I'm only returning visible windows  */
            (*propertyTokenH)->theData = theData;
            break;
            /* What's our class? */
        case pClass:
            (*propertyTokenH)->theProperty = pClass;
            (*propertyTokenH)->theDataType = typeType;
            
            theData = NewHandle(sizeof(DescType));
            HLock(theData);
            *((DescType *)*theData)=cWindow;
            HUnlock(theData);
            (*propertyTokenH)->theData = theData;
            
            break;
            
    }
    
    /* and return the token */
    if (!myErr) {
        HLock((Handle)propertyTokenH);
        myErr = AECreateDesc(typeProperty, (Ptr)*propertyTokenH, sizeof(PropertyToken), value);
        /* dispose of the token handle, but NOT the data handle I added, since the AEM */
        /* had no idea that I put a handle inside the data, so it didn't copy that */
        /* Later, when the token itself is disposed that handle will be  */
        /* freed up. */
        DisposeHandle((Handle)propertyTokenH);
        mAEErrorDisplay("\p creating property token", myErr);
        
    }
    return(myErr);
}
/* This allows someone to get properties from my text */
pascal OSErr PropertyFromText(DescType desiredClass, const AEDesc *container, DescType containerClass, DescType form,
                              const AEDesc *selectionData, AEDesc *value, long LongInt)
{
#pragma unused (desiredClass,containerClass,form,value,LongInt)
 
    OSErr myErr = noErr;
    PropertyTHdl propertyTokenH = (PropertyTHdl)NewHandleClear(sizeof(PropertyToken));
    CTextObjHandle myTextHandle;                            /* I include a text object token in my text property token */
    /* kinda circular, but hey... */
    /* pull a property from a text */
    
    DescType propertyType = *(DescType *)(*(selectionData->dataHandle));
    Handle theData;
    RGBColor myBlack =  {
        0, 0, 0
    };                                                      /* for the Color question */
    Str255 tempString;
    windowCHandle owningControl;
    mVerboseOutput("\p\n Getting a text property ")
    AddToAEWindow((Ptr)&propertyType, kFour);
    /* I can fill in some of the token information right now, since I have the window */
    myTextHandle = (CTextObjHandle)container->dataHandle;
    owningControl = (windowCHandle)GetWRefCon((*myTextHandle)->theOwningWindow);
    (*propertyTokenH)->owningTokenType = cText;             /* what this is a property of */
    (*propertyTokenH)->token.textHandle = (*myTextHandle)->theText;
    (*propertyTokenH)->theProperty = propertyType;
    (*propertyTokenH)->inWindow = (*myTextHandle)->theOwningWindow;
    switch (propertyType) {WritingCodePtr theCode;
        
        case pAnyCharTextStyles:
            (*propertyTokenH)->theDataType = typeBoolean;
            theData = NewHandle(sizeof(short));
            **theData = false;                              /* we have no style */
            (*propertyTokenH)->theData = theData;
            break;
        case pClass:
            (*propertyTokenH)->theDataType = typeType;
            theData = NewHandle(sizeof(DescType));
            **theData = cText;                              /* they all do */
            (*propertyTokenH)->theData = theData;
            break;
        case pColor:
            (*propertyTokenH)->theDataType = typeRGBColor;
            /* always black in this example */
            theData = NewHandle(sizeof(RGBColor));
            HLock(theData);
            BlockMove((Ptr)&myBlack, (Ptr)*theData, sizeof(RGBColor));
            HUnlock(theData);
            (*propertyTokenH)->theData = theData;
            break;
        case pFont:
            /* get the ID, translate to a name, and return as text */
            /* our token contains a TERecord, which contains the font ID */
            GetFontName((*(*propertyTokenH)->token.textHandle)->txFont, &tempString);
            theData = NewHandle(tempString[0]);
            HLock(theData);
            BlockMove((Ptr)&tempString[1], (Ptr)*theData, tempString[0]);
            HUnlock(theData);
            (*propertyTokenH)->theData = theData;
            
            break;
        case pPointSize:
            /* Object Class ID: cFixed16 */
            (*propertyTokenH)->theDataType = typeFixed;
            theData = NewHandleClear(sizeof(long));
            *((short *)*theData) = (*(*propertyTokenH)->token.textHandle)->txSize;      
            (*propertyTokenH)->theData = theData;
            
        case pScriptTag:
            (*propertyTokenH)->theDataType = typeIntlWritingCode;
            theData = NewHandle(sizeof(WritingCode));
            theCode = (WritingCodePtr) *theData;
            HLock(theData);
            theCode->theScriptCode = FontScript();
            theCode->theLangCode = iuSystemCurLang;
            HUnlock(theData);
            (*propertyTokenH)->theData = theData;
 
            break;
        case pTextStyles:
            /* in my sample, no style */
            (*propertyTokenH)->theDataType = typeBoolean;
            theData = NewHandle(sizeof(short));
            **theData = false;                              /* we have no style */
            (*propertyTokenH)->theData = theData;
            
            break;
            /*****************************/
            
    }                                                       /* endswitch */
    
    
    return(myErr);
}
 
pascal OSErr PropertyFromShape(DescType desiredClass, const AEDesc *container, DescType containerClass, DescType form,
                               const AEDesc *selectionData, AEDesc *value, long LongInt)
{
#pragma unused (desiredClass,containerClass,form,LongInt)
    OSErr myErr = noErr;
    PropertyTHdl propertyTokenH = (PropertyTHdl)NewHandleClear(sizeof(PropertyToken));
    /* pull a property from the window */
    CShapeObjHandle theShapeToken;                          /* the token for this shape */
    RGBColor white =  {
        0xFFFF, 0xFFFF, 0xFFFF
    };
    ShapesHandle theShape;
    short penSize = 1;
    long myCopy = 0;
    DescType propertyType = *(DescType *)(*(selectionData->dataHandle));
    Handle theData;
    windowCHandle owningControl;
    DescType shapeType;
    mVerboseOutput("\p\n Getting a property from a shape")
    theShapeToken = (CShapeObjHandle)container->dataHandle;
    owningControl = (windowCHandle)GetWRefCon((*theShapeToken)->theOwningWindow);
    shapeType = (*theShapeToken)->type;
    theShape = (*theShapeToken)->theShape;
    (*propertyTokenH)->owningTokenType = shapeType;
    (*propertyTokenH)->token.shapeHandle = (*theShapeToken)->theShape;
    (*propertyTokenH)->theProperty = propertyType;
    (*propertyTokenH)->inWindow = (*theShapeToken)->theOwningWindow;
    /* so after filling in a bunch, all we need now is the data type and the data itself */
    switch (propertyType) {
        case pBounds:
        case pDefinitionRect:
            /* This is the same as the bounds rect in my example */
            
            /* return a bounds rect, pretty easy */
            (*propertyTokenH)->theDataType = typeQDRectangle;
            theData = NewHandle(sizeof(Rect));
            HLock(theData);
            BlockMove((Ptr)&(*theShape)->theRect, (Ptr)*theData, sizeof(Rect));
            /* this of course stays in local coordinates for the owning port */
            (*propertyTokenH)->theData;
            break;
        case pClass:
            (*propertyTokenH)->theDataType = typeType;
            theData = NewHandle(sizeof(DescType));
            HLock(theData);
            /* I know, blockmoving 4 bytes is silly.  */
            BlockMove((Ptr)&shapeType, (Ptr)*theData, sizeof(DescType));
            break;
        case pFillColor:
            (*propertyTokenH)->theDataType = typeRGBColor;
            /* always white in this example */
            theData = NewHandle(sizeof(RGBColor));
            HLock(theData);
            BlockMove((Ptr)&white, (Ptr)*theData, sizeof(RGBColor));
            HUnlock(theData);
            (*propertyTokenH)->theData = theData;
            
            break;
        case pFillPattern:
        case pPenPattern:
            break;
        case pPenColor:
            (*propertyTokenH)->theDataType = typeRGBColor;
            theData = NewHandle(sizeof(RGBColor));
            HLock(theData);
            BlockMove((Ptr)&(*theShape)->theColor, (Ptr)*theData, sizeof(RGBColor));
            HUnlock(theData);
            (*propertyTokenH)->theData = theData;
            
            break;
            
        case pPenWidth:
            (*propertyTokenH)->theDataType = typeShortInteger;
            /* always white in this example */
            theData = NewHandle(sizeof(short));
            HLock(theData);
            BlockMove((Ptr)&penSize, (Ptr)*theData, sizeof(short));
            break;
        case pTransferMode:
            (*propertyTokenH)->theDataType = typeEnumeration;
            /* always white in this example */
            theData = NewHandle(sizeof(long));
            HLock(theData);
            BlockMove((Ptr)&myCopy, (Ptr)*theData, sizeof(long));
            break;
    }
    HUnlock(theData);
    /* dangme I forgot to move what I just did into result..... */
    if (!myErr) {
        HLock((Handle)propertyTokenH);
        myErr = AECreateDesc(typeProperty, (Ptr)*propertyTokenH, sizeof(PropertyToken), value);
        /* dispose of the token handle, but NOT the data handle I added, since the AEM */
        /* had no idea that I put a handle inside the data, so it didn't copy that */
        /* Later, when the token itself is disposed that handle will be  */
        /* freed up. */
        DisposeHandle((Handle)propertyTokenH);
        mAEErrorDisplay("\p creating property token", myErr);
        
    }
    
    return(myErr);
}
 
 
/* You may never need a dispose function.  If all you use for tokens are */
/* simple AEDescs, the OSL will do all the disposal for you. */
/* In my case, my tokens contain  handles, so I have to  */
/* do some disposal myself. */
pascal OSErr MyDisposeTokenProc(AEDesc *unneededToken)
{
    OSErr myErr = noErr;
    PropertyTHdl myProp;
    mVerboseOutput("\p\nDisposing a Token: ")
    AddToAEWindow((Ptr)&(unneededToken->descriptorType), kFour);
    /* most of the time I'll be doing a simple AEDisposeDesc.  The only time I need  */
    /* something more complex is when I'm disposing of a property descriptor, since */
    /* I have a handle inside that */
    switch (unneededToken->descriptorType) {
        /* these two are simple */
        case cWindow:
        case cText:
            myErr = AEDisposeDesc(unneededToken);
            break;
            /* these two have imbedded handles */
        case cWord:
            myErr = AEDisposeDesc(unneededToken);
            break;
        case typeProperty:
            myProp = (PropertyTHdl)unneededToken->dataHandle;
            DisposeHandle((*myProp)->theData);
            myErr = AEDisposeDesc(unneededToken);
            
            break;
            
        default:
            /* I default to just disposing of the token, ne ces pa  */
            AEDisposeDesc(unneededToken);
            break;
    }
    return(myErr);
}
 
/* not yet implemented (and also not installed as a callback) */
pascal OSErr MyCompareProc(DescType oper, const AEDesc *obj1, const AEDesc *obj2, Boolean *result)
{
#pragma unused (oper,obj1,obj2,result)
    OSErr myErr = noErr;
    mVerboseOutput("\p\nComparing Objects")
    return(myErr);
}
 
pascal OSErr MyCountProc(DescType desiredType, DescType containerClass, const AEDesc *container, long *result)
{
    OSErr myErr = noErr;
    long counter = 0;
    WindowPtr windows;
    windowCHandle tempWC;
    switch (desiredType) {
        case cWindow:
            /* should be conting windows from nul */
            mVerboseOutput("\p\nCounting Windows")
            windows = (WindowPtr)LMGetWindowList();
            while (windows) {
                counter++;
                windows = (WindowPtr)((WindowPeek)windows)->nextWindow;
            }
            *result = counter;
            break;
        case kAEMyShape:
            mVerboseOutput("\p\nCounting Shapes")
            /* getting this from a specific container, i.e. a winder. */
            switch (containerClass) {
                case cWindow:
                case cDrawingArea:
                    /* we're OK so far */
                    /* get my window pointer from the container passed */
                    CountShapes(container,&counter,nil,nil,&desiredType);
                    *result = counter;
                    break;
                case typeNull:
                    /* this would mean all the possible shapes  */
                    break;
            }
            break;
        case cText:
            mVerboseOutput("\p\nCounting Texts")
            switch (containerClass) {
                /* I can count texts in a window and in Null (the app) */
                case typeNull:
                    /* walk all my document windows and see how many texts exisit */
                    windows = FrontWindow();
                    /* there may not be any, o'course */
                    while (windows) {
                        tempWC = (windowCHandle)GetWRefCon(windows);
                        if ((*tempWC)->boxHandle)
                            counter++;
                        windows = (WindowPtr)((WindowPeek)windows)->nextWindow;
                    }
                    *result = counter;
                    break;
                case cWindow:
                    /* so we know the container desc is my window token */
                    windows = (WindowPtr)*(container->dataHandle);
                    tempWC = (windowCHandle)GetWRefCon(windows);
                    /* there is either one or none */
                    *result = (((*tempWC)->boxHandle) ? 1 : 0);
                    break;
                default:
                    mVerboseOutput("\p\nAsked for text count from something we don't understand ")
                    myErr = errAECantHandleClass;
                            /* add new Winter '92 registry error code <ckh 1.0.2>*/
                    AddToReply("\p Cant count this class",errAECantHandleClass);
 
                    counter = 0;
                    break;
            }
            *result = counter;
            break;
        case cWord:
            /* Since I have to count words in another place (when I get the word) */
            /* I will call my subroutine, saying I don't want to stop */
            mVerboseOutput("\p\nCounting Words ")
            CountWords(container, &counter, nil, nil);
            *result = counter;
            break;
        case cGraphicLine:
        case cRectangle:
        case cOval:
        case cGraphicShape:
            /* all these are basically the same */
            CountShapes(container, &counter, nil, nil, &desiredType);
            *result = counter;
            break;
        default:
            myErr = errAENoSuchObject;                      /* I don't know what's being asked for */
            break;
 
    }
    return(myErr);
}
 
void CountWords(const AEDesc *container, long *theCount, long stopAt, long *position)
{
    CTextObjHandle ourTextObject;
    Handle theText;
    SignedByte textState;
    char *textPtr;
    register char letter;
    long wordCount = 1;                                     /* I'm getting passed a 1 based count to find */
    Boolean inSpaces = false;
    register long positionNow = 0;
    long lastLetter = 0;
    /* So we have a text object here, which contains a TEHandle */
    /* We step through the handle counting words */
    /* get the text from my cText token */
    
    AddAENum(stopAt);
    ourTextObject = (CTextObjHandle)(container->dataHandle);
    *position = -1;
    theText = (Handle)TEGetText((*ourTextObject)->theText);
    textState = HGetState(theText);
    HLock(theText);                                         /* AddAENum makes some TextEdit calls, so I need to lock this */
    /* how many characters are in it? */
    lastLetter = GetHandleSize(theText);
    if (lastLetter) {
        /* now this is a slow and agonizing word count routine, it's not intended to be any more than that */
        textPtr = (char *)*theText;
        for (positionNow = 0; positionNow < lastLetter; positionNow++) {
            letter = *(textPtr + positionNow);
            if (letter == kSpace) {
                if (!inSpaces)
                    wordCount++;
                inSpaces = kMyTrue;
                if (stopAt && stopAt == wordCount) {
                    if (position)
                        *position = positionNow;
                    break;
                }
            } else {
                inSpaces = false;
            }
        }
        /* Did we have fun counting words?  Yes? */
        /* OK, we have a word count */
        /* if position is still negative, we didn't get to this word.  Maybe there weren't enough */
        if (*position >= 0)
            *theCount = wordCount;
        else
            *theCount = -1;
        AddAENum(wordCount);
    } else {
        /* no words (or letters) in this thing */
        *theCount = -1;
    }
    HSetState(theText, textState);
}
 
void CountShapes(const AEDesc *container, long *theCount, long stopAt, long *position, DescType *whichType)
{
#pragma unused (stopAt,position)
    WindowPtr owner;
    windowCHandle tempWCH;
    ShapesHandle  theShapes;
    mVerboseOutput("\p\nCounting Shapes ")
    if (container->descriptorType == cWindow || container->descriptorType == cDrawingArea) {
        owner = *((WindowPtr *)(*(container->dataHandle)));
        tempWCH = (windowCHandle)GetWRefCon(owner);
        if(*whichType == cGraphicShape){
        *theCount = (*tempWCH)->numShapes;
        } else {
        /* find specific things */
        *theCount = 0;
        theShapes = (*tempWCH)->theShapes;
        while(theShapes){
        if((*theShapes)->aeType == *whichType)*theCount +=1;
        theShapes = (*theShapes)->nextShape;
        }
        }
        
    } else {
        /* no luck joe, can't count items from this container */
        *theCount = -1;
    }
}
 
/* not yet implemented (and also not installed as a callback) */
pascal OSErr MyGetMarkTokenProc(const AEDesc *dContainerToken, DescType containerClass, AEDesc *result)
{
#pragma unused (dContainerToken,containerClass,result)
    OSErr myErr = noErr;
    mVerboseOutput("\p\nGetting a Mark token")
    return(myErr);
}
 
/* not yet implemented (and also not installed as a callback) */
pascal OSErr MyMarkProc(const AEDesc *dToken, const AEDesc *markToken, long index)
{
    OSErr myErr = noErr;
#pragma unused (dToken,markToken,index) 
    mVerboseOutput("\p\nMarking something")
    return(myErr);
}
 
/* not yet implemented (and also not installed as a callback) */
pascal OSErr MyAdjustMarksProc(long newStart, long newStop, const AEDesc *markToken)
{
#pragma unused (newStart,newStop,markToken)
    OSErr myErr = noErr;
    mVerboseOutput("\p\nAdjusting Marks")
    return(myErr);
}
 
/* This builds our GetData event, based on the things you've set up in the object */
/* definition dialogs. */
OSErr BuildGetDataEvent(AppleEvent *thisEvent, short which)
{
    OSErr myErr = noErr;
    AEDesc object;
    myErr = BuildFullObject(&object, which);
    myErr = AEPutParamDesc(thisEvent, keyDirectObject, &object);
    mAEErrorDisplay("\p putting object", myErr)
    if (!myErr)
        AEDisposeDesc(&object);
    /* one last thing to deal with.  We _can_, if we want, add a keyAERequestedType parameter. */
    /* this is optional, and specifies the format of the data we'd like to work with. */
    /* NOTE:  The receiver of this event may ignore this completely.  There may be */
    /* two reasons for the ignoring.... */
    /* 1) It's an optional param.  That means the receiver may not even look for it, or */
    /* know it's there. */
    /* 2) They may not be able to do it.  They may not be able to coerce their data to the */
    /* type you'd like to get back.  If so, they'll still send data back, it'll just */
    /* be in the standard format (as described in the registry) for this event and object. */
    /*  You will have to do the coercion yourself. */
    /* By The Way:  If you add system level coercion routines for the types you like to see */
    /* other apps on the same machine will be able to coerce to your types without having any */
    /* understanding of them. */
    /* By The Way II:  If you do not get back the type you want, you should also */
    /* try and coerce it yourself.  Perhaps you have the correct coercion */
    /* routine on your machine, one that was not available to the other app. */
    return(myErr);
}
 
/* This builds our SetData event, based on the things you've set up in the object */
/* definition dialogs. */
/* It's a little more complicated, since I have to throw in dialogs to get the  */
/* new value you want */
 
OSErr BuildSetDataEvent(AppleEvent *thisEvent, short which)
{
    OSErr myErr = noErr;
    DialogPtr tdial;
    short strung;
    RGBColor oldColor = {0,0,0};
    RGBColor newColor;
    Str63 paramString;
    short hitItem = 0;
    DescType theDataDesc;
    short doVis;
    short *tempShortPtr;
    Handle theDataHandle = NewHandle(0);
    AEDesc object;
    AEDesc dataToSet;
    /* build the window based on previous things (from dialog) */
    myErr = BuildFullObject(&object, which);
    mAEErrorDisplay("\p putting object", myErr)
    if (!myErr) {
        /* We've got the object built.  Since this is a SetData event, now we have to add the */
        /* actual data to set to the thing.  Yes kids, its DialogBox time.... */
        /* There's nothing interesting here except dialog box handling, if you want to get */
        /* on to Objects and ®s again, skip ahead to ¥¥here¥¥ */
        tdial = CommonDStart(kSetDDialog, 0, 0);
        /* show the correct items */
        switch (which) {
            case kWindowBoundsItem:
                ShowDialogItem(tdial, kRectTextItem);
                ShowDialogItem(tdial, kRectEdit1);
                ShowDialogItem(tdial, kRectEdit2);
                ShowDialogItem(tdial, kRectEdit3);
                ShowDialogItem(tdial, kRectEdit4);
                break;
            case kWindowTitleItem:
                ShowDialogItem(tdial, kNewTitleWordsItem);
                ShowDialogItem(tdial, kNewTitleEditLineItem);
                break;
            case kWindowVisiblityItem:
                ShowDialogItem(tdial, kMakeVisItem);
                ShowDialogItem(tdial, kMakeInvisItem);
                
                break;
            case kAllText:
                ShowDialogItem(tdial, kNewTextItem);
                ShowDialogItem(tdial, kNewText);
                
                break;
            case kWordText:
                ShowDialogItem(tdial, kNewWordItem);
                ShowDialogItem(tdial, kNewText);
                
                break;
            case kShapeItem:
                ShowDialogItem(tdial, kNewSBoundsRadio);
                ShowDialogItem(tdial, kNewSColorRadio);
                ShowDialogItem(tdial, kNewSColorButton);
                ShowDialogItem(tdial, kRectEdit1);
                ShowDialogItem(tdial, kRectEdit2);
                ShowDialogItem(tdial, kRectEdit3);
                ShowDialogItem(tdial, kRectEdit4);
                HiliteControl(SnatchHandle(tdial, kNewSColorButton),255);
                SetControlValue(SnatchHandle(tdial, kNewSBoundsRadio), true);   
                break;
        }
        if(which != kShapeItem)
            strung = which - 1 + kSettingVis;
        else
            strung = kSettingShape;
        GetIndString(paramString, kGeneralStrings, strung);
        /* I'm going to add a little more text here */
        if(which == kShapeItem)
            {DescType shapeTypes[] =  {
               cGraphicLine, cRectangle, cOval
            };
            Str32 theNum;
            HLock((Handle)gShapeObjSpecHandle);
            theNum[0] = 6;
            theNum[1] = ' ';
            BlockMove((Ptr)&(*gShapeObjSpecHandle)->form,(Ptr)&theNum[2],kFour);
            theNum[6] = ' ';
            AppendString(&paramString,&theNum);
            NumToString((*gShapeObjSpecHandle)->u.index,&theNum);
            AppendString(&paramString,&theNum);
            HUnlock((Handle)gShapeObjSpecHandle);
            
            }
        ParamText(paramString, "", "", "");
        ShowWindow(tdial);
        DrawDialog(tdial);
        if (which == kWindowVisiblityItem)
            SetControlValue(SnatchHandle(tdial, kMakeVisItem), true);
        doVis = true;
        
        {
            ModalFilterUPP upp = NewModalFilterProc(standardDialogFilter);
            while (hitItem != ok && hitItem != cancel) {
                ModalDialog(upp, &hitItem);
                /* the only one that has anything we need to set is the visibility one */
                if (which == kWindowVisiblityItem) {
                    if (hitItem == kMakeVisItem || hitItem == kMakeInvisItem) {
                        SetControlValue(SnatchHandle(tdial, kMakeVisItem), false);
                        SetControlValue(SnatchHandle(tdial, kMakeInvisItem), false);
                        SetControlValue(SnatchHandle(tdial, hitItem), true);
                    }
                }
                if (which == kShapeItem) {
                    switch (hitItem){
                    Point where;
                    case kNewSBoundsRadio:
                    case kNewSColorRadio:
                        SetControlValue(SnatchHandle(tdial, kNewSBoundsRadio), false);
                        SetControlValue(SnatchHandle(tdial, kNewSColorRadio), false);
                        SetControlValue(SnatchHandle(tdial, hitItem), true);
                    if(GetControlValue(SnatchHandle(tdial, kNewSColorRadio)))
                    HiliteControl(SnatchHandle(tdial, kNewSColorButton),0);
                    else
                    HiliteControl(SnatchHandle(tdial, kNewSColorButton),255);
                    break;
                    case kNewSColorButton:
                GetIndString(paramString, kGeneralStrings, kNewSCol);
                where.v = -1;
                where.h = -1;
                    GetColor(where,paramString,&oldColor,&newColor);
                    break;
                    }
                }
            }
            DisposeRoutineDescriptor(upp);
        }
        
        if (hitItem == 1) {
            /* and one more switch, pull parameters and create the actual data descriptor that */
            /* we will pass along in the SetData event. */
            /* ¥¥here¥¥ */
            /* pull the various parameters for the data to set, create a Desc to hold them, */
            /* and add that desc to our event */
            switch (which) {
                Str255 tempString;
                long theNum;
                register qq;
                case kWindowBoundsItem:
                    theDataDesc = typeQDRectangle;
                    /* ugg.  Go through all the edit lines and make a rect  */
                    SetHandleSize(theDataHandle, sizeof(Rect));
                    tempShortPtr = (short *)*theDataHandle;
                    HLock(theDataHandle);
                    for (qq = kRectEdit1; qq < kRectEdit4 + 1; qq++) {
                        GetDialogItemText((Handle)SnatchHandle(tdial, qq), tempString);
                        StringToNum(tempString, &theNum);
                        *tempShortPtr = theNum;
                        tempShortPtr = tempShortPtr + 1;        /* I have had problems with MPW C dealing with a += on a pointer, so I don't do it */
                    }
                    break;
                case kWindowTitleItem:
                    theDataDesc = typeChar;
                    GetDialogItemText((Handle)SnatchHandle(tdial, kNewTitleEditLineItem), tempString);
                    SetHandleSize(theDataHandle, tempString[0]);
                    /* move the text into the handle */
                    HLock(theDataHandle);
                    BlockMove((Ptr)&tempString[1], (Ptr)*theDataHandle, tempString[0]);
                    /* leave the data handle locked for later */
                    break;
                case kWindowVisiblityItem:
                    theDataDesc = typeBoolean;
                    doVis = GetControlValue(SnatchHandle(tdial, kMakeVisItem));
                    SetHandleSize(theDataHandle, sizeof(short));        /* a boolean is short length */
                    HLock(theDataHandle);                   /* for later */
                    tempShortPtr = (short *)*theDataHandle;
                    *tempShortPtr = (doVis ? -1 : false);
                    break;
                case kAllText:
                case kWordText:
                    /* these are both the same */
                    theDataDesc = typeChar;
                    GetDialogItemText((Handle)SnatchHandle(tdial, kNewText), tempString);
                    SetHandleSize(theDataHandle, tempString[0]);
                    /* move the text into the handle */
                    HLock(theDataHandle);
                    BlockMove((Ptr)&tempString[1], (Ptr)*theDataHandle, tempString[0]);
                    
                    break;
                case kShapeItem:
                /* make the parameters for the shape change */
                if(GetControlValue(SnatchHandle(tdial, kNewSColorRadio))){
                /* setting color, the color is in newColor */
                                    theDataDesc = typeRGBColor;
                    SetHandleSize(theDataHandle, sizeof(RGBColor));
                    HLock(theDataHandle);
                    BlockMove((Ptr)&newColor,*theDataHandle,sizeof(RGBColor));
 
                } else {
                /* setting rect, same stuff as window  */
                                    theDataDesc = typeQDRectangle;
                    SetHandleSize(theDataHandle, sizeof(Rect));
                    tempShortPtr = (short *)*theDataHandle;
                    HLock(theDataHandle);
                    for (qq = kRectEdit1; qq < kRectEdit4 + 1; qq++) {
                        GetDialogItemText((Handle)SnatchHandle(tdial, qq), tempString);
                        StringToNum(tempString, &theNum);
                        *tempShortPtr = theNum;
                        tempShortPtr = tempShortPtr + 1;        /* I have had problems with MPW C dealing with a += on a pointer, so I don't do it */
                    }
 
 
 
                }
                AddShapeProperty(&object,GetControlValue(SnatchHandle(tdial, kNewSColorRadio)));
                    break;
            }
        } else {
            myErr = userCanceledErr;
        }
        DisposeDialog(tdial);
        /* dialog is gone, the data we want is in theDataHandle, the descriptor type is in theDataDesc, */
        /* it's time to put the data on the event. */
        /* create a desc to hold it */
        if (hitItem == 1) {
            /* put the object into the event now */
            myErr = AEPutParamDesc(thisEvent, keyDirectObject, &object);
            AEDisposeDesc(&object);
 
            
            
            myErr = AECreateDesc(theDataDesc, (Ptr)*theDataHandle, GetHandleSize(theDataHandle), &dataToSet);
            DisposeHandle(theDataHandle);                    /* don't need this anymore */
            if (!myErr) {
                /* the desc created fine.  Add this to the event */
                myErr = AEPutParamDesc(thisEvent, keyAEData, &dataToSet);
                myErr = AEDisposeDesc(&dataToSet);
                /* and the event is all ready to be sent by the caller, so we return */
            }
        } else {
            myErr = userCanceledErr;
        }
    }
    return(myErr);
}
 
/* BuildFullObject builds an object specifier based on the parameters set in */
/* the object dialogs in this sample. */
OSErr BuildFullObject(AEDesc *object, short which)
{
    OSErr myErr = noErr;
    AEDesc tempDesc;
    AEDesc otherTemp;
    long number1 = 1;
    DescType Props[3] =  {
        pBounds, pName, pVisible
    };
    
    BuildWindowObject(object);
    switch (which) {
        /* we already have a window object, so for the first three, create a property desc and type */
        /* since we're only working with the properties of windows */
        case kWindowBoundsItem:
        case kWindowTitleItem:
        case kWindowVisiblityItem:
            AEDuplicateDesc(object, &otherTemp);
            myErr = AECreateDesc(typeType, (Ptr)&Props[which - 1], kFour, &tempDesc);
            myErr = CreateObjSpecifier(typeProperty, &otherTemp, formPropertyID, &tempDesc, true, object);
            
            break;
            
        case kAllText:
            /* we're asking for all the text in the window */
            AEDuplicateDesc(object, &otherTemp);
            HLock((Handle)gTextObjSpecHandle);
            if ((*gTextObjSpecHandle)->u.index == 0)
                (*gTextObjSpecHandle)->u.index = 1;
            myErr = AECreateDesc(typeLongInteger, (Ptr)&(*gTextObjSpecHandle)->u.index, sizeof(long), &tempDesc);
            
            myErr = CreateObjSpecifier(cText, &otherTemp, formAbsolutePosition, &tempDesc, true, object);
            
            HUnlock((Handle)gTextObjSpecHandle);
            break;
        case kWordText:
            /* one word from the window and text object  */
            AEDuplicateDesc(object, &otherTemp);
            HLock((Handle)gTextObjSpecHandle);
            if ((*gTextObjSpecHandle)->u.index == 0)
                (*gTextObjSpecHandle)->u.index = 1;
            myErr = AECreateDesc(typeLongInteger, (Ptr)&(*gTextObjSpecHandle)->u.index, sizeof(long), &tempDesc);
            
            myErr = CreateObjSpecifier(cText, &otherTemp, formAbsolutePosition, &tempDesc, true, object);
            AEDuplicateDesc(object, &otherTemp);
            if ((*gTextObjSpecHandle)->wordNumber == 0)
                (*gTextObjSpecHandle)->wordNumber = 0;
            myErr = AECreateDesc(typeLongInteger, (Ptr)&(*gTextObjSpecHandle)->wordNumber, sizeof(long), &tempDesc);
            myErr = CreateObjSpecifier(cWord, &otherTemp, formAbsolutePosition, &tempDesc, true, object);
            
            HUnlock((Handle)gTextObjSpecHandle);
            break;
            /* for these, we have to add the additional cDrawingArea container */
        case kShapeItem:
            AEDuplicateDesc(object, &otherTemp);
            /* this always defaults to 1, we have only one drawing area per window */
            AECreateDesc(typeLongInteger, (Ptr)&number1, sizeof(long), &tempDesc);
            myErr = CreateObjSpecifier(cDrawingArea, &otherTemp, formAbsolutePosition, &tempDesc, true, object);
            /* now which the thing */
 
            myErr = CreateShapeObject(object);
            break;
    }
    mAEErrorDisplay("\p creating object", myErr)
    
    return(myErr);
    
}
/* BuildWindowObject builds a window object from null, based on the parameters you have */
/* specified in the dialog boxes */
OSErr BuildWindowObject(AEDesc *returnedObject)
{
    long theLong = 1;
    OSErr myErr = noErr;
    AEDesc containerDesc;
    AEDesc theIdentifier;
    /* establish some defaults so the object will be right even if the Window Object dialog hasn't been used */
    DescType theForm = formAbsolutePosition;
    long number1 = 1;
    HLock((Handle)gWindObjSpecHandle);
    switch ((*gWindObjSpecHandle)->form) {
        case formAbsolutePosition:
            theForm = formAbsolutePosition;
            /* make an abs for spec */
            /* What??  What that comment really meant, was create an object specifier for a window */
            /* based on the data we have stored in our window descriptor object, that's */
            /* set with the dialog, based on absolute position or name */
            if ((*gWindObjSpecHandle)->u.index == 0)
                (*gWindObjSpecHandle)->u.index = 1;
            myErr = AECreateDesc(typeLongInteger, (Ptr)&(*gWindObjSpecHandle)->u.index, sizeof(long), &theIdentifier);
            
            break;
        case formName:
            /* make an object specifier based on window name */
            
            theForm = formName;
            myErr = AECreateDesc(typeChar, (Ptr)&(*gWindObjSpecHandle)->u.name[1], (*gWindObjSpecHandle)->u.name[0],
                                 &theIdentifier);
            break;
        default:
            /* defaults to a byindex, window one spec if nothing else has been set up */
            myErr = AECreateDesc(typeLongInteger, (Ptr)&number1, sizeof(long), &theIdentifier);
            break;
    }
    mAEErrorDisplay("\p creating formProperty for this thing", myErr)
    /* Every event will act on a window */
    
    /* we're always adding windows from nul, so duplicate nul */
    AEDuplicateDesc(&gNullDesc, &containerDesc);
    
    /* build a window object contained in nul */
    myErr = CreateObjSpecifier(cWindow, &containerDesc, theForm, &theIdentifier, true, returnedObject);
    mAEErrorDisplay("\p creating window object", myErr)
    HUnlock((Handle)gWindObjSpecHandle);
    return(myErr);
}
 
/* CreateShapeObject attached the shape object to the basic specifier */
/* already created (nul>cWindow>cDrawingArea) */
/* Since all my shapes are basically the same, we have one routine to do this for us */
 
OSErr CreateShapeObject(AEDesc *theContainer)
{
    AEDesc tempDesc;
    AEDesc indexDesc;
    DescType theType;
    OSErr myErr = noErr;
    HLock((Handle)gShapeObjSpecHandle);
    theType = (*gShapeObjSpecHandle)->form;
    /* duplicate what we have so far */
    AEDuplicateDesc(theContainer, &tempDesc);
    /* In this example, we're only implementing a ByIndex form for graphics, so... */
    if ((*gShapeObjSpecHandle)->u.index == 0)
        (*gShapeObjSpecHandle)->u.index = 1;
    myErr = AECreateDesc(typeLongInteger, (Ptr)&(*gShapeObjSpecHandle)->u.index, sizeof(long), &indexDesc);
    myErr = CreateObjSpecifier(theType, &tempDesc, formAbsolutePosition, &indexDesc, true, theContainer);
    HUnlock((Handle)gShapeObjSpecHandle);
    return(myErr);
}
 
/* AddShapeProperty tacks a property specifier onto an already created shape specifier */
OSErr AddShapeProperty(AEDesc *theObject,Boolean which)
{
    AEDesc tempDesc,otherTemp;
    DescType theProp;
    OSErr myErr = noErr;
    if(which)
        theProp = pPenColor;
    else
        theProp = pBounds;
    myErr = AEDuplicateDesc(theObject, &otherTemp);
    myErr |= AECreateDesc(typeType,(Ptr) &theProp, kFour, &tempDesc);
    myErr |= CreateObjSpecifier(typeProperty, &otherTemp, formPropertyID, &tempDesc, true, theObject);
    
    return(myErr);
}
 
#undef __AEOBJ__