Sources/MSAccessors.c

// MSAEAccessors.c
//
// Original version by Jon Lansdell and Nigel Humphreys.
// 4.0 and 3.1 updates by Greg Sutton.
// ©Apple Computer Inc 1996, all rights reserved.
 
/*
    Changes for 3.1
 
    14-Nov-95 : GS : Removed reliance on compiler setting local variable to zero
                        in WindowFormAbsolutePosition() and TextFormAbsolutePosition().
 
    Changes for 4.0
 
    29-Feb-96 : GS : Added differentiation between windows and documents.
    29-Feb-96 : GS : Added resolution of menus and menu items.
*/
 
#include "MSAccessors.h"
 
#include <Menus.h>
#ifdef THINK_C
    #include "PLStrs.h"
#else
    #include <PLStringFuncs.h>
#endif
#include <Scrap.h>
#include <TextEdit.h>
#include <AEObjects.h>
#include <AEPackObject.h>
#include <AERegistry.h>
#include "MSGlobals.h"
#include "MSUtils.h"
#include "MSAEUtils.h"
#include "MSWindow.h"
#include "MSFile.h"
#include "MSAppleEvents.h"
 
#include "MSToken.h"
#include "MSAETextUtils.h"
#include "MSAEWindowUtils.h"
#include "MSAEMenuUtils.h"
 
 
#pragma segment AppleEvent
 
// Install accessors that are used when AEResolve is called to convert
// object specifiers into internal representations (tokens).
 
OSErr   InstallAccessors(void)
{
    OSErr   err;
 
    err = AEInstallObjectAccessor(cApplication, typeNull,           NewOSLAccessorProc(ApplicationFromNullAccessor), 0, false);
    err = AEInstallObjectAccessor(cWindow,      typeNull,           NewOSLAccessorProc(WindowFromNullAccessor), 0, false);
    err = AEInstallObjectAccessor(cDocument,    typeNull,           NewOSLAccessorProc(DocumentFromNullAccessor), 0, false);
    err = AEInstallObjectAccessor(cProperty,    typeNull,           NewOSLAccessorProc(PropertyFromNullAccessor), 0, false);
    err = AEInstallObjectAccessor(cMenu,        typeNull,           NewOSLAccessorProc(MenuFromNullAccessor), 0, false);
 
    err = AEInstallObjectAccessor(cWindow,      typeMyAppl,         NewOSLAccessorProc(WindowFromNullAccessor), 0, false);
    err = AEInstallObjectAccessor(cDocument,    typeMyAppl,         NewOSLAccessorProc(DocumentFromNullAccessor), 0, false);
    err = AEInstallObjectAccessor(cProperty,    typeMyAppl,         NewOSLAccessorProc(PropertyFromApplAccessor), 0, false);
    err = AEInstallObjectAccessor(cMenu,        typeMyAppl,         NewOSLAccessorProc(MenuFromNullAccessor), 0, false);
 
    err = AEInstallObjectAccessor(cProperty,    typeMyWndw,         NewOSLAccessorProc(PropertyFromWndwAccessor), 0, false);
 
            // Handle text from a window
            // e.g. some character of last document
    err = AEInstallObjectAccessor(cInsertionPoint,typeMyDocument,   NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cChar,        typeMyDocument,     NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cText,        typeMyDocument,     NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cWord,        typeMyDocument,     NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cParagraph,   typeMyDocument,     NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cProperty,    typeMyDocument,     NewOSLAccessorProc(PropertyFromDocumentAccessor), 0, false);
 
            // Handle text items within text items
            // e.g. last word of first paragraph of front document
    err = AEInstallObjectAccessor(cInsertionPoint,typeMyText,       NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cChar,        typeMyText,         NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cText,        typeMyText,         NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cWord,        typeMyText,         NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cParagraph,   typeMyText,         NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cProperty,    typeMyText,         NewOSLAccessorProc(PropertyFromTextAccessor), 0, false);
 
            // Menus
    err = AEInstallObjectAccessor(cMenuItem,    typeMyMenu,         NewOSLAccessorProc(MenuItemFromMenuAccessor), 0, false);
    err = AEInstallObjectAccessor(cProperty,    typeMyMenu,         NewOSLAccessorProc(PropertyFromMenuAccessor), 0, false);
 
            // Menu Items
    err = AEInstallObjectAccessor(cProperty,    typeMyMenuItem,     NewOSLAccessorProc(PropertyFromMenuItemAccessor), 0, false);
 
            // Handle text items from lists (of hopefully text items) also
            //  e.g. every word of every paragraph of front document
            //  or even - every word of every character of every paragraph of front document
    err = AEInstallObjectAccessor(cInsertionPoint,typeAEList,       NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cChar,        typeAEList,         NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cText,        typeAEList,         NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cWord,        typeAEList,         NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cParagraph,   typeAEList,         NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cProperty,    typeAEList,         NewOSLAccessorProc(PropertyFromListAccessor), 0, false);
 
 
            // This is for 'select insertion point before contents of document 1'
    err = AEInstallObjectAccessor(cInsertionPoint,  typeMyWindowProp,   NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
 
            // This accessor is for getting properties of window properties
            //  e.g. font of contents of document 1
            // Relies on ability to coerce from a window property to text
            //  for certain properties.
    err = AEInstallObjectAccessor(cProperty,    typeMyDocumentProp, NewOSLAccessorProc(PropertyFromTextAccessor), 0, false);
 
    return(err);
}
 
 
// Given selectionData of formAbsolutePosition for a window this routine returns
//  a WindowToken descriptor for specified window.
//
// e.g. tell application "MenuScripter"
//          window 1
//          --first document
//          --some window
//          --every window
//      end tell
 
OSErr   WindowFormAbsolutePosition(const AEDesc *selectionData, AEDesc* result)
{
    AEDesc  itemDesc = {typeNull, NULL};
    short   windowCount,
            index;
    OSErr   err;
 
    windowCount = CountWindows();
    
    if (! windowCount)
        return(errAEIllegalIndex);
    
    if (typeAbsoluteOrdinal == selectionData->descriptorType)
    {
        err = noErr;
    
        switch (*(DescType *)*selectionData->dataHandle)
        {
            case kAEFirst:
                index = 1;
                break;
    
            case kAELast:
                index = windowCount;
                break;
    
            case kAEMiddle:
                index = (windowCount + 1) / 2;
                break;
    
            case kAEAny:
                index = (Random() % windowCount) + 1;
                break;
    
            case kAEAll:
                err = AECreateList(NULL, 0 , false, result);
                if (noErr != err) goto done;
                
                for (index = 1; index <= windowCount; index++)
                {
                    err = GetDescOfNthWindow(index, &itemDesc);
                    if (noErr != err) goto done;
                    
                    err = AEPutDesc(result, 0, &itemDesc);
                    if (noErr != err) goto done;
                    
                    if (itemDesc.dataHandle)
                        AEDisposeDesc(&itemDesc);
                }
                
                goto done;      // We have created our list descriptor
                break;          // so we can just tidy up and return.
                
            default:
                err = errAETypeError;
        }
    }
    else
        err = GetIntegerFromDescriptor(selectionData, &index);
 
    if (noErr != err) goto done;
    
    if (index < 0)      // Handle negative indexes
        index = windowCount + index + 1;
        
    if (index > windowCount || index <= 0)
        err = errAEIllegalIndex;
    else
        err = GetDescOfNthWindow(index, result);
 
done:   
    if (itemDesc.dataHandle)
        AEDisposeDesc(&itemDesc);
 
    return(err);
} // WindowFormAbsolutePosition
 
 
// Given a formName descriptor in selectionData, this routine returns a
// WindowToken descriptor for the window with that name.
//
// e.g. tell application "MenuScripter"
//          document "Untitled"
//      end tell
 
OSErr   WindowFormName(const AEDesc *selectionData, AEDesc* result)
{
    Str255      name;
    OSErr       err;
    
                // This tries to coerce it first
    err = GetPStringFromDescriptor(selectionData, name);
    if (noErr != err) goto done;
 
    err = GetDescOfNamedWindow(name, result);
 
done:
    return(err);
}
 
 
// Get a WindowToken descriptor for a window or document object specifier
// from NULL (or the application). Only handles formAbsolutePosition and formName
//
// e.g. tell application "MenuScripter"
//          window 1
//          --first document
//          --document "Untitled"
//          --every window
//      end tell
 
pascal OSErr    WindowFromNullAccessor(DescType         wantClass,
                                        const AEDesc    *container,
                                        DescType        containerClass,
                                        DescType        form, 
                                        const AEDesc    *selectionData,
                                        AEDesc          *value,
                                        long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused(container,theRefCon)
#endif
 
    OSErr       err;
    
        // Can only handle cWindow and cDocument
    if ( wantClass != cWindow )
        return errAEWrongDataType;
        
        // Can only handle typeNull and typeMyAppl
    if ( containerClass != typeNull && containerClass != typeMyAppl )
        return errAENoSuchObject;
    
    switch (form)
    {
        case formAbsolutePosition:
            err = WindowFormAbsolutePosition(selectionData, value);
            break;
            
        case formName:
            err = WindowFormName(selectionData, value);
            break;
            
        default:
            err = errAEBadTestKey;
    }
            
    return err;
} // WindowFromNullAccessor
 
 
OSErr   DocumentFormAbsolutePosition(const AEDesc   *selectionData, AEDesc* result)
{
    AEDesc  itemDesc = {typeNull, NULL};
    short   aCount,
            index;
    OSErr   err;
 
    aCount = CountDocuments();
    
    if ( ! aCount )
        return(errAEIllegalIndex);
    
    if ( typeAbsoluteOrdinal == selectionData->descriptorType )
    {
        err = noErr;
    
        switch (*(DescType *)*selectionData->dataHandle)
        {
            case kAEFirst:
                index = 1;
                break;
    
            case kAELast:
                index = aCount;
                break;
    
            case kAEMiddle:
                index = ( aCount + 1 ) / 2;
                break;
    
            case kAEAny:
                index = ( Random() % aCount ) + 1;
                break;
    
            case kAEAll:
                err = AECreateList( NULL, 0 , false, result );
                if (noErr != err) goto done;
                
                for ( index = 1; index <= aCount; index++ )
                {
                    err = GetDescOfNthDocument( index, &itemDesc );
                    if (noErr != err) goto done;
                    
                    err = AEPutDesc( result, 0, &itemDesc );
                    if (noErr != err) goto done;
                    
                    (void)AEDisposeDesc( &itemDesc );
                }
                
                goto done;      // We have created our list descriptor
                break;          // so we can just tidy up and return.
                
            default:
                err = errAETypeError;
        }
    }
    else
        err = GetIntegerFromDescriptor(selectionData, &index);
 
    if (noErr != err) goto done;
    
    if (index < 0)      // Handle negative indexes
        index = aCount + index + 1;
        
    if (index > aCount || index <= 0)
        err = errAEIllegalIndex;
    else
        err = GetDescOfNthDocument(index, result);
 
done:   
    (void)AEDisposeDesc(&itemDesc);
 
    return(err);
} // DocumentFormAbsolutePosition
 
 
OSErr   DocumentFormName(const AEDesc *selectionData, AEDesc* result)
{
    Str255      name;
    OSErr       err;
    
            // This tries to coerce it first
    err = GetPStringFromDescriptor(selectionData, name);
    if (noErr != err) goto done;
 
    err = GetDescOfNamedDocument(name, result);
 
done:
    return(err);
}
 
 
pascal OSErr    DocumentFromNullAccessor(DescType           wantClass,
                                            const AEDesc    *container,
                                            DescType        containerClass,
                                            DescType        form, 
                                            const AEDesc    *selectionData,
                                            AEDesc          *value,
                                            long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused (container,theRefCon)
#endif
 
    OSErr       err;
    
        // Can only handle cWindow and cDocument
    if ( wantClass != cDocument )
        return errAEWrongDataType;
        
        // Can only handle typeNull and typeMyAppl
    if ( containerClass != typeNull && containerClass != typeMyAppl )
        return errAENoSuchObject;
    
    switch (form)
    {
        case formAbsolutePosition:
            err = DocumentFormAbsolutePosition( selectionData, value );
            break;
            
        case formName:
            err = DocumentFormName( selectionData, value );
            break;
            
        default:
            err = errAEBadTestKey;
    }
            
    return err;
} // DocumentFromNullAccessor
 
 
pascal OSErr   ApplicationFromNullAccessor(DescType     wantClass,
                                            const AEDesc    *container,
                                            DescType        containerClass,
                                            DescType        form, 
                                            const AEDesc    *selectionData,
                                            AEDesc          *value,
                                            long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused(container,selectionData,theRefCon)
#endif
 
    OSErr    myErr;
    AppToken theApp;
    AEDesc   resultDesc;
    
    value->dataHandle     = nil;
    resultDesc.dataHandle = nil;
    
    /* 
        should only be called with wantClass = cWindow and
        with containerClass = typeNull.
        Currently accept as either formName or formAbsolutePosition
    */
    
    if ((wantClass != cApplication) || (containerClass != typeNull) ||
          !((form == formName) || (form == formAbsolutePosition)))
        return(errAEWrongDataType);
    
    if ((form == formName) || (form == formAbsolutePosition))
    {
        theApp.highLongOfPSN = 0;
        theApp.lowLongOfPSN  = kCurrentProcess;
    }
        
    myErr = AECreateDesc(typeMyAppl, (Ptr)&theApp, sizeof(theApp), value);
            
    return(myErr);
}   /* ApplicationFromNullAccessor */
 
 
// Given a formAbsolutePosition selectionData descriptor and a TextToken from
// which to index from. This routine returns a TextToken descriptor for the
// text specified.
//
// e.g. tell application "MenuScripter"
//          first word of window 1  -- window one will be dealt with in
//                                  -- WindowFromNullAccessor().
//          --some character of middle paragraph of last document
//                                  -- container token will be a paragraph
//      end tell
 
OSErr   TextFormAbsolutePosition(TextToken* containerToken, AEDesc* selectionData,
                                                    DescType wantClass, AEDesc* result)
{
    DPtr        docPtr;
    short       elementCount;
    AEDesc      aDesc = {typeNull, NULL};
    long        index;
    OSErr       err;
    
    docPtr = DPtrFromWindowPtr(containerToken->tokenWindow);
 
    if (! docPtr)
    {
        err = errAENoSuchObject;
        goto done;
    }
 
    err = CountTextElements(docPtr->theText, containerToken->tokenOffset,
                                containerToken->tokenLength, wantClass, &elementCount);
    if (noErr != err) goto done;
 
    if (typeAbsoluteOrdinal == selectionData->descriptorType)
    {
        err = noErr;
    
        switch (*(DescType *)*selectionData->dataHandle)
        {
            case kAEFirst:
                index = 1;
                break;
    
            case kAELast:
                index = elementCount;
                break;
    
            case kAEMiddle:
                index = (elementCount + 1) / 2;
                break;
    
            case kAEAny:
                index = (Random() % elementCount) + 1;
                break;
    
            case kAEAll:
                err = AECreateList(NULL, 0 , false, result);
                
                for (index = 1; index <= elementCount; index++)
                {
                    if (noErr == (err = GetDescOfNthTextElement(index, wantClass,
                                                                containerToken, &aDesc)))
                    {
                        err = AEPutDesc(result, 0, &aDesc);
                        AEDisposeDesc(&aDesc);
                    }
                }
                goto done;      // Created our result - clean up and return
                break;
    
            default:
                err = errAETypeError;
        }
    }
    else        // Try and get an index out of the descriptor
        err = GetLongIntFromDescriptor(selectionData, &index);
    
    if (noErr != err) goto done;
                    // kAEAll has already created it's list
    err = GetDescOfNthTextElement(index, wantClass,         // Checks for negatives and
                                containerToken, result);    // out of range.
                                        
done:
    if (aDesc.dataHandle)
        AEDisposeDesc(&aDesc);
 
    return(err);
}
 
 
// Given a TextToken container and a formRange descriptor. This routine
// creates a TextToken descriptor starting at the the beginning of the
// first item in the range and ending at the end of the last item in the range.
//
// e.g. tell application "MenuScripter"
//          paragraphs 2 thru 3 of document 1
//      end tell
 
OSErr   TextFormRange(TextToken* containerToken, AEDesc* selectionData,
                                                    DescType wantClass, AEDesc* result)
{
#ifdef __MWERKS__
    #pragma unused(wantClass)
#endif
 
    AEDesc      selectionRecord = {typeNull, NULL};
    TextToken   startToken,
                stopToken;
    DescType    returnedType;
    Size        actualSize;
    OSErr       err;
 
        // coerce the selection data into an AERecord
    err = AECoerceDesc(selectionData, typeAERecord, &selectionRecord);
    if (noErr != err) goto done;
    
        // get the start object as a text token this will reenter
        // TextElemFromTextAccessor() but as formAbsolutePosition via 
        // our installed coercion handler CoerceObjToAnything()
        // because the keyAERangeStart parameter is actually an object specifier.
    err = AEGetKeyPtr(&selectionRecord, keyAERangeStart, typeMyText,
                            &returnedType, (Ptr)&startToken, sizeof(startToken), &actualSize);
    if (noErr != err) goto done;
    
        // now do the same for the stop object
    err = AEGetKeyPtr(&selectionRecord, keyAERangeStop, typeMyText,
                            &returnedType, (Ptr)&stopToken, sizeof(stopToken), &actualSize);
    if (noErr != err) goto done;
    
    if (containerToken->tokenWindow != startToken.tokenWindow
            || containerToken->tokenWindow != stopToken.tokenWindow)
    {
        err = errAECorruptData;     // or whatever
        goto done;
    }
        
        // Use startToken to create result descriptor
    startToken.tokenLength = stopToken.tokenOffset + stopToken.tokenLength - startToken.tokenOffset;
                                                         
    err = AECreateDesc(typeMyText, (Ptr)&startToken, sizeof(startToken), result);
 
done:
    if (selectionRecord.dataHandle)
        AEDisposeDesc(&selectionRecord);
 
    return(err);
}
 
 
// Given a container TextToken and a formRelativePosition selectionData descriptor
// this routine returns a TextToken descriptor to the reative wantClass object
// specified.
// This routine currently only handles relative positions for a wantClass
// of cInsertionPoint.
//
// e.g. tell application "MenuScripter"
//          insertion point after word 5 of document 1
//      end tell
 
OSErr   TextFormRelativePosition(TextToken* containerToken, AEDesc* selectionData,
                                                    DescType wantClass, AEDesc* result)
{
    TextToken   aTextToken;
    DescType    aPosition;
    OSErr       err;
    
    aTextToken.tokenWindow = containerToken->tokenWindow;
 
    switch (wantClass)
    {
        case cInsertionPoint:
            err = GetEnumeratedFromDescriptor(selectionData, &aPosition);
            
            switch (aPosition)
            {
                case kAEPrevious:
                case kAEBefore:
                case kAEBeginning:
                    // No change to offset - just 0 length now
                    // containerToken.tokenOffset = containerToken.tokenOffset;
                    aTextToken.tokenOffset = containerToken->tokenOffset;
                    aTextToken.tokenLength = 0;
                    break;
            
                case kAENext:
                case kAEAfter:
                case kAEEnd:
                    aTextToken.tokenOffset = containerToken->tokenOffset + containerToken->tokenLength;
                    aTextToken.tokenLength = 0;
                    break;
                    
                default:
                    err = errAEIllegalIndex;
            }
            break;
            
        default:
            err = errAEWrongDataType;   // Could do cChar, cWordÉ but this is only a sample
    }
    
    if (noErr != err) goto done;
 
    err = AECreateDesc(typeMyText, (Ptr)&aTextToken, sizeof(aTextToken), result);
 
done:
    return(err);
}
 
 
// Tries to create a TextToken descriptor given a container which may
// be a TextToken descriptor or a WindowToken descriptor. A
// selectionData descriptor which can be of formAbsolutePosition,
// formRange or formRelativePosition, and a wantClass which should
// be cInsertionPoint, cChar, cText, cWord or cParagraph.
//
// e.g. tell application "MenuScripter"
//          first word of document 1 -- document 1 will go through WindowFromNullAccessor()
//                                   -- which will return a WindowToken descriptor. This
//                                   -- descriptor will then be coerced to a TextToken
//                                   -- descriptor and used as the container.
//      end tell
 
pascal OSErr    TextElemFromTextAccessor(DescType           wantClass,
                                            AEDesc          *container,
                                            DescType        containerClass,
                                            DescType        form,
                                            AEDesc          *selectionData,
                                            AEDesc          *value,
                                            long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused(containerClass)
#endif
 
    TextToken   containerToken;
    long        index;
    long        itemCount;
    AEDesc      aDesc = {typeNull, NULL},
                resultDesc = {typeNull, NULL};
    DescType    returnedType;
    Size        actualSize;
    OSErr       myErr;
    
                // If it's a list then we need to call this accessor for every
                // item within the list (which could of course be more lists).
    if (typeAEList == container->descriptorType)
    {
        myErr = AECreateList(NULL, 0 , false, value);   // Result will also be a list of items
        if (noErr != myErr) goto done;
        myErr = AECountItems(container, &itemCount);
        if (noErr != myErr) goto done;
 
        for (index = 1; index <= itemCount; index++)    // Do in forward order
        {
            myErr = AEGetNthDesc(container, index, typeWildCard, &returnedType, &aDesc);
 
            if (noErr == myErr)     // Call this function recursively if necessary
                myErr = TextElemFromTextAccessor(wantClass, &aDesc, returnedType,
                                            form, selectionData, &resultDesc, theRefCon);
            
            if (noErr == myErr)     // Add item to the end of our list
                myErr = AEPutDesc(value, 0, &resultDesc);
            
            if (aDesc.dataHandle)
                AEDisposeDesc(&aDesc);
            if (resultDesc.dataHandle)
                AEDisposeDesc(&resultDesc);
        }
    }
    else
    {               // We have a coercion handler from document to text
        myErr = AECoerceDesc(container, typeMyText, &aDesc);
        if (noErr != myErr) goto done;
 
                // Get the containing TextToken
        GetRawDataFromDescriptor(&aDesc, (Ptr)&containerToken, sizeof(containerToken), &actualSize);
        
        switch (form)
        {
            case formAbsolutePosition:
                myErr = TextFormAbsolutePosition(&containerToken, selectionData,
                                                                    wantClass, value);
                break;                                      
                
            case formRange:
                myErr = TextFormRange(&containerToken, selectionData,
                                                            wantClass, value);
                break;
                
            case formRelativePosition:
                myErr = TextFormRelativePosition(&containerToken, selectionData,
                                                                wantClass, value);
                break;
                
            default:
                myErr = errAEBadKeyForm;
        }
    }
 
done:
    if (aDesc.dataHandle)
        AEDisposeDesc(&aDesc);
        
    return(myErr);
}   // TextElemFromTextAccessor
 
 
// Given a TextToken descriptor as a container convert this into 
// a TextPropToken descriptor for the property.
 
pascal OSErr    PropertyFromTextAccessor(DescType           wantClass,
                                            const AEDesc    *container,
                                            DescType        containerClass,
                                            DescType        form, 
                                            const AEDesc    *selectionData,
                                            AEDesc          *value,
                                            long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused (theRefCon, containerClass)
#endif
 
    AEDesc          textDesc = {typeNull, NULL},
                    propertyDesc = {typeNull, NULL};
    Size            actualSize;
    TextToken       aTextToken;
    TextPropToken   aTextPropToken;
    DescType        aProperty;
    OSErr           err;
    
    if (cProperty != wantClass || formPropertyID != form)
        return(errAEWrongDataType);
        
                // Try and coerce to a TextToken descriptor
    err = AECoerceDesc(container, typeMyText, &textDesc);
    if (noErr != err) goto done;
    
                // Get the TextToken
    GetRawDataFromDescriptor(&textDesc, (Ptr)&aTextToken,
                            sizeof(aTextToken), &actualSize);
            
                // Make sure the selection data is typeType
    err = AECoerceDesc(selectionData, typeType, &propertyDesc);
    if (noErr != err) goto done;
 
                // Get the property
    GetRawDataFromDescriptor(&propertyDesc, (Ptr)&aProperty,
                                    sizeof(aProperty),  &actualSize);
            
                //  Combine the two into single token
    aTextPropToken.tokenTextToken = aTextToken;
    aTextPropToken.tokenProperty  = aProperty;
    
    err = AECreateDesc(typeMyTextProp, (Ptr)&aTextPropToken,
                                sizeof(aTextPropToken), value);
 
done:       
    (void)AEDisposeDesc(&textDesc);
    (void)AEDisposeDesc(&propertyDesc);
        
    return(err);
} // PropertyFromTextAccessor
 
 
// Given a WindowToken descriptor as a container convert this into 
// a WindowPropToken descriptor for the property.
 
pascal OSErr    PropertyFromWndwAccessor(DescType           wantClass,
                                            AEDesc          *container,
                                            DescType        containerClass,
                                            DescType        form, 
                                            AEDesc          *selectionData,
                                            AEDesc          *value,
                                            long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused(containerClass)
#endif
 
    long                itemCount,
                        index;
    AEDesc              resultDesc = {typeNull, NULL},
                        windowDesc = {typeNull, NULL},
                        propDesc = {typeNull, NULL};
    Size                actualSize;
    DescType            returnedType;
    WindowToken         theWindowToken;
    WindowPropToken     myWindowProp;
    OSErr               err;
    
    if (typeAEList == container->descriptorType)
    {
        err = AECreateList(NULL, 0 , false, value);     // Result will also be a list of items
        if (noErr != err) goto done;
        err = AECountItems(container, &itemCount);
        if (noErr != err) goto done;
    
        for (index = 1; index <= itemCount; index++)    // Do in forward order
        {
            err = AEGetNthDesc(container, index, typeWildCard, &returnedType, &windowDesc);
    
            if (noErr == err)                           // Recursively call this routine
                                                        // - could be another list.
                err = PropertyFromWndwAccessor(wantClass, &windowDesc, windowDesc.descriptorType,
                                                        form, selectionData, &resultDesc, theRefCon);
            
            if (noErr == err)                           // Add item to the end of our list
                err = AEPutDesc(value, 0, &resultDesc);
            
            if (windowDesc.dataHandle)
                AEDisposeDesc(&windowDesc);
            if (resultDesc.dataHandle)
                AEDisposeDesc(&resultDesc);
        }
    }
    else
    {       // get the window token - it's the container
        
        err = AECoerceDesc(container, typeMyWndw, &windowDesc);
        GetRawDataFromDescriptor(&windowDesc, (Ptr)&theWindowToken,
                                        sizeof(theWindowToken), &actualSize);
                                                            
            // Check the window exists
        
        if (theWindowToken.tokenWindow == NULL)
            err = errAEIllegalIndex;
        else
        {       // get the property - it's in the selection data
            
            err = AECoerceDesc(selectionData, typeType, &propDesc);
            GetRawDataFromDescriptor(&propDesc, (Ptr)&returnedType,
                                            sizeof(returnedType), &actualSize);
            
                // Combine the two into single token
 
            myWindowProp.tokenWindowToken = theWindowToken;
            myWindowProp.tokenProperty    = returnedType;
            
            err = AECreateDesc(typeMyWindowProp, (Ptr)&myWindowProp,
                                                sizeof(myWindowProp), value);
        }
    }
    
done:
    if (windowDesc.dataHandle)
        AEDisposeDesc(&windowDesc);
    if (propDesc.dataHandle)
        AEDisposeDesc(&propDesc);
    if (resultDesc.dataHandle)
        AEDisposeDesc(&resultDesc);
        
    return(err);
} // PropertyFromWndwAccessor
 
 
pascal OSErr    PropertyFromDocumentAccessor(DescType           wantClass,
                                                AEDesc          *container,
                                                DescType        containerClass,
                                                DescType        form, 
                                                AEDesc          *selectionData,
                                                AEDesc          *value,
                                                long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused(containerClass)
#endif
 
    long                itemCount,
                        index;
    AEDesc              resultDesc = {typeNull, NULL},
                        windowDesc = {typeNull, NULL},
                        propDesc = {typeNull, NULL};
    Size                actualSize;
    DescType            returnedType;
    WindowToken         theWindowToken;
    WindowPropToken     myWindowProp;
    OSErr               err;
    
    if (typeAEList == container->descriptorType)
    {
        err = AECreateList(NULL, 0 , false, value);     // Result will also be a list of items
        if (noErr != err) goto done;
        err = AECountItems(container, &itemCount);
        if (noErr != err) goto done;
    
        for (index = 1; index <= itemCount; index++)    // Do in forward order
        {
            err = AEGetNthDesc(container, index, typeWildCard, &returnedType, &windowDesc);
    
            if (noErr == err)                           // Recursively call this routine
                                                        // - could be another list.
                err = PropertyFromDocumentAccessor(wantClass, &windowDesc, windowDesc.descriptorType,
                                                        form, selectionData, &resultDesc, theRefCon);
            
            if (noErr == err)                           // Add item to the end of our list
                err = AEPutDesc(value, 0, &resultDesc);
            
            (void)AEDisposeDesc(&windowDesc);
            (void)AEDisposeDesc(&resultDesc);
        }
    }
    else
    {       // get the window token - it's the container
        
        err = AECoerceDesc(container, typeMyDocument, &windowDesc);
        GetRawDataFromDescriptor(&windowDesc, (Ptr)&theWindowToken,
                                        sizeof(theWindowToken), &actualSize);
                                                            
            // Check the window exists
        
        if (theWindowToken.tokenWindow == NULL)
            err = errAEIllegalIndex;
        else
        {       // get the property - it's in the selection data
            
            err = AECoerceDesc(selectionData, typeType, &propDesc);
            GetRawDataFromDescriptor(&propDesc, (Ptr)&returnedType,
                                            sizeof(returnedType), &actualSize);
            
                // Combine the two into single token
 
            myWindowProp.tokenWindowToken = theWindowToken;
            myWindowProp.tokenProperty    = returnedType;
            
            err = AECreateDesc(typeMyDocumentProp, (Ptr)&myWindowProp,
                                                sizeof(myWindowProp), value);
        }
    }
    
done:
    (void)AEDisposeDesc(&windowDesc);
    (void)AEDisposeDesc(&propDesc);
    (void)AEDisposeDesc(&resultDesc);
        
    return(err);
} // PropertyFromDocumentAccessor
 
 
pascal OSErr    PropertyFromNullAccessor(DescType           wantClass,
                                            AEDesc          *container,
                                            DescType        containerClass,
                                            DescType        form, 
                                            AEDesc          *selectionData,
                                            AEDesc          *value,
                                            long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused (container, containerClass)
#endif
 
    AEDesc      aDesc = {typeNull, NULL};
    OSErr       err;
 
    if ((wantClass != cProperty) || (form != formPropertyID))
        return(errAEWrongDataType);
 
    switch (*(DescType *)*selectionData->dataHandle)
    {
        case pSelection:    // The selection defaults to the front window
                            // selection.
            err = GetDescOfNthDocument( 1, &aDesc );    // Disposed of at end
            if (noErr != err) goto done;
            err = PropertyFromDocumentAccessor(wantClass, &aDesc, typeMyDocument, form, 
                                                    selectionData, value, theRefCon);
            break;
            
        default:            // Otherwise try an application property - it is fron NULL
            err = PropertyFromApplAccessor(wantClass, &aDesc, typeMyWndw, form, 
                                                    selectionData, value, theRefCon);
    }
    
done:
    (void)AEDisposeDesc( &aDesc );
    
    return err;
}
 
 
// Convert a list of Token descriptors to a list of Property Token descriptors.
// Only TextTokens and WindowTokens are supported in this version.
 
OSErr   TokenListToPropertyList(AEDesc* tokenList, DescType aProperty, AEDesc* result)
{
    AEDesc              aDesc = {typeNull, NULL},
                        resultDesc = {typeNull, NULL};
    DescType            returnedType;
    long                itemCount,
                        index;
    WindowPropToken     aWindowPropToken;
    TextPropToken       aTextPropToken;
    Size                actualSize;
    OSErr               err;
 
    err = AECreateList(NULL, 0 , false, result);        // Result will also be a list of items
    if (noErr != err) goto done;
    err = AECountItems(tokenList, &itemCount);      // Will return an error if not of type typeAEList
    if (noErr != err) goto done;
 
    for (index = 1; index <= itemCount; index++)    // Do in forward order
    {
        err = AEGetNthDesc(tokenList, index, typeWildCard, &returnedType, &aDesc);
 
        if (noErr == err)                           // Create appropriate property token
            switch (aDesc.descriptorType)
            {
                case typeMyWndw:
                    GetRawDataFromDescriptor(&aDesc, (Ptr)&aWindowPropToken,
                                                    sizeof(WindowToken), &actualSize);
                    aWindowPropToken.tokenProperty = aProperty;
                    err = AECreateDesc(typeMyWindowProp, (Ptr)&aWindowPropToken,
                                                    sizeof(aWindowPropToken), &resultDesc);
                    break;
                    
                case typeMyText:
                    GetRawDataFromDescriptor(&aDesc, (Ptr)&aTextPropToken,
                                                    sizeof(TextToken), &actualSize);
                    aTextPropToken.tokenProperty = aProperty;
                    err = AECreateDesc(typeMyTextProp, (Ptr)&aTextPropToken,
                                                    sizeof(aTextPropToken), &resultDesc);
                    break;
                    
                case typeAEList:                    // Recursive call if a list
                    err = TokenListToPropertyList(&aDesc, aProperty, &resultDesc);
                    break;
                    
                default:        // May already be a property token
                    err = errAEBadListItem;
            }
        
        if (noErr == err)       // Add item to the end of our list
            err = AEPutDesc(result, 0, &resultDesc);
        
        if (aDesc.dataHandle)
            AEDisposeDesc(&aDesc);
        if (resultDesc.dataHandle)
            AEDisposeDesc(&resultDesc);
            
        err = noErr;            // Try and do all the items we can in the list
    }
            
done:
    return(err);
}
 
 
// Given a container that is a list of Token descriptors, this routine
// returns a list of PropToken descriptors.
 
pascal OSErr    PropertyFromListAccessor(DescType           wantClass,
                                            AEDesc          *container,
                                            DescType        containerClass,
                                            DescType        form, 
                                            AEDesc          *selectionData,
                                            AEDesc          *value,
                                            long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused(containerClass, theRefCon)
#endif
 
    OSErr       err;
    
    if (wantClass != cProperty && form != formPropertyID)
        return(errAEBadKeyForm);
    
    err = TokenListToPropertyList(container, *(DescType *)*selectionData->dataHandle, value);
 
    return(err);
}
 
 
// Given a AppToken descriptor as a container convert this into 
// a WindowPropToken descriptor for the property.
 
pascal OSErr    PropertyFromApplAccessor(DescType           wantClass,
                                            const AEDesc    *container,
                                            DescType        containerClass,
                                            DescType        form, 
                                            const AEDesc    *selectionData,
                                            AEDesc          *value,
                                            long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused (theRefCon, containerClass)
#endif
 
    OSErr         myErr;
    OSErr         ignoreErr;
    AppToken      theApplToken;
    DescType      theProperty;
    AEDesc        applDesc;
    AEDesc        propDesc;
    Size          actualSize;
    AppPropToken myApplProp;
        
    value->dataHandle     = nil;
    applDesc.dataHandle   = nil;
    propDesc.dataHandle   = nil;
    
    if ((wantClass != cProperty) ||
          (form != formPropertyID))
    {
        return(errAEWrongDataType);
    }
    
    // get the application token - it's the container
    
    myErr = AECoerceDesc(container, typeMyAppl, &applDesc);
    GetRawDataFromDescriptor(&applDesc, (Ptr)&theApplToken,
                                sizeof(theApplToken), &actualSize);
            
    // get the property - it's in the selection data
    
    myErr = AECoerceDesc(selectionData, typeType, &propDesc);
    GetRawDataFromDescriptor(&propDesc, (Ptr)&theProperty,
                                sizeof(theProperty), &actualSize);
 
    //  Combine the two into single token
 
    myApplProp.tokenAppToken = theApplToken;
    myApplProp.tokenProperty = theProperty;
    
    myErr = AECreateDesc(typeMyApplProp, (Ptr)&myApplProp,
                                        sizeof(myApplProp), value);
        
    if (applDesc.dataHandle)
        ignoreErr = AEDisposeDesc(&applDesc);
        
    if (propDesc.dataHandle)
        ignoreErr = AEDisposeDesc(&propDesc);
        
    return(myErr);
} // PropertyFromApplAccessor
 
 
pascal OSErr    PropertyFromWinPropertyAccessor(DescType        wantClass,
                                                const AEDesc    *container,
                                                DescType        containerClass,
                                                DescType        form, 
                                                const AEDesc    *selectionData,
                                                AEDesc          *value,
                                                long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused(wantClass, containerClass, form, theRefCon)
#endif
 
    OSErr           myErr;
    OSErr           ignoreErr;
    WindowPropToken theWindowPropToken;
    AEDesc          newDesc, propDesc;
    Size            tokenSize;
    DPtr            theDocument;
    TextToken       theTextToken;
    DescType        theProperty;
    Size            actualSize;
    TextPropToken   myTextProp;
    
    // the container is a window property token. Get the token for this
    // and check that it is valid to get a property of this property
    
    myErr = AECoerceDesc(container, typeMyWindowProp, &newDesc);
    
    if (myErr)
     return(myErr);
 
    GetRawDataFromDescriptor(&newDesc, (Ptr)&theWindowPropToken,
                                sizeof(theWindowPropToken), &tokenSize);
                                                     
    // if the property is pSelection, we then want to convert this to a text token
    // and then return a text property token
    
    if (theWindowPropToken.tokenProperty == pSelection) 
    {
        theDocument = DPtrFromWindowPtr(theWindowPropToken.tokenWindowToken.tokenWindow);
        
        // build a text token to represent the selection
        theTextToken.tokenOffset = (**(theDocument->theText)).selStart + 1;
        theTextToken.tokenLength = ((**(theDocument->theText)).selEnd -
                                   (**(theDocument->theText)).selStart) -1;
        theTextToken.tokenWindow = theWindowPropToken.tokenWindowToken.tokenWindow;
        
    
        // now get the property- it's in the selection data
        myErr = AECoerceDesc(selectionData, typeType, &propDesc);
        GetRawDataFromDescriptor(&propDesc, (Ptr)&theProperty,
                                        sizeof(theProperty), &actualSize);
        
        // Combine the two into single token
        myTextProp.tokenTextToken = theTextToken;
        myTextProp.tokenProperty  = theProperty;
 
        myErr = AECreateDesc(typeMyTextProp,(Ptr)&myTextProp,
                                         sizeof(myTextProp),value);
                                         
                                         
      if (propDesc.dataHandle) ignoreErr = AEDisposeDesc(&propDesc);
        if (newDesc.dataHandle)  ignoreErr = AEDisposeDesc(&newDesc);
 
      return myErr;
                                                                                                 
    }
    
    if (newDesc.dataHandle) 
        ignoreErr = AEDisposeDesc(&newDesc);
    
    return errAEWrongDataType;
}
 
 
OSErr   MenuFormAbsolutePosition( const AEDesc *selectionData, AEDesc* result )
{
    AEDesc  itemDesc = {typeNull, NULL};
    short   aCount,
            index;
    OSErr   err;
 
    aCount = CountMenus();
    
    if (! aCount)
        return(errAEIllegalIndex);
    
    if ( typeAbsoluteOrdinal == selectionData->descriptorType )
    {
        err = noErr;
    
        switch (*(DescType *)*selectionData->dataHandle)
        {
            case kAEFirst:
                index = 1;
                break;
    
            case kAELast:
                index = aCount;
                break;
    
            case kAEMiddle:
                index = (aCount + 1) / 2;
                break;
    
            case kAEAny:
                index = (Random() % aCount) + 1;
                break;
    
            case kAEAll:
                err = AECreateList(NULL, 0 , false, result);
                if (noErr != err) goto done;
                
                for (index = 1; index <= aCount; index++)
                {
                    err = GetDescOfNthMenu(index, &itemDesc);
                    if (noErr != err) goto done;
                    
                    err = AEPutDesc(result, 0, &itemDesc);
                    if (noErr != err) goto done;
                    
                    if (itemDesc.dataHandle)
                        AEDisposeDesc(&itemDesc);
                }
                
                goto done;      // We have created our list descriptor
                break;          // so we can just tidy up and return.
                
            default:
                err = errAETypeError;
        }
    }
    else
        err = GetIntegerFromDescriptor( selectionData, &index );
 
    if (noErr != err) goto done;
    
    if (index < 0)      // Handle negative indexes
        index = aCount + index + 1;
        
    if ( index > aCount || index <= 0 )
        err = errAEIllegalIndex;
    else
        err = GetDescOfNthMenu( index, result );
 
done:   
    (void)AEDisposeDesc( &itemDesc );
 
    return err;
} // MenuFormAbsolutePosition
 
 
OSErr   MenuFormName( const AEDesc *selectionData, AEDesc* result )
{
    Str255      name;
    OSErr       err;
    
            // This tries to coerce it first
    err = GetPStringFromDescriptor( selectionData, name );
    if ( noErr != err ) goto done;
 
    err = GetDescOfNamedMenu( name, result );
 
done:
    return(err);
}
 
 
pascal OSErr    MenuFromNullAccessor( DescType          wantClass,
                                        const AEDesc    *container,
                                        DescType        containerClass,
                                        DescType        form, 
                                        const AEDesc    *selectionData,
                                        AEDesc          *value,
                                        long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused (container,theRefCon)
#endif
 
    OSErr       err;
    
        // Can only handle cWindow and cDocument
    if ( wantClass != cMenu )
        return errAEWrongDataType;
        
        // Can only handle typeNull and typeMyAppl
    if ( containerClass != typeNull && containerClass != typeMyAppl )
        return errAENoSuchObject;
    
    switch (form)
    {
        case formAbsolutePosition:
            err = MenuFormAbsolutePosition( selectionData, value );
            break;
            
        case formName:
            err = MenuFormName( selectionData, value );
            break;
            
        default:
            err = errAEBadTestKey;
    }
            
    return err;
}
 
 
pascal OSErr    PropertyFromMenuAccessor(DescType           wantClass,
                                            AEDesc          *container,
                                            DescType        containerClass,
                                            DescType        form, 
                                            AEDesc          *selectionData,
                                            AEDesc          *value,
                                            long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused(containerClass)
#endif
 
    long                itemCount,
                        index;
    AEDesc              resultDesc = {typeNull, NULL},
                        aDesc = {typeNull, NULL},
                        propDesc = {typeNull, NULL};
    Size                actualSize;
    DescType            returnedType;
    MenuToken           theToken;
    MenuPropToken       thePropertyToken;
    OSErr               err;
    
    if (typeAEList == container->descriptorType)
    {
        err = AECreateList( NULL, 0 , false, value );   // Result will also be a list of items
        if (noErr != err) goto done;
        err = AECountItems( container, &itemCount );
        if (noErr != err) goto done;
    
        for ( index = 1; index <= itemCount; index++ )  // Do in forward order
        {
            err = AEGetNthDesc( container, index, typeWildCard, &returnedType, &aDesc );
            if ( noErr != err ) goto done;
                                                        // Recursively call this routine
                                                        // - could be another list.
            err = PropertyFromMenuAccessor( wantClass, &aDesc, aDesc.descriptorType,
                                                    form, selectionData, &resultDesc, theRefCon );
            
            err = AEPutDesc( value, 0, &resultDesc );   // Add item to the end of our list
            if ( noErr != err ) goto done;
            
            (void)AEDisposeDesc( &aDesc );
            (void)AEDisposeDesc( &resultDesc );
        }
    }
    else
    {       // get the window token - it's the container
        
        err = AECoerceDesc( container, typeMyMenu, &aDesc );
        if ( noErr != err ) goto done;
        GetRawDataFromDescriptor( &aDesc, (Ptr)&theToken,
                                        sizeof(theToken), &actualSize );
                                                            
            // get the property - it's in the selection data
        
        err = AECoerceDesc( selectionData, typeType, &propDesc );
        if ( noErr != err ) goto done;
        GetRawDataFromDescriptor( &propDesc, (Ptr)&returnedType,
                                        sizeof(returnedType), &actualSize );
        
            // Combine the two into single token
 
        thePropertyToken.token = theToken;
        thePropertyToken.tokenProperty = returnedType;
        
        err = AECreateDesc( typeMyMenuProp, (Ptr)&thePropertyToken,
                                        sizeof( thePropertyToken ), value );
    }
    
done:
    (void)AEDisposeDesc( &aDesc );
    (void)AEDisposeDesc( &propDesc );
    (void)AEDisposeDesc( &resultDesc );
        
    return(err);
} // PropertyFromMenuAccessor
 
 
OSErr   MenuItemFormAbsolutePosition( MenuToken* containerToken, const AEDesc *selectionData, AEDesc* result )
{
    AEDesc  itemDesc = {typeNull, NULL};
    short   aCount,
            index;
    OSErr   err;
 
    aCount = CountMenuTokenItems( containerToken );
    
    if (! aCount)
        return(errAEIllegalIndex);
    
    if ( typeAbsoluteOrdinal == selectionData->descriptorType )
    {
        err = noErr;
    
        switch (*(DescType *)*selectionData->dataHandle)
        {
            case kAEFirst:
                index = 1;
                break;
    
            case kAELast:
                index = aCount;
                break;
    
            case kAEMiddle:
                index = (aCount + 1) / 2;
                break;
    
            case kAEAny:
                index = (Random() % aCount) + 1;
                break;
    
            case kAEAll:
                err = AECreateList(NULL, 0 , false, result);
                if (noErr != err) goto done;
                
                for (index = 1; index <= aCount; index++)
                {
                    err = GetDescOfNthMenuItem( containerToken, index, &itemDesc );
                    if (noErr != err) goto done;
                    
                    err = AEPutDesc(result, 0, &itemDesc);
                    if (noErr != err) goto done;
                    
                    if (itemDesc.dataHandle)
                        AEDisposeDesc(&itemDesc);
                }
                
                goto done;      // We have created our list descriptor
                break;          // so we can just tidy up and return.
                
            default:
                err = errAETypeError;
        }
    }
    else
        err = GetIntegerFromDescriptor( selectionData, &index );
 
    if (noErr != err) goto done;
    
    if (index < 0)      // Handle negative indexes
        index = aCount + index + 1;
        
    if ( index > aCount || index <= 0 )
        err = errAEIllegalIndex;
    else
        err = GetDescOfNthMenuItem( containerToken, index, result );
 
done:   
    (void)AEDisposeDesc( &itemDesc );
 
    return err;
} // MenuFormAbsolutePosition
 
 
OSErr   MenuItemFormName( MenuToken* containerToken, const AEDesc* selectionData, AEDesc* result )
{
    Str255      name;
    OSErr       err;
    
            // This tries to coerce it first
    err = GetPStringFromDescriptor( selectionData, name );
    if ( noErr != err ) goto done;
 
    err = GetDescOfNamedMenuItem( containerToken, name, result );
 
done:
    return(err);
}
 
 
pascal OSErr    MenuItemFromMenuAccessor( DescType          wantClass,
                                            const AEDesc    *container,
                                            DescType        containerClass,
                                            DescType        form, 
                                            const AEDesc    *selectionData,
                                            AEDesc          *value,
                                            long            theRefCon)
{
#ifdef __MWERKS__
    #pragma unused ( containerClass, theRefCon )
#endif
 
    MenuToken   containerToken;
    Size        actualSize;
    OSErr       err;
    
        // Can only handle cMenu
    if ( wantClass != cMenuItem )
        return errAEWrongDataType;
        
        // Can only handle typeMyMenu
    if ( container->descriptorType != typeMyMenu )
        return errAENoSuchObject;
 
        // Get the containing MenuToken
    GetRawDataFromDescriptor( container, (Ptr)&containerToken,
                                        sizeof( containerToken ), &actualSize );
 
    switch (form)
    {
        case formAbsolutePosition:
            err = MenuItemFormAbsolutePosition( &containerToken, selectionData, value );
            break;
            
        case formName:
            err = MenuItemFormName( &containerToken, selectionData, value );
            break;
            
        default:
            err = errAEBadTestKey;
    }
            
    return err;
}
 
 
pascal OSErr    PropertyFromMenuItemAccessor(DescType       wantClass,
                                                AEDesc      *container,
                                                DescType    containerClass,
                                                DescType    form, 
                                                AEDesc      *selectionData,
                                                AEDesc      *value,
                                                long        theRefCon)
{
#ifdef __MWERKS__
    #pragma unused(containerClass)
#endif
 
    long                itemCount,
                        index;
    AEDesc              resultDesc = {typeNull, NULL},
                        aDesc = {typeNull, NULL},
                        propDesc = {typeNull, NULL};
    Size                actualSize;
    DescType            returnedType;
    MenuItemToken       theToken;
    MenuItemPropToken   thePropertyToken;
    OSErr               err;
    
    if (typeAEList == container->descriptorType)
    {
        err = AECreateList( NULL, 0 , false, value );   // Result will also be a list of items
        if ( noErr != err ) goto done;
        err = AECountItems( container, &itemCount );
        if ( noErr != err ) goto done;
    
        for ( index = 1; index <= itemCount; index++ )  // Do in forward order
        {
            err = AEGetNthDesc( container, index, typeWildCard, &returnedType, &aDesc );
            if ( noErr != err ) goto done;
                                                        // Recursively call this routine
                                                        // - could be another list.
            err = PropertyFromMenuItemAccessor( wantClass, &aDesc, aDesc.descriptorType,
                                                form, selectionData, &resultDesc, theRefCon );
            if ( noErr != err ) goto done;
                                                    
            err = AEPutDesc( value, 0, &resultDesc );   // Add item to the end of our list
            if ( noErr != err ) goto done;
            
            (void)AEDisposeDesc( &aDesc );
            (void)AEDisposeDesc( &resultDesc );
        }
    }
    else
    {
        err = AECoerceDesc(container, typeMyMenuItem, &aDesc);
        if ( noErr != err ) goto done;
        GetRawDataFromDescriptor(&aDesc, (Ptr)&theToken,
                                        sizeof(theToken), &actualSize);
                                                            
            // get the property - it's in the selection data
        
        err = AECoerceDesc( selectionData, typeType, &propDesc );
        if ( noErr != err ) goto done;
        GetRawDataFromDescriptor( &propDesc, (Ptr)&returnedType,
                                        sizeof(returnedType), &actualSize );
        
            // Combine the two into single token
 
        thePropertyToken.token = theToken;
        thePropertyToken.tokenProperty = returnedType;
        
        err = AECreateDesc( typeMyMenuItemProp, (Ptr)&thePropertyToken,
                                        sizeof( thePropertyToken ), value );
    }
    
done:
    (void)AEDisposeDesc(&aDesc);
    (void)AEDisposeDesc(&propDesc);
    (void)AEDisposeDesc(&resultDesc);
        
    return(err);
} // PropertyFromMenuItemAccessor