CommandManager.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "EditorDefs.h"
  9. #include "CommandManager.h"
  10. #include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
  11. // Editor
  12. #include "QtViewPaneManager.h"
  13. // AzToolsFramework
  14. #include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
  15. CAutoRegisterCommandHelper* CAutoRegisterCommandHelper::s_pFirst = nullptr;
  16. CAutoRegisterCommandHelper* CAutoRegisterCommandHelper::s_pLast = nullptr;
  17. CAutoRegisterCommandHelper* CAutoRegisterCommandHelper::GetFirst()
  18. {
  19. return s_pFirst;
  20. }
  21. CAutoRegisterCommandHelper::CAutoRegisterCommandHelper(void(*registerFunc)(CEditorCommandManager &))
  22. {
  23. m_registerFunc = registerFunc;
  24. m_pNext = nullptr;
  25. if (!s_pLast)
  26. {
  27. s_pFirst = this;
  28. }
  29. else
  30. {
  31. s_pLast->m_pNext = this;
  32. }
  33. s_pLast = this;
  34. }
  35. CEditorCommandManager::CEditorCommandManager()
  36. : m_bWarnDuplicate(true) {}
  37. void CEditorCommandManager::RegisterAutoCommands()
  38. {
  39. CAutoRegisterCommandHelper* pHelper = CAutoRegisterCommandHelper::GetFirst();
  40. while (pHelper)
  41. {
  42. pHelper->m_registerFunc(*this);
  43. pHelper = pHelper->m_pNext;
  44. }
  45. }
  46. CEditorCommandManager::~CEditorCommandManager()
  47. {
  48. CommandTable::const_iterator iter = m_commands.begin(), end = m_commands.end();
  49. for (; iter != end; ++iter)
  50. {
  51. if (iter->second.deleter)
  52. {
  53. iter->second.deleter(iter->second.pCommand);
  54. }
  55. else
  56. {
  57. delete iter->second.pCommand;
  58. }
  59. }
  60. m_commands.clear();
  61. m_uiCommands.clear();
  62. }
  63. AZStd::string CEditorCommandManager::GetFullCommandName(const AZStd::string& module, const AZStd::string& name)
  64. {
  65. AZStd::string fullName = module;
  66. fullName += ".";
  67. fullName += name;
  68. return fullName;
  69. }
  70. bool CEditorCommandManager::AddCommand(CCommand* pCommand, TPfnDeleter deleter)
  71. {
  72. assert(pCommand);
  73. AZStd::string module = pCommand->GetModule();
  74. AZStd::string name = pCommand->GetName();
  75. if (IsRegistered(module.c_str(), name.c_str()) && m_bWarnDuplicate)
  76. {
  77. QString errMsg;
  78. errMsg = QStringLiteral("Error: Command %1.%2 already registered!").arg(module.c_str(), name.c_str());
  79. Warning(errMsg.toUtf8().data());
  80. return false;
  81. }
  82. SCommandTableEntry entry;
  83. entry.pCommand = pCommand;
  84. entry.deleter = deleter;
  85. m_commands.insert(
  86. CommandTable::value_type(GetFullCommandName(module, name),
  87. entry));
  88. return true;
  89. }
  90. bool CEditorCommandManager::UnregisterCommand(const char* module, const char* name)
  91. {
  92. AZStd::string fullName = GetFullCommandName(module, name);
  93. CommandTable::iterator itr = m_commands.find(fullName);
  94. if (itr != m_commands.end())
  95. {
  96. if (itr->second.deleter)
  97. {
  98. itr->second.deleter(itr->second.pCommand);
  99. }
  100. else
  101. {
  102. delete itr->second.pCommand;
  103. }
  104. m_commands.erase(itr);
  105. return true;
  106. }
  107. return false;
  108. }
  109. bool CEditorCommandManager::RegisterUICommand(
  110. const char* module,
  111. const char* name,
  112. const char* description,
  113. const char* example,
  114. const AZStd::function<void()>& functor,
  115. const CCommand0::SUIInfo& uiInfo)
  116. {
  117. bool ok = CommandManagerHelper::RegisterCommand(this, module, name, description, example, functor);
  118. if (ok == false)
  119. {
  120. return false;
  121. }
  122. return AttachUIInfo(GetFullCommandName(module, name).c_str(), uiInfo);
  123. }
  124. bool CEditorCommandManager::AttachUIInfo(const char* fullCmdName, const CCommand0::SUIInfo& uiInfo)
  125. {
  126. CommandTable::iterator iter = m_commands.find(fullCmdName);
  127. if (iter == m_commands.end())
  128. {
  129. return false;
  130. }
  131. if (iter->second.pCommand->CanBeUICommand() == false)
  132. {
  133. return false;
  134. }
  135. CCommand0* pCommand = static_cast<CCommand0*>(iter->second.pCommand);
  136. pCommand->m_uiInfo = uiInfo;
  137. if (pCommand->m_uiInfo.commandId == 0)
  138. {
  139. pCommand->m_uiInfo.commandId = GenNewCommandId();
  140. }
  141. m_uiCommands.insert(UICommandTable::value_type(pCommand->m_uiInfo.commandId, pCommand));
  142. return true;
  143. }
  144. bool CEditorCommandManager::GetUIInfo(const AZStd::string& module, const AZStd::string& name, CCommand0::SUIInfo& uiInfo) const
  145. {
  146. AZStd::string fullName = GetFullCommandName(module, name);
  147. return GetUIInfo(fullName, uiInfo);
  148. }
  149. bool CEditorCommandManager::GetUIInfo(const AZStd::string& fullCmdName, CCommand0::SUIInfo& uiInfo) const
  150. {
  151. CommandTable::const_iterator iter = m_commands.find(fullCmdName);
  152. if (iter == m_commands.end())
  153. {
  154. return false;
  155. }
  156. if (iter->second.pCommand->CanBeUICommand() == false)
  157. {
  158. return false;
  159. }
  160. CCommand0* pCommand = static_cast<CCommand0*>(iter->second.pCommand);
  161. uiInfo = pCommand->m_uiInfo;
  162. return true;
  163. }
  164. int CEditorCommandManager::GenNewCommandId()
  165. {
  166. static int uniqueId = CUSTOM_COMMAND_ID_FIRST;
  167. return uniqueId++;
  168. }
  169. QString CEditorCommandManager::Execute(const AZStd::string& module, const AZStd::string& name, const CCommand::CArgs& args)
  170. {
  171. AZStd::string fullName = GetFullCommandName(module, name);
  172. CommandTable::iterator iter = m_commands.find(fullName);
  173. if (iter != m_commands.end())
  174. {
  175. LogCommand(fullName, args);
  176. return ExecuteAndLogReturn(iter->second.pCommand, args);
  177. }
  178. else
  179. {
  180. CryLogAlways("Error: Trying to execute a unknown command, '%s'!", fullName.c_str());
  181. }
  182. return "";
  183. }
  184. QString CEditorCommandManager::Execute(const AZStd::string& cmdLine)
  185. {
  186. AZStd::string cmdTxt, argsTxt;
  187. size_t argStart = cmdLine.find_first_of(' ');
  188. cmdTxt = cmdLine.substr(0, argStart);
  189. argsTxt = "";
  190. if (argStart != AZStd::string::npos)
  191. {
  192. argsTxt = cmdLine.substr(argStart + 1);
  193. AZ::StringFunc::TrimWhiteSpace(argsTxt, true, true);
  194. }
  195. CommandTable::iterator itr = m_commands.find(cmdTxt);
  196. if (itr != m_commands.end())
  197. {
  198. CCommand::CArgs argList;
  199. GetArgsFromString(argsTxt, argList);
  200. LogCommand(cmdTxt, argList);
  201. return ExecuteAndLogReturn(itr->second.pCommand, argList);
  202. }
  203. else
  204. {
  205. CryLogAlways("Error: Trying to execute a unknown command, '%s'!", cmdLine.c_str());
  206. }
  207. return "";
  208. }
  209. void CEditorCommandManager::Execute(int commandId)
  210. {
  211. UICommandTable::iterator iter = m_uiCommands.find(commandId);
  212. if (iter != m_uiCommands.end())
  213. {
  214. LogCommand(
  215. GetFullCommandName(iter->second->GetModule(), iter->second->GetName()),
  216. CCommand::CArgs());
  217. iter->second->Execute(CCommand::CArgs());
  218. }
  219. else
  220. {
  221. CryLogAlways("Error: Trying to execute a unknown command of ID '%d'!", commandId);
  222. }
  223. }
  224. void CEditorCommandManager::GetCommandList(std::vector<AZStd::string>& cmds) const
  225. {
  226. cmds.clear();
  227. cmds.reserve(m_commands.size());
  228. CommandTable::const_iterator iter = m_commands.begin(), end = m_commands.end();
  229. for (; iter != end; ++iter)
  230. {
  231. cmds.push_back(iter->first);
  232. }
  233. std::sort(cmds.begin(), cmds.end());
  234. }
  235. AZStd::string CEditorCommandManager::AutoComplete(const AZStd::string& substr) const
  236. {
  237. std::vector<AZStd::string> cmds;
  238. GetCommandList(cmds);
  239. // If substring is empty return first command.
  240. if (substr.empty() && (cmds.empty() == false))
  241. {
  242. return cmds[0];
  243. }
  244. size_t substrLen = substr.length();
  245. for (size_t i = 0; i < cmds.size(); ++i)
  246. {
  247. size_t cmdLen = cmds[i].length();
  248. if (cmdLen >= substrLen && !strncmp(cmds[i].c_str(), substr.c_str(), substrLen))
  249. {
  250. if (substrLen == cmdLen)
  251. {
  252. ++i;
  253. if (i < cmds.size())
  254. {
  255. return cmds[i];
  256. }
  257. else
  258. {
  259. return cmds[i - 1];
  260. }
  261. }
  262. return cmds[i];
  263. }
  264. }
  265. // Not found
  266. return "";
  267. }
  268. bool CEditorCommandManager::IsRegistered(const char* module, const char* name) const
  269. {
  270. AZStd::string fullName = GetFullCommandName(module, name);
  271. CommandTable::const_iterator iter = m_commands.find(fullName);
  272. if (iter != m_commands.end())
  273. {
  274. return true;
  275. }
  276. else
  277. {
  278. return false;
  279. }
  280. }
  281. bool CEditorCommandManager::IsRegistered(const char* cmdLine_) const
  282. {
  283. AZStd::string cmdTxt, argsTxt, cmdLine(cmdLine_);
  284. size_t argStart = cmdLine.find_first_of(' ');
  285. cmdTxt = cmdLine.substr(0, argStart);
  286. CommandTable::const_iterator iter = m_commands.find(cmdTxt);
  287. if (iter != m_commands.end())
  288. {
  289. return true;
  290. }
  291. else
  292. {
  293. return false;
  294. }
  295. }
  296. bool CEditorCommandManager::IsRegistered(int commandId) const
  297. {
  298. if (CUSTOM_COMMAND_ID_FIRST <= commandId && commandId < CUSTOM_COMMAND_ID_LAST)
  299. {
  300. UICommandTable::const_iterator iter = m_uiCommands.find(commandId);
  301. if (iter != m_uiCommands.end())
  302. {
  303. return true;
  304. }
  305. }
  306. return false;
  307. }
  308. void CEditorCommandManager::SetCommandAvailableInScripting(const AZStd::string& module, const AZStd::string& name)
  309. {
  310. AZStd::string fullName = GetFullCommandName(module, name);
  311. CommandTable::iterator iter = m_commands.find(fullName);
  312. if (iter != m_commands.end())
  313. {
  314. iter->second.pCommand->SetAvailableInScripting();
  315. }
  316. }
  317. bool CEditorCommandManager::IsCommandAvailableInScripting(const AZStd::string& fullCmdName) const
  318. {
  319. CommandTable::const_iterator iter = m_commands.find(fullCmdName);
  320. if (iter != m_commands.end())
  321. {
  322. return iter->second.pCommand->IsAvailableInScripting();
  323. }
  324. return false;
  325. }
  326. bool CEditorCommandManager::IsCommandAvailableInScripting(const AZStd::string& module, const AZStd::string& name) const
  327. {
  328. AZStd::string fullName = GetFullCommandName(module, name);
  329. return IsCommandAvailableInScripting(fullName);
  330. }
  331. void CEditorCommandManager::LogCommand(const AZStd::string& fullCmdName, const CCommand::CArgs& args) const
  332. {
  333. AZStd::string cmdLine = fullCmdName;
  334. for (int i = 0; i < args.GetArgCount(); ++i)
  335. {
  336. cmdLine += " ";
  337. bool bString = args.IsStringArg(i);
  338. if (bString)
  339. {
  340. cmdLine += "'";
  341. }
  342. cmdLine += args.GetArg(i);
  343. if (bString)
  344. {
  345. cmdLine += "'";
  346. }
  347. }
  348. CLogFile::WriteLine(cmdLine.c_str());
  349. if (IsCommandAvailableInScripting(fullCmdName) == false)
  350. {
  351. return;
  352. }
  353. /// If this same command is also available in the script system,
  354. /// log this to the script terminal, too.
  355. // First, recreate a command line to be compatible to the script system.
  356. cmdLine = fullCmdName;
  357. cmdLine += "(";
  358. for (int i = 0; i < args.GetArgCount(); ++i)
  359. {
  360. bool bString = args.IsStringArg(i);
  361. if (bString)
  362. {
  363. cmdLine += "\"";
  364. }
  365. cmdLine += args.GetArg(i);
  366. if (bString)
  367. {
  368. cmdLine += "\"";
  369. }
  370. if (i < args.GetArgCount() - 1)
  371. {
  372. cmdLine += ",";
  373. }
  374. }
  375. cmdLine += ")";
  376. // Then, register it to the terminal.
  377. QtViewPane* scriptTermPane = QtViewPaneManager::instance()->GetPane(SCRIPT_TERM_WINDOW_NAME);
  378. if (!scriptTermPane)
  379. {
  380. return;
  381. }
  382. AzToolsFramework::CScriptTermDialog* pScriptTermDialog = qobject_cast<AzToolsFramework::CScriptTermDialog*>(scriptTermPane->Widget());
  383. if (pScriptTermDialog)
  384. {
  385. AZStd::string text = "> ";
  386. text += cmdLine;
  387. text += "\r\n";
  388. pScriptTermDialog->AppendText(text.c_str());
  389. }
  390. }
  391. QString CEditorCommandManager::ExecuteAndLogReturn(CCommand* pCommand, const CCommand::CArgs& args)
  392. {
  393. const QString result = pCommand->Execute(args);
  394. const QString returnMsg = QString("Returned: %1").arg(result);
  395. CLogFile::WriteLine(returnMsg.toUtf8().constData());
  396. return result;
  397. }
  398. void CEditorCommandManager::GetArgsFromString(const AZStd::string& argsTxt, CCommand::CArgs& argList)
  399. {
  400. const char quoteSymbol = '\'';
  401. size_t curPos = 0;
  402. size_t prevPos = 0;
  403. AZStd::vector<AZStd::string> tokens;
  404. AZ::StringFunc::Tokenize(argsTxt, tokens, ' ');
  405. for(AZStd::string& arg : tokens)
  406. {
  407. if (arg[0] == quoteSymbol) // A special consideration for a quoted string
  408. {
  409. if (arg.length() < 2 || arg[arg.length() - 1] != quoteSymbol)
  410. {
  411. size_t openingQuotePos = argsTxt.find(quoteSymbol, prevPos);
  412. size_t closingQuotePos = argsTxt.find(quoteSymbol, curPos);
  413. if (closingQuotePos != AZStd::string::npos)
  414. {
  415. arg = argsTxt.substr(openingQuotePos + 1, closingQuotePos - openingQuotePos - 1);
  416. size_t nextArgPos = argsTxt.find(' ', closingQuotePos + 1);
  417. curPos = nextArgPos != AZStd::string::npos ? nextArgPos + 1 : argsTxt.length();
  418. for (; curPos < argsTxt.length(); ++curPos) // Skip spaces.
  419. {
  420. if (argsTxt[curPos] != ' ')
  421. {
  422. break;
  423. }
  424. }
  425. }
  426. }
  427. else
  428. {
  429. arg = arg.substr(1, arg.length() - 2);
  430. }
  431. }
  432. argList.Add(arg.c_str());
  433. prevPos = curPos;
  434. }
  435. }