TumblerSource/Tumbler_file.c

//      file.c
//
//      File Input/Output routines.
//      
//
//      Author:     Nick Thompson & Pablo Fernicola, with thanks to the QuickDraw 3D team
//      Date:       Thursday, January 26, 1992
//
//  Modification History:
//
//      11/27/94    nick        there seems to be a problem reading metafiles into the app
//      01/25/95    nick        slotted in spin code (thanks kent), updated for viewhints.  
//                              If we have view hints, then we can export things like the 
//                              renderer type and lighting and camera in a drag or cut/copy
//
//
//  To Do:
//      factor for AppleEvents
//
//      Copyright © 1992-95 Apple Computer, Inc., All Rights Reserved
 
#include "Tumbler_globals.h"
#include "Tumbler_prototypes.h"
#include "Tumbler_resources.h"
 
#include "QD3DStorage.h"
#include "QD3DGroup.h"
#include "QD3DIO.h"
#include "QD3DShader.h"
#include "QD3DView.h"
 
#include "Tumbler_file.h"
#include "Tumbler_camera.h"
#include "Tumbler_document.h"
#include "Tumbler_PICTImport.h"
#include "Tumbler_utility.h"
 
 
#include <string.h>
 
static OSErr WriteDocumentFile(DocumentPtr theDocument);
//-----------------------------------------------------------------------------
 
TQ3Status Tumbler_ReadScene(
    TQ3FileObject       file,
    short               isText,
    TQ3SharedObject     *viewHints,
    TQ3Object           *model)
{
    char                filename[64];
//  TQ3Object           group;
    TQ3Object           object;
    TQ3Boolean          isEOF;
    
    *viewHints  = NULL;
    if( *model ) {
        Q3Object_Dispose(*model);
    }
 
    *model      = Q3DisplayGroup_New();
 
//  group       = NULL;
    object      = NULL;
    
    SetCursor(*GetCursor(watchCursor));
 
    if (Q3File_OpenRead(file, NULL) != kQ3Success)
    {
        SetCursor(&qd.arrow);
        return kQ3Failure;
    }
 
    while ((isEOF = Q3File_IsEndOfFile(file)) == kQ3False)
    {
        object = NULL;
        
        object = Q3File_ReadObject(file);
        
        if (object == NULL) 
        {
            /* 
                Check and handle errors here - 
                    Some errors may mean more objects are left in the file.
            */
            break;
        }
 
        if (Q3Object_IsType(object, kQ3SharedTypeViewHints))
        {
            if (*viewHints == NULL)
            {
                *viewHints = object;
                object = NULL;
            }
        }
        else if (Q3Object_IsDrawable(object))
        {
            if (*model)
            {
                Q3Group_AddObject(*model, object);
            }
//          else
//          {
//              group = Q3DisplayGroup_New();
//
//              Q3Group_AddObject(group, *model);
//              Q3Group_AddObject(group, object);
//              Q3Object_Dispose(*model);
//
//              *model = group;
//          }
        }
        if (object != NULL) 
            Q3Object_Dispose(object);
    }
            
    Q3File_Close(file);
    
    if (isEOF == kQ3False)
    {
        if (*model != NULL)
        {   Q3Object_Dispose(*model); *model = NULL; }
    
        if (*viewHints != NULL)
        {   Q3Object_Dispose(*viewHints); *viewHints = NULL; }
        
        SetCursor(&qd.arrow);
        return kQ3Failure;
    }
    
    SetCursor(&qd.arrow);
    return kQ3Success;
}
 
//-----------------------------------------------------------------------------
//
//  Tumbler_WriteScene()
//
 
void Tumbler_WriteScene(
    TQ3FileObject       file,
    short               textMode,
    DocumentPtr         theDocument)
{
    TQ3GroupPosition    gPos;
    TQ3Object           object;
    TQ3ViewStatus       fileStatus;
    
    SetCursor(*GetCursor(watchCursor));
 
    if (Q3File_OpenWrite(file, kQ3FileModeText) != kQ3Success)
    {
        SetCursor(&qd.arrow);
        return;
    }
    
    Q3View_StartWriting(theDocument->theView, file);
        
    if (theDocument->viewHints != NULL)
    {
        if (Q3Object_Submit(theDocument->viewHints, theDocument->theView) == kQ3Failure)
        {
            Q3File_Cancel(file);
            SetCursor(&qd.arrow);
            return;
        }
    }
    do {
        if (theDocument->documentGroup != NULL)
        {
                Q3Group_GetFirstPosition(theDocument->documentGroup, &gPos);
                while (gPos)
                {
                    TQ3Status status;
                    
                    Q3Group_GetPositionObject(theDocument->documentGroup, gPos, &object);
                    status = Q3Object_Submit(object, theDocument->theView);
                    Q3Object_Dispose(object);
                    
                    if (status != kQ3Failure)
                    {
                        Q3Group_GetNextPosition(theDocument->documentGroup, &gPos);
                    }
                }
        }
    } while ((fileStatus = Q3View_EndWriting(theDocument->theView)) == kQ3ViewStatusRetraverse);
        
    if( Q3File_Close(file) == kQ3Failure ) {
        SetCursor(&qd.arrow);
        return;
    }
    SetCursor(&qd.arrow);
    return ;
}
 
 
//-----------------------------------------------------------------------------
// ReadDocumentFile
 
short ReadDocumentFile(DocumentPtr theDocument, TQ3Boolean isText)
{
    TQ3StorageObject        storage;
    TQ3FileObject       fd;
    TQ3Object           objects = nil;
    TQ3Object           viewHints;  
    
    storage = Q3MacintoshStorage_New( theDocument->fRefNum );
    
    if (storage == nil)
        goto bail;
    
    fd = Q3File_New();
    
    if (fd == nil)
        goto bail;
 
    Q3File_SetStorage(fd, storage);
    Q3Object_Dispose(storage);
    
    Tumbler_ReadScene(
        fd,
        isText,
        &viewHints,
        &theDocument->documentGroup) ;
    
    TumblerDocument_UpdateView( theDocument, viewHints ) ;
    
    AdjustLightsPositions(theDocument);
 
    if (viewHints)
        Q3Object_Dispose(viewHints);
 
    Q3Object_Dispose(fd);
 
    return(noErr);
bail:
    Q3Object_Dispose(fd);
 
    return(fnOpnErr);
}
 
//-
 
PicHandle OpenPICTFile(
    FSSpec *theFile)
{
    OSErr       err;
    long        curEOF;
    PicHandle   my_pic;
    long        count;
    Ptr         buffer;
    short       refNum;
    
    if (FSpOpenDF(theFile, fsRdWrPerm, &refNum))
        return(0);
 
    /* get size of file */
    err = GetEOF(refNum, &curEOF);
    if (err != 0) {
        return(0);
    }
    
    /* move the file mark to 512 */
    err = SetFPos(refNum, fsFromStart, 512L);
    if (err != 0) {
        return(0);
    }
 
    /* size of data to read */
    count = curEOF - 512;
    
    /* create the PicHandle */
    my_pic = (PicHandle)NewHandle(count);
    HLock((Handle)my_pic);
    
    /* read the PICT info */
    buffer = (Ptr)(*my_pic);
    err = FSRead(refNum, &count, buffer);
    if (err != 0) {
        return(0);
    }
    HUnlock((Handle)my_pic);
 
    FSClose(refNum);
    
    return (my_pic);
}
 
 
 
OSErr WriteDocumentFile(DocumentPtr theDocument)
{
    OSErr       theResult;
    long        length;
    char        *bufPtr;
 
    SetCursor(*GetCursor(watchCursor));
 
    if (! theDocument->fRefNum)
        return(fnOpnErr);
 
    if ((theResult = SetFPos(theDocument->fRefNum, fsFromStart, 0)) != noErr ) 
        return(theResult);
 
    if( theDocument->documentGroup ) {
        TQ3StorageObject    storage;
        TQ3FileObject   fd;
        
        storage = Q3MacintoshStorage_New(theDocument->fRefNum);
        
        if (storage == nil)
            goto bail;
        
        fd = Q3File_New();
        
        if (fd == nil)
            goto bail;
 
        Q3File_SetStorage(fd, storage);
        Q3Object_Dispose(storage);
        
        Tumbler_WriteScene(fd, 
                            kQ3FileModeText,
                            theDocument) ;
 
        Q3Object_Dispose(fd);
    }
    return(noErr);
bail:
    return(fnOpnErr);
}
 
 
void DoNewDocument(void)
 
{   DocumentPtr theDocument;
 
    if ((theDocument = NewDocument(NULL, NULL)) != nil) {
        ShowWindow(theDocument->theWindow);
    }
}
 
OSErr DoOpenFile(FSSpec *theFile)
 
{   OSErr               result;
    short               refNum;
    DocumentPtr         theDocument;
    FInfo               fndrInfo ;
    TQ3Boolean          isText ;
 
    // we assume the FSSpec passed in was valid, get the file information
    // we need to know the file type, this routine may get called by an appleEvent
    // handler, so we can't assume a type, we need to get it from the fsspec.
    
    FSpGetFInfo( theFile, &fndrInfo ) ;
    
    // pull out the file type
    
    isText = (fndrInfo.fdType == 'TEXT') ;
    
    if ((result = FSpOpenDF(theFile, fsRdWrPerm, &refNum)) != noErr)
        return(result);
 
    if ((theDocument = NewDocument(NULL, NULL)) != nil) {
        theDocument->fRefNum = refNum;
        theDocument->theFileSpec = *theFile ;
        SetCursor(*GetCursor(watchCursor));
        ReadDocumentFile( theDocument, isText ) ;
        SetWTitle(theDocument->theWindow, theFile->name);
        ShowWindow(theDocument->theWindow);
        AdjustCamera(theDocument,
            theDocument->theWindow->portRect.right - theDocument->theWindow->portRect.left,
            theDocument->theWindow->portRect.bottom - theDocument->theWindow->portRect.top);
        SetCursor(&qd.arrow);
 
    } else {
        SysBeep(1);
        FSClose(refNum);
        return(memFullErr);
    }
    return(noErr);
}
 
void DoImport3DMFDocument(DocumentPtr theDocument)
{
    short               refNum;
    StandardFileReply   theReply;
    SFTypeList          theTypeList = { 'TEXT', '3DMF', 0 };
    short               numtypes = 2 ;
 
    if(theDocument == nil)
        return;
 
    StandardGetFile(0L, numtypes, theTypeList, &theReply);
 
    // read for podium files
    if (theReply.sfGood) {
        if(theReply.sfType == 'TEXT' || theReply.sfType == '3DMF') {
 
            // did we try to open a metafile ???
        
            OSErr               result;
            short               refNum;
            DocumentPtr         theDocument;
            FInfo               fndrInfo ;
            TQ3Boolean          isText ;
            
            isText = (theReply.sfType == 'TEXT') ;
            
            if ((result = FSpOpenDF(&theReply.sfFile, fsRdWrPerm, &refNum)) != noErr)
                return ;
                
            theDocument->fRefNum = refNum;
            
            ReadDocumentFile( theDocument, isText ) ;
            
#ifdef PODIUM_APP
            AdjustCamera(theDocument,
                theDocument->geometriesOffscreen->portRect.right - theDocument->geometriesOffscreen->portRect.left,
                theDocument->geometriesOffscreen->portRect.bottom - theDocument->geometriesOffscreen->portRect.top);
#else
        AdjustCamera(theDocument,
            theDocument->theWindow->portRect.right - theDocument->theWindow->portRect.left,
            theDocument->theWindow->portRect.bottom - theDocument->theWindow->portRect.top);
#endif
 
        } 
    }
}
 
 
//--------------------------------------------------------------------------------
void DoOpenDocument(DocumentPtr theDocument)
{   short               refNum;
 
#ifndef PODIUM_APP
    SFTypeList          theTypeList = { 'TEXT', '3DMF', 'PICT', 0 };
    short               numtypes = 3 ;
#else
    SFTypeList          theTypeList = { 'PICT', 0 };
    short               numtypes = 1 ;
#endif
 
    StandardFileReply   theReply;
    StandardGetFile(0L, numtypes, theTypeList, &theReply);
 
#ifndef PODIUM_APP
 
    // read for Tumbler files
    if (theReply.sfGood) {
        if( theReply.sfType == 'TEXT' || theReply.sfType == '3DMF') {
            DoOpenFile(&theReply.sfFile);
        } else if( theReply.sfType == 'PICT' && theDocument && theDocument->documentGroup) {
            PicHandle           thePict;
            TQ3StoragePixmap    textureImage;
            
            // get the picture from the file
            thePict = OpenPICTFile(&theReply.sfFile);
            
            // make a texture
            TextureFromPICT( thePict, &textureImage);
            AddTextureToDocument( theDocument, &textureImage);
            
            // and free the space occupied by the picture
            KillPicture(thePict) ;
        
        }
    }
#else
 
    // read for podium files
    if (theReply.sfGood) {
        // podium's slide files are PICT files
        if( theReply.sfType == 'PICT' ) {
 
            short           refNum;
            DocumentPtr     theDocument;
            PicHandle       theBackgroundPict ;
            OSErr           theErr ;
            
            if((theErr = FSpOpenDF(&theReply.sfFile, fsRdPerm, &refNum)) != noErr )
                return ;
        
            if ((theDocument = NewDocument(NULL, NULL)) != nil) {
                Rect            bounds;
                PicHandle       thePict;
                GWorldPtr       savedGWorld ;
                GDHandle        savedDevice ;
                
                GetGWorld(&savedGWorld,&savedDevice);
                
                theBackgroundPict = OpenPICTFile(&theReply.sfFile);
                bounds = (**theBackgroundPict).picFrame;
 
                theErr = UpdateGWorld(&theDocument->screenBuffer, 16, &bounds, nil, nil, 0L ) ;
                if( (theErr = UpdateGWorld(&theDocument->bgOffscreen, 16, &bounds, nil, nil, 0L )) != noErr )  {
                    return ;
                }
                
                // draw the picture into the offscreen  
                SetGWorld( theDocument->bgOffscreen , nil ) ;
                DrawPicture( theBackgroundPict, &bounds ) ;
                SetGWorld(savedGWorld,savedDevice);
                
                SizeWindow(theDocument->theWindow, bounds.right - bounds.left, bounds.bottom - bounds.top, false);
                {
                    Rect    screenRect = (**GetMainDevice()).gdRect;
                    Rect    winRect = theDocument->theWindow->portRect ;
            
                    CenterRectInRect(&screenRect, &winRect) ;
                    MoveWindow(theDocument->theWindow,winRect.left,winRect.top,false);
                }
                SetWTitle(theDocument->theWindow, theReply.sfFile.name);
                ShowWindow(theDocument->theWindow) ;
 
            } else {
            
                SysBeep(1);
                FSClose(refNum);
                return;
                
            }
        }
        else if(theDocument != nil && (theReply.sfType == 'TEXT' || theReply.sfType == '3DMF')) {
 
            // did we try to open a metafile ???
        
            OSErr               result;
            short               refNum;
            DocumentPtr         theDocument;
            FInfo               fndrInfo ;
            TQ3Boolean          isText ;
            
            isText = (theReply.sfType == 'TEXT') ;
            
            if ((result = FSpOpenDF(&theReply.sfFile, fsRdWrPerm, &refNum)) != noErr)
                return ;
                
            theDocument->fRefNum = refNum;
            
            ReadDocumentFile( theDocument, isText ) ;
            AdjustCamera(theDocument,
                theDocument->geometriesOffscreen->portRect.right - theDocument->geometriesOffscreen->portRect.left,
                theDocument->geometriesOffscreen->portRect.bottom - theDocument->geometriesOffscreen->portRect.top);
        } 
    }
#endif
 
}
 
 
Boolean DoSaveAsDocument(DocumentPtr theDocument)
{   short               theResult;
    Str255              thePrompt, theName;
    StandardFileReply   theReply;
 
    if (! theDocument)
        return(false);
 
    GetIndString(thePrompt, FileStringsID, slSavePromptIndex);
    GetWTitle(theDocument->theWindow, theName);
    StandardPutFile( (ConstStr255Param) &thePrompt, (ConstStr255Param) &theName, &theReply);
 
    if (theReply.sfGood) {
        if (!theReply.sfReplacing) {
            if ((theResult = FSpCreate(&theReply.sfFile, FileCreator, FileType, theReply.sfScript)) != noErr) {
                SysBeep(1);
                return(false);
            }
        }
        if (theDocument->fRefNum) {
            theResult = FSClose(theDocument->fRefNum);
        }
        if ((theResult = FSpOpenDF(&theReply.sfFile, fsRdWrPerm, &theDocument->fRefNum)) != noErr) {
            SysBeep(1);
            return(false);
        }
 
        if ((theResult = WriteDocumentFile(theDocument)) != noErr) {
            SysBeep(1);
            return(false);
        }
 
        SetWTitle(theDocument->theWindow, theReply.sfFile.name);
        theDocument->dirty = false;
 
    } else {
        return(false);
    }
    return(true);
}
 
 
Boolean DidSaveDocument(DocumentPtr theDocument)
{
 
    if ( theDocument == nil )
        return(false);
 
    if (theDocument->fRefNum) {
        if (WriteDocumentFile(theDocument)) {
            SysBeep(1);
            return(false);
        } else {
            theDocument->dirty = false;
        }
        return(true);
    } else {
        return(DoSaveAsDocument(theDocument));
    }
}
 
 
void DoRevertDocument(DocumentPtr theDocument)
 
{   Str255      theName;
 
    if (! theDocument)
        return;
 
    if (theDocument->fRefNum) {
        GetWTitle(theDocument->theWindow, theName);
        ParamText( (ConstStr255Param) &theName, (ConstStr255Param) "", (ConstStr255Param) "", (ConstStr255Param) "");
        if (Alert(idRevertALRT, 0L) == 1) {
            FInfo               fndrInfo ;
        
            // we assume the FSSpec in the document is valid, get the file information
            // we need to know the file type
            
            FSpGetFInfo( &theDocument->theFileSpec, &fndrInfo ) ;
            
            // pull out the file type
            
            ReadDocumentFile(theDocument, (fndrInfo.fdType == 'TEXT')) ;
        }
    }
}