You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
546 lines
18 KiB
C++
546 lines
18 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include <AzCore/std/string/string.h>
|
|
#include <GemCatalog/GemModel.h>
|
|
#include <GemCatalog/GemSortFilterProxyModel.h>
|
|
#include <AzCore/Casting/numeric_cast.h>
|
|
#include <AzToolsFramework/UI/Notifications/ToastBus.h>
|
|
|
|
namespace O3DE::ProjectManager
|
|
{
|
|
GemModel::GemModel(QObject* parent)
|
|
: QStandardItemModel(parent)
|
|
{
|
|
m_selectionModel = new QItemSelectionModel(this, parent);
|
|
}
|
|
|
|
QItemSelectionModel* GemModel::GetSelectionModel() const
|
|
{
|
|
return m_selectionModel;
|
|
}
|
|
|
|
void GemModel::AddGem(const GemInfo& gemInfo)
|
|
{
|
|
if (FindIndexByNameString(gemInfo.m_name).isValid())
|
|
{
|
|
// do not add gems with duplicate names
|
|
// this can happen by mistake or when a gem repo has a gem with the same name as a local gem
|
|
AZ_TracePrintf("GemModel", "Ignoring duplicate gem: %s", gemInfo.m_name.toUtf8().constData());
|
|
return;
|
|
}
|
|
|
|
QStandardItem* item = new QStandardItem();
|
|
|
|
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
|
|
|
item->setData(gemInfo.m_name, RoleName);
|
|
item->setData(gemInfo.m_displayName, RoleDisplayName);
|
|
item->setData(gemInfo.m_creator, RoleCreator);
|
|
item->setData(gemInfo.m_gemOrigin, RoleGemOrigin);
|
|
item->setData(aznumeric_cast<int>(gemInfo.m_platforms), RolePlatforms);
|
|
item->setData(aznumeric_cast<int>(gemInfo.m_types), RoleTypes);
|
|
item->setData(gemInfo.m_summary, RoleSummary);
|
|
item->setData(false, RoleWasPreviouslyAdded);
|
|
item->setData(gemInfo.m_isAdded, RoleIsAdded);
|
|
item->setData(gemInfo.m_directoryLink, RoleDirectoryLink);
|
|
item->setData(gemInfo.m_documentationLink, RoleDocLink);
|
|
item->setData(gemInfo.m_dependencies, RoleDependingGems);
|
|
item->setData(gemInfo.m_version, RoleVersion);
|
|
item->setData(gemInfo.m_lastUpdatedDate, RoleLastUpdated);
|
|
item->setData(gemInfo.m_binarySizeInKB, RoleBinarySize);
|
|
item->setData(gemInfo.m_features, RoleFeatures);
|
|
item->setData(gemInfo.m_path, RolePath);
|
|
item->setData(gemInfo.m_requirement, RoleRequirement);
|
|
item->setData(gemInfo.m_downloadStatus, RoleDownloadStatus);
|
|
item->setData(gemInfo.m_licenseText, RoleLicenseText);
|
|
item->setData(gemInfo.m_licenseLink, RoleLicenseLink);
|
|
|
|
appendRow(item);
|
|
|
|
const QModelIndex modelIndex = index(rowCount()-1, 0);
|
|
m_nameToIndexMap[gemInfo.m_displayName] = modelIndex;
|
|
m_nameToIndexMap[gemInfo.m_name] = modelIndex;
|
|
}
|
|
|
|
void GemModel::Clear()
|
|
{
|
|
clear();
|
|
m_nameToIndexMap.clear();
|
|
}
|
|
|
|
void GemModel::UpdateGemDependencies()
|
|
{
|
|
m_gemDependencyMap.clear();
|
|
m_gemReverseDependencyMap.clear();
|
|
|
|
for (auto iter = m_nameToIndexMap.begin(); iter != m_nameToIndexMap.end(); ++iter)
|
|
{
|
|
const QString& key = iter.key();
|
|
const QModelIndex modelIndex = iter.value();
|
|
QSet<QModelIndex> dependencies;
|
|
GetAllDependingGems(modelIndex, dependencies);
|
|
if (!dependencies.isEmpty())
|
|
{
|
|
m_gemDependencyMap.insert(key, dependencies);
|
|
}
|
|
}
|
|
|
|
for (auto iter = m_gemDependencyMap.begin(); iter != m_gemDependencyMap.end(); ++iter)
|
|
{
|
|
const QString& dependant = iter.key();
|
|
for (const QModelIndex& dependency : iter.value())
|
|
{
|
|
const QString& dependencyName = dependency.data(RoleName).toString();
|
|
if (!m_gemReverseDependencyMap.contains(dependencyName))
|
|
{
|
|
m_gemReverseDependencyMap.insert(dependencyName, QSet<QModelIndex>());
|
|
}
|
|
|
|
m_gemReverseDependencyMap[dependencyName].insert(m_nameToIndexMap[dependant]);
|
|
}
|
|
}
|
|
}
|
|
|
|
QString GemModel::GetName(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleName).toString();
|
|
}
|
|
|
|
QString GemModel::GetDisplayName(const QModelIndex& modelIndex)
|
|
{
|
|
QString displayName = modelIndex.data(RoleDisplayName).toString();
|
|
|
|
if (displayName.isEmpty())
|
|
{
|
|
return GetName(modelIndex);
|
|
}
|
|
else
|
|
{
|
|
return displayName;
|
|
}
|
|
}
|
|
|
|
QString GemModel::GetCreator(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleCreator).toString();
|
|
}
|
|
|
|
GemInfo::GemOrigin GemModel::GetGemOrigin(const QModelIndex& modelIndex)
|
|
{
|
|
return static_cast<GemInfo::GemOrigin>(modelIndex.data(RoleGemOrigin).toInt());
|
|
}
|
|
|
|
GemInfo::Platforms GemModel::GetPlatforms(const QModelIndex& modelIndex)
|
|
{
|
|
return static_cast<GemInfo::Platforms>(modelIndex.data(RolePlatforms).toInt());
|
|
}
|
|
|
|
GemInfo::Types GemModel::GetTypes(const QModelIndex& modelIndex)
|
|
{
|
|
return static_cast<GemInfo::Types>(modelIndex.data(RoleTypes).toInt());
|
|
}
|
|
|
|
GemInfo::DownloadStatus GemModel::GetDownloadStatus(const QModelIndex& modelIndex)
|
|
{
|
|
return static_cast<GemInfo::DownloadStatus>(modelIndex.data(RoleDownloadStatus).toInt());
|
|
}
|
|
|
|
QString GemModel::GetSummary(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleSummary).toString();
|
|
}
|
|
|
|
QString GemModel::GetDirectoryLink(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleDirectoryLink).toString();
|
|
}
|
|
|
|
QString GemModel::GetDocLink(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleDocLink).toString();
|
|
}
|
|
|
|
QModelIndex GemModel::FindIndexByNameString(const QString& nameString) const
|
|
{
|
|
const auto iterator = m_nameToIndexMap.find(nameString);
|
|
if (iterator != m_nameToIndexMap.end())
|
|
{
|
|
return iterator.value();
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
void GemModel::FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames)
|
|
{
|
|
for (QString& name : inOutGemNames)
|
|
{
|
|
QModelIndex modelIndex = FindIndexByNameString(name);
|
|
if (modelIndex.isValid())
|
|
{
|
|
name = GetDisplayName(modelIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
QStringList GemModel::GetDependingGems(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleDependingGems).toStringList();
|
|
}
|
|
|
|
void GemModel::GetAllDependingGems(const QModelIndex& modelIndex, QSet<QModelIndex>& inOutGems)
|
|
{
|
|
QStringList dependencies = GetDependingGems(modelIndex);
|
|
for (const QString& dependency : dependencies)
|
|
{
|
|
QModelIndex dependencyIndex = FindIndexByNameString(dependency);
|
|
if (!inOutGems.contains(dependencyIndex))
|
|
{
|
|
inOutGems.insert(dependencyIndex);
|
|
GetAllDependingGems(dependencyIndex, inOutGems);
|
|
}
|
|
}
|
|
}
|
|
|
|
QStringList GemModel::GetDependingGemNames(const QModelIndex& modelIndex)
|
|
{
|
|
QStringList result = GetDependingGems(modelIndex);
|
|
if (result.isEmpty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
FindGemDisplayNamesByNameStrings(result);
|
|
return result;
|
|
}
|
|
|
|
QString GemModel::GetVersion(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleVersion).toString();
|
|
}
|
|
|
|
QString GemModel::GetLastUpdated(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleLastUpdated).toString();
|
|
}
|
|
|
|
int GemModel::GetBinarySizeInKB(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleBinarySize).toInt();
|
|
}
|
|
|
|
QStringList GemModel::GetFeatures(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleFeatures).toStringList();
|
|
}
|
|
|
|
QString GemModel::GetPath(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RolePath).toString();
|
|
}
|
|
|
|
QString GemModel::GetRequirement(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleRequirement).toString();
|
|
}
|
|
|
|
QString GemModel::GetLicenseText(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleLicenseText).toString();
|
|
}
|
|
|
|
QString GemModel::GetLicenseLink(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleLicenseLink).toString();
|
|
}
|
|
|
|
GemModel* GemModel::GetSourceModel(QAbstractItemModel* model)
|
|
{
|
|
GemSortFilterProxyModel* proxyModel = qobject_cast<GemSortFilterProxyModel*>(model);
|
|
if (proxyModel)
|
|
{
|
|
return proxyModel->GetSourceModel();
|
|
}
|
|
else
|
|
{
|
|
return qobject_cast<GemModel*>(model);
|
|
}
|
|
}
|
|
|
|
const GemModel* GemModel::GetSourceModel(const QAbstractItemModel* model)
|
|
{
|
|
const GemSortFilterProxyModel* proxyModel = qobject_cast<const GemSortFilterProxyModel*>(model);
|
|
if (proxyModel)
|
|
{
|
|
return proxyModel->GetSourceModel();
|
|
}
|
|
else
|
|
{
|
|
return qobject_cast<const GemModel*>(model);
|
|
}
|
|
}
|
|
|
|
bool GemModel::IsAdded(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleIsAdded).toBool();
|
|
}
|
|
|
|
bool GemModel::IsAddedDependency(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleIsAddedDependency).toBool();
|
|
}
|
|
|
|
void GemModel::SetIsAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded)
|
|
{
|
|
// get the gemName first, because the modelIndex data change after adding because of filters
|
|
QString gemName = modelIndex.data(RoleName).toString();
|
|
model.setData(modelIndex, isAdded, RoleIsAdded);
|
|
|
|
UpdateDependencies(model, gemName, isAdded);
|
|
}
|
|
|
|
bool GemModel::HasDependentGems(const QModelIndex& modelIndex) const
|
|
{
|
|
QVector<QModelIndex> dependentGems = GatherDependentGems(modelIndex);
|
|
for (const QModelIndex& dependency : dependentGems)
|
|
{
|
|
if (IsAdded(dependency))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void GemModel::UpdateDependencies(QAbstractItemModel& model, const QString& gemName, bool isAdded)
|
|
{
|
|
GemModel* gemModel = GetSourceModel(&model);
|
|
AZ_Assert(gemModel, "Failed to obtain GemModel");
|
|
|
|
QModelIndex modelIndex = gemModel->FindIndexByNameString(gemName);
|
|
|
|
QVector<QModelIndex> dependencies = gemModel->GatherGemDependencies(modelIndex);
|
|
uint32_t numChangedDependencies = 0;
|
|
|
|
if (isAdded)
|
|
{
|
|
for (const QModelIndex& dependency : dependencies)
|
|
{
|
|
if (!IsAddedDependency(dependency))
|
|
{
|
|
SetIsAddedDependency(*gemModel, dependency, true);
|
|
|
|
// if the gem was already added then the state didn't really change
|
|
if (!IsAdded(dependency))
|
|
{
|
|
numChangedDependencies++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// still a dependency if some added gem depends on this one
|
|
bool hasDependentGems = gemModel->HasDependentGems(modelIndex);
|
|
if (IsAddedDependency(modelIndex) != hasDependentGems)
|
|
{
|
|
SetIsAddedDependency(*gemModel, modelIndex, hasDependentGems);
|
|
}
|
|
|
|
for (const QModelIndex& dependency : dependencies)
|
|
{
|
|
hasDependentGems = gemModel->HasDependentGems(dependency);
|
|
if (IsAddedDependency(dependency) != hasDependentGems)
|
|
{
|
|
SetIsAddedDependency(*gemModel, dependency, hasDependentGems);
|
|
|
|
// if the gem was already added then the state didn't really change
|
|
if (!IsAdded(dependency))
|
|
{
|
|
numChangedDependencies++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gemModel->emit gemStatusChanged(gemName, numChangedDependencies);
|
|
}
|
|
|
|
void GemModel::SetIsAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded)
|
|
{
|
|
model.setData(modelIndex, isAdded, RoleIsAddedDependency);
|
|
}
|
|
|
|
void GemModel::SetWasPreviouslyAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool wasAdded)
|
|
{
|
|
model.setData(modelIndex, wasAdded, RoleWasPreviouslyAdded);
|
|
|
|
if (wasAdded)
|
|
{
|
|
// update all dependencies
|
|
GemModel* gemModel = GetSourceModel(&model);
|
|
AZ_Assert(gemModel, "Failed to obtain GemModel");
|
|
QVector<QModelIndex> dependencies = gemModel->GatherGemDependencies(modelIndex);
|
|
for (const QModelIndex& dependency : dependencies)
|
|
{
|
|
SetWasPreviouslyAddedDependency(*gemModel, dependency, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GemModel::SetWasPreviouslyAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool wasAdded)
|
|
{
|
|
model.setData(modelIndex, wasAdded, RoleWasPreviouslyAddedDependency);
|
|
}
|
|
|
|
bool GemModel::WasPreviouslyAdded(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleWasPreviouslyAdded).toBool();
|
|
}
|
|
|
|
bool GemModel::WasPreviouslyAddedDependency(const QModelIndex& modelIndex)
|
|
{
|
|
return modelIndex.data(RoleWasPreviouslyAddedDependency).toBool();
|
|
}
|
|
|
|
bool GemModel::NeedsToBeAdded(const QModelIndex& modelIndex, bool includeDependencies)
|
|
{
|
|
bool previouslyAdded = modelIndex.data(RoleWasPreviouslyAdded).toBool();
|
|
bool added = modelIndex.data(RoleIsAdded).toBool();
|
|
if (includeDependencies)
|
|
{
|
|
previouslyAdded |= modelIndex.data(RoleWasPreviouslyAddedDependency).toBool();
|
|
added |= modelIndex.data(RoleIsAddedDependency).toBool();
|
|
}
|
|
return !previouslyAdded && added;
|
|
}
|
|
|
|
bool GemModel::NeedsToBeRemoved(const QModelIndex& modelIndex, bool includeDependencies)
|
|
{
|
|
bool previouslyAdded = modelIndex.data(RoleWasPreviouslyAdded).toBool();
|
|
bool added = modelIndex.data(RoleIsAdded).toBool();
|
|
if (includeDependencies)
|
|
{
|
|
previouslyAdded |= modelIndex.data(RoleWasPreviouslyAddedDependency).toBool();
|
|
added |= modelIndex.data(RoleIsAddedDependency).toBool();
|
|
}
|
|
return previouslyAdded && !added;
|
|
}
|
|
|
|
void GemModel::SetDownloadStatus(QAbstractItemModel& model, const QModelIndex& modelIndex, GemInfo::DownloadStatus status)
|
|
{
|
|
model.setData(modelIndex, status, RoleDownloadStatus);
|
|
}
|
|
|
|
bool GemModel::HasRequirement(const QModelIndex& modelIndex)
|
|
{
|
|
return !modelIndex.data(RoleRequirement).toString().isEmpty();
|
|
}
|
|
|
|
bool GemModel::DoGemsToBeAddedHaveRequirements() const
|
|
{
|
|
for (int row = 0; row < rowCount(); ++row)
|
|
{
|
|
const QModelIndex modelIndex = index(row, 0);
|
|
if (NeedsToBeAdded(modelIndex) && HasRequirement(modelIndex))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool GemModel::HasDependentGemsToRemove() const
|
|
{
|
|
for (int row = 0; row < rowCount(); ++row)
|
|
{
|
|
const QModelIndex modelIndex = index(row, 0);
|
|
if (GemModel::NeedsToBeRemoved(modelIndex, /*includeDependencies=*/true) &&
|
|
GemModel::WasPreviouslyAddedDependency(modelIndex))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QVector<QModelIndex> GemModel::GatherGemDependencies(const QModelIndex& modelIndex) const
|
|
{
|
|
QVector<QModelIndex> result;
|
|
const QString& gemName = modelIndex.data(RoleName).toString();
|
|
if (m_gemDependencyMap.contains(gemName))
|
|
{
|
|
for (const QModelIndex& dependency : m_gemDependencyMap[gemName])
|
|
{
|
|
result.push_back(dependency);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
QVector<QModelIndex> GemModel::GatherDependentGems(const QModelIndex& modelIndex, bool addedOnly) const
|
|
{
|
|
QVector<QModelIndex> result;
|
|
const QString& gemName = modelIndex.data(RoleName).toString();
|
|
if (m_gemReverseDependencyMap.contains(gemName))
|
|
{
|
|
for (const QModelIndex& dependency : m_gemReverseDependencyMap[gemName])
|
|
{
|
|
if (!addedOnly || GemModel::IsAdded(dependency))
|
|
{
|
|
result.push_back(dependency);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
QVector<QModelIndex> GemModel::GatherGemsToBeAdded(bool includeDependencies) const
|
|
{
|
|
QVector<QModelIndex> result;
|
|
for (int row = 0; row < rowCount(); ++row)
|
|
{
|
|
const QModelIndex modelIndex = index(row, 0);
|
|
if (NeedsToBeAdded(modelIndex, includeDependencies))
|
|
{
|
|
result.push_back(modelIndex);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
QVector<QModelIndex> GemModel::GatherGemsToBeRemoved(bool includeDependencies) const
|
|
{
|
|
QVector<QModelIndex> result;
|
|
for (int row = 0; row < rowCount(); ++row)
|
|
{
|
|
const QModelIndex modelIndex = index(row, 0);
|
|
if (NeedsToBeRemoved(modelIndex, includeDependencies))
|
|
{
|
|
result.push_back(modelIndex);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int GemModel::TotalAddedGems(bool includeDependencies) const
|
|
{
|
|
int result = 0;
|
|
for (int row = 0; row < rowCount(); ++row)
|
|
{
|
|
const QModelIndex modelIndex = index(row, 0);
|
|
if (IsAdded(modelIndex) || (includeDependencies && IsAddedDependency(modelIndex)))
|
|
{
|
|
++result;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
} // namespace O3DE::ProjectManager
|