+UndoStack* m_stack;
+public:
+
+UndoStackFiller()
+ : m_stack( 0 ){
+}
+void save( Undoable* undoable ){
+ ASSERT_NOTNULL( undoable );
+
+ if ( m_stack != 0 ) {
+ m_stack->save( undoable );
+ m_stack = 0;
+ }
+}
+void setStack( UndoStack* stack ){
+ m_stack = stack;
+}
+};
+
+typedef std::map<Undoable*, UndoStackFiller> undoables_t;
+undoables_t m_undoables;
+
+void mark_undoables( UndoStack* stack ){
+ for ( undoables_t::iterator i = m_undoables.begin(); i != m_undoables.end(); ++i )
+ {
+ ( *i ).second.setStack( stack );
+ }
+}
+
+std::size_t m_undo_levels;
+
+typedef std::set<UndoTracker*> Trackers;
+Trackers m_trackers;
+public:
+RadiantUndoSystem()
+ : m_undo_levels( 64 ){
+}
+~RadiantUndoSystem(){
+ clear();
+}
+UndoObserver* observer( Undoable* undoable ){
+ ASSERT_NOTNULL( undoable );
+
+ return &m_undoables[undoable];
+}
+void release( Undoable* undoable ){
+ ASSERT_NOTNULL( undoable );
+
+ m_undoables.erase( undoable );
+}
+void setLevels( std::size_t levels ){
+ if ( levels > MAX_UNDO_LEVELS() ) {
+ levels = MAX_UNDO_LEVELS();
+ }
+
+ while ( m_undo_stack.size() > levels )
+ {
+ m_undo_stack.pop_front();
+ }
+ m_undo_levels = levels;
+}
+std::size_t getLevels() const {
+ return m_undo_levels;
+}
+std::size_t size() const {
+ return m_undo_stack.size();
+}
+void startUndo(){
+ m_undo_stack.start( "unnamedCommand" );
+ mark_undoables( &m_undo_stack );
+}
+bool finishUndo( const char* command ){
+ bool changed = m_undo_stack.finish( command );
+ mark_undoables( 0 );
+ return changed;
+}
+void startRedo(){
+ m_redo_stack.start( "unnamedCommand" );
+ mark_undoables( &m_redo_stack );
+}
+bool finishRedo( const char* command ){
+ bool changed = m_redo_stack.finish( command );
+ mark_undoables( 0 );
+ return changed;
+}
+void start(){
+ m_redo_stack.clear();
+ if ( m_undo_stack.size() == m_undo_levels ) {
+ m_undo_stack.pop_front();
+ }
+ startUndo();
+ trackersBegin();
+}
+void finish( const char* command ){
+ if ( finishUndo( command ) ) {
+ globalOutputStream() << command << '\n';
+ }
+}
+void undo(){
+ if ( m_undo_stack.empty() ) {
+ globalOutputStream() << "Undo: no undo available\n";
+ }
+ else
+ {
+ Operation* operation = m_undo_stack.back();
+ globalOutputStream() << "Undo: " << operation->m_command.c_str() << "\n";
+
+ startRedo();
+ trackersUndo();
+ operation->m_snapshot.restore();
+ finishRedo( operation->m_command.c_str() );
+ m_undo_stack.pop_back();
+ }
+}
+void redo(){
+ if ( m_redo_stack.empty() ) {
+ globalOutputStream() << "Redo: no redo available\n";
+ }
+ else
+ {
+ Operation* operation = m_redo_stack.back();
+ globalOutputStream() << "Redo: " << operation->m_command.c_str() << "\n";
+
+ startUndo();
+ trackersRedo();
+ operation->m_snapshot.restore();
+ finishUndo( operation->m_command.c_str() );
+ m_redo_stack.pop_back();
+ }
+}
+void clear(){
+ mark_undoables( 0 );
+ m_undo_stack.clear();
+ m_redo_stack.clear();
+ trackersClear();
+}
+void trackerAttach( UndoTracker& tracker ){
+ ASSERT_MESSAGE( m_trackers.find( &tracker ) == m_trackers.end(), "undo tracker already attached" );
+ m_trackers.insert( &tracker );
+}
+void trackerDetach( UndoTracker& tracker ){
+ ASSERT_MESSAGE( m_trackers.find( &tracker ) != m_trackers.end(), "undo tracker cannot be detached" );
+ m_trackers.erase( &tracker );
+}
+void trackersClear() const {
+ for ( Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i )
+ {
+ ( *i )->clear();
+ }
+}
+void trackersBegin() const {
+ for ( Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i )
+ {
+ ( *i )->begin();
+ }
+}
+void trackersUndo() const {
+ for ( Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i )
+ {
+ ( *i )->undo();
+ }
+}
+void trackersRedo() const {
+ for ( Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i )
+ {
+ ( *i )->redo();
+ }
+}
+};
+
+
+
+void UndoLevels_importString( RadiantUndoSystem& undo, const char* value ){
+ int levels;
+ Int_importString( levels, value );
+ undo.setLevels( levels );
+}
+typedef ReferenceCaller1<RadiantUndoSystem, const char*, UndoLevels_importString> UndoLevelsImportStringCaller;
+void UndoLevels_exportString( const RadiantUndoSystem& undo, const StringImportCallback& importer ){
+ Int_exportString( static_cast<int>( undo.getLevels() ), importer );