diff --git a/include/ui/ieventmanager.h b/include/ui/ieventmanager.h index 1c5ba9ae7..e15160052 100644 --- a/include/ui/ieventmanager.h +++ b/include/ui/ieventmanager.h @@ -202,6 +202,8 @@ class IEventManager : /* greebo: Retrieves the string representation of the given event */ virtual std::string getEventStr(wxKeyEvent& ev) = 0; + + virtual void connectDeferredAccelerators() = 0; }; // Global accessor for the event manager diff --git a/plugins/script/PythonModule.cpp b/plugins/script/PythonModule.cpp index 906384a8b..1cbde5419 100644 --- a/plugins/script/PythonModule.cpp +++ b/plugins/script/PythonModule.cpp @@ -309,7 +309,7 @@ ScriptCommand::Ptr PythonModule::createScriptCommand(const std::string& scriptBa } // Successfully retrieved the command - return std::make_shared(cmdName, cmdDisplayName, relativeScriptPath); + return std::make_shared(cmdName, cmdDisplayName, relativeScriptPath, scriptBasePath); } rError() << "Script file " << relativeScriptPath << " does not export a __commandName__ value" << std::endl; diff --git a/plugins/script/ScriptCommand.cpp b/plugins/script/ScriptCommand.cpp index 16052e7bf..a68b15bee 100644 --- a/plugins/script/ScriptCommand.cpp +++ b/plugins/script/ScriptCommand.cpp @@ -7,10 +7,12 @@ namespace script ScriptCommand::ScriptCommand(const std::string& name, const std::string& displayName, - const std::string& scriptFilename) : + const std::string& scriptFilename, + const std::string& basePath) : _name(name), _displayName(displayName), - _scriptFilename(scriptFilename) + _scriptFilename(scriptFilename), + _basePath(basePath) { // Register this with the command system GlobalCommandSystem().addStatement(_name, "RunScriptCommand '" + _name + "'", false); diff --git a/plugins/script/ScriptCommand.h b/plugins/script/ScriptCommand.h index 8839a5c26..8a690f14e 100644 --- a/plugins/script/ScriptCommand.h +++ b/plugins/script/ScriptCommand.h @@ -23,12 +23,15 @@ class ScriptCommand : // The script file name to execute (relative to scripts/ folder) std::string _scriptFilename; + std::string _basePath; + public: using Ptr = std::shared_ptr; ScriptCommand(const std::string& name, const std::string& displayName, - const std::string& scriptFilename); + const std::string& scriptFilename, + const std::string& basePath); ~ScriptCommand() override; @@ -46,6 +49,11 @@ class ScriptCommand : { return _displayName; } + + const std::string& getBasePath() const + { + return _basePath; + } }; } // namespace script diff --git a/plugins/script/ScriptingSystem.cpp b/plugins/script/ScriptingSystem.cpp index 7d95c3e15..e0c53f2b6 100644 --- a/plugins/script/ScriptingSystem.cpp +++ b/plugins/script/ScriptingSystem.cpp @@ -31,10 +31,12 @@ #include "PythonModule.h" #include "SceneNodeBuffer.h" +#include "ui/ieventmanager.h" #include "os/path.h" #include #include "string/case_conv.h" +#include "settings/SettingsManager.h" namespace script { @@ -95,6 +97,8 @@ void ScriptingSystem::initialise() // Search script folder for commands reloadScripts(); + + GlobalEventManager().connectDeferredAccelerators(); } void ScriptingSystem::runScriptFile(const cmd::ArgumentList& args) @@ -138,12 +142,12 @@ void ScriptingSystem::executeCommand(const std::string& name) UndoableCommand cmd("runScriptCommand " + name); // Execute the script file behind this command - executeScriptFile(found->second->getFilename(), true); + _pythonModule->executeScriptFile(found->second->getBasePath(), found->second->getFilename(), true); } -void ScriptingSystem::loadCommandScript(const std::string& scriptFilename) +void ScriptingSystem::loadCommandScript(const std::string& basePath, const std::string& scriptFilename) { - auto command = _pythonModule->createScriptCommand(_scriptPath, scriptFilename); + auto command = _pythonModule->createScriptCommand(basePath, scriptFilename); if (!command) { @@ -173,29 +177,37 @@ void ScriptingSystem::reloadScripts() _commands.clear(); // Initialise the search's starting point - fs::path start = fs::path(_scriptPath) / COMMAND_PATH; - - if (!fs::exists(start)) + auto scanDirectory = [&](const std::string& basePath) { - rWarning() << "Couldn't find scripts folder: " << start.string() << std::endl; - return; - } + fs::path start = fs::path(basePath) / COMMAND_PATH; - for (fs::recursive_directory_iterator it(start); - it != fs::recursive_directory_iterator(); ++it) - { - // Get the candidate - const fs::path& candidate = *it; + if (!fs::exists(start)) + { + return; + } - if (fs::is_directory(candidate)) continue; + for (fs::recursive_directory_iterator it(start); + it != fs::recursive_directory_iterator(); ++it) + { + const fs::path& candidate = *it; - std::string extension = os::getExtension(candidate.string()); - string::to_lower(extension); + if (fs::is_directory(candidate)) continue; - if (extension != PYTHON_FILE_EXTENSION) continue; + std::string extension = os::getExtension(candidate.string()); + string::to_lower(extension); - // Script file found, construct a new command - loadCommandScript(os::getRelativePath(candidate.generic_string(), _scriptPath)); + if (extension != PYTHON_FILE_EXTENSION) continue; + + // Script file found, construct a new command + loadCommandScript(basePath, os::getRelativePath(candidate.generic_string(), basePath)); + } + }; + + scanDirectory(_scriptPath); + + if (!_userScriptPath.empty()) + { + scanDirectory(_userScriptPath); } rMessage() << "ScriptModule: Found " << _commands.size() << " commands." << std::endl; @@ -208,6 +220,9 @@ void ScriptingSystem::initialiseModule(const IApplicationContext& ctx) // Construct the script path _scriptPath = ctx.getRuntimeDataPath() + SCRIPT_PATH; + settings::SettingsManager manager(ctx); + _userScriptPath = manager.getCurrentVersionSettingsFolder() + SCRIPT_PATH; + // Set up the python interpreter _pythonModule.reset(new PythonModule); @@ -274,6 +289,7 @@ void ScriptingSystem::shutdownModule() _commands.clear(); _scriptPath.clear(); + _userScriptPath.clear(); _pythonModule.reset(); } diff --git a/plugins/script/ScriptingSystem.h b/plugins/script/ScriptingSystem.h index cb6d7fc4f..bfd6d0cdf 100644 --- a/plugins/script/ScriptingSystem.h +++ b/plugins/script/ScriptingSystem.h @@ -22,6 +22,7 @@ class ScriptingSystem: public IScriptingSystem // The path where the script files are hosted std::string _scriptPath; + std::string _userScriptPath; // All named script commands (pointing to .py files) std::map _commands; @@ -74,7 +75,7 @@ class ScriptingSystem: public IScriptingSystem private: void executeScriptFile(const std::string& filename, bool setExecuteCommandAttr); void reloadScripts(); - void loadCommandScript(const std::string& scriptFilename); + void loadCommandScript(const std::string& basePath, const std::string& scriptFilename); }; typedef std::shared_ptr ScriptingSystemPtr; diff --git a/radiant/eventmanager/EventManager.cpp b/radiant/eventmanager/EventManager.cpp index 65204df46..2eb3d622d 100644 --- a/radiant/eventmanager/EventManager.cpp +++ b/radiant/eventmanager/EventManager.cpp @@ -83,6 +83,7 @@ void EventManager::shutdownModule() saveEventListToRegistry(); _accelerators.clear(); + _deferredAccelerators.clear(); _events.clear(); } @@ -107,6 +108,7 @@ void EventManager::resetAcceleratorBindings() } _accelerators.clear(); + _deferredAccelerators.clear(); for (const auto& pair : _menuItems) { @@ -406,7 +408,9 @@ Accelerator& EventManager::connectAccelerator(int keyCode, unsigned int modifier } else { - return _emptyAccelerator; + rMessage() << "EventManager: Deferring shortcut for not-yet-registered command: " << command << std::endl; + _deferredAccelerators.emplace(command, accelerator); + return *accelerator; } auto result = _accelerators.emplace(command, accelerator); @@ -635,6 +639,50 @@ void EventManager::saveEventListToRegistry() shortcutSaver.visit(pair.first, *pair.second); } + + for (const auto& pair : _deferredAccelerators) + { + if (pair.second->isEmpty()) continue; + + shortcutSaver.visit(pair.first, *pair.second); + } +} + +void EventManager::connectDeferredAccelerators() +{ + auto it = _deferredAccelerators.begin(); + + while (it != _deferredAccelerators.end()) + { + const auto& command = it->first; + auto& accelerator = it->second; + + auto event = findEvent(command); + + if (!event->empty()) + { + accelerator->setEvent(event); + } + else if (GlobalCommandSystem().commandExists(command)) + { + accelerator->setStatement(command); + } + else + { + ++it; + continue; + } + + rMessage() << "EventManager: Resolved deferred shortcut for " << command << std::endl; + + _accelerators.emplace(command, accelerator); + + std::string acceleratorStr = accelerator->getString(true); + setMenuItemAccelerator(command, acceleratorStr); + setToolItemAccelerator(command, acceleratorStr); + + it = _deferredAccelerators.erase(it); + } } Accelerator& EventManager::findAccelerator(const std::string& key, const std::string& modifierStr) diff --git a/radiant/eventmanager/EventManager.h b/radiant/eventmanager/EventManager.h index f2393f866..657810cd7 100644 --- a/radiant/eventmanager/EventManager.h +++ b/radiant/eventmanager/EventManager.h @@ -32,6 +32,7 @@ class EventManager : // The command-to-accelerator map containing all registered shortcuts typedef std::map AcceleratorMap; AcceleratorMap _accelerators; + AcceleratorMap _deferredAccelerators; // The map of all registered events EventMap _events; @@ -92,6 +93,8 @@ class EventManager : void foreachEvent(IEventVisitor& eventVisitor) override; + void connectDeferredAccelerators() override; + // Tries to locate an accelerator, that is connected to the given command Accelerator& findAccelerator(wxKeyEvent& ev); bool handleKeyEvent(wxKeyEvent& keyEvent);