GetIconSuiteFromFinder.c

/*
    File:       FinderDragPro.c
    
    Description:    Sample file illustrating drag and drop techniques for use
                with file system objects.  This file illustrates how applications
                can use drag and drop commands in a way compatible with current
                and past versions of the Finder.
 
    Author:     Pete Gontier
 
    Copyright:  Copyright: © 1999 by Apple Computer, Inc.
                all rights reserved.
    
    Disclaimer: You may incorporate this sample code into your applications without
                restriction, though the sample code has been provided "AS IS" and the
                responsibility for its operation is 100% yours.  However, what you are
                not permitted to do is to redistribute the source as "DSC Sample 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 Code, but that you've made changes.
    
    Change History (most recent first):
    06/09/95    NG  last touched
    08/23/96    PG  stolen from Nitin's old FinderDrag project
    04/21/97    PG  pascal programs can be written in any language
*/
    
 
#ifndef __ICONS__
#   include <Icons.h>
#endif
 
#ifndef __ALIASES__
#   include <Aliases.h>
#endif
 
#ifndef __AEREGISTRY__
#   include <AERegistry.h>
#endif
 
#ifndef __GESTALT__
#   include <Gestalt.h>
#endif
 
#ifndef __AEPACKOBJECT__
#   include <AEPackObject.h>
#endif
 
#ifndef __AEOBJECTS__
#   include <AEObjects.h>
#endif
 
#ifndef __ERRORS__
#   include <Errors.h>
#endif
 
#ifndef __TEXTUTILS__
#   include <TextUtils.h>
#endif
 
#ifndef __PROCESSES__
#   include <Processes.h>
#endif
 
#include "GetIconSuiteFromFinder.h"
 
#define require(x,y) do { if (!(x)) goto y; } while (0)
 
AEDesc pFinderTarget;
 
//----------------------------------------------------------------------------
// FinderIsRunning
//
// Walk the Process Mgr list to check if the Finder is running
//----------------------------------------------------------------------------
 
static Boolean FinderIsRunning (void)
{
    OSErr           err;
    ProcessInfoRec  pInfo;
    ProcessSerialNumber psn;
    Boolean         foundIt;
 
    foundIt = false;
    psn.highLongOfPSN = 0; psn.lowLongOfPSN = kNoProcess;
 
    while ((foundIt == false) && (GetNextProcess(&psn) == noErr)) {
        pInfo.processName       = NULL;
        pInfo.processAppSpec    = NULL;
        pInfo.processInfoLength = sizeof(ProcessInfoRec);
    
        err = GetProcessInformation(&psn, &pInfo);
    
        if ((err == noErr) 
            && (pInfo.processSignature == 'MACS') 
            && (pInfo.processType == 'FNDR'))
            
            foundIt = true;
    }
    
    return foundIt;
}
 
//----------------------------------------------------------------------------
// HaveScriptableFinder
//
// We have it if the Gestalt bit is set and the Finder is running
//----------------------------------------------------------------------------
 
static Boolean HaveScriptableFinder(void)
{
    long        response;
    Boolean     haveScriptableFinder;
    OSErr       err;
 
    haveScriptableFinder = false;
    
    err = Gestalt(gestaltFinderAttr, &response);
    require(err == noErr, Gestalt);
 
    if ((response & (1 << gestaltOSLCompliantFinder)) && (FinderIsRunning()))
        haveScriptableFinder = true;
 
Gestalt:
    return haveScriptableFinder;
}
 
//----------------------------------------------------------------------------
// SendAppleEvent
//----------------------------------------------------------------------------
 
static OSErr SendAppleEvent(AppleEvent *ae, AppleEvent *reply, AESendMode sendMode)
{
    AppleEvent  throwAwayReply;
    OSErr       err;
 
    if (reply == NULL) {
        err = AESend(ae, &throwAwayReply, sendMode, 
                    kAENormalPriority, kAEDefaultTimeout, NULL, NULL);
        if (err == noErr)
            AEDisposeDesc(&throwAwayReply);
    }
    else
        err = AESend(ae, reply, sendMode, 
                    kAENormalPriority, kAEDefaultTimeout, NULL, NULL);
 
    return err;
}
 
 
//----------------------------------------------------------------------------
// MakeAppleEvent
//----------------------------------------------------------------------------
 
static pascal OSErr MakeAppleEvent
    (AEEventClass aeClass, AEEventID aeID, AEDesc *target, AppleEvent *ae)
{
    OSErr err = noErr;
 
    if (target->dataHandle == nil)
    {
        DescType finderSig = 'MACS';
 
        err = AECreateDesc(typeApplSignature, (Ptr) &finderSig,
            sizeof(DescType), target);
    }
 
    if (!err)
        err = AECreateAppleEvent(aeClass, aeID, target, 
            kAutoGenerateReturnID, kAnyTransactionID, ae);
    
    return err;
}
 
//----------------------------------------------------------------------------
// MakeSpecifierForSelection
//----------------------------------------------------------------------------
 
static OSErr MakeSpecifierForSelection(AEDesc *selectionSpecifier)
{
    OSErr       err;
    DescType    descData;
    AEDesc      keyData, nullDescriptor;
    
    nullDescriptor.descriptorType = typeNull;
    nullDescriptor.dataHandle = NULL;
 
    //
    // Make a descriptor whose type is 'typeType' and whose
    // contents are 'pSelection' (defined in FinderRegistry.h).
    // This descriptor specifies the property of the null container
    // that we are interested in--in this case, the selection.
    //
    descData = pSelection;
    err = AECreateDesc(typeType, (Ptr) &descData, sizeof(DescType), &keyData);
    require(err == noErr, AECreateDesc);
    
    err = CreateObjSpecifier(cProperty, &nullDescriptor, formPropertyID,
                                    &keyData, true, selectionSpecifier);
 
AECreateDesc:
    return err;
}
 
//----------------------------------------------------------------------------
// MakeSpecifierForFile
//----------------------------------------------------------------------------
static pascal OSErr MakeSpecifierForFile(FSSpecPtr hfsObj, AEDesc *fileSpecifier)
{
    OSErr       err;
    AEDesc      nullDesc, hfsData;
    AliasHandle fileAlias;
 
    //
    // Create the file descriptor with the FSSpec passed in.
    //
    err = NewAlias(NULL, hfsObj, &fileAlias);
    require(err == noErr, NewAlias);
 
    HLock((Handle) fileAlias);
    err = AECreateDesc(typeAlias, (Ptr) *fileAlias, 
                GetHandleSize((Handle) fileAlias), &hfsData);
    HUnlock((Handle) fileAlias);
    DisposeHandle((Handle) fileAlias);
    require(err == noErr, AECreateDesc);
 
    //
    // Make the object specifier with a null container
    // (i.e., "file of <null>", or just "file")
    //
    nullDesc.descriptorType = typeNull;
    nullDesc.dataHandle = NULL; 
    err = CreateObjSpecifier(typeWildCard, &nullDesc, 
                formAlias, &hfsData, false, fileSpecifier);
 
AECreateDesc:
NewAlias:
    return err;
}
 
//----------------------------------------------------------------------------
// MakePropertySpecifierForSpecifier
//----------------------------------------------------------------------------
 
static pascal OSErr MakePropertySpecifierForSpecifier
    (DescType property, AEDesc *ofSpecifier, AEDesc *propertySpecifier)
{
    OSErr       err;
    AEDesc      keyData;
    
    //
    // Create a 'type' AEDesc with the desired property
    //
    err = AECreateDesc(typeType, (Ptr) &property, sizeof(DescType), &keyData);
    require(err == noErr, AECreateDesc);
 
    //
    // With it create a property specifier for the object specifier
    // passed to us.
    //
    err = CreateObjSpecifier(cProperty, ofSpecifier, 
                formPropertyID, &keyData, false, propertySpecifier);
 
    (void) AEDisposeDesc(&keyData); 
AECreateDesc:
    return err;
}
 
//----------------------------------------------------------------------------
// GetSizeFromIconType
//----------------------------------------------------------------------------
 
static pascal Size GetSizeFromIconType (DescType iconType)
{
    Size    size = -1;
 
    switch (iconType) {
        case large8BitData:
            size = kLarge8BitIconSize;
            break;
        case large4BitData:
            size = kLarge4BitIconSize;      
            break;
        case large1BitMask:
            size = kLargeIconSize;
            break;
        case small8BitData:
            size = kSmall8BitIconSize;
            break;
        case small4BitData:
            size = kSmall4BitIconSize;
            break;
        case small1BitMask:
            size = kSmallIconSize;
            break;
    }
    return size;
}
 
 
//----------------------------------------------------------------------------
// BuildIconSuiteFromAEDesc
//
// OK, this uses the Apple Event Manager to pick the icon data out of the
// 'ifam' AEDesc.
//----------------------------------------------------------------------------
 
static pascal OSErr BuildIconSuiteFromAEDesc
    (Boolean largeIcons, Handle *iconSuite, AEDesc *iconFam)
{
    OSErr       err;
    Handle      suite, icon;
    AERecord    rec;
    Ptr         buffer;
    DescType    large[3] = {large8BitData, large4BitData, large1BitMask};
    DescType    small[3] = {small8BitData, small4BitData, small1BitMask};
    DescType    *type, iconType, typeCode;
    long        count;
    Size        maxSize, size, iconSize;
    Boolean     maskAdded;
    DescType    maskType;
 
    maskAdded = false;
    suite = NULL;
    maxSize = kLarge8BitIconSize;
 
    if (largeIcons == true) {
        type = large;
        maskType = large1BitMask;
    }
    else {
        type = small;
        maskType = small1BitMask;
    }
 
    buffer = NewPtr(maxSize);
    require(buffer != NULL, NewPtr);
    
    err = NewIconSuite(&suite);
    require(err == noErr, NewIconSuite);
    
    err = AECoerceDesc(iconFam, typeAERecord, (AEDesc *) &rec);
    require(err == noErr, AECoerceDesc);
    
    for (count = 0; count < 3; count ++) {
        //
        // loop through the icons and grab the data from the AERecord for
        // each type of icon we're interested in.
        //
        iconType = type[count];
        size = GetSizeFromIconType(iconType);
        err = AEGetKeyPtr(&rec, iconType, iconType, &typeCode, 
                        buffer, maxSize, &iconSize);
    
        if (err == noErr) {
            //
            // We don't set the error code for this unless the NewHandle
            // call fails, because it's possible that the 'ifam' doesn't
            // have an icon for one that we're interested in.
            //
            icon = NewHandle(size);
    
            if (icon != NULL) {
                //
                // OK, the memory alloc succeeded and we have data. Copy
                // it into the allocated icon and add it to the suite. 
                // Set atLeastOne to true, to indicate later that we did
                // in fact add at least one icon to this suite.
                //
                BlockMoveData(buffer, *icon, size);
                err = AddIconToSuite(icon, suite, iconType);
                if ((err == noErr) && (iconType == maskType))
                    maskAdded = true;
            }
            else
                err = memFullErr;
        }
    }
 
    (void) AEDisposeDesc(&rec);
 
AECoerceDesc:
    if ((err != noErr) || (maskAdded == false)) {
        //
        // There was either an error in a memory allocation,  or something
        // else went wrong (like no mask was added to the suite).  Get
        // rid of the partially created suite.
        //
        DisposeIconSuite(suite, true);
        suite = NULL;
    }
 
NewIconSuite:
    DisposePtr(buffer);
 
NewPtr:
    *iconSuite = suite;
    return err;
}
 
//----------------------------------------------------------------------------
// GetIconSuiteFromFinder
//
// Send a GetData AE for the 'ifam'
//----------------------------------------------------------------------------
OSErr GetIconSuiteFromFinder (FSSpecPtr hfsObj, Handle *iconSuite)
{
    OSErr       err;
    AppleEvent  finderEvent, replyEvent;
    AEDesc      fileSpecifier, iconPropertySpecifier;
    DescType    returnType;
    Size        returnSize;
    long        returnLong;
    AEDesc      iconFamily;
 
    //
    // Set up our locals for easy cleanup
    //
    *iconSuite = NULL;
 
    //
    // Make sure the Finder is scriptable and is running.
    //
    err = paramErr;
    require(HaveScriptableFinder() == true, HaveScriptableFinder);
 
    //
    // Make a GetData Apple event to send to the Finder
    //
    err = MakeAppleEvent(kAECoreSuite, kAEGetData, &pFinderTarget, 
                &finderEvent);
    require(err == noErr, MakeAppleEvent);
    
    //
    // Make an object specifier for the interesting file
    //
    err = MakeSpecifierForFile(hfsObj, &fileSpecifier);
    require(err == noErr, MakeSpecifierForFile);
    
    //
    // Make an icon family property specifier for the file
    //
    err = MakePropertySpecifierForSpecifier(pIconBitmap, &fileSpecifier, 
                &iconPropertySpecifier);
    require(err == noErr, MakePropertySpecifierForSpecifier);
    
    //
    // Stuff it in the Apple event and send it
    //
    err = AEPutParamDesc(&finderEvent, keyDirectObject, &iconPropertySpecifier);
    require(err == noErr, AEPutParamDesc);
 
    err = SendAppleEvent(&finderEvent, &replyEvent,
                kAEWaitReply + kAECanInteract + kAECanSwitchLayer);
    require(err == noErr, SendAppleEvent);
    
    //
    // Now the Finder may have sent us an error number
    //
    err = AEGetParamPtr(&replyEvent, keyErrorNumber, typeLongInteger, 
                        &returnType, &returnLong, sizeof(long), &returnSize);
 
    if (err == noErr)
        err = (OSErr) returnLong;
    else if (err == errAEDescNotFound)
        err = noErr;
 
    //
    // If not, get the icon family and build an icon suite
    //
 
    if (!err)
    {       
        err = AEGetParamDesc(&replyEvent, keyDirectObject, typeWildCard, &iconFamily);  
        require(err == noErr, AEGetParamDesc);
        err = BuildIconSuiteFromAEDesc(true, iconSuite, &iconFamily);
    }
 
    //
    // Clean up and exit
    //
 
    (void) AEDisposeDesc(&iconFamily);
 
AEGetParamDesc:
    (void) AEDisposeDesc(&replyEvent);
 
SendAppleEvent:
AEPutParamDesc:
    (void) AEDisposeDesc(&iconPropertySpecifier);
 
MakePropertySpecifierForSpecifier:
    (void) AEDisposeDesc(&fileSpecifier);
 
MakeSpecifierForFile:
    (void) AEDisposeDesc(&finderEvent);
 
MakeAppleEvent:
HaveScriptableFinder:
    return err;
}