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