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"
40 class DebugScopeTimer {
42 const char *m_operation;
44 DebugScopeTimer(const char *operation)
45 : m_operation(operation)
52 unsigned int elapsed = m_timer.elapsed_msec();
54 globalOutputStream() << m_operation << ": " << elapsed << " msec\n";
60 class RadiantUndoSystem : public UndoSystem {
61 UINT_CONSTANT(MAX_UNDO_LEVELS, 1024);
64 class StateApplicator {
71 StateApplicator(Undoable *undoable, UndoMemento *data)
72 : m_undoable(undoable), m_data(data)
78 m_undoable->importState(m_data);
87 typedef std::list<StateApplicator> states_t;
93 return m_states.empty();
96 std::size_t size() const
98 return m_states.size();
101 void save(Undoable *undoable)
103 m_states.push_front(StateApplicator(undoable, undoable->exportState()));
108 for (states_t::iterator i = m_states.begin(); i != m_states.end(); ++i) {
115 for (states_t::iterator i = m_states.begin(); i != m_states.end(); ++i) {
123 CopiedString m_command;
125 Operation(const char *command)
132 m_snapshot.release();
138 //! Note: using std::list instead of vector/deque, to avoid copying of undos
139 typedef std::list<Operation *> Operations;
142 Operation *m_pending;
145 UndoStack() : m_pending(0)
156 return m_stack.empty();
159 std::size_t size() const
161 return m_stack.size();
166 return m_stack.back();
169 const Operation *back() const
171 return m_stack.back();
176 return m_stack.front();
179 const Operation *front() const
181 return m_stack.front();
186 delete m_stack.front();
192 delete m_stack.back();
198 if (!m_stack.empty()) {
199 for (Operations::iterator i = m_stack.begin(); i != m_stack.end(); ++i) {
206 void start(const char *command)
208 if (m_pending != 0) {
211 m_pending = new Operation(command);
214 bool finish(const char *command)
216 if (m_pending != 0) {
221 ASSERT_MESSAGE(!m_stack.empty(), "undo stack empty");
222 m_stack.back()->m_command = command;
227 void save(Undoable *undoable)
229 if (m_pending != 0) {
230 m_stack.push_back(m_pending);
233 back()->m_snapshot.save(undoable);
237 UndoStack m_undo_stack;
238 UndoStack m_redo_stack;
240 class UndoStackFiller : public UndoObserver {
249 void save(Undoable *undoable)
251 ASSERT_NOTNULL(undoable);
254 m_stack->save(undoable);
259 void setStack(UndoStack *stack)
265 typedef std::map<Undoable *, UndoStackFiller> undoables_t;
266 undoables_t m_undoables;
268 void mark_undoables(UndoStack *stack)
270 for (undoables_t::iterator i = m_undoables.begin(); i != m_undoables.end(); ++i) {
271 (*i).second.setStack(stack);
275 std::size_t m_undo_levels;
277 typedef std::set<UndoTracker *> Trackers;
290 UndoObserver *observer(Undoable *undoable)
292 ASSERT_NOTNULL(undoable);
294 return &m_undoables[undoable];
297 void release(Undoable *undoable)
299 ASSERT_NOTNULL(undoable);
301 m_undoables.erase(undoable);
304 void setLevels(std::size_t levels)
306 if (levels > MAX_UNDO_LEVELS()) {
307 levels = MAX_UNDO_LEVELS();
310 while (m_undo_stack.size() > levels) {
311 m_undo_stack.pop_front();
313 m_undo_levels = levels;
316 std::size_t getLevels() const
318 return m_undo_levels;
321 std::size_t size() const
323 return m_undo_stack.size();
328 m_undo_stack.start("unnamedCommand");
329 mark_undoables(&m_undo_stack);
332 bool finishUndo(const char *command)
334 bool changed = m_undo_stack.finish(command);
341 m_redo_stack.start("unnamedCommand");
342 mark_undoables(&m_redo_stack);
345 bool finishRedo(const char *command)
347 bool changed = m_redo_stack.finish(command);
354 m_redo_stack.clear();
355 if (m_undo_stack.size() == m_undo_levels) {
356 m_undo_stack.pop_front();
362 void finish(const char *command)
364 if (finishUndo(command)) {
365 globalOutputStream() << command << '\n';
371 if (m_undo_stack.empty()) {
372 globalOutputStream() << "Undo: no undo available\n";
374 Operation *operation = m_undo_stack.back();
375 globalOutputStream() << "Undo: " << operation->m_command.c_str() << "\n";
379 operation->m_snapshot.restore();
380 finishRedo(operation->m_command.c_str());
381 m_undo_stack.pop_back();
387 if (m_redo_stack.empty()) {
388 globalOutputStream() << "Redo: no redo available\n";
390 Operation *operation = m_redo_stack.back();
391 globalOutputStream() << "Redo: " << operation->m_command.c_str() << "\n";
395 operation->m_snapshot.restore();
396 finishUndo(operation->m_command.c_str());
397 m_redo_stack.pop_back();
404 m_undo_stack.clear();
405 m_redo_stack.clear();
409 void trackerAttach(UndoTracker &tracker)
411 ASSERT_MESSAGE(m_trackers.find(&tracker) == m_trackers.end(), "undo tracker already attached");
412 m_trackers.insert(&tracker);
415 void trackerDetach(UndoTracker &tracker)
417 ASSERT_MESSAGE(m_trackers.find(&tracker) != m_trackers.end(), "undo tracker cannot be detached");
418 m_trackers.erase(&tracker);
421 void trackersClear() const
423 for (Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i) {
428 void trackersBegin() const
430 for (Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i) {
435 void trackersUndo() const
437 for (Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i) {
442 void trackersRedo() const
444 for (Trackers::const_iterator i = m_trackers.begin(); i != m_trackers.end(); ++i) {
451 void UndoLevels_importString(RadiantUndoSystem &undo, const char *value)
454 PropertyImpl<int, const char *>::Import(levels, value);
455 undo.setLevels(levels);
458 typedef ReferenceCaller<RadiantUndoSystem, void(const char *), UndoLevels_importString> UndoLevelsImportStringCaller;
460 void UndoLevels_exportString(const RadiantUndoSystem &undo, const Callback<void(const char *)> &importer)
462 PropertyImpl<int, const char *>::Export(static_cast<int>( undo.getLevels()), importer);
465 typedef ConstReferenceCaller<RadiantUndoSystem, void(
466 const Callback<void(const char *)> &), UndoLevels_exportString> UndoLevelsExportStringCaller;
468 #include "generic/callback.h"
471 static void Export(const RadiantUndoSystem &self, const Callback<void(int)> &returnz)
473 returnz(static_cast<int>(self.getLevels()));
476 static void Import(RadiantUndoSystem &self, int value)
478 self.setLevels(value);
482 void Undo_constructPreferences(RadiantUndoSystem &undo, PreferencesPage &page)
484 page.appendSpinner("Undo Queue Size", 64, 0, 1024, make_property<UndoLevels>(undo));
487 void Undo_constructPage(RadiantUndoSystem &undo, PreferenceGroup &group)
489 PreferencesPage page(group.createPage("Undo", "Undo Queue Settings"));
490 Undo_constructPreferences(undo, page);
493 void Undo_registerPreferencesPage(RadiantUndoSystem &undo)
495 PreferencesDialog_addSettingsPage(
496 ReferenceCaller<RadiantUndoSystem, void(PreferenceGroup &), Undo_constructPage>(undo));
499 class UndoSystemDependencies : public GlobalPreferenceSystemModuleRef {
502 class UndoSystemAPI {
503 RadiantUndoSystem m_undosystem;
505 typedef UndoSystem Type;
507 STRING_CONSTANT(Name, "*");
511 GlobalPreferenceSystem().registerPreference("UndoLevels", make_property_string<UndoLevels>(m_undosystem));
513 Undo_registerPreferencesPage(m_undosystem);
516 UndoSystem *getTable()
518 return &m_undosystem;
522 #include "modulesystem/singletonmodule.h"
523 #include "modulesystem/moduleregistry.h"
525 typedef SingletonModule<UndoSystemAPI, UndoSystemDependencies> UndoSystemModule;
526 typedef Static<UndoSystemModule> StaticUndoSystemModule;
527 StaticRegisterModule staticRegisterUndoSystem(StaticUndoSystemModule::instance());
530 class undoable_test : public Undoable {
531 struct state_type : public UndoMemento {
532 state_type() : test_data(0)
536 state_type(const state_type &other) : UndoMemento(other), test_data(other.test_data)
549 UndoObserver *m_observer;
552 : m_observer(GlobalUndoSystem().observer(this))
558 GlobalUndoSystem().release(this);
561 UndoMemento *exportState() const
563 return new state_type(m_state);
566 void importState(const UndoMemento *state)
568 ASSERT_NOTNULL(state);
570 m_observer->save(this);
571 m_state = *(static_cast<const state_type *>( state ));
574 void mutate(unsigned int data)
576 m_observer->save(this);
577 m_state.test_data = data;
588 GlobalUndoSystem().begin( "bleh" );
590 GlobalUndoSystem().begin( "blah" );
592 GlobalUndoSystem().undo();
593 GlobalUndoSystem().undo();
594 GlobalUndoSystem().redo();
595 GlobalUndoSystem().redo();