diff --git a/include/ifilter.h b/include/ifilter.h index 4b615f8e82..10eb5a8396 100644 --- a/include/ifilter.h +++ b/include/ifilter.h @@ -82,13 +82,17 @@ class Entity; * The filter system provides a mechanism by which certain objects or materials * can be hidden from rendered views. */ -class FilterSystem : +class IFilterSystem : public RegisterableModule { public: - /// Signal emitted when the state of filters has changed - virtual sigc::signal filtersChangedSignal() const = 0; + // Signal emitted when the state of filters has changed, + // filters have been added or removed, or when rules have been altered + virtual sigc::signal filterConfigChangedSignal() const = 0; + + // Signal emitted when filters are added, removed or renamed + virtual sigc::signal filterCollectionChangedSignal() const = 0; /** * greebo: Updates all the "Filtered" status of all Instances @@ -206,11 +210,11 @@ class FilterSystem : virtual bool setFilterRules(const std::string& filter, const FilterRules& ruleSet) = 0; }; -inline FilterSystem& GlobalFilterSystem() +inline IFilterSystem& GlobalFilterSystem() { // Cache the reference locally - static FilterSystem& _filterSystem( - *std::static_pointer_cast( + static IFilterSystem& _filterSystem( + *std::static_pointer_cast( module::GlobalModuleRegistry().getModule(MODULE_FILTERSYSTEM) ) ); diff --git a/libs/wxutil/preview/RenderPreview.cpp b/libs/wxutil/preview/RenderPreview.cpp index 111c9bd8c9..ef347fb905 100644 --- a/libs/wxutil/preview/RenderPreview.cpp +++ b/libs/wxutil/preview/RenderPreview.cpp @@ -109,9 +109,9 @@ void RenderPreview::setupToolbars(bool enableAnimation) filterToolbar->Realize(); // Get notified of filter changes - GlobalFilterSystem().filtersChangedSignal().connect( - sigc::mem_fun(this, &RenderPreview::filtersChanged) - ); + GlobalFilterSystem().filterConfigChangedSignal().connect( + sigc::mem_fun(this, &RenderPreview::onFilterConfigChanged) + ); wxToolBar* renderToolbar = findNamedObject(_mainPanel, "RenderPreviewRenderModeToolbar"); @@ -166,7 +166,7 @@ void RenderPreview::updateActiveRenderModeButton() } } -void RenderPreview::filtersChanged() +void RenderPreview::onFilterConfigChanged() { if (!getScene()->root()) return; diff --git a/libs/wxutil/preview/RenderPreview.h b/libs/wxutil/preview/RenderPreview.h index 3248aaa98d..17924a80d7 100644 --- a/libs/wxutil/preview/RenderPreview.h +++ b/libs/wxutil/preview/RenderPreview.h @@ -56,7 +56,7 @@ class RenderPreview : void onStepBackClick(wxCommandEvent& ev); void onSizeAllocate(wxSizeEvent& ev); - void filtersChanged(); + void onFilterConfigChanged(); void onRenderModeChanged(wxCommandEvent& ev); void onGridButtonClick(wxCommandEvent& ev); diff --git a/radiant/filters/BasicFilterSystem.cpp b/radiant/filters/BasicFilterSystem.cpp index a65aaf755c..29a45dccbb 100644 --- a/radiant/filters/BasicFilterSystem.cpp +++ b/radiant/filters/BasicFilterSystem.cpp @@ -16,7 +16,7 @@ namespace filters { -namespace +namespace { // Registry key for .game-defined filters const std::string RKEY_GAME_FILTERS = "/filtersystem//filter"; @@ -50,7 +50,7 @@ void BasicFilterSystem::setAllFilterStates(bool state) updateEvents(); - _filtersChangedSignal.emit(); + _filterConfigChangedSignal.emit(); // Trigger an immediate scene redraw GlobalSceneGraph().sceneChanged(); @@ -330,11 +330,19 @@ void BasicFilterSystem::shutdownModule() _eventAdapters.clear(); _activeFilters.clear(); _availableFilters.clear(); + + _filterCollectionChangedSignal.clear(); + _filterConfigChangedSignal.clear(); } -sigc::signal BasicFilterSystem::filtersChangedSignal() const +sigc::signal BasicFilterSystem::filterConfigChangedSignal() const { - return _filtersChangedSignal; + return _filterConfigChangedSignal; +} + +sigc::signal BasicFilterSystem::filterCollectionChangedSignal() const +{ + return _filterCollectionChangedSignal; } void BasicFilterSystem::update() @@ -397,7 +405,7 @@ void BasicFilterSystem::setFilterState(const std::string& filter, bool state) // Update the scenegraph instances update(); - _filtersChangedSignal.emit(); + _filterConfigChangedSignal.emit(); // Trigger an immediate scene redraw GlobalSceneGraph().sceneChanged(); @@ -442,10 +450,7 @@ bool BasicFilterSystem::addFilter(const std::string& filterName, const FilterRul // Create the event adapter ensureEventAdapter(*filter); - // Clear the cache, the rules have changed - _visibilityCache.clear(); - - _filtersChangedSignal.emit(); + _filterCollectionChangedSignal.emit(); return true; } @@ -465,18 +470,20 @@ bool BasicFilterSystem::removeFilter(const std::string& filter) // Check if the filter was active auto found = _activeFilters.find(f->first); - if (found != _activeFilters.end()) - { - _activeFilters.erase(found); - } - // Now remove the object from the available filters too _availableFilters.erase(f); - // Clear the cache, the rules have changed - _visibilityCache.clear(); + if (found != _activeFilters.end()) + { + _activeFilters.erase(found); + + // Clear the cache, the rules have changed + _visibilityCache.clear(); + + _filterConfigChangedSignal.emit(); + } - _filtersChangedSignal.emit(); + _filterCollectionChangedSignal.emit(); return true; } @@ -541,6 +548,8 @@ bool BasicFilterSystem::renameFilter(const std::string& oldFilterName, const std // Remove the old filter from the filtertable _availableFilters.erase(oldFilterName); + _filterCollectionChangedSignal.emit(); + return true; } @@ -618,7 +627,7 @@ bool BasicFilterSystem::setFilterRules(const std::string& filter, const FilterRu // Clear the cache, the ruleset has changed _visibilityCache.clear(); - _filtersChangedSignal.emit(); + _filterConfigChangedSignal.emit(); return true; } @@ -669,7 +678,6 @@ const StringSet& BasicFilterSystem::getDependencies() const { _dependencies.insert(MODULE_XMLREGISTRY); _dependencies.insert(MODULE_GAMEMANAGER); - _dependencies.insert(MODULE_EVENTMANAGER); _dependencies.insert(MODULE_COMMANDSYSTEM); } diff --git a/radiant/filters/BasicFilterSystem.h b/radiant/filters/BasicFilterSystem.h index b5574364e1..a949e0905f 100644 --- a/radiant/filters/BasicFilterSystem.h +++ b/radiant/filters/BasicFilterSystem.h @@ -22,7 +22,7 @@ const char* const DESELECT_OBJECTS_BY_FILTER_CMD = "DeselectObjectsByFilter"; /** FilterSystem implementation class. */ class BasicFilterSystem : - public FilterSystem + public IFilterSystem { private: // Hashtable of available filters, indexed by name @@ -37,7 +37,8 @@ class BasicFilterSystem : typedef std::map StringFlagCache; StringFlagCache _visibilityCache; - sigc::signal _filtersChangedSignal; + sigc::signal _filterConfigChangedSignal; + sigc::signal _filterCollectionChangedSignal; typedef std::map FilterAdapters; FilterAdapters _eventAdapters; @@ -67,7 +68,8 @@ class BasicFilterSystem : public: // FilterSystem implementation - sigc::signal filtersChangedSignal() const override; + sigc::signal filterConfigChangedSignal() const override; + sigc::signal filterCollectionChangedSignal() const override; // Invoke the InstanceUpateWalker to update the filtered status. void update() override; diff --git a/radiant/filters/InstanceUpdateWalker.h b/radiant/filters/InstanceUpdateWalker.h index 2079961837..7883b0a3eb 100644 --- a/radiant/filters/InstanceUpdateWalker.h +++ b/radiant/filters/InstanceUpdateWalker.h @@ -48,7 +48,7 @@ class InstanceUpdateWalker : public scene::NodeVisitor { private: - FilterSystem& _filterSystem; + IFilterSystem& _filterSystem; // Helper visitors to update subgraphs NodeVisibilityUpdater _hideWalker; @@ -60,7 +60,7 @@ class InstanceUpdateWalker : bool _brushesAreVisible; public: - InstanceUpdateWalker(FilterSystem& filterSystem) : + InstanceUpdateWalker(IFilterSystem& filterSystem) : _filterSystem(filterSystem), _hideWalker(true), _showWalker(false), diff --git a/radiant/ui/UserInterfaceModule.cpp b/radiant/ui/UserInterfaceModule.cpp index 9efbb82751..c1add773d7 100644 --- a/radiant/ui/UserInterfaceModule.cpp +++ b/radiant/ui/UserInterfaceModule.cpp @@ -26,7 +26,6 @@ #include "map/FindMapElements.h" #include "ui/mapinfo/MapInfoDialog.h" #include "ui/commandlist/CommandList.h" -#include "ui/filters/editor/FilterDialog.h" #include "ui/mousetool/ToolMappingDialog.h" #include "ui/about/AboutDialog.h" #include "ui/eclasstree/EClassTree.h" @@ -147,7 +146,6 @@ void UserInterfaceModule::registerUICommands() GlobalCommandSystem().addCommand("FindBrush", DoFind); GlobalCommandSystem().addCommand("MapInfo", MapInfoDialog::ShowDialog); - GlobalCommandSystem().addCommand("EditFiltersDialog", FilterDialog::ShowDialog); GlobalCommandSystem().addCommand("MouseToolMappingDialog", ToolMappingDialog::ShowDialog); GlobalCommandSystem().addCommand("FindReplaceTextures", FindAndReplaceShader::ShowDialog); @@ -176,7 +174,6 @@ void UserInterfaceModule::registerUICommands() GlobalEventManager().addCommand("FindBrush", "FindBrush"); GlobalEventManager().addCommand("MapInfo", "MapInfo"); - GlobalEventManager().addCommand("EditFiltersDialog", "EditFiltersDialog"); GlobalEventManager().addCommand("MouseToolMappingDialog", "MouseToolMappingDialog"); GlobalEventManager().addCommand("FindReplaceTextures", "FindReplaceTextures"); diff --git a/radiant/ui/entitylist/EntityList.cpp b/radiant/ui/entitylist/EntityList.cpp index 061351ec2b..5e73d8d276 100644 --- a/radiant/ui/entitylist/EntityList.cpp +++ b/radiant/ui/entitylist/EntityList.cpp @@ -146,7 +146,7 @@ void EntityList::selectionChanged(const scene::INodePtr& node, bool isComponent) _callbackActive = false; } -void EntityList::filtersChanged() +void EntityList::onFilterConfigChanged() { // Only react to filter changes if we display visible nodes only otherwise // we don't care @@ -166,7 +166,7 @@ void EntityList::_preHide() _treeModel.disconnectFromSceneGraph(); // Disconnect from the filters-changed signal - _filtersChangedConnection.disconnect(); + _filtersConfigChangedConn.disconnect(); // De-register self from the SelectionSystem GlobalSelectionSystem().removeObserver(this); @@ -191,8 +191,8 @@ void EntityList::_preShow() GlobalSelectionSystem().addObserver(this); // Get notified when filters are changing - _filtersChangedConnection = GlobalFilterSystem().filtersChangedSignal().connect( - sigc::mem_fun(Instance(), &EntityList::filtersChanged) + _filtersConfigChangedConn = GlobalFilterSystem().filterConfigChangedSignal().connect( + sigc::mem_fun(Instance(), &EntityList::onFilterConfigChanged) ); _callbackActive = true; diff --git a/radiant/ui/entitylist/EntityList.h b/radiant/ui/entitylist/EntityList.h index 69729fd87b..748ec2883e 100644 --- a/radiant/ui/entitylist/EntityList.h +++ b/radiant/ui/entitylist/EntityList.h @@ -40,7 +40,7 @@ class EntityList : wxCheckBox* _focusSelected; wxCheckBox* _visibleOnly; - sigc::connection _filtersChangedConnection; + sigc::connection _filtersConfigChangedConn; struct DataViewItemLess { @@ -76,7 +76,7 @@ class EntityList : // Called by the graph tree model void onTreeViewSelection(const wxDataViewItem& item, bool selected); - void filtersChanged(); + void onFilterConfigChanged(); void onRowExpand(wxDataViewEvent& ev); diff --git a/radiant/ui/filters/FilterUserInterface.cpp b/radiant/ui/filters/FilterUserInterface.cpp index db88938476..1247c63871 100644 --- a/radiant/ui/filters/FilterUserInterface.cpp +++ b/radiant/ui/filters/FilterUserInterface.cpp @@ -2,10 +2,12 @@ #include "ifilter.h" #include "ieventmanager.h" +#include "icommandsystem.h" #include #include "module/StaticModule.h" #include "FiltersMainMenu.h" +#include "editor/FilterDialog.h" namespace ui { @@ -23,6 +25,7 @@ const StringSet& FilterUserInterface::getDependencies() const if (_dependencies.empty()) { _dependencies.insert(MODULE_FILTERSYSTEM); + _dependencies.insert(MODULE_COMMANDSYSTEM); _dependencies.insert(MODULE_EVENTMANAGER); } @@ -33,41 +36,74 @@ void FilterUserInterface::initialiseModule(const ApplicationContext& ctx) { rMessage() << getName() << "::initialiseModule called." << std::endl; - // Create a Toggle item such that menu items can bind to it - GlobalFilterSystem().forEachFilter([&](const std::string& name) - { - auto eventName = GlobalFilterSystem().getFilterEventName(name); - auto toggleEvent = GlobalEventManager().addToggle( - eventName, - std::bind(&FilterUserInterface::toggleFilter, this, std::string(name), std::placeholders::_1) - ); - - _toggleFilterEvents.emplace(name, toggleEvent); - - toggleEvent->setToggled(GlobalFilterSystem().getFilterState(name)); - }); + // Create the Toggle objects to connect menu items and toggle buttons + refreshFilterToggles(); GlobalEventManager().addCommand("ActivateAllFilters", "ActivateAllFilters"); GlobalEventManager().addCommand("DeactivateAllFilters", "DeactivateAllFilters"); - _filtersChangedConn = GlobalFilterSystem().filtersChangedSignal().connect( - sigc::mem_fun(*this, &FilterUserInterface::onFiltersChanged) + _filterConfigChangedConn = GlobalFilterSystem().filterConfigChangedSignal().connect( + sigc::mem_fun(*this, &FilterUserInterface::onFilterConfigChanged) + ); + + _filterCollectionChangedConn = GlobalFilterSystem().filterCollectionChangedSignal().connect( + sigc::mem_fun(*this, &FilterUserInterface::onFilterCollectionChanged) ); // Create the main menu Filter entries FiltersMenu::addItemsToMainMenu(); + + // Editor + GlobalCommandSystem().addCommand("EditFiltersDialog", FilterDialog::ShowDialog); + GlobalEventManager().addCommand("EditFiltersDialog", "EditFiltersDialog"); } void FilterUserInterface::shutdownModule() { + _filterConfigChangedConn.disconnect(); + _filterCollectionChangedConn.disconnect(); +} + +void FilterUserInterface::onFilterConfigChanged() +{ + for (const auto& pair : _toggleFilterEvents) + { + pair.second.second->setToggled(GlobalFilterSystem().getFilterState(pair.first)); + } +} + +void FilterUserInterface::onFilterCollectionChanged() +{ + refreshFilterToggles(); + + // Recreate the main menu Filter entries + FiltersMenu::removeItemsFromMainMenu(); + FiltersMenu::addItemsToMainMenu(); } -void FilterUserInterface::onFiltersChanged() +void FilterUserInterface::refreshFilterToggles() { + // Remove the old set of events first for (const auto& pair : _toggleFilterEvents) { - pair.second->setToggled(GlobalFilterSystem().getFilterState(pair.first)); + GlobalEventManager().removeEvent(pair.second.first); } + + _toggleFilterEvents.clear(); + + // Create a Toggle item such that menu items can bind to it + GlobalFilterSystem().forEachFilter([&](const std::string& name) + { + auto eventName = GlobalFilterSystem().getFilterEventName(name); + auto toggleEvent = GlobalEventManager().addToggle( + eventName, + std::bind(&FilterUserInterface::toggleFilter, this, std::string(name), std::placeholders::_1) + ); + + _toggleFilterEvents.emplace(name, std::make_pair(eventName, toggleEvent)); + + toggleEvent->setToggled(GlobalFilterSystem().getFilterState(name)); + }); } void FilterUserInterface::toggleFilter(const std::string& filterName, bool newState) diff --git a/radiant/ui/filters/FilterUserInterface.h b/radiant/ui/filters/FilterUserInterface.h index 35d4a3e343..89ac0c5c5f 100644 --- a/radiant/ui/filters/FilterUserInterface.h +++ b/radiant/ui/filters/FilterUserInterface.h @@ -11,9 +11,11 @@ class FilterUserInterface : public RegisterableModule { private: - std::map _toggleFilterEvents; + typedef std::pair NamedToggle; + std::map _toggleFilterEvents; - sigc::connection _filtersChangedConn; + sigc::connection _filterConfigChangedConn; + sigc::connection _filterCollectionChangedConn; public: const std::string& getName() const override; @@ -22,7 +24,9 @@ class FilterUserInterface : void shutdownModule() override; private: - void onFiltersChanged(); + void onFilterConfigChanged(); + void onFilterCollectionChanged(); + void refreshFilterToggles(); void toggleFilter(const std::string& filterName, bool newState); };