home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / apps.to.go / DTS.Lib / TreeObj.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-18  |  60.6 KB  |  2,450 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:     DTS.Lib
  5. ** File:        TreeObj.c
  6. ** Written by:  Eric Soldan
  7. **
  8. ** Copyright © 1992-1993 Apple Computer, Inc.
  9. ** All rights reserved.
  10. */
  11.  
  12. /* You may incorporate this sample code into your applications without
  13. ** restriction, though the sample code has been provided "AS IS" and the
  14. ** responsibility for its operation is 100% yours.  However, what you are
  15. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  16. ** after having made changes. If you're going to re-distribute the source,
  17. ** we require that you make it clear in the source that the code was
  18. ** descended from Apple Sample Code, but that you've made changes. */
  19.  
  20. #include "DTS.Lib2.h"
  21. #include "DTS.Lib.protos.h"
  22.  
  23. #ifndef __FILES__
  24. #include <Files.h>
  25. #endif
  26.  
  27. //#ifndef __STDIO__
  28. //#include <StdIO.h>
  29. //#endif
  30.  
  31. //#ifndef __STRING__
  32. //#include <String.h>
  33. //#endif
  34.  
  35. #ifndef __TREEOBJ__
  36. #include "TreeObj.h"
  37. #endif
  38.  
  39.  
  40.  
  41. #define NEW_CHILD     1
  42. #define DISPOSE_CHILD 2
  43. #define MOVE_CHILD    3
  44. #define SWAP_CHILDREN 4
  45. #define CHANGE_CHILD  5
  46.  
  47.  
  48.  
  49. extern TreeObjProcPtr    gTreeObjMethods[];
  50. extern long                gMinTreeObjSize[];
  51.  
  52. static TreeObjHndl    CopyOneChild(TreeObjHndl chndl, TreeObjHndl copyToHndl, short copyCNum);
  53. static OSErr        ReadBranch(TreeObjHndl hndl, short fileRefNum);
  54. static OSErr        ReadTreeObjHeader(TreeObjHndl hndl, short fileRefNum);
  55. static OSErr        WriteTreeObjHeader(TreeObjHndl hndl, short fileRefNum);
  56.  
  57. static void            DoNumberTree0(TreeObjHndl hndl);
  58. static TreeObjHndl    GetUndoTaskHndl(TreeObjHndl undo, short editType);
  59. static TreeObjHndl    NewUndoPart(TreeObjHndl taskHndl, short action, TreeObjHndl shndl, short cnum, TreeObjHndl dhndl, short dcnum, Boolean deepCopy);
  60.  
  61. static OSErr        PostNewChild(short editType, TreeObjHndl phndl, short cnum);
  62. static OSErr        PostDisposeChild(short editType, TreeObjHndl phndl, short cnum);
  63. static void            PostMoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum);
  64. static void            PostSwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb);
  65.  
  66. static void            UndoNewChild(TreeObjHndl undoPart);
  67. static void            UndoDisposeChild(TreeObjHndl undoPart);
  68. static void            UndoMoveChild(TreeObjHndl undoPart);
  69. static void            UndoModifyChild(TreeObjHndl undoPart);
  70. static void            UndoModifyChildren(TreeObjHndl dhndl, TreeObjHndl uhndl, Boolean deepCopy);
  71. static void            UndoSwapChildren(TreeObjHndl undoPart);
  72.  
  73.  
  74.  
  75. /**********************************************************************/
  76.  
  77.  
  78.  
  79. /* Creates an object with no parent.  This object will be the root object
  80. ** for a tree.  Returns nil upon failure. */
  81.  
  82. #pragma segment TreeObj
  83. TreeObjHndl    NewRootObj(short ctype, long size)
  84. {
  85.     TreeObjHndl        root;
  86.     TreeObjPtr        ptr;
  87.     char            *cptr;
  88.     short            i;
  89.     TreeObjProcPtr    proc;
  90.  
  91.     if (size < gMinTreeObjSize[ctype])
  92.         size = gMinTreeObjSize[ctype];
  93.             /* Ensure a minimally-sized object. */
  94.  
  95.     root = (TreeObjHndl)NewHandle(sizeof(TreeObj) + size);
  96.     if (!root) return(nil);
  97.         /* Oh well...
  98.         ** Note that since we are creating an orphan, it isn't possible to purge
  99.         ** old undos to possibly make space.  This means that for anyone calling
  100.         ** NewRootObj to get a handlewho considers it okay to purge old undos to
  101.         ** get the memory if necessary, has to do the following:
  102.         ** 1)  Call NewRootObj
  103.         ** 2)  If failure, call PurgeUndo for a particular document to purge the
  104.         **     oldest undo for that document to free up ram.
  105.         ** 3)  If PurgeUndo returns true, then something was purged and there is more
  106.         **     ram available.  Loop back to step 1 and try to create the object again.
  107.         ** 4)  If PurgeUndo returns false, then there really isn't any more ram.  One
  108.         **     possibility is to try purging undos from other documents, but this is
  109.         **     probably rude. */
  110.  
  111.     cptr = GetDataPtr(root);
  112.     for (i = 0; i < size; ++i) cptr[i] = 0;
  113.         /* Initialize data area to 0's.  This is very nice of us. */
  114.  
  115.     ptr = *root;                     /* Deref root object once.      */
  116.     ptr->type        = ctype;         /* Flag what object type it is. */
  117.     ptr->numChildren = 0;            /* Give root 0 children.        */
  118.     ptr->dataSize    = size;        /* Remember the data area size. */
  119.     ptr->treeID      = 0;            /* Requisite do-nothing field.  */
  120.     ptr->parent      = nil;            /* We aren't owned yet.         */
  121.  
  122.     proc = gTreeObjMethods[ctype];
  123.     if (proc) {                                            /* If this object type has a proc...   */
  124.         if ((*proc)(root, INITMESSAGE, CREATEINIT)) {    /* Call the proc with an init message. */
  125.             DisposeHandle((Handle)root);                /* If the init complains, it's no go.  */
  126.             return(nil);
  127.         }
  128.     }
  129.  
  130.     return(root);    /* Success.  Return the handle. */
  131. }
  132.  
  133.  
  134.  
  135. /**********************************************************************/
  136.  
  137.  
  138.  
  139. /* Creates a child of the specified type and adds it to the parent at the
  140. ** specified location.  Returns nil upon failure. */
  141.  
  142. #pragma segment TreeObj
  143. TreeObjHndl    NewChild(short editType, TreeObjHndl phndl, short cnum, short ctype, long size)
  144. {
  145.     TreeObjHndl        chndl;
  146.     TreeObjProcPtr    proc;
  147.  
  148.     for (;;) {
  149.         chndl = NewRootObj(ctype, size);    /* Let somebody else do the creative work. */
  150.         if (chndl) break;                    /* We succeeded at creating an orphan. */
  151.         if (!PurgeUndo(phndl)) return(nil);
  152.             /* Oh well...
  153.             ** There is really no memory.  We even purged the undos to try to make
  154.             ** room, but we still didn't have enough ram to create the orphan object. */
  155.     };
  156.  
  157.     /* Now that we have successfully created an orphan object, make it someone's child. */
  158.  
  159.     GetChildHndlPtr(phndl, &cnum, 1);
  160.         /* Adjust cnum to within bounds.  See GetChildHndlPtr() for more info. */
  161.  
  162.     if (InsertChildHndl(phndl, chndl, cnum)) {
  163.             /* Couldn't insert the child into the parent handle table. */
  164.         proc = gTreeObjMethods[ctype];
  165.         if (proc)
  166.             (*proc)(chndl, FREEMESSAGE, 0);
  167.                 /* Since we couldn't attatch, call the object with a free message so that
  168.                 ** it can get rid of any additional memory that was allocated when an
  169.                 ** init message was sent to it. */
  170.         DisposeHandle((Handle)chndl);
  171.             /* After the free message, the object now consists of a single handle.
  172.             ** Dispose this handle and it is history completely. */
  173.         return(nil);
  174.     }
  175.  
  176.     if (PostNewChild(editType, phndl, cnum)) {
  177.         DisposeChild(NO_EDIT, phndl, cnum);
  178.         chndl = nil;
  179.     }
  180.  
  181.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOTODOC);
  182.     return(chndl);
  183. }
  184.  
  185.  
  186.  
  187. /**********************************************************************/
  188.  
  189.  
  190.  
  191. /* Disposes of the specified child and all offspring of that child. */
  192.  
  193. #pragma segment TreeObj
  194. void    DisposeChild(short editType, TreeObjHndl phndl, short cnum)
  195. {
  196.     TreeObjHndl    chndl;
  197.     static long    longZero;        /* For 68000 lame long addressing. */
  198.  
  199.     if (GetChildHndlPtr(phndl, &cnum, 0)) {
  200.         /* This test checks that there are children in the table, plus it
  201.         ** also adjusts cnum to legit values, if possible. */
  202.  
  203.         chndl = GetChildHndl(phndl, cnum);
  204.  
  205.         if (editType) {
  206.             if (PostDisposeChild(editType, phndl, cnum)) {
  207.                     /* Posting a DisposeChild can actually take more ram.
  208.                     ** The reason for this is that the object isn't disposed of.
  209.                     ** It is moved into the undo, plus information as to where to
  210.                     ** put it back is kept.  If PostDisposeChild fails, then we
  211.                     ** are in a bad way memory-wise, so we really need to let the
  212.                     ** dispose occur.  To accomplish this, we disable undos for
  213.                     ** this document, and then we go ahead and allow a straight
  214.                     ** dispose of the child.  This will get the job done, plus it
  215.                     ** will free up some ram.
  216.                     ** The reason that undos are disabled is that the edit that
  217.                     ** is occuring may have multiple operations.  We want to stop
  218.                     ** collection of the remaining operations for this edit, as
  219.                     ** it wouldn't be a complete undo anyway.  The undos will
  220.                     ** be enabled again when NewUndo is called to start a new edit. */
  221.                 DisableUndo(phndl);
  222.                     /* Undo collection off temporarily, plus all undos are purged,
  223.                     ** thus freeing up ram. */
  224.                 editType = 0;
  225.             }
  226.         }
  227.  
  228.         if (!editType) {
  229.             DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOFROMDOC);
  230.                 /* If the dispose was successfully posted, then the object got a message that
  231.                 ** it was leaving the document via PostDisposeChild calling MoveChild.  The
  232.                 ** only way that PostDisposeChild can fail (and therefore not call MoveChild)
  233.                 ** is if an undoTask couldn't be created, and this is prior to MoveChild being
  234.                 ** called.  What all this means is that if we are here, the object hadn't yet
  235.                 ** received a message about leaving the document. */
  236.             DisposeObjAndOffspring(GetChildHndl(phndl, cnum));
  237.             BlockMove(&longZero, GetChildHndlPtr(phndl, &cnum, 0), sizeof(long));
  238.                 /* Since the child no longer exists, DeleteChildHndl can't set
  239.                 ** the parent reference for the child to nil.  Setting the reference
  240.                 ** to the child prevents DeleteChildHndl from doing this. */
  241.             DeleteChildHndl(phndl, cnum);
  242.         }
  243.     }
  244. }
  245.  
  246.  
  247.  
  248. /**********************************************************************/
  249.  
  250.  
  251.  
  252. /* Copies the specified child (and all offspring of that child if deepCopy is true). */
  253.  
  254. #pragma segment TreeObj
  255. TreeObjHndl    CopyChild(short editType, TreeObjHndl shndl, short scnum,
  256.                       TreeObjHndl dhndl, short dcnum, Boolean deepCopy)
  257. {
  258.     TreeObjHndl    chndl, copyHndl;
  259.  
  260.     if (!GetChildHndlPtr(shndl, &scnum, 0)) return(nil);
  261.         /* Adjust scnum to within bounds, if possible. */
  262.  
  263.     GetChildHndlPtr(dhndl, &dcnum, 1);
  264.         /* Adjust dcnum to within bounds. */
  265.  
  266.     copyHndl = CopyOneChild(chndl = GetChildHndl(shndl, scnum), dhndl, dcnum);
  267.     if (!copyHndl) return(nil);
  268.  
  269.     if (deepCopy) {
  270.         if (CopyChildren(chndl, copyHndl)) {        /* Copy child's children. */
  271.             DisposeChild(NO_EDIT, dhndl, dcnum);
  272.             return(nil);
  273.         }
  274.     }
  275.  
  276.     if (PostNewChild(editType, dhndl, dcnum)) {
  277.         DisposeChild(NO_EDIT, dhndl, dcnum);
  278.         return(nil);
  279.     }
  280.  
  281.     DoTreeObjMethod(copyHndl, UNDOMESSAGE, UNDOTODOC);
  282.     return(copyHndl);
  283. }
  284.  
  285.  
  286.  
  287. /**********************************************************************/
  288.  
  289.  
  290.  
  291. /* Moves a child from one location on the tree to another. */
  292.  
  293. #pragma segment TreeObj
  294. OSErr    MoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum)
  295. {
  296.     TreeObjHndl    chndl;
  297.  
  298.     if (!GetChildHndlPtr(shndl, &scnum, 0)) return(paramErr);
  299.         /* Adjust scnum to within bounds.  Adjustment not possible if no children. */
  300.  
  301.     GetChildHndlPtr(dhndl, &dcnum, 1);        /* Adjust dcnum to within bounds. */
  302.  
  303.     chndl = GetChildHndl(shndl, scnum);
  304.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOFROMDOC);
  305.     DeleteChildHndl(shndl, scnum);
  306.  
  307.     GetChildHndlPtr(dhndl, &dcnum, 1);
  308.         /* Adjust dcnum to within bounds.  It may be just out of bounds if shndl
  309.         ** and dhndl are the same.  After the DeleteChildHndl, dcnum may be 2 past
  310.         ** the end, instead of the allowable 1 for a target. */
  311.  
  312.     InsertChildHndl(dhndl, chndl, dcnum);
  313.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOTODOC);
  314.  
  315.     PostMoveChild(editType, shndl, scnum, dhndl, dcnum);
  316.     return(noErr);
  317. }
  318.  
  319.  
  320.  
  321. /**********************************************************************/
  322.  
  323.  
  324.  
  325. /* Saves a copy of the child in the undo hierarchy for undo/redo purposes. */
  326.  
  327. #pragma segment TreeObj
  328. OSErr    ModifyChild(short editType, TreeObjHndl phndl, short cnum, Boolean deepCopy)
  329. {
  330.     TreeObjHndl    undo, task, part, copy;
  331.     short        i;
  332.     OSErr        err;
  333.  
  334.     if (!editType) return(noErr);
  335.  
  336.     err  = noErr;
  337.     copy = nil;
  338.     undo = GetUndoHndl(phndl);
  339.  
  340.     if (mDerefUndo(undo)->disabled)
  341.         copy = phndl;        /* Used for flag purposes. */
  342.  
  343.     else {
  344.         task = GetUndoTaskHndl(undo, editType);
  345.         if (task) {
  346.             for (i = (*task)->numChildren; i;) {
  347.                 part = GetChildHndl(task, --i);
  348.                 if (mDerefUndoPart(part)->actionType == CHANGE_CHILD)
  349.                     if ((mDerefUndoPart(part)->shndl ==phndl) && (mDerefUndoPart(part)->scnum ==cnum))
  350.                         return(noErr);
  351.                             /* Child is already posted as changed for this undo editType. */
  352.             }
  353.  
  354.             part = NewUndoPart(task, CHANGE_CHILD, phndl, cnum, nil, 0, deepCopy);
  355.             if (part)
  356.                 copy = CopyChild(NO_EDIT, phndl, cnum, part, -1, deepCopy);
  357.         }
  358.     }
  359.  
  360.     if (!copy) {
  361.         DisableUndo(phndl);
  362.         err = memFullErr;
  363.     }
  364.  
  365.     return(err);
  366. }
  367.  
  368.  
  369.  
  370. /**********************************************************************/
  371.  
  372.  
  373.  
  374. /* Swaps the child handles. */
  375.  
  376. #pragma segment TreeObj
  377. OSErr    SwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb)
  378. {
  379.     TreeObjHndl    *tptra, *tptrb, hndl;
  380.  
  381.     if (!(tptra = GetChildHndlPtr(hndla, &cnuma, 0))) return(paramErr);
  382.     if (!(tptrb = GetChildHndlPtr(hndlb, &cnumb, 0))) return(paramErr);
  383.  
  384.     BlockMove(tptra, &hndl, sizeof(TreeObjHndl));
  385.     BlockMove(tptrb, tptra, sizeof(TreeObjHndl));
  386.     BlockMove(&hndl, tptra, sizeof(TreeObjHndl));
  387.  
  388.     (*GetChildHndl(hndla, cnuma))->parent = hndla;
  389.     (*GetChildHndl(hndlb, cnumb))->parent = hndlb;
  390.  
  391.     PostSwapChildren(editType, hndla, cnuma, hndlb, cnumb);
  392.     return(noErr);
  393. }
  394.  
  395.  
  396.  
  397. /**********************************************************************/
  398.  
  399.  
  400.  
  401. /* Swaps the child data without swapping the handles. */
  402.  
  403. #pragma segment TreeObj
  404. OSErr    SwapTreeObjData(TreeObjHndl hndla, TreeObjHndl hndlb)
  405. {
  406.     TreeObjHndl    hndl;
  407.     long        size, sizea, sizeb, i;
  408.     char        *ptra, *ptrb, c;
  409.     OSErr        err;
  410.  
  411.     sizea = (*hndla)->dataSize;
  412.     sizeb = (*hndlb)->dataSize;
  413.     if (sizea > sizeb) {        /* Make hndla the small one and hndlb the big one. */
  414.         size  = sizea;
  415.         sizea = sizeb;
  416.         sizeb = size;
  417.         hndl  = hndla;
  418.         hndla = hndlb;
  419.         hndlb = hndl;
  420.     }
  421.  
  422.     err = SetDataSize(hndla, sizeb);        /* Make the small one big. */
  423.     if (!err) {
  424.         ptra = GetDataPtr(hndla);
  425.         ptrb = GetDataPtr(hndlb);
  426.         for (i = 0; i < sizeb; ++i) {
  427.             c = ptra[i];
  428.             ptra[i] = ptrb[i];
  429.             ptrb[i] = c;
  430.         }
  431.         SetDataSize(hndlb, sizea);            /* Make the big one small. */
  432.     }
  433.  
  434.     return(err);
  435. }
  436.  
  437.  
  438.  
  439. /**********************************************************************/
  440. /**********************************************************************/
  441.  
  442.  
  443.  
  444. /* This disposes of the object and any offspring of that object.  It does not remove
  445. ** the object from the parent's child handle table. */
  446.  
  447. #pragma segment TreeObj
  448. void    DisposeObjAndOffspring(TreeObjHndl ohndl)
  449. {
  450.     short            nc;
  451.     TreeObjProcPtr    proc;
  452.  
  453.     if (!ohndl) return;
  454.  
  455.     proc = gTreeObjMethods[(*ohndl)->type];
  456.     if (proc)
  457.         (*proc)(ohndl, FREEMESSAGE, 0);
  458.             /* If the object has any additional deallocation to do, let it do it. */
  459.  
  460.     while ((nc = (*ohndl)->numChildren) != 0) {
  461.         DisposeObjAndOffspring(GetChildHndl(ohndl, nc - 1));
  462.         (*ohndl)->numChildren--;
  463.     }
  464.     DisposeHandle((Handle)ohndl);
  465. }
  466.  
  467.  
  468.  
  469. /**********************************************************************/
  470.  
  471.  
  472.  
  473. /* Copies the children (and children below that and so on) of one object to
  474. ** another object.  Used internally by CopyChild for deep copies. */
  475.  
  476. #pragma segment TreeObj
  477. OSErr    CopyChildren(TreeObjHndl shndl, TreeObjHndl dhndl)
  478. {
  479.     TreeObjHndl    chndl, copyHndl;
  480.     short    i;
  481.     OSErr    err;
  482.  
  483.     for (i = (*shndl)->numChildren; i;) {
  484.         chndl    = GetChildHndl(shndl, --i);
  485.         copyHndl = CopyOneChild(chndl, dhndl, 0);
  486.         if (!copyHndl) return(memFullErr);
  487.         err = CopyChildren(chndl, copyHndl);
  488.         if (err) return(err);
  489.     }
  490.     return(noErr);
  491. }
  492.  
  493.  
  494.  
  495. /**********************************************************************/
  496.  
  497.  
  498.  
  499. /* Used internally by CopyChild for shallow copies. */
  500.  
  501. #pragma segment TreeObj
  502. static TreeObjHndl    CopyOneChild(TreeObjHndl chndl, TreeObjHndl copyToHndl, short copycnum)
  503. {
  504.     TreeObjHndl        copyHndl;
  505.     short            type;
  506.     long            size;
  507.     TreeObjProcPtr    proc;
  508.  
  509.     type = (*chndl)->type;
  510.     size = (*chndl)->dataSize;
  511.  
  512.  
  513.     proc = gTreeObjMethods[type];    /* Prevent NewChild() from calling the */
  514.     gTreeObjMethods[type] = nil;    /* object for INITMESSAGE. */
  515.  
  516.     copyHndl = NewChild(NO_EDIT, copyToHndl, copycnum, type, size);
  517.         /* NewChild takes care of bounds-checking for copycnum.  The child data has
  518.         ** not been fully initialized, as we prevented the INITMESSAGE. */
  519.  
  520.     gTreeObjMethods[type] = proc;    /* Re-enable messaging the object. */
  521.  
  522.     if (!copyHndl) return(nil);
  523.  
  524.     BlockMove(GetDataPtr(chndl), GetDataPtr(copyHndl), size);
  525.     if (proc) {
  526.         if ((*proc)(copyHndl, COPYMESSAGE, (long)chndl)) {
  527.             DisposeChild(NO_EDIT, copyHndl, copycnum);
  528.             return(nil);
  529.         }
  530.     }
  531.  
  532.     return(copyHndl);
  533. }
  534.  
  535.  
  536.  
  537. /**********************************************************************/
  538. /**********************************************************************/
  539.  
  540.  
  541.  
  542. /* Adds an existing child to a parent's child handle table. */
  543.  
  544. #pragma segment TreeObj
  545. OSErr    InsertChildHndl(TreeObjHndl phndl, TreeObjHndl chndl, short cnum)
  546. {
  547.     TreeObjHndl    *tptr;
  548.     TreeObjPtr    ptr;
  549.     long        oldSize, newSize, dhSize;
  550.     long        oldTblSize, tblOffset;
  551.     OSErr        err;
  552.  
  553.     oldSize = GetHandleSize((Handle)phndl);
  554.     dhSize  = sizeof(TreeObj) + (ptr = *phndl)->dataSize;        /* Data + header size. */
  555.  
  556.     oldTblSize = ptr->numChildren * sizeof(TreeObjHndl);
  557.  
  558.     newSize = dhSize + oldTblSize + sizeof(TreeObjHndl);
  559.     newSize |= 0x1F;
  560.     ++newSize;
  561.  
  562.     if (newSize > oldSize) {
  563.         SetHandleSize((Handle)phndl, newSize);
  564.         err = MemError();
  565.         if (err) return(err);
  566.         ptr = *phndl;
  567.     }
  568.  
  569.     tptr      = GetChildHndlPtr(phndl, &cnum, 1);
  570.     tblOffset = cnum * sizeof(TreeObjHndl);
  571.     BlockMove((char *)tptr, (char *)(tptr + 1), oldTblSize - tblOffset);
  572.  
  573.     ptr->numChildren++;
  574.     BlockMove(&chndl, tptr, sizeof(TreeObjHndl));
  575.     (*chndl)->parent = phndl;
  576.     return(noErr);
  577. }
  578.  
  579.  
  580.  
  581. /**********************************************************************/
  582.  
  583.  
  584.  
  585. /* Removes a child from the parent's child handle table. */
  586.  
  587. #pragma segment TreeObj
  588. void    DeleteChildHndl(TreeObjHndl phndl, short cnum)
  589. {
  590.     TreeObjHndl    *tptr;
  591.     TreeObjHndl    chndl;
  592.     TreeObjPtr    ptr;
  593.     long        dhSize, tblSize, tblOffset;
  594.  
  595.     if (!(tptr = GetChildHndlPtr(phndl, &cnum, 0))) return;
  596.     BlockMove(tptr, &chndl, sizeof(TreeObjHndl));
  597.  
  598.     dhSize    = sizeof(TreeObj) + (ptr = *phndl)->dataSize;
  599.     tblSize   = ptr->numChildren * sizeof(TreeObjHndl);
  600.     tblOffset = cnum * sizeof(TreeObjHndl);
  601.  
  602.     BlockMove((char *)(tptr + 1), (char *)tptr, tblSize - tblOffset - sizeof(TreeObjHndl));
  603.     SetHandleSize((Handle)phndl, ((dhSize + tblSize - sizeof(TreeObjHndl)) | 0x1F) + 1);
  604.     (*phndl)->numChildren--;
  605.  
  606.     if (chndl)
  607.         (*chndl)->parent = nil;
  608. }
  609.  
  610.  
  611.  
  612. /**********************************************************************/
  613.  
  614.  
  615.  
  616. /* Given an object handle, return the root handle. */
  617.  
  618. #pragma segment TreeObj
  619. TreeObjHndl    GetRootHndl(TreeObjHndl hndl)
  620. {
  621.     for (; (*hndl)->parent; hndl = (*hndl)->parent) {};
  622.     return(hndl);
  623. }
  624.  
  625.  
  626.  
  627. /**********************************************************************/
  628.  
  629.  
  630.  
  631. /* Given a parent handle and a child number, this returns the child handle. */
  632.  
  633. #pragma segment TreeObj
  634. TreeObjHndl    GetChildHndl(TreeObjHndl phndl, short cnum)
  635. {
  636.     TreeObjHndl    *tptr, th;
  637.  
  638.     if (!(tptr = GetChildHndlPtr(phndl, &cnum, 0))) return(nil);
  639.     BlockMove(tptr, &th, sizeof(TreeObjHndl));
  640.     return(th);
  641. }
  642.  
  643.  
  644.  
  645. /**********************************************************************/
  646.  
  647.  
  648.  
  649. /* Return a pointer into the child handle table.  This also validates (and corrects)
  650. ** cnum so that it is in range, if possible.  Depending on the usage, pointing to
  651. ** just after the child handle table is either valid or invalid.  If a handle is
  652. ** being added to the table, then pointing just after the table is valid.  If a
  653. ** handle is being removed or referenced, then pointing just after the table is
  654. ** invalid.  the parameter endCase determines which case we are dealing with.  If
  655. ** endCase is 0, then pointing just after the child handle table is invalid, and
  656. ** if the cnum value passed in causes this, then nil is returned for the pointer.
  657. ** if endCase is 1, then pointing just after the child handle table is okay, and
  658. ** therefore nil will never be returned as the pointer.  Any cnum value out of
  659. ** range will be corrected (if possible) to be within range. */
  660.  
  661. #pragma segment TreeObj
  662. TreeObjHndl    *GetChildHndlPtr(TreeObjHndl phndl, short *cnum, short endCase)
  663. {
  664.     TreeObjPtr    ptr;
  665.     short        nc;
  666.     long        dhSize, tblOffset;
  667.  
  668.     ptr = *phndl;
  669.     if (!((nc = ptr->numChildren) + endCase)) return(nil);
  670.  
  671.     if ((*cnum < 0) || (*cnum >= nc + endCase))
  672.         *cnum = nc - 1 + endCase;
  673.  
  674.     dhSize    = sizeof(TreeObj) + ptr->dataSize;
  675.     tblOffset = *cnum * sizeof(TreeObjHndl);
  676.     return((TreeObjHndl *)((char *)ptr + dhSize + tblOffset));
  677. }
  678.  
  679.  
  680.  
  681. /**********************************************************************/
  682.  
  683.  
  684.  
  685. /* Given a child handle, this returns the child number of that
  686. ** handle in the parent's child handle table. */
  687.  
  688. #pragma segment TreeObj
  689. short    GetChildNum(TreeObjHndl hndl)
  690. {
  691.     TreeObjHndl    phndl, *tptr, th;
  692.     short        nc, j;
  693.  
  694.     if (!(phndl = (*hndl)->parent)) return(-1);
  695.         /* Child doesn't have a parent, and therefore it is a root. */
  696.  
  697.     j = 0;
  698.     tptr = GetChildHndlPtr(phndl, &j, 0);
  699.     if (tptr) {
  700.         nc  = (*phndl)->numChildren;
  701.         for (; j < nc; ++j) {
  702.             BlockMove(tptr++, &th, sizeof(TreeObjHndl));
  703.             if (th == hndl) return(j);
  704.         }
  705.     }
  706.  
  707.     return(-1);
  708. }
  709.  
  710.  
  711.  
  712. /**********************************************************************/
  713. /**********************************************************************/
  714.  
  715.  
  716.  
  717. /* Adjusts the data size, either greater or smaller, depending on the sign of 'delta'. */
  718.  
  719. #pragma segment TreeObj
  720. OSErr    AdjustDataSize(TreeObjHndl hndl, long delta)
  721. {
  722.     TreeObjPtr    ptr;
  723.     long        dhSize, tblSize;
  724.     char        *cptr;
  725.     OSErr        err;
  726.  
  727.     ptr = *hndl;
  728.     tblSize = ptr->numChildren * sizeof (TreeObjHndl);
  729.     dhSize  = sizeof(TreeObj) + ptr->dataSize;
  730.  
  731.     if (!(delta & 0x80000000L)) {
  732.         SetHandleSize((Handle)hndl, ((dhSize + tblSize + delta) | 0x1F) + 1);
  733.         err = MemError();
  734.         if (err) return(err);
  735.         ptr = *hndl;
  736.     }
  737.  
  738.     cptr = (char *)ptr + dhSize;
  739.     BlockMove(cptr, cptr + delta, tblSize);
  740.     ptr->dataSize += delta;
  741.  
  742.     if (delta & 0x80000000L)
  743.         SetHandleSize((Handle)hndl, ((dhSize + tblSize + delta) | 0x1F) + 1);
  744.  
  745.     return(noErr);
  746. }
  747.  
  748.  
  749.  
  750. /**********************************************************************/
  751.  
  752.  
  753.  
  754. /* Sets the data size to newSize. */
  755.  
  756. #pragma segment TreeObj
  757. OSErr    SetDataSize(TreeObjHndl hndl, long newSize)
  758. {
  759.     return(AdjustDataSize(hndl, newSize - (*hndl)->dataSize));
  760. }
  761.  
  762.  
  763.  
  764. /**********************************************************************/
  765.  
  766.  
  767.  
  768. /* Slides some of the data in the data portion of the object starting at
  769. ** 'offset' by a 'delta' amount, either forward or backward, depending
  770. ** on the sign of 'delta'. */
  771.  
  772. #pragma segment TreeObj
  773. OSErr    SlideData(TreeObjHndl hndl, long offset, long delta)
  774. {
  775.     long    dataSize;
  776.     char    *cptr;
  777.     OSErr    err;
  778.  
  779.     dataSize = (*hndl)->dataSize;
  780.  
  781.     if (!(delta & 0x80000000L)) {
  782.         err = AdjustDataSize(hndl, delta);
  783.         if (err) return(err);
  784.     }
  785.  
  786.     cptr = (char *)GetDataPtr(hndl) + offset;
  787.     BlockMove(cptr, cptr + delta, dataSize - offset);
  788.  
  789.     if (delta & 0x80000000L)
  790.         AdjustDataSize(hndl, delta);
  791.  
  792.     return(noErr);
  793. }
  794.  
  795.  
  796.  
  797. /**********************************************************************/
  798.  
  799.  
  800.  
  801. /* Returns a pointer to the beginning of the object's data area.
  802. ** THIS DOES NOT LOCK THE HANDLE!!  The pointer may become invalid
  803. ** if memory moves. */
  804.  
  805. #pragma segment TreeObj
  806. void    *GetDataPtr(TreeObjHndl hndl)
  807. {
  808.     return(*hndl + 1);
  809. }
  810.  
  811.  
  812.  
  813. /**********************************************************************/
  814. /**********************************************************************/
  815.  
  816.  
  817.  
  818. /* Given an open file that has been previously written via WriteTree, this function
  819. ** is called to read the file data into ram.  The root object for the file is already
  820. ** created by InitDocument.  The file must be open, and the file position must be
  821. ** set to the byte location where the root object of the file starts.  Once this is
  822. ** so, just call this function with a reference to the root object and the open file
  823. ** refrence number. */
  824.  
  825. #pragma segment TreeObj
  826. OSErr    ReadTree(TreeObjHndl hndl, short fileRefNum)
  827. {
  828.     OSErr    err;
  829.  
  830.     err = ReadBranch(hndl, fileRefNum);
  831.     if (err) {
  832.         while ((*hndl)->numChildren)
  833.             DisposeChild(NO_EDIT, hndl, 0);
  834.     }
  835.     else {
  836.         DoNumberTree(hndl);
  837.         DoFTreeMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  838.     }
  839.  
  840.     return(err);
  841. }
  842.  
  843.  
  844.  
  845. /**********************************************************************/
  846.  
  847.  
  848.  
  849. /* This is an internal function for recursively reading the data from the
  850. ** open file. */
  851.  
  852. #pragma segment TreeObj
  853. static OSErr    ReadBranch(TreeObjHndl hndl, short fileRefNum)
  854. {
  855.     TreeObjHndl        chndl;
  856.     TreeObjProcPtr    proc;
  857.     short            numChildren, cnum;
  858.     OSErr            err;
  859.  
  860.     err = ReadTreeObjHeader(hndl, fileRefNum);
  861.     if (err) {
  862.         (*hndl)->numChildren = 0;    /* So we can dispose the portion that was read. */
  863.         return(err);
  864.     }
  865.  
  866.     numChildren = (*hndl)->numChildren;    /* So we can dispose of a partial read if */
  867.     (*hndl)->numChildren = 0;            /* we have a failure after this point.    */
  868.  
  869.     proc = gTreeObjMethods[(*hndl)->type];
  870.     if (proc)
  871.         err = (*proc)(hndl, FREADMESSAGE, fileRefNum);
  872.     else
  873.         err = ReadTreeObjData(hndl, fileRefNum);
  874.  
  875.     if (err) return(err);
  876.  
  877.     for (cnum = 0; cnum < numChildren; ++cnum) {
  878.         if (!(chndl = NewRootObj(EMPTYOBJ, 0))) return(memFullErr);
  879.         if (InsertChildHndl(hndl, chndl, cnum)) {
  880.             DisposeObjAndOffspring(chndl);
  881.             return(memFullErr);
  882.         }
  883.         err = ReadBranch(chndl, fileRefNum);
  884.         if (err) return(err);
  885.     }
  886.  
  887.     return(noErr);
  888. }
  889.  
  890.  
  891.  
  892. /**********************************************************************/
  893.  
  894.  
  895.  
  896. #pragma segment TreeObj
  897. static OSErr    ReadTreeObjHeader(TreeObjHndl hndl, short fileRefNum)
  898. {
  899.     TreeObj        header;
  900.     TreeObjHndl    parent;
  901.     OSErr        err;
  902.     long        count;
  903.  
  904.     count = sizeof(TreeObj) - sizeof(TreeObjHndl);
  905.     if (!(err = FSRead(fileRefNum, &count, &header))) {
  906.         parent = (*hndl)->parent;
  907.         **hndl = header;
  908.         (*hndl)->parent = parent;
  909.     }
  910.  
  911.     return(err);
  912. }
  913.  
  914.  
  915.  
  916. /**********************************************************************/
  917.  
  918.  
  919.  
  920. /* Read in dataSize number of bytes into the object. */
  921.  
  922. #pragma segment TreeObj
  923. OSErr    ReadTreeObjData(TreeObjHndl hndl, short fileRefNum)
  924. {
  925.     long    dataSize;
  926.     OSErr    err;
  927.     char    hstate;
  928.     Ptr        dataPtr;
  929.  
  930.     if (!(err = SetDataSize(hndl, dataSize = (*hndl)->dataSize))) {
  931.         hstate = HGetState((Handle)hndl);
  932.         HLock((Handle)hndl);
  933.         dataPtr = GetDataPtr(hndl);
  934.         err     = FSRead(fileRefNum, &dataSize, dataPtr);
  935.         HSetState((Handle)hndl, hstate);
  936.     }
  937.  
  938.     return(err);
  939. }
  940.  
  941.  
  942.  
  943. /**********************************************************************/
  944.  
  945.  
  946.  
  947. /* Given an open file that is ready to be written to, this function is called to
  948. ** write the file tree to the designated file. */
  949.  
  950. #pragma segment TreeObj
  951. OSErr    WriteTree(TreeObjHndl hndl, short fileRefNum)
  952. {
  953.     TreeObjProcPtr    proc;
  954.     short            cnum;
  955.     OSErr            err;
  956.  
  957.     err = WriteTreeObjHeader(hndl, fileRefNum);
  958.     if (!err) {
  959.  
  960.         DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOID);
  961.             /* Ready data to be written to file.  Any references to handles are invalid
  962.             ** when written to disk.  These need to be converted to a reference that
  963.             ** makes sense when read in from disk when a file is opened.  The standard
  964.             ** way to do this is to convert the handle reference to a tree-obj-number
  965.             ** reference.  Prior to WriteTree() being called, DoNumberTree() is called.
  966.             ** DoNumberTree() walks the tree and numbers each handle sequentially, thus
  967.             ** giving each handle a unique id number. */
  968.  
  969.         proc = gTreeObjMethods[(*hndl)->type];
  970.         if (proc)
  971.             err = (*proc)(hndl, FWRITEMESSAGE, fileRefNum);
  972.         else
  973.             err = WriteTreeObjData(hndl, fileRefNum);
  974.  
  975.         DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  976.             /* Undo any id references back to handle references. */
  977.     }
  978.  
  979.  
  980.     if (!err) {
  981.         for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) {
  982.             err = WriteTree(GetChildHndl(hndl, cnum), fileRefNum);
  983.             if (err) break;
  984.         }
  985.     }
  986.  
  987.     return(err);
  988. }
  989.  
  990.  
  991.  
  992. /**********************************************************************/
  993.  
  994.  
  995.  
  996. #pragma segment TreeObj
  997. static OSErr    WriteTreeObjHeader(TreeObjHndl hndl, short fileRefNum)
  998. {
  999.     TreeObj    header;
  1000.     OSErr    err;
  1001.     long    count;
  1002.  
  1003.     header = **hndl;
  1004.     count = sizeof(TreeObj) - sizeof(TreeObjHndl);
  1005.     err = FSWrite(fileRefNum, &count, &header);
  1006.  
  1007.     return(err);
  1008. }
  1009.  
  1010.  
  1011.  
  1012. /**********************************************************************/
  1013.  
  1014.  
  1015.  
  1016. /* Write out dataSize number of bytes from the object. */
  1017.  
  1018. #pragma segment TreeObj
  1019. OSErr    WriteTreeObjData(TreeObjHndl hndl, short fileRefNum)
  1020. {
  1021.     long    dataSize;
  1022.     OSErr    err;
  1023.     char    hstate;
  1024.     Ptr        dataPtr;
  1025.  
  1026.     dataSize = (*hndl)->dataSize;
  1027.  
  1028.     hstate  = HGetState((Handle)hndl);
  1029.     HLock((Handle)hndl);
  1030.     dataPtr = GetDataPtr(hndl);
  1031.     err     = FSWrite(fileRefNum, &dataSize, dataPtr);
  1032.     HSetState((Handle)hndl, hstate);
  1033.  
  1034.     return(err);
  1035. }
  1036.  
  1037.  
  1038.  
  1039. /**********************************************************************/
  1040.  
  1041.  
  1042.  
  1043. /* Call the object for each member of the tree (or branch) starting
  1044. ** from the back of the tree working forward. */
  1045.  
  1046. #pragma segment TreeObj
  1047. void    DoBTreeMethod(TreeObjHndl hndl, short message, long data)
  1048. {
  1049.     short    cnum;
  1050.  
  1051.     DoTreeObjMethod(hndl, message, data);
  1052.  
  1053.     for (cnum = (*hndl)->numChildren; cnum;)
  1054.         DoBTreeMethod(GetChildHndl(hndl, --cnum), message, data);
  1055. }
  1056.  
  1057.  
  1058.  
  1059. /**********************************************************************/
  1060.  
  1061.  
  1062.  
  1063. /* Same as DoBTreeMethod, except that an error aborts tree walk and returns error. */
  1064.  
  1065. #pragma segment TreeObj
  1066. OSErr    DoErrBTreeMethod(TreeObjHndl hndl, short message, long data)
  1067. {
  1068.     short    cnum;
  1069.     OSErr    err;
  1070.  
  1071.     err = DoTreeObjMethod(hndl, message, data);
  1072.     if (err) return(err);
  1073.  
  1074.     for (cnum = (*hndl)->numChildren; cnum;) {
  1075.         err = DoErrBTreeMethod(GetChildHndl(hndl, --cnum), message, data);
  1076.         if (err) return(err);
  1077.     }
  1078.  
  1079.     return(noErr);
  1080. }
  1081.  
  1082.  
  1083.  
  1084. /**********************************************************************/
  1085.  
  1086.  
  1087.  
  1088. /* Call the object for each member of the tree (or branch) starting
  1089. ** from the front of the tree working backward. */
  1090.  
  1091. #pragma segment TreeObj
  1092. void    DoFTreeMethod(TreeObjHndl hndl, short message, long data)
  1093. {
  1094.     short    cnum;
  1095.  
  1096.     DoTreeObjMethod(hndl, message, data);
  1097.  
  1098.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) 
  1099.         DoFTreeMethod(GetChildHndl(hndl, cnum), message, data);
  1100. }
  1101.  
  1102.  
  1103.  
  1104. /**********************************************************************/
  1105.  
  1106.  
  1107.  
  1108. /* Same as DoFTreeMethod, except that an error aborts tree walk and returns error. */
  1109.  
  1110. #pragma segment TreeObj
  1111. OSErr    DoErrFTreeMethod(TreeObjHndl hndl, short message, long data)
  1112. {
  1113.     short    cnum;
  1114.     OSErr    err;
  1115.  
  1116.     err = DoTreeObjMethod(hndl, message, data);
  1117.     if (err) return(err);
  1118.  
  1119.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) {
  1120.         err = DoErrFTreeMethod(GetChildHndl(hndl, cnum), message, data);
  1121.         if (err) return(err);
  1122.     }
  1123.  
  1124.     return(noErr);
  1125. }
  1126.  
  1127.  
  1128.  
  1129. /**********************************************************************/
  1130.  
  1131.  
  1132.  
  1133. /* If the object has a method procedure, call it.  If no method procedure,
  1134. ** then do nothing. */
  1135.  
  1136. #pragma segment TreeObj
  1137. long    DoTreeObjMethod(TreeObjHndl hndl, short message, long data)
  1138. {
  1139.     TreeObjProcPtr    proc;
  1140.  
  1141.     proc = gTreeObjMethods[(*hndl)->type];
  1142.     if (proc) return((*proc)(hndl, message, data));
  1143.     return(0);
  1144. }
  1145.  
  1146.  
  1147.  
  1148. /**********************************************************************/
  1149.  
  1150.  
  1151.  
  1152. /* Number each member in the tree with a unique treeID.  The tree is number
  1153. ** sequentially from front to back.  The first treeID number is 1.  0 is
  1154. ** reserved for Hndl2ID/ID2Hndl conversions where it is possible that the
  1155. ** handle value is nil.  The nil handle will convert to 0, and convert back
  1156. ** to nil. */
  1157.  
  1158. #pragma segment TreeObj
  1159. void        DoNumberTree(TreeObjHndl hndl)
  1160. {
  1161.     DoNumberTree0(GetRootHndl(hndl));
  1162. }
  1163.  
  1164.  
  1165.  
  1166. /**********************************************************************/
  1167.  
  1168.  
  1169.  
  1170. #pragma segment TreeObj
  1171. static void    DoNumberTree0(TreeObjHndl hndl)
  1172. {
  1173.     short            cnum;
  1174.     static short    nodeNum;
  1175.  
  1176.     if (!(*hndl)->parent)
  1177.         nodeNum = 0;
  1178.     (*hndl)->treeID = ++nodeNum;
  1179.  
  1180.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum)
  1181.         DoNumberTree0(GetChildHndl(hndl, cnum));
  1182. }
  1183.  
  1184.  
  1185.  
  1186. /**********************************************************************/
  1187.  
  1188.  
  1189.  
  1190. /* This function is used to convert a handle reference into a treeID reference.
  1191. ** A pointer to the handle reference is passed in.  Typical usage will be where
  1192. ** a handle object has a reference to another handle object.  Handle object
  1193. ** references aren't meaningful when saved to disk, and therefore don't persist
  1194. ** in their native form.  These handle references need to be converted into
  1195. ** a treeID reference so that they can be saved.
  1196. ** The tree first needs to be numbered so that the treeID references are unique
  1197. ** and meaningful.  The tree is numbered by first calling DoNumberTree().  It
  1198. ** numbers all the members of the tree hierarchy uniquely and sequentially. */
  1199.  
  1200. #pragma segment TreeObj
  1201. void    Hndl2ID(TreeObjHndl *hndl)
  1202. {
  1203.     if (*hndl)
  1204.         *hndl = (TreeObjHndl)(**hndl)->treeID;
  1205. }
  1206.  
  1207.  
  1208.  
  1209. /**********************************************************************/
  1210.  
  1211.  
  1212.  
  1213. /* Given a tree object ID and a reference object (any member of the tree),
  1214. ** return the cooresponding object handle.  DoNumberTree() must be called
  1215. ** prior to using this function, and after the last change to the tree, as
  1216. ** it generates the object treeID numbers for the entire tree. */
  1217.  
  1218. #pragma segment TreeObj
  1219. void    ID2Hndl(TreeObjHndl refHndl, TreeObjHndl *hndl)
  1220. {
  1221.     short        cnum;
  1222.     TreeObjHndl    chndl;
  1223.  
  1224.     if ((!refHndl) || (!*hndl)) return;
  1225.  
  1226.     refHndl = GetRootHndl(refHndl);
  1227.     for (;;) {
  1228.         if ((*refHndl)->treeID == (long)*hndl) {
  1229.             *hndl = refHndl;
  1230.             return;
  1231.         }
  1232.         if (!(cnum = (*refHndl)->numChildren)) return;
  1233.         for (; cnum;) {
  1234.             chndl = GetChildHndl(refHndl, --cnum);
  1235.             if ((*chndl)->treeID <= (long)*hndl) {
  1236.                 refHndl = chndl;
  1237.                 break;
  1238.             }
  1239.         }
  1240.     }
  1241. }
  1242.  
  1243.  
  1244.  
  1245. /**********************************************************************/
  1246.  
  1247.  
  1248.  
  1249. /* Given an object handle, return the undo handle. */
  1250.  
  1251. #pragma segment TreeObj
  1252. TreeObjHndl    GetUndoHndl(TreeObjHndl undo)
  1253. {
  1254.     for (; (*undo)->parent; undo = (*undo)->parent) {};
  1255.     if ((*undo)->type == ROOTOBJ)
  1256.         undo = mDerefRoot(undo)->undo;
  1257.     return(undo);
  1258. }
  1259.  
  1260.  
  1261.  
  1262. /**********************************************************************/
  1263.  
  1264.  
  1265.  
  1266. /* Used to close out an old undo task.  Closing out the old task means that
  1267. ** any editing to the document will be recorded into a new undo task.  Use
  1268. ** this to separate two edits of the same edit type that would otherwise
  1269. ** get recorded into the same undo task. */
  1270.  
  1271. #pragma segment TreeObj
  1272. void    NewUndo(TreeObjHndl hndl)
  1273. {
  1274.     TreeObjHndl    undo;
  1275.  
  1276.     undo = GetUndoHndl(hndl);
  1277.     mDerefUndo(undo)->lastEditType = NO_EDIT;
  1278.     mDerefUndo(undo)->disabled     = false;
  1279. }
  1280.  
  1281.  
  1282.  
  1283. /**********************************************************************/
  1284.  
  1285.  
  1286.  
  1287. /* If an edit fails, it can be backed out of by calling this.  All edits
  1288. ** that were done will be undone, thus recovering the state of the
  1289. ** document prior to the edit. */
  1290.  
  1291. #pragma segment TreeObj
  1292. void    RevertEdit(TreeObjHndl hndl, Boolean fixup)
  1293. {
  1294.     TreeObjHndl    root;
  1295.     FileRecHndl    frHndl;
  1296.     Boolean        docDirty;
  1297.  
  1298.     root     = GetRootHndl(hndl);
  1299.     frHndl   = mDerefRoot(root)->frHndl;
  1300.     docDirty = (*frHndl)->fileState.docDirty;
  1301.  
  1302.     DoUndoTask(hndl, DOUNDO, fixup);
  1303.         /* Use the undo mechanism to back out of the edit task. */
  1304.  
  1305.     DisposeChild(NO_EDIT, GetUndoHndl(hndl), -1);
  1306.         /* Get rid of the undo that was just used to revert.
  1307.         ** Leave the rest of the undos. */
  1308.  
  1309.     (*frHndl)->fileState.docDirty = docDirty;
  1310. }
  1311.  
  1312.  
  1313.  
  1314. /**********************************************************************/
  1315.  
  1316.  
  1317.  
  1318. /* Call this to undo or redo editing to the document.  If redo is false,
  1319. ** the it is an undo task. */
  1320.  
  1321. #pragma segment TreeObj
  1322. void    DoUndoTask(TreeObjHndl hndl, Boolean redo, Boolean fixup)
  1323. {
  1324.     FileRecHndl    frHndl;
  1325.     TreeObjHndl    undo, undoTask, undoPart;
  1326.     short        numUndos, undoDepth, undoPartNum;
  1327.     short        beg, end, inc;
  1328.     Point        contOrg;
  1329.  
  1330.     NewUndo(undo = GetUndoHndl(hndl));
  1331.  
  1332.     numUndos  = (*undo)->numChildren;
  1333.     undoDepth = mDerefUndo(undo)->undoDepth;
  1334.  
  1335.     if ((redo) && (numUndos == undoDepth)) return;
  1336.     if (!(numUndos | undoDepth)) return;
  1337.  
  1338.     frHndl = mDerefUndo(undo)->frHndl;
  1339.  
  1340.     if (redo) undoDepth++;
  1341.     undoTask = GetChildHndl(undo, --undoDepth);
  1342.  
  1343.     if (!redo) {
  1344.         GetContentOrigin((*frHndl)->fileState.window, &contOrg);
  1345.         mDerefUndoTask(undoTask)->redoOrigin = contOrg;
  1346.         contOrg = mDerefUndoTask(undoTask)->undoOrigin;
  1347.     }
  1348.     else contOrg = mDerefUndoTask(undoTask)->redoOrigin;
  1349.  
  1350.     if (fixup)
  1351.         DoUndoFixup(frHndl, contOrg, 0);
  1352.             /* Prepare for undo task, such as deselecting the current selection so
  1353.             ** that the undone stuff can be displayed as the only selected stuff. */
  1354.  
  1355.     beg = (*undoTask)->numChildren - 1;
  1356.     end = -1;
  1357.     inc = -1;
  1358.     if (redo) {
  1359.         end = ++beg;
  1360.         beg = 0;
  1361.         inc = 1;
  1362.     }
  1363.  
  1364.     for (undoPartNum = beg; undoPartNum != end; undoPartNum += inc) {
  1365.         undoPart = GetChildHndl(undoTask, undoPartNum);
  1366.         switch(mDerefUndoPart(undoPart)->actionType) {
  1367.             case NEW_CHILD:
  1368.                 UndoNewChild(undoPart);
  1369.                 break;
  1370.             case DISPOSE_CHILD:
  1371.                 UndoDisposeChild(undoPart);
  1372.                 break;
  1373.             case MOVE_CHILD:
  1374.                 UndoMoveChild(undoPart);
  1375.                 break;
  1376.             case CHANGE_CHILD:
  1377.                 UndoModifyChild(undoPart);
  1378.                 break;
  1379.             case SWAP_CHILDREN:
  1380.                 UndoSwapChildren(undoPart);
  1381.                 break;
  1382.         }
  1383.     }
  1384.  
  1385.     inc = -1;
  1386.     if (redo)
  1387.         inc = 1;
  1388.  
  1389.     mDerefUndo(undo)->undoDepth += inc;
  1390.  
  1391.     if (fixup)
  1392.         DoUndoFixup(frHndl, contOrg, 1);
  1393.             /* Clean up and redisplay after undo task. */
  1394.  
  1395.     SetDocDirty(frHndl);
  1396. }
  1397.  
  1398.  
  1399.  
  1400. /**********************************************************************/
  1401.  
  1402.  
  1403.  
  1404. #pragma segment TreeObj
  1405. static TreeObjHndl    GetUndoTaskHndl(TreeObjHndl undo, short editType)
  1406. {
  1407.     TreeObjHndl        lastTaskHndl;
  1408.     short            lastEditType, undoDepth, addNewUndo, numUndoLevels, maxNumUndos;
  1409.     Point            contOrg;
  1410.     FileRecHndl        frHndl;
  1411.     WindowPtr        window;
  1412.  
  1413.     if (!(maxNumUndos = mDerefUndo(undo)->maxNumUndos)) return(nil);
  1414.  
  1415.     lastEditType = mDerefUndo(undo)->lastEditType;
  1416.     undoDepth    = mDerefUndo(undo)->undoDepth;
  1417.  
  1418.     addNewUndo = false;
  1419.     if (editType != lastEditType)
  1420.         addNewUndo = true;
  1421.     if (!(numUndoLevels = (*undo)->numChildren))
  1422.         addNewUndo = true;
  1423.  
  1424.     while (undoDepth < numUndoLevels) {
  1425.         DisposeChild(NO_EDIT, undo, --numUndoLevels);
  1426.         addNewUndo = true;        /* Flushing old also indicates a new undo. */
  1427.     }                            /* undoDepth now is the same as numUndoLevels. */
  1428.  
  1429.     lastTaskHndl = nil;
  1430.     if (!addNewUndo) {
  1431.         lastTaskHndl = GetChildHndl(undo, -1);        /* Get last child handle. */
  1432.         if ((editType) && (editType != mDerefUndoTask(lastTaskHndl)->editType))
  1433.             lastTaskHndl = nil;
  1434.     }
  1435.  
  1436.     if (!lastTaskHndl) {
  1437.         while (numUndoLevels >= maxNumUndos) {
  1438.             DisposeChild(NO_EDIT, undo, 0);
  1439.             mDerefUndo(undo)->undoDepth--;
  1440.             numUndoLevels--;
  1441.         }    /* Restrict number of undos to designated level. */
  1442.  
  1443.         lastTaskHndl = NewChild(NO_EDIT, undo, numUndoLevels, UNDOTASKOBJ, 0);
  1444.         if (lastTaskHndl) {
  1445.             mDerefUndo(undo)->lastEditType = editType;
  1446.             mDerefUndo(undo)->undoDepth++;
  1447.             frHndl = mDerefUndo(undo)->frHndl;
  1448.             window = (*frHndl)->fileState.window;
  1449.             GetContentOrigin(window, &contOrg);
  1450.             mDerefUndoTask(lastTaskHndl)->editType   = editType;
  1451.             mDerefUndoTask(lastTaskHndl)->undoOrigin = contOrg;
  1452.             mDerefUndoTask(lastTaskHndl)->redoOrigin = contOrg;
  1453.         }
  1454.     }
  1455.  
  1456.     return(lastTaskHndl);
  1457. }
  1458.  
  1459.  
  1460.  
  1461. /**********************************************************************/
  1462.  
  1463.  
  1464.  
  1465. #pragma segment TreeObj
  1466. static TreeObjHndl    NewUndoPart(TreeObjHndl taskHndl, short action, TreeObjHndl shndl, short scnum,
  1467.                                 TreeObjHndl dhndl, short dcnum, Boolean deepCopy)
  1468. {
  1469.     TreeObjHndl    partHndl;
  1470.     UndoPartObj    *partPtr;
  1471.  
  1472.     partHndl = NewChild(NO_EDIT, taskHndl, -1, UNDOPARTOBJ, 0);    /* Add new child to end. */
  1473.     if (partHndl) {
  1474.         partPtr = GetDataPtr(partHndl);
  1475.         partPtr->actionType = action;
  1476.         partPtr->shndl      = shndl;
  1477.         partPtr->scnum      = scnum;
  1478.         partPtr->dhndl      = dhndl;
  1479.         partPtr->dcnum      = dcnum;
  1480.         partPtr->deepCopy   = deepCopy;
  1481.     }
  1482.  
  1483.     return(partHndl);
  1484. }
  1485.  
  1486.  
  1487.  
  1488. /**********************************************************************/
  1489.  
  1490.  
  1491.  
  1492. #pragma segment TreeObj
  1493. static OSErr    PostNewChild(short editType, TreeObjHndl phndl, short cnum)
  1494. {
  1495.     TreeObjHndl    undo, task;
  1496.  
  1497.     if (!editType) return(noErr);
  1498.  
  1499.     undo = GetUndoHndl(phndl);
  1500.     if (mDerefUndo(undo)->disabled) return(noErr);
  1501.  
  1502.     task = GetUndoTaskHndl(undo, editType);
  1503.     if (task)
  1504.         if (NewUndoPart(task, NEW_CHILD, phndl, cnum, nil, 0, false)) return(noErr);
  1505.  
  1506.     return(memFullErr);
  1507. }
  1508.  
  1509.  
  1510.  
  1511. /**********************************************************************/
  1512.  
  1513.  
  1514.  
  1515. #pragma segment TreeObj
  1516. static OSErr    PostDisposeChild(short editType, TreeObjHndl phndl, short cnum)
  1517. {
  1518.     TreeObjHndl    undo, task, part;
  1519.  
  1520.     if (!editType) return(noErr);
  1521.  
  1522.     undo = GetUndoHndl(phndl);
  1523.     if (mDerefUndo(undo)->disabled) return(noErr);
  1524.  
  1525.     task = GetUndoTaskHndl(undo, editType);
  1526.     if (task) {
  1527.         part = NewUndoPart(task, DISPOSE_CHILD, phndl, cnum, nil, 0, false);
  1528.         if (part) {
  1529.             MoveChild(NO_EDIT, phndl, cnum, part, -1);
  1530.             return(noErr);
  1531.         }
  1532.     }
  1533.  
  1534.     return(memFullErr);
  1535. }
  1536.  
  1537.  
  1538.  
  1539. /**********************************************************************/
  1540.  
  1541.  
  1542.  
  1543. #pragma segment TreeObj
  1544. static void    PostMoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum)
  1545. {
  1546.     TreeObjHndl    undo, task;
  1547.  
  1548.     if (editType) {
  1549.         undo = GetUndoHndl(shndl);
  1550.         if (!mDerefUndo(undo)->disabled) {
  1551.             task = GetUndoTaskHndl(undo, editType);
  1552.             if (task) {
  1553.                 NewUndoPart(task, MOVE_CHILD, shndl, scnum, dhndl, dcnum, false);
  1554.             }
  1555.         }
  1556.     }
  1557. }
  1558.  
  1559.  
  1560.  
  1561. /**********************************************************************/
  1562.  
  1563.  
  1564.  
  1565. #pragma segment TreeObj
  1566. static void    PostSwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb)
  1567. {
  1568.     TreeObjHndl    undo, task;
  1569.  
  1570.     if (editType) {
  1571.         undo = GetUndoHndl(hndla);
  1572.         if (!mDerefUndo(undo)->disabled) {
  1573.             task = GetUndoTaskHndl(undo, editType);
  1574.             if (task) {
  1575.                 NewUndoPart(task, SWAP_CHILDREN, hndla, cnuma, hndlb, cnumb, false);
  1576.             }
  1577.         }
  1578.     }
  1579. }
  1580.  
  1581.  
  1582.  
  1583. /**********************************************************************/
  1584.  
  1585.  
  1586.  
  1587. #pragma segment TreeObj
  1588. static void    UndoNewChild(TreeObjHndl undoPart)
  1589. {
  1590.     TreeObjHndl    shndl, chndl;
  1591.     short        scnum;
  1592.  
  1593.     shndl = mDerefUndoPart(undoPart)->shndl;
  1594.     scnum = mDerefUndoPart(undoPart)->scnum;
  1595.     mDerefUndoPart(undoPart)->actionType = DISPOSE_CHILD;
  1596.  
  1597.     chndl = GetChildHndl(shndl, scnum);
  1598.     MoveChild(NO_EDIT, shndl, scnum, undoPart, 0);
  1599. }
  1600.  
  1601.  
  1602.  
  1603. /**********************************************************************/
  1604.  
  1605.  
  1606.  
  1607. #pragma segment TreeObj
  1608. static void    UndoDisposeChild(TreeObjHndl undoPart)
  1609. {
  1610.     TreeObjHndl    shndl, chndl;
  1611.     short        scnum;
  1612.  
  1613.     shndl = mDerefUndoPart(undoPart)->shndl;
  1614.     scnum = mDerefUndoPart(undoPart)->scnum;
  1615.     mDerefUndoPart(undoPart)->actionType = NEW_CHILD;
  1616.  
  1617.     chndl = GetChildHndl(undoPart, 0);
  1618.     MoveChild(NO_EDIT, undoPart, 0, shndl, scnum);
  1619. }
  1620.  
  1621.  
  1622.  
  1623. /**********************************************************************/
  1624.  
  1625.  
  1626.  
  1627. #pragma segment TreeObj
  1628. static void    UndoMoveChild(TreeObjHndl undoPart)
  1629. {
  1630.     TreeObjHndl    shndl, dhndl;
  1631.     short        scnum, dcnum;
  1632.  
  1633.     shndl = mDerefUndoPart(undoPart)->shndl;
  1634.     scnum = mDerefUndoPart(undoPart)->scnum;
  1635.     dhndl = mDerefUndoPart(undoPart)->dhndl;
  1636.     dcnum = mDerefUndoPart(undoPart)->dcnum;
  1637.  
  1638.     MoveChild(NO_EDIT, dhndl, dcnum, shndl, scnum);
  1639.  
  1640.     mDerefUndoPart(undoPart)->shndl = dhndl;
  1641.     mDerefUndoPart(undoPart)->scnum = dcnum;
  1642.     mDerefUndoPart(undoPart)->dhndl = shndl;
  1643.     mDerefUndoPart(undoPart)->dcnum = scnum;
  1644. }
  1645.  
  1646.  
  1647.  
  1648. /**********************************************************************/
  1649.  
  1650.  
  1651.  
  1652. #pragma segment TreeObj
  1653. static void    UndoModifyChild(TreeObjHndl undoPart)
  1654. {
  1655.     TreeObjHndl    shndl, dchndl, uchndl;
  1656.     short        scnum;
  1657.  
  1658.     shndl = mDerefUndoPart(undoPart)->shndl;
  1659.     scnum = mDerefUndoPart(undoPart)->scnum;
  1660.  
  1661.     dchndl = GetChildHndl(shndl, scnum);    /* Document child handle. */
  1662.     uchndl = GetChildHndl(undoPart, 0);        /* Undo child handle. */
  1663.  
  1664.     UndoModifyChildren(dchndl, uchndl, mDerefUndoPart(undoPart)->deepCopy);
  1665. }
  1666.  
  1667.  
  1668.  
  1669. /**********************************************************************/
  1670.  
  1671.  
  1672.  
  1673. #pragma segment TreeObj
  1674. static void    UndoModifyChildren(TreeObjHndl dhndl, TreeObjHndl uhndl, Boolean deepCopy)
  1675. {
  1676.     TreeObjHndl    dchndl, uchndl;
  1677.     short        i;
  1678.  
  1679.     DoTreeObjMethod(dhndl, UNDOMESSAGE, UNDOFROMDOC);    /* Old data leaving document. */
  1680.     SwapTreeObjData(dhndl, uhndl);
  1681.     DoTreeObjMethod(dhndl, UNDOMESSAGE, UNDOTODOC);        /* New data entering document. */
  1682.  
  1683.     if (deepCopy) {
  1684.         for (i = (*dhndl)->numChildren; i;) {
  1685.             dchndl = GetChildHndl(dhndl, --i);
  1686.             uchndl = GetChildHndl(uhndl, i);
  1687.             UndoModifyChildren(dchndl, uchndl, deepCopy);
  1688.         }
  1689.     }
  1690. }
  1691.  
  1692.  
  1693.  
  1694. /**********************************************************************/
  1695.  
  1696.  
  1697.  
  1698. #pragma segment TreeObj
  1699. static void    UndoSwapChildren(TreeObjHndl undoPart)
  1700. {
  1701.     TreeObjHndl    shndl, dhndl, schndl, dchndl;
  1702.     short        scnum, dcnum;
  1703.  
  1704.     shndl = mDerefUndoPart(undoPart)->shndl;
  1705.     scnum = mDerefUndoPart(undoPart)->scnum;
  1706.     dhndl = mDerefUndoPart(undoPart)->dhndl;
  1707.     dcnum = mDerefUndoPart(undoPart)->dcnum;
  1708.  
  1709.     schndl = GetChildHndl(shndl, scnum);
  1710.     dchndl = GetChildHndl(dhndl, dcnum);
  1711.  
  1712.     DoTreeObjMethod(schndl, UNDOMESSAGE, UNDOFROMDOC);
  1713.     DoTreeObjMethod(dchndl, UNDOMESSAGE, UNDOFROMDOC);
  1714.  
  1715.     SwapChildren(NO_EDIT, shndl, scnum, dhndl, dcnum);
  1716.  
  1717.     DoTreeObjMethod(schndl, UNDOMESSAGE, UNDOTODOC);
  1718.     DoTreeObjMethod(dchndl, UNDOMESSAGE, UNDOTODOC);
  1719. }
  1720.  
  1721.  
  1722.  
  1723. /**********************************************************************/
  1724.  
  1725.  
  1726.  
  1727. /* Dispose of all undo information and prevent further undo collection.
  1728. ** Calling NewUndo() will re-enable undo collection. */
  1729.  
  1730. #pragma segment TreeObj
  1731. void    DisableUndo(TreeObjHndl hndl)
  1732. {
  1733.     TreeObjHndl    undo;
  1734.  
  1735.     undo = GetUndoHndl(hndl);
  1736.     while ((*undo)->numChildren) DisposeChild(NO_EDIT, undo, 0);
  1737.  
  1738.     mDerefUndo(undo)->undoDepth = 0;
  1739.     mDerefUndo(undo)->disabled  = true;
  1740. }
  1741.  
  1742.  
  1743.  
  1744. /**********************************************************************/
  1745.  
  1746.  
  1747.  
  1748. /* Dispose of all undo information and prevent further undo collection.
  1749. ** Calling NewUndo() will re-enable undo collection. */
  1750.  
  1751. #pragma segment TreeObj
  1752. void    DisposeUndos(TreeObjHndl hndl)
  1753. {
  1754.     DisableUndo(hndl);
  1755.     NewUndo(hndl);
  1756. }
  1757.  
  1758.  
  1759.  
  1760. /**********************************************************************/
  1761.  
  1762.  
  1763.  
  1764. /* Dispose of all undo information except the current undo.  The current undo
  1765. ** may still be active, and it may be needed to back out of an edit operation. */
  1766.  
  1767. #pragma segment TreeObj
  1768. Boolean    PurgeUndo(TreeObjHndl hndl)
  1769. {
  1770.     TreeObjHndl    undo;
  1771.     Boolean        didPurge;
  1772.  
  1773.     undo = GetUndoHndl(hndl);
  1774.     didPurge = false;
  1775.     while ((*undo)->numChildren > 1) {
  1776.         DisposeChild(NO_EDIT, undo, 0);
  1777.         if (mDerefUndo(undo)->undoDepth) --mDerefUndo(undo)->undoDepth;
  1778.         didPurge = true;
  1779.     }
  1780.  
  1781.     return(didPurge);
  1782. }
  1783.  
  1784.  
  1785.  
  1786. /**********************************************************************/
  1787.  
  1788.  
  1789.  
  1790. #pragma segment TreeObj
  1791. void    GetUndoInfo(FileRecHndl frHndl, short *undoDepth, short *numUndos)
  1792. {
  1793.     TreeObjHndl    undo;
  1794.  
  1795.     *undoDepth = *numUndos = 0;
  1796.     if ((*frHndl)->fileState.readOnly) return;
  1797.  
  1798.     undo = GetUndoHndl((*frHndl)->d.doc.root);
  1799.     if (!mDerefUndo(undo)->disabled) {
  1800.         *undoDepth = mDerefUndo(undo)->undoDepth;
  1801.         *numUndos  = (*undo)->numChildren;
  1802.     }
  1803. }
  1804.  
  1805.  
  1806.  
  1807. /*****************************************************************************/
  1808. /*****************************************************************************/
  1809.  
  1810.  
  1811.  
  1812. /* This function does the standard document initialization. */
  1813.  
  1814. #pragma segment TreeObj
  1815. OSErr    DefaultInitDocument(FileRecHndl frHndl, short version, short numUndos, short numSaveUndos)
  1816. {
  1817.     TreeObjHndl    root, undo;
  1818.     OSErr        err;
  1819.  
  1820.     err = noErr;
  1821.  
  1822.     (*frHndl)->d.doc.fhInfo.version = version;
  1823.  
  1824.     (*frHndl)->fileState.readDocumentHeaderProc  = DefaultReadDocumentHeader;
  1825.     (*frHndl)->fileState.writeDocumentHeaderProc = DefaultWriteDocumentHeader;
  1826.  
  1827.     root = NewRootObj(ROOTOBJ, 0);
  1828.     if (root) {                                    /* Create hierarchical data root. */
  1829.         (*frHndl)->d.doc.root = root;            /* Link file to hierarchical data. */
  1830.         undo = NewRootObj(UNDOOBJ, 0);            /* Create hierarchical undo root. */
  1831.         mDerefRoot(root)->undo    = undo;        /* Save hierarchical undo root. */
  1832.         mDerefRoot(root)->frHndl  = frHndl;
  1833.         if (undo) {
  1834.             (*frHndl)->fileState.defaultDoc = true;
  1835.             mDerefUndo(undo)->root   = root;    /* Point undo back at file root. */
  1836.             mDerefUndo(undo)->frHndl = frHndl;
  1837.             mDerefUndo(undo)->maxNumUndos  = numUndos;
  1838.             mDerefUndo(undo)->numSaveUndos = numSaveUndos;
  1839.         }
  1840.         else {
  1841.             DefaultFreeDocument(frHndl);
  1842.             err = memFullErr;
  1843.         }
  1844.     }
  1845.     else err = memFullErr;
  1846.  
  1847.     return(err);
  1848. }
  1849.  
  1850.  
  1851.  
  1852. /*****************************************************************************/
  1853.  
  1854.  
  1855.  
  1856. /* Frees up the hierarchical document and undo portions of a default document. */
  1857.  
  1858. #pragma segment TreeObj
  1859. OSErr    DefaultFreeDocument(FileRecHndl frHndl)
  1860. {
  1861.     TreeObjHndl    root, undo;
  1862.  
  1863.     root = (*frHndl)->d.doc.root;
  1864.     if (root) {                                    /* If we have a valid root object... */
  1865.         undo = mDerefRoot(root)->undo;
  1866.         if (undo)                                /* If we have a valid undo object... */
  1867.             DisposeObjAndOffspring(undo);        /* Dispose of undo info. */
  1868.         DisposeObjAndOffspring(root);            /* Dispose of hierarchical file data. */
  1869.         (*frHndl)->d.doc.root = nil;
  1870.     }
  1871.     return(noErr);
  1872. }
  1873.  
  1874.  
  1875.  
  1876. /*****************************************************************************/
  1877.  
  1878.  
  1879.  
  1880. #pragma segment TreeObj
  1881. OSErr    DefaultReadDocument(FileRecHndl frHndl)
  1882. {
  1883.     OSErr        err;
  1884.     TreeObjHndl    root, undo;
  1885.     short        resID, refNum, flags;
  1886.     Movie        movie;
  1887.     Boolean        wasChanged;
  1888.  
  1889.     refNum = (*frHndl)->fileState.refNum;
  1890.     err    = SetFPos(refNum, fsFromStart, 0);
  1891.         /* Set the file position to the beginning of the file. */
  1892.  
  1893.     err = DoReadDocumentHeader(frHndl);
  1894.  
  1895.     if (!err) {
  1896.         if ((*frHndl)->fileState.sfType == MovieFileType) {
  1897.             resID  = 0;
  1898.             flags  = (*frHndl)->fileState.movieFlags;
  1899.             err = NewMovieFromFile(&movie, refNum, &resID, nil, flags, &wasChanged);
  1900.             if (err) return(err);
  1901.             (*frHndl)->fileState.movie                  = movie;
  1902.             (*frHndl)->fileState.movieResID             = resID;
  1903.             (*frHndl)->fileState.movieDataRefWasChanged = wasChanged;
  1904.         }
  1905.         else {
  1906.             root = (*frHndl)->d.doc.root;        /* Preserve the undo field.  Preserving  */
  1907.             undo = mDerefRoot(root)->undo;        /* any application-sepcific fields is up */
  1908.                                                 /* to the application.                     */
  1909.             err = ReadTree(root, refNum);
  1910.                 /* Read in the hierarchical file data portion. */
  1911.  
  1912.             mDerefRoot(root)->undo   = undo;    /* Restore the 2 over-written fields. */
  1913.             mDerefRoot(root)->frHndl = frHndl;
  1914.         }
  1915.     }
  1916.  
  1917.     return(err);
  1918. }
  1919.  
  1920.  
  1921.  
  1922. /*****************************************************************************/
  1923.  
  1924.  
  1925.  
  1926. #pragma segment TreeObj
  1927. OSErr    DefaultReadDocumentFixup(FileRecHndl frHndl)
  1928. {
  1929.     TreeObjHndl    root, undo, chndl;
  1930.  
  1931.     root = (*frHndl)->d.doc.root;
  1932.     undo = mDerefRoot(root)->undo;
  1933.     for (;;) {
  1934.         if (!(chndl = GetChildHndl(root, -1))) break;
  1935.         if ((*chndl)->type != UNDOTASKOBJ)     break;
  1936.         MoveChild(NO_EDIT, root, -1, undo, -1);
  1937.         mDerefUndo(undo)->undoDepth++;
  1938.             /* Move any undo tasks that were saved with the document
  1939.             ** out of the document and into the undo hierarchy. */
  1940.     }
  1941.  
  1942.     return(noErr);
  1943. }
  1944.  
  1945.  
  1946.  
  1947. /*****************************************************************************/
  1948.  
  1949.  
  1950.  
  1951. #pragma segment TreeObj
  1952. OSErr    DefaultWriteDocument(FileRecHndl frHndl)
  1953. {
  1954.     short        refNum, cnum, numSaveUndos;
  1955.     OSErr        err;
  1956.     long        fpos;
  1957.     TreeObjHndl    root, undo, chndl;
  1958.  
  1959.     refNum = (*frHndl)->fileState.refNum;
  1960.     err    = DoWriteDocumentHeader(frHndl);
  1961.  
  1962.     if (!err) {
  1963.         if ((*frHndl)->fileState.sfType != MovieFileType) {
  1964.             undo         = GetUndoHndl(root = (*frHndl)->d.doc.root);
  1965.             numSaveUndos = mDerefUndo(undo)->numSaveUndos;
  1966.             for (cnum = mDerefUndo(undo)->undoDepth; ((cnum) && (numSaveUndos)); --numSaveUndos)
  1967.                 MoveChild(NO_EDIT, undo, --cnum, root, -1);
  1968.                     /* Move the designated number of undo tasks into the document side.
  1969.                     ** They will be saved to disk this way.  The designated number may be
  1970.                     ** zero, which means that undos aren't saved to disk. */
  1971.  
  1972.             DoNumberTree(root);
  1973.                 /* Assign each object in the tree a unique treeID.  This will allow
  1974.                 ** the objects to convert handle references into ID references so that
  1975.                 ** the data can be saved to disk. */
  1976.  
  1977.             err = WriteTree(root, refNum);
  1978.                 /* Write out the hierarchical data.  This includes the data in the root object.
  1979.                 ** When reading a data file, the root object data has already been initialized,
  1980.                 ** and therefore reading in the old root data from disk is a bad thing.  This is
  1981.                 ** handled by caching the root data prior to reading in a file, and then
  1982.                 ** restoring the data after the ReadTree() call has been made. */
  1983.  
  1984.             for (;;) {
  1985.                 if (!(chndl = GetChildHndl(root, -1))) break;
  1986.                 if ((*chndl)->type != UNDOTASKOBJ)     break;
  1987.                 MoveChild(NO_EDIT, root, -1, undo, cnum++);
  1988.                     /* Move any undo tasks that we previously moved into the document
  1989.                     ** out of the document and back into the undo hierarchy. */
  1990.             }
  1991.  
  1992.             if (!err) {
  1993.                 err = GetFPos(refNum, &fpos);
  1994.                 if (!err)
  1995.                     err = SetEOF(refNum, fpos);
  1996.             }            /* The document may be shorter than last time it was written to disk.
  1997.                         ** Handle this case by ending the file based on the new length. */
  1998.         }
  1999.         else {
  2000.         }
  2001.     }
  2002.  
  2003.     return(err);
  2004. }
  2005.  
  2006.  
  2007.  
  2008. /**********************************************************************/
  2009. /**********************************************************************/
  2010. /**********************************************************************/
  2011.  
  2012.  
  2013.  
  2014. #pragma segment TreeObj
  2015. OSErr    HReadTree(TreeObjHndl hndl, Handle tree)
  2016. {
  2017.     OSErr    err;
  2018.  
  2019.     if (!GetHandleSize(tree)) return(noErr);
  2020.  
  2021.     err = HReadBranch(hndl, tree);
  2022.     if (err) {
  2023.         while ((*hndl)->numChildren)
  2024.             DisposeChild(NO_EDIT, hndl, 0);
  2025.     }
  2026.     else {
  2027.         DoNumberTree(hndl);
  2028.         DoFTreeMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  2029.     }
  2030.  
  2031.     return(err);
  2032. }
  2033.  
  2034.  
  2035.  
  2036. /**********************************************************************/
  2037.  
  2038.  
  2039.  
  2040. /* This function recursively dissects the handle into separate tree objects.  The handle
  2041. ** has previously had tree objects streamed into it.  The public format is as follows:
  2042. ** 1) object header
  2043. ** 2) data length (4 bytes)
  2044. ** 3) data
  2045. **
  2046. ** After the object is created, the header is moved into it.  Then the data length is
  2047. ** fetched and a data handle is created to hold the data portion of the streamed data
  2048. ** for this object.  Once the data handle holds the object data, the tree handle has
  2049. ** the information for this object removed from it.
  2050. ** After this data separation, we call the object and pass it the data handle.
  2051. ** The object is responsible for interpreting the handle and initializing the data
  2052. ** portion of the object with it. */
  2053.  
  2054. #pragma segment TreeObj
  2055. OSErr    HReadBranch(TreeObjHndl hndl, Handle tree)
  2056. {
  2057.     TreeObjHndl        chndl;
  2058.     Handle            treeObjData;
  2059.     TreeObjProcPtr    proc;
  2060.     short            numChildren, cnum;
  2061.     long            size, diff;
  2062.     OSErr            err;
  2063.  
  2064.     if (GetHandleSize(tree) < sizeof(TreeObj)) return(paramErr);
  2065.  
  2066.     BlockMove(*tree, *hndl, sizeof(TreeObj) - sizeof(TreeObjHndl));
  2067.  
  2068.     numChildren = (*hndl)->numChildren;    /* So we can dispose of a partial read if */
  2069.     (*hndl)->numChildren = 0;            /* we have a failure after this point.    */
  2070.  
  2071.     BlockMove((long *)(*tree + sizeof(TreeObj) - sizeof(TreeObjHndl)), (Ptr)&size, sizeof(size));
  2072.         /* Get the size of the data following the header data. */
  2073.  
  2074.     if (!(treeObjData = NewHandle(size)))
  2075.         return(memFullErr);
  2076.             /* Create a handle the size of the data. */
  2077.  
  2078.     BlockMove(*tree + sizeof(TreeObj), *treeObjData, size);
  2079.         /* Copy the data into the handle. */
  2080.  
  2081.     diff = sizeof(TreeObj) + size;
  2082.     size = GetHandleSize(tree) - diff;
  2083.         /* Calculate how much we are going to shrink the tree handle. */
  2084.  
  2085.     BlockMove(*tree + diff, *tree, size);
  2086.     SetHandleSize(tree, size);
  2087.  
  2088.     proc = gTreeObjMethods[(*hndl)->type];
  2089.     if (proc)
  2090.         err = (*proc)(hndl, HREADMESSAGE, (long)treeObjData);
  2091.     else
  2092.         err = HReadTreeObjData(hndl, treeObjData);
  2093.  
  2094.     DisposeHandle(treeObjData);
  2095.  
  2096.     if (err)
  2097.         return(err);
  2098.  
  2099.     for (cnum = 0; cnum < numChildren; ++cnum) {
  2100.         if (!(chndl = NewRootObj(EMPTYOBJ, 0))) return(memFullErr);
  2101.         if (InsertChildHndl(hndl, chndl, cnum)) {
  2102.             DisposeObjAndOffspring(chndl);
  2103.             return(memFullErr);
  2104.         }
  2105.         err = HReadBranch(chndl, tree);
  2106.         if (err) return(err);
  2107.     }
  2108.  
  2109.     return(noErr);
  2110. }
  2111.  
  2112.  
  2113.  
  2114. /**********************************************************************/
  2115.  
  2116.  
  2117.  
  2118. /* The simple handle read can assume that there will be no expansion or interpretation
  2119. ** of the data.  This means that the dataSize field represents the correct data size. */
  2120.  
  2121. #pragma segment TreeObj
  2122. OSErr    HReadTreeObjData(TreeObjHndl hndl, Handle treeObjData)
  2123. {
  2124.     long    dataSize;
  2125.     OSErr    err;
  2126.  
  2127.     if (!(err = SetDataSize(hndl, dataSize = (*hndl)->dataSize)))
  2128.         BlockMove(*treeObjData, (Ptr)(*hndl + 1), dataSize);
  2129.  
  2130.     return(err);
  2131. }
  2132.  
  2133.  
  2134.  
  2135. /**********************************************************************/
  2136.  
  2137.  
  2138.  
  2139. /* Given a handle, this function is called to stream the hierarchy onto the end of the handle. */
  2140.  
  2141. #pragma segment TreeObj
  2142. OSErr    HWriteTree(TreeObjHndl hndl, Handle tree)
  2143. {
  2144.     TreeObjProcPtr    proc;
  2145.     Handle            data;
  2146.     short            cnum;
  2147.     OSErr            err;
  2148.     long            tsize, dsize;
  2149.  
  2150.     DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOID);
  2151.         /* Ready data to be written to file.  Any references to handles are invalid
  2152.         ** when written to disk.  These need to be converted to a reference that
  2153.         ** makes sense when read in from disk when a file is opened.  The standard
  2154.         ** way to do this is to convert the handle reference to a tree-obj-number
  2155.         ** reference.  Prior to WriteTree() being called, DoNumberTree() is called.
  2156.         ** DoNumberTree() walks the tree and numbers each handle sequentially, thus
  2157.         ** giving each handle a unique id number. */
  2158.  
  2159.     if (!(data = NewHandle((*hndl)->dataSize)))
  2160.         return(memFullErr);
  2161.  
  2162.     proc = gTreeObjMethods[(*hndl)->type];
  2163.     if (proc)
  2164.         err = (*proc)(hndl, HWRITEMESSAGE, (long)data);
  2165.     else
  2166.         err = HWriteTreeObjData(hndl, data);
  2167.  
  2168.     if (err) {
  2169.         DisposeHandle(data);
  2170.         return(err);
  2171.     }
  2172.  
  2173.     tsize = GetHandleSize(tree);
  2174.     dsize = GetHandleSize(data);
  2175.     SetHandleSize(tree, tsize + sizeof(TreeObj) + dsize);
  2176.     BlockMove((Ptr)*hndl,  (*tree + tsize), sizeof(TreeObj) - sizeof(TreeObjHndl));
  2177.     BlockMove((Ptr)&dsize, (*tree + tsize + sizeof(TreeObj) - sizeof(TreeObjHndl)), sizeof(long));
  2178.     BlockMove(*data,       (*tree + tsize + sizeof(TreeObj)), dsize);
  2179.  
  2180.     DisposeHandle(data);
  2181.     DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  2182.         /* Undo any id references back to handle references. */
  2183.  
  2184.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) {
  2185.         err = HWriteTree(GetChildHndl(hndl, cnum), tree);
  2186.         if (err) break;
  2187.     }
  2188.  
  2189.     return(err);
  2190. }
  2191.  
  2192.  
  2193.  
  2194. /**********************************************************************/
  2195.  
  2196.  
  2197.  
  2198. /* Write out dataSize number of bytes from the object onto end of handle. */
  2199.  
  2200. #pragma segment TreeObj
  2201. OSErr    HWriteTreeObjData(TreeObjHndl hndl, Handle data)
  2202. {
  2203.     long    dataSize;
  2204.     OSErr    err;
  2205.  
  2206.     SetHandleSize(data, dataSize = (*hndl)->dataSize);
  2207.     err = MemError();
  2208.     if (err) return(err);
  2209.  
  2210.     BlockMove((Ptr)(*hndl + 1), *data, dataSize);
  2211.     return(noErr);
  2212. }
  2213.  
  2214.  
  2215.  
  2216. /**********************************************************************/
  2217. /**********************************************************************/
  2218.  
  2219.  
  2220.  
  2221. #pragma segment TreeObj
  2222. long    GetCData(TreeObjHndl hndl, long offset, char *data)
  2223. {
  2224.     Ptr        dptr;
  2225.     long    len;
  2226.  
  2227.     dptr = (Ptr)GetDataPtr(hndl) + offset;
  2228.     len  = clen(dptr);
  2229.  
  2230.     if (data)
  2231.         ccpy(data, dptr);
  2232.  
  2233.     return(len);
  2234. }
  2235.  
  2236.  
  2237.  
  2238. /**********************************************************************/
  2239.  
  2240.  
  2241.  
  2242. #pragma segment TreeObj
  2243. OSErr    PutCData(TreeObjHndl hndl, long offset, char *data)
  2244. {
  2245.     unsigned char    *dptr;
  2246.     long            oldl, newl;
  2247.     OSErr            err;
  2248.  
  2249.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2250.  
  2251.     oldl = clen((Ptr)dptr) + 1;
  2252.     newl = clen(data) + 1;
  2253.  
  2254.     err = SlideData(hndl, offset + oldl, newl - oldl);
  2255.     if (err) return(err);
  2256.  
  2257.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2258.     ccpy((Ptr)dptr, data);
  2259.  
  2260.     return(noErr);
  2261. }
  2262.  
  2263.  
  2264.  
  2265. /**********************************************************************/
  2266.  
  2267.  
  2268.  
  2269. #pragma segment TreeObj
  2270. void    GetPData(TreeObjHndl hndl, long offset, StringPtr data)
  2271. {
  2272.     StringPtr    dptr;
  2273.  
  2274.     dptr = (StringPtr)GetDataPtr(hndl) + offset;
  2275.     pcpy(data, dptr);
  2276. }
  2277.  
  2278.  
  2279.  
  2280. /**********************************************************************/
  2281.  
  2282.  
  2283.  
  2284. #pragma segment TreeObj
  2285. OSErr    PutPData(TreeObjHndl hndl, long offset, StringPtr data)
  2286. {
  2287.     unsigned char    *dptr;
  2288.     long            oldl, newl;
  2289.     OSErr            err;
  2290.  
  2291.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2292.  
  2293.     oldl = *dptr + 1;
  2294.     newl = (*(unsigned char *)data) + 1;
  2295.  
  2296.     err = SlideData(hndl, offset + oldl, newl - oldl);
  2297.     if (err) return(err);
  2298.  
  2299.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2300.     pcpy(dptr, data);
  2301.  
  2302.     return(noErr);
  2303. }
  2304.  
  2305.  
  2306.  
  2307. /**********************************************************************/
  2308.  
  2309.  
  2310.  
  2311. #pragma segment TreeObj
  2312. OSErr    PutShortData(TreeObjHndl hndl, long offset, void *data, unsigned short size)
  2313. {
  2314.     unsigned short    *dptr;
  2315.     long            oldl, newl;
  2316.     short            ss;
  2317.     OSErr            err;
  2318.  
  2319.     dptr = (unsigned short *)((unsigned char *)GetDataPtr(hndl) + offset);
  2320.  
  2321.     BlockMove(dptr, &ss, sizeof(short));
  2322.     oldl = ss    + sizeof(short);
  2323.     newl = size  + sizeof(short);
  2324.  
  2325.     err = SlideData(hndl, offset + oldl, newl - oldl);
  2326.     if (err) return(err);
  2327.  
  2328.     dptr = (unsigned short *)((unsigned char *)GetDataPtr(hndl) + offset);
  2329.     BlockMove(&size, dptr, sizeof(short));
  2330.     BlockMove(data, dptr + 1, size);
  2331.  
  2332.     return(noErr);
  2333. }
  2334.  
  2335.  
  2336.  
  2337. /**********************************************************************/
  2338.  
  2339.  
  2340.  
  2341. #pragma segment TreeObj
  2342. OSErr    PutLongData(TreeObjHndl hndl, long offset, void *data, long size)
  2343. {
  2344.     unsigned long    *dptr, ll;
  2345.     long            oldl, newl;
  2346.     OSErr            err;
  2347.  
  2348.     dptr = (unsigned long *)((unsigned char *)GetDataPtr(hndl) + offset);
  2349.  
  2350.     BlockMove(dptr, &ll, sizeof(long));
  2351.     oldl = ll    + sizeof(long);
  2352.     newl = size  + sizeof(long);
  2353.  
  2354.     err = SlideData(hndl, offset + oldl, newl - oldl);
  2355.     if (err) return(err);
  2356.  
  2357.     dptr = (unsigned long *)((unsigned char *)GetDataPtr(hndl) + offset);
  2358.     BlockMove(&size, dptr, sizeof(long));
  2359.     BlockMove(data, dptr + 1, size);
  2360.  
  2361.     return(noErr);
  2362. }
  2363.  
  2364.  
  2365.  
  2366. /**********************************************************************/
  2367.  
  2368.  
  2369.  
  2370. #pragma segment TreeObj
  2371. unsigned long    GetDataOffset(TreeObjHndl hndl, unsigned long offset, short dtype, short dnum)
  2372. {
  2373.     unsigned char    *dptr;
  2374.     short            ss;
  2375.     long            ll;
  2376.  
  2377.     while (dnum--) {
  2378.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2379.         switch (dtype) {
  2380.             case kCStr:
  2381.                 offset += clen((Ptr)dptr) + 1;
  2382.                 break;
  2383.             case kPStr:
  2384.                 offset += *dptr + 1;
  2385.                 break;
  2386.             case kSDataBlock:
  2387.                 BlockMove(dptr, &ss, sizeof(short));
  2388.                 offset += ss + sizeof(short);
  2389.                 break;
  2390.             case kLDataBlock:
  2391.                 BlockMove(dptr, &ll, sizeof(long));
  2392.                 offset += ll + sizeof(long);
  2393.                 break;
  2394.         }
  2395.     }
  2396.  
  2397.     return(offset);
  2398. }
  2399.  
  2400.  
  2401.  
  2402. /**********************************************************************/
  2403. /**********************************************************************/
  2404.  
  2405.  
  2406.  
  2407. #pragma segment TreeObj
  2408. Boolean    EqualTreeObjData(TreeObjHndl h1, TreeObjHndl h2)
  2409. {
  2410.     TreeObjProcPtr    proc;
  2411.  
  2412.     if ((h1) && (!h2)) return(false);
  2413.     if ((h2) && (!h1)) return(false);
  2414.  
  2415.     if ((*h1)->type != (*h2)->type) return(false);
  2416.  
  2417.     proc = gTreeObjMethods[(*h1)->type];
  2418.     if (proc)
  2419.         return((*proc)(h1, COMPAREMESSAGE, (long)h2));
  2420.     else
  2421.         return(DefaultEqualTreeObjData(h1, h2));
  2422. }
  2423.  
  2424.  
  2425.  
  2426. /**********************************************************************/
  2427.  
  2428.  
  2429.  
  2430. #pragma segment TreeObj
  2431. Boolean    DefaultEqualTreeObjData(TreeObjHndl h1, TreeObjHndl h2)
  2432. {
  2433.     Ptr        p1, p2;
  2434.     long    ii, jj;
  2435.  
  2436.     if ((jj = (*h1)->dataSize) != (*h2)->dataSize) return(false);
  2437.  
  2438.     p1 = GetDataPtr(h1);
  2439.     p2 = GetDataPtr(h2);
  2440.  
  2441.     for (ii = 0; ii < jj; ++ii) {
  2442.         if (p1[ii] != p2[ii]) return(false);
  2443.     }
  2444.  
  2445.     return(true);
  2446. }
  2447.  
  2448.  
  2449.  
  2450.