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.
TumblerSource/Tumbler_AEVT.c
// Tumbler_AEVT.c |
// |
// Apple Event Routines for the Tumbler App. Contains AppleEvent Handlers and senders for |
// the Tumbler app. |
// |
// Author: Nick Thompson, 11/24/94 |
// |
// Modification History: |
// |
// 11/26/94 nick clean up, remove old TE references from dragtext, remove |
// all other dead code |
// 11/24/94 nick Initial Cut - factored code from Tumbler event, added |
// Drag related events for debugging (you can't debug the |
// handler, but if the handler sends an event you can debug |
// the AE handler). |
// |
// Copyright © 1992-95 Apple Computer, Inc., All Rights Reserved |
// |
// To Do: |
// a number of items are "duplicated" in the event we send to ourselves |
// we send the window and the document (the doc reference is stored in the |
// window refcon, window is stored in the doc) |
// |
#include <AppleEvents.h> |
#include <Drag.h> |
#include <Gestalt.h> |
#include <Memory.h> |
#include <SegLoad.h> |
#include "Tumbler_prototypes.h" |
#include "Tumbler_resources.h" |
#include "Tumbler_offscreen.h" |
#include "Tumbler_globals.h" |
#include "Tumbler_AEVT.h" |
#include "Tumbler_Traps.h" |
#include "Tumbler_Document.h" |
#include "Tumbler_file.h" |
#include "Tumbler_PICTImport.h" |
#include "Tumbler_camera.h" |
#include "Tumbler_file.h" |
#ifdef PODIUM_APP |
#include "Tumbler_Podium.h" |
#endif |
#include "QD3D.h" |
#include "QD3DDrawContext.h" |
#include "QD3DGroup.h" |
#include "QD3DIO.h" |
#include "QD3DPick.h" |
#include "QD3DShader.h" |
#include "QD3DStorage.h" |
#include "QD3DTransform.h" |
#include "QD3DView.h" |
#define kGestaltTrap 0xA0AD |
#include "Tumbler_AEVT.h" |
// local variables - to this file |
static AEAddressDesc pSelfAddress; // A self-addressed address descriptor record |
static ProcessSerialNumber pSelfPSN; // This application's psn |
static AEDesc pnilDesc; // A nil descriptor record |
// this defines a suite of events for dispatching drags |
static const AEEventClass kDraggingClass = 'drag' ; |
static const AEEventID kDragDrawID = 'draw' ; |
static const AEEventID kDragSendID = 'send' ; |
static const AEEventID kDragReceiveID = 'recv' ; |
static const AEEventID kDragTrackID = 'trak' ; |
static const AEEventID kDragDragID = 'drag' ; |
// |
static const AEKeyword keyPrivateData = 'priv' ; |
// AppleEvent Error Handling.... |
//---------------------------------------------------------------------------------- |
// If an error has occurred, I check the user interaction level. If I can |
// interact with the user, I put up a dialog and exit the application; otherwise, |
// I just exit the application. |
// |
// ***NOTE: Real applications would not handle errors in this fashion! If they |
// cannot interact with the user, they should abort the Apple event handler and |
// return the error in the reply parameter. |
void FailIfErr (OSErr error) |
{ |
OSErr interactErr = noErr ; |
if (error != noErr) |
{ |
if ((interactErr = AEInteractWithUser(kNoTimeOut, nil, nil)) == noErr) { |
// Can we interact? |
AlertUser(error); // Yes, so put up the dialog. |
ExitToShell(); |
} |
} |
} |
//---------------------------------------------------------------------------------- |
// Display an alert for the user to indicate that an error has occurred. |
// This approach would not really be good for a real app - hard coding |
// strings in this manner would be really hard to localise. But this approach |
// is good for programming because it gives us the error code. |
void AlertUser (short error) |
{ |
short itemHit; |
Str255 defaultMessage = "\pAn error was encountered" ; |
Str255 message ; |
Str255 errNo ; |
if( error < 0 ) { |
BlockMove( &defaultMessage[1], &message[1], defaultMessage[0] ); |
message[0] = defaultMessage[0] ; |
} |
else { |
GetIndString(message, rErrorStringIndex, error); |
} |
NumToString( error, errNo ) ; |
ParamText( message, |
(ConstStr255Param)"\p (", |
errNo, |
(ConstStr255Param)"\p)."); // [pwpc] |
itemHit = Alert(rUserAlert, nil); |
} |
//---------------------------------------------------------------------------------- |
void FatalError(short error) |
{ |
Str255 message ; |
GetIndString(message, rFatalErrorStringIndex, error); |
ParamText(message, (ConstStr255Param)"\p", (ConstStr255Param)"\p", (ConstStr255Param)"\p"); // [pwpc] |
(void)StopAlert( rFatalAlert, nil ) ; |
ExitToShell() ; |
} |
//----------------------------------------------------------------------- |
// call this to init the AE stuff in this file |
void InitAEStuff( void ) |
{ |
// Set up the self-addressed descriptor record. |
pSelfPSN.highLongOfPSN = 0; |
pSelfPSN.lowLongOfPSN = kCurrentProcess; // Use this instead of GetCurrentProcess |
FailIfErr(AECreateDesc(typeProcessSerialNumber,(Ptr)&pSelfPSN,sizeof(ProcessSerialNumber),&pSelfAddress)); |
pnilDesc.descriptorType = typeNull; // Initialize the global nil descriptor record. |
pnilDesc.dataHandle = nil; |
RegisterMyEvents() ; // and finally register appleEvent handlers |
} |
//----------------------------------------------------------------------- |
// returns true if the platform supports appleevents - we won't run |
// if it doesn't |
Boolean SupportsAEVT(void) |
{ |
OSErr err; |
long response; |
if (!myTrapAvailable(kGestaltTrap)) |
return false; |
err = Gestalt(gestaltAppleEventsAttr,&response); |
if (err!=noErr) |
return false; |
return (response && (response << gestaltAppleEventsPresent)); |
} |
//----------------------------------------------------------------------- |
// called to process high level appleevents |
void DoHighLevelEvent(EventRecord *ev) |
{ |
OSErr err; |
err = AEProcessAppleEvent(ev); |
} |
//----------------------------------------------------------------------- |
// called to register our appleevent handlers |
void RegisterMyEvents(void) |
{ |
OSErr err; |
if (!SupportsAEVT()) |
return; |
err = AEInstallEventHandler(kCoreEventClass,kAEOpenApplication,NewAEEventHandlerProc(MyAEHandleOAPP),0L,false); |
if (err!=noErr) |
return; |
err = AEInstallEventHandler(kCoreEventClass,kAEOpenDocuments,NewAEEventHandlerProc(MyAEHandleODOC),0L,false); |
if (err!=noErr) |
return; |
err = AEInstallEventHandler(kCoreEventClass,kAEPrintDocuments,NewAEEventHandlerProc(MyAEHandlePDOC),0L,false); |
if (err!=noErr) |
return; |
err = AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,NewAEEventHandlerProc(MyAEHandleQUIT),0L,false); |
if (err!=noErr) |
return; |
// drag stuff handlers |
err = AEInstallEventHandler(kDraggingClass,kDragReceiveID,NewAEEventHandlerProc(MyAEHandleDragRecv),0L,false); |
if (err!=noErr) |
return; |
} |
//----------------------------------------------------------------------- |
// open application event handler for the core event suite, |
// by default we just want a blank new document |
pascal OSErr MyAEHandleOAPP(AppleEvent *theAppleEvent,AppleEvent *reply,long refCon) |
{ |
OSErr err = noErr ; |
#ifndef PODIUM_APP |
DoNewDocument(); |
#endif |
return err; |
} |
//----------------------------------------------------------------------- |
// handler for the open document appleevent handler |
pascal OSErr MyAEHandleODOC(AppleEvent *theAppleEvent,AppleEvent *reply,long refCon) |
{ |
FSSpec myFSS; |
AEDescList docList; |
OSErr err, |
ignoreErr; |
long index, |
itemsInList; |
Size actualSize; |
AEKeyword keywd; |
DescType returnedType; |
err = AEGetParamDesc(theAppleEvent,keyDirectObject,typeAEList,&docList); |
if (err == noErr) { |
// see how many descriptor items are in the list |
// this is the number of documents we want to open |
err = AECountItems(&docList,&itemsInList); |
// now get each descriptor record from the list |
// coerce the returned data to an FSSpec record, and |
// open the asoociated file |
for (index=1; index <= itemsInList && err == noErr; index++) { |
err = AEGetNthPtr( &docList, |
index, |
typeFSS, |
&keywd, |
&returnedType, |
(Ptr)&myFSS, |
sizeof(myFSS), |
&actualSize); |
if (err == noErr) { |
FInfo fndrInfo ; |
// we now have a valid FSSpec to reference the file, we need to know |
// what type the file is to determine which file open function to call |
// we can determine this from the finder info for the file |
err = FSpGetFInfo( &myFSS, &fndrInfo ); |
// if we got that ok, then we switch on the file |
// type (we don't care about the creator type) |
if (err == noErr) { |
switch( fndrInfo.fdType ) { |
case 'PICT': |
break ; |
case 'TEXT': |
case '3DMF': |
err = DoOpenFile(&myFSS); |
break ; |
default: |
break ; |
} |
} |
} |
} |
ignoreErr = AEDisposeDesc(&docList); |
} |
return err ; |
} |
//----------------------------------------------------------------------- |
// handler for the print document event handler |
pascal OSErr MyAEHandlePDOC(AppleEvent *theAppleEvent,AppleEvent *reply,long refCon) |
{ |
FSSpec myFSS; |
AEDescList docList; |
OSErr err; |
long index, |
itemsInList; |
Size actualSize; |
AEKeyword keywd; |
DescType returnedType; |
err = AEGetParamDesc(theAppleEvent,keyDirectObject,typeAEList,&docList); |
if (err == noErr) { |
// see how many descriptor items are in the list |
// this is the number of documents we want to open |
err = AECountItems(&docList,&itemsInList); |
// now get each descriptor record from the list |
// coerce the returned data to an FSSpec record, and |
// open the asoociated file |
for (index=1; index <= itemsInList && err == noErr; index++) { |
err = AEGetNthPtr( &docList, |
index, |
typeFSS, |
&keywd, |
&returnedType, |
(Ptr)&myFSS, |
sizeof(myFSS), |
&actualSize); |
if (err == noErr) { |
// err = HandlePrintDoc( &myFSS ); |
err = errAEEventNotHandled ; // we don't do this yet... |
} |
} |
err = AEDisposeDesc(&docList); |
} |
return err ; |
} |
//----------------------------------------------------------------------- |
// quit appleevent handler |
pascal OSErr MyAEHandleQUIT(AppleEvent *theAppleEvent,AppleEvent *reply,long refCon) |
{ |
OSErr err = noErr ; // used as return value |
// close all windows and signal to Quit |
gQuitting = true; |
// attempt to close all documents |
if( CloseAllDocuments() == false ) |
return userCanceledErr ; // couldn't close them, so indicate the user cancelled |
// if we closed everything up successfully, we can return noErr, otherwise |
// indicate to sender of the 'quit' aevt that we canceled |
if (gQuitting) { |
gQuit = true; // user didn't cancel |
err = AEDisposeDesc(&pSelfAddress); // Dispose of my self-addressed descriptor. |
} |
else { |
err = userCanceledErr ; |
} |
return err ; |
} |
//----------------------------------------------------------------------- |
// drag receive appleevent handler |
pascal OSErr MyAEHandleDragRecv(AppleEvent *theAppleEvent,AppleEvent *reply,long refCon) |
{ |
AEDesc myDesc; |
OSErr err; |
OSErr ignoreErr ; |
TQ3Object objects = nil; |
err = AEGetParamDesc(theAppleEvent,keyPrivateData,typeChar,&myDesc); |
if (err == noErr) { |
// try to load all of the items we stuffed into the apple event |
myPrivateDataHdl thePrivateData = (myPrivateDataHdl)myDesc.dataHandle ; |
OSType theDragType = (**thePrivateData).myTypeOfData ; |
DocumentPtr theDocument = (**thePrivateData).myDocument ; |
long dataSize = (**thePrivateData).myDataSize ; |
WindowPtr theWindow = (**thePrivateData).myWindow ; |
Point theLocation = (**thePrivateData).myLocation ; |
Handle dataHdl = NewHandle( dataSize ) ; |
// sanity check |
if( dataSize > GetHandleSize( myDesc.dataHandle )) { |
ignoreErr = AEDisposeDesc(&myDesc); |
return paramErr ; |
} |
// check we could create the data handle OK |
if( dataHdl == nil ) { |
ignoreErr = AEDisposeDesc(&myDesc); |
return MemError() ; |
} |
// copy the data from the descriptor to our handle |
BlockMove( &(**thePrivateData).myData, *dataHdl, dataSize ); |
// we need to get rid of the descriptor, to save on memory |
err = AEDisposeDesc(&myDesc); |
// process the information from the drag |
if( theDragType == '3DMF' ) { |
TQ3SharedObject viewHints ; |
// do this on receipt of a drag of type 3DMF |
TQ3FileObject fd; |
TQ3StorageObject storage; |
// Lock and load |
MoveHHi( dataHdl ) ; |
HLock( dataHdl ) ; |
storage = Q3MemoryStorage_New((const unsigned char *) *dataHdl, |
(unsigned long) dataSize ); |
HUnlock( dataHdl ) ; |
if (storage == nil) |
goto bail; |
fd = Q3File_New(); |
Q3File_SetStorage(fd, storage); |
Q3Object_Dispose(storage); |
if (fd == nil) |
goto bail; |
Tumbler_ReadScene( |
fd, |
false, |
&viewHints, |
&theDocument->documentGroup) ; |
TumblerDocument_UpdateView( theDocument, viewHints ) ; |
#ifdef PODIUM_APP |
{ |
// we really want the obect to be centered about the |
// drop location. |
GrafPtr savedPort ; |
extern void Podium_UpdateDrawContextFromDropRect( DocumentPtr theDocument ) ; |
GetPort( &savedPort ) ; |
SetPort( theWindow ) ; |
GlobalToLocal(&theLocation) ; |
// make the droprect be located top left at (0, 0) |
OffsetRect( &theDocument->dropArea, -theDocument->dropArea.left, -theDocument->dropArea.top ); |
OffsetRect( &theDocument->dropArea, |
(theLocation.h - ((theDocument->dropArea.right - theDocument->dropArea.left)/2)), |
(theLocation.v - ((theDocument->dropArea.bottom - theDocument->dropArea.top)/2)) ); |
Podium_UpdateDrawContextFromDropRect( theDocument ) ; |
SetPort(savedPort) ; |
} |
#endif |
TumblerDocument_UpdateView( theDocument, viewHints ) ; |
AdjustLightsPositions(theDocument) ; |
if (viewHints) |
Q3Object_Dispose(viewHints); |
Q3Object_Dispose(fd); |
} |
else if( theDragType == 'PICT' ){ |
// do this on receipt of a drag of type PICT |
TQ3StoragePixmap textureImage; |
PicHandle thePicture = (PicHandle)dataHdl ; |
//Create a texture pixmap |
MoveHHi((Handle) thePicture ); |
HLock((Handle) thePicture ); |
if( TextureFromPICT((PicHandle) thePicture, &textureImage) == kQ3False ) { |
Alert(130, 0L); |
goto bail; |
} |
if( AddTextureToDocument( theDocument, &textureImage) != kQ3Success ) { |
Alert(130, 0L); |
goto bail; |
} |
HUnlock((Handle)thePicture); |
} |
else if( theDragType == flavorTypeHFS ){ |
// do this on receipt of an HFS flavor (a file from the finder) |
HFSFlavor theHFSFlavor ; |
// lock down the handle |
MoveHHi( dataHdl ) ; |
HLock( dataHdl ) ; |
theHFSFlavor = *((HFSFlavor *)*dataHdl) ; |
if( theHFSFlavor.fileType == 'TEXT' || theHFSFlavor.fileType == '3DMF') { |
OSErr result; |
short refNum; |
DocumentPtr theDocument; |
FInfo fndrInfo ; |
TQ3Boolean isText ; |
// we assume the FSSpec passed in was valid, get the file information |
// we need to know the file type, this routine may get called by an appleEvent |
// handler, so we can't assume a type, we need to get it from the fsspec. |
FSpGetFInfo( &theHFSFlavor.fileSpec, &fndrInfo ) ; |
// pull out the file type |
isText = ( fndrInfo.fdType == 'TEXT') ; |
if ((result = FSpOpenDF(&theHFSFlavor.fileSpec, fsRdWrPerm, &refNum)) != noErr) |
return(result); |
theDocument->fRefNum = refNum; |
theDocument->theFileSpec = theHFSFlavor.fileSpec ; |
SetCursor(*GetCursor(watchCursor)); |
ReadDocumentFile( theDocument, isText ) ; |
SetCursor(&qd.arrow); |
} else if( theHFSFlavor.fileType == 'PICT' && theDocument && theDocument->documentGroup) { |
PicHandle thePict; |
TQ3StoragePixmap textureImage; |
// get the picture from the file |
thePict = OpenPICTFile(&theHFSFlavor.fileSpec); |
// make a texture |
TextureFromPICT( thePict, &textureImage); |
AddTextureToDocument( theDocument, &textureImage); |
// and free the space occupied by the picture |
KillPicture(thePict) ; |
} |
else |
dataSize = 0; // need better handling here... |
} |
// we can free up the memory now... |
DisposeHandle( dataHdl ) ; |
dataHdl = nil ; |
// If we actually received something, insert it into the destination. |
if (dataSize != 0) { |
theDocument->dirty = true; |
AdjustLightsPositions( theDocument); |
AdjustCamera(theDocument, |
theWindow->portRect.right - theWindow->portRect.left, |
theWindow->portRect.bottom - theWindow->portRect.top); |
// Draw everything into offscreen pixmap. |
if (DrawOffscreen(theDocument)) { |
SetPort( theWindow ) ; |
InvalRect( &theWindow->portRect ) ; |
DrawOnscreen(theDocument); |
} |
} |
} |
return err ; |
bail: |
return(memFullErr); |
} |
//----------------------------------------------------------------------- |
// A P P L E E V E N T S E N D E R R O U T I N E S . . . |
//----------------------------------------------------------------------- |
// These routines are used to send ourselves AppleEvents, this is useful |
// for a number of reasons, ultimately it would help make us scriptable if |
// we implemented some of the OSL, also it's a big help for debugging drags. |
// |
// Its not really easy to do source level debugging of drag handlers and |
// trackers. By packaging up the information into an appleevent and sending |
// it to ourselves, we can to source level debugging of the drag in the AE |
// handler. |
//----------------------------------------------------------------------------------// |
// Send a Quit Application Apple Event to myself to terminate this app. |
void SendQuitApp( void ) |
{ |
AppleEvent myAppleEvent, reply; |
// Create the Apple Event. |
FailIfErr(AECreateAppleEvent( kCoreEventClass, |
kAEQuitApplication, |
&pSelfAddress, |
kAutoGenerateReturnID, |
kAnyTransactionID, |
&myAppleEvent)); |
// Send the Apple Event. |
FailIfErr(AESend( &myAppleEvent, |
&reply, |
kAENoReply+kAENeverInteract, |
kAENormalPriority, |
kAEDefaultTimeout, |
nil, |
nil)); |
AEDisposeDesc(&myAppleEvent); // Dispose of the Apple Event. |
} // SendQuitApp |
//----------------------------------------------------------------------------------// |
// Send a Open Document Application Apple Event to myself to open a document. |
void SendOpenDoc(FSSpec *myFSSpec) |
{ |
AppleEvent myAppleEvent; |
AppleEvent defReply; |
AEDescList docList; |
OSErr ignoreErr; |
myAppleEvent.dataHandle = nil; |
docList.dataHandle = nil; |
defReply.dataHandle = nil; |
// Create empty list and add one file spec |
FailIfErr(AECreateList(nil,0,false, &docList)); |
FailIfErr(AEPutPtr(&docList,1,typeFSS,(Ptr)myFSSpec,sizeof(FSSpec))); |
FailIfErr(AECreateAppleEvent( kCoreEventClass, |
kAEOpenDocuments, |
&pSelfAddress, |
kAutoGenerateReturnID, |
kAnyTransactionID, |
&myAppleEvent)); |
// Put Params into our event and send it |
FailIfErr(AEPutParamDesc( &myAppleEvent, |
keyDirectObject, |
&docList)); |
FailIfErr(AESend( &myAppleEvent, |
&defReply, |
kAENoReply+kAENeverInteract, |
kAENormalPriority, |
kAEDefaultTimeout, |
nil, |
nil)); |
if (myAppleEvent.dataHandle) |
ignoreErr = AEDisposeDesc(&myAppleEvent); |
if (docList.dataHandle) |
ignoreErr = AEDisposeDesc(&docList); |
} // SendOpenDoc |
//----------------------------------------------------------------------------------// |
// Send a Print Document Application Apple Event to myself to open a document. |
void SendPrintDoc(FSSpec *myFSSpec) |
{ |
AppleEvent myAppleEvent; |
AppleEvent defReply; |
AEDescList docList; |
OSErr ignoreErr; |
myAppleEvent.dataHandle = nil; |
docList.dataHandle = nil; |
defReply.dataHandle = nil; |
// Create empty list and add one file spec |
FailIfErr(AECreateList(nil,0,false, &docList)); |
FailIfErr(AEPutPtr(&docList,1,typeFSS,(Ptr)myFSSpec,sizeof(FSSpec))); |
FailIfErr(AECreateAppleEvent( kCoreEventClass, |
kAEPrintDocuments, |
&pSelfAddress, |
kAutoGenerateReturnID, |
kAnyTransactionID, |
&myAppleEvent)); |
// Put Params into our event and send it |
FailIfErr(AEPutParamDesc( &myAppleEvent, |
keyDirectObject, |
&docList)); |
FailIfErr(AESend( &myAppleEvent, |
&defReply, |
kAENoReply+kAENeverInteract, |
kAENormalPriority, |
kAEDefaultTimeout, |
nil, |
nil)); |
if (myAppleEvent.dataHandle) |
ignoreErr = AEDisposeDesc(&myAppleEvent); |
if (docList.dataHandle) |
ignoreErr = AEDisposeDesc(&docList); |
} // SendOpenDoc |
//---------------------------------------------------------------------------------- |
// Send a drag receive Apple Event to myself to handle the receipt of a drag. |
void SendDragRecv( myPrivateDataHdl privateHdl ) |
{ |
AppleEvent myAppleEvent; |
AppleEvent defReply; |
OSErr ignoreErr; |
ProcessSerialNumber myPSN ; |
AEDesc myAddress ; |
char cMemTags = HGetState((Handle)privateHdl); // save the state of the handle in case |
// it was already locked before we were |
// called |
myAppleEvent.dataHandle = nil; |
defReply.dataHandle = nil; |
// the static pSelfAddress that we create an usually use will cause the |
// AE handler to be caused directly (less overhead). We DON'T WANT THAT |
// to happen here. The reason being that the handler for this event needs |
// to be called in our main event loop, not directly at the send. Doing things |
// this way lets us get out of the Drag Managers context, back into our own. |
// that in turn lets us use a high level debugger. |
FailIfErr(GetCurrentProcess(&myPSN)) ; |
FailIfErr(AECreateDesc(typeProcessSerialNumber,(Ptr)&myPSN,sizeof(ProcessSerialNumber),&myAddress)); |
FailIfErr(AECreateAppleEvent( kDraggingClass, |
kDragReceiveID, |
&myAddress, |
kAutoGenerateReturnID, |
kAnyTransactionID, |
&myAppleEvent)); |
// lock down the handle |
MoveHHi( (Handle)privateHdl ); |
HLock( (Handle)privateHdl ); |
// Put Params into our event and send it |
FailIfErr(AEPutParamPtr( &myAppleEvent, |
keyPrivateData, |
typeChar, |
*privateHdl, |
GetHandleSize( (Handle)privateHdl ))); |
// restore the state of the handle (effectively the same as HUnlock) |
HSetState((Handle)privateHdl, cMemTags) ; |
FailIfErr(AESend( &myAppleEvent, |
&defReply, |
kAENoReply+kAENeverInteract, |
kAENormalPriority, |
kAEDefaultTimeout, |
nil, |
nil)); |
if (myAddress.dataHandle) |
ignoreErr = AEDisposeDesc(&myAddress); |
if (myAppleEvent.dataHandle) |
ignoreErr = AEDisposeDesc(&myAppleEvent); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14