27#include <QSharedPointer>
31#include <simgear/props/props_io.hxx>
32#include <simgear/structure/exception.hxx>
33#include <simgear/misc/sg_path.hxx>
34#include <simgear/package/Package.hxx>
35#include <simgear/package/Catalog.hxx>
36#include <simgear/package/Install.hxx>
49 case Delegate::STATUS_SUCCESS:
50 case Delegate::STATUS_REFRESHED:
51 case Delegate::STATUS_IN_PROGRESS:
65 m_model->m_packageRoot->addDelegate(
this);
70 m_model->m_packageRoot->removeDelegate(
this);
77 QModelIndex mi(indexForPackage(aInstall->package()));
78 m_model->dataChanged(mi, mi);
82 void installProgress(InstallRef aInstall,
unsigned int bytes,
unsigned int total)
override
86 QModelIndex mi(indexForPackage(aInstall->package()));
87 m_model->dataChanged(mi, mi);
92 QModelIndex mi(indexForPackage(aInstall->package()));
93 m_model->dataChanged(mi, mi);
95 if ((aReason != USER_CANCELLED) && (aReason != STATUS_SUCCESS)) {
97 m_model->installFailed(mi, aReason);
100 if (aReason == STATUS_SUCCESS) {
102 m_model->installSucceeded(mi);
105 m_model->installedAircraftCountChanged();
110 m_model->refreshPackages();
116 QModelIndex mi(indexForPackage(aInstall->package()));
117 m_model->dataChanged(mi, mi);
122 QModelIndex mi(indexForPackage(
pkg));
123 m_model->dataChanged(mi, mi);
124 m_model->installedAircraftCountChanged();
128 QModelIndex indexForPackage(
const PackageRef& ref)
const
130 auto it = std::find(m_model->m_packages.begin(),
131 m_model->m_packages.end(), ref);
132 if (it == m_model->m_packages.end()) {
133 return QModelIndex();
136 const int offset =
static_cast<int>(std::distance(m_model->m_packages.begin(), it));
137 return m_model->index(offset + m_model->m_cachedLocalAircraftCount);
140 AircraftItemModel* m_model;
145 if (aReason == STATUS_IN_PROGRESS) {
147 }
else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) {
148 m_model->refreshPackages();
149 }
else if (aReason == FAIL_VERSION) {
151 }
else if (aCatalog) {
152 qWarning() <<
"failed refresh of"
153 << QString::fromStdString(aCatalog->url()) <<
":" << aReason;
162 QAbstractListModel(pr)
166 this, &AircraftItemModel::onScanStarted);
168 this, &AircraftItemModel::onScanAddedItems);
170 this, &AircraftItemModel::onLocalCacheCleared);
182 m_delegate =
nullptr;
185 m_packageRoot = root;
194void AircraftItemModel::onScanStarted()
196 const int numToRemove = m_cachedLocalAircraftCount;
197 if (numToRemove > 0) {
198 int lastRow = numToRemove - 1;
199 beginRemoveRows(QModelIndex(), 0, lastRow);
200 m_delegateStates.remove(0, numToRemove);
201 m_cachedLocalAircraftCount = 0;
206void AircraftItemModel::refreshPackages()
208 simgear::pkg::PackageList newPkgs = m_packageRoot->allPackages();
209 const int firstRow = m_cachedLocalAircraftCount;
210 const int newSize =
static_cast<int>(newPkgs.size());
211 const int newTotalSize = firstRow + newSize;
213 if (m_packages.size() != newPkgs.size()) {
214 const int oldSize =
static_cast<int>(m_packages.size());
215 if (newSize > oldSize) {
217 int firstNewRow = firstRow + oldSize;
218 int lastNewRow = firstRow + newSize - 1;
219 beginInsertRows(QModelIndex(), firstNewRow, lastNewRow);
220 m_packages = newPkgs;
221 m_delegateStates.resize(newTotalSize);
225 int firstOldRow = firstRow + newSize;
226 int lastOldRow = firstRow + oldSize - 1;
227 beginRemoveRows(QModelIndex(), firstOldRow, lastOldRow);
228 m_packages = newPkgs;
229 m_delegateStates.resize(newTotalSize);
233 m_packages = newPkgs;
236 emit dataChanged(index(firstRow), index(firstRow + newSize - 1));
242 return m_cachedLocalAircraftCount +
static_cast<int>(m_packages.size());
247 int row = index.row();
249 return m_delegateStates.at(row).variant;
258 if (row >= m_cachedLocalAircraftCount) {
259 quint32 packageIndex =
static_cast<quint32
>(row - m_cachedLocalAircraftCount);
260 const PackageRef&
pkg(m_packages[packageIndex]);
261 InstallRef ex =
pkg->existingInstall();
264 return ex.valid() ? ex->downloadedPercent() : 0;
266 return static_cast<quint64
>(ex.valid() ? ex->downloadedBytes() : 0);
268 return QVariant::fromValue(
pkg);
271 return dataFromPackage(
pkg, m_delegateStates.at(row), role);
274 return dataFromItem(item, m_delegateStates.at(row), role);
278QVariant AircraftItemModel::dataFromItem(
AircraftItemPtr item,
const DelegateState& state,
int role)
const
281 return item->variants.count();
286 if (variantIndex == 0) {
290 Q_ASSERT(variantIndex < item->variants.size());
291 return item->variants.at(variantIndex)->name();
295 return QUrl::fromLocalFile(item->path);
299 if (state.variant <=
static_cast<quint32
>(item->variants.count())) {
301 item = item->variants.at(state.variant - 1);
305 if (role == Qt::DisplayRole) {
306 if (item->name().isEmpty()) {
307 return tr(
"Missing description for: %1").arg(item->baseName());
314 return item->authors;
321 }
else if (role == Qt::ToolTipRole) {
324 return QUrl::fromLocalFile(item->path);
327 for (
int i=0;
i<4; ++
i) {
328 have |= (item->ratings[
i] > 0);
332 return item->description();
334 return item->usesHeliports;
336 return item->usesSeaports;
342 return item->status(0 );
344 return item->minFGVersion;
350QVariant AircraftItemModel::dataFromPackage(
const PackageRef& item,
const DelegateState& state,
int role)
const
354 QString desc = QString::fromStdString(item->nameForVariant(variantIndex));
355 if (desc.isEmpty()) {
356 desc = tr(
"Missing description for: %1").arg(QString::fromStdString(item->id()));
361 if (role == Qt::DisplayRole) {
362 QString desc = QString::fromStdString(item->nameForVariant(state.variant));
363 if (desc.isEmpty()) {
364 desc = tr(
"Missing description for: %1").arg(QString::fromStdString(item->id()));
368 InstallRef
i = item->existingInstall();
370 return QString::fromStdString(
i->primarySetPath().utf8Str());
373 return QString::fromStdString(item->variants()[state.variant]);
375 InstallRef
i = item->existingInstall();
377 if (
i->isDownloading()) {
383 if (
i->hasUpdate()) {
387 const auto status =
i->status();
398 return static_cast<quint32
>(item->variants().size() - 1);
400 std::string authors = item->getLocalisedProp(
"author", state.variant);
401 if (!authors.empty()) {
402 return QString::fromStdString(authors);
405 std::string longDesc = item->getLocalisedProp(
"description", state.variant);
406 return QString::fromStdString(longDesc).simplified();
408 return static_cast<int>(item->fileSizeBytes());
410 return QUrl(
"package:" + QString::fromStdString(item->qualifiedVariantId(state.variant)));
412 return QUrl(
"package:" + QString::fromStdString(item->qualifiedId()));
414 return item->properties()->hasChild(
"rating");
420 const std::string v = item->properties()->getStringValue(
"minimum-fg-version");
422 return QString::fromStdString(v);
425 return item->hasTag(
"helicopter");
427 return item->hasTag(
"seaplane") || item->hasTag(
"floats");
433QVariant AircraftItemModel::packageRating(
const PackageRef&
p,
int ratingIndex)
const
440 int row = index.row();
441 quint32 newValue = value.toUInt();
444 if (m_delegateStates[row].variant == newValue) {
448 m_delegateStates[row].variant = newValue;
449 emit dataChanged(index, index);
455 emit dataChanged(index, index);
464 QHash<int, QByteArray> result = QAbstractListModel::roleNames();
466 result[Qt::DisplayRole] =
"title";
494 return QModelIndex();
497 if (uri.isLocalFile()) {
502 }
else if (uri.scheme() ==
"package") {
503 QString ident = uri.path();
504 int rowOffset = m_cachedLocalAircraftCount;
506 PackageRef
pkg = m_packageRoot->getPackageById(ident.toStdString());
508 const auto numPackages = m_packages.size();
509 for (
unsigned int i=0;
i < numPackages; ++
i) {
510 if (m_packages.at(
i) ==
pkg) {
511 return index(
static_cast<int>(rowOffset +
i));
515 }
else if (uri.scheme() ==
"") {
518 qWarning() <<
"Unknown aircraft URI scheme" << uri << uri.scheme();
521 return QModelIndex();
530 int variantIndex = 0;
531 QModelIndex modelIndex;
533 if (uri.isLocalFile()) {
539 modelIndex = index(row);
543 const QString path = uri.toLocalFile();
544 for (
int vr=0; vr < item->variants.size(); ++vr) {
545 if (item->variants.at(vr)->path == path) {
546 variantIndex = vr + 1;
550 }
else if (uri.scheme() ==
"package") {
551 QString ident = uri.path();
552 int rowOffset = m_cachedLocalAircraftCount;
554 PackageRef
pkg = m_packageRoot->getPackageById(ident.toStdString());
556 for (
size_t i=0;
i < m_packages.size(); ++
i) {
557 if (m_packages[
i] ==
pkg) {
558 modelIndex = index(rowOffset +
static_cast<int>(
i));
559 variantIndex =
pkg->indexOfVariant(ident.toStdString());
565 qWarning() <<
"Unknown aircraft URI scheme" << uri << uri.scheme();
569 if (modelIndex.isValid()) {
576 if (uri.isLocalFile()) {
582 const QString path = uri.toLocalFile();
583 if (item->path == path) {
588 for (
int vr=0; vr < item->variants.size(); ++vr) {
589 auto variant = item->variants.at(vr);
590 if (variant->path == path) {
591 return variant->name();
594 }
else if (uri.scheme() ==
"package") {
595 QString ident = uri.path();
596 PackageRef
pkg = m_packageRoot->getPackageById(ident.toStdString());
598 const auto variantIndex =
pkg->indexOfVariant(ident.toStdString());
599 return QString::fromStdString(
pkg->nameForVariant(variantIndex));
602 qWarning() <<
"Unknown aircraft URI scheme" << uri << uri.scheme();
610 int c = m_cachedLocalAircraftCount;
612 for (
const auto& cat : m_packageRoot->catalogs()) {
613 c +=
static_cast<int>(cat->installedPackages().size());
619void AircraftItemModel::onScanAddedItems(
int addedCount)
623 const int newItemCount = items.size() - m_cachedLocalAircraftCount;
624 const int firstRow = m_cachedLocalAircraftCount;
625 const int lastRow = firstRow + newItemCount - 1;
627 beginInsertRows(QModelIndex(), firstRow, lastRow);
628 m_delegateStates.insert(m_cachedLocalAircraftCount, newItemCount, {});
629 m_cachedLocalAircraftCount += newItemCount;
635void AircraftItemModel::onLocalCacheCleared()
637 if (m_cachedLocalAircraftCount > 0) {
638 const int firstRow = 0;
639 const int lastRow = m_cachedLocalAircraftCount - 1;
641 beginRemoveRows(QModelIndex(), firstRow, lastRow);
642 m_delegateStates.remove(0, m_cachedLocalAircraftCount);
643 m_cachedLocalAircraftCount = 0;
650void AircraftItemModel::installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason)
654 case Delegate::FAIL_CHECKSUM:
655 msg = tr(
"Invalid package checksum");
break;
656 case Delegate::FAIL_DOWNLOAD:
657 msg = tr(
"Download failed");
break;
658 case Delegate::FAIL_EXTRACT:
659 msg = tr(
"Package could not be extracted");
break;
660 case Delegate::FAIL_FILESYSTEM:
661 msg = tr(
"A local file-system error occurred");
break;
662 case Delegate::FAIL_NOT_FOUND:
663 msg = tr(
"Package file missing from download server");
break;
664 case Delegate::FAIL_UNKNOWN:
666 msg = tr(
"Unknown reason");
672void AircraftItemModel::installSucceeded(QModelIndex index)
679 if (index.row() < m_cachedLocalAircraftCount) {
683 quint32 packageIndex =
static_cast<quint32
>(index.row() - m_cachedLocalAircraftCount);
684 const PackageRef&
pkg(m_packages[packageIndex]);
685 InstallRef ex =
pkg->existingInstall();
690 return !ex->isDownloading();
bool isPackageFailure(Delegate::StatusCode status)
const int AircraftVariantCountRole
const int AircraftInstallPercentRole
const int AircraftInstallDownloadedSizeRole
const int AircraftMinVersionRole
const int AircraftRatingRole
const int AircraftIsSeaplaneRole
const int AircraftVariantDescriptionRole
const int AircraftHasRatingsRole
const int AircraftPathRole
const int AircraftPackageIdRole
const int AircraftAuthorsRole
const int AircraftLongDescriptionRole
const int AircraftPrimaryURIRole
const int AircraftPackageRefRole
const int AircraftIsHelicopterRole
const int AircraftStatusRole
const int AircraftURIRole
const int AircraftIsFavouriteRole
const int AircraftPackageSizeRole
const int AircraftVariantRole
const int AircraftPackageStatusRole
QSharedPointer< AircraftItem > AircraftItemPtr
int installedAircraftCount
~AircraftItemModel() override
QModelIndex indexOfAircraftURI(QUrl uri) const
given a -set.xml path, return the corresponding model index, if one exists.
void setPackageRoot(const simgear::pkg::RootRef &root)
bool isIndexRunnable(const QModelIndex &index) const
return if a given aircraft is ready to be run, or not.
int rowCount(const QModelIndex &parent) const override
bool setData(const QModelIndex &index, const QVariant &value, int role) override
void aircraftInstallFailed(QModelIndex index, QString errorMessage)
QHash< int, QByteArray > roleNames() const override
AircraftItemModel(QObject *pr)
AircraftItemModel::AircraftItemModel.
void selectVariantForAircraftURI(QUrl uri)
ensure the appropriate variant index is active in the model, for the corresponding aircraft URI
QVariant data(const QModelIndex &index, int role) const override
void installedAircraftCountChanged()
void aircraftInstallCompleted(QModelIndex index)
QString nameForAircraftURI(QUrl uri) const
Retrieve the display name for an aircraft specified by URI, without changing the current variant stat...
friend class PackageDelegate
bool isFavourite(QUrl u) const
bool setFavourite(QUrl u, bool b)
static FavouriteAircraftData * instance()
AircraftItemPtr itemAt(int index) const
static int ratingFromProperties(SGPropertyNode *node, int ratingIndex)
void addedItems(int count)
QVector< AircraftItemPtr > allItems() const
int findIndexWithUri(QUrl aircraftUri) const
static LocalAircraftCache * instance()
AircraftItemPtr findItemWithUri(QUrl aircraftUri) const
void finishInstall(InstallRef aInstall, StatusCode aReason) override
PackageDelegate(AircraftItemModel *model)
void catalogRefreshed(CatalogRef aCatalog, StatusCode aReason) override
void finishUninstall(const PackageRef &pkg) override
void availablePackagesChanged() override
void startInstall(InstallRef aInstall) override
void installStatusChanged(InstallRef aInstall, StatusCode aReason) override
~PackageDelegate() override
void installProgress(InstallRef aInstall, unsigned int bytes, unsigned int total) override
static QVariant packageAircraftStatus(simgear::pkg::PackageRef p)
void addSentryBreadcrumb(const std::string &, const std::string &)