SnapshotSample.c

/*
    File:       SnapshotSample.c
 
    Contains:   Code for saving and restoring desktop icons.
                This sample, when run the first time, creates a 
                snapshot file in the Preferences folder which contains a list 
                of the names and locations fo the items on the 
                desktop.
    
                Running the sample again will read the snapshot
                file and tell the Finder to position the items
                at the locations read from the snapshot file.
    
                Sample does not read/set location of Trash,
                nor of mounted volumes.
    
                Sample is provided as is... I didn't get much 
                chance to test beyond the standard, "oh, this
                works!" stage.
    
                The original Pascal version was written by
                Quinn "The Eskimo!" and released as a Freeware
                utility.  This DTS sample was created by
                Deborah Grits.  This version was tidied up
                and released by Quinn "The Eskimo!".
    
    Oh what a tangled web we weave...
 
    Written by: Quinn "The Eskimo!" 
 
    Copyright:  Copyright © 1996-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):
                7/21/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
    
#include <AppleEvents.h>
#include <Errors.h>
#include <Events.h>
#include <Fonts.h>
#include <Gestalt.h>
#include <Memory.h>
#include <Menus.h>
#include <OSUtils.h>
#include <QDOffscreen.h>
#include <QuickDraw.h>
#include <Resources.h>
#include <Script.h>
#include <ToolUtils.h>
#include <Windows.h>
#include <AEPackObject.h>
#include <AERegistry.h>
#include <AEObjects.h>
#include <Folders.h>
#include <TextEdit.h>
#include <Dialogs.h>
 
//////////////////////////////////////////////////////////////////////////
// Basic Utilities
 
static OSErr FindProcessByTypeAndCreator(OSType typeToFind, OSType creatorToFind, ProcessSerialNumber *processSN)
    // Runs through the process list looking for the indicated application.
{
    OSErr err;
        ProcessInfoRec infoRecToFill;
        
    err = noErr;
    processSN->lowLongOfPSN = kNoProcess;
    processSN->highLongOfPSN = kNoProcess;
    infoRecToFill.processInfoLength = sizeof(ProcessInfoRec);
    infoRecToFill.processName = nil;
    infoRecToFill.processAppSpec = nil;
    do {
        err = GetNextProcess(processSN);
        if (err == noErr) {
            GetProcessInformation(processSN, &infoRecToFill);
        }
    } while ((infoRecToFill.processSignature != creatorToFind || infoRecToFill.processType != typeToFind) ||
                   err != noErr);
    
    return(err);
}
 
//////////////////////////////////////////////////////////////////////////
// Global Types
 
// The snapshot file is made up of records of SnapRecord type.
// An empty record (ie with name == "") is used to indicate
// the end of the file.
 
typedef struct 
{
    Str63 name;
    Point loc;
} SnapRecord, *SnapRecordPtr, **SnapRecordHandle;
 
 
//////////////////////////////////////////////////////////////////////////
// Global Variables
 
// Used to tell the main event loop to quit.
 
static Boolean gQuit = false;
 
// An empty AppleEvent Descriptor.
 
const static AEDesc gNullDesc = {typeNull, nil};
 
//////////////////////////////////////////////////////////////////////////
// Restoring Snapshots
 
static OSErr SetItemPosition(Str255 fname, Point dest, AEDesc *setDataEvent)
    // Sends an AppleEvent to the targetDesc (which should target the Finder)
    //  to set the position of the item whose name is fname to the position dest.
{
    OSErr       err;
    AEDesc      fileNameDesc        = gNullDesc;
    AEDesc      rootDesc            = gNullDesc;
    AEDesc      propertyDescriptor  = gNullDesc;
    AEDesc      objParentDesc       = gNullDesc;
    AEDesc      objDesc             = gNullDesc;
    AEDesc      reply               = gNullDesc;
    AEDesc      newData             = gNullDesc;
    DescType    procDescData        = 'posn';
 
    err = AECreateDesc(typeChar, &fname[1], fname[0], &fileNameDesc);
        
    // Create objParentDesc to specify the parent object, ie item "xxxx" of application
    if (err == noErr) {
        err = CreateObjSpecifier(cObject, &rootDesc, formName, &fileNameDesc, false, &objParentDesc);
    }
 
    // Create propertyDescriptor to hold the property specifier, ie position
    if (err == noErr) {
        err = AECreateDesc(typeType, &procDescData, sizeof(procDescData), &propertyDescriptor);
    }
    
    // Create objDesc to reference the data to set, ie position of item "xxx" of application
    if (err == noErr) {
        err = CreateObjSpecifier(cProperty, &objParentDesc, formPropertyID, &propertyDescriptor, false, &objDesc);
    }
 
    // Create on descriptor to hold the new data, ie the new icon position.
    if (err == noErr) {
        err = AECreateDesc(typeQDPoint, (void*)&dest, sizeof(dest), &newData);
    }
 
    // Fill out the parameters, putting the objDesc into the direct object and the new data
    //  into the data parameter.
    if (err == noErr) {
        err = AEPutParamDesc(setDataEvent, keyDirectObject, &objDesc);
    }
    if (err == noErr) {
        err = AEPutParamDesc(setDataEvent, keyAEData, &newData);
    }
 
    // Send the event.
    if (err == noErr) {
        err = AESend(setDataEvent, &reply, kAEWaitReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
        
        // Some code that I enable when I'm debugging.
        
        if (false) {
            long errNum;
            Str255      errStr;
            DescType    junkType;
            Size        realSize;
            
            err = AEGetParamPtr(&reply, keyErrorNumber, typeInteger, &junkType, &errNum, sizeof(errNum), &realSize);
            err = AEGetParamPtr(&reply, keyErrorString, typeChar, &junkType, &errStr[1], 255, &realSize);
            errStr[0] = realSize;
        }
    }
 
    // Clean up all those messy descriptors.  Don't you just love AppleEvents (-:
    AEDisposeDesc(&rootDesc);
    AEDisposeDesc(&fileNameDesc);
    AEDisposeDesc(&objParentDesc);
    AEDisposeDesc(&propertyDescriptor);
    AEDisposeDesc(&objDesc);
    AEDisposeDesc(&newData);
    AEDisposeDesc(&reply);
    
    return err;
}
 
static OSErr RestoreSnapshot (FSSpec snapFss)
    // Restore the snapshot file specified by snapFss.
{
    OSErr               err;
    ProcessSerialNumber finderPSN;
    AEDesc              targetDesc      = gNullDesc;
    AEDesc              setDataEvent    = gNullDesc;
    short               snapFileRef;
    Boolean             done;
    SnapRecord          aSnapRecord;
    long                byteCount;
 
    snapFileRef = 0;
    
    // Find the Finder's ProcessSerialNumber and create the targetDesc for it.
    err = FindProcessByTypeAndCreator('FNDR', 'MACS', &finderPSN);
    if (err == noErr) {
        err = AECreateDesc(typeProcessSerialNumber, (Ptr)&finderPSN, sizeof(finderPSN), &targetDesc);
    }
    // Create the AppleEvent.
    if (err == noErr) {
            err = AECreateAppleEvent(kAECoreSuite, kAESetData, &targetDesc,
                                                                kAutoGenerateReturnID, kAnyTransactionID, &setDataEvent);
    }   
    
    // Open the file.
    if (err == noErr) {
        err = FSpOpenDF(&snapFss, fsRdPerm, &snapFileRef);
    }
    if (err == noErr) {
        
        // Read the records in the file, repositioning the icons as we go.
        done = false;
        do {
            byteCount = sizeof(SnapRecord);
            err = FSRead(snapFileRef, &byteCount, (void*) &aSnapRecord);
            
            if (err == noErr) {
                // We're done if we hit the empty string.
                done = (aSnapRecord.name[0] == 0);
                
                if (!done) {
                    // Reposition the icon.
                    err = SetItemPosition(aSnapRecord.name, aSnapRecord.loc, &setDataEvent);
                }
            }
        } while (!done);
    }
    
    if (err == eofErr) {
        err = noErr;
    }
 
    // clean up
    if (snapFileRef != 0) {
        (void) FSClose(snapFileRef);
    }
    AEDisposeDesc(&setDataEvent);
    AEDisposeDesc(&targetDesc);
        
    return err;
}
 
//////////////////////////////////////////////////////////////////////////
// Creating Snapshots
 
static OSErr WriteFileRecord(short snapFileRef, Str63 itemName, Point itemPosition)
    // Add a new record to the snapshot file.
{
    OSErr       err;
    SnapRecord  aSnapRecord;
    long        byteCount;
    
    BlockMoveData(itemName, aSnapRecord.name, sizeof(aSnapRecord.name));
    aSnapRecord.loc = itemPosition;
    
    byteCount = sizeof(SnapRecord);
    err = FSWrite(snapFileRef, &byteCount, (void*) &aSnapRecord);
    
    return err;
}
 
static OSErr CreateSnapshotFile(FSSpec snapFss)
    // Add the positions for the icons on the desktop to the snapshot file snapFss.
{
    OSErr       err;
    short       snapFileRef;
    short       deskVRefNum;
    long        deskDirID;
    CInfoPBRec  pb;
    short       fileIndex;
    Point       folderOrigin;
    Point       itemPosition;
    Str63       itemName;
    UInt32      systemVersion;
 
    snapFileRef = 0;
    
    // Find the desktop folder.
    err = FindFolder(kOnSystemDisk, kDesktopFolderType, true, &deskVRefNum, &deskDirID);
    
    // Get the origin for the desktop folder.
    pb.dirInfo.ioNamePtr = nil;
    pb.dirInfo.ioDrDirID = deskDirID;
    pb.dirInfo.ioVRefNum = deskVRefNum;
    pb.dirInfo.ioFDirIndex = -1;        // get information about ioDirID
    err = PBGetCatInfoSync(&pb);
    if (err == noErr) {
        folderOrigin.h = 0;
        folderOrigin.v = 0;
        if (Gestalt(gestaltSystemVersion, (long *) &systemVersion) != noErr || systemVersion < 0x0800) {
            // Evil fudge factor is needed for Finder 7.x.  Finder 8.0 does not fudge.
            folderOrigin.v = folderOrigin.v - 20;
        }
    }
 
    // Open up the snapshot file.
    if (err == noErr) {
        err = FSpOpenDF(&snapFss, fsRdWrPerm, &snapFileRef);
    }
 
    // Loop through each file in the folder, adding it to the snapshot file.
    if (err == noErr) {
        fileIndex = 1;
        do {
                pb.dirInfo.ioNamePtr = (StringPtr) itemName;
                pb.dirInfo.ioDrDirID = deskDirID;
                pb.dirInfo.ioVRefNum = deskVRefNum;
                pb.dirInfo.ioFDirIndex = fileIndex;
                err = PBGetCatInfoSync(&pb);
                if (err == noErr) {
                    itemPosition = pb.hFileInfo.ioFlFndrInfo.fdLocation;
                    SubPt(folderOrigin, &itemPosition);
                    err = WriteFileRecord(snapFileRef, itemName, itemPosition);
                }
                fileIndex += 1;
        } while (err == noErr);
        
        // Write the sentinel to the end of the file.
        if (err == fnfErr) {
            itemName[0] = 0;
            itemPosition.h = 0;
            itemPosition.v = 0;
            err = WriteFileRecord(snapFileRef, itemName, itemPosition);
        }
    }
        
    // Clean up.
    if (snapFileRef != 0) {
        (void) FSClose(snapFileRef);
    }
        
    return err;
}
 
//////////////////////////////////////////////////////////////////////////
// Main Line
 
static void InitToolbox()
    // Standard Macintosh toolbox init.
{
    InitGraf(&qd.thePort);
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs(nil);
    MaxApplZone();
    
    MoreMasters();
    MoreMasters();
    MoreMasters();
 
    InitCursor();
    FlushEvents(0, everyEvent); 
}
 
void main(void)
    // The application's main entry point.
{
    OSErr   err;
    FSSpec  snapFss;
    long    gestaltResponse;
 
    // First, a quick check to make sure we have Scriptable Finder.  If we don't
    //  we bail quickly.
    
    if ((Gestalt(gestaltFinderAttr, &gestaltResponse) != noErr) || ((gestaltResponse & (1 << gestaltOSLCompliantFinder)) == 0)) {
        DebugStr("\pWhoops, no Scriptable Finder.");
        ExitToShell();
    }
 
    // Now bring up the standard Mac toolbox.
    InitToolbox();
    
    // Find the Preferences folder.
    err = FindFolder(kOnSystemDisk, kPreferencesFolderType, false, &(snapFss.vRefNum), &(snapFss.parID));
 
    if (err == noErr) {
 
        // Open the snapshot file in the Preferences folder.
        //  Should get this from a resource, of course.
        
        (void) FSMakeFSSpec(snapFss.vRefNum, snapFss.parID, "\pDesktop Snap", &snapFss);
        err = FSpCreate(&snapFss, 'FR¿G', 'FR¿G', 0);
        
        if (err == dupFNErr) {                          // If we already have a snapshot file,
            err = RestoreSnapshot(snapFss);             //  restore icon positions,
        } else if (err == noErr) {                      // otherwise make a snapshot file.
            // Create the snapshot file for the desktop
            err = CreateSnapshotFile(snapFss);
        }
    }
    
    if (err != noErr) {
        DebugStr("\pSnapshotter failed.");
    }
}