//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= // // Purpose: // //============================================================================= #ifndef IDATAMODEL_H #define IDATAMODEL_H #ifdef _WIN32 #pragma once #endif #include "tier1/interface.h" #include "tier1/utlvector.h" #include "tier1/utlsymbollarge.h" #include "appframework/IAppSystem.h" #include "datamodel/dmattributetypes.h" #include "datamodel/dmxheader.h" //----------------------------------------------------------------------------- // Forward declarations: //----------------------------------------------------------------------------- class CDmAttribute; class CDmElement; class IDmeOperator; class IElementForKeyValueCallback; class CDmElementFactoryHelper; struct DmValueBase_t; class CUtlBuffer; class KeyValues; class CUtlCharConversion; class CElementIdHash; // Use this for profiling dmx binary unserialization times //#define DMX_PROFILE_UNSERIALIZE 2 // If you set it to 2, then SFM will exit(-1) right after the first session is loaded (so you can see the PROFILE_SCOPE counters) #if defined( DMX_PROFILE_UNSERIALIZE ) #define DMX_PROFILE_SCOPE( name ) PROFILE_SCOPE( name ) #else #define DMX_PROFILE_SCOPE( name ) #endif //----------------------------------------------------------------------------- // element framework phases //----------------------------------------------------------------------------- enum DmPhase_t { PH_EDIT, PH_EDIT_APPLY, PH_EDIT_RESOLVE, PH_DEPENDENCY, PH_OPERATE, PH_OPERATE_RESOLVE, PH_OUTPUT, }; //----------------------------------------------------------------------------- // Handle to an CDmAttribute //----------------------------------------------------------------------------- enum DmAttributeHandle_t { DMATTRIBUTE_HANDLE_INVALID = 0xffffffff }; //----------------------------------------------------------------------------- // Handle to an DmAttributeList_t //----------------------------------------------------------------------------- enum DmAttributeReferenceIterator_t { DMATTRIBUTE_REFERENCE_ITERATOR_INVALID = 0 }; //----------------------------------------------------------------------------- // element framework interface //----------------------------------------------------------------------------- abstract_class IDmElementFramework : public IAppSystem { public: // Methods of IAppSystem virtual bool Connect( CreateInterfaceFn factory ) = 0; virtual void Disconnect() = 0; virtual void *QueryInterface( const char *pInterfaceName ) = 0; virtual InitReturnVal_t Init() = 0; virtual void Shutdown() = 0; virtual DmPhase_t GetPhase() = 0; virtual void SetOperators( const CUtlVector< IDmeOperator* > &operators ) = 0; virtual void BeginEdit() = 0; // ends in edit phase, forces apply/resolve if from edit phase virtual void Operate( bool bResolve ) = 0; // ends in output phase virtual void Resolve() = 0; virtual const CUtlVector< IDmeOperator* > &GetSortedOperators() const = 0; }; abstract_class IDmeElementCreated { public: virtual void OnElementCreated( CDmElement *pElement ) = 0; }; //----------------------------------------------------------------------------- // Used only by aplpications to hook in the element framework //----------------------------------------------------------------------------- #define VDMELEMENTFRAMEWORK_VERSION "VDmElementFrameworkVersion001" //----------------------------------------------------------------------------- // Main interface //----------------------------------------------------------------------------- extern IDmElementFramework *g_pDmElementFramework; //----------------------------------------------------------------------------- // datamodel operator interface - for all elements that need to be sorted in the operator dependency graph //----------------------------------------------------------------------------- abstract_class IDmeOperator { public: virtual bool IsDirty() = 0; // ie needs to operate virtual void Operate() = 0; virtual const char* GetOperatorName() const { return "Unknown"; } virtual void GetInputAttributes ( CUtlVector< CDmAttribute * > &attrs ) = 0; virtual void GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs ) = 0; virtual void SetSortKey( int key ) = 0; virtual int GetSortKey() const = 0; }; //----------------------------------------------------------------------------- // Class factory methods: //----------------------------------------------------------------------------- class IDmElementFactory { public: // Creation, destruction virtual CDmElement* Create( DmElementHandle_t handle, const char *pElementType, const char *pElementName, DmFileId_t fileid, const DmObjectId_t &id ) = 0; virtual void Destroy( DmElementHandle_t hElement ) = 0; virtual void AddOnElementCreatedCallback( IDmeElementCreated *callback ) = 0; virtual void RemoveOnElementCreatedCallback( IDmeElementCreated *callback ) = 0; virtual void OnElementCreated( CDmElement* pElement ) = 0; }; //----------------------------------------------------------------------------- // Various serialization methods can be installed into the data model factory //----------------------------------------------------------------------------- enum DmConflictResolution_t { CR_DELETE_NEW, CR_DELETE_OLD, CR_COPY_NEW, CR_FORCE_COPY, }; // convert files to elements and back // current file encodings supported: binary, keyvalues2, keyvalues2_flat, keyvalues (vmf/vmt/actbusy), text? (qc/obj) class IDmSerializer { public: virtual const char *GetName() const = 0; virtual const char *GetDescription() const = 0; virtual bool IsBinaryFormat() const = 0; virtual bool StoresVersionInFile() const = 0; virtual int GetCurrentVersion() const = 0; // Write into the UtlBuffer, return true if successful // if we decide to implement non-identity conversions between formats on write, then the source and dest format will need to be passed in here virtual bool Serialize( CUtlBuffer &buf, CDmElement *pRoot ) = 0; // Read from the UtlBuffer, return true if successful, and return the read-in root in ppRoot. virtual bool Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion, const char *pSourceFormatName, int nSourceFormatVersion, DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot ) = 0; // Methods used for importing (only should return non-NULL for serializers that return false from StoresVersionInFile) virtual const char *GetImportedFormat() const = 0; virtual int GetImportedVersion() const = 0; }; // convert legacy elements to non-legacy elements // legacy formats include: sfm_vN, binary_vN, keyvalues2_v1, keyvalues2_flat_v1 // where N is a version number (1..9 for sfm, 1..2 for binary) class IDmLegacyUpdater { public: virtual const char *GetName() const = 0; virtual bool IsLatestVersion() const = 0; // Updates ppRoot to first non-legacy generic dmx format, returns false if the conversion fails virtual bool Update( CDmElement **ppRoot ) = 0; }; // converts old elements to new elements // current formats include: sfm session, animset presets, particle definitions, exported maya character, etc. class IDmFormatUpdater { public: virtual const char *GetName() const = 0; virtual const char *GetDescription() const = 0; virtual const char *GetExtension() const = 0; virtual int GetCurrentVersion() const = 0; virtual const char *GetDefaultEncoding() const = 0; // Converts pSourceRoot from nSourceVersion to the current version, returns false if the conversion fails virtual bool Update( CDmElement **pRoot, int nSourceVersion ) = 0; }; //----------------------------------------------------------------------------- // Interface for callbacks to supply element types for specific keys inside keyvalues files //----------------------------------------------------------------------------- class IElementForKeyValueCallback { public: virtual const char *GetElementForKeyValue( const char *pszKeyName, int iNestingLevel ) = 0; }; //----------------------------------------------------------------------------- // Purpose: Optional helper passed in with clipboard data which is called when it's time to clean up the clipboard data in case the application // had some dynamically allocated data attached to a KV SetPtr object... //----------------------------------------------------------------------------- abstract_class IClipboardCleanup { public: virtual void ReleaseClipboardData( CUtlVector< KeyValues * >& list ) = 0; }; //----------------------------------------------------------------------------- // Purpose: Can be installed to be called back when data changes //----------------------------------------------------------------------------- enum DmNotifySource_t { // Sources NOTIFY_SOURCE_APPLICATION = 0, NOTIFY_SOURCE_UNDO, NOTIFY_SOURCE_FIRST_DME_CONTROL_SOURCE = 4, // Sources from dme_controls starts here NOTIFY_SOURCE_FIRST_APPLICATION_SOURCE = 8, // Sources from applications starts here }; enum DmNotifyFlags_t { // Does this dirty the document? NOTIFY_SOURCE_BITS = 8, NOTIFY_SETDIRTYFLAG = (1<& list ) = 0; virtual void AddUndoElement( IUndoElement *pElement ) = 0; virtual CUtlSymbolLarge GetUndoDescInternal( const char *context ) = 0; virtual CUtlSymbolLarge GetRedoDescInternal( const char *context ) = 0; virtual void EmptyClipboard() = 0; virtual void SetClipboardData( CUtlVector< KeyValues * >& data, IClipboardCleanup *pfnOptionalCleanuFunction = 0 ) = 0; virtual void AddToClipboardData( KeyValues *add ) = 0; virtual void GetClipboardData( CUtlVector< KeyValues * >& data ) = 0; virtual bool HasClipboardData() const = 0; // Handles to attributes virtual CDmAttribute * GetAttribute( DmAttributeHandle_t h ) = 0; virtual bool IsAttributeHandleValid( DmAttributeHandle_t h ) const = 0; // file id reference methods virtual int NumFileIds() = 0; virtual DmFileId_t GetFileId( int i ) = 0; virtual DmFileId_t FindOrCreateFileId( const char *pFilename ) = 0; virtual void RemoveFileId( DmFileId_t fileid ) = 0; virtual DmFileId_t GetFileId( const char *pFilename ) = 0; virtual const char * GetFileName( DmFileId_t fileid ) = 0; virtual void SetFileName( DmFileId_t fileid, const char *pFileName ) = 0; virtual const char * GetFileFormat( DmFileId_t fileid ) = 0; virtual void SetFileFormat( DmFileId_t fileid, const char *pFormat ) = 0; virtual DmElementHandle_t GetFileRoot( DmFileId_t fileid ) = 0; virtual void SetFileRoot( DmFileId_t fileid, DmElementHandle_t hRoot ) = 0; virtual long GetFileModificationUTCTime( DmFileId_t fileid ) = 0; virtual long GetCurrentUTCTime() = 0; virtual void UTCTimeToString( char *pString, int maxChars, long fileTime ) = 0; virtual bool IsFileLoaded( DmFileId_t fileid ) = 0; virtual void MarkFileLoaded( DmFileId_t fileid ) = 0; virtual void UnloadFile( DmFileId_t fileid ) = 0; virtual int NumElementsInFile( DmFileId_t fileid ) = 0; virtual void DontAutoDelete( DmElementHandle_t hElement ) = 0; // handle validity methods - these shouldn't really be here, but the undo system needs them... virtual void MarkHandleInvalid( DmElementHandle_t hElement ) = 0; virtual void MarkHandleValid( DmElementHandle_t hElement ) = 0; virtual DmElementHandle_t FindElement( const DmObjectId_t &id ) = 0; virtual void GetExistingElements( CElementIdHash &hash ) const = 0; virtual DmAttributeReferenceIterator_t FirstAttributeReferencingElement( DmElementHandle_t hElement ) = 0; virtual DmAttributeReferenceIterator_t NextAttributeReferencingElement( DmAttributeReferenceIterator_t hAttrIter ) = 0; virtual CDmAttribute * GetAttribute( DmAttributeReferenceIterator_t hAttrIter ) = 0; // Install, remove notify callbacks associated w/ undo contexts virtual bool InstallNotificationCallback( IDmNotify *pNotify ) = 0; virtual void RemoveNotificationCallback( IDmNotify *pNotify ) = 0; virtual bool IsSuppressingNotify( ) const = 0; virtual void SetSuppressingNotify( bool bSuppress ) = 0; virtual void PushNotificationScope( const char *pReason, int nNotifySource, int nNotifyFlags ) = 0; virtual void PopNotificationScope( bool bAbort = false ) = 0; virtual const char *GetUndoString( CUtlSymbolLarge sym ) = 0; virtual bool HasElementFactory( const char *pElementType ) const = 0; // Call before you make any undo records virtual void SetUndoDepth( int nSize ) = 0; // Displays memory stats to the console (passing in invalid handle causes memory for all elements to be estimated, otherwise it's only // the element and it's recursive children) virtual void DisplayMemoryStats( DmElementHandle_t hElement = DMELEMENT_HANDLE_INVALID ) = 0; // Is this file a DMX file? virtual bool IsDMXFormat( CUtlBuffer &buf ) const = 0; // Dump the symbol table to the console virtual void DumpSymbolTable() = 0; virtual void AddOnElementCreatedCallback( const char *pElementType, IDmeElementCreated *callback ) = 0; virtual void RemoveOnElementCreatedCallback( const char *pElementType, IDmeElementCreated *callback ) = 0; }; //----------------------------------------------------------------------------- // Used only by applications to hook in the data model //----------------------------------------------------------------------------- #define VDATAMODEL_INTERFACE_VERSION "VDataModelVersion001" //----------------------------------------------------------------------------- // Main interface accessor //----------------------------------------------------------------------------- extern IDataModel *g_pDataModel; //----------------------------------------------------------------------------- // Allows clients to implement customized undo elements //----------------------------------------------------------------------------- class CUndoElement : public IUndoElement { public: CUndoElement( const char *pDesc ) { m_UndoDesc = g_pDataModel->GetUndoDescInternal( pDesc ); m_RedoDesc = g_pDataModel->GetRedoDescInternal( pDesc ); m_pDesc = pDesc; m_bEndOfStream = false; } virtual void Release() { delete this; } virtual const char *UndoDesc() const { return g_pDataModel->GetUndoString( m_UndoDesc ); } virtual const char *RedoDesc() const { return g_pDataModel->GetUndoString( m_RedoDesc ); } virtual const char *GetDesc() const { return m_pDesc; } protected: virtual bool IsEndOfStream() const { return m_bEndOfStream; } virtual void SetEndOfStream( bool end ) { m_bEndOfStream = end; } const char *m_pDesc; CUtlSymbolLarge m_UndoDesc; CUtlSymbolLarge m_RedoDesc; bool m_bEndOfStream; private: friend class CUndoManager; }; //----------------------------------------------------------------------------- // Purpose: Simple helper class //----------------------------------------------------------------------------- class CUndoScopeGuard { public: explicit CUndoScopeGuard( const char *udesc, const char *rdesc = NULL ) { m_bReleased = false; m_bNotify = false; m_pNotify = NULL; g_pDataModel->StartUndo( udesc, rdesc ? rdesc : udesc ); } explicit CUndoScopeGuard( int nChainingID, char const *udesc ) { m_bReleased = false; m_bNotify = false; m_pNotify = NULL; g_pDataModel->StartUndo( udesc, udesc, nChainingID ); } explicit CUndoScopeGuard( int nNotifySource, int nNotifyFlags, const char *udesc, const char *rdesc = NULL, int nChainingID = 0 ) { m_bReleased = false; m_bNotify = true; m_pNotify = NULL; g_pDataModel->StartUndo( udesc, rdesc ? rdesc : udesc, nChainingID ); g_pDataModel->PushNotificationScope( udesc, nNotifySource, nNotifyFlags ); } explicit CUndoScopeGuard( int nNotifySource, int nNotifyFlags, IDmNotify *pNotify, const char *udesc, const char *rdesc = NULL, int nChainingID = 0 ) { m_bReleased = false; m_bNotify = true; m_pNotify = NULL; g_pDataModel->StartUndo( udesc, rdesc ? rdesc : udesc, nChainingID ); if ( pNotify ) { if ( g_pDataModel->InstallNotificationCallback( pNotify ) ) { m_pNotify = pNotify; } } g_pDataModel->PushNotificationScope( udesc, nNotifySource, nNotifyFlags ); } ~CUndoScopeGuard() { Release(); } void Release() { if ( !m_bReleased ) { g_pDataModel->FinishUndo(); if ( m_bNotify ) { g_pDataModel->PopNotificationScope( ); m_bNotify = false; } if ( m_pNotify ) { g_pDataModel->RemoveNotificationCallback( m_pNotify ); m_pNotify = NULL; } m_bReleased = true; } } void Abort() { if ( !m_bReleased ) { g_pDataModel->AbortUndoableOperation(); if ( m_bNotify ) { g_pDataModel->PopNotificationScope( true ); m_bNotify = false; } if ( m_pNotify ) { g_pDataModel->RemoveNotificationCallback( m_pNotify ); m_pNotify = NULL; } m_bReleased = true; } } private: IDmNotify *m_pNotify; bool m_bReleased; bool m_bNotify; }; //----------------------------------------------------------------------------- // Purpose: Simple helper class to disable Undo/Redo operations when in scope //----------------------------------------------------------------------------- class CChangeUndoScopeGuard { public: CChangeUndoScopeGuard( bool bNewState ) { m_bReleased = false; m_bNotify = false; m_pNotify = NULL; m_bOldValue = g_pDataModel->IsUndoEnabled(); g_pDataModel->SetUndoEnabled( bNewState ); }; CChangeUndoScopeGuard( bool bNewState, const char *pDesc, int nNotifySource, int nNotifyFlags, IDmNotify *pNotify = NULL ) { m_bReleased = false; m_bOldValue = g_pDataModel->IsUndoEnabled(); g_pDataModel->SetUndoEnabled( bNewState ); m_bNotify = true; m_pNotify = NULL; if ( pNotify ) { if ( g_pDataModel->InstallNotificationCallback( pNotify ) ) { m_pNotify = pNotify; } } g_pDataModel->PushNotificationScope( pDesc, nNotifySource, nNotifyFlags ); }; ~CChangeUndoScopeGuard() { Release(); } void Release() { // Releases the guard... if ( !m_bReleased ) { g_pDataModel->SetUndoEnabled( m_bOldValue ); m_bReleased = true; if ( m_bNotify ) { g_pDataModel->PopNotificationScope( ); m_bNotify = false; } if ( m_pNotify ) { g_pDataModel->RemoveNotificationCallback( m_pNotify ); m_pNotify = NULL; } } } private: IDmNotify *m_pNotify; bool m_bOldValue; bool m_bReleased; bool m_bNotify; }; class CDisableUndoScopeGuard : public CChangeUndoScopeGuard { typedef CChangeUndoScopeGuard BaseClass; public: CDisableUndoScopeGuard() : BaseClass( false ) { } CDisableUndoScopeGuard( const char *pDesc, int nNotifySource, int nNotifyFlags, IDmNotify *pNotify = NULL ) : BaseClass( false, pDesc, nNotifySource, nNotifyFlags, pNotify ) {} }; class CEnableUndoScopeGuard : public CChangeUndoScopeGuard { typedef CChangeUndoScopeGuard BaseClass; public: CEnableUndoScopeGuard( ) : BaseClass( true ) { } CEnableUndoScopeGuard( const char *pDesc, int nNotifySource, int nNotifyFlags, IDmNotify *pNotify = NULL ) : BaseClass( true, pDesc, nNotifySource, nNotifyFlags, pNotify ) {} }; #define DEFINE_SOURCE_UNDO_SCOPE_GUARD( _classnameprefix, _source ) \ class C ## _classnameprefix ## UndoScopeGuard : public CUndoScopeGuard \ { \ typedef CUndoScopeGuard BaseClass; \ \ public: \ C ## _classnameprefix ## UndoScopeGuard( int nNotifyFlags, const char *pUndoDesc, const char *pRedoDesc = NULL, int nChainingID = 0 ) : \ BaseClass( _source, nNotifyFlags, pUndoDesc, pRedoDesc, nChainingID ) \ { \ } \ C ## _classnameprefix ## UndoScopeGuard( int nNotifyFlags, IDmNotify *pNotify, const char *pUndoDesc, const char *pRedoDesc = NULL, int nChainingID = 0 ) : \ BaseClass( _source, nNotifyFlags, pNotify, pUndoDesc, pRedoDesc, nChainingID ) \ { \ } \ C ## _classnameprefix ## UndoScopeGuard( int nNotifyFlags, const char *pUndoDesc, int nChainingID ) : \ BaseClass( _source, nNotifyFlags, pUndoDesc, pUndoDesc, nChainingID ) \ { \ } \ }; \ class C ## _classnameprefix ## DisableUndoScopeGuard : public CDisableUndoScopeGuard \ { \ typedef CDisableUndoScopeGuard BaseClass; \ \ public: \ C ## _classnameprefix ## DisableUndoScopeGuard( const char *pDesc, int nNotifyFlags, IDmNotify *pNotify = NULL ) : \ BaseClass( pDesc, _source, nNotifyFlags, pNotify ) \ { \ } \ }; \ class C ## _classnameprefix ## EnableUndoScopeGuard : public CEnableUndoScopeGuard \ { \ typedef CEnableUndoScopeGuard BaseClass; \ \ public: \ C ## _classnameprefix ## EnableUndoScopeGuard( const char *pDesc, int nNotifyFlags, IDmNotify *pNotify = NULL ) : \ BaseClass( pDesc, _source, nNotifyFlags, pNotify ) \ { \ } \ } //----------------------------------------------------------------------------- // Purpose: Simple helper class to disable NotifyDataChanged from current scope //----------------------------------------------------------------------------- class CNotifyScopeGuard { public: CNotifyScopeGuard( const char *pReason, int nNotifySource, int nNotifyFlags, IDmNotify *pNotify = NULL ) { m_bReleased = false; m_pNotify = NULL; g_pDataModel->PushNotificationScope( pReason, nNotifySource, nNotifyFlags ); if ( pNotify ) { if ( g_pDataModel->InstallNotificationCallback( pNotify ) ) { m_pNotify = pNotify; } } }; ~CNotifyScopeGuard() { Release(); } void Release() { // Releases the guard... if ( !m_bReleased ) { g_pDataModel->PopNotificationScope( ); if ( m_pNotify ) { g_pDataModel->RemoveNotificationCallback( m_pNotify ); m_pNotify = NULL; } m_bReleased = true; } } private: CNotifyScopeGuard( const CNotifyScopeGuard& g ); private: IDmNotify *m_pNotify; bool m_bReleased; }; #define DEFINE_SOURCE_NOTIFY_SCOPE_GUARD( _classnameprefix, _source ) \ class C ## _classnameprefix ## NotifyScopeGuard : public CNotifyScopeGuard \ { \ typedef CNotifyScopeGuard BaseClass; \ \ public: \ C ## _classnameprefix ## NotifyScopeGuard( const char *pReason, int nNotifyFlags, IDmNotify *pNotify = NULL ) : \ BaseClass( pReason, _source, nNotifyFlags, pNotify )\ { \ } \ } //----------------------------------------------------------------------------- // Purpose: Simple helper class to disable notifications when in scope //----------------------------------------------------------------------------- class CChangeNotifyScopeGuard { public: CChangeNotifyScopeGuard( bool bNewState ) { m_bReleased = false; m_bOldValue = g_pDataModel->IsSuppressingNotify(); g_pDataModel->SetSuppressingNotify( bNewState ); }; ~CChangeNotifyScopeGuard() { Release(); } void Release() { // Releases the guard... if ( !m_bReleased ) { g_pDataModel->SetSuppressingNotify( m_bOldValue ); m_bReleased = true; } } private: bool m_bOldValue; bool m_bReleased; }; class CDisableNotifyScopeGuard : public CChangeNotifyScopeGuard { typedef CChangeNotifyScopeGuard BaseClass; public: CDisableNotifyScopeGuard() : BaseClass( true ) { } private: CDisableNotifyScopeGuard( const CDisableNotifyScopeGuard& g ); }; class CEnableNotifyScopeGuard : public CChangeNotifyScopeGuard { typedef CChangeNotifyScopeGuard BaseClass; public: CEnableNotifyScopeGuard( ) : BaseClass( false ) { } private: CEnableNotifyScopeGuard( const CEnableNotifyScopeGuard& g ); }; //----------------------------------------------------------------------------- // Standard undo/notify guards for the application //----------------------------------------------------------------------------- DEFINE_SOURCE_UNDO_SCOPE_GUARD( App, NOTIFY_SOURCE_APPLICATION ); DEFINE_SOURCE_NOTIFY_SCOPE_GUARD( App, NOTIFY_SOURCE_APPLICATION ); #endif // IDATAMODEL_H