MainWindow.cpp 108 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566
  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 "MainWindow.h"
  9. #include "AssetTreeFilterModel.h"
  10. #include "AssetTreeItem.h"
  11. #include "ConnectionEditDialog.h"
  12. #include "ProductAssetTreeItemData.h"
  13. #include "ProductAssetTreeModel.h"
  14. #include "ProductDependencyTreeItemData.h"
  15. #include "SourceAssetTreeItemData.h"
  16. #include "SourceAssetTreeModel.h"
  17. #include <native/ui/BuilderData.h>
  18. #include <native/ui/BuilderDataItem.h>
  19. #include <native/ui/BuilderInfoPatternsModel.h>
  20. #include <native/ui/BuilderInfoMetricsModel.h>
  21. #include <native/ui/EnabledRelocationTypesModel.h>
  22. #include <native/ui/SourceAssetTreeFilterModel.h>
  23. #include <AzFramework/Asset/AssetSystemBus.h>
  24. #include <AzCore/JSON/stringbuffer.h>
  25. #include <AzCore/JSON/prettywriter.h>
  26. #include <AzCore/JSON/pointer.h>
  27. #include <AzQtComponents/AzQtComponentsAPI.h>
  28. #include <AzQtComponents/Components/ConfigHelpers.h>
  29. #include <AzQtComponents/Components/Style.h>
  30. #include <AzQtComponents/Components/StyleManager.h>
  31. #include <AzQtComponents/Components/Widgets/CheckBox.h>
  32. #include <AzQtComponents/Components/Widgets/LineEdit.h>
  33. #include <AzQtComponents/Utilities/QtWindowUtilities.h>
  34. #include <AzQtComponents/Utilities/DesktopUtilities.h>
  35. #include <AzQtComponents/Components/Widgets/PushButton.h>
  36. #include <native/resourcecompiler/JobsModel.h>
  37. #include "native/ui/ui_MainWindow.h"
  38. #include "native/ui/JobTreeViewItemDelegate.h"
  39. #include <native/utilities/AssetServerHandler.h>
  40. #include <native/utilities/assetUtils.h>
  41. #include "../utilities/GUIApplicationManager.h"
  42. #include "../utilities/ApplicationServer.h"
  43. #include "../connection/connectionManager.h"
  44. #include "../connection/connection.h"
  45. #include "../resourcecompiler/rccontroller.h"
  46. #include "../resourcecompiler/RCJobSortFilterProxyModel.h"
  47. #include <QClipboard>
  48. #include <QDesktopServices>
  49. #include <QListWidget>
  50. #include <QMessageBox>
  51. #include <QUrl>
  52. #include <QWidgetAction>
  53. #include <QKeyEvent>
  54. #include <QFileDialog>
  55. static const QString g_jobFilteredSearchWidgetState = QStringLiteral("jobFilteredSearchWidget");
  56. static const qint64 AssetTabFilterUpdateIntervalMs = 5000;
  57. static const int MaxVisiblePopoutMenuRows = 20;
  58. static const QString productMenuTitle(QObject::tr("View product asset..."));
  59. static const QString intermediateMenuTitle(QObject::tr("View intermediate asset..."));
  60. struct AssetRightClickMenuResult
  61. {
  62. QListWidget* m_listWidget = nullptr;
  63. QMenu* m_assetMenu = nullptr;
  64. };
  65. AssetRightClickMenuResult SetupAssetRightClickMenu(QMenu* parentMenu, QString title, QString tooltip)
  66. {
  67. AssetRightClickMenuResult result;
  68. if (!parentMenu)
  69. {
  70. return result;
  71. }
  72. result.m_assetMenu = parentMenu->addMenu(title);
  73. QWidgetAction* productMenuListAction = new QWidgetAction(result.m_assetMenu);
  74. productMenuListAction->setToolTip(tooltip);
  75. result.m_listWidget = new QListWidget(result.m_assetMenu);
  76. result.m_listWidget->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
  77. result.m_listWidget->setTextElideMode(Qt::ElideLeft);
  78. result.m_listWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  79. result.m_listWidget->setSelectionMode(QAbstractItemView::NoSelection);
  80. productMenuListAction->setDefaultWidget(result.m_listWidget);
  81. result.m_assetMenu->addAction(productMenuListAction);
  82. return result;
  83. }
  84. AssetRightClickMenuResult SetupProductAssetRightClickMenu(QMenu* parentMenu)
  85. {
  86. return SetupAssetRightClickMenu(parentMenu, productMenuTitle, QObject::tr("Shows this product asset in the Product Assets tab."));
  87. }
  88. AssetRightClickMenuResult SetupIntermediateAssetRightClickMenu(QMenu* parentMenu)
  89. {
  90. return SetupAssetRightClickMenu(parentMenu, intermediateMenuTitle, QObject::tr("Shows this intermediate asset in the Intermediate Assets tab."));
  91. }
  92. void CreateDisabledAssetRightClickMenu(QMenu* parentMenu, QMenu* existingMenu, QString title, QString tooltip)
  93. {
  94. if (!parentMenu || !existingMenu)
  95. {
  96. return;
  97. }
  98. // If there were no products, then show a disabled action with a tooltip.
  99. // Disabled menus don't support tooltips, so remove the menu first.
  100. parentMenu->removeAction(existingMenu->menuAction());
  101. existingMenu->deleteLater();
  102. QAction* disabledProductTableAction = parentMenu->addAction(title);
  103. disabledProductTableAction->setToolTip(tooltip);
  104. disabledProductTableAction->setDisabled(true);
  105. }
  106. void ResizeAssetRightClickMenuList(QListWidget* assetList, int assetCount)
  107. {
  108. // Clamp the max assets displayed at once. This is a list view, so it will show a scroll bar for anything over this.
  109. assetCount = AZStd::min(MaxVisiblePopoutMenuRows, assetCount);
  110. // Using fixed width and height because the size hints aren't working well within a qmenu popout menu.
  111. assetList->setFixedHeight(assetCount * assetList->sizeHintForRow(0));
  112. assetList->setFixedWidth(assetList->sizeHintForColumn(0));
  113. }
  114. MainWindow::Config MainWindow::loadConfig(QSettings& settings)
  115. {
  116. using namespace AzQtComponents;
  117. Config config = defaultConfig();
  118. // Asset Status
  119. {
  120. ConfigHelpers::GroupGuard assetStatus(&settings, QStringLiteral("AssetStatus"));
  121. ConfigHelpers::read<int>(settings, QStringLiteral("JobStatusColumnWidth"), config.jobStatusColumnWidth);
  122. ConfigHelpers::read<int>(settings, QStringLiteral("JobSourceColumnWidth"), config.jobSourceColumnWidth);
  123. ConfigHelpers::read<int>(settings, QStringLiteral("JobPlatformColumnWidth"), config.jobPlatformColumnWidth);
  124. ConfigHelpers::read<int>(settings, QStringLiteral("JobKeyColumnWidth"), config.jobKeyColumnWidth);
  125. ConfigHelpers::read<int>(settings, QStringLiteral("JobCompletedColumnWidth"), config.jobCompletedColumnWidth);
  126. }
  127. // Event Log Details
  128. {
  129. ConfigHelpers::GroupGuard eventLogDetails(&settings, QStringLiteral("EventLogDetails"));
  130. ConfigHelpers::read<int>(settings, QStringLiteral("LogTypeColumnWidth"), config.logTypeColumnWidth);
  131. }
  132. // Event Log Line Details
  133. {
  134. ConfigHelpers::GroupGuard eventLogDetails(&settings, QStringLiteral("EventLogLineDetails"));
  135. ConfigHelpers::read<int>(settings, QStringLiteral("contextDetailsTableMaximumRows"), config.contextDetailsTableMaximumRows);
  136. }
  137. return config;
  138. }
  139. MainWindow::Config MainWindow::defaultConfig()
  140. {
  141. // These are used if the values can't be read from AssetProcessorConfig.ini.
  142. Config config;
  143. config.jobStatusColumnWidth = 100;
  144. config.jobSourceColumnWidth = 160;
  145. config.jobPlatformColumnWidth = 100;
  146. config.jobKeyColumnWidth = 120;
  147. config.jobCompletedColumnWidth = 160;
  148. config.logTypeColumnWidth = 150;
  149. config.contextDetailsTableMaximumRows = 10;
  150. return config;
  151. }
  152. MainWindow::MainWindow(GUIApplicationManager* guiApplicationManager, QWidget* parent)
  153. : QMainWindow(parent)
  154. , m_guiApplicationManager(guiApplicationManager)
  155. , m_jobSortFilterProxy(new AssetProcessor::JobSortFilterProxyModel(this))
  156. , m_logSortFilterProxy(new LogSortFilterProxy(this))
  157. , m_jobsModel(new AssetProcessor::JobsModel(this))
  158. , m_logsModel(new AzToolsFramework::Logging::LogTableModel(this))
  159. , ui(new Ui::MainWindow)
  160. , m_loggingPanel(nullptr)
  161. , m_fileSystemWatcher(new QFileSystemWatcher(this))
  162. , m_builderList(new BuilderListModel(this))
  163. , m_builderListSortFilterProxy(new BuilderListSortFilterProxy(this))
  164. , m_builderInfoPatterns(new AssetProcessor::BuilderInfoPatternsModel(this))
  165. , m_enabledRelocationTypesModel(new AssetProcessor::EnabledRelocationTypesModel(this))
  166. {
  167. ui->setupUi(this);
  168. // Don't show the "Filter by:" text on this filter widget
  169. ui->jobFilteredSearchWidget->clearLabelText();
  170. ui->detailsFilterWidget->clearLabelText();
  171. ui->timerContainerWidget->setVisible(false);
  172. }
  173. bool MainWindow::eventFilter(QObject* /*obj*/, QEvent* event)
  174. {
  175. if (event->type() == QEvent::KeyPress)
  176. {
  177. QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
  178. if (keyEvent->key() == Qt::Key::Key_Space)
  179. {
  180. // Stop space key from opening filter list.
  181. return true;
  182. }
  183. }
  184. return false;
  185. }
  186. void MainWindow::Activate()
  187. {
  188. using namespace AssetProcessor;
  189. m_sharedDbConnection = AZStd::shared_ptr<AzToolsFramework::AssetDatabase::AssetDatabaseConnection>(aznew AzToolsFramework::AssetDatabase::AssetDatabaseConnection());
  190. m_sharedDbConnection->OpenDatabase();
  191. ui->projectLabel->setText(QStringLiteral("%1: %2")
  192. .arg(tr("Project"))
  193. .arg(QDir{m_guiApplicationManager->GetProjectPath()}.absolutePath()));
  194. ui->rootLabel->setText(QStringLiteral("%1: %2")
  195. .arg(tr("Root"))
  196. .arg(m_guiApplicationManager->GetSystemRoot().absolutePath()));
  197. ui->portLabel->setText(QStringLiteral("%1: %2")
  198. .arg(tr("Processor port"))
  199. .arg(m_guiApplicationManager->GetApplicationServer()->GetServerListeningPort()));
  200. connect(ui->supportButton, &QPushButton::clicked, this, &MainWindow::OnSupportClicked);
  201. ui->buttonList->addTab(QStringLiteral("Welcome"));
  202. ui->buttonList->addTab(QStringLiteral("Jobs"));
  203. ui->buttonList->addTab(QStringLiteral("Assets"));
  204. ui->buttonList->addTab(QStringLiteral("Logs"));
  205. ui->buttonList->addTab(QStringLiteral("Connections"));
  206. ui->buttonList->addTab(QStringLiteral("Builders"));
  207. ui->buttonList->addTab(QStringLiteral("Settings"));
  208. ui->buttonList->addTab(QStringLiteral("Shared Cache"));
  209. ui->buttonList->addTab(QStringLiteral("Asset Relocation"));
  210. connect(ui->buttonList, &AzQtComponents::SegmentBar::currentChanged, ui->dialogStack, &QStackedWidget::setCurrentIndex);
  211. const int startIndex = static_cast<int>(DialogStackIndex::Welcome);
  212. ui->dialogStack->setCurrentIndex(startIndex);
  213. ui->buttonList->setCurrentIndex(startIndex);
  214. //Connection view
  215. ui->connectionTreeView->setModel(m_guiApplicationManager->GetConnectionManager());
  216. ui->connectionTreeView->setEditTriggers(QAbstractItemView::CurrentChanged);
  217. ui->connectionTreeView->header()->setSectionResizeMode(ConnectionManager::IdColumn, QHeaderView::Stretch);
  218. ui->connectionTreeView->header()->setSectionResizeMode(ConnectionManager::AutoConnectColumn, QHeaderView::Fixed);
  219. ui->connectionTreeView->header()->resizeSection(ConnectionManager::StatusColumn, 160);
  220. ui->connectionTreeView->header()->resizeSection(ConnectionManager::IpColumn, 150);
  221. ui->connectionTreeView->header()->resizeSection(ConnectionManager::PortColumn, 60);
  222. ui->connectionTreeView->header()->resizeSection(ConnectionManager::PlatformColumn, 60);
  223. ui->connectionTreeView->header()->resizeSection(ConnectionManager::AutoConnectColumn, 60);
  224. ui->connectionTreeView->header()->setStretchLastSection(false);
  225. connect(ui->connectionTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::OnConnectionSelectionChanged);
  226. ui->editConnectionButton->setEnabled(false);
  227. ui->removeConnectionButton->setEnabled(false);
  228. connect(ui->editConnectionButton, &QPushButton::clicked, this, &MainWindow::OnEditConnection);
  229. connect(ui->addConnectionButton, &QPushButton::clicked, this, &MainWindow::OnAddConnection);
  230. connect(ui->removeConnectionButton, &QPushButton::clicked, this, &MainWindow::OnRemoveConnection);
  231. connect(ui->connectionTreeView, &QTreeView::doubleClicked, this, [this](const QModelIndex& index) {
  232. EditConnection(index);
  233. });
  234. ui->connectionTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
  235. connect(ui->connectionTreeView, &QTreeView::customContextMenuRequested, this, &MainWindow::OnConnectionContextMenu);
  236. //allowed list connections
  237. connect(m_guiApplicationManager->GetConnectionManager(), &ConnectionManager::FirstTimeAddedToRejctedList, this, &MainWindow::FirstTimeAddedToRejectedList);
  238. connect(m_guiApplicationManager->GetConnectionManager(), &ConnectionManager::SyncAllowedListAndRejectedList, this, &MainWindow::SyncAllowedListAndRejectedList);
  239. connect(ui->allowListAllowedListConnectionsListView, &QListView::clicked, this, &MainWindow::OnAllowedListConnectionsListViewClicked);
  240. ui->allowListAllowedListConnectionsListView->setModel(&m_allowedListAddresses);
  241. connect(ui->allowedListRejectedConnectionsListView, &QListView::clicked, this, &MainWindow::OnRejectedConnectionsListViewClicked);
  242. ui->allowedListRejectedConnectionsListView->setModel(&m_rejectedAddresses);
  243. connect(ui->allowedListEnableCheckBox, &QCheckBox::toggled, this, &MainWindow::OnAllowedListCheckBoxToggled);
  244. connect(ui->allowedListAddHostNameToolButton, &QToolButton::clicked, this, &MainWindow::OnAddHostNameAllowedListButtonClicked);
  245. connect(ui->allowedListAddIPToolButton, &QPushButton::clicked, this, &MainWindow::OnAddIPAllowedListButtonClicked);
  246. connect(ui->allowedListToAllowedListToolButton, &QPushButton::clicked, this, &MainWindow::OnToAllowedListButtonClicked);
  247. connect(ui->allowedListToRejectedListToolButton, &QToolButton::clicked, this, &MainWindow::OnToRejectedListButtonClicked);
  248. //set the input validator for ip addresses on the add address line edit
  249. QRegExp validHostName("^((?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|\\b-){0,61}[0-9A-Za-z])?(?:\\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|\\b-){0,61}[0-9A-Za-z])?)*\\.?)$");
  250. QRegExp validIP("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/([0-9]|[1-2][0-9]|3[0-2]))?$|^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$");
  251. QRegExpValidator* hostNameValidator = new QRegExpValidator(validHostName, this);
  252. ui->allowedListAddHostNameLineEdit->setValidator(hostNameValidator);
  253. QRegExpValidator* ipValidator = new QRegExpValidator(validIP, this);
  254. ui->allowedListAddIPLineEdit->setValidator(ipValidator);
  255. //Job view
  256. m_jobSortFilterProxy->setSourceModel(m_jobsModel);
  257. m_jobSortFilterProxy->setDynamicSortFilter(true);
  258. m_jobSortFilterProxy->setFilterKeyColumn(JobsModel::ColumnSource);
  259. ui->jobTreeView->setModel(m_jobSortFilterProxy);
  260. ui->jobTreeView->setSortingEnabled(true);
  261. ui->jobTreeView->header()->setDefaultAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
  262. ui->jobTreeView->setItemDelegate(new AssetProcessor::JobTreeViewItemDelegate(ui->jobTreeView));
  263. ui->jobTreeView->setToolTip(tr("Click to view Job Log"));
  264. ui->detailsFilterWidget->SetTypeFilterVisible(true);
  265. connect(ui->detailsFilterWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, m_logSortFilterProxy,
  266. static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&LogSortFilterProxy::setFilterRegExp));
  267. connect(ui->detailsFilterWidget, &AzQtComponents::FilteredSearchWidget::TypeFilterChanged, m_logSortFilterProxy, &LogSortFilterProxy::onTypeFilterChanged);
  268. // add filters for each logging type
  269. ui->detailsFilterWidget->AddTypeFilter("Status", "Debug", AzToolsFramework::Logging::LogLine::TYPE_DEBUG);
  270. ui->detailsFilterWidget->AddTypeFilter("Status", "Message", AzToolsFramework::Logging::LogLine::TYPE_MESSAGE);
  271. ui->detailsFilterWidget->AddTypeFilter("Status", "Warning", AzToolsFramework::Logging::LogLine::TYPE_WARNING);
  272. ui->detailsFilterWidget->AddTypeFilter("Status", "Error", AzToolsFramework::Logging::LogLine::TYPE_ERROR);
  273. m_logSortFilterProxy->setDynamicSortFilter(true);
  274. m_logSortFilterProxy->setSourceModel(m_logsModel);
  275. m_logSortFilterProxy->setFilterKeyColumn(AzToolsFramework::Logging::LogTableModel::ColumnMessage);
  276. m_logSortFilterProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
  277. ui->jobLogTableView->setModel(m_logSortFilterProxy);
  278. ui->jobLogTableView->setItemDelegate(new AzToolsFramework::Logging::LogTableItemDelegate(ui->jobLogTableView));
  279. ui->jobLogTableView->setExpandOnSelection();
  280. connect(ui->jobTreeView->header(), &QHeaderView::sortIndicatorChanged, m_jobSortFilterProxy, &AssetProcessor::JobSortFilterProxyModel::sort);
  281. connect(ui->jobTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::JobSelectionChanged);
  282. ui->jobFilteredSearchWidget->SetTypeFilterVisible(true);
  283. ui->jobFilteredSearchWidget->assetTypeSelectorButton()->installEventFilter(this);
  284. // listen for job status changes in order to update the log view with the latest log data
  285. connect(m_guiApplicationManager->GetRCController(), &AssetProcessor::RCController::JobStatusChanged, this, &MainWindow::JobStatusChanged);
  286. ui->jobContextLogTableView->setModel(new AzToolsFramework::Logging::ContextDetailsLogTableModel(ui->jobContextLogTableView));
  287. ui->jobContextLogTableView->setItemDelegate(new AzQtComponents::TableViewItemDelegate(ui->jobContextLogTableView));
  288. ui->jobContextLogTableView->setExpandOnSelection();
  289. ui->jobContextContainer->setVisible(false);
  290. connect(ui->jobLogTableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::JobLogSelectionChanged);
  291. const auto statuses =
  292. {
  293. AzToolsFramework::AssetSystem::JobStatus::Failed,
  294. AzToolsFramework::AssetSystem::JobStatus::Completed,
  295. AzToolsFramework::AssetSystem::JobStatus::Queued,
  296. AzToolsFramework::AssetSystem::JobStatus::InProgress
  297. };
  298. const auto category = tr("Status");
  299. for (const auto& status : statuses)
  300. {
  301. ui->jobFilteredSearchWidget->AddTypeFilter(category, JobsModel::GetStatusInString(status, 0, 0),
  302. QVariant::fromValue(status));
  303. }
  304. AssetProcessor::CustomJobStatusFilter customFilter{ true };
  305. ui->jobFilteredSearchWidget->AddTypeFilter(category, "Completed w/ Warnings", QVariant::fromValue(customFilter));
  306. connect(ui->jobFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TypeFilterChanged,
  307. m_jobSortFilterProxy, &AssetProcessor::JobSortFilterProxyModel::OnJobStatusFilterChanged);
  308. connect(ui->jobFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
  309. m_jobSortFilterProxy,
  310. static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetProcessor::JobSortFilterProxyModel::setFilterRegExp));
  311. {
  312. QSettings settingsObj(this);
  313. ui->jobFilteredSearchWidget->readSettings(settingsObj, g_jobFilteredSearchWidgetState);
  314. }
  315. auto writeJobFilterSettings = [this]()
  316. {
  317. QSettings settingsObj(this);
  318. ui->jobFilteredSearchWidget->writeSettings(settingsObj, g_jobFilteredSearchWidgetState);
  319. };
  320. connect(ui->jobFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TypeFilterChanged,
  321. this, writeJobFilterSettings);
  322. connect(ui->jobFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
  323. this, writeJobFilterSettings);
  324. // Asset view
  325. m_sourceAssetTreeFilterModel = new AssetProcessor::SourceAssetTreeFilterModel(this);
  326. m_sourceModel = new AssetProcessor::SourceAssetTreeModel(m_sharedDbConnection, this);
  327. m_sourceAssetTreeFilterModel->setSourceModel(m_sourceModel);
  328. ui->SourceAssetsTreeView->setModel(m_sourceAssetTreeFilterModel);
  329. ui->SourceAssetsTreeView->setColumnWidth(aznumeric_cast<int>(AssetTreeColumns::Extension), 80);
  330. ui->SourceAssetsTreeView->setColumnWidth(aznumeric_cast<int>(SourceAssetTreeColumns::AnalysisJobDuration), 170);
  331. connect(ui->assetDataFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
  332. m_sourceAssetTreeFilterModel, static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetTreeFilterModel::FilterChanged));
  333. m_intermediateAssetTreeFilterModel = new AssetProcessor::AssetTreeFilterModel(this);
  334. m_intermediateModel = new AssetProcessor::SourceAssetTreeModel(m_sharedDbConnection, this);
  335. m_intermediateModel->SetOnlyShowIntermediateAssets();
  336. m_intermediateAssetTreeFilterModel->setSourceModel(m_intermediateModel);
  337. ui->IntermediateAssetsTreeView->setModel(m_intermediateAssetTreeFilterModel);
  338. connect(
  339. ui->assetDataFilteredSearchWidget,
  340. &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
  341. m_intermediateAssetTreeFilterModel,
  342. static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetTreeFilterModel::FilterChanged));
  343. m_productAssetTreeFilterModel = new AssetProcessor::AssetTreeFilterModel(this);
  344. m_productModel = new AssetProcessor::ProductAssetTreeModel(m_sharedDbConnection, this);
  345. m_productAssetTreeFilterModel->setSourceModel(m_productModel);
  346. ui->ProductAssetsTreeView->setModel(m_productAssetTreeFilterModel);
  347. ui->ProductAssetsTreeView->setColumnWidth(aznumeric_cast<int>(AssetTreeColumns::Extension), 80);
  348. connect(ui->assetDataFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
  349. m_productAssetTreeFilterModel, static_cast<void (QSortFilterProxyModel::*)(const QString&)>(&AssetTreeFilterModel::FilterChanged));
  350. ui->intermediateAssetDetailsPanel->SetIsIntermediateAsset();
  351. AZStd::optional<AZ::s64> intermediateAssetFolderId(m_guiApplicationManager->GetAssetProcessorManager()->GetIntermediateAssetScanFolderId());
  352. ui->productAssetDetailsPanel->SetIntermediateAssetFolderId(intermediateAssetFolderId);
  353. ui->sourceAssetDetailsPanel->SetIntermediateAssetFolderId(intermediateAssetFolderId);
  354. ui->intermediateAssetDetailsPanel->SetIntermediateAssetFolderId(intermediateAssetFolderId);
  355. AzQtComponents::StyleManager::setStyleSheet(ui->sourceAssetDetailsPanel, QStringLiteral("style:AssetProcessor.qss"));
  356. AzQtComponents::StyleManager::setStyleSheet(ui->intermediateAssetDetailsPanel, QStringLiteral("style:AssetProcessor.qss"));
  357. AzQtComponents::StyleManager::setStyleSheet(ui->productAssetDetailsPanel, QStringLiteral("style:AssetProcessor.qss"));
  358. ui->sourceAssetDetailsPanel->RegisterAssociatedWidgets(
  359. ui->SourceAssetsTreeView,
  360. m_sourceModel,
  361. m_sourceAssetTreeFilterModel,
  362. ui->IntermediateAssetsTreeView,
  363. m_intermediateModel,
  364. m_intermediateAssetTreeFilterModel,
  365. ui->ProductAssetsTreeView,
  366. m_productModel,
  367. m_productAssetTreeFilterModel,
  368. ui->assetsTabWidget);
  369. ui->intermediateAssetDetailsPanel->RegisterAssociatedWidgets(
  370. ui->SourceAssetsTreeView,
  371. m_sourceModel,
  372. m_sourceAssetTreeFilterModel,
  373. ui->IntermediateAssetsTreeView,
  374. m_intermediateModel,
  375. m_intermediateAssetTreeFilterModel,
  376. ui->ProductAssetsTreeView,
  377. m_productModel,
  378. m_productAssetTreeFilterModel,
  379. ui->assetsTabWidget);
  380. ui->productAssetDetailsPanel->RegisterAssociatedWidgets(
  381. ui->SourceAssetsTreeView,
  382. m_sourceModel,
  383. m_sourceAssetTreeFilterModel,
  384. ui->IntermediateAssetsTreeView,
  385. m_intermediateModel,
  386. m_intermediateAssetTreeFilterModel,
  387. ui->ProductAssetsTreeView,
  388. m_productModel,
  389. m_productAssetTreeFilterModel,
  390. ui->assetsTabWidget);
  391. ui->productAssetDetailsPanel->SetScannerInformation(ui->missingDependencyScanResults, m_guiApplicationManager->GetAssetProcessorManager()->GetDatabaseConnection());
  392. ui->productAssetDetailsPanel->SetupDependencyGraph(
  393. ui->ProductAssetsTreeView, m_guiApplicationManager->GetAssetProcessorManager()->GetDatabaseConnection());
  394. ui->productAssetDetailsPanel->SetScanQueueEnabled(false);
  395. connect(ui->SourceAssetsTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, ui->sourceAssetDetailsPanel, &SourceAssetDetailsPanel::AssetDataSelectionChanged);
  396. connect(ui->IntermediateAssetsTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, ui->intermediateAssetDetailsPanel, &SourceAssetDetailsPanel::AssetDataSelectionChanged);
  397. connect(ui->ProductAssetsTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, ui->productAssetDetailsPanel, &ProductAssetDetailsPanel::AssetDataSelectionChanged);
  398. connect(ui->assetsTabWidget, &QTabWidget::currentChanged, this, &MainWindow::OnAssetTabChange);
  399. ui->ProductAssetsTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
  400. connect(ui->ProductAssetsTreeView, &QWidget::customContextMenuRequested, this, &MainWindow::ShowProductAssetContextMenu);
  401. ui->SourceAssetsTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
  402. connect(ui->SourceAssetsTreeView, &QWidget::customContextMenuRequested, this, &MainWindow::ShowSourceAssetContextMenu);
  403. ui->IntermediateAssetsTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
  404. connect(ui->IntermediateAssetsTreeView, &QWidget::customContextMenuRequested, this, &MainWindow::ShowIntermediateAssetContextMenu);
  405. ui->productAssetDetailsPanel->GetOutgoingProductDependenciesTreeView()->setContextMenuPolicy(Qt::CustomContextMenu);
  406. connect(
  407. ui->productAssetDetailsPanel->GetOutgoingProductDependenciesTreeView(), &QWidget::customContextMenuRequested, this,
  408. &MainWindow::ShowOutgoingProductDependenciesContextMenu);
  409. ui->productAssetDetailsPanel->GetIncomingProductDependenciesTreeView()->setContextMenuPolicy(Qt::CustomContextMenu);
  410. connect(
  411. ui->productAssetDetailsPanel->GetIncomingProductDependenciesTreeView(), &QWidget::customContextMenuRequested, this,
  412. &MainWindow::ShowIncomingProductDependenciesContextMenu);
  413. SetupAssetSelectionCaching();
  414. // the first time we open that panel we can refresh it.
  415. m_connectionForResettingAssetsView = connect(
  416. ui->dialogStack,
  417. &QStackedWidget::currentChanged,
  418. this,
  419. [&](int index)
  420. {
  421. if (index == static_cast<int>(DialogStackIndex::Assets))
  422. {
  423. // the first time we show the asset window, reset the model since its so expensive to do on every startup
  424. // and many times, the user does not even go to that panel.
  425. m_sourceModel->Reset();
  426. m_intermediateModel->Reset();
  427. m_productModel->Reset();
  428. QObject::disconnect(m_connectionForResettingAssetsView);
  429. }
  430. });
  431. //Log View
  432. m_loggingPanel = ui->LoggingPanel;
  433. m_loggingPanel->SetStorageID(AZ_CRC_CE("AssetProcessor::LogPanel"));
  434. connect(ui->logButton, &QPushButton::clicked, this, &MainWindow::DesktopOpenJobLogs);
  435. if (!m_loggingPanel->LoadState())
  436. {
  437. // if unable to load state then show the default tabs
  438. ResetLoggingPanel();
  439. }
  440. AzQtComponents::ConfigHelpers::loadConfig<Config, MainWindow>(m_fileSystemWatcher, &m_config, QStringLiteral("style:AssetProcessorConfig.ini"), this, std::bind(&MainWindow::ApplyConfig, this));
  441. ApplyConfig();
  442. connect(m_loggingPanel, &AzToolsFramework::LogPanel::StyledLogPanel::TabsReset, this, &MainWindow::ResetLoggingPanel);
  443. connect(m_guiApplicationManager->GetRCController(), &AssetProcessor::RCController::JobStatusChanged, m_jobsModel, &AssetProcessor::JobsModel::OnJobStatusChanged);
  444. connect(m_guiApplicationManager->GetAssetProcessorManager(), &AssetProcessor::AssetProcessorManager::JobRemoved, m_jobsModel, &AssetProcessor::JobsModel::OnJobRemoved);
  445. connect(m_guiApplicationManager->GetAssetProcessorManager(), &AssetProcessor::AssetProcessorManager::SourceDeleted, m_jobsModel, &AssetProcessor::JobsModel::OnSourceRemoved);
  446. connect(
  447. m_guiApplicationManager->GetAssetProcessorManager(),
  448. &AssetProcessor::AssetProcessorManager::JobProcessDurationChanged,
  449. m_jobsModel,
  450. &AssetProcessor::JobsModel::OnJobProcessDurationChanged);
  451. connect(
  452. m_guiApplicationManager->GetAssetProcessorManager(),
  453. &AssetProcessor::AssetProcessorManager::CreateJobsDurationChanged,
  454. m_sourceModel,
  455. &AssetProcessor::SourceAssetTreeModel::OnCreateJobsDurationChanged);
  456. connect(ui->jobTreeView, &AzQtComponents::TableView::customContextMenuRequested, this, &MainWindow::ShowJobViewContextMenu);
  457. connect(ui->jobContextLogTableView, &AzQtComponents::TableView::customContextMenuRequested, this, &MainWindow::ShowLogLineContextMenu);
  458. connect(ui->jobLogTableView, &AzQtComponents::TableView::customContextMenuRequested, this, &MainWindow::ShowJobLogContextMenu);
  459. m_jobsModel->PopulateJobsFromDatabase();
  460. // Builders Tab:
  461. m_builderData = new BuilderData(m_sharedDbConnection, this);
  462. m_builderListSortFilterProxy->setDynamicSortFilter(true);
  463. m_builderListSortFilterProxy->setSourceModel(m_builderList);
  464. m_builderListSortFilterProxy->sort(0);
  465. ui->builderList->setModel(m_builderListSortFilterProxy);
  466. ui->builderInfoPatternsTableView->setModel(m_builderInfoPatterns);
  467. m_builderInfoMetrics = new BuilderInfoMetricsModel(m_builderData, this);
  468. m_builderInfoMetricsSort = new BuilderInfoMetricsSortModel(this);
  469. m_builderInfoMetricsSort->setSourceModel(m_builderInfoMetrics);
  470. m_builderInfoMetricsSort->setSortRole(aznumeric_cast<int>(BuilderInfoMetricsModel::Role::SortRole));
  471. ui->builderInfoMetricsTreeView->setModel(m_builderInfoMetricsSort);
  472. ui->builderInfoMetricsTreeView->setColumnWidth(0, 400);
  473. ui->builderInfoMetricsTreeView->setColumnWidth(1, 70);
  474. ui->builderInfoMetricsTreeView->setColumnWidth(2, 150);
  475. ui->builderInfoMetricsTreeView->setColumnWidth(3, 150);
  476. connect(ui->builderList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::BuilderTabSelectionChanged);
  477. connect(m_guiApplicationManager, &GUIApplicationManager::OnBuildersRegistered, [this]()
  478. {
  479. if(m_builderList)
  480. {
  481. m_builderList->Reset();
  482. if(m_builderListSortFilterProxy)
  483. {
  484. m_builderListSortFilterProxy->sort(0);
  485. }
  486. }
  487. if (m_builderInfoMetrics)
  488. {
  489. m_builderInfoMetrics->Reset();
  490. }
  491. });
  492. connect(
  493. m_guiApplicationManager->GetAssetProcessorManager(),
  494. &AssetProcessorManager::JobProcessDurationChanged,
  495. m_builderData,
  496. &BuilderData::OnProcessJobDurationChanged);
  497. connect(
  498. m_guiApplicationManager->GetAssetProcessorManager(),
  499. &AssetProcessorManager::CreateJobsDurationChanged,
  500. m_builderData,
  501. &BuilderData::OnCreateJobsDurationChanged);
  502. connect(m_builderData, &BuilderData::DurationChanged, m_builderInfoMetrics, &BuilderInfoMetricsModel::OnDurationChanged);
  503. // Settings tab:
  504. connect(ui->fullScanButton, &QPushButton::clicked, this, &MainWindow::OnRescanButtonClicked);
  505. AzQtComponents::CheckBox::applyToggleSwitchStyle(ui->modtimeSkippingCheckBox);
  506. AzQtComponents::CheckBox::applyToggleSwitchStyle(ui->disableStartupScanCheckBox);
  507. AzQtComponents::CheckBox::applyToggleSwitchStyle(ui->debugOutputCheckBox);
  508. AzQtComponents::CheckBox::applyToggleSwitchStyle(ui->verboseLoggingCheckbox);
  509. const auto apm = m_guiApplicationManager->GetAssetProcessorManager();
  510. // Zero Analysis mode ("Fast scan mode") is enabled by default in the gui (running this mainwindow file), false in batch mode.
  511. bool zeroAnalysisMode = AssetUtilities::GetUserSetting("zeroAnalysisMode", true);
  512. bool enableBuilderDebugFlag = AssetUtilities::GetUserSetting("enableBuilderDebugFlag", apm->GetBuilderDebugFlag());
  513. bool initialScanSkippingEnabled = AssetUtilities::GetUserSetting("initialScanSkippingEnabled", apm->GetInitialScanSkippingFeatureEnabled());
  514. bool verboseLogDump = AssetUtilities::GetUserSetting("verboseLogging", false);
  515. // zero analysis flag
  516. apm->SetEnableModtimeSkippingFeature(zeroAnalysisMode);
  517. apm->SetBuilderDebugFlag(enableBuilderDebugFlag);
  518. apm->SetInitialScanSkippingFeature(initialScanSkippingEnabled);
  519. // first, update the visual checkboxes, and then connect to the "changed" notify
  520. // so that we don't save settings applied by the apm command line
  521. ui->modtimeSkippingCheckBox->setCheckState(zeroAnalysisMode ? Qt::Checked : Qt::Unchecked);
  522. ui->debugOutputCheckBox->setCheckState(enableBuilderDebugFlag ? Qt::Checked : Qt::Unchecked);
  523. ui->disableStartupScanCheckBox->setCheckState(initialScanSkippingEnabled ? Qt::Checked : Qt::Unchecked);
  524. ui->verboseLoggingCheckbox->setCheckState(verboseLogDump ? Qt::Checked : Qt::Unchecked);
  525. QObject::connect(ui->modtimeSkippingCheckBox, &QCheckBox::stateChanged, this,
  526. [this](int newCheckState)
  527. {
  528. bool newOption = newCheckState == Qt::Checked ? true : false;
  529. m_guiApplicationManager->GetAssetProcessorManager()->SetEnableModtimeSkippingFeature(newOption);
  530. AssetUtilities::SetUserSetting("zeroAnalysisMode", newOption);
  531. });
  532. QObject::connect(ui->debugOutputCheckBox, &QCheckBox::stateChanged, this,
  533. [this](int newCheckState)
  534. {
  535. bool newOption = newCheckState == Qt::Checked ? true : false;
  536. m_guiApplicationManager->GetAssetProcessorManager()->SetBuilderDebugFlag(newOption);
  537. AssetUtilities::SetUserSetting("enableBuilderDebugFlag", newOption);
  538. });
  539. QObject::connect(ui->disableStartupScanCheckBox, &QCheckBox::stateChanged, this,
  540. [](int newCheckState)
  541. {
  542. // this is not something that we change while running, so just set it for next time.
  543. bool newOption = newCheckState == Qt::Checked ? true : false;
  544. AssetUtilities::SetUserSetting("initialScanSkippingEnabled", newOption);
  545. });
  546. QObject::connect(ui->verboseLoggingCheckbox, &QCheckBox::stateChanged, this,
  547. [](int newCheckState)
  548. {
  549. bool newOption = newCheckState == Qt::Checked ? true : false;
  550. AssetUtilities::SetUserSetting("verboseLogging", newOption);
  551. // Todo: Notify the log system immediately
  552. });
  553. // Shared Cache tab:
  554. SetupAssetServerTab();
  555. m_enabledRelocationTypesModel->Reset();
  556. ui->AssetRelocationExtensionListView->setModel(m_enabledRelocationTypesModel);
  557. ui->MetaCreationDelayValue->setText(tr("%1 milliseconds").arg(m_guiApplicationManager->GetAssetProcessorManager()->GetMetaCreationDelay()));
  558. }
  559. void MainWindow::BuilderTabSelectionChanged(const QItemSelection& selected, const QItemSelection& /*deselected*/)
  560. {
  561. if (selected.size() > 0)
  562. {
  563. const auto proxyIndex = selected.indexes().at(0);
  564. if (!proxyIndex.isValid())
  565. {
  566. return;
  567. }
  568. const auto& index = m_builderListSortFilterProxy->mapToSource(proxyIndex);
  569. AssetProcessor::BuilderInfoList builders;
  570. AssetProcessor::AssetBuilderInfoBus::Broadcast(&AssetProcessor::AssetBuilderInfoBus::Events::GetAllBuildersInfo, builders);
  571. AZ_Assert(index.isValid(), "BuilderTabSelectionChanged index out of bounds");
  572. const auto builder = builders[index.row()];
  573. m_builderInfoPatterns->Reset(builder);
  574. ui->builderInfoMetricsTreeView->setRootIndex(
  575. m_builderInfoMetricsSort->mapFromSource(m_builderInfoMetrics->index(m_builderData->m_builderGuidToIndex[builder.m_busId], 0)));
  576. ui->builderInfoMetricsTreeView->expandToDepth(0);
  577. ui->builderInfoHeaderValueName->setText(builder.m_name.c_str());
  578. ui->builderInfoDetailsValueType->setText(
  579. builder.m_builderType == AssetBuilderSDK::AssetBuilderDesc::AssetBuilderType::Internal ? "Internal" : "External");
  580. ui->builderInfoDetailsValueFingerprint->setText(builder.m_analysisFingerprint.c_str());
  581. ui->builderInfoDetailsValueVersionNumber->setText(QString::number(builder.m_version));
  582. ui->builderInfoDetailsValueBusId->setText(builder.m_busId.ToFixedString().c_str());
  583. }
  584. }
  585. namespace MainWindowInternal
  586. {
  587. enum class PatternColumns
  588. {
  589. Enabled = 0,
  590. Name = 1,
  591. Type = 2,
  592. Pattern = 3,
  593. Remove = 4
  594. };
  595. }
  596. void MainWindow::SetupAssetServerTab()
  597. {
  598. using namespace AssetProcessor;
  599. using namespace MainWindowInternal;
  600. m_cacheServerData.Reset();
  601. ui->serverCacheModeOptions->addItem(QString("Inactive"), aznumeric_cast<int>(AssetProcessor::AssetServerMode::Inactive));
  602. ui->serverCacheModeOptions->addItem(QString("Server"), aznumeric_cast<int>(AssetProcessor::AssetServerMode::Server));
  603. ui->serverCacheModeOptions->addItem(QString("Client"), aznumeric_cast<int>(AssetProcessor::AssetServerMode::Client));
  604. // Asset Cache Server support button
  605. QObject::connect(ui->sharedCacheSupport, &QPushButton::clicked, this,
  606. []()
  607. {
  608. QDesktopServices::openUrl(
  609. QStringLiteral("https://o3de.org/docs/user-guide/assets/asset-processor/asset-cache-server/"));
  610. });
  611. QObject::connect(ui->serverCacheModeOptions,
  612. static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
  613. this,
  614. [this](int newIndex)
  615. {
  616. AssetServerMode inputAssetServerMode = aznumeric_cast<AssetServerMode>(newIndex);
  617. AssetServerBus::BroadcastResult(this->m_cacheServerData.m_cachingMode, &AssetServerBus::Events::GetRemoteCachingMode);
  618. if (this->m_cacheServerData.m_cachingMode != inputAssetServerMode)
  619. {
  620. this->m_cacheServerData.m_dirty = true;
  621. this->m_cacheServerData.m_cachingMode = inputAssetServerMode;
  622. this->m_cacheServerData.m_updateStatus = false;
  623. this->CheckAssetServerStates();
  624. }
  625. });
  626. // serverAddressToolButton
  627. ui->serverAddressToolButton->setIcon(QIcon(":Browse_on.png"));
  628. QObject::connect(ui->serverAddressToolButton, &QToolButton::clicked, this,
  629. [this]()
  630. {
  631. auto path = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Choose remote folder.")));
  632. if (!path.isEmpty())
  633. {
  634. ui->serverAddressLineEdit->setText(path);
  635. }
  636. });
  637. QObject::connect(ui->serverAddressLineEdit, &QLineEdit::textChanged, this,
  638. [this]()
  639. {
  640. SetServerAddress(this->ui->serverAddressLineEdit->text().toUtf8().data());
  641. });
  642. QObject::connect(ui->sharedCacheSubmitButton, &QPushButton::clicked, this,
  643. [this]()
  644. {
  645. if (this->m_cacheServerData.m_dirty)
  646. {
  647. bool changedServerAddress = false;
  648. AssembleAssetPatterns();
  649. AssetServerBus::BroadcastResult(changedServerAddress, &AssetServerBus::Events::SetServerAddress, this->m_cacheServerData.m_serverAddress);
  650. if (changedServerAddress)
  651. {
  652. AZ::IO::Path projectPath(m_guiApplicationManager->GetProjectPath().toUtf8().data());
  653. if (this->m_cacheServerData.Save(projectPath))
  654. {
  655. AssetServerBus::Broadcast(&AssetServerBus::Events::SetRemoteCachingMode, this->m_cacheServerData.m_cachingMode);
  656. this->m_cacheServerData.Reset();
  657. }
  658. }
  659. else if (this->m_cacheServerData.m_cachingMode != AssetServerMode::Inactive)
  660. {
  661. this->m_cacheServerData.m_statusLevel = CacheServerData::StatusLevel::Error;
  662. this->m_cacheServerData.m_statusMessage = AZStd::string::format("**Error**: Invalid server address!");
  663. }
  664. this->CheckAssetServerStates();
  665. }
  666. });
  667. QObject::connect(ui->sharedCacheDiscardButton, &QPushButton::clicked, this,
  668. [this]()
  669. {
  670. this->m_cacheServerData.Reset();
  671. this->ResetAssetServerView();
  672. this->m_cacheServerData.m_statusLevel = CacheServerData::StatusLevel::Notice;
  673. this->m_cacheServerData.m_statusMessage = AZStd::string::format("Reset configuration.");
  674. this->m_cacheServerData.m_updateStatus = true;
  675. this->CheckAssetServerStates();
  676. });
  677. // setting up the patterns table
  678. QObject::connect(ui->sharedCacheAddPattern, &QPushButton::clicked, this,
  679. [this]()
  680. {
  681. AddPatternRow("New Name", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard, "", true);
  682. this->m_cacheServerData.m_dirty = true;
  683. this->m_cacheServerData.m_updateStatus = false;
  684. this->CheckAssetServerStates();
  685. });
  686. ui->sharedCacheTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
  687. ui->sharedCacheTable->horizontalHeader()->setSectionResizeMode(aznumeric_cast<int>(PatternColumns::Enabled), QHeaderView::Fixed);
  688. ui->sharedCacheTable->horizontalHeader()->setSectionResizeMode(aznumeric_cast<int>(PatternColumns::Remove), QHeaderView::Fixed);
  689. ui->sharedCacheTable->setAlternatingRowColors(true);
  690. ResetAssetServerView();
  691. CheckAssetServerStates();
  692. }
  693. void MainWindow::AddPatternRow(AZStd::string_view name, AssetBuilderSDK::AssetBuilderPattern::PatternType type, AZStd::string_view pattern, bool enable)
  694. {
  695. using namespace AssetBuilderSDK;
  696. using namespace MainWindowInternal;
  697. int row = ui->sharedCacheTable->rowCount();
  698. ui->sharedCacheTable->insertRow(row);
  699. auto updateStatus = [this](int)
  700. {
  701. this->m_cacheServerData.m_dirty = true;
  702. this->m_cacheServerData.m_updateStatus = false;
  703. this->CheckAssetServerStates();
  704. };
  705. QObject::connect(ui->sharedCacheTable, &QTableWidget::cellChanged, this,
  706. [this](int, int)
  707. {
  708. this->m_cacheServerData.m_dirty = true;
  709. this->CheckAssetServerStates();
  710. });
  711. // Enabled check mark
  712. auto* enableChackmark = new QCheckBox();
  713. enableChackmark->setChecked(enable);
  714. QObject::connect(enableChackmark, &QCheckBox::stateChanged, ui->sharedCacheTable, updateStatus);
  715. ui->sharedCacheTable->setCellWidget(row, aznumeric_cast<int>(PatternColumns::Enabled), enableChackmark);
  716. ui->sharedCacheTable->setColumnWidth(aznumeric_cast<int>(PatternColumns::Enabled), 8);
  717. enableChackmark->setToolTip(tr("Temporarily disable the pattern by unchecking this box"));
  718. // Name
  719. auto* nameWidgetItem = new QTableWidgetItem(name.data());
  720. ui->sharedCacheTable->setItem(row, aznumeric_cast<int>(PatternColumns::Name), nameWidgetItem);
  721. nameWidgetItem->setToolTip(tr("Name of the pattern or title name of an asset builder"));
  722. // Type combo
  723. auto* combo = new QComboBox();
  724. QObject::connect(combo, QOverload<int>::of(&QComboBox::currentIndexChanged), ui->sharedCacheTable, updateStatus);
  725. combo->addItem("Wildcard", QVariant(AssetBuilderPattern::PatternType::Wildcard));
  726. combo->addItem("Regex", QVariant(AssetBuilderPattern::PatternType::Regex));
  727. combo->setCurrentIndex(aznumeric_cast<int>(type));
  728. ui->sharedCacheTable->setCellWidget(row, aznumeric_cast<int>(PatternColumns::Type), combo);
  729. combo->setToolTip(tr("Wildcard is a file wild card pattern; Regex is a regular expression pattern"));
  730. // Pattern
  731. auto* patternWidgetItem = new QTableWidgetItem(pattern.data());
  732. ui->sharedCacheTable->setItem(row, aznumeric_cast<int>(PatternColumns::Pattern), patternWidgetItem);
  733. patternWidgetItem->setToolTip(tr("String pattern to match source assets"));
  734. // Remove button
  735. auto* button = new QPushButton();
  736. button->setFlat(true);
  737. button->setIcon(QIcon(":/Delete.png"));
  738. button->setIconSize(QSize(14, 14));
  739. button->setStyleSheet("QPushButton { background-color: transparent; border: 0px }");
  740. ui->sharedCacheTable->setCellWidget(row, aznumeric_cast<int>(PatternColumns::Remove), button);
  741. ui->sharedCacheTable->setColumnWidth(aznumeric_cast<int>(PatternColumns::Remove), 16);
  742. button->setToolTip(tr("Removes the pattern to be considered for caching"));
  743. QObject::connect(button, &QPushButton::clicked, this,
  744. [this]()
  745. {
  746. this->ui->sharedCacheTable->removeRow(this->ui->sharedCacheTable->currentRow());
  747. this->m_cacheServerData.m_dirty = true;
  748. this->CheckAssetServerStates();
  749. });
  750. }
  751. void MainWindow::AssembleAssetPatterns()
  752. {
  753. using namespace AssetBuilderSDK;
  754. using namespace MainWindowInternal;
  755. AssetProcessor::RecognizerContainer patternContainer;
  756. int row = 0;
  757. for(; row < ui->sharedCacheTable->rowCount(); ++row)
  758. {
  759. auto pattern = AZStd::pair<AZStd::string, AssetProcessor::AssetRecognizer>();
  760. auto* itemName = ui->sharedCacheTable->item(row, aznumeric_cast<int>(PatternColumns::Name));
  761. auto* itemPattern = ui->sharedCacheTable->item(row, aznumeric_cast<int>(PatternColumns::Pattern));
  762. auto* itemType = qobject_cast<QComboBox*>(ui->sharedCacheTable->cellWidget(row, aznumeric_cast<int>(PatternColumns::Type)));
  763. auto* itemCheck = qobject_cast<QCheckBox*>(ui->sharedCacheTable->cellWidget(row, aznumeric_cast<int>(PatternColumns::Enabled)));
  764. pattern.first = itemName->text().toUtf8().data();
  765. AZStd::string filePattern { itemPattern->text().toUtf8().data() };
  766. AssetBuilderPattern::PatternType patternType{};
  767. auto typeData = itemType->itemData(itemType->currentIndex());
  768. if (typeData.toInt() == aznumeric_cast<int>(AssetBuilderPattern::PatternType::Regex))
  769. {
  770. patternType = AssetBuilderPattern::PatternType::Regex;
  771. }
  772. pattern.second.m_patternMatcher = { filePattern, patternType };
  773. pattern.second.m_checkServer = (itemCheck->checkState() == Qt::CheckState::Checked);
  774. patternContainer.emplace(AZStd::move(pattern));
  775. }
  776. m_cacheServerData.m_patternContainer = AZStd::move(patternContainer);
  777. }
  778. void MainWindow::CheckAssetServerStates()
  779. {
  780. using namespace AssetProcessor;
  781. if (m_cacheServerData.m_dirty)
  782. {
  783. ui->sharedCacheSubmitButton->setEnabled(true);
  784. ui->sharedCacheDiscardButton->setEnabled(true);
  785. }
  786. else
  787. {
  788. ui->sharedCacheSubmitButton->setEnabled(false);
  789. ui->sharedCacheDiscardButton->setEnabled(false);
  790. }
  791. switch (m_cacheServerData.m_statusLevel)
  792. {
  793. case CacheServerData::StatusLevel::None:
  794. {
  795. m_cacheServerData.m_updateStatus = true;
  796. ui->sharedCacheStatus->setStyleSheet("QLabel#sharedCacheStatus");
  797. ui->sharedCacheStatus->setText("");
  798. break;
  799. }
  800. case CacheServerData::StatusLevel::Notice:
  801. {
  802. ui->sharedCacheStatus->setText(m_cacheServerData.m_statusMessage.c_str());
  803. ui->sharedCacheStatus->setProperty("highlight", "blue");
  804. ui->sharedCacheStatus->style()->unpolish(ui->sharedCacheStatus);
  805. ui->sharedCacheStatus->style()->polish(ui->sharedCacheStatus);
  806. ui->sharedCacheStatus->update();
  807. break;
  808. }
  809. case CacheServerData::StatusLevel::Active:
  810. {
  811. m_cacheServerData.m_updateStatus = false;
  812. ui->sharedCacheStatus->setText(m_cacheServerData.m_statusMessage.c_str());
  813. ui->sharedCacheStatus->setProperty("highlight", "green");
  814. ui->sharedCacheStatus->style()->unpolish(ui->sharedCacheStatus);
  815. ui->sharedCacheStatus->style()->polish(ui->sharedCacheStatus);
  816. ui->sharedCacheStatus->update();
  817. break;
  818. }
  819. case CacheServerData::StatusLevel::Error:
  820. {
  821. m_cacheServerData.m_updateStatus = false;
  822. ui->sharedCacheStatus->setText(m_cacheServerData.m_statusMessage.c_str());
  823. ui->sharedCacheStatus->setProperty("highlight", "red");
  824. ui->sharedCacheStatus->style()->unpolish(ui->sharedCacheStatus);
  825. ui->sharedCacheStatus->style()->polish(ui->sharedCacheStatus);
  826. ui->sharedCacheStatus->update();
  827. break;
  828. }
  829. default:
  830. break;
  831. }
  832. if (m_cacheServerData.m_updateStatus)
  833. {
  834. // Change message to status after a few moments
  835. QTimer::singleShot(1000 * 5, this, [this] {
  836. if (this->m_cacheServerData.m_cachingMode == AssetServerMode::Inactive)
  837. {
  838. this->m_cacheServerData.m_statusLevel = CacheServerData::StatusLevel::Notice;
  839. this->m_cacheServerData.m_statusMessage = "Inactive";
  840. }
  841. else
  842. {
  843. this->m_cacheServerData.m_statusLevel = CacheServerData::StatusLevel::Active;
  844. this->m_cacheServerData.m_statusMessage = "Active";
  845. }
  846. this->m_cacheServerData.m_updateStatus = false;
  847. this->CheckAssetServerStates();
  848. });
  849. }
  850. }
  851. void MainWindow::ResetAssetServerView()
  852. {
  853. using namespace AssetProcessor;
  854. ui->serverCacheModeOptions->setCurrentIndex(aznumeric_cast<int>(m_cacheServerData.m_cachingMode));
  855. ui->serverAddressLineEdit->setText(QString(m_cacheServerData.m_serverAddress.c_str()));
  856. ui->sharedCacheTable->setRowCount(0);
  857. for (const auto& pattern : m_cacheServerData.m_patternContainer)
  858. {
  859. AddPatternRow(
  860. pattern.second.m_name,
  861. pattern.second.m_patternMatcher.GetBuilderPattern().m_type,
  862. pattern.second.m_patternMatcher.GetBuilderPattern().m_pattern,
  863. pattern.second.m_checkServer);
  864. }
  865. m_cacheServerData.m_dirty = false;
  866. m_cacheServerData.m_statusLevel = CacheServerData::StatusLevel::None;
  867. m_cacheServerData.m_statusMessage.clear();
  868. CheckAssetServerStates();
  869. }
  870. void MainWindow::SetServerAddress(AZStd::string_view serverAddress)
  871. {
  872. using namespace AssetProcessor;
  873. AssetServerBus::BroadcastResult(
  874. this->m_cacheServerData.m_serverAddress,
  875. &AssetServerBus::Events::GetServerAddress);
  876. if (this->m_cacheServerData.m_serverAddress != serverAddress)
  877. {
  878. this->m_cacheServerData.m_dirty = true;
  879. this->m_cacheServerData.m_serverAddress = serverAddress;
  880. this->CheckAssetServerStates();
  881. }
  882. }
  883. void MainWindow::SetupAssetSelectionCaching()
  884. {
  885. // Connect the source model resetting to preserve selection and restore it after the model is reset.
  886. connect(m_sourceModel, &QAbstractItemModel::modelAboutToBeReset, [&]()
  887. {
  888. QItemSelection sourceSelection = m_sourceAssetTreeFilterModel->mapSelectionToSource(ui->SourceAssetsTreeView->selectionModel()->selection());
  889. if (sourceSelection.indexes().count() == 0 || !sourceSelection.indexes()[0].isValid())
  890. {
  891. return;
  892. }
  893. QModelIndex sourceModelIndex = sourceSelection.indexes()[0];
  894. AssetProcessor::AssetTreeItem* childItem = static_cast<AssetProcessor::AssetTreeItem*>(sourceModelIndex.internalPointer());
  895. m_cachedSourceAssetSelection =
  896. AssetProcessor::SourceAndScanID(childItem->GetData()->m_assetDbName, childItem->GetData()->m_scanFolderID);
  897. });
  898. connect(m_sourceModel, &QAbstractItemModel::modelReset, [&]()
  899. {
  900. if (m_cachedSourceAssetSelection.first.empty() ||
  901. m_cachedSourceAssetSelection.second == AzToolsFramework::AssetDatabase::InvalidEntryId)
  902. {
  903. return;
  904. }
  905. QModelIndex goToIndex = m_sourceModel->GetIndexForSource(m_cachedSourceAssetSelection.first, m_cachedSourceAssetSelection.second);
  906. // If the cached selection was deleted or is no longer available, clear the selection.
  907. if (!goToIndex.isValid())
  908. {
  909. m_cachedSourceAssetSelection.first.clear();
  910. m_cachedSourceAssetSelection.second = AzToolsFramework::AssetDatabase::InvalidEntryId;
  911. ui->ProductAssetsTreeView->selectionModel()->clearSelection();
  912. // ClearSelection says in the Qt docs that the selectionChange signal will be sent, but that wasn't happening,
  913. // so force the details panel to refresh.
  914. ui->sourceAssetDetailsPanel->AssetDataSelectionChanged(QItemSelection(), QItemSelection());
  915. ui->intermediateAssetDetailsPanel->AssetDataSelectionChanged(QItemSelection(), QItemSelection());
  916. return;
  917. }
  918. m_sourceAssetTreeFilterModel->ForceModelIndexVisible(goToIndex);
  919. QModelIndex filterIndex = m_sourceAssetTreeFilterModel->mapFromSource(goToIndex);
  920. ui->SourceAssetsTreeView->scrollTo(filterIndex, QAbstractItemView::ScrollHint::EnsureVisible);
  921. ui->SourceAssetsTreeView->selectionModel()->select(filterIndex, AssetProcessor::AssetTreeModel::GetAssetTreeSelectionFlags());
  922. });
  923. // Connect the product model resetting to preserve selection and restore it after the model is reset.
  924. connect(m_productModel, &QAbstractItemModel::modelAboutToBeReset, [&]()
  925. {
  926. QItemSelection productSelection = m_productAssetTreeFilterModel->mapSelectionToSource(ui->ProductAssetsTreeView->selectionModel()->selection());
  927. if (productSelection.indexes().count() == 0 || !productSelection.indexes()[0].isValid())
  928. {
  929. return;
  930. }
  931. QModelIndex productModelIndex = productSelection.indexes()[0];
  932. AssetProcessor::AssetTreeItem* childItem = static_cast<AssetProcessor::AssetTreeItem*>(productModelIndex.internalPointer());
  933. m_cachedProductAssetSelection = childItem->GetData()->m_assetDbName;
  934. });
  935. connect(m_productModel, &QAbstractItemModel::modelReset, [&]()
  936. {
  937. if (m_cachedProductAssetSelection.empty())
  938. {
  939. return;
  940. }
  941. QModelIndex goToIndex = m_productModel->GetIndexForProduct(m_cachedProductAssetSelection);
  942. // If the cached selection was deleted or is no longer available, clear the selection.
  943. if (!goToIndex.isValid())
  944. {
  945. m_cachedProductAssetSelection.clear();
  946. ui->ProductAssetsTreeView->selectionModel()->clearSelection();
  947. // ClearSelection says in the Qt docs that the selectionChange signal will be sent, but that wasn't happening,
  948. // so force the details panel to refresh.
  949. ui->productAssetDetailsPanel->AssetDataSelectionChanged(QItemSelection(), QItemSelection());
  950. return;
  951. }
  952. m_productAssetTreeFilterModel->ForceModelIndexVisible(goToIndex);
  953. QModelIndex filterIndex = m_productAssetTreeFilterModel->mapFromSource(goToIndex);
  954. ui->ProductAssetsTreeView->scrollTo(filterIndex, QAbstractItemView::ScrollHint::EnsureVisible);
  955. ui->ProductAssetsTreeView->selectionModel()->select(filterIndex, AssetProcessor::AssetTreeModel::GetAssetTreeSelectionFlags());
  956. });
  957. }
  958. void MainWindow::OnRescanButtonClicked()
  959. {
  960. m_guiApplicationManager->Rescan();
  961. }
  962. void MainWindow::OnSupportClicked(bool /*checked*/)
  963. {
  964. QDesktopServices::openUrl(
  965. QStringLiteral("https://o3de.org/docs/user-guide/assets/pipeline/"));
  966. }
  967. void MainWindow::EditConnection(const QModelIndex& index)
  968. {
  969. if (index.data(ConnectionManager::UserConnectionRole).toBool())
  970. {
  971. ConnectionEditDialog dialog(m_guiApplicationManager->GetConnectionManager(), index, this);
  972. dialog.exec();
  973. }
  974. }
  975. void MainWindow::OnConnectionContextMenu(const QPoint& point)
  976. {
  977. QPersistentModelIndex index = ui->connectionTreeView->indexAt(point);
  978. bool isUserConnection = index.isValid() && index.data(ConnectionManager::UserConnectionRole).toBool();
  979. QMenu menu(this);
  980. QAction* editConnectionAction = menu.addAction("&Edit connection...");
  981. editConnectionAction->setEnabled(isUserConnection);
  982. connect(editConnectionAction, &QAction::triggered, this, [index, this] {
  983. EditConnection(index);
  984. });
  985. menu.exec(ui->connectionTreeView->viewport()->mapToGlobal(point));
  986. }
  987. void MainWindow::OnEditConnection(bool /*checked*/)
  988. {
  989. auto selectedIndices = ui->connectionTreeView->selectionModel()->selectedRows();
  990. Q_ASSERT(selectedIndices.count() > 0);
  991. // Only edit the first connection. Guaranteed above by the edit connection button only being enabled if one connection is selected
  992. EditConnection(selectedIndices[0]);
  993. }
  994. void MainWindow::OnAddConnection(bool /*checked*/)
  995. {
  996. m_guiApplicationManager->GetConnectionManager()->addUserConnection();
  997. }
  998. void MainWindow::OnAllowedListConnectionsListViewClicked()
  999. {
  1000. ui->allowedListRejectedConnectionsListView->clearSelection();
  1001. }
  1002. void MainWindow::OnRejectedConnectionsListViewClicked()
  1003. {
  1004. ui->allowListAllowedListConnectionsListView->clearSelection();
  1005. }
  1006. void MainWindow::OnAllowedListCheckBoxToggled()
  1007. {
  1008. if (!ui->allowedListEnableCheckBox->isChecked())
  1009. {
  1010. //warn this is not safe
  1011. if(QMessageBox::Ok == QMessageBox::warning(this, tr("!!!WARNING!!!"), tr("Turning off allowed listing poses a significant security risk as it would allow any device to connect to your asset processor and that device will have READ/WRITE access to the Asset Processors file system. Only do this if you sure you know what you are doing and accept the risks."),
  1012. QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel))
  1013. {
  1014. ui->allowedListRejectedConnectionsListView->clearSelection();
  1015. ui->allowListAllowedListConnectionsListView->clearSelection();
  1016. ui->allowedListAddHostNameLineEdit->setEnabled(false);
  1017. ui->allowedListAddHostNameToolButton->setEnabled(false);
  1018. ui->allowedListAddIPLineEdit->setEnabled(false);
  1019. ui->allowedListAddIPToolButton->setEnabled(false);
  1020. ui->allowListAllowedListConnectionsListView->setEnabled(false);
  1021. ui->allowedListRejectedConnectionsListView->setEnabled(false);
  1022. ui->allowedListToAllowedListToolButton->setEnabled(false);
  1023. ui->allowedListToRejectedListToolButton->setEnabled(false);
  1024. }
  1025. else
  1026. {
  1027. ui->allowedListEnableCheckBox->setChecked(true);
  1028. }
  1029. }
  1030. else
  1031. {
  1032. ui->allowedListAddHostNameLineEdit->setEnabled(true);
  1033. ui->allowedListAddHostNameToolButton->setEnabled(true);
  1034. ui->allowedListAddIPLineEdit->setEnabled(true);
  1035. ui->allowedListAddIPToolButton->setEnabled(true);
  1036. ui->allowListAllowedListConnectionsListView->setEnabled(true);
  1037. ui->allowedListRejectedConnectionsListView->setEnabled(true);
  1038. ui->allowedListToAllowedListToolButton->setEnabled(true);
  1039. ui->allowedListToRejectedListToolButton->setEnabled(true);
  1040. }
  1041. m_guiApplicationManager->GetConnectionManager()->AllowedListingEnabled(ui->allowedListEnableCheckBox->isChecked());
  1042. }
  1043. void MainWindow::OnAddHostNameAllowedListButtonClicked()
  1044. {
  1045. QString text = ui->allowedListAddHostNameLineEdit->text();
  1046. const QRegExpValidator *hostnameValidator = static_cast<const QRegExpValidator *>(ui->allowedListAddHostNameLineEdit->validator());
  1047. int pos;
  1048. QValidator::State state = hostnameValidator->validate(text, pos);
  1049. if (state == QValidator::Acceptable)
  1050. {
  1051. auto lineEdit = ui->allowedListAddHostNameLineEdit;
  1052. m_guiApplicationManager->GetConnectionManager()->AddAddressToAllowedList(text);
  1053. lineEdit->clear();
  1054. // Clear error state set in LineEdit.
  1055. lineEdit->setProperty(AzQtComponents::HasError, false);
  1056. auto errorToolButton = lineEdit->findChild<QToolButton*>(AzQtComponents::ErrorToolButton);
  1057. if (errorToolButton && AzQtComponents::LineEdit::errorIconEnabled(lineEdit))
  1058. {
  1059. errorToolButton->setVisible(false);
  1060. }
  1061. }
  1062. }
  1063. void MainWindow::OnAddIPAllowedListButtonClicked()
  1064. {
  1065. QString text = ui->allowedListAddIPLineEdit->text();
  1066. const QRegExpValidator *ipValidator = static_cast<const QRegExpValidator *>(ui->allowedListAddIPLineEdit->validator());
  1067. int pos;
  1068. QValidator::State state = ipValidator->validate(text, pos);
  1069. if (state== QValidator::Acceptable)
  1070. {
  1071. auto lineEdit = ui->allowedListAddIPLineEdit;
  1072. m_guiApplicationManager->GetConnectionManager()->AddAddressToAllowedList(text);
  1073. lineEdit->clear();
  1074. // Clear error state set in LineEdit.
  1075. lineEdit->setProperty(AzQtComponents::HasError, false);
  1076. auto errorToolButton = lineEdit->findChild<QToolButton*>(AzQtComponents::ErrorToolButton);
  1077. if (errorToolButton && AzQtComponents::LineEdit::errorIconEnabled(lineEdit))
  1078. {
  1079. errorToolButton->setVisible(false);
  1080. }
  1081. }
  1082. }
  1083. void MainWindow::OnToRejectedListButtonClicked()
  1084. {
  1085. QModelIndexList indices = ui->allowListAllowedListConnectionsListView->selectionModel()->selectedIndexes();
  1086. if(!indices.isEmpty() && indices.first().isValid())
  1087. {
  1088. QString itemText = indices.first().data(Qt::DisplayRole).toString();
  1089. m_guiApplicationManager->GetConnectionManager()->RemoveAddressFromAllowedList(itemText);
  1090. m_guiApplicationManager->GetConnectionManager()->AddRejectedAddress(itemText, true);
  1091. }
  1092. }
  1093. void MainWindow::OnToAllowedListButtonClicked()
  1094. {
  1095. QModelIndexList indices = ui->allowedListRejectedConnectionsListView->selectionModel()->selectedIndexes();
  1096. if (!indices.isEmpty() && indices.first().isValid())
  1097. {
  1098. QString itemText = indices.front().data(Qt::DisplayRole).toString();
  1099. m_guiApplicationManager->GetConnectionManager()->RemoveRejectedAddress(itemText);
  1100. m_guiApplicationManager->GetConnectionManager()->AddAddressToAllowedList(itemText);
  1101. }
  1102. }
  1103. void MainWindow::OnRemoveConnection(bool /*checked*/)
  1104. {
  1105. ConnectionManager* manager = m_guiApplicationManager->GetConnectionManager();
  1106. QModelIndexList list = ui->connectionTreeView->selectionModel()->selectedRows();
  1107. for (QModelIndex index: list)
  1108. {
  1109. manager->removeConnection(index);
  1110. }
  1111. }
  1112. void MainWindow::OnConnectionSelectionChanged(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/)
  1113. {
  1114. auto selectedIndices = ui->connectionTreeView->selectionModel()->selectedRows();
  1115. int selectionCount = selectedIndices.count();
  1116. bool anyUserConnectionsSelected = false;
  1117. for (int i = 0; i < selectionCount; i++)
  1118. {
  1119. QModelIndex selectedIndex = selectedIndices[i];
  1120. if (selectedIndex.data(ConnectionManager::UserConnectionRole).toBool())
  1121. {
  1122. anyUserConnectionsSelected = true;
  1123. break;
  1124. }
  1125. }
  1126. ui->removeConnectionButton->setEnabled(anyUserConnectionsSelected);
  1127. ui->editConnectionButton->setEnabled(anyUserConnectionsSelected && (selectionCount == 1));
  1128. }
  1129. MainWindow::~MainWindow()
  1130. {
  1131. m_guiApplicationManager = nullptr;
  1132. delete ui;
  1133. }
  1134. void MainWindow::ShowWindow()
  1135. {
  1136. show();
  1137. AzQtComponents::bringWindowToTop(this);
  1138. }
  1139. void MainWindow::SyncAllowedListAndRejectedList(QStringList allowedList, QStringList rejectedList)
  1140. {
  1141. m_allowedListAddresses.setStringList(allowedList);
  1142. m_rejectedAddresses.setStringList(rejectedList);
  1143. }
  1144. void MainWindow::FirstTimeAddedToRejectedList(QString ipAddress)
  1145. {
  1146. QMessageBox* msgBox = new QMessageBox(this);
  1147. msgBox->setText(tr("!!!Rejected Connection!!!"));
  1148. msgBox->setInformativeText(ipAddress + tr(" tried to connect and was rejected because it was not on the allowed list. If you want this connection to be allowed go to connections tab and add it to allowed list."));
  1149. msgBox->setStandardButtons(QMessageBox::Ok);
  1150. msgBox->setDefaultButton(QMessageBox::Ok);
  1151. msgBox->setWindowModality(Qt::NonModal);
  1152. msgBox->setModal(false);
  1153. msgBox->show();
  1154. }
  1155. void MainWindow::SaveLogPanelState()
  1156. {
  1157. if (m_loggingPanel)
  1158. {
  1159. m_loggingPanel->SaveState();
  1160. }
  1161. }
  1162. AssetProcessor::ProductDependencyTreeItem* MainWindow::GetProductAssetFromDependencyTreeView(bool isOutgoing, const QPoint& pos)
  1163. {
  1164. const QModelIndex assetIndex =
  1165. (isOutgoing ? ui->productAssetDetailsPanel->GetOutgoingProductDependenciesTreeView()->indexAt(pos)
  1166. : ui->productAssetDetailsPanel->GetIncomingProductDependenciesTreeView()->indexAt(pos));
  1167. if (!assetIndex.isValid())
  1168. {
  1169. return static_cast<AssetProcessor::ProductDependencyTreeItem*>(nullptr);
  1170. }
  1171. return static_cast<AssetProcessor::ProductDependencyTreeItem*>(assetIndex.internalPointer());
  1172. }
  1173. void MainWindow::ShowOutgoingProductDependenciesContextMenu(const QPoint& pos)
  1174. {
  1175. using namespace AssetProcessor;
  1176. const ProductDependencyTreeItem* cachedAsset = GetProductAssetFromDependencyTreeView(true, pos);
  1177. if (!cachedAsset || !cachedAsset->GetData())
  1178. {
  1179. return;
  1180. }
  1181. AZStd::string productName = cachedAsset->GetData()->m_productName;
  1182. QMenu menu(this);
  1183. menu.setToolTipsVisible(true);
  1184. QAction* productAction = menu.addAction(
  1185. tr("Go to product asset"), this,
  1186. [&, productName]()
  1187. {
  1188. ui->sourceAssetDetailsPanel->GoToProduct(productName);
  1189. });
  1190. if (productName.empty())
  1191. {
  1192. productAction->setDisabled(true);
  1193. productAction->setToolTip(tr("This asset is currently selected."));
  1194. }
  1195. else
  1196. {
  1197. productAction->setToolTip(tr("Selects this asset."));
  1198. }
  1199. menu.exec(ui->productAssetDetailsPanel->GetOutgoingProductDependenciesTreeView()->viewport()->mapToGlobal(pos));
  1200. }
  1201. void MainWindow::ShowIncomingProductDependenciesContextMenu(const QPoint& pos)
  1202. {
  1203. using namespace AssetProcessor;
  1204. const ProductDependencyTreeItem* cachedAsset = GetProductAssetFromDependencyTreeView(false, pos);
  1205. if (!cachedAsset || !cachedAsset->GetData())
  1206. {
  1207. return;
  1208. }
  1209. AZStd::string productName = cachedAsset->GetData()->m_productName;
  1210. QMenu menu(this);
  1211. menu.setToolTipsVisible(true);
  1212. QAction* productAction = menu.addAction(
  1213. tr("Go to product asset"), this,
  1214. [&, productName]()
  1215. {
  1216. ui->sourceAssetDetailsPanel->GoToProduct(productName);
  1217. });
  1218. if (productName.empty())
  1219. {
  1220. productAction->setDisabled(true);
  1221. productAction->setToolTip(tr("This asset is currently selected."));
  1222. }
  1223. else
  1224. {
  1225. productAction->setToolTip(tr("Selects this asset."));
  1226. }
  1227. menu.exec(ui->productAssetDetailsPanel->GetIncomingProductDependenciesTreeView()->viewport()->mapToGlobal(pos));
  1228. }
  1229. void MainWindow::ResetTimers()
  1230. {
  1231. m_scanTime = m_analysisTime = m_processTime = 0;
  1232. m_scanTimer.restart();
  1233. m_analysisTimer.invalidate();
  1234. m_processTimer.invalidate();
  1235. }
  1236. void MainWindow::CheckStartAnalysisTimers()
  1237. {
  1238. if (m_scanTimer.isValid())
  1239. {
  1240. m_scanTime = m_scanTimer.elapsed();
  1241. m_scanTimer.invalidate();
  1242. }
  1243. if (!m_analysisTimer.isValid() && !m_analysisTime)
  1244. {
  1245. m_analysisTimer.start();
  1246. }
  1247. }
  1248. void MainWindow::CheckStartProcessTimers()
  1249. {
  1250. if (m_analysisTimer.isValid())
  1251. {
  1252. m_analysisTime = m_analysisTimer.restart();
  1253. m_analysisTimer.invalidate();
  1254. }
  1255. if (!m_processTimer.isValid() && !m_processTime)
  1256. {
  1257. m_processTimer.start();
  1258. }
  1259. }
  1260. void MainWindow::CheckEndAnalysisTimer()
  1261. {
  1262. if (m_analysisTimer.isValid() && !m_analysisTime)
  1263. {
  1264. m_analysisTime = m_analysisTimer.elapsed();
  1265. m_analysisTimer.invalidate();
  1266. }
  1267. }
  1268. void MainWindow::CheckEndProcessTimer()
  1269. {
  1270. if (m_processTimer.isValid() && !m_processTime)
  1271. {
  1272. m_processTime = m_processTimer.elapsed();
  1273. m_processTimer.invalidate();
  1274. }
  1275. }
  1276. QString MainWindow::FormatStringTime(qint64 msTime) const
  1277. {
  1278. int msecInt{ static_cast<int>(msTime) };
  1279. int timeHrs{ msecInt / (1000 * 60 * 60) };
  1280. msecInt = msecInt % (1000 * 60 * 60);
  1281. int timeMins{ msecInt / (1000 * 60) };
  1282. msecInt = msecInt % (1000 * 60 );
  1283. int timeSecs{ msecInt / 1000 };
  1284. int timeMsec{ msecInt % 1000 };
  1285. QTime timeVal{ timeHrs, timeMins, timeSecs, timeMsec };
  1286. if (timeHrs)
  1287. {
  1288. return timeVal.toString("h:mm:ss.z");
  1289. }
  1290. return timeVal.toString("mm:ss.z");
  1291. }
  1292. void MainWindow::IntervalAssetTabFilterRefresh()
  1293. {
  1294. if (ui->buttonList->currentIndex() != static_cast<int>(DialogStackIndex::Assets)
  1295. || !ui->assetDataFilteredSearchWidget->hasStringFilter())
  1296. {
  1297. return;
  1298. }
  1299. if (!m_filterRefreshTimer.isValid())
  1300. {
  1301. m_filterRefreshTimer.start();
  1302. }
  1303. if (m_filterRefreshTimer.elapsed() >= AssetTabFilterUpdateIntervalMs)
  1304. {
  1305. emit ui->assetDataFilteredSearchWidget->TextFilterChanged(ui->assetDataFilteredSearchWidget->textFilter());
  1306. m_filterRefreshTimer.restart();
  1307. }
  1308. }
  1309. void MainWindow::ShutdownAssetTabFilterRefresh()
  1310. {
  1311. if (m_filterRefreshTimer.isValid())
  1312. {
  1313. emit ui->assetDataFilteredSearchWidget->TextFilterChanged(ui->assetDataFilteredSearchWidget->textFilter());
  1314. }
  1315. m_filterRefreshTimer.invalidate();
  1316. }
  1317. void MainWindow::OnAssetProcessorStatusChanged(const AssetProcessor::AssetProcessorStatusEntry entry)
  1318. {
  1319. using namespace AssetProcessor;
  1320. QString text;
  1321. switch (entry.m_status)
  1322. {
  1323. case AssetProcessorStatus::Initializing_Gems:
  1324. text = tr("Initializing Gem...%1").arg(entry.m_extraInfo);
  1325. break;
  1326. case AssetProcessorStatus::Initializing_Builders:
  1327. text = tr("Initializing Builders...");
  1328. break;
  1329. case AssetProcessorStatus::Scanning_Started:
  1330. ResetTimers();
  1331. text = tr("Scanning...");
  1332. break;
  1333. case AssetProcessorStatus::Analyzing_Jobs:
  1334. CheckStartAnalysisTimers();
  1335. m_createJobCount = entry.m_count;
  1336. if (m_processJobsCount + m_createJobCount > 0)
  1337. {
  1338. text += tr("Working, analyzing jobs remaining %1, processing jobs remaining %2...").arg(m_createJobCount).arg(m_processJobsCount);
  1339. if (!entry.m_extraInfo.isEmpty())
  1340. {
  1341. text += tr("<p style='font-size:small;'>%1</p>").arg(entry.m_extraInfo);
  1342. }
  1343. ui->timerContainerWidget->setVisible(false);
  1344. ui->productAssetDetailsPanel->SetScanQueueEnabled(false);
  1345. IntervalAssetTabFilterRefresh();
  1346. }
  1347. else
  1348. {
  1349. CheckEndAnalysisTimer();
  1350. text = tr("Idle...");
  1351. ui->timerContainerWidget->setVisible(true);
  1352. m_guiApplicationManager->RemoveOldTempFolders();
  1353. // Once the asset processor goes idle, enable the scan queue.
  1354. // This minimizes the potential for over-reporting missing dependencies (if a queued job would resolve them)
  1355. // and prevents running too many threads with too much work (scanning + processing jobs both take time).
  1356. ui->productAssetDetailsPanel->SetScanQueueEnabled(true);
  1357. ShutdownAssetTabFilterRefresh();
  1358. }
  1359. break;
  1360. case AssetProcessorStatus::Processing_Jobs:
  1361. CheckStartProcessTimers();
  1362. m_processJobsCount = entry.m_count;
  1363. if (m_processJobsCount + m_createJobCount > 0)
  1364. {
  1365. text = tr("Working, analyzing jobs remaining %1, processing jobs remaining %2...").arg(m_createJobCount).arg(m_processJobsCount);
  1366. ui->timerContainerWidget->setVisible(false);
  1367. ui->productAssetDetailsPanel->SetScanQueueEnabled(false);
  1368. IntervalAssetTabFilterRefresh();
  1369. }
  1370. else
  1371. {
  1372. CheckEndProcessTimer();
  1373. text = tr("Idle...");
  1374. ui->timerContainerWidget->setVisible(true);
  1375. m_guiApplicationManager->RemoveOldTempFolders();
  1376. // Once the asset processor goes idle, enable the scan queue.
  1377. // This minimizes the potential for over-reporting missing dependencies (if a queued job would resolve them)
  1378. // and prevents running too many threads with too much work (scanning + processing jobs both take time).
  1379. ui->productAssetDetailsPanel->SetScanQueueEnabled(true);
  1380. AZ_TracePrintf(
  1381. AssetProcessor::ConsoleChannel,
  1382. "Job processing completed. Asset Processor is currently idle. Process time: %s\n",
  1383. FormatStringTime(m_processTime).toUtf8().constData());
  1384. ShutdownAssetTabFilterRefresh();
  1385. }
  1386. break;
  1387. default:
  1388. text = QString();
  1389. break;
  1390. }
  1391. ui->APStatusValueLabel->setText(QStringLiteral("%1: %2")
  1392. .arg(tr("Status"))
  1393. .arg(text));
  1394. ui->lastScanTimer->setText(FormatStringTime(m_scanTime));
  1395. ui->analysisTimer->setText(FormatStringTime(m_analysisTime));
  1396. ui->processingTimer->setText(FormatStringTime(m_processTime));
  1397. }
  1398. void MainWindow::HighlightAsset(QString assetPath)
  1399. {
  1400. // Make sure that the currently active tab is the job list
  1401. ui->buttonList->setCurrentIndex(static_cast<int>(DialogStackIndex::Jobs));
  1402. // clear all filters
  1403. ui->jobFilteredSearchWidget->ClearTextFilter();
  1404. ui->jobFilteredSearchWidget->ClearTypeFilter();
  1405. // jobs are listed with relative source asset paths, so we need to remove
  1406. // the scan folder prefix from the absolute path
  1407. bool success = true;
  1408. AZStd::vector<AZStd::string> scanFolders;
  1409. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(success, &AzToolsFramework::AssetSystemRequestBus::Events::GetScanFolders, scanFolders);
  1410. if (success)
  1411. {
  1412. for (auto& scanFolder : scanFolders)
  1413. {
  1414. if (assetPath.startsWith(scanFolder.c_str(), Qt::CaseInsensitive))
  1415. {
  1416. // + 1 for the path separator
  1417. assetPath = assetPath.mid(static_cast<int>(scanFolder.size() + 1));
  1418. break;
  1419. }
  1420. }
  1421. }
  1422. // apply the filter for our asset path
  1423. ui->jobFilteredSearchWidget->SetTextFilter(assetPath);
  1424. // select the first item in the list
  1425. ui->jobTreeView->setCurrentIndex(m_jobSortFilterProxy->index(0, 0));
  1426. }
  1427. void MainWindow::OnAssetTabChange(int index)
  1428. {
  1429. AssetTabIndex tabIndex = static_cast<AssetTabIndex>(index);
  1430. switch (tabIndex)
  1431. {
  1432. case AssetTabIndex::Source:
  1433. ui->sourceAssetDetailsPanel->setVisible(true);
  1434. ui->intermediateAssetDetailsPanel->setVisible(false);
  1435. ui->productAssetDetailsPanel->setVisible(false);
  1436. break;
  1437. case AssetTabIndex::Product:
  1438. ui->sourceAssetDetailsPanel->setVisible(false);
  1439. ui->intermediateAssetDetailsPanel->setVisible(false);
  1440. ui->productAssetDetailsPanel->setVisible(true);
  1441. break;
  1442. case AssetTabIndex::Intermediate:
  1443. ui->sourceAssetDetailsPanel->setVisible(false);
  1444. ui->intermediateAssetDetailsPanel->setVisible(true);
  1445. ui->productAssetDetailsPanel->setVisible(false);
  1446. break;
  1447. }
  1448. }
  1449. void MainWindow::ApplyConfig()
  1450. {
  1451. using namespace AssetProcessor;
  1452. // Asset Status
  1453. ui->jobTreeView->header()->resizeSection(JobsModel::ColumnStatus, m_config.jobStatusColumnWidth);
  1454. ui->jobTreeView->header()->resizeSection(JobsModel::ColumnSource, m_config.jobSourceColumnWidth);
  1455. ui->jobTreeView->header()->resizeSection(JobsModel::ColumnPlatform, m_config.jobPlatformColumnWidth);
  1456. ui->jobTreeView->header()->resizeSection(JobsModel::ColumnJobKey, m_config.jobKeyColumnWidth);
  1457. ui->jobTreeView->header()->resizeSection(JobsModel::ColumnCompleted, m_config.jobCompletedColumnWidth);
  1458. // Event Log Details
  1459. ui->jobLogTableView->header()->resizeSection(AzToolsFramework::Logging::LogTableModel::ColumnType, m_config.logTypeColumnWidth);
  1460. }
  1461. MainWindow::LogSortFilterProxy::LogSortFilterProxy(QObject* parentOjbect) : QSortFilterProxyModel(parentOjbect)
  1462. {
  1463. }
  1464. bool MainWindow::LogSortFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
  1465. {
  1466. if (!m_logTypes.empty())
  1467. {
  1468. QModelIndex testIndex = sourceModel()->index(sourceRow, 0, sourceParent);
  1469. Q_ASSERT(testIndex.isValid());
  1470. auto indexLogType = static_cast<AzToolsFramework::Logging::LogLine::LogType>(testIndex.data(AzToolsFramework::Logging::LogTableModel::LogTypeRole).toInt());
  1471. if (!m_logTypes.contains(indexLogType))
  1472. {
  1473. return false;
  1474. }
  1475. }
  1476. return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
  1477. }
  1478. void MainWindow::LogSortFilterProxy::onTypeFilterChanged(const AzQtComponents::SearchTypeFilterList& activeTypeFilters)
  1479. {
  1480. beginResetModel();
  1481. m_logTypes.clear();
  1482. for (auto typeIter = activeTypeFilters.begin(), endIter = activeTypeFilters.end(); typeIter != endIter; ++typeIter)
  1483. {
  1484. m_logTypes.insert(static_cast<AzToolsFramework::Logging::LogLine::LogType>(typeIter->metadata.toInt()));
  1485. }
  1486. endResetModel();
  1487. }
  1488. void MainWindow::SetContextLogDetails(const QMap<QString, QString>& details)
  1489. {
  1490. auto model = qobject_cast<AzToolsFramework::Logging::ContextDetailsLogTableModel*>(ui->jobContextLogTableView->model());
  1491. model->SetDetails(details);
  1492. if (!details.isEmpty())
  1493. {
  1494. int tableRows = details.size();
  1495. if(tableRows > m_config.contextDetailsTableMaximumRows)
  1496. {
  1497. tableRows = m_config.contextDetailsTableMaximumRows;
  1498. }
  1499. ui->jobContextLogTableView->setMinimumHeight(ui->jobContextLogTableView->sizeHintForRow(0) * tableRows);
  1500. ui->jobDialogSplitter->setSizes({ ui->jobDialogSplitter->height(), ui->jobDialogSplitter->height(), 0 });
  1501. }
  1502. ui->jobContextContainer->setVisible(!details.isEmpty());
  1503. }
  1504. void MainWindow::ClearContextLogDetails()
  1505. {
  1506. SetContextLogDetails({});
  1507. }
  1508. void MainWindow::UpdateJobLogView(QModelIndex selectedIndex)
  1509. {
  1510. if (!m_logsModel)
  1511. {
  1512. return;
  1513. }
  1514. // SelectionMode is set to QAbstractItemView::SingleSelection so there is only one selected item at a time
  1515. AZStd::string jobLog = m_jobSortFilterProxy->data(selectedIndex, AssetProcessor::JobsModel::DataRoles::logRole).toString().toUtf8().data();
  1516. m_logsModel->Clear();
  1517. AzToolsFramework::Logging::LogLine::ParseLog(jobLog.c_str(), jobLog.length() + 1,
  1518. AZStd::bind(&AzToolsFramework::Logging::LogTableModel::AppendLineAsync, m_logsModel, AZStd::placeholders::_1));
  1519. m_logsModel->CommitLines();
  1520. ui->jobLogTableView->scrollToBottom();
  1521. ui->jobLogStackedWidget->setCurrentWidget(ui->jobLogTableView);
  1522. }
  1523. void MainWindow::JobSelectionChanged(const QItemSelection& selected, const QItemSelection& /*deselected*/)
  1524. {
  1525. if (selected.indexes().length() != 0)
  1526. {
  1527. UpdateJobLogView(selected.indexes()[0]);
  1528. }
  1529. // The only alternative is that there has been only a deselection, as otherwise both selected and deselected would be empty
  1530. else
  1531. {
  1532. ui->jobLogStackedWidget->setCurrentWidget(ui->jobLogPlaceholderLabel);
  1533. }
  1534. ClearContextLogDetails();
  1535. }
  1536. void MainWindow::JobStatusChanged([[maybe_unused]] AssetProcessor::JobEntry entry, [[maybe_unused]] AzToolsFramework::AssetSystem::JobStatus status)
  1537. {
  1538. QModelIndexList selectedIndexList = ui->jobTreeView->selectionModel()->selectedIndexes();
  1539. if (selectedIndexList.empty())
  1540. {
  1541. return;
  1542. }
  1543. QModelIndex selectedIndex = selectedIndexList.at(0);
  1544. // retrieve cachedJobInfo for the selected entry
  1545. const QModelIndex sourceIndex = m_jobSortFilterProxy->mapToSource(selectedIndex);
  1546. AssetProcessor::CachedJobInfo* cachedJobInfo = m_jobsModel->getItem(sourceIndex.row());
  1547. AZ_Assert(cachedJobInfo, "Failed to find cached job info");
  1548. if (!cachedJobInfo)
  1549. {
  1550. return;
  1551. }
  1552. // ignore the notification if it's not for the selected entry
  1553. if (cachedJobInfo->m_elementId.GetSourceAssetReference() != entry.m_sourceAssetReference)
  1554. {
  1555. return;
  1556. }
  1557. UpdateJobLogView(selectedIndex);
  1558. }
  1559. void MainWindow::JobLogSelectionChanged(const QItemSelection& selected, const QItemSelection& /*deselected*/)
  1560. {
  1561. if (selected.count() == 1)
  1562. {
  1563. const QMap<QString, QString> details = selected.indexes().first().data(AzToolsFramework::Logging::LogTableModel::DetailsRole).value<QMap<QString, QString>>();
  1564. SetContextLogDetails(details);
  1565. }
  1566. else
  1567. {
  1568. ClearContextLogDetails();
  1569. }
  1570. }
  1571. void MainWindow::DesktopOpenJobLogs()
  1572. {
  1573. char resolvedDir[AZ_MAX_PATH_LEN * 2];
  1574. QString currentDir;
  1575. AZ::IO::FileIOBase::GetInstance()->ResolvePath(AssetUtilities::ComputeJobLogFolder().c_str(), resolvedDir, sizeof(resolvedDir));
  1576. currentDir = QString::fromUtf8(resolvedDir);
  1577. if (QFile::exists(currentDir))
  1578. {
  1579. QDesktopServices::openUrl(QUrl::fromLocalFile(currentDir));
  1580. }
  1581. else
  1582. {
  1583. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "[Error] Logs folder (%s) does not exists.\n", currentDir.toUtf8().constData());
  1584. }
  1585. }
  1586. void MainWindow::ResetLoggingPanel()
  1587. {
  1588. if (m_loggingPanel)
  1589. {
  1590. m_loggingPanel->AddLogTab(AzToolsFramework::LogPanel::TabSettings("Messages", "", "", true, true, true, false));
  1591. m_loggingPanel->AddLogTab(AzToolsFramework::LogPanel::TabSettings("Warnings/Errors Only", "", "", false, true, true, false));
  1592. }
  1593. }
  1594. void MainWindow::ShowJobLogContextMenu(const QPoint& pos)
  1595. {
  1596. using namespace AzToolsFramework::Logging;
  1597. QModelIndex sourceIndex = ui->jobLogTableView->indexAt(pos);
  1598. // If there is no index under the mouse cursor, let check the selected index of the view
  1599. if (!sourceIndex.isValid())
  1600. {
  1601. const auto indexes = ui->jobLogTableView->selectionModel()->selectedIndexes();
  1602. if (!indexes.isEmpty())
  1603. {
  1604. sourceIndex = indexes.first();
  1605. }
  1606. }
  1607. QMenu menu;
  1608. QAction* line = menu.addAction(tr("Copy line"), this, [&]()
  1609. {
  1610. QGuiApplication::clipboard()->setText(sourceIndex.data(LogTableModel::LogLineTextRole).toString());
  1611. });
  1612. QAction* lineDetails = menu.addAction(tr("Copy line with details"), this, [&]()
  1613. {
  1614. QGuiApplication::clipboard()->setText(sourceIndex.data(LogTableModel::CompleteLogLineTextRole).toString());
  1615. });
  1616. menu.addAction(tr("Copy all"), this, [&]()
  1617. {
  1618. QGuiApplication::clipboard()->setText(m_logsModel->toString(true));
  1619. });
  1620. if (!sourceIndex.isValid())
  1621. {
  1622. line->setEnabled(false);
  1623. lineDetails->setEnabled(false);
  1624. }
  1625. menu.exec(ui->jobLogTableView->viewport()->mapToGlobal(pos));
  1626. }
  1627. static QString FindAbsoluteFilePath(const AssetProcessor::CachedJobInfo* cachedJobInfo)
  1628. {
  1629. return cachedJobInfo ? cachedJobInfo->m_elementId.GetSourceAssetReference().AbsolutePath().c_str() : QString{};
  1630. };
  1631. static void SendShowInAssetBrowserResponse(const QString& filePath, ConnectionManager* connectionManager, unsigned int connectionId, QByteArray data)
  1632. {
  1633. Connection* connection = connectionManager->getConnection(connectionId);
  1634. if (!connection)
  1635. {
  1636. return;
  1637. }
  1638. AzToolsFramework::AssetSystem::WantAssetBrowserShowResponse response;
  1639. bool readFromStream = AZ::Utils::LoadObjectFromBufferInPlace(data.data(), data.size(), response);
  1640. AZ_Assert(readFromStream, "AssetProcessor failed to deserialize from stream");
  1641. if (!readFromStream)
  1642. {
  1643. return;
  1644. }
  1645. #ifdef AZ_PLATFORM_WINDOWS
  1646. // Required on Windows to allow the other process to come to the foreground
  1647. AllowSetForegroundWindow(response.m_processId);
  1648. #endif // #ifdef AZ_PLATFORM_WINDOWS
  1649. AzToolsFramework::AssetSystem::AssetBrowserShowRequest message;
  1650. message.m_filePath = filePath.toUtf8().data();
  1651. connection->Send(AzFramework::AssetSystem::DEFAULT_SERIAL, message);
  1652. }
  1653. void MainWindow::ShowJobViewContextMenu(const QPoint& pos)
  1654. {
  1655. using namespace AssetProcessor;
  1656. auto cachedJobInfoAt = [this](const QPoint& pos)
  1657. {
  1658. const QModelIndex proxyIndex = ui->jobTreeView->indexAt(pos);
  1659. const QModelIndex sourceIndex = m_jobSortFilterProxy->mapToSource(proxyIndex);
  1660. return m_jobsModel->getItem(sourceIndex.row());
  1661. };
  1662. const CachedJobInfo* item = cachedJobInfoAt(pos);
  1663. if (!item)
  1664. {
  1665. return;
  1666. }
  1667. QMenu menu;
  1668. menu.setToolTipsVisible(true);
  1669. // Find a connection to an Editor, if it exists. This is used for showing this asset in the Asset Browser, if the Editor is available.
  1670. ConnectionManager* connectionManager = m_guiApplicationManager->GetConnectionManager();
  1671. Connection* editorConnection = nullptr;
  1672. auto& connectionMap = connectionManager->getConnectionMap();
  1673. auto connections = connectionMap.values();
  1674. for (auto connection : connections)
  1675. {
  1676. using namespace AzFramework::AssetSystem;
  1677. // If there is more than one Editor connected, this will only show this asset in the first connected Editor's asset browser.
  1678. if (connection->Identifier() == ConnectionIdentifiers::Editor)
  1679. {
  1680. editorConnection = connection;
  1681. break;
  1682. }
  1683. }
  1684. QAction* showInAssetBrowserAction = menu.addAction("Show in Asset Browser", this, [&]()
  1685. {
  1686. if (!editorConnection)
  1687. {
  1688. return;
  1689. }
  1690. QString filePath = FindAbsoluteFilePath(item);
  1691. AzToolsFramework::AssetSystem::WantAssetBrowserShowRequest requestMessage;
  1692. // Ask the Editor, and only the Editor, if it wants to receive
  1693. // the message for showing an asset in the AssetBrowser.
  1694. // This also allows the Editor to send back it's Process ID, which
  1695. // allows the Windows platform to call AllowSetForegroundWindow()
  1696. // which is required to bring the Editor window to the foreground
  1697. unsigned int connectionId = editorConnection->ConnectionId();
  1698. editorConnection->SendRequest(
  1699. requestMessage,
  1700. [connectionManager, connectionId, filePath](AZ::u32 /*type*/, QByteArray callbackData)
  1701. {
  1702. SendShowInAssetBrowserResponse(filePath, connectionManager, connectionId, callbackData);
  1703. });
  1704. });
  1705. // Disable the menu option if there is no Editor connection.
  1706. showInAssetBrowserAction->setEnabled(editorConnection != nullptr);
  1707. if (!editorConnection)
  1708. {
  1709. showInAssetBrowserAction->setToolTip(tr("Showing in the Asset Browser requires an active connection to the Editor."));
  1710. }
  1711. else
  1712. {
  1713. showInAssetBrowserAction->setToolTip(tr("Sends a request to the Editor to display this asset in the Asset Browser."));
  1714. }
  1715. menu.addAction("Reprocess Source Asset", this, [this, &item]()
  1716. {
  1717. QString pathToSource = FindAbsoluteFilePath(item);
  1718. m_guiApplicationManager->GetAssetProcessorManager()->RequestReprocess(pathToSource);
  1719. });
  1720. // Only completed items will be available in the assets tab.
  1721. QAction* assetTabSourceAction = menu.addAction(tr("View source asset"), this, [&]()
  1722. {
  1723. ui->dialogStack->setCurrentIndex(static_cast<int>(DialogStackIndex::Assets));
  1724. ui->buttonList->setCurrentIndex(static_cast<int>(DialogStackIndex::Assets));
  1725. ui->sourceAssetDetailsPanel->GoToSource(item->m_elementId.GetSourceAssetReference().AbsolutePath().c_str());
  1726. });
  1727. // Get the builder index outside the action, so the action can be disabled if it is not available.
  1728. QModelIndex builderIndex = m_builderList->GetIndexForBuilder(item->m_builderGuid);
  1729. QAction* assetTabBuilderAction = menu.addAction(tr("View builder"), this, [&]()
  1730. {
  1731. ui->dialogStack->setCurrentIndex(static_cast<int>(DialogStackIndex::Builders));
  1732. ui->buttonList->setCurrentIndex(static_cast<int>(DialogStackIndex::Builders));
  1733. QModelIndex filterIndex = m_builderListSortFilterProxy->mapFromSource(builderIndex);
  1734. ui->builderList->scrollTo(filterIndex);
  1735. ui->builderList->selectionModel()->setCurrentIndex(filterIndex, QItemSelectionModel::ClearAndSelect);
  1736. });
  1737. assetTabBuilderAction->setEnabled(builderIndex.isValid());
  1738. if (builderIndex.isValid())
  1739. {
  1740. assetTabBuilderAction->setToolTip(tr("Show the builder for this job in the Builder tab."));
  1741. }
  1742. else
  1743. {
  1744. assetTabBuilderAction->setToolTip(tr("The builder is unavailable for this asset."));
  1745. }
  1746. if (item->m_jobState != AzToolsFramework::AssetSystem::JobStatus::Completed)
  1747. {
  1748. QString disabledActionTooltip(tr("Only completed jobs are available in the Assets tab."));
  1749. assetTabSourceAction->setToolTip(disabledActionTooltip);
  1750. assetTabSourceAction->setDisabled(true);
  1751. // Disabled menus don't support tooltips, so add it as an action, instead.
  1752. QAction* productMenuAction = menu.addAction(productMenuTitle);
  1753. productMenuAction->setToolTip(disabledActionTooltip);
  1754. productMenuAction->setDisabled(true);
  1755. }
  1756. else
  1757. {
  1758. assetTabSourceAction->setToolTip(tr("Show the source asset for this job in the Assets tab."));
  1759. AssetRightClickMenuResult productAssetMenu(SetupProductAssetRightClickMenu(&menu));
  1760. AssetRightClickMenuResult intermediateAssetMenu(SetupIntermediateAssetRightClickMenu(&menu));
  1761. auto productMenuItemClicked = [this, &menu](QListWidgetItem* item)
  1762. {
  1763. if (item)
  1764. {
  1765. ui->dialogStack->setCurrentIndex(static_cast<int>(DialogStackIndex::Assets));
  1766. ui->buttonList->setCurrentIndex(static_cast<int>(DialogStackIndex::Assets));
  1767. AZStd::string productFromQString(item->text().toUtf8().data());
  1768. ui->sourceAssetDetailsPanel->GoToProduct(productFromQString);
  1769. menu.close();
  1770. }
  1771. };
  1772. connect(productAssetMenu.m_listWidget, &QListWidget::itemClicked, this, productMenuItemClicked);
  1773. auto intermediateMenuItemClicked = [this, &menu](QListWidgetItem* item)
  1774. {
  1775. if (item)
  1776. {
  1777. ui->dialogStack->setCurrentIndex(static_cast<int>(DialogStackIndex::Assets));
  1778. ui->buttonList->setCurrentIndex(static_cast<int>(DialogStackIndex::Assets));
  1779. QVariant data = item->data(Qt::UserRole);
  1780. ui->sourceAssetDetailsPanel->GoToSource(data.toString().toUtf8().constData());
  1781. menu.close();
  1782. }
  1783. };
  1784. connect(intermediateAssetMenu.m_listWidget, &QListWidget::itemClicked, this, intermediateMenuItemClicked);
  1785. int intermediateCount = 0;
  1786. int productCount = 0;
  1787. m_sharedDbConnection->QueryJobByJobRunKey(
  1788. item->m_jobRunKey,
  1789. [&](AzToolsFramework::AssetDatabase::JobDatabaseEntry& jobEntry)
  1790. {
  1791. m_sharedDbConnection->QueryProductByJobID(
  1792. jobEntry.m_jobID,
  1793. [&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& productEntry)
  1794. {
  1795. if (productEntry.m_productName.empty())
  1796. {
  1797. return true;
  1798. }
  1799. auto productPath = AssetUtilities::ProductPath::FromDatabasePath(productEntry.m_productName);
  1800. if (IsProductOutputFlagSet(productEntry, AssetBuilderSDK::ProductOutputFlags::IntermediateAsset))
  1801. {
  1802. ++intermediateCount;
  1803. auto productItem = new QListWidgetItem { AssetUtilities::StripAssetPlatform(productEntry.m_productName), intermediateAssetMenu.m_listWidget };
  1804. productItem->setData(Qt::UserRole, productPath.GetIntermediatePath().c_str());
  1805. intermediateAssetMenu.m_listWidget->addItem(productItem);
  1806. }
  1807. else
  1808. {
  1809. ++productCount;
  1810. auto productItem = new QListWidgetItem{ productEntry.m_productName.c_str(), productAssetMenu.m_listWidget };
  1811. productAssetMenu.m_listWidget->addItem(productItem);
  1812. }
  1813. return true; // Keep iterating, add all products.
  1814. });
  1815. return false; // Stop iterating, there should only be one job with this run key.
  1816. });
  1817. if (productCount == 0)
  1818. {
  1819. CreateDisabledAssetRightClickMenu(&menu, productAssetMenu.m_assetMenu, productMenuTitle, tr("This job created no products."));
  1820. productAssetMenu.m_assetMenu = nullptr;
  1821. }
  1822. else
  1823. {
  1824. ResizeAssetRightClickMenuList(productAssetMenu.m_listWidget, productCount);
  1825. }
  1826. if (intermediateCount == 0)
  1827. {
  1828. CreateDisabledAssetRightClickMenu(&menu, intermediateAssetMenu.m_assetMenu, intermediateMenuTitle, tr("This job created no intermediate product assets."));
  1829. intermediateAssetMenu.m_assetMenu = nullptr;
  1830. }
  1831. else
  1832. {
  1833. ResizeAssetRightClickMenuList(intermediateAssetMenu.m_listWidget, intermediateCount);
  1834. }
  1835. }
  1836. QAction* fileBrowserAction = menu.addAction(AzQtComponents::fileBrowserActionName(), this, [&]()
  1837. {
  1838. AzQtComponents::ShowFileOnDesktop(FindAbsoluteFilePath(item));
  1839. });
  1840. fileBrowserAction->setToolTip(tr("Opens a window in your operating system's file explorer to view the source asset for this job."));
  1841. menu.addAction(tr("Open"), this, [&]()
  1842. {
  1843. QDesktopServices::openUrl(QUrl::fromLocalFile(FindAbsoluteFilePath(item)));
  1844. });
  1845. menu.addAction(tr("Copy"), this, [&]()
  1846. {
  1847. QGuiApplication::clipboard()->setText(QDir::toNativeSeparators(FindAbsoluteFilePath(item)));
  1848. });
  1849. // Get the internal path to the log file
  1850. const QModelIndex proxyIndex = ui->jobTreeView->indexAt(pos);
  1851. const QModelIndex sourceIndex = m_jobSortFilterProxy->mapToSource(proxyIndex);
  1852. QVariant pathVariant = m_jobsModel->data(sourceIndex, JobsModel::logFileRole);
  1853. // Get the absolute path of the log file
  1854. AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
  1855. char resolvedPath[AZ_MAX_PATH_LEN];
  1856. fileIO->ResolvePath(pathVariant.toByteArray().constData(), resolvedPath, AZ_MAX_PATH_LEN);
  1857. QFileInfo fileInfo(resolvedPath);
  1858. auto openLogFile = menu.addAction(tr("Open log file"), this, [&]()
  1859. {
  1860. QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.absoluteFilePath()));
  1861. });
  1862. openLogFile->setEnabled(fileInfo.exists());
  1863. auto logDir = fileInfo.absoluteDir();
  1864. auto openLogFolder = menu.addAction(tr("Open folder with log file"), this, [&]()
  1865. {
  1866. if (fileInfo.exists())
  1867. {
  1868. AzQtComponents::ShowFileOnDesktop(fileInfo.absoluteFilePath());
  1869. }
  1870. else
  1871. {
  1872. // If the file doesn't exist, but the directory does, just open the directory
  1873. AzQtComponents::ShowFileOnDesktop(logDir.absolutePath());
  1874. }
  1875. });
  1876. // Only open and show the folder if the file actually exists, otherwise it's confusing
  1877. openLogFolder->setEnabled(fileInfo.exists());
  1878. menu.exec(ui->jobTreeView->viewport()->mapToGlobal(pos));
  1879. }
  1880. void MainWindow::SelectJobAndMakeVisible(const QModelIndex& index)
  1881. {
  1882. if (!index.isValid())
  1883. {
  1884. return;
  1885. }
  1886. // Make sure the job is visible, clear any existing filters.
  1887. // This has to be done before getting the filter index, because it will change.
  1888. ui->jobFilteredSearchWidget->ClearTextFilter();
  1889. ui->jobFilteredSearchWidget->ClearTypeFilter();
  1890. ui->dialogStack->setCurrentIndex(static_cast<int>(DialogStackIndex::Jobs));
  1891. ui->buttonList->setCurrentIndex(static_cast<int>(DialogStackIndex::Jobs));
  1892. QModelIndex proxyIndex = m_jobSortFilterProxy->mapFromSource(index);
  1893. ui->jobTreeView->scrollTo(proxyIndex, QAbstractItemView::ScrollHint::EnsureVisible);
  1894. // This isn't an asset tree, but use the same selection mode when selecting this row.
  1895. // Setting the current index works a bit better than just selecting, because the item will be treated as
  1896. // active for purposes of keyboard navigation and additional row highlighting (if the tree view itself gains focus)
  1897. ui->jobTreeView->selectionModel()->setCurrentIndex(proxyIndex, AssetProcessor::AssetTreeModel::GetAssetTreeSelectionFlags());
  1898. }
  1899. void MainWindow::ShowIntermediateAssetContextMenu(const QPoint& pos)
  1900. {
  1901. using namespace AssetProcessor;
  1902. auto sourceAt = [this](const QPoint& pos)
  1903. {
  1904. const QModelIndex proxyIndex = ui->IntermediateAssetsTreeView->indexAt(pos);
  1905. const QModelIndex sourceIndex = m_intermediateAssetTreeFilterModel->mapToSource(proxyIndex);
  1906. return static_cast<AssetTreeItem*>(sourceIndex.internalPointer());
  1907. };
  1908. const AssetTreeItem* cachedAsset = sourceAt(pos);
  1909. if (!cachedAsset)
  1910. {
  1911. return;
  1912. }
  1913. // Intermediate assets are functionally source assets, with an upstream source asset that generated them.
  1914. QMenu menu(this);
  1915. QAction* sourceAssetAction = menu.addAction(tr("View source asset"), this, [&]()
  1916. {
  1917. const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData = AZStd::rtti_pointer_cast<const SourceAssetTreeItemData>(cachedAsset->GetData());
  1918. // Generate the product path for this intermediate asset.
  1919. AZStd::string productPathForIntermediateAsset =
  1920. AssetUtilities::GetRelativeProductPathForIntermediateSourcePath(sourceItemData->m_assetDbName);
  1921. // Retrieve the source asset for that product.
  1922. m_sharedDbConnection->QueryProductByProductName(
  1923. productPathForIntermediateAsset.c_str(),
  1924. [&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& productEntry)
  1925. {
  1926. m_sharedDbConnection->QuerySourceByProductID(
  1927. productEntry.m_productID,
  1928. [&](AzToolsFramework::AssetDatabase::SourceDatabaseEntry& sourceEntry)
  1929. {
  1930. ui->sourceAssetDetailsPanel->GoToSource(SourceAssetReference(sourceEntry.m_scanFolderPK, sourceEntry.m_sourceName.c_str()).AbsolutePath().c_str());
  1931. return false; // Don't keep iterating
  1932. });
  1933. return false;
  1934. });
  1935. });
  1936. sourceAssetAction->setToolTip(tr("Show the source asset for this intermediate asset."));
  1937. BuildSourceAssetTreeContextMenu(menu, *cachedAsset);
  1938. menu.exec(ui->SourceAssetsTreeView->viewport()->mapToGlobal(pos));
  1939. }
  1940. void MainWindow::ShowSourceAssetContextMenu(const QPoint& pos)
  1941. {
  1942. using namespace AssetProcessor;
  1943. auto sourceAt = [this](const QPoint& pos)
  1944. {
  1945. const QModelIndex proxyIndex = ui->SourceAssetsTreeView->indexAt(pos);
  1946. const QModelIndex sourceIndex = m_sourceAssetTreeFilterModel->mapToSource(proxyIndex);
  1947. return static_cast<AssetTreeItem*>(sourceIndex.internalPointer());
  1948. };
  1949. const AssetTreeItem* cachedAsset = sourceAt(pos);
  1950. if (!cachedAsset)
  1951. {
  1952. return;
  1953. }
  1954. QMenu menu(this);
  1955. BuildSourceAssetTreeContextMenu(menu, *cachedAsset);
  1956. menu.exec(ui->SourceAssetsTreeView->viewport()->mapToGlobal(pos));
  1957. }
  1958. void MainWindow::BuildSourceAssetTreeContextMenu(QMenu& menu, const AssetProcessor::AssetTreeItem& sourceAssetTreeItem)
  1959. {
  1960. using namespace AssetProcessor;
  1961. menu.setToolTipsVisible(true);
  1962. const AZStd::shared_ptr<const SourceAssetTreeItemData> sourceItemData = AZStd::rtti_pointer_cast<const SourceAssetTreeItemData>(sourceAssetTreeItem.GetData());
  1963. QString jobMenuText(tr("View job..."));
  1964. AssetRightClickMenuResult productAssetMenu(SetupProductAssetRightClickMenu(&menu));
  1965. AssetRightClickMenuResult intermediateAssetMenu(SetupIntermediateAssetRightClickMenu(&menu));
  1966. QMenu* jobMenu = menu.addMenu(jobMenuText);
  1967. jobMenu->setToolTipsVisible(true);
  1968. auto productMenuItemClicked = [this, &menu](QListWidgetItem* item)
  1969. {
  1970. if (item)
  1971. {
  1972. AZStd::string productFromQString(item->text().toUtf8().data());
  1973. ui->sourceAssetDetailsPanel->GoToProduct(productFromQString);
  1974. menu.close();
  1975. }
  1976. };
  1977. connect(productAssetMenu.m_listWidget, &QListWidget::itemClicked, this, productMenuItemClicked);
  1978. auto intermediateMenuItemClicked = [this, &menu](QListWidgetItem* item)
  1979. {
  1980. if (item)
  1981. {
  1982. QVariant data = item->data(Qt::UserRole);
  1983. ui->sourceAssetDetailsPanel->GoToSource(data.toString().toUtf8().constData());
  1984. menu.close();
  1985. }
  1986. };
  1987. connect(intermediateAssetMenu.m_listWidget, &QListWidget::itemClicked, this, intermediateMenuItemClicked);
  1988. int intermediateCount = 0;
  1989. int productCount = 0;
  1990. SourceAssetReference sourceAsset(sourceItemData->m_scanFolderInfo.m_scanFolder.c_str(), sourceItemData->m_sourceInfo.m_sourceName.c_str());
  1991. m_sharedDbConnection->QueryJobBySourceID(sourceItemData->m_sourceInfo.m_sourceID,
  1992. [this, &jobMenu, &productAssetMenu, &intermediateAssetMenu, &intermediateCount, &productCount, sourceAsset](AzToolsFramework::AssetDatabase::JobDatabaseEntry& jobEntry)
  1993. {
  1994. QAction* jobAction = jobMenu->addAction(tr("with key %1 for platform %2").arg(jobEntry.m_jobKey.c_str(), jobEntry.m_platform.c_str()), this, [this, jobEntry, sourceAsset]()
  1995. {
  1996. QModelIndex jobIndex = m_jobsModel->GetJobFromSourceAndJobInfo(sourceAsset, jobEntry.m_platform, jobEntry.m_jobKey);
  1997. SelectJobAndMakeVisible(jobIndex);
  1998. });
  1999. jobAction->setToolTip(tr("Show this job in the Jobs tab."));
  2000. m_sharedDbConnection->QueryProductByJobID(
  2001. jobEntry.m_jobID,
  2002. [&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& productEntry)
  2003. {
  2004. if (productEntry.m_productName.empty())
  2005. {
  2006. return true;
  2007. }
  2008. auto productPath = AssetUtilities::ProductPath::FromDatabasePath(productEntry.m_productName);
  2009. if (IsProductOutputFlagSet(productEntry, AssetBuilderSDK::ProductOutputFlags::IntermediateAsset))
  2010. {
  2011. ++intermediateCount;
  2012. auto* productItem =
  2013. new QListWidgetItem{ AZStd::string(AssetUtilities::StripAssetPlatformNoCopy(productEntry.m_productName)).c_str(),
  2014. intermediateAssetMenu.m_listWidget };
  2015. productItem->setData(Qt::UserRole, productPath.GetIntermediatePath().c_str());
  2016. intermediateAssetMenu.m_listWidget->addItem(productItem);
  2017. }
  2018. else
  2019. {
  2020. ++productCount;
  2021. productAssetMenu.m_listWidget->addItem(productEntry.m_productName.c_str());
  2022. }
  2023. return true; // Keep iterating, add all products.
  2024. });
  2025. return true;
  2026. });
  2027. if (productCount == 0)
  2028. {
  2029. CreateDisabledAssetRightClickMenu(&menu, productAssetMenu.m_assetMenu, productMenuTitle, tr("This source asset has no products."));
  2030. productAssetMenu.m_assetMenu = nullptr;
  2031. }
  2032. else
  2033. {
  2034. ResizeAssetRightClickMenuList(productAssetMenu.m_listWidget, productCount);
  2035. }
  2036. if (intermediateCount == 0)
  2037. {
  2038. CreateDisabledAssetRightClickMenu(&menu, intermediateAssetMenu.m_assetMenu, intermediateMenuTitle, tr("This job created no intermediate product asset."));
  2039. intermediateAssetMenu.m_assetMenu = nullptr;
  2040. }
  2041. else
  2042. {
  2043. ResizeAssetRightClickMenuList(intermediateAssetMenu.m_listWidget, intermediateCount);
  2044. }
  2045. QAction* fileBrowserAction = menu.addAction(AzQtComponents::fileBrowserActionName(), this, [&]()
  2046. {
  2047. AZ::Outcome<QString> pathToSource = GetAbsolutePathToSource(sourceAssetTreeItem);
  2048. if (pathToSource.IsSuccess())
  2049. {
  2050. AzQtComponents::ShowFileOnDesktop(pathToSource.GetValue());
  2051. }
  2052. });
  2053. QString fileOrFolder(sourceAssetTreeItem.getChildCount() > 0 ? tr("folder") : tr("file"));
  2054. fileBrowserAction->setToolTip(tr("Opens a window in your operating system's file explorer to view this %1.").arg(fileOrFolder));
  2055. QAction* copyFullPathAction = menu.addAction(tr("Copy full path"), this, [&]()
  2056. {
  2057. AZ::Outcome<QString> pathToSource = GetAbsolutePathToSource(sourceAssetTreeItem);
  2058. if (pathToSource.IsSuccess())
  2059. {
  2060. QGuiApplication::clipboard()->setText(QDir::toNativeSeparators(pathToSource.GetValue()));
  2061. }
  2062. });
  2063. copyFullPathAction->setToolTip(tr("Copies the full path to this file to your clipboard."));
  2064. QString reprocessFolder{ tr("Reprocess Folder") };
  2065. QString reprocessFile{ tr("Reprocess File") };
  2066. QAction* reprocessAssetAction = menu.addAction(sourceAssetTreeItem.getChildCount() ? reprocessFolder : reprocessFile, this, [&]()
  2067. {
  2068. AZ::Outcome<QString> pathToSource = GetAbsolutePathToSource(sourceAssetTreeItem);
  2069. m_guiApplicationManager->GetAssetProcessorManager()->RequestReprocess(pathToSource.GetValue());
  2070. });
  2071. QString reprocessFolderTip{ tr("Put the source assets in the selected folder back in the processing queue") };
  2072. QString reprocessFileTip{ tr("Put the source asset back in the processing queue") };
  2073. reprocessAssetAction->setToolTip(sourceAssetTreeItem.getChildCount() ? reprocessFolderTip : reprocessFileTip);
  2074. }
  2075. void MainWindow::ShowProductAssetContextMenu(const QPoint& pos)
  2076. {
  2077. using namespace AssetProcessor;
  2078. auto productAt = [this](const QPoint& pos)
  2079. {
  2080. const QModelIndex proxyIndex = ui->ProductAssetsTreeView->indexAt(pos);
  2081. const QModelIndex sourceIndex = m_productAssetTreeFilterModel->mapToSource(proxyIndex);
  2082. return static_cast<AssetTreeItem*>(sourceIndex.internalPointer());
  2083. };
  2084. const AssetTreeItem* cachedAsset = productAt(pos);
  2085. if (!cachedAsset)
  2086. {
  2087. return;
  2088. }
  2089. QMenu menu(this);
  2090. menu.setToolTipsVisible(true);
  2091. const AZStd::shared_ptr<const ProductAssetTreeItemData> productItemData = AZStd::rtti_pointer_cast<const ProductAssetTreeItemData>(cachedAsset->GetData());
  2092. QAction* jobAction = menu.addAction("View job", this, [&]()
  2093. {
  2094. if (!productItemData)
  2095. {
  2096. return;
  2097. }
  2098. QModelIndex jobIndex = m_jobsModel->GetJobFromProduct(productItemData->m_databaseInfo, *m_sharedDbConnection);
  2099. SelectJobAndMakeVisible(jobIndex);
  2100. });
  2101. QAction* sourceAssetAction = menu.addAction("View source asset", this, [&]()
  2102. {
  2103. if (!productItemData)
  2104. {
  2105. return;
  2106. }
  2107. m_sharedDbConnection->QuerySourceByProductID(
  2108. productItemData->m_databaseInfo.m_productID,
  2109. [&](AzToolsFramework::AssetDatabase::SourceDatabaseEntry& sourceEntry)
  2110. {
  2111. ui->sourceAssetDetailsPanel->GoToSource(SourceAssetReference(sourceEntry.m_scanFolderPK, sourceEntry.m_sourceName.c_str()).AbsolutePath().c_str());
  2112. return false; // Don't keep iterating
  2113. });
  2114. });
  2115. if (cachedAsset->getChildCount() > 0)
  2116. {
  2117. sourceAssetAction->setDisabled(true);
  2118. sourceAssetAction->setToolTip(tr("Folders do not have source assets."));
  2119. jobAction->setDisabled(true);
  2120. jobAction->setToolTip(tr("Folders do not have associated jobs."));
  2121. }
  2122. else
  2123. {
  2124. sourceAssetAction->setToolTip(tr("Selects the source asset associated with this product asset."));
  2125. jobAction->setToolTip(tr("Selects the job that created this product asset in the Jobs tab."));
  2126. }
  2127. QAction* fileBrowserAction = menu.addAction(AzQtComponents::fileBrowserActionName(), this, [&]()
  2128. {
  2129. AZ::Outcome<QString> pathToProduct = GetAbsolutePathToProduct(*cachedAsset);
  2130. if (pathToProduct.IsSuccess())
  2131. {
  2132. AzQtComponents::ShowFileOnDesktop(pathToProduct.GetValue());
  2133. }
  2134. });
  2135. QString fileOrFolder(cachedAsset->getChildCount() > 0 ? tr("folder") : tr("file"));
  2136. fileBrowserAction->setToolTip(tr("Opens a window in your operating system's file explorer to view this %1.").arg(fileOrFolder));
  2137. QAction* copyFullPathAction = menu.addAction(tr("Copy full path"), this, [&]()
  2138. {
  2139. AZ::Outcome<QString> pathToProduct = GetAbsolutePathToProduct(*cachedAsset);
  2140. if (pathToProduct.IsSuccess())
  2141. {
  2142. QGuiApplication::clipboard()->setText(QDir::toNativeSeparators(pathToProduct.GetValue()));
  2143. }
  2144. });
  2145. copyFullPathAction->setToolTip(tr("Copies the full path for this %1 to your clipboard.").arg(fileOrFolder));
  2146. QAction* sourceAssetReprocessAction = menu.addAction("Reprocess source asset", this, [&]()
  2147. {
  2148. if (!productItemData )
  2149. {
  2150. return;
  2151. }
  2152. m_sharedDbConnection->QuerySourceByProductID(
  2153. productItemData->m_databaseInfo.m_productID,
  2154. [&](AzToolsFramework::AssetDatabase::SourceDatabaseEntry& sourceEntry)
  2155. {
  2156. m_sharedDbConnection->QueryScanFolderByScanFolderID(sourceEntry.m_scanFolderPK,[&] (AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry scanfolder)
  2157. {
  2158. QString reprocessSource{ scanfolder.m_scanFolder.c_str() };
  2159. reprocessSource.append("/");
  2160. reprocessSource.append(sourceEntry.m_sourceName.c_str());
  2161. m_guiApplicationManager->GetAssetProcessorManager()->RequestReprocess(reprocessSource);
  2162. return false; // Don't keep iterating
  2163. });
  2164. return false; // Don't keep iterating
  2165. });
  2166. });
  2167. if (cachedAsset->getChildCount() > 0)
  2168. {
  2169. sourceAssetReprocessAction->setDisabled(true);
  2170. }
  2171. sourceAssetReprocessAction->setToolTip(tr("Reprocess the source asset which created this product"));
  2172. menu.exec(ui->ProductAssetsTreeView->viewport()->mapToGlobal(pos));
  2173. }
  2174. void MainWindow::ShowLogLineContextMenu(const QPoint& pos)
  2175. {
  2176. using namespace AzToolsFramework::Logging;
  2177. QModelIndex sourceIndex = ui->jobContextLogTableView->indexAt(pos);
  2178. // If there is no index under the mouse cursor, let check the selected index of the view
  2179. if (!sourceIndex.isValid())
  2180. {
  2181. const auto indexes = ui->jobContextLogTableView->selectionModel()->selectedIndexes();
  2182. if (!indexes.isEmpty())
  2183. {
  2184. sourceIndex = indexes.first();
  2185. }
  2186. }
  2187. QMenu menu;
  2188. QAction* key = menu.addAction(tr("Copy selected key"), this, [&]()
  2189. {
  2190. QGuiApplication::clipboard()->setText(sourceIndex.sibling(sourceIndex.row(), ContextDetailsLogTableModel::ColumnKey).data().toString());
  2191. });
  2192. QAction* value = menu.addAction(tr("Copy selected value"), this, [&]()
  2193. {
  2194. QGuiApplication::clipboard()->setText(sourceIndex.sibling(sourceIndex.row(), ContextDetailsLogTableModel::ColumnValue).data().toString());
  2195. });
  2196. menu.addAction(tr("Copy all values"), this, [&]()
  2197. {
  2198. auto model = qobject_cast<ContextDetailsLogTableModel*>(ui->jobContextLogTableView->model());
  2199. QGuiApplication::clipboard()->setText(model->toString());
  2200. });
  2201. if (!sourceIndex.isValid())
  2202. {
  2203. key->setEnabled(false);
  2204. value->setEnabled(false);
  2205. }
  2206. menu.exec(ui->jobContextLogTableView->viewport()->mapToGlobal(pos));
  2207. }