+ 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();
+ }
+}