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.
DragStuff.c
/* |
File: DragStuff.c |
Contains: Drag Manager handlers and associated routines |
Written by: Chris White |
Copyright: Copyright © 1995-1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
8/5/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
// System includes |
#ifndef __DRAG__ |
#include <Drag.h> |
#endif |
#ifndef __ERRORS__ |
#include <Errors.h> |
#endif |
#ifndef __GESTALT__ |
#include <Gestalt.h> |
#endif |
#ifndef __CODEFRAGMENTS__ |
#include <CodeFragments.h> |
#endif |
#ifndef __SCRIPT__ |
#include <Script.h> |
#endif |
#ifndef __RESOURCES__ |
#include <Resources.h> |
#endif |
#ifndef __STDDEF__ |
#include <stddef.h> |
#endif |
// Application includes |
#ifndef __FRAGMENTTOOL__ |
#include "FragmentTool.h" |
#endif |
#ifndef __PROTOTYPES__ |
#include "Prototypes.h" |
#endif |
#include "Utilities.h" |
static Boolean gHasAcceptableDrag = false; |
static Boolean gHasHilitedList = false; |
static Boolean DragItemsAreAcceptable ( DragReference theDrag ); |
static Boolean DragIsNotInSourceWindow ( DragReference theDrag ); |
static pascal OSErr DragTracker ( DragTrackingMessage theMessage, WindowRef theWindow, |
void *handlerRefCon, DragReference theDrag ); |
static pascal OSErr DragTracker ( DragTrackingMessage theMessage, WindowRef theWindow, |
void *handlerRefCon, DragReference theDrag ); |
static pascal OSErr DragReceiver ( WindowRef theWindow, void *handlerRefCon, |
DragReference theDrag ); |
static pascal OSErr SendDataProc ( FlavorType theType, void *dragSendRefCon, |
ItemReference theItemRef, DragReference theDragRef ); |
// |
// InitDragHandlers creates the UPPs for the Drag Manager |
// callback routines _if_ the Drag Manager is available. |
// |
OSErr InitDragHandlers ( void ) |
{ |
OSErr theErr = noErr; |
if ( gHasDragManager ) |
{ |
gDragTrackingHandlerUPP = NewDragTrackingHandlerProc ( DragTracker ); |
gDragReceiveHandlerUPP = NewDragReceiveHandlerProc ( DragReceiver ); |
gDragSendDataProcUPP = NewDragSendDataProc ( SendDataProc ); |
} |
return theErr; |
} |
// |
// InstallDragHandlers attaches the tracking and receive handlers to |
// one of the application's windows. |
// |
OSErr InstallDragHandlers ( WindowRef theWindow ) |
{ |
OSErr theErr = noErr; |
if ( gHasDragManager ) |
{ |
theErr = InstallTrackingHandler ( gDragTrackingHandlerUPP, theWindow, nil ); |
if ( theErr == noErr ) |
{ |
theErr = InstallReceiveHandler ( gDragReceiveHandlerUPP, theWindow, nil ); |
if ( theErr ) |
(void) RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow ); |
} |
} |
return theErr; |
} |
// |
// RemoveDragHandlers removes the tracking and receive handlers from |
// one of the application's windows (usually just prior to disposal). |
// |
void RemoveDragHandlers ( WindowRef theWindow ) |
{ |
if ( gHasDragManager ) |
{ |
RemoveReceiveHandler ( gDragReceiveHandlerUPP, theWindow ); |
RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow ); |
} |
return; |
} |
// |
// DragItemsAreAcceptable returns true if the contents (data) of |
// the drag are acceptable. This is called by the tracking and |
// receive handlers. |
// |
static Boolean DragItemsAreAcceptable ( DragReference theDrag ) |
{ |
OSErr theErr; |
unsigned short totalItems; |
ItemReference itemRef; |
Boolean bAcceptIt; |
OSType currOSType; |
Size flavorDataSize; |
bAcceptIt = false; |
// this app can only accept the drag of a single item |
theErr = CountDragItems ( theDrag, &totalItems ); |
if ( theErr == noErr && totalItems == 1 ) |
{ |
// get the reference number of the dragged item |
theErr = GetDragItemReferenceNumber ( theDrag, 1, &itemRef ); |
if ( theErr == noErr ) |
{ |
// check if the item is one of ours |
flavorDataSize = sizeof ( OSType ); |
theErr = GetFlavorData ( theDrag, itemRef, kCreatorCode, &currOSType, |
&flavorDataSize, 0 ); |
//#if DEBUGGING |
//if ( theErr ) DebugStr ( "\p GetFlavorData returned an error" ); |
//#endif |
if ( theErr == noErr ) |
bAcceptIt = true; |
} |
} |
return bAcceptIt; |
} |
// |
// DragIsNotInSourceWindow returns true if the drag in progress |
// is not in the same window it originated in. This is called by |
// the tracking and receive handlers. |
// |
static Boolean DragIsNotInSourceWindow ( DragReference theDrag ) |
{ |
DragAttributes currDragFlags; |
GetDragAttributes ( theDrag, &currDragFlags ); |
return !(currDragFlags & kDragInsideSenderWindow); |
} |
// |
// DragTracker is called by the drag manager whenever a drag is |
// over one of the application's windows. Upon entry, the Drag |
// Manager has already set the current port current to ÔtheWindowÕ. |
pascal OSErr DragTracker ( DragTrackingMessage theMessage, WindowRef theWindow, |
void* handlerRefCon, DragReference theDrag ) |
{ |
#pragma unused(handlerRefCon) |
RgnHandle tempRgn; |
Boolean mouseInList; |
OSErr err; |
err = noErr; |
switch ( theMessage ) |
{ |
case kDragTrackingEnterHandler: |
// Any initialization for this window handler. |
gHasAcceptableDrag = DragItemsAreAcceptable ( theDrag ); |
gHasHilitedList = false; |
// Let the drag manager know if we can't accept this drag |
if ( !gHasAcceptableDrag ) |
err = dragNotAcceptedErr; |
break; |
case kDragTrackingEnterWindow: |
case kDragTrackingInWindow: |
case kDragTrackingLeaveWindow: |
// 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 ( gHasAcceptableDrag ) |
{ |
// Unless the mouse is leaving the visible area of the |
// window, check if it's in the window's content region |
mouseInList = false; |
if ( theMessage != kDragTrackingLeaveWindow ) |
{ |
if ( DragIsNotInSourceWindow ( theDrag ) ) |
{ |
Point localPt; |
(void) GetDragMouse ( theDrag, &localPt, 0L ); |
GlobalToLocal ( &localPt ); |
mouseInList = PtInList ( localPt, GetWListRef ( theWindow ) ); |
} |
} |
// If the mouse is in the list and it isn't hilited... |
if ( mouseInList && !gHasHilitedList ) |
{ |
Rect nuSpaceRect; |
GetListRect ( &nuSpaceRect, GetWListRef ( theWindow ) ); |
tempRgn = NewRgn ( ); |
RectRgn ( tempRgn, &nuSpaceRect ); |
// ...draw the hilight... |
if ( ShowDragHilite ( theDrag, tempRgn, true ) == noErr ) |
// ... and remember it's now hilited |
gHasHilitedList = mouseInList; |
DisposeRgn ( tempRgn ); |
} |
// else if the mouse is not in the list and the window is hilited... |
else if ( !mouseInList && gHasHilitedList ) |
// ...erase the hilight... |
if ( HideDragHilite ( theDrag ) == noErr ) |
// ...remember that nothing is hilited |
gHasHilitedList = false; |
} |
break; |
// do nothing for the leaveHandler message |
case kDragTrackingLeaveHandler: |
break; |
// let the drag manager know if we didn't recognize the message |
default: |
err = paramErr; |
} |
return err; |
} |
// |
// DragReceiver is called by the drag manager whenever an |
// item is dropped on one of the application's windows. |
// |
pascal OSErr DragReceiver ( WindowRef theWindow, void *handlerRefCon, DragReference theDrag ) |
{ |
#pragma unused (theWindow, handlerRefCon) |
Boolean bMove; |
ItemReference itemRef; |
Size dataSize; |
OSErr err = noErr; |
unsigned short numItems, counter; |
tDragData theData; |
int16 mouseDownModifiers, mouseUpModifiers; |
if (!DragItemsAreAcceptable(theDrag) || (gHasHilitedList == false)) |
return dragNotAcceptedErr; |
err = GetDragModifiers ( theDrag, nil, &mouseDownModifiers, &mouseUpModifiers ); |
if ( err ) goto CleanupAndBail; |
bMove = !((mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey)); |
CountDragItems(theDrag, &numItems); |
for (counter = 1; counter <= numItems; counter++) |
{ |
err = GetDragItemReferenceNumber(theDrag, counter, &itemRef); |
if (err != noErr) goto CleanupAndBail; |
dataSize = sizeof ( tDragData ); |
err = GetFlavorDataSize ( theDrag, itemRef, kCreatorCode, &dataSize ); |
if ( dataSize == sizeof ( tDragData ) ) |
err = GetFlavorData ( theDrag, itemRef, kCreatorCode, &theData, &dataSize, 0 ); |
// We're going to first remove the drag hilite from here, because |
// not doing so would result in a cleared out list rect, and when |
// we go through the trackingLeaveWindow message up above, the |
// HideDragHilite() call would draw the border again (since it's drawn |
// in XOr mode). |
if ( gHasHilitedList ) |
{ |
if ( HideDragHilite ( theDrag ) == noErr) |
// remember that nothing is hilited |
gHasHilitedList = false; |
} |
if ( bMove ) |
err = MoveWindowFragment ( theData.theWindow, theData.theIndex, theWindow ); |
else |
err = CopyWindowFragment ( theData.theWindow, theData.theIndex, theWindow ); |
} |
CleanupAndBail: |
return err; |
} |
// |
// This is the Drag Manager's SendDataProc. It's called after a successful drag, when |
// the target application wants some data that was promised by the source application. |
// |
pascal OSErr SendDataProc ( FlavorType theType, void* dragSendRefCon, |
ItemReference theItemRef, DragReference theDragRef ) |
{ |
// Gotcha: If we had just dragged out to the Finder, and we had a windowKind of |
// 20, the system would have crashed by now. Why? The Finder uses a windowKind |
// of 20, and thinks this is a drag from one of its windows. It then starts |
// interpreting the window's refCon and inevitably doesn't like what it finds. |
// A windowKind of 20 is now reserved for use by the system. |
OSErr result = noErr; |
FSSpec locationSpec; |
tHeaderHan theHeader = nil; |
hdrHand theResource = nil; |
// We use the file type for the HFSPromise flavor type |
// and we don't handle any other pomised flavour types. |
if ( theType != kCFragLibraryFileType ) |
cantGetFlavorErr; |
result = CreateTemporaryFile ( &locationSpec ); |
if ( result == noErr ) |
{ |
OSErr theErr; |
int16 theIndex = 0; |
int16 theRef, saveFile; |
WindowRef theWindow; |
ListRef theList; |
tWindowInfoPtr theInfo; |
theWindow = (WindowRef) dragSendRefCon; |
theInfo = (tWindowInfoPtr) GetWRefCon ( theWindow ); |
theList = GetWListRef ( theWindow ); |
theHeader = (tHeaderHan) NewHandleClear ( sizeof ( tHeader ) ); |
theErr = MemError ( ); |
if ( theErr ) goto CleanupAndBail; |
(*theHeader)->version = 1; // Current version number |
// First, we'll add the content to the file |
while ( GetSelection ( theList, &theIndex ) ) |
{ |
int16 itemIndex; |
itemIndex = GetIndexFromNthWindowItem ( theWindow, theIndex ); |
theErr = CopyFragment ( (tHeaderHan) theInfo->dataHandle, &theInfo->fileSpec, |
itemIndex, theHeader, &locationSpec ); |
if ( theErr ) goto CleanupAndBail; |
theIndex++; |
} |
theResource = (hdrHand) NewHandleClear ( offsetof ( cfrgHeader, arrayStart ) ); |
(*theResource)->version = 1; // Current version number |
theErr = BuildResource ( (tHeaderHan) theHeader, (Handle) theResource ); |
if ( theErr ) goto CleanupAndBail; |
saveFile = CurResFile ( ); |
theRef = FSpOpenResFile ( &locationSpec, fsRdWrPerm ); |
// If the file is already open, it may not be the current resource file |
UseResFile ( theRef ); |
AddResource ( (Handle) theResource, kCFragResourceType, kCFragResourceID, "\p" ); |
UpdateResFile ( theRef ); |
ReleaseResource ( (Handle) theResource ); |
CloseResFile ( theRef ); |
UseResFile ( saveFile ); |
// Now, set the flavor data of our kCFragLibraryFileType flavor |
// with an FSSpec to the new file. |
result = SetDragItemFlavorData ( theDragRef, theItemRef, theType, |
&locationSpec, sizeof ( FSSpec ), 0L ); |
} |
// Any errors? Return a cantGetFlavorErr |
if ( result ) |
result = cantGetFlavorErr; |
return result; |
CleanupAndBail: |
// We'll leave the temp file for the system to handle. It |
// could still be useful, if only for debugging purposes. |
if ( theHeader ) |
DisposeHandle ( (Handle) theHeader ); |
if ( theResource ) |
{ |
if ( IsAResource ( (Handle) theResource ) ) |
ReleaseResource ( (Handle) theResource ); |
else |
DisposeHandle ( (Handle) theResource ); |
} |
return cantGetFlavorErr; |
} |
// |
// This routine just promises the Drag Manager that we'll create a file |
// if the user drags out to the Finder |
// |
OSErr AddHFSPromise ( DragReference theDrag, ItemReference theItem ) |
{ |
OSErr theErr; |
PromiseHFSFlavor thePromise; |
// Here's what we're going to promise to create in our send proc |
thePromise.fileType = kCFragLibraryFileType; |
thePromise.fileCreator = kFourQuestionMarks; |
thePromise.fdFlags = 0; |
thePromise.promisedFlavor = kCFragLibraryFileType; |
// The promised flavor can be anything, just as long as we add a drag item |
// that has the same type, and set it in our send proc. Failure to do this |
// will cause the zoomback. |
theErr = AddDragItemFlavor ( theDrag, theItem, flavorTypePromiseHFS, &thePromise, |
sizeof ( PromiseHFSFlavor ), 0L ); |
if ( theErr == noErr ) |
// Here's the promised flavor we're going to deliver |
theErr = AddDragItemFlavor ( theDrag, theItem, kCFragLibraryFileType, nil, 0L, 0L ); |
return theErr; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-30