Sources/MSAECreate.c

// MSAECreate.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.
 
/*
    14-Nov-95 : GS : Removed unused local varibale in DoNewElement().
*/
 
#include "MSAECreate.h"
 
#include "MSGlobals.h"
#include "MSWindow.h"
#include "MSAEUtils.h"
#include "MSAETextUtils.h"
#include "MSAppleEvents.h"
 
#include "MSAccessors.h"
#include "MSAERecording.h"
#include "MSAESelect.h"
#include "MSAEWindowUtils.h"
#include "MSAESetData.h"
 
 
#pragma segment AppleEvent
 
 
// ------------------------------------------------------------------------
//  Name:       DoNewElement
//  Purpose:    Handles the NewElement AppleEvent. Creates windows and
//              text.
// ------------------------------------------------------------------------
 
pascal OSErr    DoNewElement(const AppleEvent   *theAppleEvent,
                                    AppleEvent  *reply, 
                                    long        handlerRefCon)
{
#ifdef __MWERKS__
    #pragma unused (handlerRefCon)
#endif
 
    DescType    returnedType,
                newElemClass;
    Size        actualSize;
    AEDesc      dataDesc = {typeNull, NULL},
                insertHereDesc = {typeNull, NULL},
                propertyDesc = {typeNull, NULL},
                resultDesc = {typeNull, NULL};
    OSErr       ignoreErr,
                err;
        
    err = AEGetParamPtr(theAppleEvent, keyAEObjectClass, typeType,
                            &returnedType, (Ptr)&newElemClass, sizeof(newElemClass), &actualSize);
    if (noErr != err) goto done;        // We have to know what object to create
    
        // Get optional parameters
    ignoreErr = AEGetParamDesc(theAppleEvent, keyAEData, typeWildCard, &dataDesc);
    ignoreErr = AEGetParamDesc(theAppleEvent, keyAEInsertHere, typeWildCard, &insertHereDesc);
    ignoreErr = AEGetParamDesc(theAppleEvent, keyAEPropData, typeWildCard, &propertyDesc);
          
        // check for missing required parameters
    err = GotRequiredParams(theAppleEvent);
    if (noErr != err) goto done;
 
    switch (newElemClass)
    {
        case cWindow:
        case cDocument:
            err = CreateDocument(&dataDesc, &insertHereDesc, &propertyDesc, &resultDesc);
            break;
            
        case cChar:
        case cText:
        case cWord:
        case cParagraph:
            err = CreateText(newElemClass, &dataDesc, &insertHereDesc, &propertyDesc, &resultDesc);
            break;
            
        default:
            err = errAEWrongDataType;
    }
    
    err = AddResultToReply(&resultDesc, reply, err);
 
done:           
    if (dataDesc.dataHandle) 
        AEDisposeDesc(&dataDesc);
    if (insertHereDesc.dataHandle) 
        AEDisposeDesc(&insertHereDesc);
    if (propertyDesc.dataHandle) 
        AEDisposeDesc(&propertyDesc);
    if (resultDesc.dataHandle) 
        AEDisposeDesc(&resultDesc);
        
    return(err);
} // DoNewElement
 
 
OSErr   CreateDocument(AEDesc* dataDesc, AEDesc* insertHereDesc,
                                                AEDesc* propertyDesc, AEDesc* result)
{
    AEDesc      insertDesc = {typeNull, NULL};
    DescType    insertType;
    DPtr        docPtr;
    WindowPtr   behindWindow;
    OSErr       err;
 
    err = GetInsertDescFromInsertHere(insertHereDesc, &insertDesc, &insertType);
    if (noErr != err) goto done;
    
    err = GetBehindWindow(&insertDesc, insertType, &behindWindow);
    if (noErr != err) goto done;
    
    docPtr = NewDocument(false, behindWindow);
 
    if (! docPtr)
    {
        err = errAENoSuchObject;
        goto done;
    }
 
    if (propertyDesc->dataHandle)
        err = SetDocumentPropertyRecord(docPtr->theWindow, propertyDesc);
    if (noErr != err) goto done;
 
    if ( dataDesc->dataHandle && dataDesc->descriptorType != typeAEList )
    {
        err = SetDocumentData(docPtr->theWindow, dataDesc);
        docPtr->dirty = true;
    }
    else
        docPtr->dirty = false;
    if (noErr != err) goto done;
 
    ShowMSWindow(docPtr->theWindow);
    err = MakeDocumentObj(docPtr->theWindow, result);
 
done:
    (void)AEDisposeDesc(&insertDesc);
 
    return(err);
}
 
OSErr   GetBehindWindow(AEDesc* insertDesc, DescType insertType, WindowPtr* behindWindow)
{
    AEDesc          windowDesc = {typeNull, NULL};
    WindowToken     aWindowToken;
    Size            actualSize;
    short           index;
    OSErr           err;
 
    if (typeNull == insertDesc->descriptorType)
    {
        *behindWindow = (WindowPtr) -1L;
        return(noErr);
    }
    
    err = AECoerceDesc(insertDesc, typeMyWndw, &windowDesc);
    if (noErr != err) goto done;
 
    GetRawDataFromDescriptor(&windowDesc, (Ptr)&aWindowToken,
                                    sizeof(aWindowToken), &actualSize);
 
    switch (insertType)
    {
        case kAEBeginning:
            *behindWindow = (WindowPtr) -1L;
            break;
    
        case kAEEnd:
            *behindWindow = NULL;
            break;
        
        case kAEBefore:
            index = GetWindowIndex(aWindowToken.tokenWindow);
            if (index > 1)
                *behindWindow = GetNthWindow(index - 1);
            else
                *behindWindow = (WindowPtr) -1L;    // Stick at front because no
            break;                                  // windows before that.
        
        case kAEAfter:
            *behindWindow = aWindowToken.tokenWindow;
            break;
        
        case kAEReplace:
        default:
            err = errAEEventFailed;     // We won't allow a new window to replace an existing one
    }
    
done:
    if (windowDesc.dataHandle)
        (void)AEDisposeDesc(&windowDesc);
 
    return(err);
}
 
OSErr   SetDocumentPropertyRecord(WindowPtr theWindow, AEDesc* propertyRecord)
{
    WindowPropToken     aWindowPropToken;
    AEDesc              dataDesc = {typeNull, NULL},
                        propertyDesc = {typeNull, NULL};
    AEKeyword           theAEKeyword;
    long                index;
    OSErr               err;
    
    aWindowPropToken.tokenWindowToken.tokenWindow = theWindow;
                                    
    err = AECountItems(propertyRecord, &index);
    if (noErr != err) goto done;
    
            // Step through each property - creating a window property token AEDesc
            // and letting SetDocumentProperty() do the work.
    for (; index > 0; index--)
    {
        err = AEGetNthDesc(propertyRecord, index, typeWildCard, &theAEKeyword, &dataDesc);
        if (noErr != err) goto done;
 
        aWindowPropToken.tokenProperty = theAEKeyword;
        err = AECreateDesc(typeMyDocumentProp, (Ptr)&aWindowPropToken, 
                                        sizeof(aWindowPropToken), &propertyDesc);
        if (noErr != err) goto done;
        
        err = SetDocumentProperty(&propertyDesc, &dataDesc);
        if (noErr != err) goto done;
        
        (void)AEDisposeDesc(&dataDesc);
        (void)AEDisposeDesc(&propertyDesc);
    }
    
done:
    (void)AEDisposeDesc(&dataDesc);
    (void)AEDisposeDesc(&propertyDesc);
    
    return(err);
}
 
// We'll just assume it's text and put it through as the selection.
 
OSErr   SetDocumentData( WindowPtr theWindow, AEDesc* dataDesc)
{
    WindowPropToken     aPropToken;
    OSErr               err;
    
    aPropToken.tokenWindowToken.tokenWindow = theWindow;
    aPropToken.tokenProperty = pSelection;
    
    err = SetDocumentTokenProperty( &aPropToken, dataDesc );
 
    return err;
}
 
 
OSErr   CreateText(DescType textType, AEDesc* dataDesc, AEDesc* insertHereDesc,
                                                AEDesc* propertyDesc, AEDesc* result)
{
    AEDesc      insertDesc = {typeNull, NULL};
    DescType    insertType;
    TextToken   anInsertToken;
    short       ignore;
    OSErr       err = noErr;
 
    err = GetInsertDescFromInsertHere(insertHereDesc, &insertDesc, &insertType);
    if (noErr != err) goto done;
    
    if (typeNull == insertType)     // Default to setting the selection in the front window
        err = GetWindowSelection(FrontWindow(), &anInsertToken, &ignore);
    else                            // Otherwise get a selection from the insertDesc
        err = GetInsertToken(&insertDesc, insertType, &anInsertToken);
        
    if (noErr != err) goto done;
    
    err = CreateAtTextToken(textType, dataDesc, &anInsertToken, propertyDesc, result);
 
done:
    if (insertDesc.dataHandle)
        AEDisposeDesc(&insertDesc);
 
    return(err);
}
 
// Get a TextToken for the location of where to insert the text. If the insertType is
// a relative position then work this out. Otherwise just use the insertDesc.
 
OSErr   GetInsertToken(AEDesc* insertDesc, DescType insertType, TextToken* resultToken)
{
    AEDesc      textDesc = {typeNull, NULL};
    TextToken   aTextToken;
    Size        actualSize;
    OSErr       err;
 
    err = AECoerceDesc(insertDesc, typeMyText, &textDesc);
    if (noErr != err) goto done;
 
    GetRawDataFromDescriptor(&textDesc, (Ptr)&aTextToken,
                                    sizeof(aTextToken), &actualSize);
    resultToken->tokenWindow = aTextToken.tokenWindow;
 
    switch (insertType)
    {
        case kAEBeginning:
        case kAEBefore:
            resultToken->tokenOffset = aTextToken.tokenOffset;
            resultToken->tokenLength = 0;
            break;
    
        case kAEEnd:
        case kAEAfter:
            resultToken->tokenOffset = aTextToken.tokenOffset + aTextToken.tokenLength;
            resultToken->tokenLength = 0;
            break;
        
        case kAEReplace:
        default:            // default is probably some text token to replace
                            // e.g make new word at middle word of document 1 with data "Iris"
                            // It has been coerced to text so it's okay
            resultToken->tokenOffset = aTextToken.tokenOffset;
            resultToken->tokenLength = aTextToken.tokenLength;
            break;
    }
 
done:
    if (textDesc.dataHandle)
        (void)AEDisposeDesc(&textDesc);
    
    return(err);
}
 
// Create text using the dataDesc (which could be text, styled text, etcÉ) at the
// the location held in the TextToken.
// Properties in the propertyDesc will be applied to the new text.
// Returns an object specifier to the text.
 
OSErr      CreateAtTextToken(DescType textType, const AEDesc* dataDesc, TextToken* theToken,
                                                    AEDesc* propertyDesc, AEDesc* result)
{
#ifdef __MWERKS__
    #pragma unused(textType)
#endif
 
    DPtr        docPtr;
    TextToken   aSelectionToken;
    short       oldLength;
    OSErr       err;
 
    docPtr = DPtrFromWindowPtr(theToken->tokenWindow);
    
    if (! docPtr)
        return(errAENoSuchObject);
 
            // Copy the current selection - so we can restore it after
    err = GetWindowSelection(theToken->tokenWindow, &aSelectionToken, &oldLength);
    if (noErr != err) goto done;
 
            // Set the selection we want to insert the new text into
    err = SelectTextToken(theToken);
    if (noErr != err) goto done;
 
    err = PutStyledTextFromDescIntoTEHandle(dataDesc, docPtr->theText);
    if (noErr != err) goto done;
    
            // Update the selection and get the length of the insertion
    err = UpdateSelectionToken(theToken, &aSelectionToken, oldLength,
                                                        &theToken->tokenLength);
    if (noErr != err) goto done;
    
    // Need to check on token type in here and make what user wanted
    // e.g. make sure a word is a word.
    // Would have to remember to balance token for any chages to TEHandle.
 
    if (propertyDesc->dataHandle)
    {                                   // I doubt we'll handle setting the text property
                                        // or anything that changes the token lengthÉ oh well
        err = SetTextPropertyRecord(theToken, propertyDesc);
        if (noErr != err) goto done;
    }
            // Make the returned object
    err = MakeTextObjFromToken(theToken, result);
    
done:
    return(err);
}
 
 
// Take a TextToken and apply the properties in the propertyRecord descriptor
// to the text.
// e.g. make new word at beginning of document 1 with data "Bert" Â
//                              with properties {size:32, font:"Courier"}
// The with properties part is the property record.
 
OSErr   SetTextPropertyRecord(TextToken* aTextToken, AEDesc* propertyRecord)
{
    TextPropToken       aTextPropToken;
    AEDesc              dataDesc = {typeNull, NULL},
                        propertyDesc = {typeNull, NULL};
    AEKeyword           theAEKeyword;
    long                index;
    OSErr               err;
    
    aTextPropToken.tokenTextToken = *aTextToken;
                                    
    err = AECountItems(propertyRecord, &index);
    if (noErr != err) goto done;
    
            // Step through each property - creating a window property token AEDesc
            // and letting SetWindowProperty() do the work.
    for (; index > 0; index--)
    {
        err = AEGetNthDesc(propertyRecord, index, typeWildCard, &theAEKeyword, &dataDesc);
        if (noErr != err) goto done;
 
        aTextPropToken.tokenProperty = theAEKeyword;
        err = AECreateDesc(typeMyTextProp, (Ptr)&aTextPropToken, 
                                        sizeof(aTextPropToken), &propertyDesc);
        if (noErr != err) goto done;
        
        err = SetTextProperty(&propertyDesc, &dataDesc);
        if (noErr != err) goto done;
        
        if (dataDesc.dataHandle)
            AEDisposeDesc(&dataDesc);
        if (propertyDesc.dataHandle)
            AEDisposeDesc(&propertyDesc);
    }
    
done:
    if (dataDesc.dataHandle)
        AEDisposeDesc(&dataDesc);
    if (propertyDesc.dataHandle)
        AEDisposeDesc(&propertyDesc);
    
    return(err);
}