home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Sample Code / AppsToGo / DTS.Lib / File2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-24  |  32.3 KB  |  1,100 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:         DTS.Lib
  5. ** File:            file2.c
  6. ** Some code from:  Traffic Light 2.0 version, by Keith Rollin & John Harvey
  7. ** Modified by:     Eric Soldan
  8. **
  9. ** Copyright © 1990-1993 Apple Computer, Inc.
  10. ** All rights reserved.
  11. */
  12.  
  13. /* You may incorporate this sample code into your applications without
  14. ** restriction, though the sample code has been provided "AS IS" and the
  15. ** responsibility for its operation is 100% yours.  However, what you are
  16. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  17. ** after having made changes. If you're going to re-distribute the source,
  18. ** we require that you make it clear in the source that the code was
  19. ** descended from Apple Sample Code, but that you've made changes. */
  20.  
  21.  
  22.  
  23. /*****************************************************************************/
  24.  
  25.  
  26.  
  27. #include "DTS.Lib2.h"
  28. #include "DTS.Lib.protos.h"
  29.  
  30. #ifndef __ERRORS__
  31. #include <Errors.h>
  32. #endif
  33.  
  34. #ifndef __FILES__
  35. #include <Files.h>
  36. #endif
  37.  
  38. #if __MOVIESUPPORT__
  39.  
  40. #ifndef __IMAGECOMPRESSION__
  41. #include <ImageCompression.h>
  42. #endif
  43.  
  44. #ifndef __MOVIES__
  45. #include <Movies.h>
  46. #endif
  47.  
  48. #endif
  49.  
  50. #ifndef __PACKAGES__
  51. #include <Packages.h>
  52. #endif
  53.  
  54. #ifndef __RESOURCES__
  55. #include <Resources.h>
  56. #endif
  57.  
  58. #ifndef __TOOLUTILS__
  59. #include <ToolUtils.h>
  60. #endif
  61.  
  62.  
  63.  
  64. /*****************************************************************************/
  65.  
  66.  
  67.  
  68. extern OSType        gDocCreator;    /* Initialized to gSignature by Init. */
  69. extern long            gAppWindowAttr;    /* Initialized by app in Window.c. */
  70. extern long            gQTVersion;        /* Initialized by Utilities.c in InitQuickTime. */
  71.  
  72. extern TreeObjHndl    gWindowFormats;    /* If non-nil, then get descriptions of windows from here. */
  73.  
  74. extern short        gTypeListLen;    /* Initialized by app in File.c. */
  75. extern OSType        *gTypeListPtr;    /* Initialized by app in File.c. */
  76.  
  77. FileFilterUPP        gSFGetFileFilterUPP;
  78.  
  79. static OSErr        Create_OpenFile(FSSpec *file, short *refNum, OSType sftype);
  80.  
  81.  
  82.  
  83. /*****************************************************************************/
  84. /*****************************************************************************/
  85.  
  86.  
  87.  
  88. /* This function disposes of the document.  It checks to see if a file is
  89. ** currently open for the document.  If it is, then the document is closed.
  90. ** Once there is no open file for the document, the memory occupied by the
  91. ** document is released. */
  92.  
  93. #pragma segment File
  94. OSErr    DisposeDocument(FileRecHndl frHndl)
  95. {
  96.     OSErr        err, err2;
  97.     short        refNum;
  98.     WindowPtr    window;
  99.     Movie        movie;
  100.  
  101.     err = noErr;
  102.  
  103.     if (frHndl) {
  104.  
  105.         if ((refNum = (*frHndl)->fileState.refNum) != kInvalRefNum) {        /* If file open... */
  106.  
  107.             if ((*frHndl)->fileState.sfType == MovieFileType)        /* If movie file... */
  108.                 err = CloseMovieFile(refNum);                        /* Close it. */
  109.  
  110.             else                                                    /* If not movie file... */
  111.                 err = FSClose(refNum);                                /* Close it.       */
  112.         }
  113.  
  114.         CloseDocResFile(frHndl);                    /* Close resource fork, if opened. */
  115.  
  116.         window = (*frHndl)->fileState.window;
  117.         err2    = DoFreeDocument(frHndl);            /* Free all application-specific document ram. */
  118.  
  119.         movie = (*frHndl)->fileState.movie;
  120.         if (movie)
  121.             DisposeMovie(movie);                    /* If we have a movie, dispose it. */
  122.  
  123.         if (window)
  124.             SetWRefCon(window, (long)nil);            /* Mark window as no longer having a document. */
  125.  
  126.         if (!err)
  127.             err = err2;
  128.  
  129.         DisposeHandle((Handle)frHndl);                /* Release memory for the document handle. */
  130.     }
  131.  
  132.     return(err);
  133. }
  134.  
  135.  
  136.  
  137. /*****************************************************************************/
  138.  
  139.  
  140.  
  141. /* This function creates a new document.  A handle is created as the
  142. ** reference to the document.  Header information is placed in this handle.
  143. ** The application-specific data follows this header information.  The
  144. ** handle is returned (or nil upon failure), and typically the handle is
  145. ** then stored in the refCon field of the window.  Note that this is a
  146. ** convention, and is not mandatory.  This allows a document to exist that
  147. ** has no window.  A document with no window is useful when the application
  148. ** is called from the finder in response to a print request.  The document
  149. ** can be loaded and printed without involving a window on the screen. */
  150.  
  151. #pragma segment File
  152. OSErr    NewDocument(FileRecHndl *returnHndl, OSType sftype, Boolean incTitleNum)
  153. {
  154.     long                size;
  155.     FileRecHndl            frHndl;
  156.     FileRecPtr            frPtr;
  157.     Str255                untitled;
  158.     StringPtr            pstr;
  159.     OSErr                err;
  160.     short                i;
  161.     Movie                movie;
  162.     TreeObjHndl            wobj;
  163.     PositionWndProcPtr    windowPlacementProc;
  164.     static short        untitledCount;
  165.  
  166.     if (!sftype) {
  167.         if (returnHndl == (FileRecHndl *)-1)
  168.             --untitledCount;
  169.         if (!returnHndl)
  170.             untitledCount = 0;
  171.         return(noErr);
  172.     }
  173.  
  174.     if (returnHndl)
  175.         *returnHndl = nil;
  176.  
  177.     err  = memFullErr;                /* Assume that we will fail. */
  178.  
  179.     size = InitDocumentSize(sftype);
  180.         /* Call the application and ask it how big the frHndl should be for
  181.         ** this document type.  We can't know, so we'll ask. */
  182.  
  183.     frHndl = (FileRecHndl)NewHandleClear(size);
  184.     if (frHndl) {
  185.         /* Create (or try to) the frHndl, initialized to all 0's */
  186.  
  187.         if (returnHndl)
  188.             *returnHndl = frHndl;
  189.  
  190.         windowPlacementProc = StaggerWindow;
  191.         wobj = nil;
  192.         if (gWindowFormats) {
  193.             for (i = 0; i < (*gWindowFormats)->numChildren; ++i) {
  194.                 wobj = GetChildHndl(gWindowFormats, i);
  195.                 if (sftype == mDerefWFMT(wobj)->sfType) {
  196.                     windowPlacementProc = nil;
  197.                         /* If using 'WFMT' descriptions, this will be fully determined later. */
  198.                     pcpy(untitled, (StringPtr)mDerefWFMT(wobj)->title);
  199.                     if (untitled[0])
  200.                         if (untitled[1] == ' ')
  201.                             untitled[0] = 0;
  202.                                 /* If title begins with a space, it's just a comment. */
  203.  
  204.                     break;
  205.                 }
  206.                 wobj = nil;
  207.             }
  208.         }
  209.         if (!wobj) {
  210.             for (i = gTypeListLen; --i;) if (sftype == gTypeListPtr[i]) break;
  211.                 /* Walk the typeList to find this file type.  We are interested in
  212.                 ** where we find the entry.  The position we find it is used as a
  213.                 ** string number into the rDefaultTitles STR# resource.  We get
  214.                 ** an individual string from this location.  This allows us to
  215.                 ** have different default titles for different document types. */
  216.     
  217.             for (++i; i; i--) {
  218.                 GetIndString(untitled, rDefaultTitles, i);
  219.                 if (untitled[0]) break;        /* Quit if we succeeded at getting one. */
  220.             }
  221.         }
  222.  
  223.         (*frHndl)->fileState.modNum = GetModNum();
  224.             /* In case GetModNum gets moved to another code segment, set this value
  225.             ** prior to dereferencing frHndl into frPtr. */
  226.  
  227.         frPtr = *frHndl;
  228.         frPtr->fileState.sfType                  = sftype;
  229.         frPtr->fileState.modTick                 = TickCount();
  230.         frPtr->fileState.refNum                  = kInvalRefNum;
  231.         frPtr->fileState.resRefNum               = kInvalRefNum;
  232.         frPtr->fileState.fss.vRefNum             = kInvalVRefNum;
  233.         frPtr->fileState.windowID                = rWindow;
  234.             /* The above sets the fileState constants for the document.  Note
  235.             ** that we use a default 'WIND' ID for the expected window resource.
  236.             ** This can be changed later, if the default isn't good enough. */
  237.  
  238.         if (wobj) {
  239.             frPtr->fileState.windowID      = mDerefWFMT(wobj)->windowID;
  240.             frPtr->fileState.attributes    = mDerefWFMT(wobj)->attributes;
  241.             frPtr->fileState.hScrollIndent = mDerefWFMT(wobj)->hScrollIndent;
  242.             frPtr->fileState.vScrollIndent = mDerefWFMT(wobj)->vScrollIndent;
  243.             frPtr->fileState.leftSidebar   = mDerefWFMT(wobj)->leftSidebar;
  244.             frPtr->fileState.topSidebar    = mDerefWFMT(wobj)->topSidebar;
  245.                 /* Set window attributes as described in resource. */
  246.         }
  247.         else
  248.             frPtr->fileState.attributes = gAppWindowAttr;
  249.                 /* Set window attributes for the main document type. If the document
  250.                 ** is not the main type, then the application's InitDocument function
  251.                 ** will have to change it. */
  252.  
  253.         frPtr->fileState.getDocWindow        = windowPlacementProc;
  254.         frPtr->fileState.adjustMenuItemsProc = AdjustMenuItems;
  255.         frPtr->fileState.doMenuItemProc      = DoMenuItem;
  256.         switch (frPtr->fileState.attributes & (kwIsPalette | kwIsModalDialog)) {
  257.             case kwIsPalette:
  258.                 frPtr->fileState.calcFrameRgnProc  = PaletteCalcFrameRgn;
  259.                 frPtr->fileState.contentClickProc  = PaletteContentClick;
  260.                 frPtr->fileState.contentKeyProc    = PaletteContentKey;
  261.                 frPtr->fileState.drawFrameProc     = PaletteDrawFrame;
  262.                 frPtr->fileState.freeDocumentProc  = PaletteFreeDocument;
  263.                 frPtr->fileState.freeWindowProc    = PaletteFreeWindow;
  264.                 frPtr->fileState.imageProc         = PaletteImageDocument;
  265.                 frPtr->fileState.initContentProc   = PaletteInitContent;
  266.                 frPtr->fileState.readDocumentProc  = nil;
  267.                 frPtr->fileState.resizeContentProc = PaletteResizeContent;
  268.                 frPtr->fileState.scrollFrameProc   = PaletteScrollFrame;
  269.                 frPtr->fileState.undoFixupProc     = PaletteUndoFixup;
  270.                 frPtr->fileState.windowCursorProc  = PaletteWindowCursor;
  271.                 frPtr->fileState.writeDocumentProc = nil;
  272.                 break;
  273.             case kwIsModalDialog:
  274.                 frPtr->fileState.calcFrameRgnProc    = DialogCalcFrameRgn;
  275.                 frPtr->fileState.contentClickProc    = DialogContentClick;
  276.                 frPtr->fileState.contentKeyProc      = DialogContentKey;
  277.                 frPtr->fileState.drawFrameProc       = DialogDrawFrame;
  278.                 frPtr->fileState.freeDocumentProc    = DialogFreeDocument;
  279.                 frPtr->fileState.freeWindowProc      = DialogFreeWindow;
  280.                 frPtr->fileState.imageProc           = DialogImageDocument;
  281.                 frPtr->fileState.initContentProc     = DialogInitContent;
  282.                 frPtr->fileState.readDocumentProc    = nil;
  283.                 frPtr->fileState.resizeContentProc   = DialogResizeContent;
  284.                 frPtr->fileState.scrollFrameProc     = DialogScrollFrame;
  285.                 frPtr->fileState.undoFixupProc       = DialogUndoFixup;
  286.                 frPtr->fileState.windowCursorProc    = DialogWindowCursor;
  287.                 frPtr->fileState.writeDocumentProc   = nil;
  288.                 frPtr->fileState.adjustMenuItemsProc = DialogAdjustMenuItems;
  289.                 frPtr->fileState.doMenuItemProc      = DialogDoMenuItem;
  290.                 break;
  291.             default:
  292.                 frPtr->fileState.calcFrameRgnProc  = CalcFrameRgn;
  293.                 frPtr->fileState.contentClickProc  = ContentClick;
  294.                 frPtr->fileState.contentKeyProc    = ContentKey;
  295.                 frPtr->fileState.drawFrameProc     = DrawFrame;
  296.                 frPtr->fileState.freeDocumentProc  = FreeDocument;
  297.                 frPtr->fileState.freeWindowProc    = FreeWindow;
  298.                 frPtr->fileState.imageProc         = ImageDocument;
  299.                 frPtr->fileState.initContentProc   = InitContent;
  300.                 frPtr->fileState.readDocumentProc  = ReadDocument;
  301.                 frPtr->fileState.resizeContentProc = ResizeContent;
  302.                 frPtr->fileState.scrollFrameProc   = ScrollFrame;
  303.                 frPtr->fileState.undoFixupProc     = UndoFixup;
  304.                 frPtr->fileState.windowCursorProc  = WindowCursor;
  305.                 frPtr->fileState.writeDocumentProc = WriteDocument;
  306.                 break;
  307.         }
  308.  
  309.         frPtr->fileState.windowSizeBounds.left   = kMinWindowWidth;
  310.         frPtr->fileState.windowSizeBounds.top    = kMinWindowHeight;
  311.         frPtr->fileState.windowSizeBounds.right  = kMaxWindowWidth;
  312.         frPtr->fileState.windowSizeBounds.bottom = kMaxWindowHeight;
  313.             /* Default min/max window size for growIcon. */
  314.  
  315.         pstr = frPtr->fileState.fss.name;
  316.         pcpy(pstr, untitled);
  317.         if (pstr[0]) {
  318.             if (incTitleNum)
  319.                 ++untitledCount;
  320.             pcatdec(pstr, untitledCount);
  321.                 /* Create the default document title.  It is stored in the FSSpec,
  322.                 ** as we can't place it in the window title.  We don't have a window
  323.                 ** yet to "title".  This will happen later.  The title is gotten from
  324.                 ** the FSSpec, so we are effectively done. */
  325.         }
  326.  
  327.         err = InitDocument(frHndl);
  328.             /* Call the application for any additional document initialization.
  329.             ** Other handles may need to be allocated.  The default values above
  330.             ** may be incorrect for a certain document type.  This gives the
  331.             ** application a chance to change any defaults that are incorrect. */
  332.  
  333.         if ((*frHndl)->fileState.sfType == MovieFileType) {
  334.             InitQuickTime();
  335.             if (!gQTVersion) return(paramErr);
  336.             movie = NewMovie(newMovieActive);
  337.             err   = GetMoviesError();
  338.             if (!err) {
  339.                 ClearMovieChanged(movie);
  340.                 (*frHndl)->fileState.movie = movie;
  341.             }
  342.         }
  343.  
  344.         if (err) {
  345.             DisposeHandle((Handle)frHndl);
  346.             if (returnHndl)
  347.                 *returnHndl = nil;
  348.                     /* If the application couldn't complete the document
  349.                     ** initialization, pitch the handle. */
  350.         }
  351.     }
  352.  
  353.     return(err);
  354. }
  355.  
  356.  
  357.  
  358. /*****************************************************************************/
  359.  
  360.  
  361.  
  362. #pragma segment File
  363. OSErr    OpenDocument(FileRecHndl *result, FSSpecPtr fileToOpen, char permission)
  364. {
  365.     StandardFileReply    reply;
  366.     short                refNum;
  367.     FileRecHndl            frHndl;
  368.     OSErr                err;
  369.     FSSpec                myFileSpec;
  370.     DialogPtr            openDialog;
  371.     short                item;
  372.     FInfo                finderInfo;
  373.     Boolean                openMovie;
  374.     Movie                movie;
  375.     static SFTypeList    typeList = {MovieFileType};
  376.  
  377.     *result = nil;        /* Assume we will fail. */
  378.  
  379.     openMovie = false;
  380.     if (fileToOpen == kOpenMovie) {
  381.         if (!gQTVersion) return(paramErr);        /* Can't do movies without QuickTime. */
  382.         fileToOpen = nil;
  383.         openMovie  = true;
  384.     }
  385.  
  386.     if (!fileToOpen) {
  387.         if (openMovie) {
  388.             StandardGetFilePreview(0L, 1, typeList, &reply);
  389.             if (reply.sfGood)
  390.                 myFileSpec = reply.sfFile;
  391.             else
  392.                 return(userCanceledErr);    /* User canceled. */
  393.         }
  394.         else {
  395.             if (DisplayGetFile(&reply, gTypeListLen, gTypeListPtr))    /* Let user pick file. */
  396.                 myFileSpec = reply.sfFile;                            /* User's choice.       */
  397.             else
  398.                 return(userCanceledErr);    /* User canceled. */
  399.         }
  400.     }
  401.     else {
  402.         err = HGetFInfo(fileToOpen->vRefNum, fileToOpen->parID, fileToOpen->name, &finderInfo);
  403.         if (err) return(err);
  404.         reply.sfType = finderInfo.fdType;        /* OSType of file. */
  405.         myFileSpec   = *fileToOpen;                /* Pre-designated file to open. */
  406.     }
  407.  
  408.     err = NewDocument(&frHndl, reply.sfType, false);
  409.     if (err) return(err);
  410.         /* We couldn't create an empty document, so give it up. */
  411.  
  412.     err = HOpenDF(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  413.     if (err == paramErr)
  414.         err = HOpen(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  415.  
  416.     if (err == permErr) {
  417.         permission = fsRdPerm;
  418.         err = HOpenDF(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  419.         if (err == paramErr)
  420.             err = HOpen(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  421.         if (!err)
  422.             (*frHndl)->fileState.readOnly = true;
  423.     }
  424.  
  425.     if (err == opWrErr) {
  426.  
  427.         ParamText(myFileSpec.name, nil, nil, nil);
  428.         openDialog = GetCenteredDialog(rOpenReadOnly, nil, nil, (WindowPtr)-1L);
  429.         if (!openDialog) {
  430.             DisposeDocument(frHndl);
  431.             return(err);
  432.         }
  433.  
  434.         OutlineDialogItem(openDialog, kOpenYes);
  435.         DoSetCursor(&qd.arrow);
  436.         UnhiliteWindows();
  437.         ModalDialog(gKeyEquivFilterUPP, &item);
  438.         DisposeDialog(openDialog);
  439.         HiliteWindows();
  440.         if (item != kOpenYes) {
  441.             DisposeDocument(frHndl);
  442.             return(userCanceledErr);
  443.         }
  444.  
  445.         (*frHndl)->fileState.readOnly = true;
  446.         permission = fsRdPerm;
  447.         err = HOpenDF(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  448.         if (err == paramErr)
  449.             err = HOpen(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  450.     }
  451.  
  452.     if (err) {
  453.         DisposeDocument(frHndl);
  454.         return(err);
  455.     }
  456.  
  457.     if ((*frHndl)->fileState.sfType == MovieFileType) {
  458.         FSClose(refNum);                            /* Close file, as it wasn't opened as movie. */
  459.         movie = (*frHndl)->fileState.movie;
  460.         if (movie) {
  461.             DisposeMovie(movie);                    /* NewDocument created an empty movie, */
  462.             (*frHndl)->fileState.movie = nil;        /* so first get rid of it. */
  463.         }
  464.         err = OpenMovieFile(&myFileSpec, &refNum, permission);
  465.         if (err) {
  466.             DisposeDocument(frHndl);
  467.             return(err);
  468.         }
  469.     }
  470.  
  471.     (*frHndl)->fileState.fss    = myFileSpec;
  472.     (*frHndl)->fileState.refNum = refNum;
  473.  
  474.     err = DoReadDocument(frHndl);
  475.     if (err) {
  476.         DisposeDocument(frHndl);
  477.         return(err);
  478.     }
  479.  
  480.     *result = frHndl;
  481.     return(noErr);
  482. }
  483.  
  484.  
  485.  
  486. /*****************************************************************************/
  487.  
  488.  
  489.  
  490. #pragma segment File
  491. OSErr    SaveDocument(FileRecHndl frHndl, WindowPtr window, short saveMode)
  492. {
  493.     Str255                closeOrQuit;
  494.     short                item, refNum, resID, resWasOpen;
  495.     long                createFlags;
  496.     StandardFileReply    reply;
  497.     OSErr                err;
  498.     Movie                movie;
  499.     Boolean                doPrompt;
  500.     DialogPtr            saveDialog;
  501.  
  502.     err = noErr;
  503.  
  504. /*    When entering, saveMode is set to the menu command number of the
  505. **    the item that prompted this. Current settings are kSave, kSaveAs,
  506. **    kClose, and kQuit. */
  507.  
  508.     if (saveMode != kSaveAs) {                            /* If not save as...                  */
  509.         if (!(*frHndl)->fileState.docDirty) {            /* If file clean...                      */
  510.             if ((*frHndl)->fileState.refNum)            /* If document has a file...          */
  511.                 if (!(*frHndl)->fileState.readOnly)        /* If we are allowed to touch file... */
  512.                     if ((*frHndl)->fileState.attributes & kwOpenAtOldLoc)
  513.                         DoWriteDocumentHeader(frHndl);
  514.                             /* Write out document location and print record information. */
  515.                             /* Ignore errors, as saving the location is a bonus. */
  516.             return(noErr);                                /* Consider it saved. */
  517.         }
  518.     }
  519.  
  520.     pcpy(reply.sfFile.name, (*frHndl)->fileState.fss.name);
  521.  
  522.     if ((saveMode == kClose) || (saveMode == kQuit)) {
  523.         /* If implicit save... */
  524.  
  525.         GetIndString(closeOrQuit, rFileIOStrings,
  526.                      (saveMode == kClose) ? sWClosing : sQuitting);
  527.         ParamText(reply.sfFile.name, closeOrQuit, nil, nil);
  528.  
  529.         saveDialog = GetCenteredDialog(rYesNoCancel, nil, window, (WindowPtr)-1L);
  530.  
  531.         if (saveDialog) {
  532.             OutlineDialogItem(saveDialog, kSaveYes);
  533.             DoSetCursor(&qd.arrow);
  534.             UnhiliteWindows();
  535.             ModalDialog(gKeyEquivFilterUPP, &item);
  536.             DisposeDialog(saveDialog);
  537.             HiliteWindows();
  538.         }
  539.         else
  540.             item = kSaveNo;
  541.                 /* If the dialog isn't displayed, then AppleScript doesn't want it to.
  542.                 ** In this case, we were probably AppleScripted the whole time, so
  543.                 ** the document is an AppleScript-produced document.  The script is
  544.                 ** done with the document, so ditch the document. */
  545.  
  546.         if (item != kSaveYes) {
  547.             err = noErr;
  548.             if (item == kSaveCanceled)
  549.                 err = userCanceledErr;
  550.             return(err);
  551.         }
  552.     }
  553.  
  554.     doPrompt = (
  555.         (saveMode == kSaveAs) ||
  556.         ((*frHndl)->fileState.refNum == kInvalRefNum)
  557.     );
  558.  
  559.     if (doPrompt) {
  560.         /* Prompt with SFGetFile if doing a Save As or have never saved before. */
  561.  
  562.         if (!DisplayPutFile(&reply)) return(userCanceledErr);    /* User canceled the save. */
  563.  
  564.         resWasOpen = (*frHndl)->fileState.resRefNum;
  565.         if ((*frHndl)->fileState.sfType != MovieFileType) {
  566.             if ((*frHndl)->fileState.refNum != kInvalRefNum) {
  567.                 CloseDocResFile(frHndl);        /* Close resource fork, if opened. */
  568.                 FSClose((*frHndl)->fileState.refNum);
  569.             }            /* Close the old file.  Don't respond to any error here because
  570.                         ** the user may be trying to do a save-as because their old file
  571.                         ** is bad.  If we fail to close the old file, and then respond
  572.                         ** to the error, the user won't get the opportunity to save
  573.                         ** their document to a new file. */
  574.  
  575.             (*frHndl)->fileState.refNum      = kInvalRefNum;
  576.             (*frHndl)->fileState.fss.vRefNum = kInvalVRefNum;
  577.             err = Create_OpenFile(&reply.sfFile, &refNum, (*frHndl)->fileState.sfType);
  578.             if (err) return(err);
  579.         }
  580.         else {
  581.             createFlags = createMovieFileDeleteCurFile;
  582.             err = CreateMovieFile(&reply.sfFile, gDocCreator, 0, createFlags, &refNum, nil);
  583.             if (err) return(err);
  584.             resID = 0;
  585.             err = AddMovieResource((*frHndl)->fileState.movie, refNum, &resID, nil);
  586.             if (err) return(err);
  587.             (*frHndl)->fileState.movieResID = resID;
  588.         }
  589.  
  590.         (*frHndl)->fileState.fss    = reply.sfFile;        /* This is the new file. */
  591.         (*frHndl)->fileState.refNum = refNum;
  592.  
  593.         if (resWasOpen) {
  594.             UseDocResFile(frHndl, &resWasOpen, fsRdWrPerm);
  595.             UseResFile(resWasOpen);
  596.         }
  597.  
  598.         if (window)
  599.             NewWindowTitle(window, nil);
  600.     }
  601.     else {
  602.         if ((*frHndl)->fileState.sfType == MovieFileType) {
  603.             movie  = (*frHndl)->fileState.movie;
  604.             refNum = (*frHndl)->fileState.refNum;
  605.             resID  = (*frHndl)->fileState.movieResID;
  606.             err = UpdateMovieResource(movie, refNum, resID, nil);
  607.             if (err) return(err);
  608.         }
  609.     }
  610.  
  611.     err = DoWriteDocument(frHndl);
  612.     if (err) return(err);
  613.  
  614.     (*frHndl)->fileState.docDirty = false;
  615.     (*frHndl)->fileState.readOnly = false;
  616.     return(noErr);
  617. }
  618.  
  619.  
  620.  
  621. /*****************************************************************************/
  622.  
  623.  
  624.  
  625. /* ConvertOldToNewSFReply
  626. **
  627. ** struct StandardFileReply {            struct SFReply {
  628. **     Boolean     sfGood;                <-    Boolean good;
  629. **     Boolean     sfReplacing;        <-    Boolean copy;
  630. **     OSType         sfType;                <-    OSType fType;
  631. **     FSSpec        sfFile;
  632. **                     vRefNum;        <-    real vRefnum from (short vRefNum)
  633. **                     parID;            <-    real dirID from (short vRefNum)
  634. **                     name;            <-    Str63 fName;
  635. **     ScriptCode    sfScript;            <-    iuSystemScript
  636. **     short         sfFlags;            <-    0
  637. **     Boolean     sfIsFolder;            <-    false
  638. **     Boolean     sfIsVolume;            <-    false
  639. **     long        sfReserved1;        <-    0
  640. **     short        sfReserved2;        <-    0
  641. ** };                                    };
  642. */
  643.  
  644. #pragma segment File
  645. void    ConvertOldToNewSFReply(SFReply *oldReply, StandardFileReply *newReply)
  646. {
  647.     OSErr        err;
  648.     long        ignoredProcID;
  649.  
  650.     newReply->sfGood        = oldReply->good;
  651.     newReply->sfReplacing    = oldReply->copy;        /* Correct assignment? */
  652.     newReply->sfType        = oldReply->fType;
  653.  
  654.     err = GetWDInfo(oldReply->vRefNum,
  655.                     &newReply->sfFile.vRefNum,
  656.                     &newReply->sfFile.parID,
  657.                     &ignoredProcID);
  658.     pcpy(newReply->sfFile.name, oldReply->fName);
  659.  
  660.     /* Punt on the rest... */
  661.     newReply->sfScript        = iuSystemScript;
  662.     newReply->sfFlags        = 0;
  663.     newReply->sfIsFolder    = false;
  664.     newReply->sfIsVolume    = false;
  665.     newReply->sfReserved1    = 0;
  666.     newReply->sfReserved2    = 0;
  667. }
  668.  
  669.  
  670.  
  671. /*****************************************************************************/
  672.  
  673.  
  674.  
  675. /* Opens the file specified by the passed FSSpec, creating it if it doesn't
  676. ** already exist. Returns the refnum of the open file to the application.
  677. ** File Manager errors are reported and returned. */
  678.  
  679. #pragma segment File
  680. static OSErr    Create_OpenFile(FSSpec *file, short *refNum, OSType sftype)
  681. {
  682.     OSErr    err;
  683.  
  684.     err = HCreate(file->vRefNum, file->parID, file->name, gDocCreator, sftype);
  685.     if (err == dupFNErr) {
  686.  
  687.         /* The user already told Standard File to replace the old file
  688.            so let's get rid of it. */
  689.  
  690.         HDelete(file->vRefNum, file->parID, file->name);
  691.  
  692.         /* Try creating it again. */
  693.         err = HCreate(file->vRefNum, file->parID, file->name, gDocCreator, sftype);
  694.     }
  695.  
  696.     if (!err) {
  697.         err = HOpenDF(file->vRefNum, file->parID, file->name, fsRdWrPerm, refNum);
  698.         if (err == paramErr)
  699.             err = HOpen(file->vRefNum, file->parID, file->name, fsRdWrPerm, refNum);
  700.         if (err)
  701.             HDelete(file->vRefNum, file->parID, file->name);
  702.     }
  703.  
  704.     return(err);
  705. }
  706.  
  707.  
  708.  
  709. /*****************************************************************************/
  710.  
  711.  
  712.  
  713. /* Simple routine to display a list of files with our file type. */
  714.  
  715. #pragma segment File
  716. Boolean    DisplayGetFile(StandardFileReply *reply, short typeListLen, SFTypeList typeList)
  717. {
  718.     Point        where = {100, 100};
  719.     SFReply        oldReply;
  720.  
  721.     if (gSystemVersion >= 0x0700)        /* If new standard file available... */
  722.         StandardGetFile(gSFGetFileFilterUPP, typeListLen, typeList, reply);
  723.  
  724.     else {
  725.         SFGetFile(where, "\pSelect a document to open.",
  726.                          gSFGetFileFilterUPP, typeListLen, typeList, nil, &oldReply);
  727.         ConvertOldToNewSFReply(&oldReply, reply);
  728.     }
  729.  
  730.     return(reply->sfGood);
  731. }
  732.  
  733.  
  734.  
  735. /*****************************************************************************/
  736.  
  737.  
  738.  
  739. /* Displays the StandardFile PutFile dialog box. Fills out the passed reply
  740. ** record, and returns the sfGood field as a result. */
  741.  
  742. #pragma segment File
  743. Boolean    DisplayPutFile(StandardFileReply *reply)
  744. {
  745.     Str255        prompt;
  746.     Point        where = {100, 100};
  747.     SFReply        oldReply;
  748.  
  749.     GetIndString(prompt, rFileIOStrings, sSFprompt);
  750.  
  751.     if (gSystemVersion >= 0x0700)    /* If new standard file available... */
  752.         StandardPutFile(prompt, reply->sfFile.name, reply);
  753.     else {
  754.         SFPutFile(where, prompt, reply->sfFile.name, nil, &oldReply);
  755.         ConvertOldToNewSFReply(&oldReply, reply);
  756.     }
  757.  
  758.     return(reply->sfGood);
  759. }
  760.  
  761.  
  762.  
  763. /*****************************************************************************/
  764.  
  765.  
  766.  
  767. /* Use the resource fork for the designated document file.  This function
  768. ** also returns the old CurResFile, so you can set it back when you are done.
  769. ** Simply call this function, whether or not you have a resource fork.  If
  770. ** there isn't a resource fork, then one will be created.  If there is one,
  771. ** but it isn't open yet, it will be opened.  If it is already opened, it
  772. ** sets it as the current resource fork.  What more do you want? */
  773.  
  774. #define fcbFlgRBit 0x200
  775.  
  776. #pragma segment File
  777. OSErr    UseDocResFile(FileRecHndl frHndl, short *oldRes, char perm)
  778. {
  779.     OSErr        err;
  780.     FSSpec        fss;
  781.     short        res, vrn;
  782.     long        pid;
  783.     FCBPBRec    pb;
  784.     CInfoPBRec    resOpen;
  785.  
  786.     if (oldRes)
  787.         *oldRes = CurResFile();
  788.  
  789.     if ((res = (*frHndl)->fileState.resRefNum) != kInvalRefNum) {
  790.         UseResFile(res);        /* If the resource fork already open, use it. */
  791.         return(ResError());
  792.     }
  793.  
  794.     SetMem(&pb, 0, sizeof(FCBPBRec));            /* Make most of the param block happy. */
  795.     pb.ioRefNum = res = (*frHndl)->fileState.refNum;
  796.     err = PBGetFCBInfoSync(&pb);
  797.     if (err) return(err);
  798.     if (pb.ioFCBFlags & fcbFlgRBit) {
  799.         (*frHndl)->fileState.resRefNum = res;
  800.         UseResFile(res);
  801.         return(ResError());
  802.     }
  803.  
  804.     fss = (*frHndl)->fileState.fss;
  805.     vrn = fss.vRefNum;
  806.     pid = fss.parID;
  807.  
  808.     SetMem(&resOpen, 0, sizeof(CInfoPBRec));    /* Make most of the param block happy. */
  809.     resOpen.hFileInfo.ioVRefNum = vrn;
  810.     resOpen.hFileInfo.ioDirID   = pid;
  811.     resOpen.hFileInfo.ioNamePtr = fss.name;
  812.     err = PBGetCatInfoSync(&resOpen);
  813.     if (err) return(err);
  814.     if (resOpen.hFileInfo.ioFlAttrib & 0x04) {
  815.         /* The 0x04 is to look at the bit that says whether or not the resource fork is
  816.         ** already open.  Why did we do this?  To keep from re-opening a resource fork.
  817.         ** Reopening it actually works, but things get a bit ugly when it is closed.  The
  818.         ** one-and-only reference to the open resource fork would get closed if we thought
  819.         ** we opened it.  This keeps us from doing way-bad things later. */
  820.         UseResFile(resOpen.hFileInfo.ioFRefNum);
  821.         return(ResError());
  822.     }
  823.  
  824.     if ((*frHndl)->fileState.readOnly)
  825.         if (perm  & fsWrPerm) perm ^= fsWrPerm;
  826.  
  827.     SetResLoad(false);
  828.     res = HOpenResFile(vrn, pid, fss.name, perm);
  829.     err = ResError();
  830.     SetResLoad(true);
  831.         /* Try opening the resource fork. */
  832.  
  833.     if (err) {
  834.         if (err != eofErr) return(err);                    /* Some errors we can't handle here. */
  835.         HCreateResFile(vrn, pid, fss.name);                /* No resource fork, so create one.  */
  836.         err = ResError();
  837.         if (err) return(err);                            /* Error creating the resource fork. */
  838.         res = HOpenResFile(vrn, pid, fss.name, perm);    /* Now that it exists, open it.  */
  839.         err = ResError();                                /* Return whatever error occurs. */
  840.     }
  841.  
  842.     if (!err) {        /* If no error, then we can use the resource fork. */
  843.         (*frHndl)->fileState.resRefNum = res;
  844.         UseResFile(res);
  845.         err = ResError();
  846.     }
  847.  
  848.     return(err);
  849. }
  850.  
  851.  
  852.  
  853. /*****************************************************************************/
  854.  
  855.  
  856.  
  857. /* If there is a resource fork open for this document, this closes it. */
  858.  
  859. #pragma segment File
  860. OSErr    CloseDocResFile(FileRecHndl frHndl)
  861. {
  862.     short    res;
  863.  
  864.     if ((res = (*frHndl)->fileState.resRefNum) == kInvalRefNum) return(noErr);
  865.         /* If it was never opened, then there's nothing to close. */
  866.  
  867.     if ((*frHndl)->fileState.refNum == res) {
  868.         (*frHndl)->fileState.resRefNum = kInvalRefNum;
  869.         return(noErr);
  870.     }
  871.  
  872.     CloseResFile(res);                                    /* Close the resource fork. */
  873.     (*frHndl)->fileState.resRefNum = kInvalRefNum;        /* Mark it as closed. */
  874.  
  875.     return(ResError());
  876. }
  877.  
  878.  
  879.  
  880. /*****************************************************************************/
  881.  
  882.  
  883.  
  884. #pragma segment File
  885. long    GetModNum(void)
  886. {
  887.     static    modNum = 0;
  888.  
  889.     return(++modNum);
  890. }
  891.  
  892.  
  893.  
  894. /*****************************************************************************/
  895.  
  896.  
  897.  
  898. /* This function returns the state of the document.  If the document
  899. ** is dirty, then true is returned.  If the document is clean, then false
  900. ** is returned. */
  901.  
  902. #pragma segment File
  903. Boolean    GetDocDirty(FileRecHndl frHndl)
  904. {
  905.     if (frHndl) return((*frHndl)->fileState.docDirty);
  906.     return(false);
  907. }
  908.  
  909.  
  910.  
  911. /*****************************************************************************/
  912.  
  913.  
  914.  
  915. /* This function returns the state of the window's document.  If the document
  916. ** is dirty, then true is returned.  If the document is clean, or the window
  917. ** has no document, then false is returned. */
  918.  
  919. #pragma segment File
  920. Boolean    GetWindowDirty(WindowPtr window)
  921. {
  922.     if (IsAppWindow(window)) return(GetDocDirty((FileRecHndl)GetWRefCon(window)));
  923.     return(false);
  924. }
  925.  
  926.  
  927.  
  928. /*****************************************************************************/
  929.  
  930.  
  931.  
  932. #pragma segment File
  933. void    SetDocDirty(FileRecHndl frHndl)
  934. {
  935.     if (frHndl) {
  936.         if (!((*frHndl)->fileState.attributes & kwRuntimeOnlyDoc)) {
  937.             (*frHndl)->fileState.docDirty = true;
  938.             (*frHndl)->fileState.modNum   = GetModNum();
  939.             (*frHndl)->fileState.modTick  = TickCount();
  940.         }
  941.     }
  942. }
  943.  
  944.  
  945.  
  946. /*****************************************************************************/
  947.  
  948.  
  949.  
  950. #pragma segment File
  951. void    SetWindowDirty(WindowPtr window)
  952. {
  953.     if (IsAppWindow(window))
  954.         SetDocDirty((FileRecHndl)GetWRefCon(window));
  955. }
  956.  
  957.  
  958.  
  959. /*****************************************************************************/
  960.  
  961.  
  962.  
  963. /*  The SetDefault function sets the default volume and directory to the volume specified
  964. **  by newVRefNum and the directory specified by newDirID. The current default volume 
  965. **  and directory are returned in oldVRefNum and oldDir and should be used to restore 
  966. **  things to their previous condition *as soon as possible* with the RestoreDefault 
  967. **  function. These two functions are designed to be used as a wrapper around
  968. **  Standard C I/O routines where the location of the file is implied to be the
  969. **  default volume and directory. In other words, this is how you should use these
  970. **  functions:
  971. **
  972. **        err = SetDefault(newVRefNum, newDirID, &oldVRefNum, &oldDirID);
  973. **        if (!err)
  974. **            {
  975. **                -- call the Stdio functions like remove, rename, tmpfile, fopen,   --
  976. **                -- freopen, etc. or non-ANSI extentions like fdopen, fsetfileinfo, --
  977. **                -- create, open, unlink, etc. here!                                   --
  978. **
  979. **                err = RestoreDefault(oldVRefNum, oldDirID);
  980. **            }
  981. **
  982. **  By using these functions as a wrapper, you won't need to open a working directory 
  983. **  (because they use HSetVol) and you won't have to worry about the effects of using
  984. **  HSetVol (documented in Technical Note #140: Why PBHSetVol is Dangerous 
  985. **  and in the Inside Macintosh: Files book in the description of the HSetVol and 
  986. **  PBHSetVol functions) because the default volume/directory is restored before 
  987. **  giving up control to code that might be affected by HSetVol.
  988. ** Use this and the below call instead of the old-style FSpSetWD and FSpResetWD. */
  989.  
  990. #pragma segment File
  991. OSErr    SetDefault(short newVRefNum, long newDirID, short *oldVRefNum, long *oldDirID)
  992. {
  993.     OSErr    err;
  994.  
  995.     err = HGetVol(nil, oldVRefNum, oldDirID);
  996.         /* Get the current default volume/directory. */
  997.  
  998.     if (!err)
  999.         err = HSetVol(nil, newVRefNum, newDirID);
  1000.             /* Set the new default volume/directory */
  1001.  
  1002.     return(err);
  1003. }
  1004.  
  1005.  
  1006.  
  1007. /*****************************************************************************/
  1008.  
  1009.  
  1010.  
  1011. #pragma segment File
  1012. OSErr    RestoreDefault(short oldVRefNum, long oldDirID)
  1013. {
  1014.     OSErr    err;
  1015.     short    defaultVRefNum;
  1016.     long    defaultDirID;
  1017.     long    defaultProcID;
  1018.  
  1019.     err = GetWDInfo(oldVRefNum, &defaultVRefNum, &defaultDirID, &defaultProcID);
  1020.         /* Determine if the default volume was a wdRefNum. */
  1021.  
  1022.     if (!err) {
  1023.         /* Restore the old default volume/directory, one way or the other. */
  1024.  
  1025.         if (defaultDirID != fsRtDirID)
  1026.             err = SetVol(nil, oldVRefNum);
  1027.                 /* oldVRefNum was a wdRefNum - use SetVol */
  1028.         else
  1029.             err = HSetVol(nil, oldVRefNum, oldDirID);
  1030.                 /* oldVRefNum was a real vRefNum - use HSetVol */
  1031.     }
  1032.  
  1033.     return(err);
  1034. }
  1035.  
  1036.  
  1037.  
  1038. /*****************************************************************************/
  1039. /*****************************************************************************/
  1040. /*****************************************************************************/
  1041.  
  1042.  
  1043.  
  1044. /* Get the vRefNum and dirID of a file, which is its location. */
  1045.  
  1046. #pragma segment File
  1047. OSErr    GetFileLocation(short refNum, short *vRefNum, long *dirID, StringPtr fileName)
  1048. {
  1049.     FCBPBRec pb;
  1050.     OSErr err;
  1051.  
  1052.     pb.ioNamePtr = fileName;
  1053.     pb.ioVRefNum = 0;
  1054.     pb.ioRefNum  = refNum;
  1055.     pb.ioFCBIndx = 0;
  1056.  
  1057.     err = PBGetFCBInfoSync(&pb);
  1058.  
  1059.     if (vRefNum)
  1060.         *vRefNum = pb.ioFCBVRefNum;
  1061.     if (dirID)
  1062.         *dirID = pb.ioFCBParID;
  1063.  
  1064.     return(err);
  1065. }
  1066.  
  1067.  
  1068.  
  1069. /*****************************************************************************/
  1070.  
  1071.  
  1072.  
  1073. /* After getting a resource, you can't actually be sure that it came from the current
  1074. ** resource file.  Even if you make a call such as Get1Resource, starting with system 7.1,
  1075. ** you can't really be sure that it came from the current resource file.  (The resource
  1076. ** files may be overridden, or they may be flagged to be extended, as is the case with
  1077. ** font files.)  This checks to see that the resource actually came from the current
  1078. ** resource file.  If it didn't, then the handle returned is nil, and the error returned
  1079. ** is resNotFound.  (You probably don't need this function unless you are doing some kind
  1080. ** of resource-editing function.) */
  1081.  
  1082. #pragma segment File
  1083. OSErr    CurResOnly(Handle *hndl)
  1084. {
  1085.     short    cr, hr;
  1086.     OSErr    err;
  1087.  
  1088.     cr  = CurResFile();
  1089.     hr  = HomeResFile(*hndl);
  1090.     err = ResError();
  1091.     if (hr == -1) err = resNotFound;
  1092.     if (!hr)      hr  = 2;                /* Home res file is the system file. */
  1093.  
  1094.     if (cr != hr) *hndl = nil;
  1095.     return(err);
  1096. }
  1097.  
  1098.  
  1099.  
  1100.