/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include "CommandManager.h" #include "IconManager.h" #include "ScriptTermDialog.h" #include "MainWindow.h" #include "QtViewPaneManager.h" CAutoRegisterCommandHelper* CAutoRegisterCommandHelper::s_pFirst = 0; CAutoRegisterCommandHelper* CAutoRegisterCommandHelper::s_pLast = 0; CAutoRegisterCommandHelper* CAutoRegisterCommandHelper::GetFirst() { return s_pFirst; } CAutoRegisterCommandHelper::CAutoRegisterCommandHelper(void(*registerFunc)(CEditorCommandManager &)) { m_registerFunc = registerFunc; m_pNext = 0; if (!s_pLast) { s_pFirst = this; } else { s_pLast->m_pNext = this; } s_pLast = this; } CEditorCommandManager::CEditorCommandManager() : m_bWarnDuplicate(true) {} void CEditorCommandManager::RegisterAutoCommands() { CAutoRegisterCommandHelper* pHelper = CAutoRegisterCommandHelper::GetFirst(); while (pHelper) { pHelper->m_registerFunc(*this); pHelper = pHelper->m_pNext; } } CEditorCommandManager::~CEditorCommandManager() { CommandTable::const_iterator iter = m_commands.begin(), end = m_commands.end(); for (; iter != end; ++iter) { if (iter->second.deleter) { iter->second.deleter(iter->second.pCommand); } else { delete iter->second.pCommand; } } m_commands.clear(); m_uiCommands.clear(); } string CEditorCommandManager::GetFullCommandName(const string& module, const string& name) { string fullName = module; fullName += "."; fullName += name; return fullName; } bool CEditorCommandManager::AddCommand(CCommand* pCommand, TPfnDeleter deleter) { assert(pCommand); string module = pCommand->GetModule(); string name = pCommand->GetName(); if (IsRegistered(module, name) && m_bWarnDuplicate) { QString errMsg; errMsg = QStringLiteral("Error: Command %1.%2 already registered!").arg(module.c_str(), name.c_str()); Warning(errMsg.toUtf8().data()); return false; } SCommandTableEntry entry; entry.pCommand = pCommand; entry.deleter = deleter; m_commands.insert( CommandTable::value_type(GetFullCommandName(module, name), entry)); return true; } bool CEditorCommandManager::UnregisterCommand(const char* module, const char* name) { string fullName = GetFullCommandName(module, name); CommandTable::iterator itr = m_commands.find(fullName); if (itr != m_commands.end()) { if (itr->second.deleter) { itr->second.deleter(itr->second.pCommand); } else { delete itr->second.pCommand; } m_commands.erase(itr); return true; } return false; } bool CEditorCommandManager::RegisterUICommand( const char* module, const char* name, const char* description, const char* example, const Functor0& functor, const CCommand0::SUIInfo& uiInfo) { bool ok = CommandManagerHelper::RegisterCommand(this, module, name, description, example, functor); if (ok == false) { return false; } return AttachUIInfo(GetFullCommandName(module, name), uiInfo); } bool CEditorCommandManager::AttachUIInfo(const char* fullCmdName, const CCommand0::SUIInfo& uiInfo) { CommandTable::iterator iter = m_commands.find(fullCmdName); if (iter == m_commands.end()) { return false; } if (iter->second.pCommand->CanBeUICommand() == false) { return false; } CCommand0* pCommand = static_cast(iter->second.pCommand); pCommand->m_uiInfo = uiInfo; if (pCommand->m_uiInfo.commandId == 0) { pCommand->m_uiInfo.commandId = GenNewCommandId(); } m_uiCommands.insert(UICommandTable::value_type(pCommand->m_uiInfo.commandId, pCommand)); if (uiInfo.iconFilename.empty() == false) { GetIEditor()->GetIconManager()->RegisterCommandIcon(uiInfo.iconFilename.c_str(), pCommand->m_uiInfo.commandId); } return true; } bool CEditorCommandManager::GetUIInfo(const string& module, const string& name, CCommand0::SUIInfo& uiInfo) const { string fullName = GetFullCommandName(module, name); return GetUIInfo(fullName, uiInfo); } bool CEditorCommandManager::GetUIInfo(const string& fullCmdName, CCommand0::SUIInfo& uiInfo) const { CommandTable::const_iterator iter = m_commands.find(fullCmdName); if (iter == m_commands.end()) { return false; } if (iter->second.pCommand->CanBeUICommand() == false) { return false; } CCommand0* pCommand = static_cast(iter->second.pCommand); uiInfo = pCommand->m_uiInfo; return true; } int CEditorCommandManager::GenNewCommandId() { static int uniqueId = CUSTOM_COMMAND_ID_FIRST; return uniqueId++; } QString CEditorCommandManager::Execute(const string& module, const string& name, const CCommand::CArgs& args) { string fullName = GetFullCommandName(module, name); CommandTable::iterator iter = m_commands.find(fullName); if (iter != m_commands.end()) { LogCommand(fullName, args); return ExecuteAndLogReturn(iter->second.pCommand, args); } else { QString errMsg; errMsg = QStringLiteral("Error: Trying to execute a unknown command, '%1'!").arg(fullName.c_str()); CryLogAlways(errMsg.toUtf8().data()); } return ""; } QString CEditorCommandManager::Execute(const string& cmdLine) { string cmdTxt, argsTxt; size_t argStart = cmdLine.find_first_of(' '); cmdTxt = cmdLine.substr(0, argStart); argsTxt = ""; if (argStart != string::npos) { argsTxt = cmdLine.substr(argStart + 1); argsTxt.Trim(); } CommandTable::iterator itr = m_commands.find(cmdTxt); if (itr != m_commands.end()) { CCommand::CArgs argList; GetArgsFromString(argsTxt, argList); LogCommand(cmdTxt, argList); return ExecuteAndLogReturn(itr->second.pCommand, argList); } else { QString errMsg; errMsg = QStringLiteral("Error: Trying to execute a unknown command, '%1'!").arg(cmdLine.c_str()); CryLogAlways(errMsg.toUtf8().data()); } return ""; } void CEditorCommandManager::Execute(int commandId) { UICommandTable::iterator iter = m_uiCommands.find(commandId); if (iter != m_uiCommands.end()) { LogCommand( GetFullCommandName(iter->second->GetModule(), iter->second->GetName()), CCommand::CArgs()); iter->second->Execute(CCommand::CArgs()); } else { QString errMsg; errMsg = QStringLiteral("Error: Trying to execute a unknown command of ID '%1'!").arg(commandId); CryLogAlways(errMsg.toUtf8().data()); } } void CEditorCommandManager::GetCommandList(std::vector& cmds) const { cmds.clear(); cmds.reserve(m_commands.size()); CommandTable::const_iterator iter = m_commands.begin(), end = m_commands.end(); for (; iter != end; ++iter) { cmds.push_back(iter->first); } std::sort(cmds.begin(), cmds.end()); } string CEditorCommandManager::AutoComplete(const string& substr) const { std::vector cmds; GetCommandList(cmds); // If substring is empty return first command. if (substr.empty() && (cmds.empty() == false)) { return cmds[0]; } size_t substrLen = substr.length(); for (size_t i = 0; i < cmds.size(); ++i) { size_t cmdLen = cmds[i].length(); if (cmdLen >= substrLen && !strncmp(cmds[i].c_str(), substr.c_str(), substrLen)) { if (substrLen == cmdLen) { ++i; if (i < cmds.size()) { return cmds[i]; } else { return cmds[i - 1]; } } return cmds[i]; } } // Not found return ""; } bool CEditorCommandManager::IsRegistered(const char* module, const char* name) const { string fullName = GetFullCommandName(module, name); CommandTable::const_iterator iter = m_commands.find(fullName); if (iter != m_commands.end()) { return true; } else { return false; } } bool CEditorCommandManager::IsRegistered(const char* cmdLine_) const { string cmdTxt, argsTxt, cmdLine(cmdLine_); size_t argStart = cmdLine.find_first_of(' '); cmdTxt = cmdLine.substr(0, argStart); CommandTable::const_iterator iter = m_commands.find(cmdTxt); if (iter != m_commands.end()) { return true; } else { return false; } } bool CEditorCommandManager::IsRegistered(int commandId) const { if (CUSTOM_COMMAND_ID_FIRST <= commandId && commandId < CUSTOM_COMMAND_ID_LAST) { UICommandTable::const_iterator iter = m_uiCommands.find(commandId); if (iter != m_uiCommands.end()) { return true; } } return false; } void CEditorCommandManager::SetCommandAvailableInScripting(const string& module, const string& name) { string fullName = GetFullCommandName(module, name); CommandTable::iterator iter = m_commands.find(fullName); if (iter != m_commands.end()) { iter->second.pCommand->SetAvailableInScripting(); } } bool CEditorCommandManager::IsCommandAvailableInScripting(const string& fullCmdName) const { CommandTable::const_iterator iter = m_commands.find(fullCmdName); if (iter != m_commands.end()) { return iter->second.pCommand->IsAvailableInScripting(); } return false; } bool CEditorCommandManager::IsCommandAvailableInScripting(const string& module, const string& name) const { string fullName = GetFullCommandName(module, name); return IsCommandAvailableInScripting(fullName); } void CEditorCommandManager::LogCommand(const string& fullCmdName, const CCommand::CArgs& args) const { string cmdLine = fullCmdName; for (int i = 0; i < args.GetArgCount(); ++i) { cmdLine += " "; bool bString = args.IsStringArg(i); if (bString) { cmdLine += "'"; } cmdLine += args.GetArg(i); if (bString) { cmdLine += "'"; } } CLogFile::WriteLine(cmdLine.c_str()); if (IsCommandAvailableInScripting(fullCmdName) == false) { return; } /// If this same command is also available in the script system, /// log this to the script terminal, too. // First, recreate a command line to be compatible to the script system. cmdLine = fullCmdName; cmdLine += "("; for (int i = 0; i < args.GetArgCount(); ++i) { bool bString = args.IsStringArg(i); if (bString) { cmdLine += "\""; } cmdLine += args.GetArg(i); if (bString) { cmdLine += "\""; } if (i < args.GetArgCount() - 1) { cmdLine += ","; } } cmdLine += ")"; // If it's not SandBox main editor (one case is the standalone material editor triggered by 3ds Max exporter), // we should not cast it into main editor for further operation. if (GetIEditor()->IsInMatEditMode()) { return; } // Then, register it to the terminal. QtViewPane* scriptTermPane = QtViewPaneManager::instance()->GetPane(SCRIPT_TERM_WINDOW_NAME); if (!scriptTermPane) { return; } CScriptTermDialog* pScriptTermDialog = qobject_cast(scriptTermPane->Widget()); if (pScriptTermDialog) { string text = "> "; text += cmdLine; text += "\r\n"; pScriptTermDialog->AppendText(text.c_str()); } } QString CEditorCommandManager::ExecuteAndLogReturn(CCommand* pCommand, const CCommand::CArgs& args) { const QString result = pCommand->Execute(args); const QString returnMsg = QString("Returned: %1").arg(result); CLogFile::WriteLine(returnMsg.toUtf8().constData()); return result; } void CEditorCommandManager::GetArgsFromString(const string& argsTxt, CCommand::CArgs& argList) { const char quoteSymbol = '\''; int curPos = 0; int prevPos = 0; string arg = argsTxt.Tokenize(" ", curPos); while (!arg.empty()) { if (arg[0] == quoteSymbol) // A special consideration for a quoted string { if (arg.length() < 2 || arg[arg.length() - 1] != quoteSymbol) { size_t openingQuotePos = argsTxt.find(quoteSymbol, prevPos); size_t closingQuotePos = argsTxt.find(quoteSymbol, curPos); if (closingQuotePos != string::npos) { arg = argsTxt.substr(openingQuotePos + 1, closingQuotePos - openingQuotePos - 1); size_t nextArgPos = argsTxt.find(' ', closingQuotePos + 1); curPos = nextArgPos != string::npos ? nextArgPos + 1 : argsTxt.length(); for (; curPos < argsTxt.length(); ++curPos) // Skip spaces. { if (argsTxt[curPos] != ' ') { break; } } } } else { arg = arg.substr(1, arg.length() - 2); } } argList.Add(arg.c_str()); prevPos = curPos; arg = argsTxt.Tokenize(" ", curPos); } }