2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "debugging/debugging.h"
28 #include "preferencesystem.h"
29 #include "string/string.h"
30 #include "generic/callback.h"
31 #include "preferences.h"
43 const char* m_operation;
45 DebugScopeTimer(const char* operation)
46 : m_operation(operation)
52 unsigned int elapsed = m_timer.elapsed_msec();
55 globalOutputStream() << m_operation << ": " << elapsed << " msec\n";
61 class RadiantUndoSystem : public UndoSystem
63 INTEGER_CONSTANT(MAX_UNDO_LEVELS, 1024);
75 StateApplicator(Undoable* undoable, UndoMemento* data)
76 : m_undoable(undoable), m_data(data)
81 m_undoable->importState(m_data);
89 typedef std::list<StateApplicator> states_t;
95 return m_states.empty();
97 std::size_t size() const
99 return m_states.size();
101 void save(Undoable* undoable)
103 m_states.push_front(StateApplicator(undoable, undoable->exportState()));
107 for(states_t::iterator i = m_states.begin(); i != m_states.end(); ++i)
114 for(states_t::iterator i = m_states.begin(); i != m_states.end(); ++i)
124 CopiedString m_command;
126 Operation(const char* command)
132 m_snapshot.release();
139 //! Note: using std::list instead of vector/deque, to avoid copying of undos
140 typedef std::list<Operation*> Operations;
143 Operation* m_pending;
146 UndoStack() : m_pending(0)
155 return m_stack.empty();
157 std::size_t size() const
159 return m_stack.size();
163 return m_stack.back();
165 const Operation* back() const
167 return m_stack.back();
171 return m_stack.front();
173 const Operation* front() const
175 return m_stack.front();
179 delete m_stack.front();
184 delete m_stack.back();
191 for(Operations::iterator i = m_stack.begin(); i != m_stack.end(); ++i)
198 void start(const char* command)
204 m_pending = new Operation(command);
206 bool finish(const char* command)
216 ASSERT_MESSAGE(!m_stack.empty(), "undo stack empty");
217 m_stack.back()->m_command = command;
221 void save(Undoable* undoable)
225 m_stack.push_back(m_pending);
228 back()->m_snapshot.save(undoable);
232 UndoStack m_undo_stack;
233 UndoStack m_redo_stack;
235 class UndoStackFiller : public UndoObserver
244 void save(Undoable* undoable)
246 ASSERT_NOTNULL(undoable);
250 m_stack->save(undoable);
254 void setStack(UndoStack* stack)
260 typedef std::map<Undoable*, UndoStackFiller> undoables_t;
261 undoables_t m_undoables;
263 void mark_undoables(UndoStack* stack)
265 for(undoables_t::iterator i = m_undoables.begin(); i != m_undoables.end(); ++i)
267 (*i).second.setStack(stack);
271 std::size_t m_undo_levels;
273 typedef std::set<UndoTracker*> Trackers;
284 UndoObserver* observer(Undoable* undoable)
286 ASSERT_NOTNULL(undoable);
288 return &m_undoables[undoable];
290 void release(Undoable* undoable)
292 ASSERT_NOTNULL(undoable);
294 m_undoables.erase(undoable);
296 void setLevels(std::size_t levels)
298 if(levels > MAX_UNDO_LEVELS())
300 levels = MAX_UNDO_LEVELS();
303 while(m_undo_stack.size() > levels)
305 m_undo_stack.pop_front();
307 m_undo_levels = levels;
309 std::size_t getLevels() const
311 return m_undo_levels;
313 std::size_t size() const
315 return m_undo_stack.size();
319 m_undo_stack.start("unnamedCommand");
320 mark_undoables(&m_undo_stack);
322 bool finishUndo(const char* command)
324 bool changed = m_undo_stack.finish(command);
330 m_redo_stack.start("unnamedCommand");
331 mark_undoables(&m_redo_stack);
333 bool finishRedo(const char* command)
335 bool changed = m_redo_stack.finish(command);
341 m_redo_stack.clear();
342 if(m_undo_stack.size() == m_undo_levels)
344 m_undo_stack.pop_front();
349 void finish(const char* command)
351 if(finishUndo(command))
353 globalOutputStream() << command << '\n';
358 if(m_undo_stack.empty())
360 globalOutputStream() << "Undo: no undo available\n";
364 Operation* operation = m_undo_stack.back();
365 globalOutputStream() << "Undo: " << operation->m_command.c_str() << "\n";
369 operation->m_snapshot.restore();
370 finishRedo(operation->m_command.c_str());
371 m_undo_stack.pop_back();
376 if(m_redo_stack.empty())
378 globalOutputStream() << "Redo: no redo available\n";
382 Operation* operation = m_redo_stack.back();
383 globalOutputStream() << "Redo: " << operation->m_command.c_str() << "\n";
387 operation->m_snapshot.restore();
388 finishUndo(operation->m_command.c_str());
389 m_redo_stack.pop_back();
395 m_undo_stack.clear();
396 m_redo_stack.clear();
399 void trackerAttach(UndoTracker& tracker)
401 ASSERT_MESSAGE(m_trackers.find(&tracker) == m_trackers.end(), "undo tracker already attached");
402 m_trackers.insert(&tracker);
404 void trackerDetach(UndoTracker& tracker)
406 ASSERT_MESSAGE(m_trackers.find(&tracker) != m_trackers.end(), "undo tracker cannot be detached");
407 m_trackers.erase(&tracker);
409 void trackersClear() const
411 for(Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i)
416 void trackersBegin() const
418 for(Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i)
423 void trackersUndo() const
425 for(Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i)
430 void trackersRedo() const
432 for(Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i)
441 void UndoLevels_importString(RadiantUndoSystem& undo, const char* value)
444 Int_importString(levels, value);
445 undo.setLevels(levels);
447 typedef ReferenceCaller1<RadiantUndoSystem, const char*, UndoLevels_importString> UndoLevelsImportStringCaller;
448 void UndoLevels_exportString(const RadiantUndoSystem& undo, const StringImportCallback& importer)
450 Int_exportString(static_cast<int>(undo.getLevels()), importer);
452 typedef ConstReferenceCaller1<RadiantUndoSystem, const StringImportCallback&, UndoLevels_exportString> UndoLevelsExportStringCaller;
454 #include "generic/callback.h"
456 void UndoLevelsImport(RadiantUndoSystem& self, int value)
458 self.setLevels(value);
460 typedef ReferenceCaller1<RadiantUndoSystem, int, UndoLevelsImport> UndoLevelsImportCaller;
461 void UndoLevelsExport(const RadiantUndoSystem& self, const IntImportCallback& importCallback)
463 importCallback(static_cast<int>(self.getLevels()));
465 typedef ConstReferenceCaller1<RadiantUndoSystem, const IntImportCallback&, UndoLevelsExport> UndoLevelsExportCaller;
468 void Undo_constructPreferences(RadiantUndoSystem& undo, PreferencesPage& page)
470 page.appendSpinner("Undo Queue Size", 64, 0, 1024, IntImportCallback(UndoLevelsImportCaller(undo)), IntExportCallback(UndoLevelsExportCaller(undo)));
472 void Undo_constructPage(RadiantUndoSystem& undo, PreferenceGroup& group)
474 PreferencesPage page(group.createPage("Undo", "Undo Queue Settings"));
475 Undo_constructPreferences(undo, page);
477 void Undo_registerPreferencesPage(RadiantUndoSystem& undo)
479 PreferencesDialog_addSettingsPage(ReferenceCaller1<RadiantUndoSystem, PreferenceGroup&, Undo_constructPage>(undo));
482 class UndoSystemDependencies : public GlobalPreferenceSystemModuleRef
488 RadiantUndoSystem m_undosystem;
490 typedef UndoSystem Type;
491 STRING_CONSTANT(Name, "*");
495 GlobalPreferenceSystem().registerPreference("UndoLevels", makeIntStringImportCallback(UndoLevelsImportCaller(m_undosystem)), makeIntStringExportCallback(UndoLevelsExportCaller(m_undosystem)));
497 Undo_registerPreferencesPage(m_undosystem);
499 UndoSystem* getTable()
501 return &m_undosystem;
505 #include "modulesystem/singletonmodule.h"
506 #include "modulesystem/moduleregistry.h"
508 typedef SingletonModule<UndoSystemAPI, UndoSystemDependencies> UndoSystemModule;
509 typedef Static<UndoSystemModule> StaticUndoSystemModule;
510 StaticRegisterModule staticRegisterUndoSystem(StaticUndoSystemModule::instance());
521 class undoable_test : public Undoable
523 struct state_type : public UndoMemento
525 state_type() : test_data(0)
528 state_type(const state_type& other) : UndoMemento(other), test_data(other.test_data)
539 UndoObserver* m_observer;
542 : m_observer(GlobalUndoSystem().observer(this))
547 GlobalUndoSystem().release(this);
549 UndoMemento* exportState() const
551 return new state_type(m_state);
553 void importState(const UndoMemento* state)
555 ASSERT_NOTNULL(state);
557 m_observer->save(this);
558 m_state = *(static_cast<const state_type*>(state));
561 void mutate(unsigned int data)
563 m_observer->save(this);
564 m_state.test_data = data;
576 GlobalUndoSystem().begin("bleh");
578 GlobalUndoSystem().begin("blah");
580 GlobalUndoSystem().undo();
581 GlobalUndoSystem().undo();
582 GlobalUndoSystem().redo();
583 GlobalUndoSystem().redo();