X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=radiant%2Fundo.cpp;h=fb741abdd9db93059102f34a1ced0a595a78d531;hb=32dc1da26645b97a5823ff22175000542225c6dd;hp=dd92e8ab3c3e829c3b87a8af5bbf4cc193bcff40;hpb=02a51890a3d97a0e937fbb11071cf7c41cc00aa9;p=xonotic%2Fnetradiant.git diff --git a/radiant/undo.cpp b/radiant/undo.cpp index dd92e8ab..fb741abd 100644 --- a/radiant/undo.cpp +++ b/radiant/undo.cpp @@ -37,486 +37,397 @@ #include "timer.h" -class DebugScopeTimer { - Timer m_timer; - const char *m_operation; +class DebugScopeTimer +{ +Timer m_timer; +const char* m_operation; public: - DebugScopeTimer(const char *operation) - : m_operation(operation) - { - m_timer.start(); - } - - ~DebugScopeTimer() - { - unsigned int elapsed = m_timer.elapsed_msec(); - if (elapsed > 0) { - globalOutputStream() << m_operation << ": " << elapsed << " msec\n"; - } - } +DebugScopeTimer( const char* operation ) + : m_operation( operation ){ + m_timer.start(); +} +~DebugScopeTimer(){ + unsigned int elapsed = m_timer.elapsed_msec(); + if ( elapsed > 0 ) { + globalOutputStream() << m_operation << ": " << elapsed << " msec\n"; + } +} }; -class RadiantUndoSystem : public UndoSystem { - UINT_CONSTANT(MAX_UNDO_LEVELS, 1024); - - class Snapshot { - class StateApplicator { - public: - Undoable *m_undoable; - private: - UndoMemento *m_data; - public: - - StateApplicator(Undoable *undoable, UndoMemento *data) - : m_undoable(undoable), m_data(data) - { - } - - void restore() - { - m_undoable->importState(m_data); - } - - void release() - { - m_data->release(); - } - }; - - typedef std::list states_t; - states_t m_states; - - public: - bool empty() const - { - return m_states.empty(); - } - - std::size_t size() const - { - return m_states.size(); - } - - void save(Undoable *undoable) - { - m_states.push_front(StateApplicator(undoable, undoable->exportState())); - } - - void restore() - { - for (states_t::iterator i = m_states.begin(); i != m_states.end(); ++i) { - (*i).restore(); - } - } - - void release() - { - for (states_t::iterator i = m_states.begin(); i != m_states.end(); ++i) { - (*i).release(); - } - } - }; - - struct Operation { - Snapshot m_snapshot; - CopiedString m_command; - - Operation(const char *command) - : m_command(command) - { - } - - ~Operation() - { - m_snapshot.release(); - } - }; - - - class UndoStack { -//! Note: using std::list instead of vector/deque, to avoid copying of undos - typedef std::list Operations; - - Operations m_stack; - Operation *m_pending; - - public: - 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); - } - }; - - UndoStack m_undo_stack; - UndoStack m_redo_stack; - - class UndoStackFiller : public UndoObserver { - 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 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; +class RadiantUndoSystem : public UndoSystem +{ +UINT_CONSTANT( MAX_UNDO_LEVELS, 1024 ); - typedef std::set Trackers; - Trackers m_trackers; +class Snapshot +{ +class StateApplicator +{ +public: +Undoable* m_undoable; +private: +UndoMemento* m_data; 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; - } +StateApplicator( Undoable* undoable, UndoMemento* data ) + : m_undoable( undoable ), m_data( data ){ +} +void restore(){ + m_undoable->importState( m_data ); +} +void release(){ + m_data->release(); +} +}; - std::size_t getLevels() const - { - return m_undo_levels; - } +typedef std::list states_t; +states_t m_states; - std::size_t size() const - { - return m_undo_stack.size(); - } +public: +bool empty() const { + return m_states.empty(); +} +std::size_t size() const { + return m_states.size(); +} +void save( Undoable* undoable ){ + m_states.push_front( StateApplicator( undoable, undoable->exportState() ) ); +} +void restore(){ + for ( states_t::iterator i = m_states.begin(); i != m_states.end(); ++i ) + { + ( *i ).restore(); + } +} +void release(){ + for ( states_t::iterator i = m_states.begin(); i != m_states.end(); ++i ) + { + ( *i ).release(); + } +} +}; - void startUndo() - { - m_undo_stack.start("unnamedCommand"); - mark_undoables(&m_undo_stack); - } +struct Operation +{ + Snapshot m_snapshot; + CopiedString m_command; + + Operation( const char* command ) + : m_command( command ){ + } + ~Operation(){ + m_snapshot.release(); + } +}; - 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); - } +class UndoStack +{ +//! Note: using std::list instead of vector/deque, to avoid copying of undos +typedef std::list Operations; - bool finishRedo(const char *command) - { - bool changed = m_redo_stack.finish(command); - mark_undoables(0); - return changed; - } +Operations m_stack; +Operation* m_pending; - void start() - { - m_redo_stack.clear(); - if (m_undo_stack.size() == m_undo_levels) { - m_undo_stack.pop_front(); - } - startUndo(); - trackersBegin(); - } +public: +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 ); +} +}; - void finish(const char *command) - { - if (finishUndo(command)) { - globalOutputStream() << command << '\n'; - } - } +UndoStack m_undo_stack; +UndoStack m_redo_stack; - 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(); - } - } +class UndoStackFiller : public UndoObserver +{ +UndoStack* m_stack; +public: - 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(); - } - } +UndoStackFiller() + : m_stack( 0 ){ +} +void save( Undoable* undoable ){ + ASSERT_NOTNULL( undoable ); - void clear() - { - mark_undoables(0); - m_undo_stack.clear(); - m_redo_stack.clear(); - trackersClear(); - } + if ( m_stack != 0 ) { + m_stack->save( undoable ); + m_stack = 0; + } +} +void setStack( UndoStack* stack ){ + m_stack = stack; +} +}; - void trackerAttach(UndoTracker &tracker) - { - ASSERT_MESSAGE(m_trackers.find(&tracker) == m_trackers.end(), "undo tracker already attached"); - m_trackers.insert(&tracker); - } +typedef std::map undoables_t; +undoables_t m_undoables; - void trackerDetach(UndoTracker &tracker) - { - ASSERT_MESSAGE(m_trackers.find(&tracker) != m_trackers.end(), "undo tracker cannot be detached"); - m_trackers.erase(&tracker); - } +void mark_undoables( UndoStack* stack ){ + for ( undoables_t::iterator i = m_undoables.begin(); i != m_undoables.end(); ++i ) + { + ( *i ).second.setStack( stack ); + } +} - void trackersClear() const - { - for (Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i) { - (*i)->clear(); - } - } +std::size_t m_undo_levels; - void trackersBegin() const - { - for (Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i) { - (*i)->begin(); - } - } +typedef std::set Trackers; +Trackers m_trackers; +public: +RadiantUndoSystem() + : m_undo_levels( 64 ){ +} +~RadiantUndoSystem(){ + clear(); +} +UndoObserver* observer( Undoable* undoable ){ + ASSERT_NOTNULL( undoable ); - void trackersUndo() const - { - for (Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i) { - (*i)->undo(); - } - } + return &m_undoables[undoable]; +} +void release( Undoable* undoable ){ + ASSERT_NOTNULL( undoable ); - void trackersRedo() const - { - for (Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i) { - (*i)->redo(); - } - } + 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; - PropertyImpl::Import(levels, value); - undo.setLevels(levels); -} - -typedef ReferenceCaller UndoLevelsImportStringCaller; -void UndoLevels_exportString(const RadiantUndoSystem &undo, const Callback &importer) -{ - PropertyImpl::Export(static_cast( undo.getLevels()), importer); +void UndoLevels_importString( RadiantUndoSystem& undo, const char* value ){ + int levels; + PropertyImpl::Import( levels, value ); + undo.setLevels( levels ); } - -typedef ConstReferenceCaller &), UndoLevels_exportString> UndoLevelsExportStringCaller; +typedef ReferenceCaller UndoLevelsImportStringCaller; +void UndoLevels_exportString( const RadiantUndoSystem& undo, const Callback & importer ){ + PropertyImpl::Export( static_cast( undo.getLevels() ), importer ); +} +typedef ConstReferenceCaller &), UndoLevels_exportString> UndoLevelsExportStringCaller; #include "generic/callback.h" struct UndoLevels { - static void Export(const RadiantUndoSystem &self, const Callback &returnz) - { + static void Export(const RadiantUndoSystem &self, const Callback &returnz) { returnz(static_cast(self.getLevels())); } - static void Import(RadiantUndoSystem &self, int value) - { + static void Import(RadiantUndoSystem &self, int value) { self.setLevels(value); } }; -void Undo_constructPreferences(RadiantUndoSystem &undo, PreferencesPage &page) -{ +void Undo_constructPreferences( RadiantUndoSystem& undo, PreferencesPage& page ){ page.appendSpinner("Undo Queue Size", 64, 0, 1024, make_property(undo)); } - -void Undo_constructPage(RadiantUndoSystem &undo, PreferenceGroup &group) -{ - PreferencesPage page(group.createPage("Undo", "Undo Queue Settings")); - Undo_constructPreferences(undo, page); +void Undo_constructPage( RadiantUndoSystem& undo, PreferenceGroup& group ){ + PreferencesPage page( group.createPage( "Undo", "Undo Queue Settings" ) ); + Undo_constructPreferences( undo, page ); } - -void Undo_registerPreferencesPage(RadiantUndoSystem &undo) -{ - PreferencesDialog_addSettingsPage( - ReferenceCaller(undo)); +void Undo_registerPreferencesPage( RadiantUndoSystem& undo ){ + PreferencesDialog_addSettingsPage( ReferenceCaller( undo ) ); } -class UndoSystemDependencies : public GlobalPreferenceSystemModuleRef { +class UndoSystemDependencies : public GlobalPreferenceSystemModuleRef +{ }; -class UndoSystemAPI { - RadiantUndoSystem m_undosystem; +class UndoSystemAPI +{ +RadiantUndoSystem m_undosystem; public: - typedef UndoSystem Type; - - STRING_CONSTANT(Name, "*"); +typedef UndoSystem Type; +STRING_CONSTANT( Name, "*" ); - UndoSystemAPI() - { - GlobalPreferenceSystem().registerPreference("UndoLevels", make_property_string(m_undosystem)); +UndoSystemAPI(){ + GlobalPreferenceSystem().registerPreference("UndoLevels", make_property_string(m_undosystem)); - Undo_registerPreferencesPage(m_undosystem); - } - - UndoSystem *getTable() - { - return &m_undosystem; - } + Undo_registerPreferencesPage( m_undosystem ); +} +UndoSystem* getTable(){ + return &m_undosystem; +} }; #include "modulesystem/singletonmodule.h" @@ -524,58 +435,54 @@ public: typedef SingletonModule UndoSystemModule; typedef Static StaticUndoSystemModule; -StaticRegisterModule staticRegisterUndoSystem(StaticUndoSystemModule::instance()); +StaticRegisterModule staticRegisterUndoSystem( StaticUndoSystemModule::instance() ); -class undoable_test : public Undoable { - struct state_type : public UndoMemento { - state_type() : test_data(0) - { - } - state_type(const state_type &other) : UndoMemento(other), test_data(other.test_data) - { - } - void release() - { - delete this; - } - int test_data; - }; - state_type m_state; - UndoObserver *m_observer; -public: - undoable_test() - : m_observer(GlobalUndoSystem().observer(this)) - { - } - ~undoable_test() - { - GlobalUndoSystem().release(this); - } - UndoMemento *exportState() const - { - return new state_type(m_state); - } - void importState(const UndoMemento *state) - { - ASSERT_NOTNULL(state); - m_observer->save(this); - m_state = *(static_cast( state )); - } +class undoable_test : public Undoable +{ +struct state_type : public UndoMemento +{ + state_type() : test_data( 0 ){ + } + state_type( const state_type& other ) : UndoMemento( other ), test_data( other.test_data ){ + } + void release(){ + delete this; + } + + int test_data; +}; +state_type m_state; +UndoObserver* m_observer; +public: +undoable_test() + : m_observer( GlobalUndoSystem().observer( this ) ){ +} +~undoable_test(){ + GlobalUndoSystem().release( this ); +} +UndoMemento* exportState() const { + return new state_type( m_state ); +} +void importState( const UndoMemento* state ){ + ASSERT_NOTNULL( state ); - void mutate(unsigned int data) - { - m_observer->save(this); - m_state.test_data = data; - } + m_observer->save( this ); + m_state = *( static_cast( state ) ); +} + +void mutate( unsigned int data ){ + m_observer->save( this ); + m_state.test_data = data; +} }; #if 0 @@ -584,15 +491,15 @@ class TestUndo { public: TestUndo(){ - undoable_test test; - GlobalUndoSystem().begin( "bleh" ); - test.mutate( 3 ); - GlobalUndoSystem().begin( "blah" ); - test.mutate( 4 ); - GlobalUndoSystem().undo(); - GlobalUndoSystem().undo(); - GlobalUndoSystem().redo(); - GlobalUndoSystem().redo(); + undoable_test test; + GlobalUndoSystem().begin( "bleh" ); + test.mutate( 3 ); + GlobalUndoSystem().begin( "blah" ); + test.mutate( 4 ); + GlobalUndoSystem().undo(); + GlobalUndoSystem().undo(); + GlobalUndoSystem().redo(); + GlobalUndoSystem().redo(); } };