referencemgr.h

Go to the documentation of this file.
00001 //**************************************************************************/
00002 // Copyright (c) 1998-2006 Autodesk, Inc.
00003 // All rights reserved.
00004 // 
00005 // These coded instructions, statements, and computer programs contain
00006 // unpublished proprietary information written by Autodesk, Inc., and are
00007 // protected by Federal copyright law. They may not be disclosed to third
00008 // parties or copied or duplicated in any form, in whole or in part, without
00009 // the prior written consent of Autodesk, Inc.
00010 //**************************************************************************/
00011 // FILE:        referenceMgr.h
00012 // DESCRIPTION: manages references to any maxsdk ReferenceTarget
00013 // AUTHOR:      michael malone (mjm)
00014 // HISTORY:     created February 01, 2000
00015 //**************************************************************************/
00016 
00017 #pragma once
00018 
00019 #include "maxheap.h"
00020 #include "ref.h"
00021 
00022 class RefTarget: public MaxHeapOperators
00023 {
00024 protected:
00025     RefTargetHandle mRef;
00026     Interval mValid;
00027     bool mHeld;
00028 
00029 public:
00030     RefTarget() : mRef(NULL), mHeld(false) { }
00031     RefTarget(RefTargetHandle ref, const Interval& valid = NEVER) : mRef(ref), mHeld(false) { mValid = valid; }
00032     RefTarget(const RefTarget& refTarget) : mRef(refTarget.mRef), mHeld(false) { mValid = refTarget.mValid; }
00033     virtual ~RefTarget() {;}
00034     virtual RefTarget& operator=(const RefTarget& refTarget) { mRef = refTarget.mRef; mValid = refTarget.mValid; return *this; }
00035 
00036     RefTargetHandle GetRefTargetHandle() const { return mRef; }
00037     void SetRefTargetHandle(RefTargetHandle ref) { mRef = ref; }
00038     const Interval& GetValidity() const { return mValid; }
00039     void SetValidity(const Interval& valid) { mValid = valid; }
00040     bool IsHeld() const { return mHeld; }
00041     void SetHeld(bool held) { mHeld = held; }
00042 }; 
00043 
00044 // forward declaration
00045 template <class T> class RefMgr;
00046 
00047 // template type must be class RefTarget (or derived from it)
00048 // We handle undo and redo special in the RefMgr class. This is because
00049 // We need to save the object that we are storing along with the
00050 // reference. We override DeleteReference to add a special undo
00051 // record to the undo stack, and we also add an undo record at the
00052 template <class T> class RefMgrAddDeleteRestore : public RestoreObj, public ReferenceMaker
00053 {
00054     RefMgr<T>*          mMgr;           // The RefMgr that we are restoring
00055     T*                  mRefTarg;       // The data that needs to be restored
00056                                         // This is NULL if the reference needs removing
00057     RefTargetHandle     mRef;           // The ReferenceTarget we are restoring
00058     BOOL                undone;         // True if undone
00059 
00060 public:
00061     RefMgrAddDeleteRestore(RefMgr<T>* mgr, int which, RefTargetHandle ref)
00062         : mMgr(NULL), mRefTarg(which < 0 ? NULL : mgr->GetRefTarget(which)), mRef(ref), undone(FALSE)
00063     {
00064         ReplaceReference(0, mgr);
00065         if (mRefTarg != NULL)
00066             mRefTarg->SetHeld(true);
00067     }
00068         
00069     ~RefMgrAddDeleteRestore()
00070     {
00071         if (mRefTarg != NULL) {
00072             if (mMgr != NULL)
00073                 mMgr->RemoveRef(mRefTarg);
00074             delete mRefTarg;
00075         }
00076         DeleteAllRefs();
00077     }
00078 
00079     virtual Class_ID ClassID() { return Class_ID(0x102f266c, 0x23492667); }
00080 
00081 // Restore handles both undo and redo. It swaps mRefTarg with any
00082 // reference already in the RefMgr.
00083 // Really should implement both Restore and Redo, but doing so breaks 
00084 // compatibility. So Restore needs to check to see whether it is really doing
00085 // a Restore or a Redo.
00086 // In some cases, Restore can be called twice in a row, with no
00087 // interleaving Redo(). For example, theHold.Restore();theHold.Cancel();
00088 // No know case of Redo being called twice in a row, but play it safe.
00089 // member variable 'undone' used as interlock
00090     void Restore(int isUndo)
00091     {
00092         int isUndo2;
00093         BOOL isInRestore = theHold.Restoring(isUndo2);
00094         if (isInRestore)  // really doing a restore
00095         {
00096             if (undone) return;
00097             undone = TRUE;
00098         }
00099         else  // really doing a redo
00100         {
00101             if (!undone) return;
00102             undone = FALSE;
00103         }
00104         if (mMgr != NULL) {
00105             if (mRefTarg == NULL) {
00106                 int i = mMgr->FindRefTargetHandleIndex(mRef);
00107                 if (i >= 0) {
00108                     mRefTarg = mMgr->mRefTargetPtrs[i];
00109                     mRefTarg->SetHeld(true);
00110                     mMgr->mRefTargetPtrs.Delete(i, 1);
00111                 }
00112             } else {
00113                 mMgr->RemoveRef(mRefTarg);
00114                 mRefTarg->SetRefTargetHandle(mRef);
00115                 mMgr->mRefTargetPtrs.Append(1, &mRefTarg, 3);
00116                 mRefTarg->SetHeld(false);
00117                 mRefTarg = NULL;
00118             }
00119         }
00120     }
00121                 
00122     void Redo() { Restore(false); }
00123 
00124     MSTR Description() { return _M("RefMgrAddDeleteRestore"); }
00125 
00126     virtual RefResult NotifyRefChanged(
00127         Interval            changeInt,
00128         RefTargetHandle     hTarget,
00129         PartID&             partID,
00130         RefMessage          message
00131     )
00132     {
00133         switch (message) {
00134         case REFMSG_TARGET_DELETED:
00135             mMgr = NULL;
00136             break;
00137         }
00138 
00139         return REF_SUCCEED;
00140     }
00141     virtual int NumRefs() { return 1; }
00142     virtual RefTargetHandle GetReference(int i) { return i == 0 ? mMgr : NULL; }
00143 protected:
00144     virtual void SetReference(int i, RefTargetHandle rtarg)
00145     {
00146         if (i == 0)
00147             mMgr = static_cast<RefMgr<T>*>(rtarg);
00148     }
00149 public:
00150     virtual BOOL CanTransferReference(int i) { return FALSE; }
00151 };
00152 
00153 
00154 // template type must be class RefTarget (or derived from it)
00155 template <class T> class RefMgr : public ReferenceTarget
00156 {
00157     friend class RefMgrAddDeleteRestore<T>;
00158 
00159 protected:
00160     typedef RefResult (* TNotifyCB)(void *cbParam, Interval changeInt, T* pRefTarget, RefTargetHandle hTarger, PartID& partID, RefMessage message);
00161 
00162     Tab<T*> mRefTargetPtrs;
00163     TNotifyCB mNotifyCB;
00164     void *mNotifyCBParam;
00165 
00166 public:
00167     RefMgr() : mNotifyCB(NULL), mNotifyCBParam(NULL) { }
00168 
00169     virtual ~RefMgr() { DeleteAllRefs(); }
00170 
00171     virtual Class_ID ClassID() { return Class_ID(0x7eff2f08, 0x25707aa2); }
00172 
00173     virtual void DeleteThis() 
00174     { 
00175         delete this; 
00176     }
00177 
00178     virtual void Init(TNotifyCB notifyCB=NULL, void *notifyCBParam=NULL)
00179     {
00180         mRefTargetPtrs.ZeroCount();
00181         mNotifyCB = notifyCB;
00182         mNotifyCBParam = notifyCBParam;
00183     }
00184 
00185     virtual int Count() { return mRefTargetPtrs.Count(); }
00186 
00187     virtual int FindRefTargetHandleIndex(RefTargetHandle rtarg)
00188     {
00189         int n = mRefTargetPtrs.Count();
00190         for (int i=0; i<n; i++)
00191         {
00192             if (mRefTargetPtrs[i]->GetRefTargetHandle() == rtarg)
00193                 return i;
00194         }
00195         return -1;
00196     }
00197 
00198     virtual int FindRefTargetIndex(T *pRefTarget)
00199     {
00200         int n = mRefTargetPtrs.Count();
00201         for (int i=0; i<n; i++)
00202         {
00203             if (mRefTargetPtrs[i]->GetRefTargetHandle() == pRefTarget->GetRefTargetHandle())
00204                 return i;
00205         }
00206         return -1;
00207     }
00208 
00209     virtual T* FindRefTarget(RefTargetHandle rtarg)
00210     {
00211         int index = FindRefTargetHandleIndex(rtarg);
00212         return (index == -1) ? NULL : mRefTargetPtrs[index];
00213     }
00214 
00215     virtual T* GetRefTarget(int i)
00216     {
00217         if ( i < mRefTargetPtrs.Count() )
00218             return mRefTargetPtrs[i];
00219         else
00220             return NULL;
00221     }
00222 
00223     virtual bool AddUndo(int which, RefTargetHandle ref)
00224     {
00225         bool undo = false;
00226         if (!theHold.RestoreOrRedoing()) {
00227             int resumeCount = 0;
00228 
00229             while (theHold.IsSuspended()) {
00230                 theHold.Resume();
00231                 ++resumeCount;
00232             }
00233 
00234             undo = theHold.Holding() != 0;
00235             if (undo)
00236                 theHold.Put(new RefMgrAddDeleteRestore<T>(this, which, ref));
00237             
00238             while (--resumeCount >= 0)
00239                 theHold.Suspend();
00240         }
00241         return undo;
00242     }
00243 
00244     virtual bool AddRef(T* pRefTarget)
00245     {
00246         // make sure rtarg is valid and hasn't already been referenced
00247         RefTargetHandle pRefTargHandle = pRefTarget->GetRefTargetHandle();
00248         if ( (pRefTargHandle != NULL) && (FindRefTargetIndex(pRefTarget) == -1) )
00249         {
00250             pRefTarget->SetRefTargetHandle(NULL);
00251             mRefTargetPtrs.Append(1, &pRefTarget);
00252             HoldSuspend hs;
00253             if ( ReplaceReference (mRefTargetPtrs.Count()-1, pRefTargHandle) != REF_SUCCEED )
00254             {
00255                 pRefTarget->SetRefTargetHandle(pRefTargHandle);
00256                 mRefTargetPtrs.Delete(mRefTargetPtrs.Count()-1, 1);
00257                 return false;
00258             }
00259             hs.Resume();
00260             NotifyDependents(FOREVER, 0, REFMSG_SUBANIM_STRUCTURE_CHANGED);
00261 
00262             // Handle undo if we need to
00263             AddUndo(-1, pRefTarget->GetRefTargetHandle());
00264             return true;
00265         }
00266         else
00267             return false;
00268     }
00269 
00270 // mjm - begin - 06.20.00
00271     virtual bool RemoveRef(RefTargetHandle rtarg)
00272     {
00273         return RemoveRef( FindRefTargetHandleIndex(rtarg) );
00274     }
00275 
00276     virtual bool RemoveRef(T* pRefTarget)
00277     {
00278         return RemoveRef( FindRefTargetIndex(pRefTarget) );
00279     }
00280 
00281     virtual bool RemoveRef(int index)
00282     {
00283         if ( (index >= 0) && ( index < Count() ) )
00284         {
00285             DeleteReference(index);
00286             T* targ = mRefTargetPtrs[index];
00287             if (targ != NULL && !targ->IsHeld())
00288             {
00289                 delete targ;
00290             }
00291             mRefTargetPtrs.Delete(index, 1);
00292             NotifyDependents(FOREVER, 0, REFMSG_SUBANIM_STRUCTURE_CHANGED);
00293             return true;
00294         }
00295         return false;
00296     }
00297 // mjm - end
00298 
00299     virtual void Clear()
00300     {
00301         DeleteAllRefsFromMe();
00302         for ( int i = mRefTargetPtrs.Count(); --i >= 0; ) {
00303             T* targ = mRefTargetPtrs[i];
00304             if (targ != NULL && !targ->IsHeld())
00305             {
00306                 delete targ;
00307             }
00308         }
00309         mRefTargetPtrs.ZeroCount();
00310         NotifyDependents(FOREVER, 0, REFMSG_SUBANIM_STRUCTURE_CHANGED);
00311     }
00312 
00313 
00314     // from class ReferenceMaker
00315     virtual int NumRefs() { return Count(); }
00316 
00317     // In order to make undo work we need to keep an undo record
00318     // for the T* object we keep with the reference.
00319     virtual RefResult DeleteReference(int which) {
00320         RefTargetHandle ref = GetReference(which);
00321         RefResult ret = ReferenceTarget::DeleteReference(which);
00322         if (ret == REF_SUCCEED) {
00323             AddUndo(which, ref);
00324         }
00325         return ret;
00326     }
00327 
00328     virtual ReferenceTarget* GetReference(int i)
00329     {
00330         if ( i < mRefTargetPtrs.Count() )
00331             return mRefTargetPtrs[i]->GetRefTargetHandle();
00332         else
00333             return NULL;
00334     }
00335 
00336 protected:
00337     virtual void SetReference(int i, RefTargetHandle rtarg)
00338     {
00339         // Ignore SetReference during redo or undo, this is because the
00340         // RefMgrAddDeleteRestore will fix up everything.
00341         if (!theHold.RestoreOrRedoing()) {
00342             int count = mRefTargetPtrs.Count();
00343             assert(i < count || rtarg == NULL);
00344             if ( i < count )
00345                 mRefTargetPtrs[i]->SetRefTargetHandle(rtarg);
00346         }
00347     }
00348 public:
00349     virtual RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID,  RefMessage message)
00350     {
00351         RefResult refResult(REF_SUCCEED);
00352         if (mNotifyCB)
00353         {
00354             T* pRefTarget = FindRefTarget(hTarget);
00355             refResult = mNotifyCB(mNotifyCBParam, changeInt, pRefTarget, hTarget, partID,  message);
00356         }
00357 
00358         switch (message)
00359         {
00360             case REFMSG_TARGET_DELETED:
00361             {
00362 // mjm - begin - 6.20.00
00363                 int i = FindRefTargetHandleIndex(hTarget);
00364                 assert(i >= 0);
00365                 if (i >= 0)
00366                 {
00367                     // We need to handle undo
00368                     if (!AddUndo(i, hTarget))
00369                     {
00370                         delete mRefTargetPtrs[i];
00371                     }
00372                     mRefTargetPtrs.Delete(i, 1);
00373                 }
00374 // mjm - end
00375                 break;
00376             }
00377         }
00378         return refResult;
00379     }
00380 };
00381