CustomPutSuffix.c

/*
    File:       CustomPutSuffix.c
 
    Contains:   This demonstrates a CustomPutDialog with an Save button that checks
                to see if several files (with the name filename+suffix) already exist.
                a dialog hook procedure is responsible for presenting separate "Replace?"                   dialogs.  When the Save button is hit and, if the user allows the save,
                the Save action is map to a Cancel action but the reply is marked as good.
 
    Written by: David Hayward   
 
    Copyright:  Copyright © 1993-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/1/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                5/12/95                     updated project for Metrowerks
                5/3/93                      first draft
*/
 
 
#include <stddef.h>
 
#include <QuickDraw.h>
#include <Memory.h>
#include <Files.h>
#include <Errors.h>
#include <Fonts.h>
#include <Resources.h>
#include <StandardFile.h>
#include <Files.h>
#include <Dialogs.h>
#include <TextUtils.h>
 
#include "InitMac.h"
 
 
/**\
|**| ==============================================================================
|**| DEFINES
|**| ==============================================================================
\**/
#define outputDlogID        128
#define outputQuit          1
#define outputAgain         2
 
#define replaceDlogID       6045
#define replaceCancel       1
#define replaceOK           2
 
#define SuffixID            128
 
 
/**\
|**| ==============================================================================
|**| TYPEDEFS
|**| ==============================================================================
\**/
 
/*------------------------------------------------------------------------------*\
    myData
 *------------------------------------------------------------------------------*
        the 'myData' structure is passed to CustomPutFile() so that 
        MyDlgHook() can access the list of suffixes and return the results 
\*------------------------------------------------------------------------------*/
typedef struct myData
{
    StandardFileReply       *replyPtr;  /* we also need to pass in the SFR so that */
                                        /* MyDlgHook() knows what user specified in */
                                        /* the CustomPutFile dialog */
    short                   num;        /* the number of entries in the array */
    
    struct
    {                                   /* struct for each suffix to check */
        Str63  suffix;                  /* suffix string to append */
        FSSpec spec;                    /* FSSpec for the file */
        FInfo  info;                    /* returned info and err just in case */
        OSErr  err;                     /* we need this information later */
    }                       files[];    /* variable length array of the above */
} myData, *myDataPtr;
 
 
 
/**\
|**| ==============================================================================
|**| FUNCTION PROTOTYPES
|**| ==============================================================================
\**/
void            AppendStrToStr      ( StringPtr dst, StringPtr src, unsigned char maxDstLen );
void            AppendStrToHdl      ( Handle dst, StringPtr src );
pascal short    MyDlgHook           ( short item, DialogPtr theDialog, void *data );
short           myReplaceAlert      ( myDataPtr dataPtr );
short           outputDlog          ( myDataPtr dataPtr );
void            initializeData      ( myDataPtr *dataPtr, StandardFileReply *replyPtr );
void            main                ( void );
 
 
/*------------------------------------------------------------------------------*\
    AppendStrToStr()
 *------------------------------------------------------------------------------*
        utility function to append one string to another
        this function should be improved to handle errors
\*------------------------------------------------------------------------------*/
void AppendStrToStr ( StringPtr dst, StringPtr src, unsigned char maxDstLen )
{
    short       offset = dst[0]+1;
    short       size   = src[0];
    
    if ( dst[0] + src[0] > maxDstLen)           // make sure were not too big
        size = maxDstLen - dst[0];              // you should return a warning here
    BlockMove( src+1, dst+offset, size);
    dst[0] += size;
}
 
    
/*------------------------------------------------------------------------------*\
    AppendStrToHdl()
 *------------------------------------------------------------------------------*
        utility function to append a string to a handle
        this function should be improved to handle errors
\*------------------------------------------------------------------------------*/
void AppendStrToHdl ( Handle dst, StringPtr src )
{
    short       size = src[0];
    PtrAndHand( src+1, dst, size);
}
 
    
/*------------------------------------------------------------------------------*\
    MyDlgHook()
 *------------------------------------------------------------------------------*
        the dialog hook procedure responsible for presenting my own "Replace?" 
        dialogs when the Save button is hit and, if the user allows the save,
        then map the Save action to a Cancel action but mark the reply as good.
\*------------------------------------------------------------------------------*/
pascal short MyDlgHook ( short item, DialogPtr theDialog, void *data )
{
    long                        refCon;
    short                       r;
 
    refCon = GetWRefCon((WindowPtr)theDialog);          /* get the refCon of the current dialog */
 
    if (refCon == sfMainDialogRefCon)                   /* if its the "Save AsÉ" dialog */
    {
        if (item == sfItemOpenButton)                   /* if user hit Save button */
        {
            r = myReplaceAlert((myDataPtr)data);        /* present my "Replce? dialogs */
            if ( r == replaceCancel )                   /* if user hit a Cancel button */
                item = sfHookNullEvent;                 /* then do nothing */
            else                                        /* if user hit replace button */
            {                                           /* then user wants to save so...*/
                item  = sfItemCancelButton;             /* change to Cancel action but */ 
                ((myDataPtr)data)->replyPtr->sfGood = 1;/* mark reply as good */
            }
        }
    }
    
    return item;
}
 
 
/*------------------------------------------------------------------------------*\
    myReplaceAlert()
 *------------------------------------------------------------------------------*
        this routine, called from MyDlgHook(), presents a "Replce? dialog
        for any filename+suffix that already exist.  If one of these
        dialogs is canceles then this procedure returns 'cancel'
\*------------------------------------------------------------------------------*/
short myReplaceAlert ( myDataPtr dataPtr )
{
    short       i, item;
    OSErr       err;
    FSSpec      spec;
    FInfo       info;
    
    for (i=0; i<dataPtr->num; i++)                      /* loop thru each suffix in dataPtr ... */
    {
        spec = dataPtr->replyPtr->sfFile;               /* spec starts out as the FSSpec (vRefNum, */
                                                        /* parID & name) the user specified in the */
                                                        /* CustomPutFile dialog  */
        
        AppendStrToStr( spec.name,
                        dataPtr->files[i].suffix, 63);  /* append suffix to filename */
 
        err = FSpGetFInfo(&spec, &info);                /* try to get info for this spec */
        
        dataPtr->files[i].spec = spec;                  /* save the spec, info & err for this */
        dataPtr->files[i].info = info;                  /* file in dataPtr just in case we need  */
        dataPtr->files[i].err = err;                    /* this information later */ 
 
        if (err == 0)                                   /* filename+suffix already exists so.. */ 
        {
            ParamText( spec.name, nil, nil, nil);       /* setup param text for "Replace?" alert*/
            item = Alert(replaceDlogID,nil);            /* put up alert */
            if (item == replaceCancel)                  /* if user hit cancel button */
                return replaceCancel;                   /* then stop and return 'cancel' */
        }
        else if (err == fnfErr)                         /* if filename+suffix doesn't exist */
        {
            /* all you need to do in here is make sure that filename+suffix */
            /* isn't a directory and put up an an alert if it is */
        }
        else                                            /* if some other err was returned ... */
        {
            /* here you need to handle other errors such as 'badNamErr' */
        }
    }
    return replaceOK;
}
 
 
/*------------------------------------------------------------------------------*\
    outputDlog()
 *------------------------------------------------------------------------------*
        output vital stats of the StandardFileReply to an dialog box
\*------------------------------------------------------------------------------*/
short outputDlog ( myDataPtr dataPtr )
{
    DialogPtr   dlg;
    Rect        iRect;
    Handle      iHndl;
    short       iType;
    short       item;
    short       i;
    
    dlg = GetNewDialog(outputDlogID, nil, (WindowPtr)-1);
    
    GetDialogItem( dlg, 3, &iType, &iHndl, &iRect );            /* get handle to static text field */
 
    if (dataPtr->replyPtr->sfGood == 0)                 /* if reply is not good */
        AppendStrToHdl (iHndl, "\pUser canceled.");
    else
    {
        for (i=0; i<dataPtr->num; i++)                  /* loop thru each suffix in dataPtr ... */
        {
            AppendStrToHdl( iHndl, dataPtr->files[i].spec.name);    /* append filename */
            
            if ( dataPtr->files[i].err == 0)            /* if filename+suffix already exists */
                AppendStrToHdl( iHndl, "\p (exists)");  /* append (exists) */
            else if ( dataPtr->files[i].err != fnfErr)
                AppendStrToHdl( iHndl, "\p (error)");   /* append (error) */
    
            AppendStrToHdl( iHndl, "\p\r");             /* append CR */
        }
    }
    
    ShowWindow( dlg );
    
    do
    {
        ModalDialog(0,&item);
    } while ( (item != outputAgain) && (item != outputQuit));
    
    DisposeDialog(dlg);
    
    return item;
}
 
 
/*------------------------------------------------------------------------------*\
    initializeData()
 *------------------------------------------------------------------------------*
        initialize data stucture to be passed to CustomPutFile
        this means alocating a pointer of the right size and filling 
        in the suffix strings from the apps resource fork 
\*------------------------------------------------------------------------------*/
void initializeData ( myDataPtr *dataPtr, StandardFileReply *replyPtr )
{
    short   i=0, num=0;
    Str255  temp;
    
    do                                              /* figure out how many suffixes */ 
    {                                               /* are in the apps resource fork */
        GetIndString( temp, SuffixID, num+1);
        num++;
    } while (temp[0] !=0);                          /* if temp[0]==0 then no more */
    num--;                                          /* counted 1 too many so decrement */
    
                                                    /* allocate myDataPtr stucture */
    *dataPtr = (myDataPtr) NewPtrClear ( offsetof(myData,files[num]) );
    
    (**dataPtr).replyPtr = replyPtr;                /* fill in StandardFileReply ptr */ 
    (**dataPtr).num = num;                          /* fill in num of suffixes */ 
 
    for (i=0; i<num; i++)                           /* fill in suffix strings */
        GetIndString( (**dataPtr).files[i].suffix, SuffixID, i+1);
}
 
 
/*------------------------------------------------------------------------------*\
    main()
 *------------------------------------------------------------------------------*
        initialize mamagers and the keep doing 
        CustomPutFile until the user has had enough
        the structure 'dataPtr' is passed to CustomPutFile() so that 
        MyDlgHook() can access the list of suffixes and return the results 
\*------------------------------------------------------------------------------*/
void main ( void )
{
    myDataPtr           dataPtr;
    StandardFileReply   reply;
    Point               where = {-1,-1};            /* center dialog on main screen */
    
    InitToolBox(2);
    
    initializeData (&dataPtr, &reply);
    
    do
    {
        CustomPutFile(  "\pSave files as:",         /* the prompt string */
                        "\pUntitled",               /* the default filename */
                        &reply,                     /* the StandardFileReply structure */
                        0,                          /* the default dialog */
                        where,                      /* position of dialog on screen */
                        NewDlgHookYDProc(MyDlgHook),/* the Dialog Hook procedure */
                        nil,                        /* no modal dialog filterProc */
                        nil,                        /* no activeListPtr */
                        nil,                        /* no activateProc */
                        dataPtr                     /* pass in myDataPtr struct so that */
                      );                            /* dlgHook() can access its contents */
    }                                                           
    while (outputDlog(dataPtr)==outputAgain);       /* repeat until user has had enough */
    
    DisposePtr( (Ptr)dataPtr);
}