TumblerSource/Tumbler_drag.c

// Tumbler_drag.c
//
// Dragging routines. The code in this file implements the geometry
// dragging features of this program.  The last function in this file,
// DoDragObjects is called when a mouseDown is detected in the hilite region
// of the current object.
//  
// Here's some background from Nick for debugging this mess.  First off the drag manager 
// is not a simple animal.
//
// High-level or source-level debuggers won't work with Drag Manager callback routines.  
// That's because of the way the Drag Manager performs process switching.  The only 
// high-level debugger that does work with the Drag Manager is VoodooMonkey, an "experimental 
// prototype" debugger you can find on the November Developer CD.  Note that VoodooMonkey 
// is unsupported by Apple.  The other more-supportable option is to use MacsBug (or your 
// favorite low-level debugger) for real-time debugging.  But since this 3d stuff will only
// work on power macintosh cpu's (unless you have the souce code, which developers don't :0>)
// MacsBug won't help you much.  And I was using a version of CodeWarrior that didn't seem to 
// want to put any symbols in, which means Macsbug is not really an option (debugging what
// MB thinks is the 68k equivalent of the actual PPC instructions, with no symbols, ain't
// my idea of fun.  And don't think DebugStr will help you any - if you are running with 
// the MetroWerks (MW) high level debugger, your mAchine will crash big time.  MW debugger
// wants to display the debugstr in a nice little dialog, which means it has to do a context
// switch.  Why is this bad... read on McDuff:
//
// The Drag Manager is actually making full context switches for each process that has  
// tracking/receive handlers as an object is dragged over a valid drag region.  While this 
// may sound a bit drastic or scary, it isn't really that bad because a drag is a modal operation,  
// and as long as the user is dragging an object, the System (namely the Drag Manager) is in  
// control to call tracking and receive handlers as appropriate.  So when your receive handler  
// is called and all events pending for your process have been handled, your application is  
// eliglble for a context switch the next time WaitNextEvent is called.  And when WNE is called  
// and a context switch occurs, the Drag Manager has lost track over what process is currently  
// the context.  This is also why you cannot use source-level debuggers to debug drag handlers,  
// because context switches are controlled by those debuggers, and once you're in a Drag you  
// cannot change the Process Manager state.
//  
// In case you're interested, one technique that works well for receive handlers is to send an  
// Apple event to yourself with the relevant information about the drag, and process it at  
// WaitNextEvent time.  Sending an Apple event to yourself can be done even if you're not the  
// frontmost application, and it allows you to process the Drag at WaitNextEvent time, meaning  
// you can call your Alert routine and change process states without any problems.  Another  
// benefit to this approach is that you'll be able to use source level debuggers to work on  
// your receive handlers.
//
// The only thing to beware of if you do this (and this is what do here) is to get the PSN 
// directly - AppleEvents.h gives a constant (kCurrentProcess) that you can stick in the low
// longword of the PSN that will cause the event to essentially be dispatched as a function
// call, and that is not what we want here.  By getting the process serial number (PSN) of
// our own process and using that in the AppleEvent we ensure that the event is dispatched 
// to our process so we can deal with it in the main event loop, rather than being mapped into
// and unwanted funtion call (which since we would still be in the drag managers context would
// prevent us from being able to debug using anything reasonable).  Check out Tumbler_AEVT for
// more details on this.
//
// Nitin Gantatra whined so much that I'd stolen the comments above, that I feel compelled to 
// add:
//
//  Author of lame gratuitous drag manager comments: Jim Luther
//
//  Author:     Nick Thompson, based on the original by
//              Pablo Fernicola, based on Rob Johnson's drag text sample,
//              with extensive stealing from grobbins SimpleDrag sample.
//
//  Modification history:
//
//  11/26/94    nick    removed dead code, tightened up the drag code, based on
//                      grobbins' excellent simpledrag sample
//  11/25/94    nick    added events for dragging, this lets us do
//                      source level debugging of the drag receiver, see Tumbler_aevt.
//
//  Copyright © 1992-95 Apple Computer, Inc., All Rights Reserved
//
// to do: store the document reference as a handle in the window's refcon
//
 
 
#define NAGELS_DEMO     // pre-allocate handles
 
 
#include <QDOffscreen.h>
#include <Folders.h>
#include <Drag.h>
#include <SegLoad.h>
#include <Types.h>
 
#include "Tumbler_globals.h"
 
#include "QD3D.h"
#include "QD3DGroup.h"
#include "QD3DStorage.h"
#include "QD3DIO.h"
#include "QD3DView.h"
#include "QD3DDrawContext.h"
#include "QD3DPick.h"
#include "QD3DShader.h"
#include "QD3DTransform.h"
#include "QD3DGroup.h"
 
#include "Tumbler_prototypes.h"
#include "Tumbler_file.h"
#include "Tumbler_resources.h"
#include "Tumbler_offscreen.h"
 
 
#include "Tumbler_AEVT.h"           // grab our apleEvent handlers and data structures
                                    // used for debugging
#include "Tumbler_drag.h"
 
 
 
//  Can use custom drawing procedure. Just uncomment out this line:
//  #define USE_CUSTOM_DRAWING
 
 
// global data for my Drag Manager handlers
 
typedef struct DragHandlerGlobals 
{
    Boolean         acceptableDragFlag;
    Boolean         windowIsHilightedFlag;
} ;
 
typedef struct DragHandlerGlobals
    DragHandlerGlobals, *DragHandlerGlobalsPtr;
 
static DragHandlerGlobals pDragHandlerGlobals;
 
 
//-----------------------------------------------------------------------
//  MyDrawingProc
//
//  Simple drawing proc for dragging objects. This drawing procedure is very similar to
//  the Drag Manager's built in drawing proc, except that it uses a solid black pattern
//  to draw the region instead of using the dithered gray pattern.
//
// DONT USE THIS ON POWERPC as there is a quickdraw bug that leaves artifacts
// everywhere (talk to Nitin Ganatra for the gory details).
 
 
static
pascal OSErr MyDrawingProc(DragRegionMessage message,
                           RgnHandle showRgn, Point showOrigin,
                           RgnHandle hideRgn, Point hideOrigin,
                           void *dragDrawingRefCon, DragReference theDragRef)
 
{   OSErr           result = paramErr;
    RgnHandle       tempRgn;
 
    switch(message) {
 
        case dragRegionBegin:
 
            //
            //  No initialization necessary for our drawing proc. Make sure noErr
            //  is returned, otherwise the Drag Manager will revert back to it's
            //  built in drawing proc.
            //
 
            result = noErr;
            break;
 
        case dragRegionDraw:
 
            //
            //  Find the difference between the region needed to be shown and the
            //  region needed to be hidden. Inverting the difference removes the pixels
            //  that must be hidden and shows the pixels that must be shown in one step.
            //
 
            XorRgn(showRgn, hideRgn, tempRgn = NewRgn());
            InvertRgn(tempRgn);
            DisposeRgn(tempRgn);
            result = noErr;
            break;
 
        case dragRegionHide:
 
            //
            //  Simply hide the region given to us by inverting it.
            //
 
            InvertRgn(hideRgn);
            result = noErr;
            break;
    }
 
    return(result);
}
 
 
 
//------------------------------------------------------------------------------------
//  MySendDataProc - provides data for the drag when requested.
 
static pascal OSErr MySendDataProc(FlavorType theType, void *refCon,
                            ItemReference theItem, DragReference theDrag)
 
{
    DocumentPtr theDocument = (DocumentPtr ) refCon;
    unsigned long   validSize;
    
    // so what do they want??
    
    if (theType == '3DMF') {
 
        if( theDocument->documentGroup ) {
        
            Handle              theData;
            TQ3StorageObject        storage;
            TQ3FileObject       fd;
            TQ3TransformObject  xform;
            TQ3GroupPosition        position;
            Boolean             didAllocate = false;        // set to true if we did allocate memory
            
            if( Q3Object_IsType(theDocument->documentGroup, kQ3DisplayGroupTypeOrdered)) {
                Q3Group_GetFirstPositionOfType(theDocument->documentGroup, kQ3ShapeTypeTransform,&position);
            } else {
                Q3Group_GetFirstPosition(theDocument->documentGroup, &position);
            }
 
            xform = Q3MatrixTransform_New( &theDocument->modelRotation);
            
            position = Q3Group_AddObjectBefore(theDocument->documentGroup, position, xform);
    
            Q3Object_Dispose(xform);
            
#ifdef NAGELS_DEMO
            // HO HO HO.  Actually we *do* need a way to approximate the 
            // size this handle needs to be so we can preflight
                        
            if((theData = NewHandle(1024 * 1024 )) != nil)  {       // ask for 1M
            
                MoveHHi( theData ) ;
                HLock( theData ) ;
                didAllocate = true ;
                storage = Q3HandleStorage_New( theData, GetHandleSize( theData ) );
                
            }
            else {
            
                storage = Q3HandleStorage_New( nil, 0 );
                
            }
#else
            storage = Q3HandleStorage_New(nil,0);
#endif          
            if (storage == nil)
                goto bail;
            
            fd = Q3File_New();
            
            if (fd == nil)
                goto bail;
 
            Q3File_SetStorage(fd, storage);
                        
            Tumbler_WriteScene(fd, 
                                false,
                                theDocument) ;
    
            Q3HandleStorage_Get(storage, &theData, &validSize);
            HLock(theData);
            SetDragItemFlavorData(theDrag, theItem, '3DMF', (Ptr) *theData,
                                  validSize, 0L);
            HUnlock(theData);
            Q3Object_Dispose(storage);
            Q3Object_Dispose(fd);
            
#ifdef NAGELS_DEMO
            if( didAllocate == true ) {
                DisposeHandle(theData);
            }
#endif
 
        } 
        else {
            return(badDragFlavorErr);
        }
    }  
    else {
 
        return(badDragFlavorErr);
 
    }
    return(noErr);
    
bail:
    return(badDragFlavorErr);
}
 
 
//-----------------------------------------------------------------------
// DragItemsAreAcceptable returns true if the contents (data) of
// the drag are acceptable by a window of this application
//
// DragItemsAreAcceptable is called by the tracking and 
// receive handlers
Boolean DragItemsAreAcceptable(DragReference theDrag) ;
 
Boolean DragItemsAreAcceptable(DragReference theDrag)
{
    OSErr           retCode;
    unsigned short  totalItems;
    ItemReference   itemRef;
    Boolean         acceptableFlag;
    HFSFlavor       currHFSFlavor;
    Size            flavorDataSize;
    FlavorFlags     currFlavorFlags;
    
 
    acceptableFlag = false;
 
    // this app can only accept the drag of a single item
    retCode = CountDragItems(theDrag, &totalItems);
    if (retCode == noErr && totalItems == 1) {
    
        // get the reference number of the dragged item
        retCode = GetDragItemReferenceNumber(theDrag, 1, &itemRef);
        if (retCode == noErr) {
            
            // use GetFlavorFlags to check on flavor existence of PICT data
            // without forcing translation
            
            if (GetFlavorFlags (theDrag, itemRef, 'PICT', &currFlavorFlags) == noErr) {
                acceptableFlag = true;
            }
            else if (GetFlavorFlags (theDrag, itemRef, '3DMF', &currFlavorFlags) == noErr) {
                acceptableFlag = true;
            }
            else {
            
                // check if the item is a file spec for a PICT file
                flavorDataSize = sizeof(HFSFlavor);
                retCode = GetFlavorData(theDrag, itemRef, flavorTypeHFS, &currHFSFlavor,
                    &flavorDataSize, 0);
                
                if (retCode == noErr &&  (currHFSFlavor.fileType == 'PICT' 
                                || currHFSFlavor.fileType == '3DMF'
                                || currHFSFlavor.fileType == 'TEXT' )) 
                    acceptableFlag = true;
            }
        }
    }
 
    return acceptableFlag;
}
 
 
//-----------------------------------------------------------------------
// DragIsNotInSourceWindow returns true if the drag in progress
// is not in the same window it originated in
//
// DragIsNotInSourceWindow is called by the tracking and receive handlers
//
// Note that, if this application allowed items to be dragged within
// its windows, this function would not be appropriate.
// Instead, hilighting would probably occur in the source window
// when the dragHasLeftSourceWindow flag is set, and the receive
// handler wouldn't check this at all
 
static
Boolean DragIsNotInSourceWindow(DragReference theDrag)
{
    DragAttributes currDragFlags;
    
    (void) GetDragAttributes(theDrag, &currDragFlags);
    return ((currDragFlags & dragInsideSenderWindow) == 0);
}
 
 
//-----------------------------------------------------------------------
// MouseInContentRgn returns true if the current mouse is in the content
// area of the window (but not necessarily in the visible rgn)
 
static
Boolean MouseIsInContentRgn(DragReference theDrag, WindowPtr theWindow)
{
    Point mousePt;
    
    (void) GetDragMouse(theDrag, &mousePt, nil);
    return PtInRgn(mousePt, ((WindowPeek) theWindow)->contRgn);
}
 
//-----------------------------------------------------------------------
//  MyReceiveDropHandler
//
//  Called by the Drag Manager when a drop occurs over one of the DoDragObjects document windows.
 
pascal OSErr MyReceiveDropHandler(WindowPtr theWindow, unsigned long handlerRefCon,
                                  DragReference theDrag);
 
pascal OSErr MyReceiveDropHandler(WindowPtr theWindow, unsigned long handlerRefCon,
                                  DragReference theDrag)
 
{
    OSErr               result;
    Rect                theRect, srcRect;
    unsigned short      items, index;
    ItemReference       theItem;
    DragAttributes      attributes;
    StScrpHandle        stylHandle;
    Size                dataSize, pictSize;
    short               offset, selStart, selEnd, mouseDownModifiers, mouseUpModifiers, moveText;
    DocumentPtr         theDocument = (DocumentPtr ) handlerRefCon;
    Point               thePoint;
    TQ3Object           objects = nil;
 
    Boolean             dataObtainedFlag;
    OSErr               retCode;
    
    OSType              theDragFlavor ;
    Point               mouseLoc ;
    
    
    dataObtainedFlag = false;
    if (!DragItemsAreAcceptable(theDrag) ||
            !MouseIsInContentRgn(theDrag, theWindow) ||
            !DragIsNotInSourceWindow(theDrag)) 
        return dragNotAcceptedErr;
                
    // We will only support one item, so get its reference number.
    result = GetDragItemReferenceNumber(theDrag, 1, &theItem);
    if (result != noErr)
        return result;
 
    GetDragAttributes(theDrag, &attributes);
    GetDragModifiers(theDrag, 0L, &mouseDownModifiers, &mouseUpModifiers);
    
    //  Loop through all of the drag items contained in this drag
    
    SetRect(&theRect, 0, 0, 0, 0);
 
    // we ensured above that we only deal with one item being dragged to us...
    
//  CountDragItems(theDrag, &items);
 
    //  Get the item's reference, so we can refer to it.
    GetDragItemReferenceNumber(theDrag, 1, &theItem);
    
    // get the location the user dropped the data
    GetDragMouse(theDrag, &mouseLoc, nil ) ;
 
    //  Try to get the flags for a '3DMF' flavor. If this returns noErr,
    //  then we know that a '3DMF' flavor exists in the item.
 
    result = GetFlavorDataSize(theDrag, theItem, '3DMF', &dataSize);
    if (result == noErr) {
        theDragFlavor = '3DMF' ;
    }
    else if((result = GetFlavorDataSize(theDrag, theItem, 'PICT', &dataSize)) == noErr) {
        theDragFlavor = 'PICT' ;
    } 
    else {
        // Couldn't get 3DMF or PICT data so try to get HFS-flavor data.
        HFSFlavor       theHFSFlavor;
        dataSize = sizeof(HFSFlavor);
        result = GetFlavorData(theDrag, theItem, flavorTypeHFS, 
                                                &theHFSFlavor, &dataSize, 0);
                                                
        if(theHFSFlavor.fileType != 'PICT' 
            || theHFSFlavor.fileType != '3DMF'
            || theHFSFlavor.fileType != 'TEXT' ) {
            result = paramErr ;
        }
    }
 
    
    // so we either have nothing or something (in the form of 3DMF or PICT data)
    if (result == noErr) {
        
        // create a new data handle to hold the data the user just dragged in
        // this needs to be the size of the data, plus the size of the 
 
        myPrivateDataHdl        thePrivateData = nil ;
        long                    recordSize ;    
 
 
        recordSize = sizeof( myPrivateDataRec ) ;
        
        // create a handle large enough for the data
        thePrivateData = (myPrivateDataHdl)NewHandle( (long)(dataSize +  recordSize)) ;
        
        if( thePrivateData == nil )
            return MemError() ;
    
        
        (**thePrivateData).myTypeOfData = theDragFlavor ;
        (**thePrivateData).myDocument = theDocument ;       // ideally we should store the doc in the windows refcon, this is hacky ???
        (**thePrivateData).myDataSize = dataSize ;
        (**thePrivateData).myWindow = theWindow ;
        (**thePrivateData).myLocation = mouseLoc ;
 
        // Lock and load, I dunno if GetFlavorData moves memory, I'm just assuming it may...
        
        MoveHHi( (Handle)thePrivateData ) ;
        HLock( (Handle)thePrivateData ) ;
 
        //  passing (**thePrivateData) in dereferenced - ASSUMES the hnadle is locked
        GetFlavorData(theDrag, theItem, theDragFlavor, &(**thePrivateData).myData[0], &dataSize, 0L);
 
        HUnlock( (Handle)thePrivateData ) ;
 
        // package this puppy up and send it to ourselves
        SendDragRecv( thePrivateData ) ;
        
        // and dispose of the data
        DisposeHandle((Handle)thePrivateData ) ;
 
        if (attributes & dragHasLeftSenderWindow) {
            HideDragHilite(theDrag);
        }
    }
    
    return(noErr);
    
bail:
    return(memFullErr);
}
 
 
//-----------------------------------------------------------------------
//  MyTrackingHandler
//
//  This is the drag tracking handler for windows in the Dragging application.
 
pascal OSErr MyTrackingHandler(short theMessage, WindowPtr theWindow,
                               void *handlerRefCon, DragReference theDrag);
 
pascal OSErr MyTrackingHandler(short theMessage, WindowPtr theWindow,
                               void *handlerRefCon, DragReference theDrag)
 
{
 
    RgnHandle   tempRgn;
    Boolean     mouseInContentFlag;
    OSErr       retCode;
    
    retCode = noErr;
    
    switch (theMessage) {
    
        case dragTrackingEnterHandler:
            
            // determine if the items are acceptable and store
            // that flag in the globals, plus reset the
            // hilighted global flag
            
            pDragHandlerGlobals.acceptableDragFlag = 
                DragItemsAreAcceptable(theDrag);
            pDragHandlerGlobals.windowIsHilightedFlag = false;
            
            // let the drag manager know if we can't accept this drag
            if (!pDragHandlerGlobals.acceptableDragFlag)
                retCode = dragNotAcceptedErr;
            break;
            
        case dragTrackingEnterWindow: 
        case dragTrackingInWindow:
        case dragTrackingLeaveWindow:
            
            // highlighting of the window during a drag is done
            // here.  Do it only if we can accept these items
            // and we're not in the source window...
            
            if (pDragHandlerGlobals.acceptableDragFlag &&
                DragIsNotInSourceWindow(theDrag)) {
                
                // unless the mouse is leaving the visible area of the
                // window, check if it's in the window's content region
                
                if (theMessage == dragTrackingLeaveWindow)
                    mouseInContentFlag = false;
 
                else
                    mouseInContentFlag = MouseIsInContentRgn(theDrag, theWindow);
                
                // if the mouse is in the content area and the window
                // is not yet hilighted, then do the hilighting
                
                if (mouseInContentFlag &&
                    !pDragHandlerGlobals.windowIsHilightedFlag) {
                    
                    // set the proper clip
                    ClipRect(&theWindow->portRect);
                    
                    // make a region bordering the window content
                    tempRgn = NewRgn();
                    RectRgn(tempRgn, &theWindow->portRect);
                    
                    // draw the hilight
                    if (ShowDragHilite(theDrag, tempRgn, true) == noErr) {
                        // remember that hilighting is now on
                        pDragHandlerGlobals.windowIsHilightedFlag = true;
                    }                   
                    
                    // free up the region
                    DisposeRgn(tempRgn);
                }
                
                // else if the mouse is not in the content region
                // and the window is hilighted, erase the hilight
                
                else if (!mouseInContentFlag &&
                    pDragHandlerGlobals.windowIsHilightedFlag) {
                    
                    // set the proper clip
                    ClipRect(&theWindow->portRect);
                    
                    // erase the hilight and restore the port
                    if (HideDragHilite(theDrag) == noErr)
                    
                        // remember that hilighting is now off
                        pDragHandlerGlobals.windowIsHilightedFlag = false;
                }
            }
            break;
 
        // do nothing for the leaveHandler message
        case dragTrackingLeaveHandler:
            break;
        
        // let the drag manager know if we didn't recognize the message
        default:
            retCode = paramErr;
    }
    
    return retCode;
}
 
 
//
//  DropLocationIsFinderTrash
//
//  Returns true if the given dropLocation AEDesc is a descriptor of the Finder's Trash.
//
 
static Boolean DropLocationIsFinderTrash(AEDesc *dropLocation)
 
{   OSErr           result;
    AEDesc          dropSpec;
    FSSpec          *theSpec;
    CInfoPBRec      thePB;
    short           trashVRefNum;
    long            trashDirID;
 
    //
    //  Coerce the dropLocation descriptor to an FSSpec. If there's no dropLocation or
    //  it can't be coerced into an FSSpec, then it couldn't have been the Trash.
    //
 
    if ((dropLocation->descriptorType != typeNull) &&
        (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr)) {
 
        HLock(dropSpec.dataHandle);
        theSpec = (FSSpec *) *dropSpec.dataHandle;
 
        //
        //  Get the directory ID of the given dropLocation object.
        //
 
        thePB.dirInfo.ioCompletion = 0L;
        thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
        thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
        thePB.dirInfo.ioFDirIndex = 0;
        thePB.dirInfo.ioDrDirID = theSpec->parID;
 
        result = PBGetCatInfo(&thePB, false);
 
        HUnlock(dropSpec.dataHandle);
        AEDisposeDesc(&dropSpec);
 
        if (result != noErr)
            return(false);
 
        //
        //  If the result is not a directory, it must not be the Trash.
        //
 
        if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
            return(false);
 
        //
        //  Get information about the Trash folder.
        //
 
        FindFolder(theSpec->vRefNum, kTrashFolderType, kCreateFolder, &trashVRefNum, &trashDirID);
 
        //
        //  If the directory ID of the dropLocation object is the same as the directory ID
        //  returned by FindFolder, then the drop must have occurred into the Trash.
        //
 
        if (thePB.dirInfo.ioDrDirID == trashDirID)
            return(true);
    }
 
    return(false);
}
 
 
//
//  DoDragObjects
//
//  Drag the selected text in the given document.
//
 
short DoDragObjects(DocumentPtr theDocument, EventRecord *theEvent, RgnHandle hiliteRgn)
{
    short               result;
    RgnHandle           dragRegion, tempRgn;
    Point               theLoc;
    DragReference       theDrag;
    StScrpHandle        theStyl;
    AEDesc              dropLocation;
    DragAttributes      attributes;
    short               mouseDownModifiers, mouseUpModifiers, copyText;
    unsigned long       validSize;
 
    if( theDocument->documentGroup ) {
 
        Handle              theData;
        TQ3StorageObject        storage;
        TQ3FileObject       fd;
        TQ3GroupPosition        position;
        TQ3TransformObject  xform;
        
        DragSendDataUPP     mySendDataProcUPP ;
        
        //Q3Object_Dispose(pickObject);
 
        CopyRgn(hiliteRgn, dragRegion = NewRgn());
        SetPt(&theLoc, 0, 0);
        LocalToGlobal(&theLoc);
        OffsetRgn(dragRegion, theLoc.h, theLoc.v);
    
        //
        //  Wait for the mouse to move to the mouse button to be released. If the mouse button was
        //  released before the mouse moves, return false. Returing false from DoDragObjects means that
        //  a drag operation did not occur.
        //
    
        if (! WaitMouseMoved(theEvent->where)) {
            return(false);
        }
    
        // create a new reference for a track to pass to track drag
        NewDrag(&theDrag);
    
        //  We promise '3DMF' and 'PICT'.  If a receiver requests either, the Drag Manager
        //  will call our MySendDataProc to provide the data at drop time. The MySendDataProc
        //  is specified by calling SetDragSendProc.
        AddDragItemFlavor(theDrag, 1, '3DMF', nil, 0L, 0L);
//      AddDragItemFlavor(theDrag, 1, 'PICT', nil, 0L, 0L);
        
        mySendDataProcUPP = NewDragSendDataProc(MySendDataProc) ;
        SetDragSendProc(theDrag, mySendDataProcUPP, (void *) theDocument);
    
        //  Set the item's bounding rectangle in global coordinates.
        SetDragItemBounds(theDrag, 1, &(**dragRegion).rgnBBox);
    
        //  Prepare the drag region.
        tempRgn = NewRgn();
        CopyRgn(dragRegion, tempRgn);
        InsetRgn(tempRgn, 1, 1);
        DiffRgn(dragRegion, tempRgn, dragRegion);
        DisposeRgn(tempRgn);
    
        // on PPC the drawing proc leaves artifacts on the display when 
        // highlighting and upon removal of highlighting for a window, so
        // although I may prefer the drawing that my custom proc does, I
        // just use the default.  This may be a bug in the drag manager
        // or it may be us, but right now we'll just a void doing this and fix later
//          SetDragDrawingProc(theDrag,NewDragDrawingProc(MyDrawingProc), 0L);
    
        //  Drag the stuff. TrackDrag will return userCanceledErr if the drop zoomed-back
        //  for any reason.
        result = TrackDrag(theDrag, theEvent, dragRegion);
        
        // get rid of the UPP
        DisposeRoutineDescriptor( mySendDataProcUPP ) ;
    
        if (result != noErr && result != userCanceledErr) {
            return(true);
        }
    
        //  Check to see if the drop occurred in the Finder's Trash. If the drop occurred
        //  in the Finder's Trash and a copy operation wasn't specified, delete the
        //  source selection. Note that we can continute to get the attributes, drop location
        //  modifiers, etc. of the drag until we dispose of it using DisposeDrag.
        GetDragAttributes(theDrag, &attributes);
        if (!(attributes & dragInsideSenderApplication)) {
    
            GetDropLocation(theDrag, &dropLocation);
    
            GetDragModifiers(theDrag, 0L, &mouseDownModifiers, &mouseUpModifiers);
            copyText = (mouseDownModifiers | mouseUpModifiers) & optionKey;
    
            if ((!copyText) && (DropLocationIsFinderTrash(&dropLocation))) {
                theDocument->dirty = true;
                Q3Object_Dispose(theDocument->documentGroup);
                theDocument->documentGroup = nil;
            }
    
            AEDisposeDesc(&dropLocation);
        }
 
        //  Dispose of the drag.
        DisposeDrag(theDrag);
        DisposeRgn(dragRegion);
        return(true);
    }
bail:
    return(false);
}