Util.cpp 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064
  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 <Atom/ImageProcessing/ImageObject.h>
  9. #include <Atom/ImageProcessing/ImageProcessingBus.h>
  10. #include <Atom/RPI.Edit/Common/AssetUtils.h>
  11. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  12. #include <AtomToolsFramework/AtomToolsFrameworkSystemRequestBus.h>
  13. #include <AtomToolsFramework/Util/Util.h>
  14. #include <AzCore/IO/ByteContainerStream.h>
  15. #include <AzCore/IO/SystemFile.h>
  16. #include <AzCore/Jobs/Algorithms.h>
  17. #include <AzCore/Jobs/JobFunction.h>
  18. #include <AzCore/Settings/SettingsRegistry.h>
  19. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  20. #include <AzCore/StringFunc/StringFunc.h>
  21. #include <AzCore/Utils/Utils.h>
  22. #include <AzCore/std/algorithm.h>
  23. #include <AzCore/std/sort.h>
  24. #include <AzCore/std/string/regex.h>
  25. #include <AzFramework/API/ApplicationAPI.h>
  26. #include <AzFramework/FileFunc/FileFunc.h>
  27. #include <AzQtComponents/Components/Widgets/FileDialog.h>
  28. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  29. #include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
  30. #include <AzToolsFramework/API/EditorWindowRequestBus.h>
  31. #include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
  32. #include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
  33. #include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
  34. #include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryUtils.h>
  35. #include <AzToolsFramework/AssetDatabase/AssetDatabaseConnection.h>
  36. #include <AzToolsFramework/ToolsComponents/EditorAssetMimeDataContainer.h>
  37. AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
  38. #include <QApplication>
  39. #include <QDialog>
  40. #include <QDialogButtonBox>
  41. #include <QListWidget>
  42. #include <QMenu>
  43. #include <QMessageBox>
  44. #include <QMimeData>
  45. #include <QProcess>
  46. #include <QTimer>
  47. #include <QVBoxLayout>
  48. AZ_POP_DISABLE_WARNING
  49. namespace AtomToolsFramework
  50. {
  51. void LoadImageAsync(const AZStd::string& path, LoadImageAsyncCallback callback)
  52. {
  53. AZ::Job* job = AZ::CreateJobFunction(
  54. [path, callback]()
  55. {
  56. ImageProcessingAtom::IImageObjectPtr imageObject;
  57. ImageProcessingAtom::ImageProcessingRequestBus::BroadcastResult(
  58. imageObject, &ImageProcessingAtom::ImageProcessingRequests::LoadImagePreview, path);
  59. if (imageObject)
  60. {
  61. AZ::u8* imageBuf = nullptr;
  62. AZ::u32 pitch = 0;
  63. AZ::u32 mip = 0;
  64. imageObject->GetImagePointer(mip, imageBuf, pitch);
  65. const AZ::u32 width = imageObject->GetWidth(mip);
  66. const AZ::u32 height = imageObject->GetHeight(mip);
  67. QImage image(imageBuf, width, height, pitch, QImage::Format_RGBA8888);
  68. if (callback)
  69. {
  70. callback(image);
  71. }
  72. }
  73. },
  74. true);
  75. job->Start();
  76. }
  77. QWidget* GetToolMainWindow()
  78. {
  79. QWidget* mainWindow = QApplication::activeWindow();
  80. AzToolsFramework::EditorWindowRequestBus::BroadcastResult(
  81. mainWindow, &AzToolsFramework::EditorWindowRequestBus::Events::GetAppMainWindow);
  82. return mainWindow;
  83. }
  84. AZStd::string GetFirstNonEmptyString(const AZStd::vector<AZStd::string>& values, const AZStd::string& defaultValue)
  85. {
  86. for (const auto& value : values)
  87. {
  88. if (!value.empty())
  89. {
  90. return value;
  91. }
  92. }
  93. return defaultValue;
  94. }
  95. void ReplaceSymbolsInContainer(const AZStd::string& findText, const AZStd::string& replaceText, AZStd::vector<AZStd::string>& container)
  96. {
  97. const AZStd::regex findRegex(findText);
  98. for (auto& sourceText : container)
  99. {
  100. sourceText = AZStd::regex_replace(sourceText, findRegex, replaceText);
  101. }
  102. }
  103. void ReplaceSymbolsInContainer(
  104. const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& substitutionSymbols, AZStd::vector<AZStd::string>& container)
  105. {
  106. for (const auto& substitutionSymbolPair : substitutionSymbols)
  107. {
  108. ReplaceSymbolsInContainer(substitutionSymbolPair.first, substitutionSymbolPair.second, container);
  109. }
  110. }
  111. AZStd::string GetSymbolNameFromText(const AZStd::string& text)
  112. {
  113. QString symbolName(text.c_str());
  114. // Remove all leading whitespace
  115. symbolName.replace(QRegExp("^\\s+"), "");
  116. // Remove all trailing whitespace
  117. symbolName.replace(QRegExp("\\s+$"), "");
  118. // Replace non alphanumeric characters with _
  119. symbolName.replace(QRegExp("[^a-zA-Z\\d]"), "_");
  120. // Insert a _ between a lowercase or numeric character followed by an uppercase character
  121. symbolName.replace(QRegExp("([a-z\\d])([A-Z])"), "\\1_\\2");
  122. // Insert an underscore at the beginning of the string if it starts with a digit
  123. symbolName.replace(QRegExp("^(\\d)"), "_\\1");
  124. // Replace every sequence of whitespace characters with underscores
  125. symbolName.replace(QRegExp("\\s+"), "_");
  126. // Replace Sequences of _ with a single _
  127. symbolName.replace(QRegExp("_+"), "_");
  128. return symbolName.toLower().toUtf8().constData();
  129. }
  130. AZStd::string GetDisplayNameFromText(const AZStd::string& text)
  131. {
  132. QString displayName(text.c_str());
  133. // Remove all leading whitespace
  134. displayName.replace(QRegExp("^\\s+"), "");
  135. // Remove all trailing whitespace
  136. displayName.replace(QRegExp("\\s+$"), "");
  137. // Replace non alphanumeric characters with space
  138. displayName.replace(QRegExp("[^a-zA-Z\\d]"), " ");
  139. // Insert a space between a lowercase or numeric character followed by an uppercase character
  140. displayName.replace(QRegExp("([a-z\\d])([A-Z])"), "\\1 \\2");
  141. // Tokenize the string where separated by whitespace
  142. QStringList displayNameParts = displayName.split(QRegExp("\\s"), Qt::SkipEmptyParts);
  143. for (QString& part : displayNameParts)
  144. {
  145. // Capitalize the first character of every token
  146. part.replace(0, 1, part[0].toUpper());
  147. }
  148. // Recombine all of the strings separated by a single space
  149. return displayNameParts.join(" ").toUtf8().constData();
  150. }
  151. AZStd::string GetDisplayNameFromPath(const AZStd::string& path)
  152. {
  153. QFileInfo fileInfo(path.c_str());
  154. return GetDisplayNameFromText(fileInfo.baseName().toUtf8().constData());
  155. }
  156. bool GetStringListFromDialog(
  157. AZStd::vector<AZStd::string>& selectedStrings,
  158. const AZStd::vector<AZStd::string>& availableStrings,
  159. const AZStd::string& title,
  160. const bool multiSelect)
  161. {
  162. // Create a dialog that will display a list of string options and prompt the user for input.
  163. QDialog dialog(GetToolMainWindow());
  164. dialog.setModal(true);
  165. dialog.setWindowTitle(title.c_str());
  166. dialog.setLayout(new QVBoxLayout());
  167. // Fill the list widget with all of the available strings for the user to select.
  168. QListWidget listWidget(&dialog);
  169. listWidget.setSelectionMode(multiSelect ? QAbstractItemView::ExtendedSelection : QAbstractItemView::SingleSelection);
  170. for (const auto& availableString : availableStrings)
  171. {
  172. listWidget.addItem(availableString.c_str());
  173. }
  174. listWidget.sortItems();
  175. // The selected strings vector already has items then attempt to select those in the list.
  176. for (const auto& selection : selectedStrings)
  177. {
  178. for (auto item : listWidget.findItems(selection.c_str(), Qt::MatchExactly))
  179. {
  180. item->setSelected(true);
  181. }
  182. }
  183. // Create the button box that will provide default dialog buttons to allow the user to accept or reject their selections.
  184. QDialogButtonBox buttonBox(&dialog);
  185. buttonBox.setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
  186. QObject::connect(&buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
  187. QObject::connect(&buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
  188. // Add the list widget and button box to the layout so they appear and the dialog.
  189. dialog.layout()->addWidget(&listWidget);
  190. dialog.layout()->addWidget(&buttonBox);
  191. // Temporarily forcing a fixed size before showing it to compensate for window management centering and resizing the dialog.
  192. dialog.setFixedSize(400, 200);
  193. dialog.show();
  194. dialog.setMinimumSize(0, 0);
  195. dialog.setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
  196. // If the user accepts their selections then the selected strings director will be cleared and refilled with them.
  197. if (dialog.exec() == QDialog::Accepted)
  198. {
  199. selectedStrings.clear();
  200. for (auto item : listWidget.selectedItems())
  201. {
  202. selectedStrings.push_back(item->text().toUtf8().constData());
  203. }
  204. return true;
  205. }
  206. return false;
  207. }
  208. AZStd::string GetFileFilterFromSupportedExtensions(const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& supportedExtensions)
  209. {
  210. // Build an ordered table of all of the supported extensions and their display names. This will be transformed into the file filter
  211. // that is displayed in the dialog.
  212. AZStd::map<AZStd::string, AZStd::set<AZStd::string>> orderedExtensions;
  213. for (const auto& extensionPair : supportedExtensions)
  214. {
  215. if (!extensionPair.second.empty())
  216. {
  217. // Sift all of the extensions into display name groups. If no display name was provided then we will use a default one.
  218. const auto& group = !extensionPair.first.empty() ? extensionPair.first : "Supported";
  219. // Convert the extension into a wild card.
  220. orderedExtensions[group].insert("*." + extensionPair.second);
  221. }
  222. }
  223. // Transform each of the ordered extension groups into individual file dialog filters that represent one or more extensions.
  224. AZStd::vector<AZStd::string> individualFilters;
  225. for (const auto& orderedExtensionPair : orderedExtensions)
  226. {
  227. AZStd::string combinedExtensions;
  228. AZ::StringFunc::Join(combinedExtensions, orderedExtensionPair.second, " ");
  229. individualFilters.push_back(orderedExtensionPair.first + " (" + combinedExtensions + ")");
  230. }
  231. // Combine all of the individual filters into a single expression that can be used directly with the file dialog.
  232. AZStd::string combinedFilters;
  233. AZ::StringFunc::Join(combinedFilters, individualFilters, ";;");
  234. return combinedFilters;
  235. }
  236. AZStd::string GetFirstValidSupportedExtension(const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& supportedExtensions)
  237. {
  238. for (const auto& extensionPair : supportedExtensions)
  239. {
  240. if (!extensionPair.second.empty())
  241. {
  242. return extensionPair.second;
  243. }
  244. }
  245. return AZStd::string();
  246. }
  247. AZStd::string GetFirstMatchingSupportedExtension(
  248. const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& supportedExtensions, const AZStd::string& path)
  249. {
  250. for (const auto& extensionPair : supportedExtensions)
  251. {
  252. if (!extensionPair.second.empty() && path.ends_with(extensionPair.second))
  253. {
  254. return extensionPair.second;
  255. }
  256. }
  257. return AZStd::string();
  258. }
  259. AZStd::string GetSaveFilePathFromDialog(
  260. const AZStd::string& initialPath,
  261. const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& supportedExtensions,
  262. const AZStd::string& title)
  263. {
  264. // Build the file dialog filter from all of the supported extensions.
  265. const auto& combinedFilters = GetFileFilterFromSupportedExtensions(supportedExtensions);
  266. // If no valid extensions were provided then return immediately.
  267. if (combinedFilters.empty())
  268. {
  269. QMessageBox::critical(GetToolMainWindow(), "Error", QString("No supported extensions were specified."));
  270. return AZStd::string();
  271. }
  272. // Remove any aliasing from the initial path to feed to the file dialog.
  273. AZStd::string displayedPath = GetPathWithoutAlias(initialPath);
  274. // If the display name is empty or invalid then build a unique default display name using the first supported extension.
  275. if (displayedPath.empty())
  276. {
  277. displayedPath = GetUniqueUntitledFilePath(GetFirstValidSupportedExtension(supportedExtensions));
  278. }
  279. // Prompt the user to select a save file name using the input path and the list of filtered extensions.
  280. const QFileInfo selectedFileInfo(AzQtComponents::FileDialog::GetSaveFileName(
  281. GetToolMainWindow(), QObject::tr("Save %1").arg(title.c_str()), displayedPath.c_str(), combinedFilters.c_str()));
  282. // If the returned path is empty this means that the user cancelled or escaped from the dialog and is canceling the operation.
  283. if (selectedFileInfo.absoluteFilePath().isEmpty())
  284. {
  285. return AZStd::string();
  286. }
  287. // Find the supported extension corresponding to the user selection.
  288. const auto& selectedExtension =
  289. GetFirstMatchingSupportedExtension(supportedExtensions, selectedFileInfo.absoluteFilePath().toUtf8().constData());
  290. // If the selected path does not match any of the supported extensions consider it invalid and return.
  291. if (selectedExtension.empty())
  292. {
  293. QMessageBox::critical(GetToolMainWindow(), "Error", QString("File name does not match supported extension."));
  294. return AZStd::string();
  295. }
  296. // Reconstruct the path to compensate for known problems with the file dialog and complex extensions containing multiple "." like
  297. // *.lightingpreset.azasset
  298. return QFileInfo(QString("%1/%2.%3").arg(selectedFileInfo.absolutePath()).arg(selectedFileInfo.baseName()).arg(selectedExtension.c_str()))
  299. .absoluteFilePath().toUtf8().constData();
  300. }
  301. AZStd::vector<AZStd::string> GetOpenFilePathsFromDialog(
  302. const AZStd::vector<AZStd::string>& selectedFilePaths,
  303. const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& supportedExtensions,
  304. const AZStd::string& title,
  305. const bool multiSelect)
  306. {
  307. // Removing aliases from all incoming paths because the asset selection model does not recognize them.
  308. AZStd::vector<AZStd::string> selectedFilePathsWithoutAliases = selectedFilePaths;
  309. for (auto& path : selectedFilePathsWithoutAliases)
  310. {
  311. path = GetPathWithoutAlias(path);
  312. }
  313. // Create a custom filter function to plug into the asset selection model. The filter function will only display source assets
  314. // matching one of the supported extensions. It will also ignore files in the cache folder, usually intermediate assets. This is
  315. // much faster than the previous iteration using regular expressions.
  316. auto filterFn = [&](const AssetBrowserEntry* entry)
  317. {
  318. if (entry->GetEntryType() != AssetBrowserEntry::AssetEntryType::Source)
  319. {
  320. return false;
  321. }
  322. const auto& path = entry->GetFullPath();
  323. return !IsPathIgnored(path) &&
  324. AZStd::any_of(
  325. supportedExtensions.begin(),
  326. supportedExtensions.end(),
  327. [&](const auto& extensionPair)
  328. {
  329. return path.ends_with(AZStd::fixed_string<32>::format(".%s", extensionPair.second.c_str()));
  330. });
  331. };
  332. AssetSelectionModel selection;
  333. selection.SetDisplayFilter(FilterConstType(new CustomFilter(filterFn)));
  334. selection.SetSelectionFilter(FilterConstType(new CustomFilter(filterFn)));
  335. selection.SetTitle(title.c_str());
  336. selection.SetMultiselect(multiSelect);
  337. selection.SetSelectedFilePaths(selectedFilePathsWithoutAliases);
  338. AzToolsFramework::AssetBrowser::AssetBrowserComponentRequestBus::Broadcast(
  339. &AzToolsFramework::AssetBrowser::AssetBrowserComponentRequests::PickAssets, selection, GetToolMainWindow());
  340. // Return absolute paths for all results.
  341. AZStd::vector<AZStd::string> results;
  342. results.reserve(selection.GetResults().size());
  343. for (const auto& result : selection.GetResults())
  344. {
  345. results.push_back(result->GetFullPath());
  346. }
  347. return results;
  348. }
  349. AZStd::string GetUniqueFilePath(const AZStd::string& initialPath)
  350. {
  351. int counter = 0;
  352. QFileInfo fileInfo(initialPath.c_str());
  353. const QString extension = "." + fileInfo.completeSuffix();
  354. const QString basePathAndName = fileInfo.absoluteFilePath().left(fileInfo.absoluteFilePath().size() - extension.size());
  355. while (fileInfo.exists())
  356. {
  357. fileInfo = QString("%1_%2%3").arg(basePathAndName).arg(++counter).arg(extension);
  358. }
  359. return fileInfo.absoluteFilePath().toUtf8().constData();
  360. }
  361. AZStd::string GetUniqueUntitledFilePath(const AZStd::string& extension)
  362. {
  363. return GetUniqueFilePath(AZStd::string::format("%s/Assets/untitled.%s", AZ::Utils::GetProjectPath().c_str(), extension.c_str()));
  364. }
  365. bool ValidateDocumentPath(AZStd::string& path)
  366. {
  367. if (path.empty())
  368. {
  369. return false;
  370. }
  371. path = GetPathWithoutAlias(path);
  372. if (!AzFramework::StringFunc::Path::Normalize(path))
  373. {
  374. return false;
  375. }
  376. if (AzFramework::StringFunc::Path::IsRelative(path.c_str()))
  377. {
  378. return false;
  379. }
  380. if (!IsDocumentPathInSupportedFolder(path))
  381. {
  382. return false;
  383. }
  384. if (!IsDocumentPathEditable(path))
  385. {
  386. return false;
  387. }
  388. return true;
  389. }
  390. bool IsDocumentPathInSupportedFolder(const AZStd::string& path)
  391. {
  392. const auto& fullPath = GetPathWithoutAlias(path);
  393. const AZ::IO::FixedMaxPath assetPath = AZ::IO::PathView(fullPath).LexicallyNormal();
  394. for (const auto& assetFolder : GetSupportedSourceFolders())
  395. {
  396. // Check if the path is relative to the asset folder
  397. if (assetPath.IsRelativeTo(AZ::IO::PathView(assetFolder)))
  398. {
  399. return true;
  400. }
  401. }
  402. return false;
  403. }
  404. bool IsDocumentPathEditable(const AZStd::string& path)
  405. {
  406. bool result = true;
  407. AtomToolsFrameworkSystemRequestBus::BroadcastResult(result, &AtomToolsFrameworkSystemRequestBus::Events::IsPathEditable, path);
  408. return result;
  409. }
  410. bool IsDocumentPathPreviewable(const AZStd::string& path)
  411. {
  412. bool result = true;
  413. AtomToolsFrameworkSystemRequestBus::BroadcastResult(result, &AtomToolsFrameworkSystemRequestBus::Events::IsPathPreviewable, path);
  414. return result;
  415. }
  416. bool LaunchTool(const QString& baseName, const QStringList& arguments)
  417. {
  418. AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath();
  419. AZ_Assert(!engineRoot.empty(), "Cannot query Engine Path");
  420. AZ::IO::FixedMaxPath launchPath =
  421. AZ::IO::FixedMaxPath(AZ::Utils::GetExecutableDirectory()) / (baseName + AZ_TRAIT_OS_EXECUTABLE_EXTENSION).toUtf8().constData();
  422. return QProcess::startDetached(launchPath.c_str(), arguments, engineRoot.c_str());
  423. }
  424. AZStd::string GetWatchFolder(const AZStd::string& sourcePath)
  425. {
  426. bool relativePathFound = false;
  427. AZStd::string relativePath;
  428. AZStd::string relativePathFolder;
  429. // GenerateRelativeSourcePath is necessary when saving new files because it allows us to get the info for files that may not exist yet.
  430. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  431. relativePathFound,
  432. &AzToolsFramework::AssetSystem::AssetSystemRequest::GenerateRelativeSourcePath,
  433. sourcePath,
  434. relativePath,
  435. relativePathFolder);
  436. return relativePathFolder;
  437. }
  438. AZStd::string GetPathToExteralReference(const AZStd::string& exportPath, const AZStd::string& referencePath)
  439. {
  440. // An empty reference path signifies that there is no external reference and we can return immediately.
  441. if (referencePath.empty())
  442. {
  443. return {};
  444. }
  445. // Path aliases should be supported wherever possible to allow referencing files between different gems and projects. De-alias the
  446. // paths to compare them and attempt to generate a relative path.
  447. AZ::IO::FixedMaxPath exportPathWithoutAlias;
  448. AZ::IO::FileIOBase::GetInstance()->ReplaceAlias(exportPathWithoutAlias, AZ::IO::PathView{ exportPath });
  449. const AZ::IO::PathView exportFolder = exportPathWithoutAlias.ParentPath();
  450. AZ::IO::FixedMaxPath referencePathWithoutAlias;
  451. AZ::IO::FileIOBase::GetInstance()->ReplaceAlias(referencePathWithoutAlias, AZ::IO::PathView{ referencePath });
  452. // If both paths are contained underneath the same watch folder hierarchy then attempt to construct a relative path between them.
  453. if (GetWatchFolder(exportPath) == GetWatchFolder(referencePath))
  454. {
  455. const auto relativePath = referencePathWithoutAlias.LexicallyRelative(exportFolder);
  456. if (!relativePath.empty())
  457. {
  458. return relativePath.StringAsPosix();
  459. }
  460. }
  461. // If a relative path could not be constructed from the export path to the reference path then return the aliased path for the
  462. // reference.
  463. return GetPathWithAlias(referencePath);
  464. }
  465. bool SaveSettingsToFile(const AZ::IO::FixedMaxPath& savePath, const AZStd::vector<AZStd::string>& filters)
  466. {
  467. auto registry = AZ::SettingsRegistry::Get();
  468. if (registry == nullptr)
  469. {
  470. AZ_Warning("AtomToolsFramework", false, "Unable to access global settings registry.");
  471. return false;
  472. }
  473. AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
  474. dumperSettings.m_prettifyOutput = true;
  475. dumperSettings.m_includeFilter = [filters](AZStd::string_view path)
  476. {
  477. for (const auto& filter : filters)
  478. {
  479. if (filter.starts_with(path.substr(0, filter.size())))
  480. {
  481. return true;
  482. }
  483. }
  484. return false;
  485. };
  486. AZStd::string stringBuffer;
  487. AZ::IO::ByteContainerStream stringStream(&stringBuffer);
  488. if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(*registry, "", stringStream, dumperSettings))
  489. {
  490. AZ_Warning("AtomToolsFramework", false, R"(Unable to save changes to the registry file at "%s"\n)", savePath.c_str());
  491. return false;
  492. }
  493. if (stringBuffer.empty())
  494. {
  495. return false;
  496. }
  497. bool saved = false;
  498. constexpr auto configurationMode =
  499. AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
  500. if (AZ::IO::SystemFile outputFile; outputFile.Open(savePath.c_str(), configurationMode))
  501. {
  502. saved = outputFile.Write(stringBuffer.c_str(), stringBuffer.size()) == stringBuffer.size();
  503. }
  504. AZ_Warning("AtomToolsFramework", saved, R"(Unable to save registry file to path "%s"\n)", savePath.c_str());
  505. return saved;
  506. }
  507. AZStd::string GetPathWithoutAlias(const AZStd::string& path)
  508. {
  509. AZ::IO::FixedMaxPath pathWithoutAlias;
  510. AZ::IO::FileIOBase::GetInstance()->ReplaceAlias(pathWithoutAlias, AZ::IO::PathView{ path });
  511. return pathWithoutAlias.StringAsPosix();
  512. }
  513. AZStd::string GetPathWithAlias(const AZStd::string& path)
  514. {
  515. AZ::IO::FixedMaxPath pathWithAlias;
  516. AZ::IO::FileIOBase::GetInstance()->ConvertToAlias(pathWithAlias, AZ::IO::PathView{ path });
  517. return pathWithAlias.StringAsPosix();
  518. }
  519. AZStd::set<AZStd::string> GetPathsFromMimeData(const QMimeData* mimeData)
  520. {
  521. AZStd::set<AZStd::string> paths;
  522. if (!mimeData)
  523. {
  524. return paths;
  525. }
  526. if (mimeData->hasFormat(AzToolsFramework::EditorAssetMimeDataContainer::GetMimeType()))
  527. {
  528. AzToolsFramework::EditorAssetMimeDataContainer container;
  529. if (container.FromMimeData(mimeData))
  530. {
  531. for (const auto& asset : container.m_assets)
  532. {
  533. AZStd::string path = AZ::RPI::AssetUtils::GetSourcePathByAssetId(asset.m_assetId);
  534. if (ValidateDocumentPath(path))
  535. {
  536. paths.insert(path);
  537. }
  538. }
  539. }
  540. }
  541. AZStd::vector<const AzToolsFramework::AssetBrowser::AssetBrowserEntry*> entries;
  542. if (AzToolsFramework::AssetBrowser::Utils::FromMimeData(mimeData, entries))
  543. {
  544. for (const auto entry : entries)
  545. {
  546. AZStd::string path = entry->GetFullPath();
  547. if (ValidateDocumentPath(path))
  548. {
  549. paths.insert(path);
  550. }
  551. }
  552. }
  553. for (const auto& url : mimeData->urls())
  554. {
  555. if (url.isLocalFile())
  556. {
  557. AZStd::string path = url.toLocalFile().toUtf8().constData();
  558. if (ValidateDocumentPath(path))
  559. {
  560. paths.insert(path);
  561. }
  562. }
  563. }
  564. return paths;
  565. }
  566. AZStd::string GetAbsolutePathForSourceAsset(const AZStd::string& path)
  567. {
  568. bool found = false;
  569. AZ::Data::AssetInfo sourceInfo;
  570. AZStd::string rootFolder;
  571. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  572. found, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, path.c_str(), sourceInfo, rootFolder);
  573. if (found)
  574. {
  575. const AZ::IO::Path result = AZ::IO::Path(rootFolder) / sourceInfo.m_relativePath;
  576. if (!result.empty())
  577. {
  578. return result.LexicallyNormal().String();
  579. }
  580. }
  581. return path;
  582. }
  583. AZStd::vector<AZStd::string> GetPathsForAssetSourceDependencies(const AZ::Data::AssetInfo& sourceInfo)
  584. {
  585. AzToolsFramework::AssetDatabase::AssetDatabaseConnection assetDatabaseConnection;
  586. assetDatabaseConnection.OpenDatabase();
  587. AzToolsFramework::AssetDatabase::SourceDatabaseEntry sourceEntry;
  588. assetDatabaseConnection.QuerySourceBySourceName(
  589. sourceInfo.m_relativePath.c_str(),
  590. [&sourceEntry](const AzToolsFramework::AssetDatabase::SourceDatabaseEntry& entry)
  591. {
  592. sourceEntry = entry;
  593. return false;
  594. });
  595. if (sourceEntry.m_sourceGuid.IsNull())
  596. {
  597. return {};
  598. }
  599. AZStd::vector<AZStd::string> sourcePaths;
  600. assetDatabaseConnection.QueryDependsOnSourceBySourceDependency(
  601. sourceEntry.m_sourceGuid,
  602. AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_Any,
  603. [&sourcePaths, &assetDatabaseConnection](AzToolsFramework::AssetDatabase::SourceFileDependencyEntry& entry)
  604. {
  605. AZStd::string dependencyName = entry.m_dependsOnSource.GetPath();
  606. if (entry.m_dependsOnSource.IsUuid())
  607. {
  608. assetDatabaseConnection.QuerySourceBySourceGuid(
  609. entry.m_dependsOnSource.GetUuid(),
  610. [&dependencyName](AzToolsFramework::AssetDatabase::SourceDatabaseEntry& entry)
  611. {
  612. dependencyName = entry.m_sourceName;
  613. return false;
  614. });
  615. }
  616. if (!dependencyName.empty())
  617. {
  618. sourcePaths.emplace_back(GetAbsolutePathForSourceAsset(dependencyName));
  619. }
  620. return true;
  621. });
  622. assetDatabaseConnection.CloseDatabase();
  623. AZStd::sort(sourcePaths.begin(), sourcePaths.end());
  624. sourcePaths.erase(AZStd::unique(sourcePaths.begin(), sourcePaths.end()), sourcePaths.end());
  625. return sourcePaths;
  626. }
  627. AZStd::vector<AZStd::string> GetPathsForAssetSourceDependenciesById(const AZ::Data::AssetId& assetId)
  628. {
  629. bool found = false;
  630. AZ::Data::AssetInfo sourceInfo;
  631. AZStd::string watchFolder;
  632. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  633. found, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourceUUID, assetId.m_guid, sourceInfo, watchFolder);
  634. return GetPathsForAssetSourceDependencies(sourceInfo);
  635. }
  636. AZStd::vector<AZStd::string> GetPathsForAssetSourceDependenciesByPath(const AZStd::string& sourcePath)
  637. {
  638. bool found = false;
  639. AZ::Data::AssetInfo sourceInfo;
  640. AZStd::string watchFolder;
  641. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  642. found,
  643. &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath,
  644. GetPathWithoutAlias(sourcePath).c_str(),
  645. sourceInfo,
  646. watchFolder);
  647. return GetPathsForAssetSourceDependencies(sourceInfo);
  648. }
  649. AZStd::vector<AZStd::string> GetPathsForAssetSourceDependents(const AZ::Data::AssetInfo& sourceInfo)
  650. {
  651. AzToolsFramework::AssetDatabase::AssetDatabaseConnection assetDatabaseConnection;
  652. assetDatabaseConnection.OpenDatabase();
  653. AzToolsFramework::AssetDatabase::SourceDatabaseEntry sourceEntry;
  654. assetDatabaseConnection.QuerySourceBySourceName(
  655. sourceInfo.m_relativePath.c_str(),
  656. [&sourceEntry](const AzToolsFramework::AssetDatabase::SourceDatabaseEntry& entry)
  657. {
  658. sourceEntry = entry;
  659. return false;
  660. });
  661. if (sourceEntry.m_sourceGuid.IsNull())
  662. {
  663. return {};
  664. }
  665. AZStd::string scanFolderPath;
  666. assetDatabaseConnection.QueryScanFolderByScanFolderID(
  667. sourceEntry.m_scanFolderPK,
  668. [&scanFolderPath](const AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry& entry)
  669. {
  670. scanFolderPath = entry.m_scanFolder;
  671. return false;
  672. });
  673. auto absolutePath = AZ::IO::Path(scanFolderPath) / sourceEntry.m_sourceName;
  674. AZStd::vector<AZStd::string> sourcePaths;
  675. assetDatabaseConnection.QuerySourceDependencyByDependsOnSource(
  676. sourceEntry.m_sourceGuid,
  677. sourceEntry.m_sourceName.c_str(),
  678. absolutePath.FixedMaxPathStringAsPosix().c_str(),
  679. AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_Any,
  680. [&sourcePaths, &assetDatabaseConnection](AzToolsFramework::AssetDatabase::SourceFileDependencyEntry& entry)
  681. {
  682. AZStd::string sourceName;
  683. assetDatabaseConnection.QuerySourceBySourceGuid(
  684. entry.m_sourceGuid,
  685. [&sourceName](AzToolsFramework::AssetDatabase::SourceDatabaseEntry& entry)
  686. {
  687. sourceName = entry.m_sourceName;
  688. return false;
  689. });
  690. if (!sourceName.empty())
  691. {
  692. sourcePaths.emplace_back(GetAbsolutePathForSourceAsset(sourceName));
  693. }
  694. return true;
  695. });
  696. assetDatabaseConnection.CloseDatabase();
  697. AZStd::sort(sourcePaths.begin(), sourcePaths.end());
  698. sourcePaths.erase(AZStd::unique(sourcePaths.begin(), sourcePaths.end()), sourcePaths.end());
  699. return sourcePaths;
  700. }
  701. AZStd::vector<AZStd::string> GetPathsForAssetSourceDependentsById(const AZ::Data::AssetId& assetId)
  702. {
  703. bool found = false;
  704. AZ::Data::AssetInfo sourceInfo;
  705. AZStd::string watchFolder;
  706. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  707. found, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourceUUID, assetId.m_guid, sourceInfo, watchFolder);
  708. return GetPathsForAssetSourceDependents(sourceInfo);
  709. }
  710. AZStd::vector<AZStd::string> GetPathsForAssetSourceDependentsByPath(const AZStd::string& sourcePath)
  711. {
  712. bool found = false;
  713. AZ::Data::AssetInfo sourceInfo;
  714. AZStd::string watchFolder;
  715. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  716. found,
  717. &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath,
  718. GetPathWithoutAlias(sourcePath).c_str(),
  719. sourceInfo,
  720. watchFolder);
  721. return GetPathsForAssetSourceDependents(sourceInfo);
  722. }
  723. void VisitFilesInFolder(
  724. const AZStd::string& folder, const AZStd::function<bool(const AZStd::string&)> visitorFn, bool recurse)
  725. {
  726. if (!visitorFn || IsPathIgnored(folder))
  727. {
  728. return;
  729. }
  730. AZStd::string fullFilter = folder + AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING + "*";
  731. AZ::StringFunc::Replace(fullFilter, "\\", "/");
  732. AZStd::string fullPath;
  733. AZ::IO::SystemFile::FindFiles(
  734. fullFilter.c_str(),
  735. [&](const char* item, bool is_file)
  736. {
  737. // Skip the '.' and '..' folders
  738. if ((azstricmp(".", item) == 0) || (azstricmp("..", item) == 0))
  739. {
  740. return true;
  741. }
  742. // Continue if we can
  743. fullPath.clear();
  744. if (!AzFramework::StringFunc::Path::Join(folder.c_str(), item, fullPath))
  745. {
  746. return false;
  747. }
  748. AZ::StringFunc::Replace(fullPath, "\\", "/");
  749. if (is_file)
  750. {
  751. return visitorFn(fullPath);
  752. }
  753. VisitFilesInFolder(fullPath, visitorFn, recurse);
  754. return true;
  755. });
  756. }
  757. void VisitFilesInScanFolders(const AZStd::function<bool(const AZStd::string&)> visitorFn)
  758. {
  759. if (visitorFn)
  760. {
  761. for (const AZStd::string& scanFolder : GetSupportedSourceFolders())
  762. {
  763. VisitFilesInFolder(scanFolder, visitorFn, true);
  764. }
  765. }
  766. }
  767. AZStd::vector<AZStd::string> GetPathsInSourceFoldersMatchingFilter(const AZStd::function<bool(const AZStd::string&)> filterFn)
  768. {
  769. const auto& scanFolders = GetSupportedSourceFolders();
  770. AZStd::vector<AZStd::string> results;
  771. results.reserve(scanFolders.size());
  772. AZStd::for_each(
  773. scanFolders.begin(),
  774. scanFolders.end(),
  775. [&](const AZStd::string& scanFolder)
  776. {
  777. VisitFilesInFolder(
  778. scanFolder,
  779. [&](const AZStd::string& path)
  780. {
  781. if (!filterFn || filterFn(path))
  782. {
  783. results.emplace_back(path);
  784. }
  785. return true;
  786. },
  787. true);
  788. });
  789. // Sorting the container and removing duplicate paths to ensure uniqueness in case of nested or overlapping scan folders.
  790. // This was previously done automatically with a set but using a vector for compatibility with behavior context and Python.
  791. AZStd::sort(results.begin(), results.end());
  792. results.erase(AZStd::unique(results.begin(), results.end()), results.end());
  793. return results;
  794. }
  795. AZStd::vector<AZStd::string> GetPathsInSourceFoldersMatchingExtension(const AZStd::string& extension)
  796. {
  797. if (extension.empty())
  798. {
  799. return {};
  800. }
  801. const AZStd::string& extensionWithDot = (extension[0] == '.') ? extension : AZStd::string::format(".%s", extension.c_str());
  802. return GetPathsInSourceFoldersMatchingFilter(
  803. [&](const AZStd::string& path)
  804. {
  805. return path.ends_with(extensionWithDot) && IsDocumentPathEditable(path);
  806. });
  807. }
  808. bool IsPathIgnored(const AZStd::string& path)
  809. {
  810. bool result = false;
  811. AtomToolsFrameworkSystemRequestBus::BroadcastResult(result, &AtomToolsFrameworkSystemRequestBus::Events::IsPathIgnored, path);
  812. return result;
  813. }
  814. AZStd::vector<AZStd::string> GetSupportedSourceFolders()
  815. {
  816. AZStd::vector<AZStd::string> scanFolders;
  817. scanFolders.reserve(100);
  818. AzToolsFramework::AssetSystemRequestBus::Broadcast(
  819. &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAssetSafeFolders, scanFolders);
  820. AZStd::erase_if(scanFolders, [](const AZStd::string& path){ return IsPathIgnored(path); });
  821. return scanFolders;
  822. }
  823. void AddRegisteredScriptToMenu(QMenu* menu, const AZStd::string& registryKey, const AZStd::vector<AZStd::string>& arguments)
  824. {
  825. // Map containing vectors of script file paths organized by category
  826. using ScriptsSettingsMap = AZStd::map<AZStd::string, AZStd::vector<AZStd::string>>;
  827. // Retrieve and iterate over all of the registered script settings to add them to the menu
  828. for (const auto& [scriptCategoryName, scriptPathVec] : GetSettingsObject(registryKey, ScriptsSettingsMap()))
  829. {
  830. // Create a parent category menu group to contain all of the individual script menu actions.
  831. QMenu* scriptCategoryMenu = menu;
  832. if (!scriptCategoryName.empty())
  833. {
  834. scriptCategoryMenu = menu->findChild<QMenu*>(scriptCategoryName.c_str());
  835. if (!scriptCategoryMenu)
  836. {
  837. scriptCategoryMenu = menu->addMenu(scriptCategoryName.c_str());
  838. }
  839. }
  840. // Create menu actions for executing the individual scripts.
  841. for (AZStd::string scriptPath : scriptPathVec)
  842. {
  843. // Removing the alias for the path so that we can check for its existence and add it to the menu.
  844. scriptPath = GetPathWithoutAlias(scriptPath);
  845. if (QFile::exists(scriptPath.c_str()))
  846. {
  847. // Use the file name instead of the full path as the display name for the menu action.
  848. AZStd::string filename;
  849. AZ::StringFunc::Path::GetFullFileName(scriptPath.c_str(), filename);
  850. scriptCategoryMenu->addAction(filename.c_str(), [scriptPath, arguments]() {
  851. // Delay execution of the script until the next frame.
  852. AZ::SystemTickBus::QueueFunction([scriptPath, arguments]() {
  853. AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(
  854. &AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs,
  855. scriptPath,
  856. AZStd::vector<AZStd::string_view>(arguments.begin(), arguments.end()));
  857. });
  858. });
  859. }
  860. }
  861. }
  862. // Create menu action for running arbitrary Python script.
  863. menu->addAction(QObject::tr("&Run Python Script..."), [arguments]() {
  864. const QString scriptPath = QFileDialog::getOpenFileName(
  865. GetToolMainWindow(), QObject::tr("Run Python Script"), QString(AZ::Utils::GetProjectPath().c_str()), QString("*.py"));
  866. if (!scriptPath.isEmpty())
  867. {
  868. // Delay execution of the script until the next frame.
  869. AZ::SystemTickBus::QueueFunction([scriptPath, arguments]() {
  870. AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(
  871. &AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs,
  872. scriptPath.toUtf8().constData(),
  873. AZStd::vector<AZStd::string_view>(arguments.begin(), arguments.end()));
  874. });
  875. }
  876. });
  877. }
  878. void ReflectUtilFunctions(AZ::ReflectContext* context)
  879. {
  880. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  881. {
  882. // This will put these methods into the 'azlmbr.atomtools.util' module
  883. auto addUtilFunc = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
  884. {
  885. methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  886. ->Attribute(AZ::Script::Attributes::Category, "Editor")
  887. ->Attribute(AZ::Script::Attributes::Module, "atomtools.util");
  888. };
  889. addUtilFunc(behaviorContext->Method("GetSymbolNameFromText", GetSymbolNameFromText, nullptr, ""));
  890. addUtilFunc(behaviorContext->Method("GetDisplayNameFromText", GetDisplayNameFromText, nullptr, ""));
  891. addUtilFunc(behaviorContext->Method("GetDisplayNameFromPath", GetDisplayNameFromPath, nullptr, ""));
  892. addUtilFunc(behaviorContext->Method("GetStringListFromDialog", GetStringListFromDialog, nullptr, ""));
  893. addUtilFunc(behaviorContext->Method("GetFileFilterFromSupportedExtensions", GetFileFilterFromSupportedExtensions, nullptr, ""));
  894. addUtilFunc(behaviorContext->Method("GetFirstValidSupportedExtension", GetFirstValidSupportedExtension, nullptr, ""));
  895. addUtilFunc(behaviorContext->Method("GetFirstMatchingSupportedExtension", GetFirstMatchingSupportedExtension, nullptr, ""));
  896. addUtilFunc(behaviorContext->Method("GetSaveFilePathFromDialog", GetSaveFilePathFromDialog, nullptr, ""));
  897. addUtilFunc(behaviorContext->Method("GetOpenFilePathsFromDialog", GetOpenFilePathsFromDialog, nullptr, ""));
  898. addUtilFunc(behaviorContext->Method("GetUniqueFilePath", GetUniqueFilePath, nullptr, ""));
  899. addUtilFunc(behaviorContext->Method("GetUniqueUntitledFilePath", GetUniqueUntitledFilePath, nullptr, ""));
  900. addUtilFunc(behaviorContext->Method("ValidateDocumentPath", ValidateDocumentPath, nullptr, ""));
  901. addUtilFunc(behaviorContext->Method("IsDocumentPathInSupportedFolder", IsDocumentPathInSupportedFolder, nullptr, ""));
  902. addUtilFunc(behaviorContext->Method("IsDocumentPathEditable", IsDocumentPathEditable, nullptr, ""));
  903. addUtilFunc(behaviorContext->Method("IsDocumentPathPreviewable", IsDocumentPathPreviewable, nullptr, ""));
  904. addUtilFunc(behaviorContext->Method("GetPathToExteralReference", GetPathToExteralReference, nullptr, ""));
  905. addUtilFunc(behaviorContext->Method("GetPathWithoutAlias", GetPathWithoutAlias, nullptr, ""));
  906. addUtilFunc(behaviorContext->Method("GetPathWithAlias", GetPathWithAlias, nullptr, ""));
  907. addUtilFunc(behaviorContext->Method("GetAbsolutePathForSourceAsset", GetAbsolutePathForSourceAsset, nullptr, ""));
  908. addUtilFunc(behaviorContext->Method("GetPathsForAssetSourceDependencies", GetPathsForAssetSourceDependencies, nullptr, ""));
  909. addUtilFunc(behaviorContext->Method("GetPathsForAssetSourceDependenciesById", GetPathsForAssetSourceDependenciesById, nullptr, ""));
  910. addUtilFunc(behaviorContext->Method("GetPathsForAssetSourceDependenciesByPath", GetPathsForAssetSourceDependenciesByPath, nullptr, ""));
  911. addUtilFunc(behaviorContext->Method("GetPathsForAssetSourceDependents", GetPathsForAssetSourceDependents, nullptr, ""));
  912. addUtilFunc(behaviorContext->Method("GetPathsForAssetSourceDependentsById", GetPathsForAssetSourceDependentsById, nullptr, ""));
  913. addUtilFunc(behaviorContext->Method("GetPathsForAssetSourceDependentsByPath", GetPathsForAssetSourceDependentsByPath, nullptr, ""));
  914. addUtilFunc(behaviorContext->Method("GetPathsInSourceFoldersMatchingExtension", GetPathsInSourceFoldersMatchingExtension, nullptr, ""));
  915. addUtilFunc(behaviorContext->Method("IsPathIgnored", IsPathIgnored, nullptr, ""));
  916. addUtilFunc(behaviorContext->Method("GetSupportedSourceFolders", GetSupportedSourceFolders, nullptr, ""));
  917. addUtilFunc(behaviorContext->Method("GetSettingsValue_bool", GetSettingsValue<bool>, nullptr, ""));
  918. addUtilFunc(behaviorContext->Method("SetSettingsValue_bool", SetSettingsValue<bool>, nullptr, ""));
  919. addUtilFunc(behaviorContext->Method("GetSettingsValue_s64", GetSettingsValue<AZ::s64>, nullptr, ""));
  920. addUtilFunc(behaviorContext->Method("SetSettingsValue_s64", SetSettingsValue<AZ::s64>, nullptr, ""));
  921. addUtilFunc(behaviorContext->Method("GetSettingsValue_u64", GetSettingsValue<AZ::u64>, nullptr, ""));
  922. addUtilFunc(behaviorContext->Method("SetSettingsValue_u64", SetSettingsValue<AZ::u64>, nullptr, ""));
  923. addUtilFunc(behaviorContext->Method("GetSettingsValue_double", GetSettingsValue<double>, nullptr, ""));
  924. addUtilFunc(behaviorContext->Method("SetSettingsValue_double", SetSettingsValue<double>, nullptr, ""));
  925. addUtilFunc(behaviorContext->Method("GetSettingsValue_string", GetSettingsValue<AZStd::string>, nullptr, ""));
  926. addUtilFunc(behaviorContext->Method("SetSettingsValue_string", SetSettingsValue<AZStd::string>, nullptr, ""));
  927. }
  928. }
  929. } // namespace AtomToolsFramework