-UndoStack() : m_pending( 0 ){
-}
-~UndoStack(){
- clear();
-}
-bool empty() const {
- return m_stack.empty();
-}
-std::size_t size() const {
- return m_stack.size();
-}
-Operation* back(){
- return m_stack.back();
-}
-const Operation* back() const {
- return m_stack.back();
-}
-Operation* front(){
- return m_stack.front();
-}
-const Operation* front() const {
- return m_stack.front();
-}
-void pop_front(){
- delete m_stack.front();
- m_stack.pop_front();
-}
-void pop_back(){
- delete m_stack.back();
- m_stack.pop_back();
-}
-void clear(){
- if ( !m_stack.empty() ) {
- for ( Operations::iterator i = m_stack.begin(); i != m_stack.end(); ++i )
- {
- delete *i;
- }
- m_stack.clear();
- }
-}
-void start( const char* command ){
- if ( m_pending != 0 ) {
- delete m_pending;
- }
- m_pending = new Operation( command );
-}
-bool finish( const char* command ){
- if ( m_pending != 0 ) {
- delete m_pending;
- m_pending = 0;
- return false;
- }
- else
- {
- ASSERT_MESSAGE( !m_stack.empty(), "undo stack empty" );
- m_stack.back()->m_command = command;
- return true;
- }
-}
-void save( Undoable* undoable ){
- if ( m_pending != 0 ) {
- m_stack.push_back( m_pending );
- m_pending = 0;
- }
- back()->m_snapshot.save( undoable );
-}
+ 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();
+ }
+ }