X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=radiant%2Fundo.cpp;h=fb741abdd9db93059102f34a1ced0a595a78d531;hb=32dc1da26645b97a5823ff22175000542225c6dd;hp=aa75d950e27e61f415963dbfa624c0b5bbeef581;hpb=107765f0e4b543dfc346851ee5b4605cc17eb1c6;p=xonotic%2Fnetradiant.git diff --git a/radiant/undo.cpp b/radiant/undo.cpp index aa75d950..fb741abd 100644 --- a/radiant/undo.cpp +++ b/radiant/undo.cpp @@ -1,23 +1,23 @@ /* -Copyright (C) 2001-2006, William Joseph. -All Rights Reserved. + Copyright (C) 2001-2006, William Joseph. + All Rights Reserved. -This file is part of GtkRadiant. + This file is part of GtkRadiant. -GtkRadiant is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + GtkRadiant is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. -GtkRadiant is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + GtkRadiant is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -You should have received a copy of the GNU General Public License -along with GtkRadiant; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + You should have received a copy of the GNU General Public License + along with GtkRadiant; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ #include "undo.h" @@ -39,444 +39,374 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA class DebugScopeTimer { - Timer m_timer; - const char* m_operation; +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 { - INTEGER_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(); - } - } - }; +UINT_CONSTANT( MAX_UNDO_LEVELS, 1024 ); - struct Operation - { - Snapshot m_snapshot; - CopiedString m_command; +class Snapshot +{ +class StateApplicator +{ +public: +Undoable* m_undoable; +private: +UndoMemento* m_data; +public: - Operation(const char* command) - : m_command(command) - { - } - ~Operation() - { - m_snapshot.release(); - } - }; +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; - class UndoStack - { - //! Note: using std::list instead of vector/deque, to avoid copying of undos - typedef std::list Operations; +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(); + } +} +}; - Operations m_stack; - Operation* m_pending; +struct Operation +{ + Snapshot m_snapshot; + CopiedString m_command; + + Operation( const char* command ) + : m_command( command ){ + } + ~Operation(){ + m_snapshot.release(); + } +}; - 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 UndoStack +{ +//! Note: using std::list instead of vector/deque, to avoid copying of undos +typedef std::list Operations; - class UndoStackFiller : public UndoObserver - { - UndoStack* m_stack; - public: +Operations m_stack; +Operation* m_pending; - 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; - } - }; +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 ); +} +}; - typedef std::map undoables_t; - undoables_t m_undoables; +UndoStack m_undo_stack; +UndoStack m_redo_stack; - void mark_undoables(UndoStack* stack) - { - for(undoables_t::iterator i = m_undoables.begin(); i != m_undoables.end(); ++i) - { - (*i).second.setStack(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; +} +}; - std::size_t m_undo_levels; +typedef std::map undoables_t; +undoables_t m_undoables; - typedef std::set Trackers; - Trackers m_trackers; +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 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(); - } +RadiantUndoSystem() + : m_undo_levels( 64 ){ +} +~RadiantUndoSystem(){ + clear(); +} +UndoObserver* observer( Undoable* undoable ){ + ASSERT_NOTNULL( undoable ); - 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(); - } - } + 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); +void UndoLevels_importString( RadiantUndoSystem& undo, const char* value ){ + int levels; + PropertyImpl::Import( levels, value ); + undo.setLevels( levels ); } -typedef ReferenceCaller1 UndoLevelsImportStringCaller; -void UndoLevels_exportString(const RadiantUndoSystem& undo, const StringImportCallback& importer) -{ - Int_exportString(static_cast(undo.getLevels()), importer); +typedef ReferenceCaller UndoLevelsImportStringCaller; +void UndoLevels_exportString( const RadiantUndoSystem& undo, const Callback & importer ){ + PropertyImpl::Export( static_cast( undo.getLevels() ), importer ); } -typedef ConstReferenceCaller1 UndoLevelsExportStringCaller; +typedef ConstReferenceCaller &), UndoLevels_exportString> UndoLevelsExportStringCaller; #include "generic/callback.h" -void UndoLevelsImport(RadiantUndoSystem& self, int value) -{ - self.setLevels(value); -} -typedef ReferenceCaller1 UndoLevelsImportCaller; -void UndoLevelsExport(const RadiantUndoSystem& self, const IntImportCallback& importCallback) -{ - importCallback(static_cast(self.getLevels())); -} -typedef ConstReferenceCaller1 UndoLevelsExportCaller; +struct UndoLevels { + static void Export(const RadiantUndoSystem &self, const Callback &returnz) { + returnz(static_cast(self.getLevels())); + } + static void Import(RadiantUndoSystem &self, int value) { + self.setLevels(value); + } +}; -void Undo_constructPreferences(RadiantUndoSystem& undo, PreferencesPage& page) -{ - page.appendSpinner("Undo Queue Size", 64, 0, 1024, IntImportCallback(UndoLevelsImportCaller(undo)), IntExportCallback(UndoLevelsExportCaller(undo))); +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(ReferenceCaller1(undo)); +void Undo_registerPreferencesPage( RadiantUndoSystem& undo ){ + PreferencesDialog_addSettingsPage( ReferenceCaller( undo ) ); } class UndoSystemDependencies : public GlobalPreferenceSystemModuleRef @@ -485,21 +415,19 @@ class UndoSystemDependencies : public GlobalPreferenceSystemModuleRef class UndoSystemAPI { - RadiantUndoSystem m_undosystem; +RadiantUndoSystem m_undosystem; public: - typedef UndoSystem Type; - STRING_CONSTANT(Name, "*"); - - UndoSystemAPI() - { - GlobalPreferenceSystem().registerPreference("UndoLevels", makeIntStringImportCallback(UndoLevelsImportCaller(m_undosystem)), makeIntStringExportCallback(UndoLevelsExportCaller(m_undosystem))); - - Undo_registerPreferencesPage(m_undosystem); - } - UndoSystem* getTable() - { - return &m_undosystem; - } +typedef UndoSystem Type; +STRING_CONSTANT( Name, "*" ); + +UndoSystemAPI(){ + GlobalPreferenceSystem().registerPreference("UndoLevels", make_property_string(m_undosystem)); + + Undo_registerPreferencesPage( m_undosystem ); +} +UndoSystem* getTable(){ + return &m_undosystem; +} }; #include "modulesystem/singletonmodule.h" @@ -507,7 +435,7 @@ public: typedef SingletonModule UndoSystemModule; typedef Static StaticUndoSystemModule; -StaticRegisterModule staticRegisterUndoSystem(StaticUndoSystemModule::instance()); +StaticRegisterModule staticRegisterUndoSystem( StaticUndoSystemModule::instance() ); @@ -520,49 +448,41 @@ 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; +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)); - } - - void mutate(unsigned int data) - { - m_observer->save(this); - m_state.test_data = data; - } +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 ) ); +} + +void mutate( unsigned int data ){ + m_observer->save( this ); + m_state.test_data = data; +} }; #if 0 @@ -570,21 +490,19 @@ public: 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(); - } +TestUndo(){ + undoable_test test; + GlobalUndoSystem().begin( "bleh" ); + test.mutate( 3 ); + GlobalUndoSystem().begin( "blah" ); + test.mutate( 4 ); + GlobalUndoSystem().undo(); + GlobalUndoSystem().undo(); + GlobalUndoSystem().redo(); + GlobalUndoSystem().redo(); +} }; TestUndo g_TestUndo; #endif -