From a53ba84fa1d24036fe4420842962f2e3fa399517 Mon Sep 17 00:00:00 2001 From: codereader Date: Sun, 24 Oct 2021 18:05:59 +0200 Subject: [PATCH] #5408: After some restructuring and a few paper cuts, the UndoSystem is now a child of the IMapRootNode implementations. The GlobalUndoSystem() accessor now refers to the GlobalMapModule()'s undo system, the undo system module has been transformed into the GlobalUndoSystemFactory. I'm not entirely happy with the way the Map module is subscribing to each map resource's undo system on map change, since the root node of a MapResource is not always available. --- include/imap.h | 10 ++ include/iundo.h | 33 ++++-- libs/scene/BasicRootNode.h | 15 ++- radiant/ui/einspector/EntityInspector.cpp | 4 +- radiantcore/CMakeLists.txt | 1 + radiantcore/brush/BrushModule.cpp | 1 - radiantcore/map/Map.cpp | 99 +++++++++++++----- radiantcore/map/Map.h | 6 ++ radiantcore/map/RootNode.cpp | 14 ++- radiantcore/map/RootNode.h | 4 + radiantcore/undo/UndoSystem.cpp | 113 +-------------------- radiantcore/undo/UndoSystem.h | 35 ++----- radiantcore/undo/UndoSystemFactory.cpp | 51 ++++++++++ test/UndoRedo.cpp | 5 + tools/msvc/DarkRadiantCore.vcxproj | 1 + tools/msvc/DarkRadiantCore.vcxproj.filters | 3 + 16 files changed, 210 insertions(+), 185 deletions(-) create mode 100644 radiantcore/undo/UndoSystemFactory.cpp diff --git a/include/imap.h b/include/imap.h index 02e3ebe4a9..0bdbb34598 100644 --- a/include/imap.h +++ b/include/imap.h @@ -31,6 +31,9 @@ class ITargetManager; // see ilayer.h class ILayerManager; +// see iundo.h +class IUndoSystem; + namespace selection { class ISelectionSetManager; @@ -85,6 +88,9 @@ class IMapRootNode : * Provides methods to create and assign layers in this map. */ virtual ILayerManager& getLayerManager() = 0; + + // The UndoSystem of this map + virtual IUndoSystem& getUndoSystem() = 0; }; typedef std::shared_ptr IMapRootNodePtr; @@ -181,6 +187,10 @@ class IMap : // Signal emitted when an operation affecting the map has been redone virtual sigc::signal& signal_postRedo() = 0; + // Accessor to the undo system of the main scene. + // Throws std::runtime_error if no map resource has been loaded. + virtual IUndoSystem& getUndoSystem() = 0; + // Caution: this is upposed to be called on startup, since it doesn't ask the user // whether to save the current map. Use the "NewMap" command for regular purposes. virtual void createNewMap() = 0; diff --git a/include/iundo.h b/include/iundo.h index 7a9a77cdbc..2fa27d4899 100644 --- a/include/iundo.h +++ b/include/iundo.h @@ -4,6 +4,7 @@ /// \brief The undo-system interface. Uses the 'memento' pattern. #include "imodule.h" +#include "imap.h" #include #include #include @@ -53,12 +54,11 @@ class IUndoStateSaver virtual void save(IUndoable& undoable) = 0; }; -const char* const MODULE_UNDOSYSTEM("UndoSystem"); - -class IUndoSystem : - public RegisterableModule +class IUndoSystem { public: + using Ptr = std::shared_ptr; + // Undoable objects need to call this to get hold of a StateSaver instance // which will take care of exporting and saving the state. The passed map file change // tracker will be notified when the state is saved. @@ -104,11 +104,28 @@ class IUndoSystem : virtual void detachTracker(Tracker& tracker) = 0; }; -// The accessor function -inline IUndoSystem& GlobalUndoSystem() +class IUndoSystemFactory : + public RegisterableModule +{ +public: + virtual ~IUndoSystemFactory() {} + + // Create a new UndoSystem instance for use in a map root node + virtual IUndoSystem::Ptr createUndoSystem() = 0; +}; + +constexpr const char* const MODULE_UNDOSYSTEM_FACTORY("UndoSystemFactory"); + +inline IUndoSystemFactory& GlobalUndoSystemFactory() +{ + static module::InstanceReference _reference(MODULE_UNDOSYSTEM_FACTORY); + return _reference; +} + +// The accessor function to the main map's undo system +inline IUndoSystem& GlobalUndoSystem() { - static module::InstanceReference _reference(MODULE_UNDOSYSTEM); - return _reference; + return GlobalMapModule().getUndoSystem(); } class UndoableCommand diff --git a/libs/scene/BasicRootNode.h b/libs/scene/BasicRootNode.h index 9223e0dbe8..43173aebd5 100644 --- a/libs/scene/BasicRootNode.h +++ b/libs/scene/BasicRootNode.h @@ -4,6 +4,7 @@ #include "mapfile.h" #include "ilayer.h" #include "ientity.h" +#include "iundo.h" #include "iselectiongroup.h" #include "iselectionset.h" #include "Node.h" @@ -16,7 +17,7 @@ namespace scene // A very simple implementation of a Map Root Node // for use in the preview widget's scenes. -class BasicRootNode : +class BasicRootNode final : public IMapRootNode, public Node, public KeyValueStore @@ -29,6 +30,7 @@ class BasicRootNode : selection::ISelectionGroupManager::Ptr _selectionGroupManager; selection::ISelectionSetManager::Ptr _selectionSetManager; ILayerManager::Ptr _layerManager; + IUndoSystem::Ptr _undoSystem; AABB _emptyAABB; public: @@ -39,12 +41,10 @@ class BasicRootNode : _selectionGroupManager = GlobalSelectionGroupModule().createSelectionGroupManager(); _selectionSetManager = GlobalSelectionSetModule().createSelectionSetManager(); _layerManager = GlobalLayerModule().createLayerManager(); + _undoSystem = GlobalUndoSystemFactory().createUndoSystem(); } - virtual ~BasicRootNode() - {} - - virtual void setName(const std::string& name) override + void setName(const std::string& name) override { _name = name; } @@ -79,6 +79,11 @@ class BasicRootNode : return *_layerManager; } + IUndoSystem& getUndoSystem() override + { + return *_undoSystem; + } + const AABB& localAABB() const override { return _emptyAABB; diff --git a/radiant/ui/einspector/EntityInspector.cpp b/radiant/ui/einspector/EntityInspector.cpp index ad05ee469b..a67d0eafff 100644 --- a/radiant/ui/einspector/EntityInspector.cpp +++ b/radiant/ui/einspector/EntityInspector.cpp @@ -505,10 +505,10 @@ const StringSet& EntityInspector::getDependencies() const MODULE_XMLREGISTRY, MODULE_GROUPDIALOG, MODULE_SELECTIONSYSTEM, - MODULE_UNDOSYSTEM, MODULE_GAMEMANAGER, MODULE_COMMANDSYSTEM, - MODULE_MAINFRAME + MODULE_MAINFRAME, + MODULE_MAP, }; return _dependencies; diff --git a/radiantcore/CMakeLists.txt b/radiantcore/CMakeLists.txt index 2962ebe6a4..8a9b3e6c69 100644 --- a/radiantcore/CMakeLists.txt +++ b/radiantcore/CMakeLists.txt @@ -292,6 +292,7 @@ add_library(radiantcore MODULE shaders/textures/TextureManipulator.cpp skins/Doom3SkinCache.cpp undo/UndoSystem.cpp + undo/UndoSystemFactory.cpp versioncontrol/VersionControlManager.cpp vfs/DeflatedInputStream.cpp vfs/DirectoryArchive.cpp diff --git a/radiantcore/brush/BrushModule.cpp b/radiantcore/brush/BrushModule.cpp index 8cd476fc62..7d8685d13c 100644 --- a/radiantcore/brush/BrushModule.cpp +++ b/radiantcore/brush/BrushModule.cpp @@ -100,7 +100,6 @@ const StringSet& BrushModuleImpl::getDependencies() const { _dependencies.insert(MODULE_GAMEMANAGER); _dependencies.insert(MODULE_XMLREGISTRY); _dependencies.insert(MODULE_PREFERENCESYSTEM); - _dependencies.insert(MODULE_UNDOSYSTEM); } return _dependencies; diff --git a/radiantcore/map/Map.cpp b/radiantcore/map/Map.cpp index df0380feaf..7c9961f0b5 100644 --- a/radiantcore/map/Map.cpp +++ b/radiantcore/map/Map.cpp @@ -84,6 +84,25 @@ void Map::clearMapResource() // Rename the map to "unnamed" in any case to avoid overwriting the failed map setMapName(_(MAP_UNNAMED_STRING)); + + connectToUndoSystem(); +} + +void Map::connectToUndoSystem() +{ + _postUndoListener.disconnect(); + _postRedoListener.disconnect(); + + if (!_resource->getRootNode()) return; + + _postUndoListener = _resource->getRootNode()->getUndoSystem().signal_postUndo().connect([this]() + { + _mapPostUndoSignal.emit(); + }); + _postRedoListener = _resource->getRootNode()->getUndoSystem().signal_postRedo().connect([this]() + { + _mapPostRedoSignal.emit(); + }); } void Map::loadMapResourceFromPath(const std::string& path) @@ -113,10 +132,7 @@ void Map::loadMapResourceFromLocation(const MapLocation& location) GlobalMapResourceManager().createFromArchiveFile(location.path, location.archiveRelativePath) : GlobalMapResourceManager().createFromPath(location.path); - if (!_resource) - { - return; - } + assert(_resource); try { @@ -133,6 +149,8 @@ void Map::loadMapResourceFromLocation(const MapLocation& location) clearMapResource(); } + connectToUndoSystem(); + // Take the new node and insert it as map root GlobalSceneGraph().setRoot(_resource->getRootNode()); @@ -452,6 +470,18 @@ sigc::signal& Map::signal_postRedo() return _mapPostRedoSignal; } +IUndoSystem& Map::getUndoSystem() +{ + const auto& rootNode = _resource->getRootNode(); + + if (!rootNode) + { + throw std::runtime_error("No map resource loaded"); + } + + return rootNode->getUndoSystem(); +} + // move the view to a certain position void Map::focusViews(const Vector3& point, const Vector3& angles) { @@ -774,6 +804,8 @@ bool Map::saveAs() return false; } + connectToUndoSystem(); + // Resource save was successful, notify about this name change rename(fileInfo.fullPath); @@ -924,6 +956,34 @@ void Map::registerCommands() cmd::ARGTYPE_INT | cmd::ARGTYPE_OPTIONAL, cmd::ARGTYPE_INT | cmd::ARGTYPE_OPTIONAL, cmd::ARGTYPE_INT | cmd::ARGTYPE_OPTIONAL }); + + // Add undo commands + GlobalCommandSystem().addCommand("Undo", std::bind(&Map::undoCmd, this, std::placeholders::_1)); + GlobalCommandSystem().addCommand("Redo", std::bind(&Map::redoCmd, this, std::placeholders::_1)); +} + +void Map::undoCmd(const cmd::ArgumentList& args) +{ + try + { + getUndoSystem().undo(); + } + catch (const std::runtime_error& err) + { + throw cmd::ExecutionNotPossible(err.what()); + } +} + +void Map::redoCmd(const cmd::ArgumentList& args) +{ + try + { + getUndoSystem().redo(); + } + catch (const std::runtime_error& err) + { + throw cmd::ExecutionNotPossible(err.what()); + } } // Static command targets @@ -1352,18 +1412,15 @@ const std::string& Map::getName() const const StringSet& Map::getDependencies() const { - static StringSet _dependencies; - - if (_dependencies.empty()) - { - _dependencies.insert(MODULE_GAMEMANAGER); - _dependencies.insert(MODULE_SCENEGRAPH); - _dependencies.insert(MODULE_MAPINFOFILEMANAGER); - _dependencies.insert(MODULE_FILETYPES); - _dependencies.insert(MODULE_MAPRESOURCEMANAGER); - _dependencies.insert(MODULE_COMMANDSYSTEM); - _dependencies.insert(MODULE_UNDOSYSTEM); - } + static StringSet _dependencies + { + MODULE_GAMEMANAGER, + MODULE_SCENEGRAPH, + MODULE_MAPINFOFILEMANAGER, + MODULE_FILETYPES, + MODULE_MAPRESOURCEMANAGER, + MODULE_COMMANDSYSTEM + }; return _dependencies; } @@ -1404,16 +1461,6 @@ void Map::initialiseModule(const IApplicationContext& ctx) radiant::IMessage::Type::ApplicationShutdownRequest, radiant::TypeListener( sigc::mem_fun(this, &Map::handleShutdownRequest))); - - // Prepare to dispatch the undo/redo signals - _postUndoListener = GlobalUndoSystem().signal_postUndo().connect([this]() - { - _mapPostUndoSignal.emit(); - }); - _postRedoListener = GlobalUndoSystem().signal_postRedo().connect([this]() - { - _mapPostRedoSignal.emit(); - }); } void Map::shutdownModule() diff --git a/radiantcore/map/Map.h b/radiantcore/map/Map.h index c24ed602c5..27699e37ec 100644 --- a/radiantcore/map/Map.h +++ b/radiantcore/map/Map.h @@ -195,6 +195,8 @@ class Map : sigc::signal& signal_postUndo() override; sigc::signal& signal_postRedo() override; + IUndoSystem& getUndoSystem() override; + // greebo: Creates a new, empty map file. void createNewMap() override; @@ -282,6 +284,7 @@ class Map : void emitMapEvent(MapEvent ev); void clearMapResource(); + void connectToUndoSystem(); void cleanupMergeOperation(); @@ -289,6 +292,9 @@ class Map : */ void focusViews(const Vector3& point, const Vector3& angles); void focusViewCmd(const cmd::ArgumentList& args); + + void undoCmd(const cmd::ArgumentList& args); + void redoCmd(const cmd::ArgumentList& args); }; } // namespace map diff --git a/radiantcore/map/RootNode.cpp b/radiantcore/map/RootNode.cpp index 5f5172722a..20250edfad 100644 --- a/radiantcore/map/RootNode.cpp +++ b/radiantcore/map/RootNode.cpp @@ -11,8 +11,6 @@ RootNode::RootNode(const std::string& name) : // Apply root status to this node setIsRoot(true); - GlobalUndoSystem().attachTracker(*this); - // Create a new namespace _namespace = GlobalNamespaceFactory().createNamespace(); assert(_namespace); @@ -28,11 +26,16 @@ RootNode::RootNode(const std::string& name) : _layerManager = GlobalLayerModule().createLayerManager(); assert(_layerManager); + + _undoSystem = GlobalUndoSystemFactory().createUndoSystem(); + assert(_undoSystem); + + _undoSystem->attachTracker(*this); } RootNode::~RootNode() { - GlobalUndoSystem().detachTracker(*this); + _undoSystem->detachTracker(*this); // Remove all child nodes to trigger their destruction removeAllChildNodes(); @@ -68,6 +71,11 @@ scene::ILayerManager& RootNode::getLayerManager() return *_layerManager; } +IUndoSystem& RootNode::getUndoSystem() +{ + return *_undoSystem; +} + std::string RootNode::name() const { return _name; diff --git a/radiantcore/map/RootNode.h b/radiantcore/map/RootNode.h index 6acf52c27c..5af6000f24 100644 --- a/radiantcore/map/RootNode.h +++ b/radiantcore/map/RootNode.h @@ -10,6 +10,7 @@ #include "UndoFileChangeTracker.h" #include "transformlib.h" #include "KeyValueStore.h" +#include "undo/UndoSystem.h" namespace map { @@ -46,6 +47,8 @@ class RootNode : scene::ILayerManager::Ptr _layerManager; + IUndoSystem::Ptr _undoSystem; + AABB _emptyAABB; public: @@ -62,6 +65,7 @@ class RootNode : selection::ISelectionGroupManager& getSelectionGroupManager() override; selection::ISelectionSetManager& getSelectionSetManager() override; scene::ILayerManager& getLayerManager() override; + IUndoSystem& getUndoSystem() override; // Renderable implementation (empty) void renderSolid(RenderableCollector& collector, const VolumeTest& volume) const override diff --git a/radiantcore/undo/UndoSystem.cpp b/radiantcore/undo/UndoSystem.cpp index 3bed0beff3..60ee403605 100644 --- a/radiantcore/undo/UndoSystem.cpp +++ b/radiantcore/undo/UndoSystem.cpp @@ -1,31 +1,19 @@ #include "UndoSystem.h" -#include "i18n.h" - #include "itextstream.h" -#include "ipreferencesystem.h" #include "iscenegraph.h" #include -#include "registry/registry.h" -#include "module/StaticModule.h" #include "Operation.h" #include "StackFiller.h" namespace undo { -namespace -{ - const std::string RKEY_UNDO_QUEUE_SIZE = "user/ui/undo/queueSize"; - const std::size_t MAX_UNDO_LEVELS = 16384; -} - -// Constructor UndoSystem::UndoSystem() : _activeUndoStack(nullptr), - _undoLevels(64) + _undoLevels(RKEY_UNDO_QUEUE_SIZE) {} UndoSystem::~UndoSystem() @@ -33,11 +21,6 @@ UndoSystem::~UndoSystem() clear(); } -void UndoSystem::keyChanged() -{ - _undoLevels = registry::getValue(RKEY_UNDO_QUEUE_SIZE); -} - IUndoStateSaver* UndoSystem::getStateSaver(IUndoable& undoable, IMapFileChangeTracker& tracker) { auto result = _undoables.insert(std::make_pair(&undoable, UndoStackFiller(tracker))); @@ -64,7 +47,7 @@ std::size_t UndoSystem::size() const void UndoSystem::start() { _redoStack.clear(); - if (_undoStack.size() == _undoLevels) + if (_undoStack.size() == _undoLevels.get()) { _undoStack.pop_front(); } @@ -198,89 +181,6 @@ void UndoSystem::detachTracker(Tracker& tracker) _trackers.erase(&tracker); } -const std::string& UndoSystem::getName() const -{ - static std::string _name(MODULE_UNDOSYSTEM); - return _name; -} - -const StringSet& UndoSystem::getDependencies() const -{ - static StringSet _dependencies; - - if (_dependencies.empty()) - { - _dependencies.insert(MODULE_XMLREGISTRY); - _dependencies.insert(MODULE_PREFERENCESYSTEM); - _dependencies.insert(MODULE_COMMANDSYSTEM); - _dependencies.insert(MODULE_SCENEGRAPH); - _dependencies.insert(MODULE_MAP); - } - - return _dependencies; -} - -void UndoSystem::initialiseModule(const IApplicationContext& ctx) -{ - rMessage() << "UndoSystem::initialiseModule called" << std::endl; - - // Add commands for console input - GlobalCommandSystem().addCommand("Undo", std::bind(&UndoSystem::undoCmd, this, std::placeholders::_1)); - GlobalCommandSystem().addCommand("Redo", std::bind(&UndoSystem::redoCmd, this, std::placeholders::_1)); - - _undoLevels = registry::getValue(RKEY_UNDO_QUEUE_SIZE); - - // Add self to the key observers to get notified on change - GlobalRegistry().signalForKey(RKEY_UNDO_QUEUE_SIZE).connect( - sigc::mem_fun(this, &UndoSystem::keyChanged) - ); - - // add the preference settings - constructPreferences(); - - GlobalMapModule().signal_mapEvent().connect( - sigc::mem_fun(*this, &UndoSystem::onMapEvent) - ); -} - -void UndoSystem::undoCmd(const cmd::ArgumentList& args) -{ - undo(); -} - -void UndoSystem::redoCmd(const cmd::ArgumentList& args) -{ - redo(); -} - -void UndoSystem::onMapEvent(IMap::MapEvent ev) -{ - if (ev == IMap::MapUnloaded) - { - clear(); - } -} - -// Sets the size of the undoStack -void UndoSystem::setLevels(std::size_t levels) -{ - if (levels > MAX_UNDO_LEVELS) - { - levels = MAX_UNDO_LEVELS; - } - - while (_undoStack.size() > levels) - { - _undoStack.pop_front(); - } - _undoLevels = levels; -} - -std::size_t UndoSystem::getLevels() const -{ - return _undoLevels; -} - void UndoSystem::startUndo() { _undoStack.start("unnamedCommand"); @@ -346,13 +246,4 @@ void UndoSystem::trackersRedo() const foreachTracker([&] (Tracker& tracker) { tracker.redo(); }); } -void UndoSystem::constructPreferences() -{ - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Undo System")); - page.appendSpinner(_("Undo Queue Size"), RKEY_UNDO_QUEUE_SIZE, 0, 1024, 1); -} - -// Static module instance -module::StaticModule _staticUndoSystemModule; - } // namespace undo diff --git a/radiantcore/undo/UndoSystem.h b/radiantcore/undo/UndoSystem.h index f4a44ec230..b4237e4876 100644 --- a/radiantcore/undo/UndoSystem.h +++ b/radiantcore/undo/UndoSystem.h @@ -6,14 +6,16 @@ #include "iundo.h" #include "icommandsystem.h" -#include "imap.h" #include "Stack.h" #include "StackFiller.h" +#include "registry/CachedKey.h" namespace undo { +constexpr const char* const RKEY_UNDO_QUEUE_SIZE = "user/ui/undo/queueSize"; + /** * greebo: The UndoSystem (interface: iundo.h) is maintaining two internal * stacks of Operations (one for Undo, one for Redo), each containing a list @@ -32,7 +34,7 @@ namespace undo * The RedoStack is discarded as soon as a new Undoable Operation is recorded * and pushed to the UndoStack. */ -class UndoSystem : +class UndoSystem final : public IUndoSystem { private: @@ -45,7 +47,7 @@ class UndoSystem : typedef std::map UndoablesMap; UndoablesMap _undoables; - std::size_t _undoLevels; + registry::CachedKey _undoLevels; typedef std::set Trackers; Trackers _trackers; @@ -54,10 +56,8 @@ class UndoSystem : sigc::signal _signalPostRedo; public: - // Constructor UndoSystem(); - - virtual ~UndoSystem(); + ~UndoSystem(); IUndoStateSaver* getStateSaver(IUndoable& undoable, IMapFileChangeTracker& tracker) override; @@ -88,28 +88,7 @@ class UndoSystem : void attachTracker(Tracker& tracker) override; void detachTracker(Tracker& tracker) override; - // RegisterableModule implementation - const std::string& getName() const override; - const StringSet& getDependencies() const override; - void initialiseModule(const IApplicationContext& ctx) override; - private: - // This is connected to the CommandSystem - void undoCmd(const cmd::ArgumentList& args); - - // This is connected to the CommandSystem - void redoCmd(const cmd::ArgumentList& args); - - // Gets called as soon as the observed registry key is changed - void keyChanged(); - - void onMapEvent(IMap::MapEvent ev); - - // Sets the size of the undoStack - void setLevels(std::size_t levels); - - std::size_t getLevels() const; - void startUndo(); bool finishUndo(const std::string& command); @@ -125,8 +104,6 @@ class UndoSystem : void trackersBegin() const; void trackersUndo() const; void trackersRedo() const; - - void constructPreferences(); }; } diff --git a/radiantcore/undo/UndoSystemFactory.cpp b/radiantcore/undo/UndoSystemFactory.cpp new file mode 100644 index 0000000000..02558d5b0c --- /dev/null +++ b/radiantcore/undo/UndoSystemFactory.cpp @@ -0,0 +1,51 @@ +#include "iundo.h" + +#include "i18n.h" +#include "ipreferencesystem.h" +#include "UndoSystem.h" +#include "module/StaticModule.h" + +namespace undo +{ + +class UndoSystemFactory final : + public IUndoSystemFactory +{ +public: + const std::string& getName() const override + { + static std::string _name(MODULE_UNDOSYSTEM_FACTORY); + return _name; + } + + const StringSet& getDependencies() const override + { + static StringSet _dependencies{ MODULE_PREFERENCESYSTEM }; + return _dependencies; + } + + void initialiseModule(const IApplicationContext& ctx) override + { + rMessage() << getName() << "::initialiseModule called" << std::endl; + + // add the preference settings + constructPreferences(); + } + + IUndoSystem::Ptr createUndoSystem() override + { + return std::make_shared(); + } + +private: + void constructPreferences() + { + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Undo System")); + page.appendSpinner(_("Undo Queue Size"), RKEY_UNDO_QUEUE_SIZE, 0, 1024, 1); + } +}; + +// Static module instance +module::StaticModule _undoSystemFactoryModule; + +} diff --git a/test/UndoRedo.cpp b/test/UndoRedo.cpp index 5d304e1a27..62cd6de892 100644 --- a/test/UndoRedo.cpp +++ b/test/UndoRedo.cpp @@ -19,6 +19,11 @@ namespace test using UndoTest = RadiantTest; +TEST_F(UndoTest, UndoSystemFactory) +{ + EXPECT_TRUE(GlobalUndoSystemFactory().createUndoSystem()) << "Undo system factory must not return empty references"; +} + TEST_F(UndoTest, BrushCreation) { std::string mapPath = "maps/simple_brushes.map"; diff --git a/tools/msvc/DarkRadiantCore.vcxproj b/tools/msvc/DarkRadiantCore.vcxproj index 33a9d81a4c..fcd86cde27 100644 --- a/tools/msvc/DarkRadiantCore.vcxproj +++ b/tools/msvc/DarkRadiantCore.vcxproj @@ -705,6 +705,7 @@ + diff --git a/tools/msvc/DarkRadiantCore.vcxproj.filters b/tools/msvc/DarkRadiantCore.vcxproj.filters index 153fe9da1f..8f9412af4f 100644 --- a/tools/msvc/DarkRadiantCore.vcxproj.filters +++ b/tools/msvc/DarkRadiantCore.vcxproj.filters @@ -1108,6 +1108,9 @@ src\selection\algorithm + + src\undo +