Source/SVAEAccessors.c

/*
    File:       SVAEAccessors.c
 
    Contains:   
 
    Written by: Original version by Jon Lansdell and Nigel Humphreys.
                3.1 updates by Greg Sutton.
 
    Copyright:  Copyright © 1995-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                7/20/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
 
#include "SVAEAccessors.h"
 
#include <Menus.h>
#include <PLStringFuncs.h>
#include <Scrap.h>
#include <TextEdit.h>
#include <AEObjects.h>
#include <AEPackObject.h>
#include <AERegistry.h>
#include "SVEditGlobals.h"
#include "SVEditUtils.h"
#include "SVEditAEUtils.h"
#include "SVEditWindow.h"
#include "SVEditFile.h"
#include "SVAppleEvents.h"
 
#include "SVToken.h"
#include "SVAETextUtils.h"
#include "SVAEWindowUtils.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(WindowFromNullAccessor), 0, false);
    err = AEInstallObjectAccessor(cProperty,    typeNull,           NewOSLAccessorProc(PropertyFromNullAccessor), 0, false);
 
    err = AEInstallObjectAccessor(cWindow,      typeMyAppl,         NewOSLAccessorProc(WindowFromNullAccessor), 0, false);
    err = AEInstallObjectAccessor(cDocument,    typeMyAppl,         NewOSLAccessorProc(WindowFromNullAccessor), 0, false);
    err = AEInstallObjectAccessor(cProperty,    typeMyAppl,         NewOSLAccessorProc(PropertyFromApplAccessor), 0, false);
 
            // Handle text from a window
            // e.g. some character of last window
    err = AEInstallObjectAccessor(cInsertionPoint,typeMyWndw,       NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cChar,        typeMyWndw,         NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cText,        typeMyWndw,         NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cWord,        typeMyWndw,         NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cParagraph,   typeMyWndw,         NewOSLAccessorProc(TextElemFromTextAccessor), 0, false);
    err = AEInstallObjectAccessor(cProperty,    typeMyWndw,         NewOSLAccessorProc(PropertyFromWndwAccessor), 0, false);
 
            // Handle text items within text items
            // e.g. last word of first paragraph of front window
    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);
 
            // Handle text items from lists (of hopefully text items) also
            // e.g. every word of every paragraph of front window
            // or even - every word of every character of every paragraph of front window
    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 window 1
            // Relies on ability to coerce from a window property to text
            // for certain properties.
    err = AEInstallObjectAccessor(cProperty,        typeMyWindowProp,   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 '7Edit'
//          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)
    {
        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 '7Edit'
//          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 '7Edit'
//          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)
{
#pragma unused (container,theRefCon)
 
    OSErr       err;
    
        // Can only handle cWindow and cDocument
    if (wantClass != cWindow && wantClass != cDocument)
        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
 
 
pascal OSErr   ApplicationFromNullAccessor(DescType     wantClass,
                                            const AEDesc    *container,
                                            DescType        containerClass,
                                            DescType        form, 
                                            const AEDesc    *selectionData,
                                            AEDesc          *value,
                                            long            theRefCon)
{
#pragma unused(container,selectionData,theRefCon)
 
    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 '7Edit'
//          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)
    {
        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 '7Edit'
//          paragraphs 2 thru 3 of document 1
//      end tell
 
OSErr   TextFormRange(TextToken* containerToken, AEDesc* selectionData,
                                                    DescType wantClass, AEDesc* result)
{
#pragma unused(wantClass)
 
    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 '7Edit'
//          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 '7Edit'
//          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)
{
#pragma unused(containerClass)
 
    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 window 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)
{
#pragma unused (theRefCon, containerClass)
 
    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:       
    if (textDesc.dataHandle)
        AEDisposeDesc(&textDesc);
    if (propertyDesc.dataHandle)
        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)
{
#pragma unused(containerClass)
 
    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    PropertyFromNullAccessor(DescType           wantClass,
                                            AEDesc          *container,
                                            DescType        containerClass,
                                            DescType        form, 
                                            AEDesc          *selectionData,
                                            AEDesc          *value,
                                            long            theRefCon)
{
#pragma unused (container, containerClass)
 
    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 = GetDescOfNthWindow(1, &aDesc);    // Disposed of at end
            if (noErr != err) goto done;
            err = PropertyFromWndwAccessor(wantClass, &aDesc, typeMyWndw, 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:
    if (aDesc.dataHandle)
        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)
{
#pragma unused(containerClass, theRefCon)
 
    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)
{
#pragma unused (theRefCon, containerClass)
 
    OSErr         myErr;
    OSErr         ignoreErr;
    AppToken      theApplToken;
    DescType      theProperty;
    AEDesc        applDesc;
    AEDesc        propDesc;
    Size          actualSize;
    ApplPropToken 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.tokenApplToken    = theApplToken;
    myApplProp.tokenApplProperty = 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)
{
#pragma unused(wantClass, containerClass, form, theRefCon)
 
    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;
}