ADC Home > Reference Library > Technical Notes > Carbon > User Experience >
| 
 | 
| 
 Introducing  | 
| Note: This can be especially confusing when an
                     application promises  In this Technote, I make an effort to avoid using the word "promise" in more than one sense at a time; nevertheless, read carefully. | 
| Important: The situation may even be worse. This document came into existence several years after the release of the Drag Manager. As a result, it's been difficult for early adopters of the Drag Manager to implement these flavors properly. And there may be applications which aren't as conscientious as yours. You should be extra careful to check error return values and build assertions into your code, so that your app is ready to cope with other apps which unexpectedly deviate from the conventions documented in this Note. | 
flavorTypeHFSIn theory, usingflavorTypeHFS data appears
         simple, but in practice there are a few tricks you need to
         know. The following sections document a few of those tricks
         for you. For quick reference, here's a copy of the HFSFlavor
         declaration from <Drag.h>: 
| struct HFSFlavor
{
    OSType          fileType;       // file type
    OSType          fileCreator;    // file creator
    unsigned short  fdFlags;        // Finder flags
    FSSpec          fileSpec;       // file system specification
};
typedef struct HFSFlavor HFSFlavor;
 | 
To originate a drag containing flavorTypeHFS
         data, you must first declare an HFSFlavor record. This
         record contains an FSSpec and a few other fields which allow
         some potential drag receivers to avoid calling FSpGetFInfo.
         
The next step is to initialize the fileSpec field
         appropriately, then decide whether the data refers to a
         file. If so, simply set the fileType, fileCreator, and
         fdFlags fields to match the appropriate information for the
         file. If the flavorTypeHFS data refers to a directory or
         volume, set the fileType and fileCreator fields in the
         HFSFlavor record according to Table 1: 
Table 1. The fileType and fileCreator fields
         for the HFSFlavor record
| entity type | 
 | 
 | 
| directory (folder) | 
 | 
 | 
| volume (disk) | 
 | 
 | 
These values are a hint to potential drag receivers that they are dealing with something other than a file. They are the same as the ones you would use in your application's bundle resource to let Finder know your app will accept folders and disks dropped onto your application's icon.
| pascal OSErr MakeHFSFlavor
    (short vRefNum, long dirID, ConstStr255Param path,
        HFSFlavor register *hfsFlavorP)
{
    OSErr err = noErr;
    if (!(err = FSMakeFSSpec
        (vRefNum,dirID,path,&(hfsFlavorP->fileSpec))))
    {
        CInfoPBPtr cipbp =
            (CInfoPBPtr) NewPtrClear (sizeof (*cipbp));
        if (!(err = MemError ( )))
        {
            cipbp->hFileInfo.ioVRefNum  =
                hfsFlavorP->fileSpec.vRefNum;
            cipbp->hFileInfo.ioDirID    =
                hfsFlavorP->fileSpec.parID;
            cipbp->hFileInfo.ioNamePtr  =
                hfsFlavorP->fileSpec.name;
            if (!(err = PBGetCatInfoSync (cipbp)))
            {
                hfsFlavorP->fdFlags =
                    cipbp->hFileInfo.ioFlFndrInfo.fdFlags;
                if (hfsFlavorP->fileSpec.parID == fsRtParID)
                {
                    hfsFlavorP->fileCreator     = 'MACS';
                    hfsFlavorP->fileType        = 'disk';
                }
                else if (cipbp->hFileInfo.ioFlAttrib & ioDirMask)
                {
                    hfsFlavorP->fileCreator     = 'MACS';
                    hfsFlavorP->fileType        = 'fold';
                }
                else
                {
                    hfsFlavorP->fileCreator     =
                        cipbp->hFileInfo.ioFlFndrInfo.fdCreator;
                    hfsFlavorP->fileType        =
                        cipbp->hFileInfo.ioFlFndrInfo.fdType;
                }
            }
            DisposePtr ((Ptr) cipbp);
            if (!err) err = MemError ( );
        }
    }
    return err;
} | 
| Snippet 1. Deciding how to set the  | 
Dragging flavorTypeHFS data from your
         application to Finder has always supposed to have been
         possible. However, Finder bugs have prevented most
         applications from successfully using this feature.
From Finder's perspective, there are two cases for
         receiving flavorTypeHFS data. The second case
         is more interesting.
flavorTypeHFS data, Finder simply moves the
            file to the drop location.Finder is AppleEvent-intensive. It sends itself
         AppleEvents to order itself to do all sorts of things,
         including displaying the progress window for copying files.
         However, Finder's drag-receiving code mistakenly sends these
         particular AppleEvents to the front process instead of the
         current process. The front process is generally the
         application which originated the drag. Since the application
         does not have handlers for these events, AppleEvent Manager
         returns an error to Finder's AESend call and Finder cancels
         the entire operation.
Until this bug is fixed, your application can work around the problem by "handling" these AppleEvents. On systems under which Finder has been fixed, the handler will simply lie dormant in your app, because the AppleEvents will be sent to Finder, not your app. Unfortunately, it doesn't do any good to "reflect" these events back to the Finder; trust us, we've tried. This means you'll have to do without the progress dialog, but this is better than abject failure.
| pascal OSErr BogusFinderEventHandler
    (const AppleEvent *, AppleEvent *, long)
{
    return noErr; // just drop that bad boy on the floor
}
pascal OSErr InstallBogusFinderEventHandler (void)
{
    OSErr err = noErr;
    static AEEventHandlerUPP bogusFinderEventHandlerUPP;
    if (!bogusFinderEventHandlerUPP)
    {
        bogusFinderEventHandlerUPP =
            NewAEEventHandlerProc (BogusFinderEventHandler);
        if (!bogusFinderEventHandlerUPP)
            err = nilHandleErr;
        else
        {
            err = AEInstallEventHandler
                ('cwin','****',bogusFinderEventHandlerUPP,0,false);
            if (err)
            {
                DisposeRoutineDescriptor (bogusFinderEventHandlerUPP);
                bogusFinderEventHandlerUPP = nil;
            }
        }
    }
    return err;
} | 
| Snippet 2. Receiving bogus AppleEvents from Finder | 
flavorTypeHFSReceiving flavorTypeHFS, often from Finder,
         is much like receiving any other flavor of data. However, be
         aware that some applications will offer you a truncated
         record; they do not provide the unused bytes at the end of
         the name field of the fileSpec field of the HFSFlavor
         record. (This is a bug in the sending application, but if
         it's not your app, you probably don't have an opportunity to fix it.)
| static pascal Size MinimumBytesForFSSpec (const FSSpec *fss)
{
    // callers can and do assume this does not move memory
    return sizeof (*fss) - sizeof (fss->name) + *(fss->name) + 1;
} | 
| Snippet 3. Calculating minimum bytes for
          (called from snippets #4 , #6, and #14) | 
| pascal OSErr GetHFSFlavorFromDragReference
    (DragReference dragRef, ItemReference itemRef,
        HFSFlavor *hfsFlavor)
{
    OSErr err = noErr;
    Size size = sizeof (*hfsFlavor);
    err = GetFlavorData
        (dragRef,itemRef,flavorTypeHFS,hfsFlavor,&size,0);
    if (!err)
    {
        Size minSize = sizeof (*hfsFlavor) -
            sizeof (hfsFlavor->fileSpec);
        minSize += MinimumBytesForFSSpec (&(hfsFlavor->fileSpec));
            // see snippet 3 for MinimumBytesForFSSpec
        if (size < minSize)
            err = cantGetFlavorErr;
    }
    return err;
} | 
| Snippet 4. Extracting  | 
flavorTypePromiseHFSUsing flavorTypePromiseHFS data is significantly more
         complicated than using flavorTypeHFS data. The chief area of
         confusion is centered on the multi-part nature of
         flavorTypePromiseHFS data. For quick reference, here's a
         copy of the PromiseHFSFlavor declaration from
         <Drag.h>: 
| struct PromiseHFSFlavor
{
    OSType          fileType;           // file type
    OSType          fileCreator;        // file creator
    unsigned short  fdFlags;            // Finder flags
    FlavorType      promisedFlavor;     // promised flavor
};
typedef struct PromiseHFSFlavor PromiseHFSFlavor; | 
flavorTypePromiseHFSBefore calling TrackDrag, your application
         should call AddDragItemFlavor twice, passing
         the same ItemReference value both times, once
         for each part of the data.
For the first call, declare a record of type
         PromiseHFSFlavor and put 'fssP'
         (0x66737350) in the promisedFlavor field. The
         Drag Manager Programmer's Guide tells you to
         put any value you like into promisedFlavor, but
         we're now recommending this specific value. (Details can be
         found below; if your application already uses something
         else, don't worry too much right now unless it's
         'rWm1'.) Fill in the other fields of the
         PromiseHFSFlavor record appropriately and add
         the record to the DragReference, passing
         flavorTypePromiseHFS for the
         FlavorType parameter.
With the second call to AddDragItemFlavor,
         pass 'fssP' for the FlavorType
         parameter. Pass 0 for the dataPtr and
         dataSize parameters to set up a promise to be
         kept later.
| pascal OSErr AddDragItemFlavorTypePromiseHFS
    (DragReference dragRef, ItemReference itemRef,
        OSType fileType, OSType fileCreator,
            UInt16 fdFlags, FlavorType promisedFlavor)
{
    OSErr err = noErr;
    PromiseHFSFlavor phfs;
    phfs.fileType           = fileType;
    phfs.fileCreator        = fileCreator;
    phfs.fdFlags            = fdFlags;
    phfs.promisedFlavor     = promisedFlavor;
    if (!(err = AddDragItemFlavor
        (dragRef,itemRef,flavorTypePromiseHFS,
            &phfs,sizeof(phfs),flavorNotSaved)))
    {
        err = AddDragItemFlavor
            (dragRef,itemRef,promisedFlavor,nil,0,flavorNotSaved);
    }
    return err;
} | 
| Snippet 5. Adding  | 
| Important: | 
| Note: | 
         Add any other flavors you might want to
         provide in this DragReference, and you're ready
         to call TrackDrag.
         
When Drag Manager requests a FlavorType equal to the
         promisedFlavor field of your flavorTypePromiseHFS data, it's
         your cue to keep your promise by delivering the 
         file. Keeping the promise involves finding out where the
         drag receiver wants the file to end up, deciding where to
         create the file, and creating the file. You'll do this in
         your DragSendDataProc associated with the DragReference.
         
First, your DragSendDataProc will need to find out where
         the drag receiver wants the file. You'll need to call
         GetDropLocation, which will produce an AEDesc
         record. The type of the data found in this record is defined
         by the drag receiver. Finder, for example, provides typeAlias
         data. To convert this data to an
         FSSpec, coerce its type to typeFSS and copy the FSSpec data
         out of the resulting descriptor.
| pascal OSErr GetDropDirectory (DragReference dragRef, FSSpecPtr fssOut)
{
    OSErr err = noErr;
    AEDesc dropLocAlias = { typeNull, nil };
    if (!(err = GetDropLocation (dragRef,&dropLocAlias)))
    {
        if (dropLocAlias.descriptorType != typeAlias)
            err = paramErr;
        else
        {
            AEDesc dropLocFSS = { typeNull, nil };
            if (!(err = AECoerceDesc
                (&dropLocAlias,typeFSS,&dropLocFSS)))
            {
                // assume MinimumBytesForFSSpec does not move memory
                FSSpecPtr fss = (FSSpecPtr) *(dropLocFSS.dataHandle);
                BlockMoveData (fss,fssOut,MinimumBytesForFSSpec(fss));
                // see snippet 3 for MinimumBytesForFSSpec
                err = AEDisposeDesc (&dropLocFSS);
            }
        }
        if (dropLocAlias.dataHandle)
        {
            OSErr err2 = AEDisposeDesc (&dropLocAlias);
            if (!err) err = err2;
        }
    }
    return err;
} | 
| Snippet 6. Extracting the drop folder | 
| Note: | 
If the drop location data is not of typeAlias, the call to 
         AECoerceDesc will fail. 
         Your DragSendDataProc will probably want to provide no data
         and return an error in this situation. However, be aware
         that applications other than Finder are free to provide a
         drop location of typeAlias (and some even do), so don't rely
         on typeAlias signifying that Finder is the drop receiver.
         
| Note: | 
| Note: | 
Once you've decided where to put the file, you can create it by calling a function like this one:
| pascal OSErr CreatePromisedFileOrFolder
   (const PromiseHFSFlavor *phfs, const FSSpec *fss,
      ScriptCode scriptTag)
{
   OSErr err = noErr;
   if (phfs->promisedFlavor == kPromisedFlavorFindFile)
      err = paramErr;
   else if (phfs->fileType == 'disk')
      err = paramErr;
   else if (phfs->fileType == 'fold')
      err = CreatePromisedFolder (phfs,fss,scriptTag); // see Snippet 9
   else
      err = CreatePromisedFile (phfs,fss,scriptTag); // see Snippet 8
   return err;
} | 
| Snippet 7. Creating the promised file or folder | 
| static pascal CreatePromisedFile
    (const PromiseHFSFlavor *phfs, const FSSpec *fss,
        ScriptCode scriptTag)
{
    OSErr err = noErr;
    if (!(err = FSpCreate
        (fss,phfs->fileCreator,phfs->fileType,scriptTag)))
    {
        if (phfs->fdFlags)
        {
            FInfo finderInfo;
            if (!(err = FSpGetFInfo (fss,&finderInfo)))
            {
                finderInfo.fdFlags = phfs->fdFlags;
                err = FSpSetFInfo (fss,&finderInfo);
            }
        }
    }
    return err;
} | 
| Snippet 8. Called by snippet #7 | 
| static pascal CreatePromisedFolder
    (const PromiseHFSFlavor *phfs, const FSSpec *fss,
        ScriptCode scriptTag)
{
    OSErr err = noErr;
    long newDirID; // scratch
    if (!(err = FSpDirCreate (fss,scriptTag,&newDirID)))
    {
        if (phfs->fdFlags)
        {
            DInfo finderInfo;
            // see Appendix B for FSpGetDInfo and FSpSetDInfo
            if (!(err = FSpGetDInfo (fss,&finderInfo)))
            {
                finderInfo.frFlags = phfs->fdFlags;
                err = FSpSetDInfo (fss,&finderInfo);
            }
        }
    }
    return err;
} | 
| Snippet 9. Called by Snippet #7 | 
Once the file is created, you may or may not want to
         write its contents in your DragSendDataProc. If the file is
         large or your app needs some time to generate the data that
         will be in the file, you may want to defer writing the file.
         Since Process Manager context switches are disabled during Drag
         Manager callbacks, other applications would get no execution
         time if you were to spend time writing the file, even if it
         were safe to periodically call WaitNextEvent, which it is
         not.
In this situation, you'll want to open the file in your
         DragSendDataProc and leave it open. In addition, set a 
         flag to tell another part of your
         application it needs to write the file. After TrackDrag
         returns, have that part of your app write the file with
         periodic calls to WaitNextEvent.
Once (and only if) the file has been successfully
         created, you should let the drag receiver know what the
         filename was and where the file was created. To do this,
         call SetItemFlavorData. For the FlavorType parameter, pass
         the value of the promisedFlavor field of the
         PromiseHFSFlavor record. For the flavor data, pass an FSSpec
         record describing the name and location of the file. The
         promisedFlavor data should always be an FSSpec, not an
         HFSFlavor. This snippet consists of simple glue which adds
         the data correctly: 
| pascal OSErr SetPromisedHFSFlavorData
    (DragReference dragRef, ItemReference itemRef,
        const PromiseHFSFlavor *phfs, const FSSpec *fss)
{
    return SetDragItemFlavorData
        (dragRef,itemRef,phfs->promisedFlavor,fss,sizeof(*fss),0);
}
 | 
| Snippet 10. Adding the promised  | 
If you need to provide a DragReference which refers to an
         existing file or files, then if at all possible you should
         be sending flavorTypeHFS. But if you discover a compelling
         reason to send flavorTypePromiseHFS instead, make sure you: 
         
promisedFlavor field of your PromiseHFSFlavor
            record to 'rWm1' (0x72576D31).DragSendDataProc for
            'rWm1' data and GetDropLocation produces 
            an AEDesc whose
            descriptorType field contains typeNull, provide the
            original location of the file.GetDropLocation produces an AEDesc whose
            descriptorType field contains typeAlias, copy the file
            into the drop location. 'rWm1' is only a hint to the drag
            receiver, and the drag receiver may not take the hint.| Important: | 
The section Coping with Find File elsewhere in this Note details why these steps are necessary. The following snippet implements a decision tree which tells its caller whether to copy a file the caller is dropping:
| pascal OSErr ShouldCopyToDropLoc
    (DragReference dragRef, FlavorType promisedFlavor,
        Boolean *shouldCopy)
{
    OSErr err = noErr;
    AEDesc dropLoc = { typeNull, nil };
    *shouldCopy = false;
    if (!(err = GetDropLocation (dragRef,&dropLoc)))
    {
        if (dropLoc.descriptorType == typeAlias)
        {
            // no hint or receiver missed it
            *shouldCopy = true;
        }
        else if (dropLoc.descriptorType != typeNull)
        {
            // unknown drop location descriptor type
            err = paramErr;
        }
        else if (promisedFlavor != kPromisedFlavorFindFile)
        {
            // null descriptor but no hint intended (DragPeeker)
            err = dirNFErr;
        }
        if (dropLoc.dataHandle)
        {
            OSErr err2 = AEDisposeDesc (&dropLoc);
            if (!err) err = err2;
        }
    }
    return err;
} | 
| Snippet 11. Deciding whether to copy a dropped file | 
flavorTypePromiseHFSMost applications have no need to receive
         flavorTypePromiseHFS data; flavorTypeHFS should suffice for
         most needs. More senders provide flavorTypeHFS, although
         there is at least one important application (Find File)
         which provides flavorTypePromiseHFS. In any case, seriously
         consider flavorTypeHFS before investing effort in
         flavorTypePromiseHFS.
In your drag tracking handler, you may retrieve the
         flavorTypePromiseHFS data, which is a PromiseHFSFlavor, but
         don't try to retrieve the promisedFlavor data. Your drag
         tracking handler can't know whether a given window in your
         application will be the ultimate receiver of the data
         - the ultimate receiver might be another window in
         your app or one of the windows of another app. If your drag
         tracking handler were to ask for the promisedFlavor
         data, Drag Manager would call the sender's SendDataProc, and
         the data would thereafter be cached in the DragReference.
         Consequently, other potential receivers would get the
         cached data and the sender would not have a chance to adjust
         it according to the receiver's drop location.
In your drag receive handler, it's safe to retrieve both
         the flavorTypePromiseHFS data and the promisedFlavor data.
         Before requesting the promisedFlavor data, however, make
         sure to call SetDropLocation. The next snippet is a function
         which administrates this process. Note that the folder
         parameter can be NIL; this means the caller
         supports Find File; we'll explain how this works and why
         you'd want to do it a little later.
| pascal OSErr ReceivePromisedFile
    (DragReference dragRef, ItemReference itemRef,
        HFSFlavor *hfsFlavor, const FSSpec *folder)
{
    OSErr err = noErr;
    if (folder)
        // see Snippet 13 for SetDropFolder
        err = SetDropFolder (dragRef,folder);
    if (!err)
    {
        // we'll explain 'isSupposedlyFromFindFile' later
        Boolean isSupposedlyFromFindFile = (folder == nil);
        err = GetHFSFlavorFromPromise // see snippet 14
            (dragRef, itemRef, hfsFlavor, isSupposedlyFromFindFile);
    }
    return err;
} | 
| Snippet 12. Receiving  | 
This part of receiving flavorTypePromiseHFS
         is relatively easy. First, create an alias to the drop
         location, which for flavorTypePromiseHFS should
         always be a directory. Next, copy the alias into an
         AEDesc. Finally, call
         SetDropLocation. This procedure is demonstrated
         in the next snippet.
| static pascal OSErr SetDropFolder
    (DragReference dragRef, const FSSpec *folder)
{
    OSErr err = noErr;
    AliasHandle aliasH;
    if (!(err = NewAliasMinimal (folder,&aliasH)))
    {
        HLockHi ((Handle) aliasH);
        if (!(err = MemError ( )))
        {
            Size size = GetHandleSize ((Handle) aliasH);
            if (!(err = MemError ( )))
            {
                AEDesc dropLoc;
                if (!(err = AECreateDesc
                    (typeAlias,*aliasH,size,&dropLoc)))
                {
                    OSErr err2;
                    err = SetDropLocation (dragRef,&dropLoc);
                    err2 = AEDisposeDesc (&dropLoc);
                    if (!err) err = err2;
                }
            }
        }
        DisposeHandle ((Handle) aliasH);
        if (!err) err = MemError ( );
    }
    return err;
} | 
| Snippet 13. Called by Snippet #12 | 
Many drag receivers would like to be able to receive data
         dragged from a Find File results window. The first flavor
         most developers would look for in the DragReference would be
         flavorTypeHFS. However, Find File provides
         flavorTypePromiseHFS instead, in an attempt to work around
         Finder bugs mentioned elsewhere in this Technote.
Find File's workaround works pretty well within
                  the scope of Finder, but it doesn't work very well
                  with many other applications which receive
                  flavorTypePromiseHFS. You'll remember that
                  flavorTypePromiseHFS is a promise to create a file
                  which doesn't exist yet, but Find File's results
                  window contains only existing files. Right away
                  there's semantic conflict. Let's look at a concrete
                  example to see how this conflict can cause
                  problems: 
If an email application were to accept
                  flavorTypePromiseHFS as an enclosure to a message
                  and assumed that the drag sender were honoring the
                  semantics of flavorTypePromiseHFS as documented in
                  this Technote, the email app would probably want to
                  set the drop location to its outgoing spool folder
                  and delete the file when the associated message
                  were successfully sent. After all, the semantics of
                  flavorTypePromiseHFS are to create a file expressly
                  for the exclusive use of the receiving app.
However, if instead Find File were merely to move a pre-existing file into that spool folder, the email app might well be deleting the user's only copy of that data, and at the very least Find File would be moving a file to a place the user isn't likely to expect or understand. This is in fact what Find File does.
Why? Well, since Finder is buggy, Find File
                  convinces Finder a drop has occurred and then
                  proceeds to delete the dropped file and send
                  AppleEvents to Finder to induce it to do what it
                  should have done with flavorTypeHFS on its own. 
                  The only data Find
                  File really wants from Finder is the drop location.
                  
Regardless of any of the background information in this sidebar, your application should conform as strictly as possible to the rest of this Technote.
The Find File engineers didn't just bludgeon the Finder into working the way they wanted; they also provided a way for other applications to receive HFS-related drags sensibly. It just hasn't been documented until now.
In your drag tracking handler, retrieve the
         flavorTypePromiseHFS data and compare its promisedFlavor
         field to 'rWm1' (0x72576D31). This is the value which Find
         File always uses. If promisedFlavor has this value, set a
         flag to remind you not to call SetDropLocation later.
In your drag receive handler, you'd normally call
         SetDropLocation before asking for the promisedFlavor data.
         However, if you're receiving flavorTypePromiseHFS data from
         Find File, skip this step before asking the Drag Manager for
         the promisedFlavor data (and, of course, in this case
         promisedFlavor will always have the value 'rWm1'). 
         This will
         produce FSSpec data without inducing Find File to move or
         copy the file.
And now we can see why the value of promisedFlavor is
         important; if it's 'rWm1', the data comes from Find File,
         and if the value is anything else (we've recommended 'fssP'
         [0x66737350]; but if your program already uses
         something else, don't worry about it), the data comes from
         some other application. Applications other than Find File
         should conform to the semantics of
         flavorTypePromiseHFS as documented in this Technote.
The next snippet shows how to retrieve both flavors, with some extra checking thrown in to make sure nobody is confused about Find File.
| static pascal OSErr GetHFSFlavorFromPromise
    (DragReference dragRef, ItemReference itemRef,
        HFSFlavor *hfs, Boolean isSupposedlyFromFindFile)
{
    OSErr             err = noErr;
    PromiseHFSFlavor  phfs;
    Size              size = sizeof (phfs);
    err = GetFlavorData
        (dragRef,itemRef,flavorTypePromiseHFS,&phfs,&size,0);
    if (!err)
    {
        if (size != sizeof (phfs))
            err = cantGetFlavorErr;
        else
        {
            Boolean isFromFindFile =
                phfs.promisedFlavor == kPromisedFlavorFindFile;
            if (isSupposedlyFromFindFile != isFromFindFile)
                err = paramErr;
            else
            {
                size = sizeof (hfs->fileSpec);
                err = GetFlavorData
                    (dragRef,itemRef,phfs.promisedFlavor,
                        &(hfs->fileSpec),&size,0);
                if (!err)
                {
                    Size minSize = MinimumBytesForFSSpec
                        (&(hfs->fileSpec));
                    // see snippet 3 for MinimumBytesForFSSpec
                    if (size < minSize)
                        err = cantGetFlavorErr;
                    else
                    {
                        hfs->fileType     = phfs.fileType;
                        hfs->fileCreator  = phfs.fileCreator;
                        hfs->fdFlags      = phfs.fdFlags;
                    }
                }
            }
        }
    }
    return err;
} | 
| Snippet 14. Called by Snippet #12 | 
There are two file system-oriented flavor types
         associated with the Drag Manager. One,
         flavorTypeHFS, is a relatively simple flavor
         which can be handled like most others except for some simple
         workarounds for bugs in Finder. The other,
         flavorTypePromiseHFS, is probably the most
         complex flavor type developers will encounter and requires a
         high degree of care, attention to detail, and tolerance for
         intrusive workarounds to implement correctly.
Here are some important lessons worth repeating:
flavorTypeHFS. For files
            which don't yet exist but you're willing to create, use
            flavorTypePromiseHFS.flavorTypePromiseHFS. They're both
            promises, but they are significantly different kinds of promises.GetDropLocation and SetDropLocation are your friends.flavorTypePromiseHFS for a file you
            plan to delete, make sure you do the right thing with
            Find File to avoid destroying data the user wanted to keep.The Drag Manager Programmer's Guide, available on the Developer CD Series Mac OS SDK disc. In addition, you can download it here.
AEDesc is an AppleEvent Manager data
            structure documented starting on page 3-12 of 
            
            Inside Macintosh: Interapplication Communication. 
The Appendices to this Technote contain code snippets which are necessary for a full understanding of other snippets in the Technote but would have obstructed the flow of the main text stream.
This is a utility function called by the functions in
         Appendices B and C. It allocates and populates a
         CInfoPBRec so that it contains information on
         the given directory. The caller is expected to dispose the
         CInfoPBRec if the function does not return an error.
| static pascal OSErr FSpGetDirInfo
    (const FSSpec *spec, CInfoPBPtr *cipbpp)
{
    OSErr err = noErr;
    CInfoPBPtr pbp = (CInfoPBPtr) NewPtrClear (sizeof (*pbp));
    *cipbpp = nil;
    if (!(err = MemError ( )))
    {
        pbp->dirInfo.ioVRefNum = spec->vRefNum;
        pbp->dirInfo.ioDrDirID = spec->parID;
        pbp->dirInfo.ioNamePtr = (StringPtr) spec->name;
        err = PBGetCatInfoSync (pbp);
        if (!err && !(pbp->hFileInfo.ioFlAttrib & ioDirMask))
            err = dirNFErr;
        if (err)
            DisposePtr ((Ptr) pbp);
        else
            *cipbpp = pbp;
    }
    return err;
} | 
These functions are intended to follow the same API as
         FSpGetFinfo and FSpSetFInfo. They both call
         FSpGetDirInfo, which can be found in Appendix
         A.
| static pascal OSErr FSpGetDInfo
    (const FSSpec *spec, DInfo *fndrInfo)
{
    OSErr err = noErr;
    CInfoPBPtr cipbp;
    if (!(err = FSpGetDirInfo (spec,&cipbp)))
    {
        *fndrInfo = cipbp->dirInfo.ioDrUsrWds;
        DisposePtr ((Ptr) cipbp);
        if (!err) err = MemError ( );
    }
    return err;
}
static pascal OSErr FSpSetDInfo
    (const FSSpec *spec, const DInfo *fndrInfo)
{
    OSErr err = noErr;
    CInfoPBPtr cipbp;
    if (!(err = FSpGetDirInfo (spec,&cipbp)))
    {
        cipbp->dirInfo.ioDrUsrWds    = *fndrInfo;
        cipbp->dirInfo.ioDrDirID    = spec->parID;
        err = PBSetCatInfoSync (cipbp);
        DisposePtr ((Ptr) cipbp);
        if (!err) err = MemError ( );
    }
    return err;
} | 
This function returns the directory ID of a given folder.
         It calls FSpGetDirInfo, which can be found in Appendix A.
         
| pascal OSErr GetDirectoryID (const FSSpec *spec, long *dirID)
{
    OSErr err = noErr;
    CInfoPBPtr cipbp;
    if (!(err = FSpGetDirInfo (spec,&cipbp)))
    {
        *dirID = cipbp->dirInfo.ioDrDirID;
        DisposePtr ((Ptr) cipbp);
        if (!err) err = MemError ( );
    }
    return err;
} | 
| 
 | Acrobat version of this Note (92K). | |
| 
 | Acrobat version of Drag Manager Programmer's Guide (378K) | |
| 
 | Binhexed FinderDragPro Metrowerks Project (220K) | 
| 
 |