Source/SVEditFile.c

/*
    File:       SVEditFile.c
 
    Contains:   
 
    Written by: Original version by Jon Lansdell and Nigel Humphreys.
                            3.1 updates by Greg Sutton. 
 
    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):
                7/19/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
#include <Errors.h>
#include <Resources.h>
#include <Desk.h>
#include <DriverServices.h>
#include <AppleEvents.h>
#include <AERegistry.h>
#include <StandardFile.h>
#include "SVEditFile.h"
#include "Offscreen.h"
#include <TextUtils.h>
 
 
 
 
 
/**-----------------------------------------------------------------------
        Name:           FileError
        Purpose:        Puts up an error alert.
    -----------------------------------------------------------------------**/
 
    
#pragma segment File
 
pascal void FileError(Str255 s, Str255 f)
  {
    short    alertResult;
 
    SetCursor(&qd.arrow);
        ParamText(s, f, (unsigned char *)"", (unsigned char *)"");
    alertResult = Alert(ErrorAlert, nil);
    }
 
/**-----------------------------------------------------------------------
        Name:           DoClose
        Purpose:        Closes a window.
    -----------------------------------------------------------------------**/
 
#pragma segment File
 
pascal OSErr DoClose(WindowPtr aWindow,Boolean canInteract,DescType dialogAnswer)
{
    DPtr    aDocument;
    short   alertResult;
    OSErr   myErr;
                        
    myErr = noErr;
        
    if (gWCount>0)
    {
        aDocument = DPtrFromWindowPtr(aWindow);
            
        if (aDocument->dirty)
            if (canInteract && (dialogAnswer==kAEAsk))
            {
                alertResult = DoFileDialog ( kSaveDontsaveDialog, aWindow );
                switch ( alertResult )
                {
                    case kStdOkItemIndex:
                        if (aDocument->everSaved == false)
                        {
                            myErr = GetFileNameToSaveAs(aDocument);
                            if (myErr == noErr)
                                myErr = DoSave(aDocument, aDocument->theFSSpec);
                                                                                                    }
                        else
                            myErr = SaveUsingTemp(aDocument);
                    break;
                                                                
                    case kStdCancelItemIndex:
                        return(userCanceledErr);
                    break;
                    
                    case kDontSaveItemIndex:
                        aDocument->dirty = false;
                    break;
                }
            }
            else
            {
                if (dialogAnswer==kAEYes)
                    if (aDocument->everSaved == false)
                    {
                        if (canInteract)
                        {
                            myErr = GetFileNameToSaveAs(aDocument);
                            if (myErr==noErr)
                                myErr = DoSave(aDocument, aDocument->theFSSpec);
                        }
                        else    
                            return(errAENoUserInteraction);
                    }
                    else
                        myErr = SaveUsingTemp(aDocument);
                else
                    myErr = noErr; /* Don't save */
            }
                
            if (myErr==noErr)
            {
                CloseMyWindow(aWindow);
            }
    }
    else
        myErr = errAEIllegalIndex;
            
    return(myErr);
}
 
 
 
//
// This is called to display the "save before closing" and "revert" dialogs.
//
short DoFileDialog ( short theDlogID, WindowRef theWindow )
{
    short       theItem;
    DialogRef   theDialog;
    WindowRef   dialogWindow;
    DPtr        theDocument;
    Str255      theTitle, theReason;
    
    
    theDocument = DPtrFromWindowPtr ( theWindow );
    
    theDialog = GetNewDialog ( theDlogID, nil, (WindowRef) -1 );
    dialogWindow = GetDialogWindow ( theDialog );
    SetPortWindowPort ( dialogWindow );
    
    
    if ( theDocument->everSaved == false )
        GetWTitle ( theWindow, theTitle );      // Pick it up as a script may have changed it
    else
        PStrCopy ( theTitle, theDocument->theFileName );
 
    if ( gQuitting )
        GetIndString ( theReason, kMiscStrings, kQuittingIndex );
    else
        GetIndString ( theReason, kMiscStrings, kClosingindex );
    
    SetCursor ( &qd.arrow );
    ParamText ( theTitle, theReason, nil, nil );
    ShowWindow ( dialogWindow );
    SelectWindow ( dialogWindow );
    
    SetDialogDefaultItem ( theDialog, kStdOkItemIndex );
    SetDialogCancelItem ( theDialog, kStdCancelItemIndex );
    
    
    // As long as the only enabled items are the dimissers, 
    // there is no need to call ModalDialog within a loop.
    ModalDialog ( nil, &theItem );
    DisposeDialog ( theDialog );
    
    return theItem;
}       
 
 
 
#pragma segment File
 
//  DoQuit
//  saveOpt - one of kAEAsk,kAEYes,kAENo
//  if kAEYes or kAEAsk then AEInteactWithUser should have been called
//  before DoQuit. Assumes that it can interact if it needs to.
 
pascal void DoQuit ( DescType saveOpt )
{
    WindowPtr    aWindow;
    WindowPtr    nextWindow;
    WindowPeek   nextWPeek;
    short        theKind;
    OSErr        check;
    
    aWindow = FrontWindow();
    
    gQuitting = true;
    while ( aWindow )
    {
        nextWPeek  = ((WindowPeek)aWindow)->nextWindow;
        nextWindow = &nextWPeek->port;
        if (Ours(aWindow))
        {
            check = DoClose(aWindow, true, saveOpt);
            if ( check != noErr )
            {
                gQuitting = false;
                return;
            }
        }
        else
        {
            theKind = ((WindowPeek)aWindow)->windowKind;
            if (theKind < 0)
                CloseDeskAcc(theKind);
        }
        aWindow = nextWindow;
    }
    
    return;
    
} // DoQuit
 
 
 
pascal OSErr GetFile(FSSpec *theFSSpec)
  {
    SFTypeList         myTypes;
    StandardFileReply  reply;
 
        myTypes[0] = 'TEXT';
 
        StandardGetFile(nil, 1, myTypes, &reply);
 
        if (reply.sfGood)
            {
                *theFSSpec = reply.sfFile;
                return(noErr);
            }
        else
            return(userCanceledErr);
 }
 
#pragma segment File
 
 
pascal OSErr DoCreate(FSSpec theSpec)
  {
      OSErr err;
 
      err = FSpCreate(&theSpec, SVEditAppSig, 'TEXT', smSystemScript);
 
      if (err != noErr)
              ShowError((unsigned char *)"\pDoCreate", err);
                
            return(err);
    }
 
#pragma segment File
 
 
pascal OSErr WriteFile(DPtr theDocument, short refNum, FSSpec theFSSpec)
  {
    short        resFile;
    long         length;
    HHandle      theHHandle;
    StScrpHandle theSHandle;
    OSErr        err;
    StringHandle theAppName;
    short        oldSelStart;
    short        oldSelEnd;
    Handle       thePHandle;
    Handle       myText;
    
    tWindowOffscreen*   theOffscreen = nil;
 
                
/*        WriteFile := 1; */
 
    /*first write out the text to the data fork*/
        
        length = (*(theDocument->theText))->teLength;
        
        myText = (*(theDocument->theText))->hText;
        
    HLock(myText);
    
        err = FSWrite(refNum, &length, *myText);
        if (err)
          return(err);
            
    HUnlock(myText);
 
        /*we are writing to a temporary file, so we need to create the resource file*/
        /*before writing out the resources*/
        /*now open the resource file*/
        
        HCreateResFile(theFSSpec.vRefNum, theFSSpec.parID, theFSSpec.name);
        err = ResError();
        if (err)
          {
                ShowError((unsigned char *)"\pHCreateResFile", err);
                return(err);
            }
 
    resFile = HOpenResFile(theFSSpec.vRefNum, theFSSpec.parID, theFSSpec.name, fsWrPerm);
    err = ResError();
 
    if (err)
          {
                ShowError((unsigned char *)"\pHOpenResFile", err);
                return(err);
            }
 
     /*write out our 'TFSF' resource to file*/
     
    //  Draw everything into offscreen pixmap.
    theOffscreen = DrawOffscreen ( theDocument->theWindow );
    if ( theOffscreen  )
        (*theDocument->theText)->inPort = (GrafPtr) theOffscreen->offscreenWorld;
    
    
    oldSelStart = (*(theDocument->theText))->selStart;
    oldSelEnd   = (*(theDocument->theText))->selEnd;
    TESetSelect(0,32000, theDocument->theText);
    
    theSHandle = TEGetStyleScrapHandle(theDocument->theText);
    
    TESetSelect(oldSelStart,oldSelEnd, theDocument->theText);
    
    
    if ( theOffscreen )
    {
        // If it wasn't for the caret, we wouldn't need to draw this
        theOffscreen = DrawOnscreen ( theOffscreen );
        (*theDocument->theText)->inPort = theDocument->theWindow;
    }
    
    AddResource((Handle)theSHandle, 'TFSF', 255, (unsigned char *)"\pStyle Info");
    err = ResError();
    if (err)
          {
                ShowError((unsigned char *)"\pAddResource- TFSF", err);
                return(err);
            }
                    
// write out the printer info
                
    // First the QD info
                
        if (theDocument->thePrintSetup)
            {
                thePHandle = (Handle)theDocument->thePrintSetup;
                err = HandToHand(&thePHandle);
                
                AddResource(thePHandle, 'TFSP', 255, (unsigned char *)"\pPrinter Info");
                err = ResError();
                if (err)
                    {
                        ShowError((unsigned char *)"\pAddResource- TFSP", err);
                        return(err);
                    }
            }
                
                
        theHHandle = (HHandle)NewHandle(sizeof(HeaderRec));
    HLock((Handle)theHHandle);
 
        GetFontName(theDocument->theFont, (unsigned char *)&(*theHHandle)->theFont);
        (*theHHandle)->theSize     = theDocument->theSize;
        (*theHHandle)->theStyle    = theDocument->theStyle;
        (*theHHandle)->lastID      = theDocument->lastID;
 
        HUnlock((Handle)theHHandle);
 
        AddResource((Handle)theHHandle, 'TFSS', 255, (unsigned char *)"\pHeader Info");
 
        err = ResError();
        if (err)
            {
                ShowError((unsigned char *)"\pAddResource- TFSS", err);
                return(err);
            }
 
        /*if we have any sections, write out the records and resources*/
        
        /*Now put an AppName in for Finder in 7.0*/
 
        theAppName = (StringHandle)NewHandle(6);
        PStrCopy(*theAppName,(unsigned char *)"\p7Edit");
        
        AddResource((Handle)theAppName, 'STR ', - 16396, (unsigned char *)"\pFinder App Info");
 
        err = ResError();
 
        if (err)
            {
                ShowError((unsigned char *)"\pAppName", err);
                return(err);
            }
 
        CloseResFile(resFile);
 
        return(noErr);
    } /* WriteFile */
 
#pragma segment File
 
pascal OSErr ReadFile(DPtr theDocument, short  refNum, Str255 fn)
 {
        long            theSize;
        short           resFile;
        OSErr           err;
        HHandle         aHandle;
        Handle          gHandle;
        Boolean         gotQDPrintRec;
        
        gotQDPrintRec = false;
 
        err = GetEOF(refNum, &theSize);
                if (err)
          return(err);
 
        /*we're only using TE, so check that there is not more than 32K worth of text*/
                
        if (theSize > 32000)
          return(1);
 
        gHandle = NewHandle(theSize);
        HLock(gHandle);
                err = FSRead(refNum, &theSize, *gHandle);
                
        if (err)
          {
            HUnlock(gHandle);
                        return(err);
          }
                                
                resFile = HOpenResFile(theDocument->theFSSpec.vRefNum, 
                                                             theDocument->theFSSpec.parID,
                                                             fn,
                                                             fsWrPerm);
                if (resFile == -1)
                    err = fnfErr;
                    
                if (err==noErr)
                    {
                        aHandle = nil;
        
                        if (Count1Resources('TFSS'))
                            aHandle = (HHandle)Get1Resource('TFSS', 255);
                        /*
                            New Format Info
                        */
                        
                        aHandle = nil;
                        
                        if (Count1Resources('TFSF'))
                            aHandle = (HHandle)Get1Resource('TFSF', 255);
                            
                        HLock(gHandle);
                        TEStyleInsert(  *gHandle,
                                                    GetHandleSize(gHandle),
                                                    (StScrpHandle)aHandle,
                                                    theDocument->theText);
                                        
                        HUnlock(gHandle);
                        
                        /*
                            If there is a print record saved, ditch the old one
                            created by new document and fill this one in
                        */
                        if (Count1Resources('TFSP'))
                        {
                            if (theDocument->thePrintSetup)
                                DisposeHandle((Handle)theDocument->thePrintSetup);
                            
                            theDocument->thePrintSetup = (THPrint)Get1Resource('TFSP', 255);
                            err = HandToHand((Handle *)&theDocument->thePrintSetup);
                            
                            gotQDPrintRec = true;
            
                        }
 
                        CloseResFile(resFile);
        
                        err = ResError();
                        if (err)
                            {
                                ShowError((unsigned char *)"\pread file- CloseResFile", err);
                                return(err);
                            }
          }
                else
                    TESetText(*gHandle, 
                              GetHandleSize(gHandle), 
                                        theDocument->theText);
                    
                if (gHandle)
                    DisposeHandle(gHandle);
                    
                if (err==fnfErr)
                    err = noErr;
 
        return(err);
        } /* ReadFile */
 
/** -----------------------------------------------------------------------
        Name:           GetFileContents
        Purpose:        Opens the document specified by theFSSpec and puts
                                the contents into theDocument.
     -----------------------------------------------------------------------**/
 
    
#pragma segment File
 
pascal OSErr GetFileContents(FSSpec theFSSpec, DPtr theDocument)
  {
     OSErr            err;
     short            theRefNum;
 
            /*this can be called from two places- on an OpenDoc AppleEvent*/
            /*and by the user just selecting Open from the File Menu*/
            /*assume that the CFS is correct when the routine is called*/
 
            err = FSpOpenDF(&theFSSpec,
                                          fsRdWrPerm,
                                          &theRefNum);
            if (err)
                {
                    ShowError((unsigned char *)"\pFSpOpenDF", err);
                    return(err);
                }
            else
                {
                    err = ReadFile(theDocument, theRefNum, theFSSpec.name);
                    if (err)
                        {
                            ShowError((unsigned char *)"\pReadFile", err);
                            return(err);
                        }
                    err=FSClose(theRefNum);
                    if (err)
                        {
                            ShowError((unsigned char *)"\pFSClose", err);
                            return(err);
                        }
                    return(noErr);
                }
        }
 
    
#pragma segment File
 
pascal OSErr SaveUsingTemp(DPtr theDocument)
  {
    Str255           tempName;
    OSErr            err;
        FSSpec               tempFSSpec;
 
        /*save the file to disk using a temporary file*/
        /*this is the recommended way of doing things*/
        /*first write out the file to disk using a temporary filename*/
        /*if it is sucessfully written, exchange the temporary file with the last one saved*/
    /*then delete the temporary file- so if anything goes wrong, the original version is still there*/
        /*first generate the temporary filename*/
 
        GetTempFileName(theDocument, tempName);
        /*create this file on disk*/
 
        tempFSSpec      = theDocument->theFSSpec;
        PStrCopy(tempFSSpec.name,tempName);
            
        err = DoCreate(tempFSSpec); 
 
        /*now save the file as normal*/
        
        if (err==noErr)
            err = DoSave(theDocument, tempFSSpec);
        
        if (err == noErr)
            err = FSpExchangeFiles(&tempFSSpec, &theDocument->theFSSpec);
 
        /*we've exchanged the files, now delete the temporary one*/
        
        if (err==noErr)
          err = FSpDelete(&tempFSSpec);
 
        return(err);
    }
 
    
#pragma segment File
 
/*
    Fills in the document record with the user chosen destination
*/
 
pascal OSErr GetFileNameToSaveAs(DPtr theDocument)
    {           
    StandardFileReply   reply;
    OSErr               err;
        Str255              suggestName;
 
        GetWTitle(theDocument->theWindow, suggestName);
 
        StandardPutFile((unsigned char *)"\pSave Document As:", suggestName, &reply);
 
    if (reply.sfGood)
            {               
                err = FSpDelete(&reply.sfFile);
                
                if (!((err==noErr) || (err==fnfErr)))
                    return(err);
                else
                    err = noErr;
                    
                theDocument->theFSSpec = reply.sfFile;
                PStrCopy(theDocument->theFileName, reply.sfFile.name);
            }
        else
            err = userCanceledErr;
        
        return(err);
 } /* GetFileNameToSaveAs */
 
    
#pragma segment File
 
pascal OSErr DoSave(DPtr   theDocument, FSSpec theFSSpec)
  {
    short      refNum;
    OSErr      fileErr;
 
        fileErr = FSpOpenDF(&theFSSpec, fsRdWrPerm, &refNum);
        
        if (fileErr == fnfErr)
            {
              fileErr = DoCreate(theFSSpec);
                
                if (fileErr)
                    return(fileErr);
                    
                fileErr = FSpOpenDF(&theFSSpec, fsRdWrPerm, &refNum);
            }
 
        if (fileErr == noErr)
            {
              fileErr = WriteFile(theDocument, refNum, theFSSpec);
                
                if (fileErr==noErr)
                    theDocument->dirty = false;
                    
                fileErr = FSClose(refNum);
            }
        else
            FileError((unsigned char *)"\perror opening file ", theFSSpec.name);
        
        return(fileErr);
    }
 
    
#pragma segment File
 
pascal OSErr OpenOld(FSSpec aFSSpec)
    {       
      DPtr  theDocument;
        OSErr fileErr;
 
        theDocument = NewDocument(true, (WindowPtr)-1L);
        
        SetWTitle(theDocument->theWindow, aFSSpec.name);
        
        SetPort(theDocument->theWindow);
        
        theDocument->theFSSpec   = aFSSpec;
        
        PStrCopy(theDocument->theFileName,aFSSpec.name);
        
        theDocument->dirty       = false;
        theDocument->everSaved   = true;
 
    fileErr = GetFileContents(aFSSpec, theDocument);
        
        if (fileErr == noErr)
            {
                DoResizeWindow(theDocument);
                ShowWindow(theDocument->theWindow);
            }
        else
            FileError((unsigned char *)"\pError Opening ", aFSSpec.name);
                    
        return(fileErr);
    } /* OpenOld */
 
    
#pragma segment File
 
pascal OSErr OpenUsingAlias(AliasHandle theAliasH)
  {
    OSErr    err;
    FSSpec   aFSSpec;
    Boolean  dummy;
 
        err = ResolveAlias(nil, theAliasH, &aFSSpec, &dummy);
        
        if (err == noErr)
            err = OpenOld(aFSSpec);
                    
        return(err);
    }